@openagents-org/agent-launcher 0.2.142 → 0.2.145
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/package.json +1 -1
- package/registry.json +274 -66
- package/src/adapters/aider.js +634 -0
- package/src/adapters/amp.js +477 -0
- package/src/adapters/base.js +108 -7
- package/src/adapters/claude.js +50 -34
- package/src/adapters/cline-stream.js +632 -0
- package/src/adapters/cline.js +914 -0
- package/src/adapters/codex.js +14 -16
- package/src/adapters/copilot-stream-parser.js +380 -0
- package/src/adapters/copilot.js +796 -0
- package/src/adapters/cursor.js +9 -15
- package/src/adapters/gemini.js +10 -12
- package/src/adapters/goose-stream.js +268 -0
- package/src/adapters/goose.js +649 -0
- package/src/adapters/health-status.js +166 -0
- package/src/adapters/hermes.js +8 -17
- package/src/adapters/index.js +16 -1
- package/src/adapters/nanoclaw-bridge.js +264 -0
- package/src/adapters/nanoclaw-channel-install.js +411 -0
- package/src/adapters/nanoclaw-control.js +491 -0
- package/src/adapters/nanoclaw-delivery-store.js +0 -0
- package/src/adapters/nanoclaw-protocol.js +349 -0
- package/src/adapters/nanoclaw.js +661 -9
- package/src/adapters/opencode.js +665 -79
- package/src/agent-rows.js +13 -1
- package/src/auth-guidance.js +98 -0
- package/src/cli.js +29 -7
- package/src/config.js +8 -2
- package/src/daemon.js +108 -6
- package/src/installer.js +509 -10
- package/src/paths.js +125 -2
- package/src/tui.js +29 -2
package/src/adapters/claude.js
CHANGED
|
@@ -19,7 +19,7 @@ const { execSync, spawn } = require('child_process');
|
|
|
19
19
|
const BaseAdapter = require('./base');
|
|
20
20
|
const { formatAttachmentsForPrompt, SESSION_DEFAULT_RE, generateSessionTitle } = require('./utils');
|
|
21
21
|
const { buildClaudeSystemPrompt, buildClaudeSkillMd } = require('./workspace-prompt');
|
|
22
|
-
const { defaultAgentWorkdir, whichBinary,
|
|
22
|
+
const { defaultAgentWorkdir, whichBinary, whereBinary } = require('../paths');
|
|
23
23
|
|
|
24
24
|
const IS_WINDOWS = process.platform === 'win32';
|
|
25
25
|
|
|
@@ -37,6 +37,12 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
37
37
|
this._channelSessions = {}; // channel → Claude CLI session_id
|
|
38
38
|
this._channelProcesses = {}; // channel → child process
|
|
39
39
|
this._stoppingChannels = new Set();
|
|
40
|
+
// Channels that have already announced "Execution stopped by user." for the
|
|
41
|
+
// current stop. Two paths race to post it (the control-action handler that
|
|
42
|
+
// kills the process, and the in-flight message handler that sees
|
|
43
|
+
// pp.userStopped after exit), so this dedups to a single notice. Reset when
|
|
44
|
+
// a new message starts processing in the channel.
|
|
45
|
+
this._stopNoticeSent = new Set();
|
|
40
46
|
this._persistentProcs = {}; // channel → { proc, lineBuffer, pendingLines, idleTimer, messageResolve }
|
|
41
47
|
this._IDLE_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
|
|
42
48
|
this._WATCHDOG_INTERVAL_MS = 15_000; // 15s between checks
|
|
@@ -84,9 +90,7 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
84
90
|
await this._stopProcess(proc);
|
|
85
91
|
delete this._channelProcesses[channel];
|
|
86
92
|
delete this._channelQueues[channel];
|
|
87
|
-
|
|
88
|
-
await this.sendResponse(channel, 'Execution stopped by user.');
|
|
89
|
-
} catch {}
|
|
93
|
+
await this._postStopNotice(channel);
|
|
90
94
|
} else {
|
|
91
95
|
for (const pp of Object.values(this._persistentProcs)) pp.userStopped = true;
|
|
92
96
|
await this._stopAllProcesses('Execution stopped by user.');
|
|
@@ -258,6 +262,18 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
258
262
|
);
|
|
259
263
|
}
|
|
260
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Post "Execution stopped by user." at most once per channel for a given
|
|
267
|
+
* stop. The control-action handler and the in-flight message handler both
|
|
268
|
+
* race to announce a stop; without this guard the user sees it twice. The
|
|
269
|
+
* guard is reset when a new message starts processing in the channel.
|
|
270
|
+
*/
|
|
271
|
+
async _postStopNotice(channel) {
|
|
272
|
+
if (!channel || this._stopNoticeSent.has(channel)) return;
|
|
273
|
+
this._stopNoticeSent.add(channel);
|
|
274
|
+
try { await this.sendResponse(channel, 'Execution stopped by user.'); } catch {}
|
|
275
|
+
}
|
|
276
|
+
|
|
261
277
|
async _stopAllProcesses(completionMessage = 'Execution stopped.') {
|
|
262
278
|
for (const channel of Object.keys(this._persistentProcs)) {
|
|
263
279
|
this._killPersistentProc(channel);
|
|
@@ -288,7 +304,9 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
288
304
|
for (const c of candidates) {
|
|
289
305
|
if (fs.existsSync(c)) return c;
|
|
290
306
|
}
|
|
291
|
-
|
|
307
|
+
// Fall back to the node already running this daemon (absolute, always valid).
|
|
308
|
+
// Bare 'node' can be off-PATH in a packaged daemon or CI runner.
|
|
309
|
+
return process.execPath;
|
|
292
310
|
}
|
|
293
311
|
|
|
294
312
|
/**
|
|
@@ -343,24 +361,13 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
343
361
|
const portableCandidate = path.join(portableBin, `claude${ext}`);
|
|
344
362
|
if (fs.existsSync(portableCandidate)) return portableCandidate;
|
|
345
363
|
|
|
346
|
-
// Tier 1: PATH search
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (IS_WINDOWS) {
|
|
354
|
-
const r = execSync('where claude.cmd 2>nul || where claude.exe 2>nul || where claude 2>nul', {
|
|
355
|
-
encoding: 'utf-8', timeout: 5000, windowsHide: true, env,
|
|
356
|
-
});
|
|
357
|
-
const hit = r.split(/\r?\n/)[0].trim();
|
|
358
|
-
if (hit) return hit;
|
|
359
|
-
} else {
|
|
360
|
-
const hit = execSync('which claude', { encoding: 'utf-8', timeout: 5000, windowsHide: true, env }).trim();
|
|
361
|
-
if (hit) return hit;
|
|
362
|
-
}
|
|
363
|
-
} catch {}
|
|
364
|
+
// Tier 1: PATH search via a codepage-safe lookup (whereBinary forces UTF-8
|
|
365
|
+
// output + verifies existence, so a non-ASCII/Chinese username isn't mangled
|
|
366
|
+
// into an ENOENT). Uses the ENRICHED env so a packaged daemon's minimal PATH
|
|
367
|
+
// still sees the node-version-manager / homebrew / npm-global dirs the
|
|
368
|
+
// launcher adds — that's why a bare `which claude` came up empty before.
|
|
369
|
+
const viaWhere = whereBinary('claude');
|
|
370
|
+
if (viaWhere) return viaWhere;
|
|
364
371
|
|
|
365
372
|
// Tier 2: Next to current Node.js interpreter (npm global)
|
|
366
373
|
const nodeBinDir = path.dirname(process.execPath);
|
|
@@ -558,15 +565,9 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
558
565
|
const oaPortable = path.join(home3, '.openagents', 'nodejs', 'node_modules', '.bin', `openagents${oaExt}`);
|
|
559
566
|
if (fs.existsSync(oaPortable)) oaBin = oaPortable;
|
|
560
567
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
encoding: 'utf-8', timeout: 5000,
|
|
565
|
-
}).split(/\r?\n/)[0].trim();
|
|
566
|
-
} else {
|
|
567
|
-
oaBin = execSync('which openagents', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
568
|
-
}
|
|
569
|
-
} catch {}
|
|
568
|
+
// Codepage-safe lookup so a non-ASCII/Chinese username in the path isn't
|
|
569
|
+
// mangled into an ENOENT (see whereBinary).
|
|
570
|
+
if (!oaBin) oaBin = whereBinary('openagents');
|
|
570
571
|
if (!oaBin) {
|
|
571
572
|
this._log('Could not find openagents binary — MCP tools may not be available');
|
|
572
573
|
mcpCommand = 'openagents';
|
|
@@ -805,6 +806,20 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
805
806
|
if (pp.messageResolve) {
|
|
806
807
|
pp.messageResolve({ resultEvent: event });
|
|
807
808
|
pp.messageResolve = null;
|
|
809
|
+
} else {
|
|
810
|
+
// Background-triggered turn: the CLI produced output on its own after
|
|
811
|
+
// the user-initiated turn already resolved (e.g. a run_in_background
|
|
812
|
+
// task finished and fed its result back to the model). That text was
|
|
813
|
+
// streamed as `thinking` only — with no messageResolve waiting,
|
|
814
|
+
// _handleMessage never posts it as a `chat`, so the thread visually
|
|
815
|
+
// hangs on "thinking…/Working…". Finalize the accumulated text as a
|
|
816
|
+
// real answer so the turn settles on an actual message bubble.
|
|
817
|
+
const trailing = pp.lastResponseText.join('\n').trim();
|
|
818
|
+
if (trailing && !event.is_error) {
|
|
819
|
+
this._resetIdleTimer(pp.msgChannel);
|
|
820
|
+
try { await this.sendResponse(pp.msgChannel, trailing); } catch {}
|
|
821
|
+
}
|
|
822
|
+
pp.lastResponseText.length = 0;
|
|
808
823
|
}
|
|
809
824
|
} else if (eventType === 'system') {
|
|
810
825
|
const subtype = event.subtype || '';
|
|
@@ -923,6 +938,7 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
923
938
|
|
|
924
939
|
const msgChannel = msg.sessionId || this.channelName;
|
|
925
940
|
this._stoppingChannels.delete(msgChannel);
|
|
941
|
+
this._stopNoticeSent.delete(msgChannel);
|
|
926
942
|
const sender = msg.senderName || msg.senderType || 'user';
|
|
927
943
|
this._log(`Processing message from ${sender} in ${msgChannel}: ${content.slice(0, 80)}...`);
|
|
928
944
|
|
|
@@ -987,7 +1003,7 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
987
1003
|
}
|
|
988
1004
|
if (existingPP.userStopped) {
|
|
989
1005
|
if (!existingPP.everPostedAnything) {
|
|
990
|
-
|
|
1006
|
+
await this._postStopNotice(msgChannel);
|
|
991
1007
|
}
|
|
992
1008
|
return;
|
|
993
1009
|
}
|
|
@@ -1087,7 +1103,7 @@ class ClaudeAdapter extends BaseAdapter {
|
|
|
1087
1103
|
this._log(`Process exited during first message (attempt ${attempt + 1}), userStopped=${pp.userStopped}`);
|
|
1088
1104
|
if (pp.userStopped) {
|
|
1089
1105
|
if (!pp.everPostedAnything) {
|
|
1090
|
-
|
|
1106
|
+
await this._postStopNotice(msgChannel);
|
|
1091
1107
|
}
|
|
1092
1108
|
break;
|
|
1093
1109
|
}
|