@agent-relay/wrapper 0.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.
- package/dist/__fixtures__/claude-outputs.d.ts +49 -0
- package/dist/__fixtures__/claude-outputs.d.ts.map +1 -0
- package/dist/__fixtures__/claude-outputs.js +443 -0
- package/dist/__fixtures__/claude-outputs.js.map +1 -0
- package/dist/__fixtures__/codex-outputs.d.ts +9 -0
- package/dist/__fixtures__/codex-outputs.d.ts.map +1 -0
- package/dist/__fixtures__/codex-outputs.js +94 -0
- package/dist/__fixtures__/codex-outputs.js.map +1 -0
- package/dist/__fixtures__/gemini-outputs.d.ts +19 -0
- package/dist/__fixtures__/gemini-outputs.d.ts.map +1 -0
- package/dist/__fixtures__/gemini-outputs.js +144 -0
- package/dist/__fixtures__/gemini-outputs.js.map +1 -0
- package/dist/__fixtures__/index.d.ts +68 -0
- package/dist/__fixtures__/index.d.ts.map +1 -0
- package/dist/__fixtures__/index.js +44 -0
- package/dist/__fixtures__/index.js.map +1 -0
- package/dist/auth-detection.d.ts +49 -0
- package/dist/auth-detection.d.ts.map +1 -0
- package/dist/auth-detection.js +199 -0
- package/dist/auth-detection.js.map +1 -0
- package/dist/base-wrapper.d.ts +225 -0
- package/dist/base-wrapper.d.ts.map +1 -0
- package/dist/base-wrapper.js +572 -0
- package/dist/base-wrapper.js.map +1 -0
- package/dist/client.d.ts +254 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +801 -0
- package/dist/client.js.map +1 -0
- package/dist/id-generator.d.ts +35 -0
- package/dist/id-generator.d.ts.map +1 -0
- package/dist/id-generator.js +60 -0
- package/dist/id-generator.js.map +1 -0
- package/dist/idle-detector.d.ts +110 -0
- package/dist/idle-detector.d.ts.map +1 -0
- package/dist/idle-detector.js +304 -0
- package/dist/idle-detector.js.map +1 -0
- package/dist/inbox.d.ts +37 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +73 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +236 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +1238 -0
- package/dist/parser.js.map +1 -0
- package/dist/prompt-composer.d.ts +67 -0
- package/dist/prompt-composer.d.ts.map +1 -0
- package/dist/prompt-composer.js +168 -0
- package/dist/prompt-composer.js.map +1 -0
- package/dist/relay-pty-orchestrator.d.ts +407 -0
- package/dist/relay-pty-orchestrator.d.ts.map +1 -0
- package/dist/relay-pty-orchestrator.js +1885 -0
- package/dist/relay-pty-orchestrator.js.map +1 -0
- package/dist/shared.d.ts +201 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +341 -0
- package/dist/shared.js.map +1 -0
- package/dist/stuck-detector.d.ts +161 -0
- package/dist/stuck-detector.d.ts.map +1 -0
- package/dist/stuck-detector.js +402 -0
- package/dist/stuck-detector.js.map +1 -0
- package/dist/tmux-resolver.d.ts +55 -0
- package/dist/tmux-resolver.d.ts.map +1 -0
- package/dist/tmux-resolver.js +175 -0
- package/dist/tmux-resolver.js.map +1 -0
- package/dist/tmux-wrapper.d.ts +345 -0
- package/dist/tmux-wrapper.d.ts.map +1 -0
- package/dist/tmux-wrapper.js +1747 -0
- package/dist/tmux-wrapper.js.map +1 -0
- package/dist/trajectory-integration.d.ts +292 -0
- package/dist/trajectory-integration.d.ts.map +1 -0
- package/dist/trajectory-integration.js +979 -0
- package/dist/trajectory-integration.js.map +1 -0
- package/dist/wrapper-types.d.ts +41 -0
- package/dist/wrapper-types.d.ts.map +1 -0
- package/dist/wrapper-types.js +7 -0
- package/dist/wrapper-types.js.map +1 -0
- package/package.json +63 -0
- package/src/__fixtures__/claude-outputs.ts +471 -0
- package/src/__fixtures__/codex-outputs.ts +99 -0
- package/src/__fixtures__/gemini-outputs.ts +151 -0
- package/src/__fixtures__/index.ts +47 -0
- package/src/auth-detection.ts +244 -0
- package/src/base-wrapper.test.ts +540 -0
- package/src/base-wrapper.ts +741 -0
- package/src/client.test.ts +262 -0
- package/src/client.ts +984 -0
- package/src/id-generator.test.ts +71 -0
- package/src/id-generator.ts +69 -0
- package/src/idle-detector.test.ts +390 -0
- package/src/idle-detector.ts +370 -0
- package/src/inbox.test.ts +233 -0
- package/src/inbox.ts +89 -0
- package/src/index.ts +170 -0
- package/src/parser.regression.test.ts +251 -0
- package/src/parser.test.ts +1359 -0
- package/src/parser.ts +1477 -0
- package/src/prompt-composer.test.ts +219 -0
- package/src/prompt-composer.ts +231 -0
- package/src/relay-pty-orchestrator.test.ts +1027 -0
- package/src/relay-pty-orchestrator.ts +2270 -0
- package/src/shared.test.ts +221 -0
- package/src/shared.ts +454 -0
- package/src/stuck-detector.test.ts +303 -0
- package/src/stuck-detector.ts +511 -0
- package/src/tmux-resolver.test.ts +104 -0
- package/src/tmux-resolver.ts +207 -0
- package/src/tmux-wrapper.test.ts +316 -0
- package/src/tmux-wrapper.ts +2010 -0
- package/src/trajectory-detection.test.ts +151 -0
- package/src/trajectory-integration.ts +1261 -0
- package/src/wrapper-types.ts +45 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StuckDetector - Detect when an agent is stuck
|
|
3
|
+
*
|
|
4
|
+
* Implements agent-relay-501: Stuck detection heuristics
|
|
5
|
+
*
|
|
6
|
+
* Detects five stuck conditions:
|
|
7
|
+
* 1. Extended idle (no output for 10+ minutes)
|
|
8
|
+
* 2. Error loop (same error message repeated 3+ times)
|
|
9
|
+
* 3. Output loop (same output pattern repeated 3+ times)
|
|
10
|
+
* 4. Tool loop (same file operated on 10+ times in 5 minutes)
|
|
11
|
+
* 5. Output flood (abnormally high output rate suggesting infinite loop)
|
|
12
|
+
*
|
|
13
|
+
* NOTE: Message intent detection (agent says "I'll send" but doesn't) was removed
|
|
14
|
+
* because pattern-based NLP detection is unreliable. A protocol-level approach
|
|
15
|
+
* (detecting stale outbox files) should be implemented in relay-pty instead.
|
|
16
|
+
*
|
|
17
|
+
* Emits 'stuck' event when detected, with reason and details.
|
|
18
|
+
*/
|
|
19
|
+
import { EventEmitter } from 'node:events';
|
|
20
|
+
const DEFAULT_CONFIG = {
|
|
21
|
+
extendedIdleMs: 10 * 60 * 1000, // 10 minutes
|
|
22
|
+
loopThreshold: 3,
|
|
23
|
+
checkIntervalMs: 30 * 1000, // 30 seconds
|
|
24
|
+
minLoopLength: 20, // Minimum chars to consider a meaningful loop
|
|
25
|
+
errorPatterns: [
|
|
26
|
+
/error:/i,
|
|
27
|
+
/failed:/i,
|
|
28
|
+
/exception:/i,
|
|
29
|
+
/timeout/i,
|
|
30
|
+
/connection refused/i,
|
|
31
|
+
/permission denied/i,
|
|
32
|
+
/command not found/i,
|
|
33
|
+
/no such file/i,
|
|
34
|
+
],
|
|
35
|
+
toolLoopThreshold: 10, // Same file operated on 10+ times = likely stuck
|
|
36
|
+
toolLoopWindowMs: 5 * 60 * 1000, // 5 minute window for tool loop detection
|
|
37
|
+
outputFloodLinesPerMinute: 5000, // 5000+ lines/min is abnormal
|
|
38
|
+
outputFloodMinDurationMs: 2 * 60 * 1000, // Wait 2 minutes before flood detection
|
|
39
|
+
};
|
|
40
|
+
/** Patterns to extract tool invocations from Claude Code output */
|
|
41
|
+
const TOOL_PATTERNS = [
|
|
42
|
+
// Claude Code tool patterns: ⏺ Write(path), ⏺ Read(path), etc.
|
|
43
|
+
/[⏺●]\s*(Read|Write|Edit|Glob|Grep|Bash)\(([^)]+)\)/g,
|
|
44
|
+
// Alternative patterns without symbols
|
|
45
|
+
/\b(Read|Write|Edit)\s*\(\s*([^)]+)\s*\)/g,
|
|
46
|
+
];
|
|
47
|
+
export class StuckDetector extends EventEmitter {
|
|
48
|
+
config;
|
|
49
|
+
lastOutputTime = Date.now();
|
|
50
|
+
recentOutputs = [];
|
|
51
|
+
checkInterval = null;
|
|
52
|
+
isStuck = false;
|
|
53
|
+
stuckReason = null;
|
|
54
|
+
// Tool loop detection
|
|
55
|
+
toolInvocations = [];
|
|
56
|
+
// Output flood detection
|
|
57
|
+
outputLineCount = 0;
|
|
58
|
+
outputStartTime = Date.now();
|
|
59
|
+
constructor(config = {}) {
|
|
60
|
+
super();
|
|
61
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Start monitoring for stuck conditions.
|
|
65
|
+
* Call this after the agent process starts.
|
|
66
|
+
*/
|
|
67
|
+
start() {
|
|
68
|
+
this.stop(); // Clear any existing interval
|
|
69
|
+
this.isStuck = false;
|
|
70
|
+
this.stuckReason = null;
|
|
71
|
+
this.lastOutputTime = Date.now();
|
|
72
|
+
this.recentOutputs = [];
|
|
73
|
+
this.toolInvocations = [];
|
|
74
|
+
this.outputLineCount = 0;
|
|
75
|
+
this.outputStartTime = Date.now();
|
|
76
|
+
this.checkInterval = setInterval(() => {
|
|
77
|
+
this.checkStuck();
|
|
78
|
+
}, this.config.checkIntervalMs);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Stop monitoring.
|
|
82
|
+
*/
|
|
83
|
+
stop() {
|
|
84
|
+
if (this.checkInterval) {
|
|
85
|
+
clearInterval(this.checkInterval);
|
|
86
|
+
this.checkInterval = null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Feed output to the detector.
|
|
91
|
+
* Call this for every output chunk from the agent.
|
|
92
|
+
*/
|
|
93
|
+
onOutput(chunk) {
|
|
94
|
+
this.lastOutputTime = Date.now();
|
|
95
|
+
// Track output volume (count newlines)
|
|
96
|
+
const lineCount = (chunk.match(/\n/g) || []).length;
|
|
97
|
+
this.outputLineCount += lineCount;
|
|
98
|
+
// Extract and track tool invocations
|
|
99
|
+
this.extractToolInvocations(chunk);
|
|
100
|
+
// Normalize and store recent output
|
|
101
|
+
const normalized = this.normalizeOutput(chunk);
|
|
102
|
+
if (normalized.length >= this.config.minLoopLength) {
|
|
103
|
+
this.recentOutputs.push(normalized);
|
|
104
|
+
// Keep only recent outputs (5x threshold for pattern detection)
|
|
105
|
+
const maxOutputs = this.config.loopThreshold * 5;
|
|
106
|
+
if (this.recentOutputs.length > maxOutputs) {
|
|
107
|
+
this.recentOutputs = this.recentOutputs.slice(-maxOutputs);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// If we were stuck, check if we're unstuck now
|
|
111
|
+
if (this.isStuck) {
|
|
112
|
+
this.isStuck = false;
|
|
113
|
+
this.stuckReason = null;
|
|
114
|
+
this.emit('unstuck', { timestamp: Date.now() });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Extract tool invocations from output and track them.
|
|
119
|
+
*/
|
|
120
|
+
extractToolInvocations(chunk) {
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
// Strip ANSI codes for cleaner matching
|
|
123
|
+
// eslint-disable-next-line no-control-regex
|
|
124
|
+
const clean = chunk.replace(/\x1B(?:\[[0-9;?]*[A-Za-z]|\].*?(?:\x07|\x1B\\)|[@-Z\\-_])/g, '');
|
|
125
|
+
// Track what we've already matched in this chunk to prevent duplicates
|
|
126
|
+
const matchedInChunk = new Set();
|
|
127
|
+
for (const pattern of TOOL_PATTERNS) {
|
|
128
|
+
// Reset lastIndex for global regex
|
|
129
|
+
pattern.lastIndex = 0;
|
|
130
|
+
let match;
|
|
131
|
+
while ((match = pattern.exec(clean)) !== null) {
|
|
132
|
+
const tool = match[1];
|
|
133
|
+
const target = match[2].trim();
|
|
134
|
+
// Normalize file paths (remove ~ prefix, trailing whitespace)
|
|
135
|
+
const normalizedTarget = target
|
|
136
|
+
.replace(/^~\//, '')
|
|
137
|
+
.replace(/\s+$/, '');
|
|
138
|
+
// Deduplicate within this chunk (multiple patterns may match same invocation)
|
|
139
|
+
const key = `${tool}:${normalizedTarget}`;
|
|
140
|
+
if (matchedInChunk.has(key))
|
|
141
|
+
continue;
|
|
142
|
+
matchedInChunk.add(key);
|
|
143
|
+
this.toolInvocations.push({
|
|
144
|
+
tool,
|
|
145
|
+
target: normalizedTarget,
|
|
146
|
+
timestamp: now,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Prune old invocations outside the window
|
|
151
|
+
const windowStart = now - this.config.toolLoopWindowMs;
|
|
152
|
+
this.toolInvocations = this.toolInvocations.filter(inv => inv.timestamp >= windowStart);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Normalize output for comparison (strip ANSI, trim, lowercase).
|
|
156
|
+
*/
|
|
157
|
+
normalizeOutput(output) {
|
|
158
|
+
// Strip ANSI escape codes
|
|
159
|
+
// eslint-disable-next-line no-control-regex
|
|
160
|
+
let normalized = output.replace(/\x1B(?:\[[0-9;?]*[A-Za-z]|\].*?(?:\x07|\x1B\\)|[@-Z\\-_])/g, '');
|
|
161
|
+
// Normalize whitespace
|
|
162
|
+
normalized = normalized.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
163
|
+
return normalized;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Check for stuck conditions.
|
|
167
|
+
*/
|
|
168
|
+
checkStuck() {
|
|
169
|
+
// Don't re-emit if already stuck
|
|
170
|
+
if (this.isStuck)
|
|
171
|
+
return;
|
|
172
|
+
// Check 1: Extended idle
|
|
173
|
+
const idleDuration = Date.now() - this.lastOutputTime;
|
|
174
|
+
if (idleDuration >= this.config.extendedIdleMs) {
|
|
175
|
+
this.emitStuck({
|
|
176
|
+
reason: 'extended_idle',
|
|
177
|
+
details: `No output for ${Math.round(idleDuration / 60000)} minutes`,
|
|
178
|
+
timestamp: Date.now(),
|
|
179
|
+
idleDurationMs: idleDuration,
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Check 2: Error loop
|
|
184
|
+
const errorLoop = this.detectErrorLoop();
|
|
185
|
+
if (errorLoop) {
|
|
186
|
+
this.emitStuck({
|
|
187
|
+
reason: 'error_loop',
|
|
188
|
+
details: `Same error repeated ${errorLoop.count} times`,
|
|
189
|
+
timestamp: Date.now(),
|
|
190
|
+
repeatedContent: errorLoop.error,
|
|
191
|
+
repetitions: errorLoop.count,
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
// Check 3: Output loop
|
|
196
|
+
const outputLoop = this.detectOutputLoop();
|
|
197
|
+
if (outputLoop) {
|
|
198
|
+
this.emitStuck({
|
|
199
|
+
reason: 'output_loop',
|
|
200
|
+
details: `Same output repeated ${outputLoop.count} times`,
|
|
201
|
+
timestamp: Date.now(),
|
|
202
|
+
repeatedContent: outputLoop.output.substring(0, 100),
|
|
203
|
+
repetitions: outputLoop.count,
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// Check 4: Tool loop (same file operated on repeatedly)
|
|
208
|
+
const toolLoop = this.detectToolLoop();
|
|
209
|
+
if (toolLoop) {
|
|
210
|
+
this.emitStuck({
|
|
211
|
+
reason: 'tool_loop',
|
|
212
|
+
details: `${toolLoop.tool} called on "${toolLoop.target}" ${toolLoop.count} times in ${Math.round(this.config.toolLoopWindowMs / 60000)} minutes`,
|
|
213
|
+
timestamp: Date.now(),
|
|
214
|
+
targetFile: toolLoop.target,
|
|
215
|
+
toolName: toolLoop.tool,
|
|
216
|
+
repetitions: toolLoop.count,
|
|
217
|
+
});
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
// Check 5: Output flood (abnormally high output rate)
|
|
221
|
+
const flood = this.detectOutputFlood();
|
|
222
|
+
if (flood) {
|
|
223
|
+
this.emitStuck({
|
|
224
|
+
reason: 'output_flood',
|
|
225
|
+
details: `Abnormally high output: ${flood.linesPerMinute.toFixed(0)} lines/min over ${Math.round(flood.durationMs / 60000)} minutes`,
|
|
226
|
+
timestamp: Date.now(),
|
|
227
|
+
linesPerMinute: flood.linesPerMinute,
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Detect repeated error messages.
|
|
234
|
+
*/
|
|
235
|
+
detectErrorLoop() {
|
|
236
|
+
const errorOutputs = [];
|
|
237
|
+
for (const output of this.recentOutputs) {
|
|
238
|
+
for (const pattern of this.config.errorPatterns) {
|
|
239
|
+
if (pattern.test(output)) {
|
|
240
|
+
errorOutputs.push(output);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (errorOutputs.length < this.config.loopThreshold) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
// Check if the same error appears repeatedly
|
|
249
|
+
const errorCounts = new Map();
|
|
250
|
+
for (const error of errorOutputs) {
|
|
251
|
+
const count = (errorCounts.get(error) || 0) + 1;
|
|
252
|
+
errorCounts.set(error, count);
|
|
253
|
+
if (count >= this.config.loopThreshold) {
|
|
254
|
+
return { error, count };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Detect repeated output patterns (not necessarily errors).
|
|
261
|
+
*/
|
|
262
|
+
detectOutputLoop() {
|
|
263
|
+
if (this.recentOutputs.length < this.config.loopThreshold) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
// Check for identical consecutive outputs
|
|
267
|
+
const outputCounts = new Map();
|
|
268
|
+
for (const output of this.recentOutputs) {
|
|
269
|
+
const count = (outputCounts.get(output) || 0) + 1;
|
|
270
|
+
outputCounts.set(output, count);
|
|
271
|
+
if (count >= this.config.loopThreshold) {
|
|
272
|
+
return { output, count };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Detect when the same file is being operated on repeatedly.
|
|
279
|
+
* This catches cases like an agent repeatedly reading/writing the same file
|
|
280
|
+
* in a loop, even if the output content differs each time.
|
|
281
|
+
*/
|
|
282
|
+
detectToolLoop() {
|
|
283
|
+
if (this.toolInvocations.length < this.config.toolLoopThreshold) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
// Count operations per file (combining all tool types)
|
|
287
|
+
const fileCounts = new Map();
|
|
288
|
+
for (const inv of this.toolInvocations) {
|
|
289
|
+
const existing = fileCounts.get(inv.target);
|
|
290
|
+
if (existing) {
|
|
291
|
+
existing.count++;
|
|
292
|
+
existing.tools.add(inv.tool);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
fileCounts.set(inv.target, { count: 1, tools: new Set([inv.tool]) });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Find files that exceed the threshold
|
|
299
|
+
for (const [target, data] of Array.from(fileCounts.entries())) {
|
|
300
|
+
if (data.count >= this.config.toolLoopThreshold) {
|
|
301
|
+
// Report the most common tool used on this file
|
|
302
|
+
const toolCounts = new Map();
|
|
303
|
+
for (const inv of this.toolInvocations) {
|
|
304
|
+
if (inv.target === target) {
|
|
305
|
+
toolCounts.set(inv.tool, (toolCounts.get(inv.tool) || 0) + 1);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
let maxTool = 'Unknown';
|
|
309
|
+
let maxCount = 0;
|
|
310
|
+
for (const [tool, toolCount] of Array.from(toolCounts.entries())) {
|
|
311
|
+
if (toolCount > maxCount) {
|
|
312
|
+
maxTool = tool;
|
|
313
|
+
maxCount = toolCount;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return { tool: maxTool, target, count: data.count };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Detect abnormally high output rates that suggest an infinite loop.
|
|
323
|
+
* Only triggers after minimum duration to avoid false positives during
|
|
324
|
+
* normal high-output operations (like builds or tests).
|
|
325
|
+
*/
|
|
326
|
+
detectOutputFlood() {
|
|
327
|
+
const durationMs = Date.now() - this.outputStartTime;
|
|
328
|
+
// Don't check until minimum duration has passed
|
|
329
|
+
if (durationMs < this.config.outputFloodMinDurationMs) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
const durationMinutes = durationMs / 60000;
|
|
333
|
+
const linesPerMinute = this.outputLineCount / durationMinutes;
|
|
334
|
+
if (linesPerMinute >= this.config.outputFloodLinesPerMinute) {
|
|
335
|
+
return { linesPerMinute, durationMs };
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Emit stuck event.
|
|
341
|
+
*/
|
|
342
|
+
emitStuck(event) {
|
|
343
|
+
this.isStuck = true;
|
|
344
|
+
this.stuckReason = event.reason;
|
|
345
|
+
this.emit('stuck', event);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Check if currently detected as stuck.
|
|
349
|
+
*/
|
|
350
|
+
getIsStuck() {
|
|
351
|
+
return this.isStuck;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Get the reason for being stuck (if stuck).
|
|
355
|
+
*/
|
|
356
|
+
getStuckReason() {
|
|
357
|
+
return this.stuckReason;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get time since last output in milliseconds.
|
|
361
|
+
*/
|
|
362
|
+
getIdleDuration() {
|
|
363
|
+
return Date.now() - this.lastOutputTime;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Reset state.
|
|
367
|
+
*/
|
|
368
|
+
reset() {
|
|
369
|
+
this.isStuck = false;
|
|
370
|
+
this.stuckReason = null;
|
|
371
|
+
this.lastOutputTime = Date.now();
|
|
372
|
+
this.recentOutputs = [];
|
|
373
|
+
this.toolInvocations = [];
|
|
374
|
+
this.outputLineCount = 0;
|
|
375
|
+
this.outputStartTime = Date.now();
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get current output statistics (useful for debugging).
|
|
379
|
+
*/
|
|
380
|
+
getOutputStats() {
|
|
381
|
+
const durationMs = Date.now() - this.outputStartTime;
|
|
382
|
+
const durationMinutes = Math.max(durationMs / 60000, 0.001); // Avoid division by zero
|
|
383
|
+
return {
|
|
384
|
+
lineCount: this.outputLineCount,
|
|
385
|
+
durationMs,
|
|
386
|
+
linesPerMinute: this.outputLineCount / durationMinutes,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Get recent tool invocations (useful for debugging).
|
|
391
|
+
*/
|
|
392
|
+
getToolInvocations() {
|
|
393
|
+
return [...this.toolInvocations];
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Create a stuck detector with default configuration.
|
|
398
|
+
*/
|
|
399
|
+
export function createStuckDetector(config = {}) {
|
|
400
|
+
return new StuckDetector(config);
|
|
401
|
+
}
|
|
402
|
+
//# sourceMappingURL=stuck-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stuck-detector.js","sourceRoot":"","sources":["../src/stuck-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tmux Binary Resolver
|
|
3
|
+
*
|
|
4
|
+
* Locates tmux binary with fallback to bundled version.
|
|
5
|
+
* Priority:
|
|
6
|
+
* 1. System tmux (in PATH)
|
|
7
|
+
* 2. Bundled tmux within the agent-relay package (bin/tmux)
|
|
8
|
+
*/
|
|
9
|
+
/** Path where bundled tmux binary is installed (within the package) */
|
|
10
|
+
export declare function getBundledTmuxDir(): string;
|
|
11
|
+
export declare function getBundledTmuxPath(): string;
|
|
12
|
+
export declare const BUNDLED_TMUX_DIR: string;
|
|
13
|
+
export declare const BUNDLED_TMUX_PATH: string;
|
|
14
|
+
/** Minimum supported tmux version */
|
|
15
|
+
export declare const MIN_TMUX_VERSION = "3.0";
|
|
16
|
+
export interface TmuxInfo {
|
|
17
|
+
/** Full path to tmux binary */
|
|
18
|
+
path: string;
|
|
19
|
+
/** Version string (e.g., "3.6a") */
|
|
20
|
+
version: string;
|
|
21
|
+
/** Whether this is the bundled version */
|
|
22
|
+
isBundled: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolve tmux binary path with fallback to bundled version.
|
|
26
|
+
* Returns null if tmux is not available.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveTmux(): TmuxInfo | null;
|
|
29
|
+
/**
|
|
30
|
+
* Get the tmux command to use. Throws if tmux is not available.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getTmuxPath(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Check if tmux is available (either system or bundled)
|
|
35
|
+
*/
|
|
36
|
+
export declare function isTmuxAvailable(): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Get platform identifier for downloading binaries
|
|
39
|
+
*/
|
|
40
|
+
export declare function getPlatformIdentifier(): string | null;
|
|
41
|
+
/**
|
|
42
|
+
* Error thrown when tmux is not available
|
|
43
|
+
*/
|
|
44
|
+
export declare class TmuxNotFoundError extends Error {
|
|
45
|
+
constructor();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if installed tmux version meets minimum requirements
|
|
49
|
+
*/
|
|
50
|
+
export declare function checkTmuxVersion(): {
|
|
51
|
+
ok: boolean;
|
|
52
|
+
version: string | null;
|
|
53
|
+
minimum: string;
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=tmux-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux-resolver.d.ts","sourceRoot":"","sources":["../src/tmux-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAoBH,uEAAuE;AACvE,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAGD,eAAO,MAAM,gBAAgB,QAAsB,CAAC;AACpD,eAAO,MAAM,iBAAiB,QAAuB,CAAC;AAEtD,qCAAqC;AACrC,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC,MAAM,WAAW,QAAQ;IACvB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,SAAS,EAAE,OAAO,CAAC;CACpB;AAkCD;;;GAGG;AACH,wBAAgB,WAAW,IAAI,QAAQ,GAAG,IAAI,CA4B7C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,IAAI,CAYrD;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;;CAoB3C;AAaD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAc3F"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tmux Binary Resolver
|
|
3
|
+
*
|
|
4
|
+
* Locates tmux binary with fallback to bundled version.
|
|
5
|
+
* Priority:
|
|
6
|
+
* 1. System tmux (in PATH)
|
|
7
|
+
* 2. Bundled tmux within the agent-relay package (bin/tmux)
|
|
8
|
+
*/
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import os from 'node:os';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
/**
|
|
17
|
+
* Get the package root directory (where agent-relay is installed)
|
|
18
|
+
* This works whether we're in dist/utils/ or src/utils/
|
|
19
|
+
*/
|
|
20
|
+
function getPackageRoot() {
|
|
21
|
+
// Navigate up from dist/utils or src/utils to package root
|
|
22
|
+
return path.resolve(__dirname, '..', '..');
|
|
23
|
+
}
|
|
24
|
+
/** Path where bundled tmux binary is installed (within the package) */
|
|
25
|
+
export function getBundledTmuxDir() {
|
|
26
|
+
return path.join(getPackageRoot(), 'bin');
|
|
27
|
+
}
|
|
28
|
+
export function getBundledTmuxPath() {
|
|
29
|
+
return path.join(getBundledTmuxDir(), 'tmux');
|
|
30
|
+
}
|
|
31
|
+
// Legacy exports for backwards compatibility
|
|
32
|
+
export const BUNDLED_TMUX_DIR = getBundledTmuxDir();
|
|
33
|
+
export const BUNDLED_TMUX_PATH = getBundledTmuxPath();
|
|
34
|
+
/** Minimum supported tmux version */
|
|
35
|
+
export const MIN_TMUX_VERSION = '3.0';
|
|
36
|
+
/**
|
|
37
|
+
* Check if tmux exists at a given path and get its version
|
|
38
|
+
*/
|
|
39
|
+
function getTmuxVersion(tmuxPath) {
|
|
40
|
+
try {
|
|
41
|
+
const output = execSync(`"${tmuxPath}" -V`, {
|
|
42
|
+
encoding: 'utf-8',
|
|
43
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
44
|
+
});
|
|
45
|
+
// Output format: "tmux 3.6a" or similar
|
|
46
|
+
const match = output.trim().match(/tmux\s+(\d+\.\d+\w?)/i);
|
|
47
|
+
return match ? match[1] : null;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Find tmux in system PATH
|
|
55
|
+
*/
|
|
56
|
+
function findSystemTmux() {
|
|
57
|
+
try {
|
|
58
|
+
const output = execSync('which tmux', {
|
|
59
|
+
encoding: 'utf-8',
|
|
60
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
61
|
+
});
|
|
62
|
+
return output.trim() || null;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve tmux binary path with fallback to bundled version.
|
|
70
|
+
* Returns null if tmux is not available.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveTmux() {
|
|
73
|
+
// 1. Check system tmux first
|
|
74
|
+
const systemPath = findSystemTmux();
|
|
75
|
+
if (systemPath) {
|
|
76
|
+
const version = getTmuxVersion(systemPath);
|
|
77
|
+
if (version) {
|
|
78
|
+
return {
|
|
79
|
+
path: systemPath,
|
|
80
|
+
version,
|
|
81
|
+
isBundled: false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// 2. Check bundled tmux (within the package)
|
|
86
|
+
const bundledPath = getBundledTmuxPath();
|
|
87
|
+
if (fs.existsSync(bundledPath)) {
|
|
88
|
+
const version = getTmuxVersion(bundledPath);
|
|
89
|
+
if (version) {
|
|
90
|
+
return {
|
|
91
|
+
path: bundledPath,
|
|
92
|
+
version,
|
|
93
|
+
isBundled: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get the tmux command to use. Throws if tmux is not available.
|
|
101
|
+
*/
|
|
102
|
+
export function getTmuxPath() {
|
|
103
|
+
const info = resolveTmux();
|
|
104
|
+
if (!info) {
|
|
105
|
+
throw new TmuxNotFoundError();
|
|
106
|
+
}
|
|
107
|
+
return info.path;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check if tmux is available (either system or bundled)
|
|
111
|
+
*/
|
|
112
|
+
export function isTmuxAvailable() {
|
|
113
|
+
return resolveTmux() !== null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get platform identifier for downloading binaries
|
|
117
|
+
*/
|
|
118
|
+
export function getPlatformIdentifier() {
|
|
119
|
+
const platform = os.platform();
|
|
120
|
+
const arch = os.arch();
|
|
121
|
+
if (platform === 'darwin') {
|
|
122
|
+
return arch === 'arm64' ? 'macos-arm64' : 'macos-x86_64';
|
|
123
|
+
}
|
|
124
|
+
else if (platform === 'linux') {
|
|
125
|
+
return arch === 'arm64' ? 'linux-arm64' : 'linux-x86_64';
|
|
126
|
+
}
|
|
127
|
+
// Unsupported platform
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Error thrown when tmux is not available
|
|
132
|
+
*/
|
|
133
|
+
export class TmuxNotFoundError extends Error {
|
|
134
|
+
constructor() {
|
|
135
|
+
const platformInstructions = (() => {
|
|
136
|
+
switch (os.platform()) {
|
|
137
|
+
case 'darwin':
|
|
138
|
+
return ' macOS: brew install tmux';
|
|
139
|
+
case 'linux':
|
|
140
|
+
return ' Ubuntu/Debian: sudo apt install tmux\n Fedora: sudo dnf install tmux\n Arch: sudo pacman -S tmux';
|
|
141
|
+
case 'win32':
|
|
142
|
+
return ' Windows: tmux requires WSL (Windows Subsystem for Linux)\n Install WSL, then: sudo apt install tmux';
|
|
143
|
+
default:
|
|
144
|
+
return ' See: https://github.com/tmux/tmux/wiki/Installing';
|
|
145
|
+
}
|
|
146
|
+
})();
|
|
147
|
+
super(`tmux is required but not found.\n\nInstall tmux:\n${platformInstructions}\n\nThen reinstall agent-relay: npm install agent-relay`);
|
|
148
|
+
this.name = 'TmuxNotFoundError';
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Parse version string to compare versions
|
|
153
|
+
*/
|
|
154
|
+
function parseVersion(version) {
|
|
155
|
+
const match = version.match(/(\d+)\.(\d+)/);
|
|
156
|
+
if (!match) {
|
|
157
|
+
return { major: 0, minor: 0 };
|
|
158
|
+
}
|
|
159
|
+
return { major: parseInt(match[1], 10), minor: parseInt(match[2], 10) };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if installed tmux version meets minimum requirements
|
|
163
|
+
*/
|
|
164
|
+
export function checkTmuxVersion() {
|
|
165
|
+
const info = resolveTmux();
|
|
166
|
+
if (!info) {
|
|
167
|
+
return { ok: false, version: null, minimum: MIN_TMUX_VERSION };
|
|
168
|
+
}
|
|
169
|
+
const installed = parseVersion(info.version);
|
|
170
|
+
const required = parseVersion(MIN_TMUX_VERSION);
|
|
171
|
+
const ok = installed.major > required.major ||
|
|
172
|
+
(installed.major === required.major && installed.minor >= required.minor);
|
|
173
|
+
return { ok, version: info.version, minimum: MIN_TMUX_VERSION };
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=tmux-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux-resolver.js","sourceRoot":"","sources":["../src/tmux-resolver.ts"],"names":[],"mappings":"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"}
|