@defai.digital/ax-cli 3.8.6 → 3.8.8

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 (196) hide show
  1. package/README.md +28 -393
  2. package/config-defaults/models.yaml +0 -24
  3. package/config-defaults/settings.yaml +16 -16
  4. package/dist/agent/dependency-resolver.js +22 -1
  5. package/dist/agent/dependency-resolver.js.map +1 -1
  6. package/dist/agent/llm-agent.d.ts +23 -2
  7. package/dist/agent/llm-agent.js +126 -122
  8. package/dist/agent/llm-agent.js.map +1 -1
  9. package/dist/agent/loop-detector.d.ts +70 -0
  10. package/dist/agent/loop-detector.js +339 -0
  11. package/dist/agent/loop-detector.js.map +1 -0
  12. package/dist/agent/progress-tracker.d.ts +94 -0
  13. package/dist/agent/progress-tracker.js +222 -0
  14. package/dist/agent/progress-tracker.js.map +1 -0
  15. package/dist/agent/status-reporter.js +2 -2
  16. package/dist/agent/status-reporter.js.map +1 -1
  17. package/dist/agent/subagent.js +7 -3
  18. package/dist/agent/subagent.js.map +1 -1
  19. package/dist/analyzers/architecture/project-structure-scanner.js +6 -2
  20. package/dist/analyzers/architecture/project-structure-scanner.js.map +1 -1
  21. package/dist/analyzers/git/churn-calculator.js +2 -1
  22. package/dist/analyzers/git/churn-calculator.js.map +1 -1
  23. package/dist/checkpoint/manager.js +18 -4
  24. package/dist/checkpoint/manager.js.map +1 -1
  25. package/dist/checkpoint/storage.d.ts +6 -0
  26. package/dist/checkpoint/storage.js +96 -49
  27. package/dist/checkpoint/storage.js.map +1 -1
  28. package/dist/commands/cache.js +8 -6
  29. package/dist/commands/cache.js.map +1 -1
  30. package/dist/commands/doctor.js +19 -27
  31. package/dist/commands/doctor.js.map +1 -1
  32. package/dist/commands/mcp-migrate.js +6 -5
  33. package/dist/commands/mcp-migrate.js.map +1 -1
  34. package/dist/commands/mcp.js +14 -2
  35. package/dist/commands/mcp.js.map +1 -1
  36. package/dist/commands/models.js +8 -12
  37. package/dist/commands/models.js.map +1 -1
  38. package/dist/commands/plan.js +1 -10
  39. package/dist/commands/plan.js.map +1 -1
  40. package/dist/commands/setup.js +4 -3
  41. package/dist/commands/setup.js.map +1 -1
  42. package/dist/commands/status.js +40 -14
  43. package/dist/commands/status.js.map +1 -1
  44. package/dist/constants.d.ts +12 -0
  45. package/dist/constants.js +16 -4
  46. package/dist/constants.js.map +1 -1
  47. package/dist/hooks/hook-runner.d.ts +138 -0
  48. package/dist/hooks/hook-runner.js +429 -0
  49. package/dist/hooks/hook-runner.js.map +1 -0
  50. package/dist/hooks/index.d.ts +6 -0
  51. package/dist/hooks/index.js +7 -0
  52. package/dist/hooks/index.js.map +1 -0
  53. package/dist/index.js +3 -21
  54. package/dist/index.js.map +1 -1
  55. package/dist/llm/client.d.ts +9 -0
  56. package/dist/llm/client.js +306 -45
  57. package/dist/llm/client.js.map +1 -1
  58. package/dist/llm/tools.js +2 -39
  59. package/dist/llm/tools.js.map +1 -1
  60. package/dist/llm/types.d.ts +1 -47
  61. package/dist/llm/types.js +0 -18
  62. package/dist/llm/types.js.map +1 -1
  63. package/dist/mcp/automatosx-loader.js +2 -1
  64. package/dist/mcp/automatosx-loader.js.map +1 -1
  65. package/dist/mcp/client-v2.d.ts +3 -0
  66. package/dist/mcp/client-v2.js +85 -19
  67. package/dist/mcp/client-v2.js.map +1 -1
  68. package/dist/mcp/config-migrator.js +3 -2
  69. package/dist/mcp/config-migrator.js.map +1 -1
  70. package/dist/mcp/config-v2.d.ts +5 -0
  71. package/dist/mcp/config-v2.js +26 -0
  72. package/dist/mcp/config-v2.js.map +1 -1
  73. package/dist/mcp/error-formatter.js +4 -1
  74. package/dist/mcp/error-formatter.js.map +1 -1
  75. package/dist/mcp/health.js +1 -1
  76. package/dist/mcp/health.js.map +1 -1
  77. package/dist/mcp/reconnection.js +2 -1
  78. package/dist/mcp/reconnection.js.map +1 -1
  79. package/dist/mcp/registry.js +3 -2
  80. package/dist/mcp/registry.js.map +1 -1
  81. package/dist/mcp/resources.js +2 -1
  82. package/dist/mcp/resources.js.map +1 -1
  83. package/dist/mcp/validation.js +9 -0
  84. package/dist/mcp/validation.js.map +1 -1
  85. package/dist/memory/context-store.js +4 -6
  86. package/dist/memory/context-store.js.map +1 -1
  87. package/dist/memory/types.d.ts +2 -0
  88. package/dist/memory/types.js +4 -1
  89. package/dist/memory/types.js.map +1 -1
  90. package/dist/permissions/index.d.ts +6 -0
  91. package/dist/permissions/index.js +7 -0
  92. package/dist/permissions/index.js.map +1 -0
  93. package/dist/permissions/permission-manager.d.ts +145 -0
  94. package/dist/permissions/permission-manager.js +401 -0
  95. package/dist/permissions/permission-manager.js.map +1 -0
  96. package/dist/planner/plan-storage.js +3 -2
  97. package/dist/planner/plan-storage.js.map +1 -1
  98. package/dist/planner/task-planner.js +2 -1
  99. package/dist/planner/task-planner.js.map +1 -1
  100. package/dist/planner/types.d.ts +6 -6
  101. package/dist/schemas/settings-schemas.d.ts +0 -14
  102. package/dist/schemas/settings-schemas.js +0 -10
  103. package/dist/schemas/settings-schemas.js.map +1 -1
  104. package/dist/schemas/tool-schemas.d.ts +2 -2
  105. package/dist/schemas/yaml-schemas.d.ts +15 -0
  106. package/dist/schemas/yaml-schemas.js +3 -0
  107. package/dist/schemas/yaml-schemas.js.map +1 -1
  108. package/dist/tools/bash.js +35 -10
  109. package/dist/tools/bash.js.map +1 -1
  110. package/dist/tools/confirmation-tool.js +3 -2
  111. package/dist/tools/confirmation-tool.js.map +1 -1
  112. package/dist/tools/registry.d.ts +1 -1
  113. package/dist/tools/registry.js +2 -1
  114. package/dist/tools/registry.js.map +1 -1
  115. package/dist/tools/search.js +12 -13
  116. package/dist/tools/search.js.map +1 -1
  117. package/dist/tools/text-editor.d.ts +46 -0
  118. package/dist/tools/text-editor.js +455 -11
  119. package/dist/tools/text-editor.js.map +1 -1
  120. package/dist/tools/todo-tool.js +5 -4
  121. package/dist/tools/todo-tool.js.map +1 -1
  122. package/dist/ui/components/chat-input.js +10 -1
  123. package/dist/ui/components/chat-input.js.map +1 -1
  124. package/dist/ui/components/chat-interface.js +1 -0
  125. package/dist/ui/components/chat-interface.js.map +1 -1
  126. package/dist/ui/components/tool-group-display.js +0 -6
  127. package/dist/ui/components/tool-group-display.js.map +1 -1
  128. package/dist/ui/hooks/use-input-handler.js +7 -6
  129. package/dist/ui/hooks/use-input-handler.js.map +1 -1
  130. package/dist/ui/hooks/use-input-history.js +21 -13
  131. package/dist/ui/hooks/use-input-history.js.map +1 -1
  132. package/dist/ui/utils/tool-grouper.d.ts +1 -2
  133. package/dist/ui/utils/tool-grouper.js +4 -15
  134. package/dist/ui/utils/tool-grouper.js.map +1 -1
  135. package/dist/utils/api-error.d.ts +61 -0
  136. package/dist/utils/api-error.js +176 -0
  137. package/dist/utils/api-error.js.map +1 -0
  138. package/dist/utils/audit-logger.js +2 -1
  139. package/dist/utils/audit-logger.js.map +1 -1
  140. package/dist/utils/auto-accept-logger.js +3 -3
  141. package/dist/utils/auto-accept-logger.js.map +1 -1
  142. package/dist/utils/config-loader.d.ts +3 -0
  143. package/dist/utils/config-loader.js +27 -2
  144. package/dist/utils/config-loader.js.map +1 -1
  145. package/dist/utils/encryption.js +2 -1
  146. package/dist/utils/encryption.js.map +1 -1
  147. package/dist/utils/file-cache.js +4 -2
  148. package/dist/utils/file-cache.js.map +1 -1
  149. package/dist/utils/history-migration.js +5 -4
  150. package/dist/utils/history-migration.js.map +1 -1
  151. package/dist/utils/onboarding-manager.js +2 -1
  152. package/dist/utils/onboarding-manager.js.map +1 -1
  153. package/dist/utils/path-helpers.d.ts +8 -0
  154. package/dist/utils/path-helpers.js +35 -0
  155. package/dist/utils/path-helpers.js.map +1 -0
  156. package/dist/utils/path-security.js +3 -2
  157. package/dist/utils/path-security.js.map +1 -1
  158. package/dist/utils/retry-helper.d.ts +61 -0
  159. package/dist/utils/retry-helper.js +206 -0
  160. package/dist/utils/retry-helper.js.map +1 -0
  161. package/dist/utils/settings-manager.d.ts +1 -21
  162. package/dist/utils/settings-manager.js +2 -82
  163. package/dist/utils/settings-manager.js.map +1 -1
  164. package/dist/utils/streaming-analyzer.d.ts +2 -13
  165. package/dist/utils/streaming-analyzer.js +3 -25
  166. package/dist/utils/streaming-analyzer.js.map +1 -1
  167. package/dist/utils/token-counter.d.ts +13 -1
  168. package/dist/utils/token-counter.js +31 -6
  169. package/dist/utils/token-counter.js.map +1 -1
  170. package/package.json +3 -2
  171. package/packages/schemas/README.md +1 -1
  172. package/packages/schemas/package.json +1 -1
  173. package/dist/tools/web-search/cache.d.ts +0 -62
  174. package/dist/tools/web-search/cache.js +0 -105
  175. package/dist/tools/web-search/cache.js.map +0 -1
  176. package/dist/tools/web-search/engines/crates.d.ts +0 -19
  177. package/dist/tools/web-search/engines/crates.js +0 -87
  178. package/dist/tools/web-search/engines/crates.js.map +0 -1
  179. package/dist/tools/web-search/engines/npm.d.ts +0 -18
  180. package/dist/tools/web-search/engines/npm.js +0 -86
  181. package/dist/tools/web-search/engines/npm.js.map +0 -1
  182. package/dist/tools/web-search/engines/pypi.d.ts +0 -18
  183. package/dist/tools/web-search/engines/pypi.js +0 -75
  184. package/dist/tools/web-search/engines/pypi.js.map +0 -1
  185. package/dist/tools/web-search/index.d.ts +0 -11
  186. package/dist/tools/web-search/index.js +0 -11
  187. package/dist/tools/web-search/index.js.map +0 -1
  188. package/dist/tools/web-search/router.d.ts +0 -34
  189. package/dist/tools/web-search/router.js +0 -245
  190. package/dist/tools/web-search/router.js.map +0 -1
  191. package/dist/tools/web-search/types.d.ts +0 -45
  192. package/dist/tools/web-search/types.js +0 -6
  193. package/dist/tools/web-search/types.js.map +0 -1
  194. package/dist/tools/web-search/web-search-tool.d.ts +0 -51
  195. package/dist/tools/web-search/web-search-tool.js +0 -246
  196. package/dist/tools/web-search/web-search-tool.js.map +0 -1
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Intelligent Loop Detection System
3
+ *
4
+ * Based on Claude Code and industry best practices:
5
+ * 1. Progress-based detection (checks if state changes)
6
+ * 2. Tool-specific thresholds (different tools have different legitimate repeat patterns)
7
+ * 3. Sequence pattern detection (A→B→A→B cycles)
8
+ * 4. Configurable and transparent
9
+ *
10
+ * Key insight: The problem isn't repeated tool calls, it's repeated tool calls
11
+ * that don't make progress. Creating 10 files is fine. Trying to edit the same
12
+ * file 10 times with the same failing edit is not.
13
+ */
14
+ import { AGENT_CONFIG } from "../constants.js";
15
+ /**
16
+ * Configuration for tool-specific thresholds
17
+ * Higher thresholds for tools that are legitimately called repeatedly
18
+ */
19
+ const TOOL_THRESHOLDS = {
20
+ // File exploration - often need to view many files
21
+ view_file: 10,
22
+ read_file: 10,
23
+ list_files: 8,
24
+ // File creation - creating multiple files is normal
25
+ create_file: 15,
26
+ write_to_file: 15,
27
+ // Editing - more restrictive since repeated edits usually mean failure
28
+ str_replace_editor: 4,
29
+ // Search - may need multiple searches
30
+ search_files: 6,
31
+ search: 6,
32
+ // Bash - varies widely, use moderate threshold
33
+ bash: 8,
34
+ execute_bash: 8,
35
+ // Todo list - frequently updated
36
+ create_todo_list: 3,
37
+ update_todo_list: 10, // Higher because progress updates are normal
38
+ // Default for unknown tools
39
+ default: 5,
40
+ };
41
+ /**
42
+ * Tools that should be tracked by unique path/target
43
+ * These count repetitions per-target rather than globally
44
+ */
45
+ const PATH_TRACKED_TOOLS = new Set([
46
+ 'view_file',
47
+ 'read_file',
48
+ 'create_file',
49
+ 'write_to_file',
50
+ 'str_replace_editor',
51
+ ]);
52
+ /**
53
+ * Tools where failure should lower the threshold
54
+ * (repeated failures are more likely to be loops)
55
+ */
56
+ const FAILURE_SENSITIVE_TOOLS = new Set([
57
+ 'str_replace_editor',
58
+ 'bash',
59
+ 'execute_bash',
60
+ ]);
61
+ export class LoopDetector {
62
+ /** Recent tool calls with full context */
63
+ callHistory = [];
64
+ /** Signature -> count for quick lookup */
65
+ signatureCounts = new Map();
66
+ /** Signature -> consecutive failure count */
67
+ failureCounts = new Map();
68
+ /** Last N signatures for sequence detection */
69
+ recentSequence = [];
70
+ /** Maximum history size */
71
+ maxHistorySize = 100;
72
+ /** Maximum sequence length for pattern detection */
73
+ maxSequenceLength = 20;
74
+ /**
75
+ * Check if a tool call would create a loop
76
+ * Call this BEFORE executing the tool
77
+ */
78
+ checkForLoop(toolCall) {
79
+ // Check if loop detection is disabled
80
+ if (!AGENT_CONFIG.ENABLE_LOOP_DETECTION) {
81
+ return { isLoop: false, count: 0, threshold: Infinity };
82
+ }
83
+ try {
84
+ const args = this.parseArgs(toolCall);
85
+ const signature = this.createSignature(toolCall.function.name, args);
86
+ const threshold = this.getThreshold(toolCall.function.name, signature);
87
+ const currentCount = this.signatureCounts.get(signature) || 0;
88
+ const failureCount = this.failureCounts.get(signature) || 0;
89
+ // Adjust threshold based on failures
90
+ const adjustedThreshold = this.adjustThresholdForFailures(toolCall.function.name, threshold, failureCount);
91
+ // Check 1: Simple count-based detection with tool-specific threshold
92
+ if (currentCount >= adjustedThreshold) {
93
+ return {
94
+ isLoop: true,
95
+ reason: `Tool "${toolCall.function.name}" called ${currentCount + 1} times with same signature (threshold: ${adjustedThreshold})`,
96
+ suggestion: this.getSuggestion(toolCall.function.name, args),
97
+ count: currentCount + 1,
98
+ threshold: adjustedThreshold,
99
+ };
100
+ }
101
+ // Check 2: Sequence pattern detection (A→B→A→B cycles)
102
+ const cycleResult = this.detectCycle(signature);
103
+ if (cycleResult.isLoop) {
104
+ return {
105
+ ...cycleResult,
106
+ count: currentCount + 1,
107
+ threshold: adjustedThreshold,
108
+ };
109
+ }
110
+ return {
111
+ isLoop: false,
112
+ count: currentCount + 1,
113
+ threshold: adjustedThreshold,
114
+ };
115
+ }
116
+ catch (error) {
117
+ // On parse error, don't block
118
+ return { isLoop: false, count: 0, threshold: Infinity };
119
+ }
120
+ }
121
+ /**
122
+ * Record a tool call after execution
123
+ * Call this AFTER executing the tool
124
+ */
125
+ recordToolCall(toolCall, success, outputHash) {
126
+ const args = this.parseArgs(toolCall);
127
+ const signature = this.createSignature(toolCall.function.name, args);
128
+ const filePath = this.extractFilePath(args);
129
+ // Record in history
130
+ const record = {
131
+ signature,
132
+ toolName: toolCall.function.name,
133
+ args,
134
+ timestamp: Date.now(),
135
+ success,
136
+ filePath,
137
+ outputHash,
138
+ };
139
+ this.callHistory.push(record);
140
+ // Update signature count
141
+ const count = (this.signatureCounts.get(signature) || 0) + 1;
142
+ this.signatureCounts.set(signature, count);
143
+ // Update failure count
144
+ if (!success) {
145
+ const failures = (this.failureCounts.get(signature) || 0) + 1;
146
+ this.failureCounts.set(signature, failures);
147
+ }
148
+ else {
149
+ // Reset failure count on success
150
+ this.failureCounts.delete(signature);
151
+ }
152
+ // Update sequence
153
+ this.recentSequence.push(signature);
154
+ if (this.recentSequence.length > this.maxSequenceLength) {
155
+ this.recentSequence.shift();
156
+ }
157
+ // Cleanup old entries
158
+ this.cleanup();
159
+ }
160
+ /**
161
+ * Reset all tracking (call at start of new conversation)
162
+ */
163
+ reset() {
164
+ this.callHistory = [];
165
+ this.signatureCounts.clear();
166
+ this.failureCounts.clear();
167
+ this.recentSequence = [];
168
+ }
169
+ /**
170
+ * Get current stats for debugging
171
+ */
172
+ getStats() {
173
+ return {
174
+ historySize: this.callHistory.length,
175
+ uniqueSignatures: this.signatureCounts.size,
176
+ failedSignatures: this.failureCounts.size,
177
+ };
178
+ }
179
+ // ============================================================================
180
+ // Private Methods
181
+ // ============================================================================
182
+ parseArgs(toolCall) {
183
+ if (!toolCall.function.arguments) {
184
+ return {};
185
+ }
186
+ if (typeof toolCall.function.arguments === 'string') {
187
+ try {
188
+ return JSON.parse(toolCall.function.arguments);
189
+ }
190
+ catch {
191
+ return { raw: toolCall.function.arguments };
192
+ }
193
+ }
194
+ return toolCall.function.arguments;
195
+ }
196
+ createSignature(toolName, args) {
197
+ // For path-tracked tools, use path as primary key
198
+ if (PATH_TRACKED_TOOLS.has(toolName)) {
199
+ const path = this.extractFilePath(args);
200
+ if (path) {
201
+ // For editors, include edit content hash to distinguish different edits
202
+ if (toolName === 'str_replace_editor') {
203
+ const oldStr = typeof args.old_str === 'string' ? args.old_str : '';
204
+ const contentKey = this.hashString(oldStr.substring(0, 200));
205
+ return `${toolName}:${path}:${contentKey}`;
206
+ }
207
+ return `${toolName}:${path}`;
208
+ }
209
+ }
210
+ // For bash, use command as key
211
+ if (toolName === 'bash' || toolName === 'execute_bash') {
212
+ const cmd = typeof args.command === 'string'
213
+ ? args.command.trim().replace(/\s+/g, ' ')
214
+ : '';
215
+ return `${toolName}:${cmd}`;
216
+ }
217
+ // For search, use query as key
218
+ if (toolName === 'search' || toolName === 'search_files') {
219
+ const query = typeof args.query === 'string'
220
+ ? args.query.trim().toLowerCase()
221
+ : '';
222
+ return `${toolName}:${query}`;
223
+ }
224
+ // Default: tool name + stable hash of args
225
+ return `${toolName}:${this.hashString(JSON.stringify(args))}`;
226
+ }
227
+ extractFilePath(args) {
228
+ // Try common path argument names
229
+ for (const key of ['path', 'file_path', 'filepath', 'file']) {
230
+ if (typeof args[key] === 'string') {
231
+ return args[key];
232
+ }
233
+ }
234
+ return undefined;
235
+ }
236
+ getThreshold(toolName, _signature) {
237
+ // Use tool-specific threshold or default
238
+ return TOOL_THRESHOLDS[toolName] || TOOL_THRESHOLDS.default;
239
+ }
240
+ adjustThresholdForFailures(toolName, baseThreshold, failureCount) {
241
+ // For failure-sensitive tools, reduce threshold based on consecutive failures
242
+ if (FAILURE_SENSITIVE_TOOLS.has(toolName) && failureCount > 0) {
243
+ // Each failure reduces threshold by 1, minimum of 2
244
+ return Math.max(2, baseThreshold - failureCount);
245
+ }
246
+ return baseThreshold;
247
+ }
248
+ detectCycle(currentSignature) {
249
+ // Need at least 4 items for a 2-element cycle (A-B-A-B)
250
+ if (this.recentSequence.length < 4) {
251
+ return { isLoop: false, count: 0, threshold: Infinity };
252
+ }
253
+ // Check for 2-element cycles (A-B-A-B pattern)
254
+ const len = this.recentSequence.length;
255
+ const last4 = [...this.recentSequence.slice(-3), currentSignature];
256
+ if (last4[0] === last4[2] && last4[1] === last4[3]) {
257
+ // Check if this pattern has repeated 3+ times
258
+ let patternCount = 1;
259
+ for (let i = len - 4; i >= 1; i -= 2) {
260
+ if (this.recentSequence[i] === last4[1] && this.recentSequence[i - 1] === last4[0]) {
261
+ patternCount++;
262
+ }
263
+ else {
264
+ break;
265
+ }
266
+ }
267
+ if (patternCount >= 3) {
268
+ return {
269
+ isLoop: true,
270
+ reason: `Detected repeating cycle pattern (${patternCount} repetitions)`,
271
+ suggestion: 'The same sequence of operations is repeating. Try a different approach.',
272
+ count: patternCount,
273
+ threshold: 3,
274
+ };
275
+ }
276
+ }
277
+ return { isLoop: false, count: 0, threshold: Infinity };
278
+ }
279
+ getSuggestion(toolName, _args) {
280
+ switch (toolName) {
281
+ case 'str_replace_editor':
282
+ return 'The edit may be failing repeatedly. Check if the old_str matches exactly (including whitespace).';
283
+ case 'bash':
284
+ case 'execute_bash':
285
+ return 'The command may be failing. Check the error output and try a different approach.';
286
+ case 'view_file':
287
+ case 'read_file':
288
+ return 'Consider if you need to read this file again, or if the information is already available.';
289
+ case 'search':
290
+ case 'search_files':
291
+ return 'Try a different search query or search in a different location.';
292
+ default:
293
+ return 'Try a different approach to accomplish your goal.';
294
+ }
295
+ }
296
+ hashString(str) {
297
+ // Simple hash for signature creation
298
+ let hash = 0;
299
+ for (let i = 0; i < str.length; i++) {
300
+ const char = str.charCodeAt(i);
301
+ hash = ((hash << 5) - hash) + char;
302
+ hash = hash & hash; // Convert to 32bit integer
303
+ }
304
+ return hash.toString(16);
305
+ }
306
+ cleanup() {
307
+ // Remove old history entries
308
+ if (this.callHistory.length > this.maxHistorySize) {
309
+ const removeCount = this.callHistory.length - this.maxHistorySize + 20;
310
+ this.callHistory.splice(0, removeCount);
311
+ }
312
+ // Clean up signature counts for signatures not in recent history
313
+ if (this.signatureCounts.size > this.maxHistorySize * 2) {
314
+ const recentSignatures = new Set(this.callHistory.slice(-this.maxHistorySize).map(r => r.signature));
315
+ for (const sig of this.signatureCounts.keys()) {
316
+ if (!recentSignatures.has(sig)) {
317
+ this.signatureCounts.delete(sig);
318
+ this.failureCounts.delete(sig);
319
+ }
320
+ }
321
+ }
322
+ }
323
+ }
324
+ /**
325
+ * Singleton instance
326
+ */
327
+ let loopDetectorInstance = null;
328
+ export function getLoopDetector() {
329
+ if (!loopDetectorInstance) {
330
+ loopDetectorInstance = new LoopDetector();
331
+ }
332
+ return loopDetectorInstance;
333
+ }
334
+ export function resetLoopDetector() {
335
+ if (loopDetectorInstance) {
336
+ loopDetectorInstance.reset();
337
+ }
338
+ }
339
+ //# sourceMappingURL=loop-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-detector.js","sourceRoot":"","sources":["../../src/agent/loop-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Progress-Based Detection System (Phase 2)
3
+ *
4
+ * Tracks state changes to detect actual progress vs. loops.
5
+ * Key insight: The problem isn't repeated tool calls, it's repeated tool calls
6
+ * that don't make progress.
7
+ *
8
+ * Progress Indicators:
9
+ * - File operations: Content changed (hash comparison)
10
+ * - Bash commands: Exit code 0 vs non-zero
11
+ * - Edits: File actually modified
12
+ */
13
+ /**
14
+ * File state snapshot for comparison
15
+ */
16
+ interface FileState {
17
+ path: string;
18
+ hash: string;
19
+ size: number;
20
+ mtime: number;
21
+ exists: boolean;
22
+ }
23
+ /**
24
+ * Progress result for a tool execution
25
+ */
26
+ export interface ProgressResult {
27
+ madeProgress: boolean;
28
+ reason: string;
29
+ stateChange?: {
30
+ before: FileState | null;
31
+ after: FileState | null;
32
+ };
33
+ }
34
+ /**
35
+ * Bash execution result for progress tracking
36
+ */
37
+ export interface BashProgressResult {
38
+ madeProgress: boolean;
39
+ reason: string;
40
+ exitCode: number;
41
+ outputChanged: boolean;
42
+ }
43
+ /**
44
+ * Progress tracking for all tool operations
45
+ */
46
+ export declare class ProgressTracker {
47
+ /** Cache of file states (path -> state) */
48
+ private fileStateCache;
49
+ /** Cache of bash output hashes (command -> hash) */
50
+ private bashOutputCache;
51
+ /** Recent file modifications for pattern detection */
52
+ private recentFileOps;
53
+ /** Maximum recent operations to track */
54
+ private maxRecentOps;
55
+ /**
56
+ * Capture file state before an operation
57
+ */
58
+ captureFileState(path: string): Promise<FileState | null>;
59
+ /**
60
+ * Check if a file operation made progress
61
+ * Call this AFTER the operation completes
62
+ */
63
+ checkFileProgress(path: string, operation: 'create' | 'edit' | 'view', success: boolean): Promise<ProgressResult>;
64
+ /**
65
+ * Check if a bash command made progress
66
+ */
67
+ checkBashProgress(command: string, exitCode: number, output: string): BashProgressResult;
68
+ /**
69
+ * Detect if we're stuck in a file operation loop
70
+ * Returns true if the same file is being operated on repeatedly without progress
71
+ */
72
+ detectFileOpLoop(path: string): {
73
+ isLoop: boolean;
74
+ reason?: string;
75
+ suggestion?: string;
76
+ };
77
+ /**
78
+ * Get progress statistics
79
+ */
80
+ getStats(): {
81
+ trackedFiles: number;
82
+ trackedCommands: number;
83
+ recentOperations: number;
84
+ };
85
+ /**
86
+ * Reset all tracking
87
+ */
88
+ reset(): void;
89
+ private hashContent;
90
+ private recordFileOp;
91
+ }
92
+ export declare function getProgressTracker(): ProgressTracker;
93
+ export declare function resetProgressTracker(): void;
94
+ export {};
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Progress-Based Detection System (Phase 2)
3
+ *
4
+ * Tracks state changes to detect actual progress vs. loops.
5
+ * Key insight: The problem isn't repeated tool calls, it's repeated tool calls
6
+ * that don't make progress.
7
+ *
8
+ * Progress Indicators:
9
+ * - File operations: Content changed (hash comparison)
10
+ * - Bash commands: Exit code 0 vs non-zero
11
+ * - Edits: File actually modified
12
+ */
13
+ import { createHash } from 'crypto';
14
+ import { readFile, stat } from 'fs/promises';
15
+ /**
16
+ * Progress tracking for all tool operations
17
+ */
18
+ export class ProgressTracker {
19
+ /** Cache of file states (path -> state) */
20
+ fileStateCache = new Map();
21
+ /** Cache of bash output hashes (command -> hash) */
22
+ bashOutputCache = new Map();
23
+ /** Recent file modifications for pattern detection */
24
+ recentFileOps = [];
25
+ /** Maximum recent operations to track */
26
+ maxRecentOps = 50;
27
+ /**
28
+ * Capture file state before an operation
29
+ */
30
+ async captureFileState(path) {
31
+ try {
32
+ const stats = await stat(path);
33
+ const content = await readFile(path);
34
+ const hash = this.hashContent(content);
35
+ const state = {
36
+ path,
37
+ hash,
38
+ size: stats.size,
39
+ mtime: stats.mtimeMs,
40
+ exists: true,
41
+ };
42
+ // Cache for later comparison
43
+ this.fileStateCache.set(path, state);
44
+ return state;
45
+ }
46
+ catch (error) {
47
+ // File doesn't exist
48
+ return {
49
+ path,
50
+ hash: '',
51
+ size: 0,
52
+ mtime: 0,
53
+ exists: false,
54
+ };
55
+ }
56
+ }
57
+ /**
58
+ * Check if a file operation made progress
59
+ * Call this AFTER the operation completes
60
+ */
61
+ async checkFileProgress(path, operation, success) {
62
+ const beforeState = this.fileStateCache.get(path) || null;
63
+ // Record this operation
64
+ this.recordFileOp(path, operation, success);
65
+ if (!success) {
66
+ return {
67
+ madeProgress: false,
68
+ reason: `Operation failed on ${path}`,
69
+ stateChange: { before: beforeState, after: null },
70
+ };
71
+ }
72
+ // For view operations, always count as progress (reading info)
73
+ if (operation === 'view') {
74
+ return {
75
+ madeProgress: true,
76
+ reason: 'File content retrieved',
77
+ };
78
+ }
79
+ // For create/edit operations, check if file state changed
80
+ const afterState = await this.captureFileState(path);
81
+ // File was created
82
+ if (!beforeState?.exists && afterState?.exists) {
83
+ return {
84
+ madeProgress: true,
85
+ reason: 'File created',
86
+ stateChange: { before: beforeState, after: afterState },
87
+ };
88
+ }
89
+ // File content changed
90
+ if (beforeState?.hash !== afterState?.hash) {
91
+ return {
92
+ madeProgress: true,
93
+ reason: 'File content modified',
94
+ stateChange: { before: beforeState, after: afterState },
95
+ };
96
+ }
97
+ // No change detected
98
+ return {
99
+ madeProgress: false,
100
+ reason: 'File content unchanged (operation had no effect)',
101
+ stateChange: { before: beforeState, after: afterState },
102
+ };
103
+ }
104
+ /**
105
+ * Check if a bash command made progress
106
+ */
107
+ checkBashProgress(command, exitCode, output) {
108
+ const outputHash = this.hashContent(Buffer.from(output));
109
+ const previousHash = this.bashOutputCache.get(command);
110
+ // Update cache
111
+ this.bashOutputCache.set(command, outputHash);
112
+ // Cleanup old cache entries
113
+ if (this.bashOutputCache.size > 100) {
114
+ const keys = Array.from(this.bashOutputCache.keys()).slice(0, 50);
115
+ for (const key of keys) {
116
+ this.bashOutputCache.delete(key);
117
+ }
118
+ }
119
+ // Exit code 0 = success
120
+ if (exitCode === 0) {
121
+ return {
122
+ madeProgress: true,
123
+ reason: 'Command executed successfully',
124
+ exitCode,
125
+ outputChanged: previousHash !== outputHash,
126
+ };
127
+ }
128
+ // Non-zero exit code
129
+ const outputChanged = previousHash !== outputHash;
130
+ return {
131
+ madeProgress: outputChanged,
132
+ reason: outputChanged
133
+ ? 'Command failed but produced different output'
134
+ : 'Command failed with same output (likely stuck)',
135
+ exitCode,
136
+ outputChanged,
137
+ };
138
+ }
139
+ /**
140
+ * Detect if we're stuck in a file operation loop
141
+ * Returns true if the same file is being operated on repeatedly without progress
142
+ */
143
+ detectFileOpLoop(path) {
144
+ const recentOpsForPath = this.recentFileOps.filter((op) => op.path === path && Date.now() - op.timestamp < 60000 // Last minute
145
+ );
146
+ // Check for repeated failures
147
+ const failures = recentOpsForPath.filter((op) => !op.success);
148
+ if (failures.length >= 3) {
149
+ return {
150
+ isLoop: true,
151
+ reason: `File "${path}" has failed ${failures.length} times in the last minute`,
152
+ suggestion: 'Check if the file exists and has correct permissions',
153
+ };
154
+ }
155
+ // Check for excessive operations on same file
156
+ if (recentOpsForPath.length >= 5) {
157
+ const edits = recentOpsForPath.filter((op) => op.operation === 'edit');
158
+ if (edits.length >= 4) {
159
+ return {
160
+ isLoop: true,
161
+ reason: `File "${path}" edited ${edits.length} times in rapid succession`,
162
+ suggestion: 'Consider if the edits are achieving the intended changes',
163
+ };
164
+ }
165
+ }
166
+ return { isLoop: false };
167
+ }
168
+ /**
169
+ * Get progress statistics
170
+ */
171
+ getStats() {
172
+ return {
173
+ trackedFiles: this.fileStateCache.size,
174
+ trackedCommands: this.bashOutputCache.size,
175
+ recentOperations: this.recentFileOps.length,
176
+ };
177
+ }
178
+ /**
179
+ * Reset all tracking
180
+ */
181
+ reset() {
182
+ this.fileStateCache.clear();
183
+ this.bashOutputCache.clear();
184
+ this.recentFileOps = [];
185
+ }
186
+ // ============================================================================
187
+ // Private Methods
188
+ // ============================================================================
189
+ hashContent(content) {
190
+ return createHash('md5')
191
+ .update(typeof content === 'string' ? content : content)
192
+ .digest('hex');
193
+ }
194
+ recordFileOp(path, operation, success) {
195
+ this.recentFileOps.push({
196
+ path,
197
+ operation,
198
+ timestamp: Date.now(),
199
+ success,
200
+ });
201
+ // Cleanup old entries
202
+ if (this.recentFileOps.length > this.maxRecentOps) {
203
+ this.recentFileOps = this.recentFileOps.slice(-this.maxRecentOps);
204
+ }
205
+ }
206
+ }
207
+ /**
208
+ * Singleton instance
209
+ */
210
+ let progressTrackerInstance = null;
211
+ export function getProgressTracker() {
212
+ if (!progressTrackerInstance) {
213
+ progressTrackerInstance = new ProgressTracker();
214
+ }
215
+ return progressTrackerInstance;
216
+ }
217
+ export function resetProgressTracker() {
218
+ if (progressTrackerInstance) {
219
+ progressTrackerInstance.reset();
220
+ }
221
+ }
222
+ //# sourceMappingURL=progress-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-tracker.js","sourceRoot":"","sources":["../../src/agent/progress-tracker.ts"],"names":[],"mappings":"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"}