@integrity-labs/agt-cli 0.27.74 → 0.27.76
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/bin/agt.js +3 -3
- package/dist/{chunk-7YM2F3DG.js → chunk-FAR2FJBQ.js} +293 -51
- package/dist/chunk-FAR2FJBQ.js.map +1 -0
- package/dist/{chunk-CADO5HXF.js → chunk-NANSSDFG.js} +1 -1
- package/dist/{claude-pair-runtime-2XWSYFSY.js → claude-pair-runtime-EZ73GJW7.js} +2 -2
- package/dist/lib/manager-worker.js +32 -109
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/{persistent-session-S7OJTKKE.js → persistent-session-4HYXVGKO.js} +2 -2
- package/dist/responsiveness-probe-NUGYDDMS.js +73 -0
- package/dist/responsiveness-probe-NUGYDDMS.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-7YM2F3DG.js.map +0 -1
- package/dist/responsiveness-probe-LGNXOX43.js +0 -33
- package/dist/responsiveness-probe-LGNXOX43.js.map +0 -1
- /package/dist/{chunk-CADO5HXF.js.map → chunk-NANSSDFG.js.map} +0 -0
- /package/dist/{claude-pair-runtime-2XWSYFSY.js.map → claude-pair-runtime-EZ73GJW7.js.map} +0 -0
- /package/dist/{persistent-session-S7OJTKKE.js.map → persistent-session-4HYXVGKO.js.map} +0 -0
package/dist/bin/agt.js
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
success,
|
|
29
29
|
table,
|
|
30
30
|
warn
|
|
31
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-NANSSDFG.js";
|
|
32
32
|
import {
|
|
33
33
|
CHANNEL_REGISTRY,
|
|
34
34
|
DEPLOYMENT_TEMPLATES,
|
|
@@ -4930,7 +4930,7 @@ import { execFileSync, execSync } from "child_process";
|
|
|
4930
4930
|
import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
|
|
4931
4931
|
import chalk18 from "chalk";
|
|
4932
4932
|
import ora16 from "ora";
|
|
4933
|
-
var cliVersion = true ? "0.27.
|
|
4933
|
+
var cliVersion = true ? "0.27.76" : "dev";
|
|
4934
4934
|
async function fetchLatestVersion() {
|
|
4935
4935
|
const host2 = getHost();
|
|
4936
4936
|
if (!host2) return null;
|
|
@@ -5853,7 +5853,7 @@ function handleError(err) {
|
|
|
5853
5853
|
}
|
|
5854
5854
|
|
|
5855
5855
|
// src/bin/agt.ts
|
|
5856
|
-
var cliVersion2 = true ? "0.27.
|
|
5856
|
+
var cliVersion2 = true ? "0.27.76" : "dev";
|
|
5857
5857
|
var program = new Command();
|
|
5858
5858
|
program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
|
|
5859
5859
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-XWVM4KPK.js";
|
|
8
8
|
|
|
9
9
|
// src/lib/persistent-session.ts
|
|
10
|
-
import { spawn, execSync, execFileSync as
|
|
10
|
+
import { spawn, execSync, execFileSync as execFileSync3 } from "child_process";
|
|
11
11
|
import { join as join2, dirname } from "path";
|
|
12
12
|
import { homedir as homedir2, platform, userInfo } from "os";
|
|
13
13
|
import { existsSync as existsSync3, readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync3, appendFileSync, mkdirSync as mkdirSync2, chmodSync, copyFileSync, rmSync } from "fs";
|
|
@@ -279,6 +279,226 @@ function probeClaudeProcessInTmux(tmuxSession) {
|
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
// src/lib/claude-dialogs.ts
|
|
283
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
284
|
+
function isLoginPickerVisible(screen) {
|
|
285
|
+
return screen.includes("Select login method") || screen.includes("Claude account with subscription") && screen.includes("Anthropic Console account");
|
|
286
|
+
}
|
|
287
|
+
function isResumeModeDialogVisible(screen) {
|
|
288
|
+
return screen.includes("Resume from summary") && screen.includes("Don't ask me again");
|
|
289
|
+
}
|
|
290
|
+
function isSessionFeedbackDialogVisible(screen) {
|
|
291
|
+
return screen.includes("How is Claude doing this session") && screen.includes("0: Dismiss");
|
|
292
|
+
}
|
|
293
|
+
function sweepDialogs(screen) {
|
|
294
|
+
if (screen.includes("Choose the text style") || screen.includes("Dark mode") && screen.includes("Light mode")) {
|
|
295
|
+
return {
|
|
296
|
+
kind: "theme-picker",
|
|
297
|
+
keys: ["Enter"],
|
|
298
|
+
interKeyDelayMs: 0,
|
|
299
|
+
logMessage: "Auto-accepted theme picker"
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (screen.includes("Yes, I trust this folder")) {
|
|
303
|
+
return {
|
|
304
|
+
kind: "folder-trust",
|
|
305
|
+
keys: ["Enter"],
|
|
306
|
+
interKeyDelayMs: 0,
|
|
307
|
+
logMessage: "Auto-accepted folder trust"
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
if (isResumeModeDialogVisible(screen)) {
|
|
311
|
+
return {
|
|
312
|
+
kind: "resume-mode",
|
|
313
|
+
keys: ["3", "Enter"],
|
|
314
|
+
interKeyDelayMs: 300,
|
|
315
|
+
logMessage: "Auto-dismissed resume-mode dialog (picked 'Don't ask me again')"
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (screen.includes("I am using this for local development")) {
|
|
319
|
+
return {
|
|
320
|
+
kind: "dev-channels",
|
|
321
|
+
keys: ["Enter"],
|
|
322
|
+
interKeyDelayMs: 0,
|
|
323
|
+
logMessage: "Auto-accepted dev channels"
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
if (screen.includes("Enter to confirm") && screen.includes("MCP")) {
|
|
327
|
+
return {
|
|
328
|
+
kind: "mcp-servers",
|
|
329
|
+
keys: ["Enter"],
|
|
330
|
+
interKeyDelayMs: 0,
|
|
331
|
+
logMessage: "Auto-accepted MCP servers"
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
if (screen.includes("Yes, I accept") && screen.includes("Bypass Permissions")) {
|
|
335
|
+
return {
|
|
336
|
+
kind: "bypass-permissions",
|
|
337
|
+
keys: ["2", "Enter"],
|
|
338
|
+
interKeyDelayMs: 300,
|
|
339
|
+
logMessage: "Auto-accepted bypass permissions"
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
if (isSessionFeedbackDialogVisible(screen)) {
|
|
343
|
+
return {
|
|
344
|
+
kind: "session-feedback",
|
|
345
|
+
keys: ["0"],
|
|
346
|
+
interKeyDelayMs: 0,
|
|
347
|
+
logMessage: "Auto-dismissed session-feedback dialog"
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
async function sendDialogKeys(tmuxSession, action) {
|
|
353
|
+
for (let i = 0; i < action.keys.length; i++) {
|
|
354
|
+
if (i > 0 && action.interKeyDelayMs > 0) {
|
|
355
|
+
await new Promise((r) => setTimeout(r, action.interKeyDelayMs));
|
|
356
|
+
}
|
|
357
|
+
execFileSync2("tmux", ["send-keys", "-t", tmuxSession, action.keys[i]], {
|
|
358
|
+
stdio: "ignore"
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function simpleTextHash(s) {
|
|
363
|
+
let h = 0;
|
|
364
|
+
for (let i = 0; i < s.length; i++) {
|
|
365
|
+
h = (h << 5) - h + s.charCodeAt(i) | 0;
|
|
366
|
+
}
|
|
367
|
+
return h.toString(16);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/lib/channel-input-watchdog.ts
|
|
371
|
+
var STUCK_THRESHOLD_MS = 5e3;
|
|
372
|
+
var MAX_ENTER_FIRES = 3;
|
|
373
|
+
var ATTACHED_STUCK_THRESHOLD_MS = 15e3;
|
|
374
|
+
var INPUT_BOX_DIVIDER = /^[─━]{10,}/;
|
|
375
|
+
var PROMPT_PREFIX = "\u276F ";
|
|
376
|
+
function decide(pane, prev, now, config = {}) {
|
|
377
|
+
const threshold = config.stuckThresholdMs ?? STUCK_THRESHOLD_MS;
|
|
378
|
+
const maxFires = config.maxEnterFires ?? MAX_ENTER_FIRES;
|
|
379
|
+
const dialogAction = sweepDialogs(pane);
|
|
380
|
+
if (dialogAction) {
|
|
381
|
+
return { fire: false, dialog: dialogAction, next: prev };
|
|
382
|
+
}
|
|
383
|
+
const inputText = extractInputBoxText(pane);
|
|
384
|
+
if (!inputText) {
|
|
385
|
+
return { fire: false, next: void 0 };
|
|
386
|
+
}
|
|
387
|
+
if (isActivelyProcessing(pane)) {
|
|
388
|
+
return { fire: false, next: prev };
|
|
389
|
+
}
|
|
390
|
+
const hash = simpleTextHash(inputText);
|
|
391
|
+
if (!prev || prev.lastInputHash !== hash) {
|
|
392
|
+
return {
|
|
393
|
+
fire: false,
|
|
394
|
+
next: { lastInputHash: hash, firstSeenAt: now, fires: 0, lastFireAt: 0, gaveUpLogged: false }
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
if (prev.fires >= maxFires) {
|
|
398
|
+
if (!prev.gaveUpLogged) {
|
|
399
|
+
return { fire: false, gaveUp: true, next: { ...prev, gaveUpLogged: true } };
|
|
400
|
+
}
|
|
401
|
+
return { fire: false, next: prev };
|
|
402
|
+
}
|
|
403
|
+
const sinceLastAttempt = prev.fires === 0 ? now - prev.firstSeenAt : now - prev.lastFireAt;
|
|
404
|
+
if (sinceLastAttempt < threshold) return { fire: false, next: prev };
|
|
405
|
+
return {
|
|
406
|
+
fire: true,
|
|
407
|
+
next: { ...prev, fires: prev.fires + 1, lastFireAt: now }
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function extractInputBoxText(pane) {
|
|
411
|
+
const lines = pane.split("\n");
|
|
412
|
+
for (let i = 1; i < lines.length; i++) {
|
|
413
|
+
const line = lines[i] ?? "";
|
|
414
|
+
if (!line.startsWith(PROMPT_PREFIX)) continue;
|
|
415
|
+
let j = i - 1;
|
|
416
|
+
while (j >= 0 && (lines[j] ?? "").trim() === "") j--;
|
|
417
|
+
if (j < 0) continue;
|
|
418
|
+
if (!INPUT_BOX_DIVIDER.test((lines[j] ?? "").trim())) continue;
|
|
419
|
+
const text = line.slice(PROMPT_PREFIX.length).trim();
|
|
420
|
+
return text.length > 0 ? text : null;
|
|
421
|
+
}
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
function isActivelyProcessing(pane) {
|
|
425
|
+
const lines = pane.split("\n");
|
|
426
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
427
|
+
const line = (lines[i] ?? "").trim();
|
|
428
|
+
if (!line.startsWith("\u273B")) continue;
|
|
429
|
+
if (/\bfor\s+\d+s\s*$/.test(line)) return false;
|
|
430
|
+
if (/\b\w+ing[…\.]{0,3}\s*$/i.test(line)) return true;
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
function checkChannelInputs(codeNames, io, config = {}, states = sharedStates) {
|
|
436
|
+
const live = new Set(codeNames);
|
|
437
|
+
for (const codeName of codeNames) {
|
|
438
|
+
try {
|
|
439
|
+
checkOne(codeName, io, config, states);
|
|
440
|
+
} catch (err) {
|
|
441
|
+
io.log(`[channel-input-watchdog] '${codeName}': ${err.message}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
for (const key of [...states.keys()]) {
|
|
445
|
+
if (!live.has(key)) states.delete(key);
|
|
446
|
+
}
|
|
447
|
+
for (const key of [...giveUpCounts.keys()]) {
|
|
448
|
+
if (!live.has(key)) giveUpCounts.delete(key);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function checkOne(codeName, io, config, states) {
|
|
452
|
+
const pane = io.capturePane(codeName);
|
|
453
|
+
if (!pane) {
|
|
454
|
+
states.delete(codeName);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const attached = io.isClientAttached(codeName);
|
|
458
|
+
const effectiveConfig = attached ? {
|
|
459
|
+
...config,
|
|
460
|
+
stuckThresholdMs: config.attachedStuckThresholdMs ?? ATTACHED_STUCK_THRESHOLD_MS
|
|
461
|
+
} : config;
|
|
462
|
+
const prev = states.get(codeName);
|
|
463
|
+
const { fire, dialog, gaveUp, next } = decide(pane, prev, io.now(), effectiveConfig);
|
|
464
|
+
if (next === void 0) {
|
|
465
|
+
states.delete(codeName);
|
|
466
|
+
} else {
|
|
467
|
+
states.set(codeName, next);
|
|
468
|
+
}
|
|
469
|
+
if (dialog) {
|
|
470
|
+
io.log(
|
|
471
|
+
`[channel-input-watchdog] '${codeName}': ${dialog.logMessage} (dialog was blocking the input box)`
|
|
472
|
+
);
|
|
473
|
+
io.sendKeys(codeName, dialog.keys, dialog.interKeyDelayMs);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const text = extractInputBoxText(pane) ?? "";
|
|
477
|
+
const hash = next?.lastInputHash ?? simpleTextHash(text);
|
|
478
|
+
if (gaveUp) {
|
|
479
|
+
const maxFires = effectiveConfig.maxEnterFires ?? MAX_ENTER_FIRES;
|
|
480
|
+
io.log(
|
|
481
|
+
`[channel-input-watchdog] '${codeName}': GIVING UP after ${maxFires} Enter attempts \u2014 input remains unsubmitted (input_hash=${hash}, len=${text.length})`
|
|
482
|
+
);
|
|
483
|
+
giveUpCounts.set(codeName, (giveUpCounts.get(codeName) ?? 0) + 1);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (fire) {
|
|
487
|
+
const maxFires = effectiveConfig.maxEnterFires ?? MAX_ENTER_FIRES;
|
|
488
|
+
io.log(
|
|
489
|
+
`[channel-input-watchdog] '${codeName}': stuck channel input \u2014 firing Enter (attempt ${next?.fires ?? 1}/${maxFires}, input_hash=${hash}, len=${text.length})`
|
|
490
|
+
);
|
|
491
|
+
io.sendEnter(codeName);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
var sharedStates = /* @__PURE__ */ new Map();
|
|
495
|
+
var giveUpCounts = /* @__PURE__ */ new Map();
|
|
496
|
+
function takeWatchdogGiveUpCount(codeName) {
|
|
497
|
+
const count = giveUpCounts.get(codeName) ?? 0;
|
|
498
|
+
giveUpCounts.delete(codeName);
|
|
499
|
+
return count;
|
|
500
|
+
}
|
|
501
|
+
|
|
282
502
|
// src/lib/persistent-session.ts
|
|
283
503
|
function syncClaudeCredsToRoot() {
|
|
284
504
|
if (platform() !== "linux") return true;
|
|
@@ -405,7 +625,7 @@ function getAcpxBin() {
|
|
|
405
625
|
}
|
|
406
626
|
function defaultAcpxSessionProbe(acpxBin, projectDir) {
|
|
407
627
|
try {
|
|
408
|
-
|
|
628
|
+
execFileSync3(acpxBin, ["claude", "list-sessions"], {
|
|
409
629
|
cwd: projectDir,
|
|
410
630
|
timeout: 5e3,
|
|
411
631
|
stdio: "ignore"
|
|
@@ -655,12 +875,6 @@ function spawnSession(config, session) {
|
|
|
655
875
|
session.restartCount++;
|
|
656
876
|
}
|
|
657
877
|
}
|
|
658
|
-
function isLoginPickerVisible(screen) {
|
|
659
|
-
return screen.includes("Select login method") || screen.includes("Claude account with subscription") && screen.includes("Anthropic Console account");
|
|
660
|
-
}
|
|
661
|
-
function isResumeModeDialogVisible(screen) {
|
|
662
|
-
return screen.includes("Resume from summary") && screen.includes("Don't ask me again");
|
|
663
|
-
}
|
|
664
878
|
function hasMcpChildren(tmuxSession) {
|
|
665
879
|
try {
|
|
666
880
|
const claudePidOut = execSync(
|
|
@@ -710,40 +924,10 @@ async function acceptDialogs(tmuxSession, codeName, log, primaryModel = null, se
|
|
|
710
924
|
continue;
|
|
711
925
|
}
|
|
712
926
|
dialogIterations++;
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
718
|
-
if (screen.includes("Yes, I trust this folder")) {
|
|
719
|
-
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
720
|
-
log(`[persistent-session] Auto-accepted workspace trust for '${codeName}'`);
|
|
721
|
-
continue;
|
|
722
|
-
}
|
|
723
|
-
if (isResumeModeDialogVisible(screen)) {
|
|
724
|
-
execFileSync2("tmux", ["send-keys", "-t", tmuxSession, "3"], { stdio: "ignore" });
|
|
725
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
726
|
-
execFileSync2("tmux", ["send-keys", "-t", tmuxSession, "Enter"], { stdio: "ignore" });
|
|
727
|
-
log(
|
|
728
|
-
`[persistent-session] Auto-dismissed resume-mode dialog for '${codeName}' (picked 'Don't ask me again')`
|
|
729
|
-
);
|
|
730
|
-
continue;
|
|
731
|
-
}
|
|
732
|
-
if (screen.includes("I am using this for local development")) {
|
|
733
|
-
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
734
|
-
log(`[persistent-session] Auto-accepted dev channels for '${codeName}'`);
|
|
735
|
-
continue;
|
|
736
|
-
}
|
|
737
|
-
if (screen.includes("Enter to confirm") && screen.includes("MCP")) {
|
|
738
|
-
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
739
|
-
log(`[persistent-session] Auto-accepted MCP servers for '${codeName}'`);
|
|
740
|
-
continue;
|
|
741
|
-
}
|
|
742
|
-
if (screen.includes("Yes, I accept") && screen.includes("Bypass Permissions")) {
|
|
743
|
-
execSync(`tmux send-keys -t ${tmuxSession} 2`, { stdio: "ignore" });
|
|
744
|
-
await new Promise((r) => setTimeout(r, 300));
|
|
745
|
-
execSync(`tmux send-keys -t ${tmuxSession} Enter`, { stdio: "ignore" });
|
|
746
|
-
log(`[persistent-session] Auto-accepted bypass permissions for '${codeName}'`);
|
|
927
|
+
const dialogAction = sweepDialogs(screen);
|
|
928
|
+
if (dialogAction) {
|
|
929
|
+
await sendDialogKeys(tmuxSession, dialogAction);
|
|
930
|
+
log(`[persistent-session] ${dialogAction.logMessage} for '${codeName}'`);
|
|
747
931
|
continue;
|
|
748
932
|
}
|
|
749
933
|
if (screen.includes("\u276F") && !screen.includes("Enter to confirm")) {
|
|
@@ -794,7 +978,7 @@ async function waitForPromptReady(tmuxSession) {
|
|
|
794
978
|
const deadline = Date.now() + 1e4;
|
|
795
979
|
while (Date.now() < deadline) {
|
|
796
980
|
try {
|
|
797
|
-
const screen =
|
|
981
|
+
const screen = execFileSync3(
|
|
798
982
|
"tmux",
|
|
799
983
|
["capture-pane", "-t", tmuxSession, "-p"],
|
|
800
984
|
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
|
|
@@ -813,11 +997,11 @@ function sleepBlockingMs(ms) {
|
|
|
813
997
|
}
|
|
814
998
|
function defaultArmSender(tmuxSession, command) {
|
|
815
999
|
try {
|
|
816
|
-
|
|
1000
|
+
execFileSync3("tmux", ["send-keys", "-t", tmuxSession, "-l", command], {
|
|
817
1001
|
stdio: ["ignore", "ignore", "pipe"]
|
|
818
1002
|
});
|
|
819
1003
|
sleepBlockingMs(SEND_KEYS_ENTER_DELAY_MS);
|
|
820
|
-
|
|
1004
|
+
execFileSync3("tmux", ["send-keys", "-t", tmuxSession, "Enter"], {
|
|
821
1005
|
stdio: ["ignore", "ignore", "pipe"]
|
|
822
1006
|
});
|
|
823
1007
|
return true;
|
|
@@ -826,6 +1010,50 @@ function defaultArmSender(tmuxSession, command) {
|
|
|
826
1010
|
}
|
|
827
1011
|
}
|
|
828
1012
|
var armSender = defaultArmSender;
|
|
1013
|
+
function defaultPaneCapture(tmuxSession) {
|
|
1014
|
+
try {
|
|
1015
|
+
return execFileSync3("tmux", ["capture-pane", "-t", tmuxSession, "-p"], {
|
|
1016
|
+
encoding: "utf-8",
|
|
1017
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1018
|
+
timeout: 2e3
|
|
1019
|
+
});
|
|
1020
|
+
} catch {
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
var paneCapture = defaultPaneCapture;
|
|
1025
|
+
var defaultHygieneKeySender = async (tmuxSession, keys, interKeyDelayMs) => {
|
|
1026
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1027
|
+
if (i > 0 && interKeyDelayMs > 0) {
|
|
1028
|
+
await new Promise((r) => setTimeout(r, interKeyDelayMs));
|
|
1029
|
+
}
|
|
1030
|
+
execFileSync3("tmux", ["send-keys", "-t", tmuxSession, keys[i]], {
|
|
1031
|
+
stdio: "ignore"
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
var hygieneKeySender = defaultHygieneKeySender;
|
|
1036
|
+
async function preSendPaneHygiene(tmuxSession, codeName, log) {
|
|
1037
|
+
try {
|
|
1038
|
+
let screen = paneCapture(tmuxSession);
|
|
1039
|
+
if (screen === null) return;
|
|
1040
|
+
const action = sweepDialogs(screen);
|
|
1041
|
+
if (action) {
|
|
1042
|
+
await hygieneKeySender(tmuxSession, action.keys, action.interKeyDelayMs);
|
|
1043
|
+
log(`[inject] ${action.logMessage} for '${codeName}' before injection`);
|
|
1044
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
1045
|
+
screen = paneCapture(tmuxSession) ?? "";
|
|
1046
|
+
}
|
|
1047
|
+
const orphan = extractInputBoxText(screen);
|
|
1048
|
+
if (orphan) {
|
|
1049
|
+
log(
|
|
1050
|
+
`[inject] clearing orphaned input for '${codeName}' before injection (input_hash=${simpleTextHash(orphan)}, len=${orphan.length})`
|
|
1051
|
+
);
|
|
1052
|
+
await hygieneKeySender(tmuxSession, ["C-u"], 0);
|
|
1053
|
+
}
|
|
1054
|
+
} catch {
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
829
1057
|
function sendToAgent(tmuxSession, command) {
|
|
830
1058
|
return armSender(tmuxSession, command);
|
|
831
1059
|
}
|
|
@@ -848,6 +1076,17 @@ var _internals = {
|
|
|
848
1076
|
__setArmSender(fn) {
|
|
849
1077
|
armSender = fn ?? defaultArmSender;
|
|
850
1078
|
},
|
|
1079
|
+
// ENG-6017 test seam: swap the pane capture used by the inject-time
|
|
1080
|
+
// hygiene so unit tests can simulate dialog overlays / orphaned input
|
|
1081
|
+
// without a tmux server. null restores the real capture.
|
|
1082
|
+
__setPaneCapture(fn) {
|
|
1083
|
+
paneCapture = fn ?? defaultPaneCapture;
|
|
1084
|
+
},
|
|
1085
|
+
// ENG-6017 test seam: swap the hygiene key sender (dialog dismissal +
|
|
1086
|
+
// C-u clear) so unit tests can assert keystrokes without tmux.
|
|
1087
|
+
__setHygieneKeySender(fn) {
|
|
1088
|
+
hygieneKeySender = fn ?? defaultHygieneKeySender;
|
|
1089
|
+
},
|
|
851
1090
|
// ENG-5758 test seam: swap the acpx session probe so unit tests can
|
|
852
1091
|
// simulate "session present" / "no session" without spawning acpx.
|
|
853
1092
|
__setAcpxSessionProbe(fn) {
|
|
@@ -922,6 +1161,7 @@ async function injectMessageWithStatus(codeName, type, content, meta, log) {
|
|
|
922
1161
|
_log(`[inject] acpx binary not found \u2014 falling back to tmux send-keys`);
|
|
923
1162
|
}
|
|
924
1163
|
const singleLineText = text.replace(/\s*\n+\s*/g, " ").trim();
|
|
1164
|
+
await preSendPaneHygiene(`agt-${codeName}`, codeName, _log);
|
|
925
1165
|
const sent = sendToAgent(`agt-${codeName}`, singleLineText);
|
|
926
1166
|
if (sent) {
|
|
927
1167
|
_log(`[inject] tmux send-keys sent for '${codeName}' \u2014 unverified (delivered=false, fallbackUsed=true)`);
|
|
@@ -942,7 +1182,7 @@ function stopPersistentSession(codeName, log) {
|
|
|
942
1182
|
try {
|
|
943
1183
|
const acpx = getAcpxBin();
|
|
944
1184
|
if (acpx) {
|
|
945
|
-
|
|
1185
|
+
execFileSync3(acpx, ["claude", "sessions", "close", `agt-${codeName}`], {
|
|
946
1186
|
cwd: getProjectDir(codeName),
|
|
947
1187
|
timeout: 5e3,
|
|
948
1188
|
stdio: "ignore"
|
|
@@ -1019,7 +1259,7 @@ function isSessionHealthy(codeName) {
|
|
|
1019
1259
|
if (!claudeAlive) {
|
|
1020
1260
|
const paneTail = readPaneLogTail(codeName);
|
|
1021
1261
|
try {
|
|
1022
|
-
|
|
1262
|
+
execFileSync3("tmux", ["kill-session", "-t", tmuxSession], { stdio: "ignore" });
|
|
1023
1263
|
} catch {
|
|
1024
1264
|
}
|
|
1025
1265
|
session.status = "crashed";
|
|
@@ -1059,13 +1299,13 @@ function collectDiagnostics(codeNames) {
|
|
|
1059
1299
|
let launchArgs = null;
|
|
1060
1300
|
let channelStatus = null;
|
|
1061
1301
|
try {
|
|
1062
|
-
|
|
1302
|
+
execFileSync3("tmux", ["has-session", "-t", tmuxSession], { stdio: "ignore" });
|
|
1063
1303
|
tmuxAlive = true;
|
|
1064
1304
|
} catch {
|
|
1065
1305
|
}
|
|
1066
1306
|
if (tmuxAlive) {
|
|
1067
1307
|
try {
|
|
1068
|
-
screenCapture =
|
|
1308
|
+
screenCapture = execFileSync3("tmux", ["capture-pane", "-t", tmuxSession, "-p", "-S", "-30"], {
|
|
1069
1309
|
encoding: "utf-8",
|
|
1070
1310
|
timeout: 3e3
|
|
1071
1311
|
}).trim();
|
|
@@ -1073,7 +1313,7 @@ function collectDiagnostics(codeNames) {
|
|
|
1073
1313
|
}
|
|
1074
1314
|
}
|
|
1075
1315
|
try {
|
|
1076
|
-
const psOutput =
|
|
1316
|
+
const psOutput = execFileSync3("ps", ["aux"], { encoding: "utf-8", timeout: 3e3 });
|
|
1077
1317
|
const line = psOutput.split("\n").find((l) => l.includes(`agt-${codeName}`) && !l.includes("grep"));
|
|
1078
1318
|
if (line) {
|
|
1079
1319
|
const match = line.match(/claude\s+.*/);
|
|
@@ -1187,6 +1427,8 @@ export {
|
|
|
1187
1427
|
isAgentIdle,
|
|
1188
1428
|
isStaleForToday,
|
|
1189
1429
|
peekCurrentSession,
|
|
1430
|
+
checkChannelInputs,
|
|
1431
|
+
takeWatchdogGiveUpCount,
|
|
1190
1432
|
resolveClaudeBinary,
|
|
1191
1433
|
writePersistentClaudeWrapper,
|
|
1192
1434
|
paneLogPath,
|
|
@@ -1210,4 +1452,4 @@ export {
|
|
|
1210
1452
|
stopAllSessionsAndWait,
|
|
1211
1453
|
getProjectDir
|
|
1212
1454
|
};
|
|
1213
|
-
//# sourceMappingURL=chunk-
|
|
1455
|
+
//# sourceMappingURL=chunk-FAR2FJBQ.js.map
|