open-agents-ai 0.103.82 → 0.103.85
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/index.js +293 -89
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1343,10 +1343,22 @@ var init_shell = __esm({
|
|
|
1343
1343
|
workingDir;
|
|
1344
1344
|
defaultTimeout;
|
|
1345
1345
|
_sudoPassword = null;
|
|
1346
|
+
/** Active child processes — killed on abort for instant /stop */
|
|
1347
|
+
_activeChildren = /* @__PURE__ */ new Set();
|
|
1346
1348
|
constructor(workingDir, defaultTimeout = 3e4) {
|
|
1347
1349
|
this.workingDir = workingDir;
|
|
1348
1350
|
this.defaultTimeout = defaultTimeout;
|
|
1349
1351
|
}
|
|
1352
|
+
/** Kill all active child processes immediately (called by /stop) */
|
|
1353
|
+
killAll() {
|
|
1354
|
+
for (const child of this._activeChildren) {
|
|
1355
|
+
try {
|
|
1356
|
+
child.kill("SIGKILL");
|
|
1357
|
+
} catch {
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
this._activeChildren.clear();
|
|
1361
|
+
}
|
|
1350
1362
|
/** Set a cached sudo password for the session. The shell tool will
|
|
1351
1363
|
* automatically retry permission-denied commands with `sudo -S`. */
|
|
1352
1364
|
setSudoPassword(password) {
|
|
@@ -1397,17 +1409,14 @@ ${stdinInput ?? ""}`);
|
|
|
1397
1409
|
cwd: this.workingDir,
|
|
1398
1410
|
env: {
|
|
1399
1411
|
...process.env,
|
|
1400
|
-
// Non-interactive mode: libraries like prompts, inquirer,
|
|
1401
|
-
// create-next-app, and many Node CLI tools check CI to skip
|
|
1402
|
-
// interactive prompts and use defaults instead.
|
|
1403
1412
|
CI: "true",
|
|
1404
1413
|
NONINTERACTIVE: "1",
|
|
1405
|
-
// Disable color in most tools to get clean output
|
|
1406
1414
|
NO_COLOR: "1",
|
|
1407
1415
|
FORCE_COLOR: "0"
|
|
1408
1416
|
},
|
|
1409
1417
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1410
1418
|
});
|
|
1419
|
+
this._activeChildren.add(child);
|
|
1411
1420
|
let stdout = "";
|
|
1412
1421
|
let stderr = "";
|
|
1413
1422
|
let killed = false;
|
|
@@ -1450,6 +1459,7 @@ ${stdinInput ?? ""}`);
|
|
|
1450
1459
|
}
|
|
1451
1460
|
child.stdin.end();
|
|
1452
1461
|
child.on("exit", (code) => {
|
|
1462
|
+
this._activeChildren.delete(child);
|
|
1453
1463
|
exitFlushTimer = setTimeout(() => {
|
|
1454
1464
|
const durationMs = performance.now() - start;
|
|
1455
1465
|
if (killed) {
|
|
@@ -4703,6 +4713,24 @@ Process error: ${err.message}`;
|
|
|
4703
4713
|
entry.finishedAt = Date.now();
|
|
4704
4714
|
return true;
|
|
4705
4715
|
}
|
|
4716
|
+
/** Kill ALL running tasks immediately (for /stop and /quit authority) */
|
|
4717
|
+
stopAll() {
|
|
4718
|
+
let killed = 0;
|
|
4719
|
+
for (const [, entry] of this.tasks) {
|
|
4720
|
+
if (entry.status !== "running")
|
|
4721
|
+
continue;
|
|
4722
|
+
if (entry.child) {
|
|
4723
|
+
try {
|
|
4724
|
+
entry.child.kill("SIGKILL");
|
|
4725
|
+
} catch {
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
entry.status = "stopped";
|
|
4729
|
+
entry.finishedAt = Date.now();
|
|
4730
|
+
killed++;
|
|
4731
|
+
}
|
|
4732
|
+
return killed;
|
|
4733
|
+
}
|
|
4706
4734
|
list() {
|
|
4707
4735
|
return Array.from(this.tasks.values()).map(toInfo);
|
|
4708
4736
|
}
|
|
@@ -12432,8 +12460,8 @@ async function handleCmd(cmd) {
|
|
|
12432
12460
|
// succeeds in <1s. Racing them eliminates the wait.
|
|
12433
12461
|
dlog('remote_infer: invoking ' + riCapName + ' on peer ' + riTargetPeer.slice(0, 20) + ' (libp2p' + (_natsConn ? ' + NATS parallel' : '') + ')');
|
|
12434
12462
|
try {
|
|
12435
|
-
var RI_TIMEOUT =
|
|
12436
|
-
var riInvokeP = nexus.invokeCapability(riTargetPeer, riCapName, riData, { stream: false, maxDurationMs:
|
|
12463
|
+
var RI_TIMEOUT = 60000; // 60s outer timeout \u2014 fail fast for interactive use
|
|
12464
|
+
var riInvokeP = nexus.invokeCapability(riTargetPeer, riCapName, riData, { stream: false, maxDurationMs: 45000 });
|
|
12437
12465
|
var riTimeoutP = new Promise(function(_, reject) { setTimeout(function() { reject(new Error('remote_infer timed out after ' + (RI_TIMEOUT / 1000) + 's')); }, RI_TIMEOUT); });
|
|
12438
12466
|
|
|
12439
12467
|
// Build race candidates \u2014 libp2p + timeout always present
|
|
@@ -12444,7 +12472,7 @@ async function handleCmd(cmd) {
|
|
|
12444
12472
|
var _riNatsRaceP = (async function() {
|
|
12445
12473
|
var _rSubject = 'nexus.invoke.' + riTargetPeer;
|
|
12446
12474
|
var _rPayload = JSON.stringify({ capability: riCapName, input: riData, from: nexus.peerId || '' });
|
|
12447
|
-
var _rResp = await _natsConn.request(_rSubject, _natsCodec.encode(_rPayload), { timeout:
|
|
12475
|
+
var _rResp = await _natsConn.request(_rSubject, _natsCodec.encode(_rPayload), { timeout: 45000 });
|
|
12448
12476
|
var _rResult = JSON.parse(_natsCodec.decode(_rResp.data));
|
|
12449
12477
|
if (_rResult && _rResult.error) throw new Error('NATS: ' + _rResult.error);
|
|
12450
12478
|
dlog('remote_infer: NATS won the race');
|
|
@@ -12483,8 +12511,8 @@ async function handleCmd(cmd) {
|
|
|
12483
12511
|
var rPid = riCandidates[ri];
|
|
12484
12512
|
dlog('remote_infer: trying peer ' + rPid.slice(0, 20) + '...');
|
|
12485
12513
|
try {
|
|
12486
|
-
var PEER_INVOKE_TIMEOUT =
|
|
12487
|
-
var specInvoke = nexus.invokeCapability(rPid, riCapName, riData, { stream: false, maxDurationMs:
|
|
12514
|
+
var PEER_INVOKE_TIMEOUT = 30000; // 30s per peer in discovery \u2014 fail fast, try next
|
|
12515
|
+
var specInvoke = nexus.invokeCapability(rPid, riCapName, riData, { stream: false, maxDurationMs: 25000 });
|
|
12488
12516
|
var specTimeout = new Promise(function(_, reject) {
|
|
12489
12517
|
setTimeout(function() { reject(new Error('peer invoke timeout')); }, PEER_INVOKE_TIMEOUT);
|
|
12490
12518
|
});
|
|
@@ -12513,7 +12541,7 @@ async function handleCmd(cmd) {
|
|
|
12513
12541
|
try {
|
|
12514
12542
|
var _dpSubject = 'nexus.invoke.' + _dpPid;
|
|
12515
12543
|
var _dpPayload = JSON.stringify({ capability: riCapName, input: riData, from: nexus.peerId || '' });
|
|
12516
|
-
var _dpResp = await _natsConn.request(_dpSubject, _natsCodec.encode(_dpPayload), { timeout:
|
|
12544
|
+
var _dpResp = await _natsConn.request(_dpSubject, _natsCodec.encode(_dpPayload), { timeout: 30000 });
|
|
12517
12545
|
var _dpResult = JSON.parse(_natsCodec.decode(_dpResp.data));
|
|
12518
12546
|
if (_dpResult && !_dpResult.error) {
|
|
12519
12547
|
riResult = _dpResult;
|
|
@@ -18628,6 +18656,7 @@ var init_agenticRunner = __esm({
|
|
|
18628
18656
|
handlers = [];
|
|
18629
18657
|
pendingUserMessages = [];
|
|
18630
18658
|
aborted = false;
|
|
18659
|
+
_abortController = new AbortController();
|
|
18631
18660
|
_paused = false;
|
|
18632
18661
|
_pauseResolve = null;
|
|
18633
18662
|
_sudoPassword = null;
|
|
@@ -18837,14 +18866,30 @@ ${this.options.dynamicContext}`,
|
|
|
18837
18866
|
injectUserMessage(content) {
|
|
18838
18867
|
this.pendingUserMessages.push(content);
|
|
18839
18868
|
}
|
|
18840
|
-
/**
|
|
18869
|
+
/** Retract the last pending user message (Esc cancel).
|
|
18870
|
+
* Returns the retracted text, or null if queue is empty or already consumed. */
|
|
18871
|
+
retractLastPendingMessage() {
|
|
18872
|
+
if (this.pendingUserMessages.length === 0)
|
|
18873
|
+
return null;
|
|
18874
|
+
return this.pendingUserMessages.pop();
|
|
18875
|
+
}
|
|
18876
|
+
/** Abort the current task run — cancels in-flight requests and kills child processes */
|
|
18841
18877
|
abort() {
|
|
18842
18878
|
this.aborted = true;
|
|
18879
|
+
this._abortController.abort();
|
|
18880
|
+
const shellTool = this.tools.get("shell");
|
|
18881
|
+
if (shellTool && typeof shellTool.killAll === "function") {
|
|
18882
|
+
shellTool.killAll();
|
|
18883
|
+
}
|
|
18843
18884
|
if (this._pauseResolve) {
|
|
18844
18885
|
this._pauseResolve();
|
|
18845
18886
|
this._pauseResolve = null;
|
|
18846
18887
|
}
|
|
18847
18888
|
}
|
|
18889
|
+
/** Get the current abort signal for passing to fetch/spawn */
|
|
18890
|
+
get abortSignal() {
|
|
18891
|
+
return this._abortController.signal;
|
|
18892
|
+
}
|
|
18848
18893
|
/**
|
|
18849
18894
|
* Pause the current task gracefully. The run loop will suspend at the next
|
|
18850
18895
|
* turn boundary (between tool calls / model requests) without destroying state.
|
|
@@ -18955,6 +19000,11 @@ Respond with your assessment, then take action.`;
|
|
|
18955
19000
|
}
|
|
18956
19001
|
/** Run a task through the agentic loop */
|
|
18957
19002
|
async run(task, context) {
|
|
19003
|
+
this.aborted = false;
|
|
19004
|
+
this._abortController = new AbortController();
|
|
19005
|
+
if (typeof this.backend.setAbortSignal === "function") {
|
|
19006
|
+
this.backend.setAbortSignal(this._abortController.signal);
|
|
19007
|
+
}
|
|
18958
19008
|
const start = Date.now();
|
|
18959
19009
|
const taskTimeoutMs = this.options.taskTimeoutMs;
|
|
18960
19010
|
const selfEvalInterval = taskTimeoutMs;
|
|
@@ -19101,6 +19151,10 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
|
|
|
19101
19151
|
try {
|
|
19102
19152
|
response = this.options.streamEnabled && this.hasStreamingSupport() ? await this.streamingRequest(chatRequest, turn) : await this.backend.chatCompletion(chatRequest);
|
|
19103
19153
|
} catch (reqErr) {
|
|
19154
|
+
if (reqErr instanceof Error && reqErr.fatal) {
|
|
19155
|
+
this.emit({ type: "error", content: `Backend error: ${reqErr.message}`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
19156
|
+
break;
|
|
19157
|
+
}
|
|
19104
19158
|
const recovered = await this.retryOnTransient(reqErr, chatRequest, turn);
|
|
19105
19159
|
if (!recovered) {
|
|
19106
19160
|
const errMsg = reqErr instanceof Error ? reqErr.message : String(reqErr);
|
|
@@ -19615,6 +19669,10 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
19615
19669
|
try {
|
|
19616
19670
|
response = this.options.streamEnabled && this.hasStreamingSupport() ? await this.streamingRequest(chatRequest, turn) : await this.backend.chatCompletion(chatRequest);
|
|
19617
19671
|
} catch (reqErr) {
|
|
19672
|
+
if (this.aborted || reqErr instanceof Error && reqErr.name === "AbortError") {
|
|
19673
|
+
this.emit({ type: "error", content: "Task aborted by user", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
19674
|
+
break;
|
|
19675
|
+
}
|
|
19618
19676
|
const recovered = await this.retryOnTransient(reqErr, chatRequest, turn);
|
|
19619
19677
|
if (!recovered) {
|
|
19620
19678
|
const errMsg2 = reqErr instanceof Error ? reqErr.message : String(reqErr);
|
|
@@ -20857,6 +20915,8 @@ ${transcript}`
|
|
|
20857
20915
|
// -------------------------------------------------------------------------
|
|
20858
20916
|
/** Detect whether an error is transient (worth retrying) */
|
|
20859
20917
|
isTransientError(err) {
|
|
20918
|
+
if (err instanceof Error && err.fatal)
|
|
20919
|
+
return false;
|
|
20860
20920
|
const msg = err instanceof Error ? err.message : String(err);
|
|
20861
20921
|
if (/Backend HTTP (502|503|504)/i.test(msg))
|
|
20862
20922
|
return true;
|
|
@@ -20866,6 +20926,10 @@ ${transcript}`
|
|
|
20866
20926
|
return true;
|
|
20867
20927
|
if (/model is loading|server busy|overloaded/i.test(msg))
|
|
20868
20928
|
return true;
|
|
20929
|
+
if (/multiaddrs failed|all multiaddrs failed|dial to peer failed|stream was reset|Cannot reach peer/i.test(msg))
|
|
20930
|
+
return true;
|
|
20931
|
+
if (/NATS.*timeout|relay.*timeout|invoke.*timeout/i.test(msg))
|
|
20932
|
+
return true;
|
|
20869
20933
|
return false;
|
|
20870
20934
|
}
|
|
20871
20935
|
/**
|
|
@@ -21004,12 +21068,18 @@ ${transcript}`
|
|
|
21004
21068
|
model;
|
|
21005
21069
|
apiKey;
|
|
21006
21070
|
thinking;
|
|
21071
|
+
/** Abort signal — set by the runner so /stop can cancel in-flight requests */
|
|
21072
|
+
_abortSignal = null;
|
|
21007
21073
|
constructor(baseUrl, model, apiKey, thinking) {
|
|
21008
21074
|
this.baseUrl = normalizeBaseUrl(baseUrl);
|
|
21009
21075
|
this.model = model;
|
|
21010
21076
|
this.apiKey = apiKey ?? "";
|
|
21011
21077
|
this.thinking = thinking ?? true;
|
|
21012
21078
|
}
|
|
21079
|
+
/** Set the abort signal from the runner (called at run start) */
|
|
21080
|
+
setAbortSignal(signal) {
|
|
21081
|
+
this._abortSignal = signal;
|
|
21082
|
+
}
|
|
21013
21083
|
/** Build auth headers — all providers use standard Bearer token auth. */
|
|
21014
21084
|
authHeaders() {
|
|
21015
21085
|
const headers = { "Content-Type": "application/json" };
|
|
@@ -21027,11 +21097,14 @@ ${transcript}`
|
|
|
21027
21097
|
max_tokens: request.maxTokens,
|
|
21028
21098
|
think: this.thinking
|
|
21029
21099
|
};
|
|
21030
|
-
const
|
|
21100
|
+
const fetchOpts = {
|
|
21031
21101
|
method: "POST",
|
|
21032
21102
|
headers: this.authHeaders(),
|
|
21033
21103
|
body: JSON.stringify(body)
|
|
21034
|
-
}
|
|
21104
|
+
};
|
|
21105
|
+
if (this._abortSignal)
|
|
21106
|
+
fetchOpts.signal = this._abortSignal;
|
|
21107
|
+
const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, fetchOpts);
|
|
21035
21108
|
if (!resp.ok) {
|
|
21036
21109
|
const text = await resp.text().catch(() => "");
|
|
21037
21110
|
const isHtml = text.trimStart().startsWith("<!") || text.trimStart().startsWith("<html");
|
|
@@ -21089,11 +21162,14 @@ ${transcript}`
|
|
|
21089
21162
|
stream_options: { include_usage: true },
|
|
21090
21163
|
think: this.thinking
|
|
21091
21164
|
};
|
|
21092
|
-
const
|
|
21165
|
+
const streamFetchOpts = {
|
|
21093
21166
|
method: "POST",
|
|
21094
21167
|
headers: this.authHeaders(),
|
|
21095
21168
|
body: JSON.stringify(body)
|
|
21096
|
-
}
|
|
21169
|
+
};
|
|
21170
|
+
if (this._abortSignal)
|
|
21171
|
+
streamFetchOpts.signal = this._abortSignal;
|
|
21172
|
+
const resp = await fetch(`${this.baseUrl}/v1/chat/completions`, streamFetchOpts);
|
|
21097
21173
|
if (!resp.ok) {
|
|
21098
21174
|
const text = await resp.text().catch(() => "");
|
|
21099
21175
|
const isHtml = text.trimStart().startsWith("<!") || text.trimStart().startsWith("<html");
|
|
@@ -21181,6 +21257,15 @@ var init_nexusBackend = __esm({
|
|
|
21181
21257
|
this.authKey = authKey || "";
|
|
21182
21258
|
this.thinking = thinking ?? true;
|
|
21183
21259
|
}
|
|
21260
|
+
/** Reset the consecutive failure counter (called on endpoint switch / reconnect) */
|
|
21261
|
+
resetFailures() {
|
|
21262
|
+
this.consecutiveFailures = 0;
|
|
21263
|
+
}
|
|
21264
|
+
/** Update target peer (allows switching peers without recreating the backend) */
|
|
21265
|
+
setTargetPeer(peerId) {
|
|
21266
|
+
this.targetPeer = peerId;
|
|
21267
|
+
this.consecutiveFailures = 0;
|
|
21268
|
+
}
|
|
21184
21269
|
async chatCompletion(request) {
|
|
21185
21270
|
if (this.consecutiveFailures >= _NexusAgenticBackend.MAX_CONSECUTIVE_FAILURES) {
|
|
21186
21271
|
const err = new Error(`Remote peer unreachable after ${this.consecutiveFailures} attempts. Use /endpoint to switch to a local model or reconnect.`);
|
|
@@ -21217,7 +21302,10 @@ var init_nexusBackend = __esm({
|
|
|
21217
21302
|
}
|
|
21218
21303
|
if (parsed.success === false) {
|
|
21219
21304
|
this.consecutiveFailures++;
|
|
21220
|
-
|
|
21305
|
+
const output = String(parsed.output || rawResult);
|
|
21306
|
+
const isDialErr = /multiaddrs failed|Cannot reach peer|dial to peer|timeout|ECONNREFUSED/i.test(output);
|
|
21307
|
+
const hint = isDialErr ? ` (${this.consecutiveFailures}/${_NexusAgenticBackend.MAX_CONSECUTIVE_FAILURES} failures \u2014 will stop after ${_NexusAgenticBackend.MAX_CONSECUTIVE_FAILURES})` : "";
|
|
21308
|
+
throw new Error(`Remote inference failed: ${output}${hint}`);
|
|
21221
21309
|
}
|
|
21222
21310
|
this.consecutiveFailures = 0;
|
|
21223
21311
|
const invokeResult = parsed.result;
|
|
@@ -38670,6 +38758,8 @@ var init_voice = __esm({
|
|
|
38670
38758
|
await this.ensureLuxtts();
|
|
38671
38759
|
this.luxttsActive = true;
|
|
38672
38760
|
this.mlxActive = false;
|
|
38761
|
+
this.ensureLuxttsDaemon().catch(() => {
|
|
38762
|
+
});
|
|
38673
38763
|
} else if (isMlx) {
|
|
38674
38764
|
await this.ensureMlxAudio();
|
|
38675
38765
|
this.mlxActive = true;
|
|
@@ -38722,6 +38812,8 @@ var init_voice = __esm({
|
|
|
38722
38812
|
await this.ensureLuxtts();
|
|
38723
38813
|
this.luxttsActive = true;
|
|
38724
38814
|
this.mlxActive = false;
|
|
38815
|
+
this.ensureLuxttsDaemon().catch(() => {
|
|
38816
|
+
});
|
|
38725
38817
|
} else if (isMlx) {
|
|
38726
38818
|
await this.ensureMlxAudio();
|
|
38727
38819
|
this.mlxActive = true;
|
|
@@ -39308,7 +39400,7 @@ var init_voice = __esm({
|
|
|
39308
39400
|
isMlxCapable() {
|
|
39309
39401
|
return platform3() === "darwin" && process.arch === "arm64";
|
|
39310
39402
|
}
|
|
39311
|
-
/** Resolve python3 binary path */
|
|
39403
|
+
/** Resolve python3 binary path (cached after first call) */
|
|
39312
39404
|
findPython3() {
|
|
39313
39405
|
if (this.python3Path)
|
|
39314
39406
|
return this.python3Path;
|
|
@@ -39324,6 +39416,52 @@ var init_voice = __esm({
|
|
|
39324
39416
|
}
|
|
39325
39417
|
return null;
|
|
39326
39418
|
}
|
|
39419
|
+
/** Async version of findPython3 — non-blocking */
|
|
39420
|
+
async findPython3Async() {
|
|
39421
|
+
if (this.python3Path)
|
|
39422
|
+
return this.python3Path;
|
|
39423
|
+
for (const bin of ["python3", "python"]) {
|
|
39424
|
+
try {
|
|
39425
|
+
const path = await this.asyncShell(`which ${bin}`, 5e3);
|
|
39426
|
+
if (path) {
|
|
39427
|
+
this.python3Path = path;
|
|
39428
|
+
return path;
|
|
39429
|
+
}
|
|
39430
|
+
} catch {
|
|
39431
|
+
}
|
|
39432
|
+
}
|
|
39433
|
+
return null;
|
|
39434
|
+
}
|
|
39435
|
+
/** Non-blocking shell execution — async alternative to execSync.
|
|
39436
|
+
* Returns stdout string on exit 0, rejects otherwise. */
|
|
39437
|
+
asyncShell(command, timeoutMs = 3e4) {
|
|
39438
|
+
return new Promise((resolve31, reject) => {
|
|
39439
|
+
const proc = nodeSpawn("sh", ["-c", command], {
|
|
39440
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
39441
|
+
cwd: tmpdir6()
|
|
39442
|
+
});
|
|
39443
|
+
let stdout = "";
|
|
39444
|
+
let stderr = "";
|
|
39445
|
+
proc.stdout.on("data", (d) => {
|
|
39446
|
+
stdout += d.toString();
|
|
39447
|
+
});
|
|
39448
|
+
proc.stderr.on("data", (d) => {
|
|
39449
|
+
stderr += d.toString();
|
|
39450
|
+
});
|
|
39451
|
+
proc.on("error", reject);
|
|
39452
|
+
const timer = setTimeout(() => {
|
|
39453
|
+
proc.kill("SIGKILL");
|
|
39454
|
+
reject(new Error(`Timeout after ${timeoutMs}ms`));
|
|
39455
|
+
}, timeoutMs);
|
|
39456
|
+
proc.on("close", (code) => {
|
|
39457
|
+
clearTimeout(timer);
|
|
39458
|
+
if (code === 0)
|
|
39459
|
+
resolve31(stdout.trim());
|
|
39460
|
+
else
|
|
39461
|
+
reject(new Error(stderr.slice(0, 300) || `Exit code ${code}`));
|
|
39462
|
+
});
|
|
39463
|
+
});
|
|
39464
|
+
}
|
|
39327
39465
|
/** Check if mlx-audio is installed */
|
|
39328
39466
|
checkMlxInstalled() {
|
|
39329
39467
|
if (this.mlxInstalled !== null)
|
|
@@ -39496,9 +39634,10 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
39496
39634
|
* Ensure LuxTTS venv is created with all dependencies.
|
|
39497
39635
|
* On first run: creates venv, installs PyTorch + LuxTTS + deps (~5-15 min).
|
|
39498
39636
|
* Subsequent runs: checks venv exists and import works.
|
|
39637
|
+
* All shell commands use asyncShell — never blocks the event loop.
|
|
39499
39638
|
*/
|
|
39500
39639
|
async ensureLuxtts() {
|
|
39501
|
-
const py = this.
|
|
39640
|
+
const py = await this.findPython3Async();
|
|
39502
39641
|
if (!py) {
|
|
39503
39642
|
throw new Error("python3 not found. LuxTTS requires Python 3.10+. Try: apt install python3 / brew install python3");
|
|
39504
39643
|
}
|
|
@@ -39506,23 +39645,25 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
39506
39645
|
const venvPy = luxttsVenvPy();
|
|
39507
39646
|
if (existsSync34(venvPy)) {
|
|
39508
39647
|
try {
|
|
39509
|
-
|
|
39648
|
+
await this.asyncShell(`${venvPy} -c "import sys; sys.path.insert(0, '${luxttsRepoDir()}'); from zipvoice.luxvoice import LuxTTS; print('ok')"`, 3e4);
|
|
39510
39649
|
let hasCudaSys = false;
|
|
39511
39650
|
try {
|
|
39512
|
-
|
|
39651
|
+
await this.asyncShell("nvidia-smi", 5e3);
|
|
39513
39652
|
hasCudaSys = true;
|
|
39514
39653
|
} catch {
|
|
39515
39654
|
}
|
|
39516
39655
|
if (hasCudaSys) {
|
|
39517
|
-
|
|
39518
|
-
const torchCheck = execSync27(`${venvPy} -c "import torch; print('cuda' if torch.cuda.is_available() else 'cpu')"`, { stdio: "pipe", timeout: 15e3 }).toString().trim();
|
|
39656
|
+
this.asyncShell(`${venvPy} -c "import torch; print('cuda' if torch.cuda.is_available() else 'cpu')"`, 15e3).then(async (torchCheck) => {
|
|
39519
39657
|
if (torchCheck === "cpu") {
|
|
39520
|
-
renderWarning("GPU detected but PyTorch is CPU-only. Reinstalling with CUDA support...");
|
|
39521
|
-
|
|
39522
|
-
|
|
39658
|
+
renderWarning("GPU detected but PyTorch is CPU-only. Reinstalling with CUDA support in background...");
|
|
39659
|
+
try {
|
|
39660
|
+
await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet --force-reinstall torch torchaudio --index-url https://download.pytorch.org/whl/cu124`, 6e5);
|
|
39661
|
+
renderInfo("PyTorch reinstalled with CUDA GPU support.");
|
|
39662
|
+
} catch {
|
|
39663
|
+
}
|
|
39523
39664
|
}
|
|
39524
|
-
}
|
|
39525
|
-
}
|
|
39665
|
+
}).catch(() => {
|
|
39666
|
+
});
|
|
39526
39667
|
}
|
|
39527
39668
|
this.writeLuxttsInferScript();
|
|
39528
39669
|
this.autoDetectCloneRef();
|
|
@@ -39535,27 +39676,24 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
39535
39676
|
if (!existsSync34(venvDir)) {
|
|
39536
39677
|
renderInfo(" Creating Python virtual environment...");
|
|
39537
39678
|
try {
|
|
39538
|
-
|
|
39539
|
-
stdio: "pipe",
|
|
39540
|
-
timeout: 6e4
|
|
39541
|
-
});
|
|
39679
|
+
await this.asyncShell(`${py} -m venv ${JSON.stringify(venvDir)}`, 6e4);
|
|
39542
39680
|
} catch (err) {
|
|
39543
39681
|
throw new Error(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
|
|
39544
39682
|
}
|
|
39545
39683
|
}
|
|
39546
39684
|
let hasCuda = false;
|
|
39547
39685
|
try {
|
|
39548
|
-
|
|
39686
|
+
await this.asyncShell("nvidia-smi", 5e3);
|
|
39549
39687
|
hasCuda = true;
|
|
39550
39688
|
} catch {
|
|
39551
39689
|
}
|
|
39552
39690
|
if (hasCuda) {
|
|
39553
39691
|
renderInfo(" Installing PyTorch (CUDA GPU)...");
|
|
39554
39692
|
try {
|
|
39555
|
-
|
|
39693
|
+
await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cu124`, 6e5);
|
|
39556
39694
|
} catch {
|
|
39557
39695
|
try {
|
|
39558
|
-
|
|
39696
|
+
await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, 6e5);
|
|
39559
39697
|
} catch (err2) {
|
|
39560
39698
|
throw new Error(`Failed to install PyTorch (CUDA): ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
39561
39699
|
}
|
|
@@ -39563,10 +39701,10 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
39563
39701
|
} else {
|
|
39564
39702
|
renderInfo(" Installing PyTorch (CPU \u2014 no NVIDIA GPU detected)...");
|
|
39565
39703
|
try {
|
|
39566
|
-
|
|
39704
|
+
await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio --index-url https://download.pytorch.org/whl/cpu`, 6e5);
|
|
39567
39705
|
} catch {
|
|
39568
39706
|
try {
|
|
39569
|
-
|
|
39707
|
+
await this.asyncShell(`${JSON.stringify(venvPy)} -m pip install --quiet torch torchaudio`, 6e5);
|
|
39570
39708
|
} catch (err2) {
|
|
39571
39709
|
throw new Error(`Failed to install PyTorch: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
39572
39710
|
}
|
|
@@ -39577,9 +39715,9 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
39577
39715
|
renderInfo(" Cloning LuxTTS repository...");
|
|
39578
39716
|
try {
|
|
39579
39717
|
if (existsSync34(repoDir)) {
|
|
39580
|
-
|
|
39718
|
+
await this.asyncShell(`rm -rf ${JSON.stringify(repoDir)}`, 3e4);
|
|
39581
39719
|
}
|
|
39582
|
-
|
|
39720
|
+
await this.asyncShell(`git clone --depth 1 https://github.com/ysharma3501/LuxTTS.git ${JSON.stringify(repoDir)}`, 12e4);
|
|
39583
39721
|
} catch (err) {
|
|
39584
39722
|
throw new Error(`Failed to clone LuxTTS: ${err instanceof Error ? err.message : String(err)}`);
|
|
39585
39723
|
}
|
|
@@ -39587,19 +39725,15 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
39587
39725
|
renderInfo(" Installing LuxTTS dependencies...");
|
|
39588
39726
|
const pipCmd = JSON.stringify(venvPy);
|
|
39589
39727
|
const installCmds = [
|
|
39590
|
-
// Core deps from requirements.txt
|
|
39591
39728
|
`${pipCmd} -m pip install --quiet lhotse huggingface_hub safetensors pydub onnxruntime librosa "transformers<=4.57.6" inflect numpy vocos "setuptools<81"`,
|
|
39592
|
-
// Tokenization deps (piper_phonemize from custom index, plus CJK support)
|
|
39593
39729
|
`${pipCmd} -m pip install --quiet piper-phonemize --find-links https://k2-fsa.github.io/icefall/piper_phonemize.html`,
|
|
39594
39730
|
`${pipCmd} -m pip install --quiet jieba pypinyin cn2an`,
|
|
39595
|
-
// LinaCodec custom vocoder (required by Zipvoice)
|
|
39596
39731
|
`${pipCmd} -m pip install --quiet "git+https://github.com/ysharma3501/LinaCodec.git"`,
|
|
39597
|
-
// Zipvoice package from the cloned repo (installs as 'zipvoice')
|
|
39598
39732
|
`${pipCmd} -m pip install --quiet -e ${JSON.stringify(repoDir)}`
|
|
39599
39733
|
];
|
|
39600
39734
|
for (const cmd of installCmds) {
|
|
39601
39735
|
try {
|
|
39602
|
-
|
|
39736
|
+
await this.asyncShell(cmd, 3e5);
|
|
39603
39737
|
} catch (err) {
|
|
39604
39738
|
const msg = err instanceof Error ? err.message : String(err);
|
|
39605
39739
|
if (msg.includes("piper") || msg.includes("LinaCodec")) {
|
|
@@ -39610,7 +39744,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
39610
39744
|
}
|
|
39611
39745
|
}
|
|
39612
39746
|
try {
|
|
39613
|
-
|
|
39747
|
+
await this.asyncShell(`${venvPy} -c "import sys; sys.path.insert(0, '${repoDir}'); from zipvoice.luxvoice import LuxTTS; print('ok')"`, 3e4);
|
|
39614
39748
|
} catch (err) {
|
|
39615
39749
|
throw new Error(`LuxTTS installed but import failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
39616
39750
|
}
|
|
@@ -45505,8 +45639,11 @@ var init_status_bar = __esm({
|
|
|
45505
45639
|
active = false;
|
|
45506
45640
|
scrollRegionTop = 1;
|
|
45507
45641
|
stdinHooked = false;
|
|
45508
|
-
/** Track previous terminal
|
|
45642
|
+
/** Track previous terminal dimensions to clear ghost separators on resize */
|
|
45509
45643
|
_prevTermRows = 0;
|
|
45644
|
+
_prevTermCols = 0;
|
|
45645
|
+
/** Debounce timer for resize events — prevents separator stacking during drag */
|
|
45646
|
+
_resizeTimer = null;
|
|
45510
45647
|
/**
|
|
45511
45648
|
* Depth-counted content write guard. Incremented by beginContentWrite(),
|
|
45512
45649
|
* decremented by endContentWrite(). Footer is only redrawn and cursor
|
|
@@ -45953,6 +46090,7 @@ var init_status_bar = __esm({
|
|
|
45953
46090
|
this.scrollRegionTop = scrollRegionTop ?? 1;
|
|
45954
46091
|
this.active = true;
|
|
45955
46092
|
this._prevTermRows = process.stdout.rows ?? 24;
|
|
46093
|
+
this._prevTermCols = process.stdout.columns ?? 80;
|
|
45956
46094
|
this.applyScrollRegion();
|
|
45957
46095
|
this.renderFooterAndPositionInput();
|
|
45958
46096
|
this.hookStdin();
|
|
@@ -45990,24 +46128,42 @@ var init_status_bar = __esm({
|
|
|
45990
46128
|
get reservedRows() {
|
|
45991
46129
|
return this._currentFooterHeight;
|
|
45992
46130
|
}
|
|
45993
|
-
/** Handle terminal resize —
|
|
46131
|
+
/** Handle terminal resize — debounced to prevent separator stacking during drag */
|
|
45994
46132
|
handleResize() {
|
|
45995
46133
|
if (!this.active)
|
|
45996
46134
|
return;
|
|
46135
|
+
if (this._resizeTimer)
|
|
46136
|
+
clearTimeout(this._resizeTimer);
|
|
46137
|
+
this._resizeTimer = setTimeout(() => {
|
|
46138
|
+
this._resizeTimer = null;
|
|
46139
|
+
this._handleResizeImmediate();
|
|
46140
|
+
}, 50);
|
|
46141
|
+
}
|
|
46142
|
+
/** Actual resize handler — called after debounce settles */
|
|
46143
|
+
_handleResizeImmediate() {
|
|
46144
|
+
if (!this.active)
|
|
46145
|
+
return;
|
|
46146
|
+
const prevRows = this._prevTermRows;
|
|
46147
|
+
const prevCols = this._prevTermCols;
|
|
45997
46148
|
this.updateFooterHeight();
|
|
45998
46149
|
const rows = process.stdout.rows ?? 24;
|
|
45999
|
-
const
|
|
46150
|
+
const cols = process.stdout.columns ?? 80;
|
|
46000
46151
|
this._prevTermRows = rows;
|
|
46152
|
+
this._prevTermCols = cols;
|
|
46001
46153
|
const pos = this.rowPositions(rows);
|
|
46002
46154
|
const w = getTermWidth();
|
|
46003
46155
|
const sep = c2.dim("\u2500".repeat(w));
|
|
46004
|
-
let
|
|
46005
|
-
if (prevRows > 0 && rows
|
|
46006
|
-
|
|
46156
|
+
let clearBuf = "";
|
|
46157
|
+
if (prevRows > 0 && (rows !== prevRows || cols !== prevCols)) {
|
|
46158
|
+
const wrapFactor = cols > 0 ? Math.ceil(prevCols / cols) : 1;
|
|
46159
|
+
const extraWrappedRows = Math.max(0, (wrapFactor - 1) * 3);
|
|
46160
|
+
const safetyMargin = extraWrappedRows + 2;
|
|
46161
|
+
const clearFrom = Math.max(1, pos.bufferRow - safetyMargin);
|
|
46162
|
+
clearBuf = "\x1B[1;" + rows + `r\x1B[${clearFrom};1H\x1B[J`;
|
|
46007
46163
|
}
|
|
46008
46164
|
if (this.writeDepth > 0) {
|
|
46009
46165
|
const inputWrap = this.wrapInput(w);
|
|
46010
|
-
let buf =
|
|
46166
|
+
let buf = clearBuf + `\x1B[${this.scrollRegionTop};${pos.scrollEnd}r\x1B[?25l\x1B[?7l\x1B[${pos.bufferRow};1H\x1B[2K${this.buildBufferContent(w)}\x1B[${pos.topSepRow};1H\x1B[2K${sep}`;
|
|
46011
46167
|
for (let i = 0; i < inputWrap.lines.length; i++) {
|
|
46012
46168
|
const row = pos.inputStartRow + i;
|
|
46013
46169
|
const prefix = i === 0 ? this.promptText : " ".repeat(this.promptWidth);
|
|
@@ -46016,8 +46172,8 @@ var init_status_bar = __esm({
|
|
|
46016
46172
|
buf += `\x1B[${pos.bottomSepRow};1H\x1B[2K${sep}\x1B[${pos.metricsRow};1H\x1B[2K${this.buildMetricsLine()}\x1B[?7h\x1B[${pos.scrollEnd};1H`;
|
|
46017
46173
|
process.stdout.write(buf);
|
|
46018
46174
|
} else {
|
|
46019
|
-
if (
|
|
46020
|
-
process.stdout.write(
|
|
46175
|
+
if (clearBuf)
|
|
46176
|
+
process.stdout.write(clearBuf);
|
|
46021
46177
|
this.applyScrollRegion();
|
|
46022
46178
|
this.renderFooterAndPositionInput();
|
|
46023
46179
|
}
|
|
@@ -47869,8 +48025,9 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
47869
48025
|
let currentTaskType;
|
|
47870
48026
|
let sessionFilesTouched = [];
|
|
47871
48027
|
let sessionToolCallCount = 0;
|
|
48028
|
+
let lastSteeringInput = "";
|
|
48029
|
+
let lastSteeringRetracted = false;
|
|
47872
48030
|
let restoredSessionContext = null;
|
|
47873
|
-
let pendingSessionRestore = false;
|
|
47874
48031
|
let sessionSudoPassword = null;
|
|
47875
48032
|
let sudoPromptPending = false;
|
|
47876
48033
|
const idlePrompt = `${c2.bold(c2.white("\u276F "))}`;
|
|
@@ -49130,7 +49287,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
49130
49287
|
if (!activeTask)
|
|
49131
49288
|
return false;
|
|
49132
49289
|
activeTask.runner.abort();
|
|
49133
|
-
|
|
49290
|
+
const bgKilled = taskManager.stopAll();
|
|
49291
|
+
writeContent(() => renderInfo(`Task aborted.${bgKilled > 0 ? ` ${bgKilled} background task(s) killed.` : ""}`));
|
|
49134
49292
|
return true;
|
|
49135
49293
|
},
|
|
49136
49294
|
pauseTask() {
|
|
@@ -49222,28 +49380,40 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
49222
49380
|
const lastTime = lastEntry.savedAt ? new Date(lastEntry.savedAt) : null;
|
|
49223
49381
|
const timeAgo = lastTime ? formatTimeAgo(lastTime) : "unknown";
|
|
49224
49382
|
const lastTask = lastEntry.task?.slice(0, 80) || "unknown";
|
|
49225
|
-
setTimeout(() => {
|
|
49383
|
+
setTimeout(async () => {
|
|
49226
49384
|
writeContent(() => {
|
|
49227
49385
|
renderInfo(`Previous session found (${savedCtx.entries.length} entries, last active ${timeAgo})`);
|
|
49228
49386
|
renderInfo(`Last task: ${lastTask}${lastEntry.task && lastEntry.task.length > 80 ? "..." : ""}`);
|
|
49229
|
-
renderInfo(`Restore previous context? (y/n) [auto-restores in 15s]`);
|
|
49230
49387
|
});
|
|
49231
|
-
|
|
49232
|
-
|
|
49233
|
-
setTimeout(() => {
|
|
49234
|
-
if (pendingSessionRestore) {
|
|
49235
|
-
pendingSessionRestore = false;
|
|
49236
|
-
const prompt = buildContextRestorePrompt(repoRoot);
|
|
49237
|
-
if (prompt) {
|
|
49238
|
-
restoredSessionContext = prompt;
|
|
49239
|
-
const info = loadSessionContext(repoRoot);
|
|
49240
|
-
writeContent(() => renderInfo(`Context auto-restored from ${info?.entries.length ?? 0} session(s).`));
|
|
49241
|
-
} else {
|
|
49242
|
-
writeContent(() => renderInfo("No context to restore. Starting fresh."));
|
|
49243
|
-
}
|
|
49244
|
-
showPrompt();
|
|
49245
|
-
}
|
|
49388
|
+
let autoRestoreTimer = setTimeout(() => {
|
|
49389
|
+
autoRestoreTimer = null;
|
|
49246
49390
|
}, 15e3);
|
|
49391
|
+
const result = await tuiSelect({
|
|
49392
|
+
items: [
|
|
49393
|
+
{ key: "restore", label: "Restore previous context" },
|
|
49394
|
+
{ key: "fresh", label: "Start fresh" }
|
|
49395
|
+
],
|
|
49396
|
+
activeKey: "restore",
|
|
49397
|
+
title: "Restore previous session context?",
|
|
49398
|
+
rl
|
|
49399
|
+
});
|
|
49400
|
+
if (autoRestoreTimer)
|
|
49401
|
+
clearTimeout(autoRestoreTimer);
|
|
49402
|
+
if (result.confirmed && result.key === "restore" || !result.confirmed && !autoRestoreTimer) {
|
|
49403
|
+
const prompt = buildContextRestorePrompt(repoRoot);
|
|
49404
|
+
if (prompt) {
|
|
49405
|
+
restoredSessionContext = prompt;
|
|
49406
|
+
const info = loadSessionContext(repoRoot);
|
|
49407
|
+
writeContent(() => renderInfo(`Context restored from ${info?.entries.length ?? 0} session(s).`));
|
|
49408
|
+
} else {
|
|
49409
|
+
writeContent(() => renderInfo("No context to restore. Starting fresh."));
|
|
49410
|
+
}
|
|
49411
|
+
} else if (result.confirmed && result.key === "fresh") {
|
|
49412
|
+
writeContent(() => renderInfo("Starting fresh."));
|
|
49413
|
+
} else {
|
|
49414
|
+
writeContent(() => renderInfo("Starting fresh."));
|
|
49415
|
+
}
|
|
49416
|
+
showPrompt();
|
|
49247
49417
|
}, 150);
|
|
49248
49418
|
}
|
|
49249
49419
|
}
|
|
@@ -49366,22 +49536,6 @@ ${result.content.slice(0, 2e3)}${result.content.length > 2e3 ? "\n[truncated]" :
|
|
|
49366
49536
|
rl.on("line", (line) => {
|
|
49367
49537
|
persistHistoryLine(line);
|
|
49368
49538
|
const input = line.trim();
|
|
49369
|
-
if (pendingSessionRestore) {
|
|
49370
|
-
pendingSessionRestore = false;
|
|
49371
|
-
const answer = input.toLowerCase();
|
|
49372
|
-
if (answer === "y" || answer === "yes") {
|
|
49373
|
-
const prompt = buildContextRestorePrompt(repoRoot);
|
|
49374
|
-
if (prompt) {
|
|
49375
|
-
restoredSessionContext = prompt;
|
|
49376
|
-
const info = loadSessionContext(repoRoot);
|
|
49377
|
-
writeContent(() => renderInfo(`Context restored from ${info?.entries.length ?? 0} session(s). Will be injected into your next task.`));
|
|
49378
|
-
}
|
|
49379
|
-
} else {
|
|
49380
|
-
writeContent(() => renderInfo("Starting fresh."));
|
|
49381
|
-
}
|
|
49382
|
-
showPrompt();
|
|
49383
|
-
return;
|
|
49384
|
-
}
|
|
49385
49539
|
if (!input) {
|
|
49386
49540
|
if (pasteBuffer.length > 0) {
|
|
49387
49541
|
flushPasteBuffer();
|
|
@@ -49436,6 +49590,7 @@ ${result.content.slice(0, 2e3)}${result.content.length > 2e3 ? "\n[truncated]" :
|
|
|
49436
49590
|
if (activeTask) {
|
|
49437
49591
|
activeTask.runner.abort();
|
|
49438
49592
|
}
|
|
49593
|
+
taskManager.stopAll();
|
|
49439
49594
|
if (blessEngine?.isActive)
|
|
49440
49595
|
blessEngine.stop();
|
|
49441
49596
|
if (telegramBridge?.isActive)
|
|
@@ -49505,6 +49660,10 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
49505
49660
|
const isImage = isImagePath(cleanPath) && existsSync40(resolve28(repoRoot, cleanPath));
|
|
49506
49661
|
const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync40(resolve28(repoRoot, cleanPath));
|
|
49507
49662
|
if (activeTask) {
|
|
49663
|
+
if (activeTask.runner.isPaused) {
|
|
49664
|
+
activeTask.runner.resume();
|
|
49665
|
+
statusBar.setProcessing(true);
|
|
49666
|
+
}
|
|
49508
49667
|
if (isImage) {
|
|
49509
49668
|
try {
|
|
49510
49669
|
const imgPath = resolve28(repoRoot, cleanPath);
|
|
@@ -49532,8 +49691,16 @@ ${result.text}`;
|
|
|
49532
49691
|
writeContent(() => renderUserInterrupt(`[Media: ${cleanPath}]`));
|
|
49533
49692
|
}
|
|
49534
49693
|
} else {
|
|
49694
|
+
const isReplacement = lastSteeringRetracted;
|
|
49695
|
+
lastSteeringInput = input;
|
|
49696
|
+
lastSteeringRetracted = false;
|
|
49535
49697
|
const lineCount = input.split("\n").length;
|
|
49536
|
-
if (
|
|
49698
|
+
if (isReplacement) {
|
|
49699
|
+
const displayText2 = lineCount > 1 ? `[pasted ${lineCount} lines]` : input;
|
|
49700
|
+
writeContent(() => process.stdout.write(`
|
|
49701
|
+
${c2.green("\u21BB")} ${c2.bold("Context replaced:")} ${displayText2}
|
|
49702
|
+
`));
|
|
49703
|
+
} else if (lineCount > 1) {
|
|
49537
49704
|
writeContent(() => renderUserInterrupt(`[pasted ${lineCount} lines]`));
|
|
49538
49705
|
} else {
|
|
49539
49706
|
writeContent(() => renderUserInterrupt(input));
|
|
@@ -49904,6 +50071,7 @@ ${c2.dim("(paste cancelled)")}
|
|
|
49904
50071
|
}
|
|
49905
50072
|
if (activeTask) {
|
|
49906
50073
|
activeTask.runner.abort();
|
|
50074
|
+
taskManager.stopAll();
|
|
49907
50075
|
writeContent(() => renderTaskAborted());
|
|
49908
50076
|
} else {
|
|
49909
50077
|
writeContent(() => process.stdout.write(`
|
|
@@ -49912,6 +50080,42 @@ ${c2.dim("(Use /quit to exit)")}
|
|
|
49912
50080
|
}
|
|
49913
50081
|
showPrompt();
|
|
49914
50082
|
});
|
|
50083
|
+
if (process.stdin.isTTY) {
|
|
50084
|
+
readline2.emitKeypressEvents(process.stdin, rl);
|
|
50085
|
+
process.stdin.on("keypress", (_str, key) => {
|
|
50086
|
+
if (!key)
|
|
50087
|
+
return;
|
|
50088
|
+
if (key.name === "escape" && activeTask) {
|
|
50089
|
+
if (!activeTask.runner.isPaused) {
|
|
50090
|
+
activeTask.runner.pause();
|
|
50091
|
+
statusBar.setProcessing(false);
|
|
50092
|
+
}
|
|
50093
|
+
const retracted = activeTask.runner.retractLastPendingMessage();
|
|
50094
|
+
if (retracted) {
|
|
50095
|
+
lastSteeringInput = retracted;
|
|
50096
|
+
lastSteeringRetracted = true;
|
|
50097
|
+
writeContent(() => process.stdout.write(` ${c2.yellow("\u23F8")} ${c2.dim("\x1B[9m" + retracted.slice(0, 120) + "\x1B[29m")} ${c2.dim("\u2190 retracted, edit below and press Enter")}
|
|
50098
|
+
`));
|
|
50099
|
+
rl.line = retracted;
|
|
50100
|
+
rl.cursor = retracted.length;
|
|
50101
|
+
if (statusBar.isActive)
|
|
50102
|
+
statusBar.renderFooterPreserveCursor?.();
|
|
50103
|
+
} else if (lastSteeringInput) {
|
|
50104
|
+
lastSteeringRetracted = true;
|
|
50105
|
+
writeContent(() => process.stdout.write(` ${c2.yellow("\u23F8")} ${c2.dim("\x1B[9m" + lastSteeringInput.slice(0, 120) + "\x1B[29m")} ${c2.dim("\u2190 already sent, re-type below to override")}
|
|
50106
|
+
`));
|
|
50107
|
+
rl.line = lastSteeringInput;
|
|
50108
|
+
rl.cursor = lastSteeringInput.length;
|
|
50109
|
+
if (statusBar.isActive)
|
|
50110
|
+
statusBar.renderFooterPreserveCursor?.();
|
|
50111
|
+
} else {
|
|
50112
|
+
writeContent(() => process.stdout.write(` ${c2.yellow("\u23F8")} ${c2.dim("Paused. /resume to continue, /stop to kill.")}
|
|
50113
|
+
`));
|
|
50114
|
+
}
|
|
50115
|
+
showPrompt();
|
|
50116
|
+
}
|
|
50117
|
+
});
|
|
50118
|
+
}
|
|
49915
50119
|
}
|
|
49916
50120
|
async function runWithTUI(task, config, repoPath) {
|
|
49917
50121
|
const repoRoot = resolve28(repoPath ?? cwd());
|
package/package.json
CHANGED