agentgui 1.0.149 → 1.0.150
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/lib/claude-runner.js +36 -26
- package/package.json +1 -1
package/lib/claude-runner.js
CHANGED
|
@@ -139,7 +139,24 @@ class AgentRunner {
|
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
async runACP(prompt, cwd, config = {}) {
|
|
142
|
+
async runACP(prompt, cwd, config = {}, _retryCount = 0) {
|
|
143
|
+
const maxRetries = config.maxRetries ?? 1;
|
|
144
|
+
try {
|
|
145
|
+
return await this._runACPOnce(prompt, cwd, config);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
const isEmptyExit = err.message && err.message.includes('ACP exited with code');
|
|
148
|
+
const isBinaryError = err.code === 'ENOENT' || (err.message && err.message.includes('ENOENT'));
|
|
149
|
+
if ((isEmptyExit || isBinaryError) && _retryCount < maxRetries) {
|
|
150
|
+
const delay = Math.min(1000 * Math.pow(2, _retryCount), 5000);
|
|
151
|
+
console.error(`[${this.id}] ACP attempt ${_retryCount + 1} failed: ${err.message}. Retrying in ${delay}ms...`);
|
|
152
|
+
await new Promise(r => setTimeout(r, delay));
|
|
153
|
+
return this.runACP(prompt, cwd, config, _retryCount + 1);
|
|
154
|
+
}
|
|
155
|
+
throw err;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async _runACPOnce(prompt, cwd, config = {}) {
|
|
143
160
|
return new Promise((resolve, reject) => {
|
|
144
161
|
const {
|
|
145
162
|
timeout = 300000,
|
|
@@ -147,11 +164,10 @@ class AgentRunner {
|
|
|
147
164
|
onError = null
|
|
148
165
|
} = config;
|
|
149
166
|
|
|
150
|
-
// Use adapter if required (e.g., for Claude Code via Zed adapter)
|
|
151
167
|
const cmd = this.requiresAdapter && this.adapterCommand ? this.adapterCommand : this.command;
|
|
152
168
|
const baseArgs = this.requiresAdapter && this.adapterCommand ? this.adapterArgs : ['acp'];
|
|
153
169
|
const args = [...baseArgs];
|
|
154
|
-
|
|
170
|
+
|
|
155
171
|
const proc = spawn(cmd, args, { cwd });
|
|
156
172
|
|
|
157
173
|
if (config.onPid) {
|
|
@@ -163,6 +179,7 @@ class AgentRunner {
|
|
|
163
179
|
let sessionId = null;
|
|
164
180
|
let requestId = 0;
|
|
165
181
|
let initialized = false;
|
|
182
|
+
let stderrText = '';
|
|
166
183
|
|
|
167
184
|
const timeoutHandle = setTimeout(() => {
|
|
168
185
|
timedOut = true;
|
|
@@ -170,12 +187,9 @@ class AgentRunner {
|
|
|
170
187
|
reject(new Error(`${this.name} ACP timeout after ${timeout}ms`));
|
|
171
188
|
}, timeout);
|
|
172
189
|
|
|
173
|
-
// ACP protocol handler
|
|
174
190
|
const handleMessage = (message) => {
|
|
175
|
-
// Normalize ACP message to common format
|
|
176
191
|
const normalized = this.protocolHandler(message, { sessionId, initialized });
|
|
177
192
|
if (!normalized) {
|
|
178
|
-
// Check for initialization response
|
|
179
193
|
if (message.id === 1 && message.result) {
|
|
180
194
|
initialized = true;
|
|
181
195
|
}
|
|
@@ -217,13 +231,13 @@ class AgentRunner {
|
|
|
217
231
|
|
|
218
232
|
proc.stderr.on('data', (chunk) => {
|
|
219
233
|
const errorText = chunk.toString();
|
|
234
|
+
stderrText += errorText;
|
|
220
235
|
console.error(`[${this.id}] stderr:`, errorText);
|
|
221
236
|
if (onError) {
|
|
222
237
|
try { onError(errorText); } catch (e) {}
|
|
223
238
|
}
|
|
224
239
|
});
|
|
225
240
|
|
|
226
|
-
// Send ACP initialize request (protocolVersion must be an integer per ACP spec)
|
|
227
241
|
const initRequest = {
|
|
228
242
|
jsonrpc: '2.0',
|
|
229
243
|
id: ++requestId,
|
|
@@ -245,12 +259,10 @@ class AgentRunner {
|
|
|
245
259
|
|
|
246
260
|
let sessionCreated = false;
|
|
247
261
|
|
|
248
|
-
// Wait for initialization then create session and send prompt
|
|
249
262
|
const checkInitAndSend = () => {
|
|
250
263
|
if (initialized && !sessionCreated) {
|
|
251
264
|
sessionCreated = true;
|
|
252
|
-
|
|
253
|
-
// Step 1: Create a new session
|
|
265
|
+
|
|
254
266
|
const sessionRequest = {
|
|
255
267
|
jsonrpc: '2.0',
|
|
256
268
|
id: ++requestId,
|
|
@@ -269,14 +281,11 @@ class AgentRunner {
|
|
|
269
281
|
let promptId = null;
|
|
270
282
|
let completed = false;
|
|
271
283
|
|
|
272
|
-
// Handle session creation response and send prompt
|
|
273
284
|
const originalHandler = handleMessage;
|
|
274
285
|
const enhancedHandler = (message) => {
|
|
275
|
-
// Check for session/new response
|
|
276
286
|
if (message.id && message.result && message.result.sessionId) {
|
|
277
287
|
sessionId = message.result.sessionId;
|
|
278
|
-
|
|
279
|
-
// Step 2: Send the prompt
|
|
288
|
+
|
|
280
289
|
promptId = ++requestId;
|
|
281
290
|
const promptRequest = {
|
|
282
291
|
jsonrpc: '2.0',
|
|
@@ -290,8 +299,7 @@ class AgentRunner {
|
|
|
290
299
|
proc.stdin.write(JSON.stringify(promptRequest) + '\n');
|
|
291
300
|
return;
|
|
292
301
|
}
|
|
293
|
-
|
|
294
|
-
// Check for prompt response (end of turn)
|
|
302
|
+
|
|
295
303
|
if (message.id === promptId && message.result && message.result.stopReason) {
|
|
296
304
|
completed = true;
|
|
297
305
|
clearTimeout(timeoutHandle);
|
|
@@ -299,11 +307,10 @@ class AgentRunner {
|
|
|
299
307
|
resolve({ outputs, sessionId });
|
|
300
308
|
return;
|
|
301
309
|
}
|
|
302
|
-
|
|
310
|
+
|
|
303
311
|
originalHandler(message);
|
|
304
312
|
};
|
|
305
313
|
|
|
306
|
-
// Override the message handler
|
|
307
314
|
buffer = '';
|
|
308
315
|
proc.stdout.removeAllListeners('data');
|
|
309
316
|
proc.stdout.on('data', (chunk) => {
|
|
@@ -317,12 +324,11 @@ class AgentRunner {
|
|
|
317
324
|
if (line.trim()) {
|
|
318
325
|
try {
|
|
319
326
|
const message = JSON.parse(line);
|
|
320
|
-
|
|
321
|
-
// Check for initialization response
|
|
327
|
+
|
|
322
328
|
if (message.id === 1 && message.result) {
|
|
323
329
|
initialized = true;
|
|
324
330
|
}
|
|
325
|
-
|
|
331
|
+
|
|
326
332
|
enhancedHandler(message);
|
|
327
333
|
} catch (e) {
|
|
328
334
|
console.error(`[${this.id}] JSON parse error:`, line.substring(0, 100));
|
|
@@ -340,7 +346,8 @@ class AgentRunner {
|
|
|
340
346
|
if (code === 0 || outputs.length > 0) {
|
|
341
347
|
resolve({ outputs, sessionId });
|
|
342
348
|
} else {
|
|
343
|
-
|
|
349
|
+
const detail = stderrText ? `: ${stderrText.substring(0, 200)}` : '';
|
|
350
|
+
reject(new Error(`${this.name} ACP exited with code ${code}${detail}`));
|
|
344
351
|
}
|
|
345
352
|
});
|
|
346
353
|
|
|
@@ -386,12 +393,15 @@ class AgentRegistry {
|
|
|
386
393
|
}
|
|
387
394
|
|
|
388
395
|
listACPAvailable() {
|
|
389
|
-
|
|
390
|
-
const { execSync } = require('child_process');
|
|
396
|
+
const { spawnSync } = require('child_process');
|
|
391
397
|
return this.list().filter(agent => {
|
|
392
398
|
try {
|
|
393
|
-
|
|
394
|
-
return
|
|
399
|
+
const which = spawnSync('which', [agent.command], { encoding: 'utf-8', timeout: 3000 });
|
|
400
|
+
if (which.status !== 0) return false;
|
|
401
|
+
const binPath = (which.stdout || '').trim();
|
|
402
|
+
if (!binPath) return false;
|
|
403
|
+
const check = spawnSync(binPath, ['--version'], { encoding: 'utf-8', timeout: 10000 });
|
|
404
|
+
return check.status === 0 && (check.stdout || '').trim().length > 0;
|
|
395
405
|
} catch {
|
|
396
406
|
return false;
|
|
397
407
|
}
|