ragent-cli 1.11.11 → 1.11.13
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 +521 -178
- package/dist/sbom.json +5 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "ragent-cli",
|
|
34
|
-
version: "1.11.
|
|
34
|
+
version: "1.11.13",
|
|
35
35
|
description: "CLI agent for rAgent Live \u2014 browser-first terminal control plane for AI coding agents",
|
|
36
36
|
main: "dist/index.js",
|
|
37
37
|
bin: {
|
|
@@ -1991,20 +1991,43 @@ function setBlock(turns, turnId, blockId, block) {
|
|
|
1991
1991
|
};
|
|
1992
1992
|
}
|
|
1993
1993
|
var MAX_SNAPSHOT_TURNS = 1e3;
|
|
1994
|
+
var MAX_SNAPSHOT_SIZE_CHARS = 45e4;
|
|
1994
1995
|
function trimSnapshot(snapshot, maxTurns = MAX_SNAPSHOT_TURNS) {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
1996
|
+
let currentSnapshot = snapshot;
|
|
1997
|
+
if (currentSnapshot.turnOrder.length > maxTurns) {
|
|
1998
|
+
const keptOrder = currentSnapshot.turnOrder.slice(-maxTurns);
|
|
1999
|
+
const keptTurns = {};
|
|
2000
|
+
for (const id of keptOrder) {
|
|
2001
|
+
if (currentSnapshot.turns[id]) {
|
|
2002
|
+
keptTurns[id] = currentSnapshot.turns[id];
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
currentSnapshot = {
|
|
2006
|
+
...currentSnapshot,
|
|
2007
|
+
turnOrder: keptOrder,
|
|
2008
|
+
turns: keptTurns
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
let serialized = JSON.stringify(currentSnapshot);
|
|
2012
|
+
if (serialized.length <= MAX_SNAPSHOT_SIZE_CHARS) {
|
|
2013
|
+
return currentSnapshot;
|
|
2014
|
+
}
|
|
2015
|
+
while (currentSnapshot.turnOrder.length > 1 && serialized.length > MAX_SNAPSHOT_SIZE_CHARS) {
|
|
2016
|
+
const keptOrder = currentSnapshot.turnOrder.slice(1);
|
|
2017
|
+
const keptTurns = {};
|
|
2018
|
+
for (const id of keptOrder) {
|
|
2019
|
+
if (currentSnapshot.turns[id]) {
|
|
2020
|
+
keptTurns[id] = currentSnapshot.turns[id];
|
|
2021
|
+
}
|
|
2001
2022
|
}
|
|
2023
|
+
currentSnapshot = {
|
|
2024
|
+
...currentSnapshot,
|
|
2025
|
+
turnOrder: keptOrder,
|
|
2026
|
+
turns: keptTurns
|
|
2027
|
+
};
|
|
2028
|
+
serialized = JSON.stringify(currentSnapshot);
|
|
2002
2029
|
}
|
|
2003
|
-
return
|
|
2004
|
-
...snapshot,
|
|
2005
|
-
turnOrder: keptOrder,
|
|
2006
|
-
turns: keptTurns
|
|
2007
|
-
};
|
|
2030
|
+
return currentSnapshot;
|
|
2008
2031
|
}
|
|
2009
2032
|
|
|
2010
2033
|
// src/native-stream-manager.ts
|
|
@@ -2541,6 +2564,8 @@ function releaseSessionSource(sessionId) {
|
|
|
2541
2564
|
|
|
2542
2565
|
// src/transcript-watcher.ts
|
|
2543
2566
|
var log7 = createLogger("transcript-watcher");
|
|
2567
|
+
var cachedTicksPerSecond = null;
|
|
2568
|
+
var transcriptDiscoveryCache = /* @__PURE__ */ new Map();
|
|
2544
2569
|
var ClaudeCodeParser = class {
|
|
2545
2570
|
name = "claude-code";
|
|
2546
2571
|
/** Maps tool_use id → tool name for matching results back to tools. */
|
|
@@ -3054,6 +3079,13 @@ function discoverTranscriptForPid(pid, agentType) {
|
|
|
3054
3079
|
}
|
|
3055
3080
|
}
|
|
3056
3081
|
function listChildPids(pid) {
|
|
3082
|
+
if (os3.platform() !== "darwin") {
|
|
3083
|
+
try {
|
|
3084
|
+
const content = fs2.readFileSync(`/proc/${pid}/task/${pid}/children`, "utf8");
|
|
3085
|
+
return content.trim().split(/\s+/).map((value) => parseInt(value, 10)).filter((value) => Number.isFinite(value) && value > 0);
|
|
3086
|
+
} catch {
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3057
3089
|
try {
|
|
3058
3090
|
return (0, import_child_process2.execFileSync)("pgrep", ["-P", String(pid)], {
|
|
3059
3091
|
encoding: "utf-8",
|
|
@@ -3064,6 +3096,16 @@ function listChildPids(pid) {
|
|
|
3064
3096
|
}
|
|
3065
3097
|
}
|
|
3066
3098
|
function getProcessCommand(pid) {
|
|
3099
|
+
if (os3.platform() !== "darwin") {
|
|
3100
|
+
try {
|
|
3101
|
+
const raw = fs2.readFileSync(`/proc/${pid}/cmdline`, "utf8");
|
|
3102
|
+
const parts = raw.split("\0").filter(Boolean);
|
|
3103
|
+
if (parts.length > 0) {
|
|
3104
|
+
return parts.join(" ");
|
|
3105
|
+
}
|
|
3106
|
+
} catch {
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3067
3109
|
try {
|
|
3068
3110
|
return (0, import_child_process2.execFileSync)("ps", ["-p", String(pid), "-o", "args="], {
|
|
3069
3111
|
encoding: "utf-8",
|
|
@@ -3105,10 +3147,13 @@ function getProcessStartEpochMs(pid) {
|
|
|
3105
3147
|
const bootLine = procStat.split("\n").find((line) => line.startsWith("btime "));
|
|
3106
3148
|
const bootSeconds = Number(bootLine?.split(/\s+/)[1]);
|
|
3107
3149
|
if (!Number.isFinite(bootSeconds) || bootSeconds <= 0) return null;
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3150
|
+
if (cachedTicksPerSecond === null) {
|
|
3151
|
+
cachedTicksPerSecond = Number((0, import_child_process2.execFileSync)("getconf", ["CLK_TCK"], {
|
|
3152
|
+
encoding: "utf-8",
|
|
3153
|
+
timeout: 3e3
|
|
3154
|
+
}).trim());
|
|
3155
|
+
}
|
|
3156
|
+
const ticksPerSecond = cachedTicksPerSecond;
|
|
3112
3157
|
if (!Number.isFinite(ticksPerSecond) || ticksPerSecond <= 0) return null;
|
|
3113
3158
|
return (bootSeconds + startTicks / ticksPerSecond) * 1e3;
|
|
3114
3159
|
} catch {
|
|
@@ -3254,29 +3299,66 @@ function discoverAntigravityTranscriptFromHome(pid, minMtimeMs) {
|
|
|
3254
3299
|
const brainDir = path2.join(userHome, ".gemini", "antigravity-cli", "brain");
|
|
3255
3300
|
if (!fs2.existsSync(brainDir)) return null;
|
|
3256
3301
|
const matches = [];
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
entries = fs2.readdirSync(current, { withFileTypes: true });
|
|
3266
|
-
} catch {
|
|
3267
|
-
continue;
|
|
3268
|
-
}
|
|
3269
|
-
for (const entry of entries) {
|
|
3270
|
-
const fullPath = path2.join(current, entry.name);
|
|
3271
|
-
if (entry.isDirectory()) {
|
|
3272
|
-
stack.push(fullPath);
|
|
3273
|
-
} else if (entry.isFile() && entry.name === "transcript.jsonl") {
|
|
3274
|
-
matches.push(fullPath);
|
|
3302
|
+
try {
|
|
3303
|
+
const folders = fs2.readdirSync(brainDir, { withFileTypes: true });
|
|
3304
|
+
for (const folder of folders) {
|
|
3305
|
+
if (folder.isDirectory()) {
|
|
3306
|
+
const transcriptPath = path2.join(brainDir, folder.name, ".system_generated", "logs", "transcript.jsonl");
|
|
3307
|
+
if (fs2.existsSync(transcriptPath)) {
|
|
3308
|
+
matches.push(transcriptPath);
|
|
3309
|
+
}
|
|
3275
3310
|
}
|
|
3276
3311
|
}
|
|
3312
|
+
} catch {
|
|
3277
3313
|
}
|
|
3278
3314
|
return getNewestPath(matches, { minMtimeMs });
|
|
3279
3315
|
}
|
|
3316
|
+
function discoverAntigravityViaCwd(paneCwd, pid) {
|
|
3317
|
+
const userHome = resolveUserHome(pid);
|
|
3318
|
+
if (!userHome) return null;
|
|
3319
|
+
const historyFile = path2.join(userHome, ".gemini", "antigravity-cli", "history.jsonl");
|
|
3320
|
+
if (!fs2.existsSync(historyFile)) return null;
|
|
3321
|
+
let targetCwd = paneCwd;
|
|
3322
|
+
try {
|
|
3323
|
+
targetCwd = fs2.realpathSync(paneCwd);
|
|
3324
|
+
} catch {
|
|
3325
|
+
}
|
|
3326
|
+
try {
|
|
3327
|
+
const content = fs2.readFileSync(historyFile, "utf-8");
|
|
3328
|
+
const lines = content.split("\n").filter(Boolean).reverse();
|
|
3329
|
+
for (const line of lines) {
|
|
3330
|
+
try {
|
|
3331
|
+
const entry = JSON.parse(line);
|
|
3332
|
+
if (entry.workspace) {
|
|
3333
|
+
let entryCwd = entry.workspace;
|
|
3334
|
+
try {
|
|
3335
|
+
entryCwd = fs2.realpathSync(entry.workspace);
|
|
3336
|
+
} catch {
|
|
3337
|
+
}
|
|
3338
|
+
if (entryCwd === targetCwd && entry.conversationId) {
|
|
3339
|
+
const transcriptPath = path2.join(
|
|
3340
|
+
userHome,
|
|
3341
|
+
".gemini",
|
|
3342
|
+
"antigravity-cli",
|
|
3343
|
+
"brain",
|
|
3344
|
+
entry.conversationId,
|
|
3345
|
+
".system_generated",
|
|
3346
|
+
"logs",
|
|
3347
|
+
"transcript.jsonl"
|
|
3348
|
+
);
|
|
3349
|
+
if (fs2.existsSync(transcriptPath)) {
|
|
3350
|
+
return transcriptPath;
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
} catch {
|
|
3355
|
+
continue;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
} catch {
|
|
3359
|
+
}
|
|
3360
|
+
return null;
|
|
3361
|
+
}
|
|
3280
3362
|
function discoverViaCwd(paneCwd, pid, minMtimeMs) {
|
|
3281
3363
|
const userHome = resolveUserHome(pid);
|
|
3282
3364
|
if (!userHome) return null;
|
|
@@ -3296,31 +3378,72 @@ function discoverViaCwd(paneCwd, pid, minMtimeMs) {
|
|
|
3296
3378
|
return null;
|
|
3297
3379
|
}
|
|
3298
3380
|
}
|
|
3299
|
-
function discoverTranscriptFile(sessionId, agentType) {
|
|
3381
|
+
function discoverTranscriptFile(sessionId, agentType, forceRefresh = false) {
|
|
3382
|
+
const now2 = Date.now();
|
|
3383
|
+
const cacheKey = `${sessionId}:${agentType || ""}`;
|
|
3384
|
+
if (!forceRefresh) {
|
|
3385
|
+
const cached = transcriptDiscoveryCache.get(cacheKey);
|
|
3386
|
+
if (cached && cached.expiresAt > now2) {
|
|
3387
|
+
return cached.value;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
const result = discoverTranscriptFileImpl(sessionId, agentType);
|
|
3391
|
+
const isProcessSession = sessionId.startsWith("process:");
|
|
3392
|
+
const ttl = result ? isProcessSession ? 3600 * 1e3 : 5e3 : 5e3;
|
|
3393
|
+
transcriptDiscoveryCache.set(cacheKey, {
|
|
3394
|
+
value: result,
|
|
3395
|
+
expiresAt: Date.now() + ttl
|
|
3396
|
+
});
|
|
3397
|
+
return result;
|
|
3398
|
+
}
|
|
3399
|
+
function discoverTranscriptFileImpl(sessionId, agentType) {
|
|
3300
3400
|
const processPid = parseProcessPid(sessionId);
|
|
3301
3401
|
if (processPid) {
|
|
3402
|
+
const t0 = performance.now();
|
|
3302
3403
|
const procResult = discoverViaProc(processPid, agentType);
|
|
3303
|
-
if (procResult)
|
|
3404
|
+
if (procResult) {
|
|
3405
|
+
console.log(`[DISCOVER] discoverViaProc found file for PID ${processPid} in ${(performance.now() - t0).toFixed(2)} ms`);
|
|
3406
|
+
return procResult;
|
|
3407
|
+
}
|
|
3304
3408
|
if (agentType === "Claude Code") {
|
|
3409
|
+
const t1 = performance.now();
|
|
3305
3410
|
const startMs = getProcessStartEpochMs(processPid);
|
|
3306
3411
|
const cwdResult = discoverViaProcessCwd(processPid, startMs ? startMs - 6e4 : void 0);
|
|
3412
|
+
console.log(`[DISCOVER] Claude Code discovery for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3307
3413
|
if (cwdResult) return cwdResult;
|
|
3308
3414
|
}
|
|
3309
3415
|
if (agentType === "Codex CLI") {
|
|
3416
|
+
const t1 = performance.now();
|
|
3310
3417
|
const startMs = getProcessStartEpochMs(processPid);
|
|
3311
3418
|
const command = getProcessCommand(processPid);
|
|
3312
3419
|
if (commandMatchesAgent(command, agentType)) {
|
|
3313
3420
|
const codexResult = discoverCodexTranscriptFromHome(processPid, startMs ? startMs - 6e4 : void 0);
|
|
3421
|
+
console.log(`[DISCOVER] Codex CLI discovery for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3314
3422
|
if (codexResult) return codexResult;
|
|
3315
3423
|
}
|
|
3316
3424
|
}
|
|
3317
3425
|
if (agentType === "Gemini CLI") {
|
|
3426
|
+
const t1 = performance.now();
|
|
3318
3427
|
const geminiResult = discoverGeminiTranscriptFromHome(processPid);
|
|
3428
|
+
console.log(`[DISCOVER] Gemini CLI discovery for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3319
3429
|
if (geminiResult) return geminiResult;
|
|
3320
3430
|
}
|
|
3321
3431
|
if (agentType === "Antigravity CLI") {
|
|
3432
|
+
const t1 = performance.now();
|
|
3322
3433
|
const startMs = getProcessStartEpochMs(processPid);
|
|
3434
|
+
try {
|
|
3435
|
+
const processCwd = fs2.readlinkSync(`/proc/${processPid}/cwd`);
|
|
3436
|
+
if (processCwd) {
|
|
3437
|
+
const cwdResult = discoverAntigravityViaCwd(processCwd, processPid);
|
|
3438
|
+
if (cwdResult) {
|
|
3439
|
+
console.log(`[DISCOVER] Antigravity CLI discovery (CWD path) for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3440
|
+
return cwdResult;
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
} catch {
|
|
3444
|
+
}
|
|
3323
3445
|
const antigravityResult = discoverAntigravityTranscriptFromHome(processPid, startMs ? startMs - 6e4 : void 0);
|
|
3446
|
+
console.log(`[DISCOVER] Antigravity CLI discovery (home path) for PID ${processPid} took ${(performance.now() - t1).toFixed(2)} ms`);
|
|
3324
3447
|
if (antigravityResult) return antigravityResult;
|
|
3325
3448
|
}
|
|
3326
3449
|
return null;
|
|
@@ -3380,6 +3503,34 @@ function discoverTranscriptFile(sessionId, agentType) {
|
|
|
3380
3503
|
if (agentType === "Gemini CLI") {
|
|
3381
3504
|
return discoverGeminiTranscriptFromHome(panePid ?? void 0);
|
|
3382
3505
|
}
|
|
3506
|
+
if (agentType === "Antigravity CLI") {
|
|
3507
|
+
const agentInfos = panePid ? findAgentProcessInfos(panePid, agentType) : [];
|
|
3508
|
+
for (const info of agentInfos) {
|
|
3509
|
+
if (info.cwd) {
|
|
3510
|
+
const cwdResult = discoverAntigravityViaCwd(info.cwd, info.pid);
|
|
3511
|
+
if (cwdResult) return cwdResult;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
try {
|
|
3515
|
+
const paneCwd = (0, import_child_process2.execFileSync)(
|
|
3516
|
+
"tmux",
|
|
3517
|
+
["display-message", "-t", paneTarget, "-p", "#{pane_current_path}"],
|
|
3518
|
+
{ env: cleanEnv, timeout: 5e3, encoding: "utf-8" }
|
|
3519
|
+
).trim();
|
|
3520
|
+
if (paneCwd) {
|
|
3521
|
+
const cwdResult = discoverAntigravityViaCwd(paneCwd, panePid ?? void 0);
|
|
3522
|
+
if (cwdResult) return cwdResult;
|
|
3523
|
+
}
|
|
3524
|
+
} catch {
|
|
3525
|
+
}
|
|
3526
|
+
const newestForAgent = getNewestPath(
|
|
3527
|
+
agentInfos.map((info) => discoverAntigravityTranscriptFromHome(
|
|
3528
|
+
info.pid,
|
|
3529
|
+
info.startEpochMs ? info.startEpochMs - 6e4 : void 0
|
|
3530
|
+
)).filter((value) => Boolean(value))
|
|
3531
|
+
);
|
|
3532
|
+
if (newestForAgent) return newestForAgent;
|
|
3533
|
+
}
|
|
3383
3534
|
return null;
|
|
3384
3535
|
}
|
|
3385
3536
|
return null;
|
|
@@ -3387,6 +3538,7 @@ function discoverTranscriptFile(sessionId, agentType) {
|
|
|
3387
3538
|
var MAX_PARTIAL_BUFFER = 256 * 1024;
|
|
3388
3539
|
var MAX_REPLAY_TURNS = 1e3;
|
|
3389
3540
|
var POLL_INTERVAL_MS = 800;
|
|
3541
|
+
var DOC_READ_DEBOUNCE_MS = 200;
|
|
3390
3542
|
var REDISCOVERY_INTERVAL_MS = 5e3;
|
|
3391
3543
|
var ENABLE_RETRY_DELAYS_MS = [1e3, 2e3, 5e3];
|
|
3392
3544
|
var TranscriptWatcher = class {
|
|
@@ -3403,6 +3555,7 @@ var TranscriptWatcher = class {
|
|
|
3403
3555
|
snapshot;
|
|
3404
3556
|
watcher = null;
|
|
3405
3557
|
pollTimer = null;
|
|
3558
|
+
docReadDebounceTimer = null;
|
|
3406
3559
|
stopped = false;
|
|
3407
3560
|
subscriberCount = 0;
|
|
3408
3561
|
documentFingerprint = "";
|
|
@@ -3457,6 +3610,10 @@ var TranscriptWatcher = class {
|
|
|
3457
3610
|
clearInterval(this.pollTimer);
|
|
3458
3611
|
this.pollTimer = null;
|
|
3459
3612
|
}
|
|
3613
|
+
if (this.docReadDebounceTimer) {
|
|
3614
|
+
clearTimeout(this.docReadDebounceTimer);
|
|
3615
|
+
this.docReadDebounceTimer = null;
|
|
3616
|
+
}
|
|
3460
3617
|
}
|
|
3461
3618
|
/** Get accumulated turns for replay (up to MAX_REPLAY_TURNS). */
|
|
3462
3619
|
getReplayTurns(fromSeq) {
|
|
@@ -3515,7 +3672,16 @@ var TranscriptWatcher = class {
|
|
|
3515
3672
|
startWatching() {
|
|
3516
3673
|
try {
|
|
3517
3674
|
this.watcher = fs2.watch(this.filePath, () => {
|
|
3518
|
-
if (
|
|
3675
|
+
if (this.stopped) return;
|
|
3676
|
+
if (this.parser.parseDocument) {
|
|
3677
|
+
if (this.docReadDebounceTimer) return;
|
|
3678
|
+
this.docReadDebounceTimer = setTimeout(() => {
|
|
3679
|
+
this.docReadDebounceTimer = null;
|
|
3680
|
+
if (!this.stopped) this.readNewData();
|
|
3681
|
+
}, DOC_READ_DEBOUNCE_MS);
|
|
3682
|
+
return;
|
|
3683
|
+
}
|
|
3684
|
+
this.readNewData();
|
|
3519
3685
|
});
|
|
3520
3686
|
this.watcher.on("error", () => {
|
|
3521
3687
|
this.watcher?.close();
|
|
@@ -3570,16 +3736,20 @@ var TranscriptWatcher = class {
|
|
|
3570
3736
|
}
|
|
3571
3737
|
}
|
|
3572
3738
|
readDocument(force) {
|
|
3573
|
-
let content;
|
|
3574
3739
|
let stat;
|
|
3575
3740
|
try {
|
|
3576
3741
|
stat = fs2.statSync(this.filePath);
|
|
3577
|
-
content = fs2.readFileSync(this.filePath, "utf-8");
|
|
3578
3742
|
} catch {
|
|
3579
3743
|
return;
|
|
3580
3744
|
}
|
|
3581
3745
|
const fingerprint = `${stat.mtimeMs}:${stat.size}`;
|
|
3582
3746
|
if (!force && fingerprint === this.documentFingerprint) return;
|
|
3747
|
+
let content;
|
|
3748
|
+
try {
|
|
3749
|
+
content = fs2.readFileSync(this.filePath, "utf-8");
|
|
3750
|
+
} catch {
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3583
3753
|
this.documentFingerprint = fingerprint;
|
|
3584
3754
|
const parsedTurns = this.parser.parseDocument?.(content) ?? [];
|
|
3585
3755
|
this.seq++;
|
|
@@ -3695,7 +3865,7 @@ var TranscriptWatcherManager = class {
|
|
|
3695
3865
|
const existing = this.active.get(sessionId);
|
|
3696
3866
|
let replacingExisting = false;
|
|
3697
3867
|
if (existing) {
|
|
3698
|
-
const newPath = discoverTranscriptFile(sessionId, agentType);
|
|
3868
|
+
const newPath = discoverTranscriptFile(sessionId, agentType, true);
|
|
3699
3869
|
const agentChanged = existing.agentType !== requestedAgentType;
|
|
3700
3870
|
const pathChanged = Boolean(newPath && newPath !== existing.filePath);
|
|
3701
3871
|
const existingPathGone = !fs2.existsSync(existing.filePath);
|
|
@@ -3720,7 +3890,7 @@ var TranscriptWatcherManager = class {
|
|
|
3720
3890
|
this.cancelEnableRetry(sessionId);
|
|
3721
3891
|
return { ok: false, reason: "unsupported_agent" };
|
|
3722
3892
|
}
|
|
3723
|
-
const filePath = discoverTranscriptFile(sessionId, agentType);
|
|
3893
|
+
const filePath = discoverTranscriptFile(sessionId, agentType, isRetry || replacingExisting);
|
|
3724
3894
|
if (!filePath) {
|
|
3725
3895
|
log7.info("no transcript file found", { sessionId });
|
|
3726
3896
|
this.armEnableRetry(sessionId, agentType);
|
|
@@ -3846,7 +4016,7 @@ var TranscriptWatcherManager = class {
|
|
|
3846
4016
|
this.stopRediscovery(sessionId);
|
|
3847
4017
|
return;
|
|
3848
4018
|
}
|
|
3849
|
-
const newPath = discoverTranscriptFile(sessionId, agentType);
|
|
4019
|
+
const newPath = discoverTranscriptFile(sessionId, agentType, true);
|
|
3850
4020
|
if (!newPath || newPath === session.filePath) return;
|
|
3851
4021
|
log7.info("transcript file changed", { sessionId, newPath });
|
|
3852
4022
|
session.watcher.stop();
|
|
@@ -9524,6 +9694,39 @@ function resolveRuntimeTranscriptSource(sessionId) {
|
|
|
9524
9694
|
// src/sessions.ts
|
|
9525
9695
|
var isMac = os5.platform() === "darwin";
|
|
9526
9696
|
var MARKDOWN_AGENT_TYPES = /* @__PURE__ */ new Set(["Claude Code", "Codex CLI", "Gemini CLI", "Antigravity CLI"]);
|
|
9697
|
+
var isTmuxAvailable = null;
|
|
9698
|
+
var isScreenAvailable = null;
|
|
9699
|
+
var isZellijAvailable = null;
|
|
9700
|
+
async function checkTmuxAvailable() {
|
|
9701
|
+
if (isTmuxAvailable !== null) return isTmuxAvailable;
|
|
9702
|
+
try {
|
|
9703
|
+
await execAsync("tmux -V");
|
|
9704
|
+
isTmuxAvailable = true;
|
|
9705
|
+
} catch {
|
|
9706
|
+
isTmuxAvailable = false;
|
|
9707
|
+
}
|
|
9708
|
+
return isTmuxAvailable;
|
|
9709
|
+
}
|
|
9710
|
+
async function checkScreenAvailable() {
|
|
9711
|
+
if (isScreenAvailable !== null) return isScreenAvailable;
|
|
9712
|
+
try {
|
|
9713
|
+
await execAsync("screen -v");
|
|
9714
|
+
isScreenAvailable = true;
|
|
9715
|
+
} catch {
|
|
9716
|
+
isScreenAvailable = false;
|
|
9717
|
+
}
|
|
9718
|
+
return isScreenAvailable;
|
|
9719
|
+
}
|
|
9720
|
+
async function checkZellijAvailable() {
|
|
9721
|
+
if (isZellijAvailable !== null) return isZellijAvailable;
|
|
9722
|
+
try {
|
|
9723
|
+
await execAsync("zellij --version");
|
|
9724
|
+
isZellijAvailable = true;
|
|
9725
|
+
} catch {
|
|
9726
|
+
isZellijAvailable = false;
|
|
9727
|
+
}
|
|
9728
|
+
return isZellijAvailable;
|
|
9729
|
+
}
|
|
9527
9730
|
function getProcessStartTime(pid) {
|
|
9528
9731
|
if (isMac) return null;
|
|
9529
9732
|
try {
|
|
@@ -9540,7 +9743,38 @@ function getProcessStartTime(pid) {
|
|
|
9540
9743
|
function collectStartTimes(pids) {
|
|
9541
9744
|
return pids.map((pid) => getProcessStartTime(pid) ?? 0);
|
|
9542
9745
|
}
|
|
9543
|
-
async function
|
|
9746
|
+
async function getProcessTable() {
|
|
9747
|
+
try {
|
|
9748
|
+
const raw = await execAsync("ps axo pid,ppid,tty,stat,comm,args");
|
|
9749
|
+
const rows = [];
|
|
9750
|
+
const lines = raw.split("\n");
|
|
9751
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9752
|
+
const trimmed = lines[i].trim();
|
|
9753
|
+
if (!trimmed) continue;
|
|
9754
|
+
if (i === 0 && /^\s*PID\b/i.test(lines[i])) continue;
|
|
9755
|
+
const parts = trimmed.split(/\s+/);
|
|
9756
|
+
if (parts.length < 6) continue;
|
|
9757
|
+
const pid = Number(parts[0]);
|
|
9758
|
+
const ppid = Number(parts[1]);
|
|
9759
|
+
const tty = parts[2];
|
|
9760
|
+
const stat = parts[3];
|
|
9761
|
+
const comm = parts[4];
|
|
9762
|
+
const args = parts.slice(5).join(" ");
|
|
9763
|
+
rows.push({
|
|
9764
|
+
pid,
|
|
9765
|
+
ppid: Number.isFinite(ppid) ? ppid : 0,
|
|
9766
|
+
tty,
|
|
9767
|
+
stat,
|
|
9768
|
+
comm,
|
|
9769
|
+
args
|
|
9770
|
+
});
|
|
9771
|
+
}
|
|
9772
|
+
return rows;
|
|
9773
|
+
} catch {
|
|
9774
|
+
return [];
|
|
9775
|
+
}
|
|
9776
|
+
}
|
|
9777
|
+
async function detectVscodeEnvironment(pid, processByPid) {
|
|
9544
9778
|
if (!isMac) {
|
|
9545
9779
|
try {
|
|
9546
9780
|
const environ = fs4.readFileSync(`/proc/${pid}/environ`, "utf8");
|
|
@@ -9550,17 +9784,36 @@ async function detectVscodeEnvironment(pid) {
|
|
|
9550
9784
|
} catch {
|
|
9551
9785
|
}
|
|
9552
9786
|
}
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9787
|
+
if (!processByPid || processByPid.size === 0) {
|
|
9788
|
+
try {
|
|
9789
|
+
const raw = await execAsync(`ps -p ${pid} -o ppid=`);
|
|
9790
|
+
const ppid = Number(raw.trim());
|
|
9791
|
+
if (ppid > 1) {
|
|
9792
|
+
const parentInfo = await execAsync(`ps -p ${ppid} -o comm=`);
|
|
9793
|
+
const parentComm = parentInfo.trim().toLowerCase();
|
|
9794
|
+
if (parentComm === "code" || parentComm === "code-server") {
|
|
9795
|
+
return true;
|
|
9796
|
+
}
|
|
9561
9797
|
}
|
|
9798
|
+
} catch {
|
|
9562
9799
|
}
|
|
9563
|
-
|
|
9800
|
+
return false;
|
|
9801
|
+
}
|
|
9802
|
+
let currentPid = pid;
|
|
9803
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9804
|
+
while (currentPid > 1 && !visited.has(currentPid)) {
|
|
9805
|
+
visited.add(currentPid);
|
|
9806
|
+
const proc = processByPid.get(currentPid);
|
|
9807
|
+
if (!proc) break;
|
|
9808
|
+
const parentPid = proc.ppid;
|
|
9809
|
+
if (parentPid <= 1) break;
|
|
9810
|
+
const parentProc = processByPid.get(parentPid);
|
|
9811
|
+
if (!parentProc) break;
|
|
9812
|
+
const parentComm = parentProc.comm.toLowerCase();
|
|
9813
|
+
if (parentComm === "code" || parentComm === "code-server") {
|
|
9814
|
+
return true;
|
|
9815
|
+
}
|
|
9816
|
+
currentPid = parentPid;
|
|
9564
9817
|
}
|
|
9565
9818
|
return false;
|
|
9566
9819
|
}
|
|
@@ -9619,12 +9872,14 @@ function classifyFromCommand(command) {
|
|
|
9619
9872
|
}
|
|
9620
9873
|
return { agentType, confidence: 0.7 };
|
|
9621
9874
|
}
|
|
9622
|
-
async function collectTmuxSessions() {
|
|
9623
|
-
|
|
9624
|
-
await execAsync("tmux -V");
|
|
9625
|
-
} catch {
|
|
9875
|
+
async function collectTmuxSessions(processByPid) {
|
|
9876
|
+
if (!await checkTmuxAvailable()) {
|
|
9626
9877
|
return [];
|
|
9627
9878
|
}
|
|
9879
|
+
if (!processByPid) {
|
|
9880
|
+
const list = await getProcessTable();
|
|
9881
|
+
processByPid = new Map(list.map((p) => [p.pid, p]));
|
|
9882
|
+
}
|
|
9628
9883
|
try {
|
|
9629
9884
|
const raw = await execAsync(
|
|
9630
9885
|
"tmux list-panes -a -F '#{session_name}|#{window_index}|#{pane_index}|#{pane_current_command}|#{session_attached}|#{window_activity}|#{pane_pid}|#{window_layout}|#{pane_current_path}|#{window_name}|#{pane_title}|#{window_flags}|#{session_group}|#{session_grouped}'"
|
|
@@ -9658,11 +9913,14 @@ async function collectTmuxSessions() {
|
|
|
9658
9913
|
const pid = Number(panePid);
|
|
9659
9914
|
if (pid > 0) pids.push(pid);
|
|
9660
9915
|
const classification = classifyAgent(command, { workingDir: paneCurrentPath });
|
|
9661
|
-
const vscodeDetected = pid > 0 ? await detectVscodeEnvironment(pid) : false;
|
|
9916
|
+
const vscodeDetected = pid > 0 ? await detectVscodeEnvironment(pid, processByPid) : false;
|
|
9917
|
+
const dirName = paneCurrentPath ? paneCurrentPath.split("/").filter(Boolean).pop() : "";
|
|
9918
|
+
const baseName = `${sessionName}:${windowIndex}.${paneIndex}`;
|
|
9919
|
+
const finalName = dirName ? `${baseName} (${dirName})` : baseName;
|
|
9662
9920
|
results.push({
|
|
9663
9921
|
id,
|
|
9664
9922
|
type: "tmux",
|
|
9665
|
-
name:
|
|
9923
|
+
name: finalName,
|
|
9666
9924
|
status: Number(attachedCount) > 0 ? "active" : "detached",
|
|
9667
9925
|
command,
|
|
9668
9926
|
agentType: classification?.agentType,
|
|
@@ -9687,10 +9945,8 @@ async function collectTmuxSessions() {
|
|
|
9687
9945
|
return [];
|
|
9688
9946
|
}
|
|
9689
9947
|
}
|
|
9690
|
-
async function collectScreenSessions() {
|
|
9691
|
-
|
|
9692
|
-
await execAsync("screen -v");
|
|
9693
|
-
} catch {
|
|
9948
|
+
async function collectScreenSessions(childrenByPpid) {
|
|
9949
|
+
if (!await checkScreenAvailable()) {
|
|
9694
9950
|
return [];
|
|
9695
9951
|
}
|
|
9696
9952
|
try {
|
|
@@ -9702,7 +9958,7 @@ async function collectScreenSessions() {
|
|
|
9702
9958
|
if (!match) continue;
|
|
9703
9959
|
const [, screenPid, sessionName, state] = match;
|
|
9704
9960
|
const pid = Number(screenPid);
|
|
9705
|
-
const childInfo = await getChildAgentInfo(pid);
|
|
9961
|
+
const childInfo = await getChildAgentInfo(pid, childrenByPpid);
|
|
9706
9962
|
if (!childInfo) continue;
|
|
9707
9963
|
const id = `screen:${sessionName}:${screenPid}`;
|
|
9708
9964
|
const allPids = [pid, ...childInfo.childPids];
|
|
@@ -9733,10 +9989,8 @@ async function collectScreenSessions() {
|
|
|
9733
9989
|
return [];
|
|
9734
9990
|
}
|
|
9735
9991
|
}
|
|
9736
|
-
async function collectZellijSessions() {
|
|
9737
|
-
|
|
9738
|
-
await execAsync("zellij --version");
|
|
9739
|
-
} catch {
|
|
9992
|
+
async function collectZellijSessions(childrenByPpid, processList) {
|
|
9993
|
+
if (!await checkZellijAvailable()) {
|
|
9740
9994
|
return [];
|
|
9741
9995
|
}
|
|
9742
9996
|
try {
|
|
@@ -9745,19 +9999,25 @@ async function collectZellijSessions() {
|
|
|
9745
9999
|
for (const line of raw.split("\n")) {
|
|
9746
10000
|
const sessionName = line.trim();
|
|
9747
10001
|
if (!sessionName) continue;
|
|
9748
|
-
let serverPids;
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
);
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
10002
|
+
let serverPids = [];
|
|
10003
|
+
if (processList) {
|
|
10004
|
+
serverPids = processList.filter(
|
|
10005
|
+
(p) => p.args.includes("zellij") && p.args.includes(`--session ${sessionName}`)
|
|
10006
|
+
).map((p) => p.pid);
|
|
10007
|
+
} else {
|
|
10008
|
+
try {
|
|
10009
|
+
const psOut = await execAsync(
|
|
10010
|
+
`ps axo pid,args | grep 'zellij.*--session ${sessionName}' | grep -v grep`
|
|
10011
|
+
);
|
|
10012
|
+
serverPids = psOut.split("\n").map((l) => Number(l.trim().split(/\s+/)[0])).filter((p) => p > 0);
|
|
10013
|
+
} catch {
|
|
10014
|
+
serverPids = [];
|
|
10015
|
+
}
|
|
9756
10016
|
}
|
|
9757
10017
|
const allChildPids = [];
|
|
9758
10018
|
let foundAgent = null;
|
|
9759
10019
|
for (const spid of serverPids) {
|
|
9760
|
-
const childInfo = await getChildAgentInfo(spid);
|
|
10020
|
+
const childInfo = await getChildAgentInfo(spid, childrenByPpid);
|
|
9761
10021
|
if (childInfo) {
|
|
9762
10022
|
foundAgent = childInfo;
|
|
9763
10023
|
allChildPids.push(...childInfo.childPids);
|
|
@@ -9809,7 +10069,7 @@ async function getProcessWorkingDir(pid) {
|
|
|
9809
10069
|
}
|
|
9810
10070
|
return null;
|
|
9811
10071
|
}
|
|
9812
|
-
const cwd = await
|
|
10072
|
+
const cwd = await fs4.promises.readlink(`/proc/${pid}/cwd`);
|
|
9813
10073
|
return cwd.trim() || null;
|
|
9814
10074
|
} catch {
|
|
9815
10075
|
return null;
|
|
@@ -9827,35 +10087,20 @@ function transcriptSourceForSession(agentType, sessionId) {
|
|
|
9827
10087
|
if (!agentType) return void 0;
|
|
9828
10088
|
return getTranscriptSourceForAgentType(agentType);
|
|
9829
10089
|
}
|
|
9830
|
-
async function collectBareAgentProcesses(excludePids) {
|
|
10090
|
+
async function collectBareAgentProcesses(excludePids, processList, processByPid) {
|
|
9831
10091
|
try {
|
|
9832
|
-
const
|
|
10092
|
+
const tStart = performance.now();
|
|
10093
|
+
if (!processList || !processByPid) {
|
|
10094
|
+
const list = await getProcessTable();
|
|
10095
|
+
processList = list;
|
|
10096
|
+
processByPid = new Map(list.map((p) => [p.pid, p]));
|
|
10097
|
+
}
|
|
10098
|
+
const tInit = performance.now();
|
|
9833
10099
|
const sessions = [];
|
|
9834
10100
|
const seen = /* @__PURE__ */ new Set();
|
|
9835
10101
|
const excluded = excludePids ?? /* @__PURE__ */ new Set();
|
|
9836
|
-
const
|
|
9837
|
-
const
|
|
9838
|
-
for (let i = 0; i < lines.length; i++) {
|
|
9839
|
-
const trimmed = lines[i].trim();
|
|
9840
|
-
if (!trimmed) continue;
|
|
9841
|
-
if (i === 0 && /^\s*PID\b/i.test(lines[i])) continue;
|
|
9842
|
-
const parts = trimmed.split(/\s+/);
|
|
9843
|
-
if (parts.length < 6) continue;
|
|
9844
|
-
const pid = Number(parts[0]);
|
|
9845
|
-
const ppid = Number(parts[1]);
|
|
9846
|
-
const tty = parts[2];
|
|
9847
|
-
const stat = parts[3];
|
|
9848
|
-
const args = parts.slice(5).join(" ");
|
|
9849
|
-
rows.push({
|
|
9850
|
-
pid,
|
|
9851
|
-
ppid: Number.isFinite(ppid) ? ppid : 0,
|
|
9852
|
-
tty,
|
|
9853
|
-
stat,
|
|
9854
|
-
args
|
|
9855
|
-
});
|
|
9856
|
-
}
|
|
9857
|
-
rows.sort((a, b) => a.pid - b.pid);
|
|
9858
|
-
const parentByPid = new Map(rows.map((row) => [row.pid, row.ppid]));
|
|
10102
|
+
const parentByPid = new Map(processList.map((row) => [row.pid, row.ppid]));
|
|
10103
|
+
const tParentMap = performance.now();
|
|
9859
10104
|
const hasTrackedAncestor = (pid, tracked) => {
|
|
9860
10105
|
const visited = /* @__PURE__ */ new Set();
|
|
9861
10106
|
let current = parentByPid.get(pid) ?? 0;
|
|
@@ -9866,24 +10111,60 @@ async function collectBareAgentProcesses(excludePids) {
|
|
|
9866
10111
|
}
|
|
9867
10112
|
return false;
|
|
9868
10113
|
};
|
|
9869
|
-
|
|
10114
|
+
const tFilterStart = performance.now();
|
|
10115
|
+
const candidates = processList.filter((row) => {
|
|
9870
10116
|
const { pid, tty, stat, args } = row;
|
|
9871
|
-
if (!hasControllingTty(tty))
|
|
9872
|
-
if (stat.startsWith("Z"))
|
|
9873
|
-
if (excluded.has(pid) || hasTrackedAncestor(pid, excluded))
|
|
9874
|
-
|
|
10117
|
+
if (!hasControllingTty(tty)) return false;
|
|
10118
|
+
if (stat.startsWith("Z")) return false;
|
|
10119
|
+
if (excluded.has(pid) || hasTrackedAncestor(pid, excluded)) return false;
|
|
10120
|
+
const lowerArgs = args.toLowerCase();
|
|
10121
|
+
const hasKeyword = lowerArgs.includes("claude") || lowerArgs.includes("codex") || lowerArgs.includes("aider") || lowerArgs.includes("cursor") || lowerArgs.includes("windsurf") || lowerArgs.includes("gemini") || lowerArgs.includes("agy") || lowerArgs.includes("antigravity") || lowerArgs.includes("amazon") || lowerArgs.includes("copilot");
|
|
10122
|
+
if (!hasKeyword) return false;
|
|
10123
|
+
return true;
|
|
10124
|
+
});
|
|
10125
|
+
const tFilterEnd = performance.now();
|
|
10126
|
+
const tCwdsStart = performance.now();
|
|
10127
|
+
const cwds = await Promise.all(
|
|
10128
|
+
candidates.map(async (row) => {
|
|
10129
|
+
const cwd = await getProcessWorkingDir(row.pid);
|
|
10130
|
+
return { pid: row.pid, cwd };
|
|
10131
|
+
})
|
|
10132
|
+
);
|
|
10133
|
+
const cwdByPid = new Map(cwds.map((c) => [c.pid, c.cwd]));
|
|
10134
|
+
const tCwdsEnd = performance.now();
|
|
10135
|
+
let tLoopAncestor = 0;
|
|
10136
|
+
let tLoopClassify = 0;
|
|
10137
|
+
let tLoopVscode = 0;
|
|
10138
|
+
let tLoopMarkdown = 0;
|
|
10139
|
+
let tLoopStartTimes = 0;
|
|
10140
|
+
const tLoopStart = performance.now();
|
|
10141
|
+
for (const row of candidates) {
|
|
10142
|
+
const { pid, args } = row;
|
|
10143
|
+
const t0 = performance.now();
|
|
10144
|
+
const skip = seen.has(pid) || hasTrackedAncestor(pid, seen);
|
|
10145
|
+
tLoopAncestor += performance.now() - t0;
|
|
10146
|
+
if (skip) continue;
|
|
10147
|
+
const workingDir = cwdByPid.get(pid) || null;
|
|
10148
|
+
const t1 = performance.now();
|
|
9875
10149
|
let classification = classifyAgent(args);
|
|
9876
10150
|
if (!classification) {
|
|
9877
|
-
|
|
9878
|
-
classification = classifyAgent(args, { workingDir: inferredWorkingDir ?? void 0 });
|
|
10151
|
+
classification = classifyAgent(args, { workingDir: workingDir ?? void 0 });
|
|
9879
10152
|
}
|
|
10153
|
+
tLoopClassify += performance.now() - t1;
|
|
9880
10154
|
if (!classification) continue;
|
|
9881
10155
|
seen.add(pid);
|
|
9882
|
-
const workingDir = await getProcessWorkingDir(pid);
|
|
9883
10156
|
const dirName = workingDir ? workingDir.split("/").pop() : null;
|
|
9884
10157
|
const displayName = dirName && dirName !== "/" && dirName !== "" ? `${classification.agentType} (${dirName})` : `${classification.agentType} (pid ${pid})`;
|
|
9885
|
-
const
|
|
10158
|
+
const t2 = performance.now();
|
|
10159
|
+
const vscodeDetected = await detectVscodeEnvironment(pid, processByPid);
|
|
10160
|
+
tLoopVscode += performance.now() - t2;
|
|
9886
10161
|
const id = `process:${pid}`;
|
|
10162
|
+
const t3 = performance.now();
|
|
10163
|
+
const mdAvail = markdownAvailableForSession(id, classification.agentType);
|
|
10164
|
+
tLoopMarkdown += performance.now() - t3;
|
|
10165
|
+
const t4 = performance.now();
|
|
10166
|
+
const startTimes = collectStartTimes([pid]);
|
|
10167
|
+
tLoopStartTimes += performance.now() - t4;
|
|
9887
10168
|
sessions.push({
|
|
9888
10169
|
id,
|
|
9889
10170
|
type: "process",
|
|
@@ -9892,32 +10173,54 @@ async function collectBareAgentProcesses(excludePids) {
|
|
|
9892
10173
|
command: args,
|
|
9893
10174
|
agentType: classification.agentType,
|
|
9894
10175
|
agentConfidence: classification.confidence,
|
|
9895
|
-
markdownAvailable:
|
|
10176
|
+
markdownAvailable: mdAvail,
|
|
9896
10177
|
transcriptSource: transcriptSourceForSession(classification.agentType, id),
|
|
9897
10178
|
workingDir: workingDir || void 0,
|
|
9898
10179
|
environment: vscodeDetected ? "vscode" : void 0,
|
|
9899
10180
|
lastActivityAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9900
10181
|
pids: [pid],
|
|
9901
|
-
startTimes
|
|
10182
|
+
startTimes
|
|
9902
10183
|
});
|
|
9903
10184
|
}
|
|
10185
|
+
const tLoopEnd = performance.now();
|
|
10186
|
+
console.log(`[PROFILE-BARE] Init took ${(tInit - tStart).toFixed(2)} ms`);
|
|
10187
|
+
console.log(`[PROFILE-BARE] Parent map took ${(tParentMap - tInit).toFixed(2)} ms`);
|
|
10188
|
+
console.log(`[PROFILE-BARE] Filtering took ${(tFilterEnd - tFilterStart).toFixed(2)} ms`);
|
|
10189
|
+
console.log(`[PROFILE-BARE] CWDs took ${(tCwdsEnd - tCwdsStart).toFixed(2)} ms`);
|
|
10190
|
+
console.log(`[PROFILE-BARE] Loop total took ${(tLoopEnd - tLoopStart).toFixed(2)} ms`);
|
|
10191
|
+
console.log(` [PROFILE-BARE-LOOP] Ancestor checks: ${tLoopAncestor.toFixed(2)} ms`);
|
|
10192
|
+
console.log(` [PROFILE-BARE-LOOP] Classify: ${tLoopClassify.toFixed(2)} ms`);
|
|
10193
|
+
console.log(` [PROFILE-BARE-LOOP] VS Code env: ${tLoopVscode.toFixed(2)} ms`);
|
|
10194
|
+
console.log(` [PROFILE-BARE-LOOP] Markdown check: ${tLoopMarkdown.toFixed(2)} ms`);
|
|
10195
|
+
console.log(` [PROFILE-BARE-LOOP] Start times: ${tLoopStartTimes.toFixed(2)} ms`);
|
|
9904
10196
|
return sessions;
|
|
9905
|
-
} catch {
|
|
10197
|
+
} catch (err) {
|
|
10198
|
+
console.error("[PROFILE-BARE] Error:", err);
|
|
9906
10199
|
return [];
|
|
9907
10200
|
}
|
|
9908
10201
|
}
|
|
9909
10202
|
async function collectSessionInventory(hostId, command) {
|
|
10203
|
+
const processList = await getProcessTable();
|
|
10204
|
+
const processByPid = /* @__PURE__ */ new Map();
|
|
10205
|
+
const childrenByPpid = /* @__PURE__ */ new Map();
|
|
10206
|
+
for (const p of processList) {
|
|
10207
|
+
processByPid.set(p.pid, p);
|
|
10208
|
+
if (!childrenByPpid.has(p.ppid)) {
|
|
10209
|
+
childrenByPpid.set(p.ppid, []);
|
|
10210
|
+
}
|
|
10211
|
+
childrenByPpid.get(p.ppid).push(p);
|
|
10212
|
+
}
|
|
9910
10213
|
const [tmux, screen, zellij] = await Promise.all([
|
|
9911
|
-
collectTmuxSessions(),
|
|
9912
|
-
collectScreenSessions(),
|
|
9913
|
-
collectZellijSessions()
|
|
10214
|
+
collectTmuxSessions(processByPid),
|
|
10215
|
+
collectScreenSessions(childrenByPpid),
|
|
10216
|
+
collectZellijSessions(childrenByPpid, processList)
|
|
9914
10217
|
]);
|
|
9915
10218
|
const multiplexerPids = /* @__PURE__ */ new Set();
|
|
9916
10219
|
for (const s of [...screen, ...zellij]) {
|
|
9917
10220
|
if (s.pids) s.pids.forEach((p) => multiplexerPids.add(p));
|
|
9918
10221
|
}
|
|
9919
10222
|
const tmuxChildTasks = tmux.flatMap(
|
|
9920
|
-
(s) => (s.pids ?? []).map(async (pid) => ({ session: s, pid, childInfo: await getChildAgentInfo(pid) }))
|
|
10223
|
+
(s) => (s.pids ?? []).map(async (pid) => ({ session: s, pid, childInfo: await getChildAgentInfo(pid, childrenByPpid) }))
|
|
9921
10224
|
);
|
|
9922
10225
|
const tmuxChildResults = await Promise.all(tmuxChildTasks);
|
|
9923
10226
|
for (const { session: s, pid, childInfo } of tmuxChildResults) {
|
|
@@ -9932,7 +10235,7 @@ async function collectSessionInventory(hostId, command) {
|
|
|
9932
10235
|
s.markdownAvailable = markdownAvailableForSession(s.id, s.agentType);
|
|
9933
10236
|
s.transcriptSource = transcriptSourceForSession(s.agentType, s.id);
|
|
9934
10237
|
}
|
|
9935
|
-
const bare = await collectBareAgentProcesses(multiplexerPids);
|
|
10238
|
+
const bare = await collectBareAgentProcesses(multiplexerPids, processList, processByPid);
|
|
9936
10239
|
const ptyClassification = classifyAgent(command);
|
|
9937
10240
|
const ptySession = {
|
|
9938
10241
|
id: `pty:${hostId}`,
|
|
@@ -9985,7 +10288,7 @@ function detectAgentType(command) {
|
|
|
9985
10288
|
return "Windsurf";
|
|
9986
10289
|
}
|
|
9987
10290
|
if (binary === "gemini" || scriptArg === "gemini") return "Gemini CLI";
|
|
9988
|
-
if (binary === "agy" || binary === "antigravity" || binary === "antigravity-cli") return "Antigravity CLI";
|
|
10291
|
+
if (binary === "agy" || scriptArg === "agy" || binary === "antigravity" || scriptArg === "antigravity" || binary === "antigravity-cli" || scriptArg === "antigravity-cli") return "Antigravity CLI";
|
|
9989
10292
|
if (binary === "amazon-q" || binary === "amazon_q") return "Amazon Q";
|
|
9990
10293
|
if (binary === "copilot" || scriptArg === "copilot") return "Copilot CLI";
|
|
9991
10294
|
return void 0;
|
|
@@ -9999,42 +10302,62 @@ function sessionInventoryFingerprint(sessions) {
|
|
|
9999
10302
|
return `${s.id}|${s.type}|${s.name}|${s.status}|${s.command || ""}|${s.runningCommand || ""}|${pidStr}|${markdownFlag}|${sourceFlag}`;
|
|
10000
10303
|
}).join("\n");
|
|
10001
10304
|
}
|
|
10002
|
-
async function getChildAgentInfo(parentPid, visited = /* @__PURE__ */ new Set()) {
|
|
10305
|
+
async function getChildAgentInfo(parentPid, childrenByPpid, visited = /* @__PURE__ */ new Set()) {
|
|
10003
10306
|
if (visited.has(parentPid)) return null;
|
|
10004
10307
|
visited.add(parentPid);
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10018
|
-
|
|
10019
|
-
|
|
10020
|
-
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
deeper
|
|
10029
|
-
|
|
10030
|
-
|
|
10308
|
+
if (!childrenByPpid || childrenByPpid.size === 0) {
|
|
10309
|
+
try {
|
|
10310
|
+
const pgrepOut = await execAsync(`pgrep -P ${parentPid}`);
|
|
10311
|
+
const pidList = pgrepOut.split("\n").map((l) => l.trim()).filter(Boolean).map(Number).filter((p) => p > 0);
|
|
10312
|
+
if (pidList.length === 0) return null;
|
|
10313
|
+
const raw = await execAsync(`ps -p ${pidList.join(",")} -o pid,args`);
|
|
10314
|
+
const descendantPids2 = new Set(pidList);
|
|
10315
|
+
let foundAgent2 = null;
|
|
10316
|
+
const lines = raw.split("\n");
|
|
10317
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10318
|
+
const trimmed = lines[i].trim();
|
|
10319
|
+
if (!trimmed) continue;
|
|
10320
|
+
if (i === 0 && /^\s*PID\b/i.test(lines[i])) continue;
|
|
10321
|
+
const spaceIdx = trimmed.indexOf(" ");
|
|
10322
|
+
if (spaceIdx < 0) continue;
|
|
10323
|
+
const pid = Number(trimmed.slice(0, spaceIdx));
|
|
10324
|
+
const args = trimmed.slice(spaceIdx + 1).trim();
|
|
10325
|
+
if (pid <= 0) continue;
|
|
10326
|
+
const agentType = detectAgentType(args);
|
|
10327
|
+
if (agentType && !foundAgent2) {
|
|
10328
|
+
foundAgent2 = { command: args, agentType };
|
|
10329
|
+
}
|
|
10330
|
+
const deeper = await getChildAgentInfo(pid, void 0, visited);
|
|
10331
|
+
if (deeper) {
|
|
10332
|
+
deeper.childPids.forEach((childPid) => descendantPids2.add(childPid));
|
|
10333
|
+
if (!foundAgent2) {
|
|
10334
|
+
foundAgent2 = { command: deeper.command, agentType: deeper.agentType };
|
|
10335
|
+
}
|
|
10031
10336
|
}
|
|
10032
10337
|
}
|
|
10338
|
+
return foundAgent2 ? { ...foundAgent2, childPids: [...descendantPids2] } : null;
|
|
10339
|
+
} catch {
|
|
10340
|
+
return null;
|
|
10033
10341
|
}
|
|
10034
|
-
return foundAgent ? { ...foundAgent, childPids: [...descendantPids] } : null;
|
|
10035
|
-
} catch {
|
|
10036
|
-
return null;
|
|
10037
10342
|
}
|
|
10343
|
+
const children = childrenByPpid.get(parentPid) ?? [];
|
|
10344
|
+
if (children.length === 0) return null;
|
|
10345
|
+
const descendantPids = new Set(children.map((c) => c.pid));
|
|
10346
|
+
let foundAgent = null;
|
|
10347
|
+
for (const child of children) {
|
|
10348
|
+
const agentType = detectAgentType(child.args);
|
|
10349
|
+
if (agentType && !foundAgent) {
|
|
10350
|
+
foundAgent = { command: child.args, agentType };
|
|
10351
|
+
}
|
|
10352
|
+
const deeper = await getChildAgentInfo(child.pid, childrenByPpid, visited);
|
|
10353
|
+
if (deeper) {
|
|
10354
|
+
deeper.childPids.forEach((childPid) => descendantPids.add(childPid));
|
|
10355
|
+
if (!foundAgent) {
|
|
10356
|
+
foundAgent = { command: deeper.command, agentType: deeper.agentType };
|
|
10357
|
+
}
|
|
10358
|
+
}
|
|
10359
|
+
}
|
|
10360
|
+
return foundAgent ? { ...foundAgent, childPids: [...descendantPids] } : null;
|
|
10038
10361
|
}
|
|
10039
10362
|
|
|
10040
10363
|
// src/auth.ts
|
|
@@ -11667,16 +11990,16 @@ var InventoryManager = class {
|
|
|
11667
11990
|
this.options = options;
|
|
11668
11991
|
}
|
|
11669
11992
|
/** Announce to the registry group via WebSocket. */
|
|
11670
|
-
async announceToRegistry(ws, registryGroup, type = "heartbeat") {
|
|
11993
|
+
async announceToRegistry(ws, registryGroup, type = "heartbeat", sessions) {
|
|
11671
11994
|
if (ws.readyState !== import_ws2.default.OPEN || !registryGroup) return;
|
|
11672
|
-
const
|
|
11995
|
+
const finalSessions = sessions || await collectSessionInventory(this.options.hostId, this.options.command);
|
|
11673
11996
|
const vitals = this.collectVitals();
|
|
11674
11997
|
sendToGroup(ws, registryGroup, {
|
|
11675
11998
|
type,
|
|
11676
11999
|
hostId: this.options.hostId,
|
|
11677
12000
|
hostName: this.options.hostName,
|
|
11678
12001
|
environment: os7.hostname(),
|
|
11679
|
-
sessions,
|
|
12002
|
+
sessions: finalSessions,
|
|
11680
12003
|
vitals,
|
|
11681
12004
|
agentVersion: CURRENT_VERSION,
|
|
11682
12005
|
lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -11693,7 +12016,7 @@ var InventoryManager = class {
|
|
|
11693
12016
|
const checkpointDue = Date.now() - this.lastHttpHeartbeatAt > HTTP_HEARTBEAT_MS;
|
|
11694
12017
|
if (changed || force) {
|
|
11695
12018
|
if (ws && ws.readyState === import_ws2.default.OPEN) {
|
|
11696
|
-
await this.announceToRegistry(ws, groups.registryGroup, "inventory");
|
|
12019
|
+
await this.announceToRegistry(ws, groups.registryGroup, "inventory", sessions);
|
|
11697
12020
|
}
|
|
11698
12021
|
this.lastSentFingerprint = fingerprint;
|
|
11699
12022
|
}
|
|
@@ -11819,10 +12142,36 @@ var ConnectionManager = class {
|
|
|
11819
12142
|
resetReconnectDelay() {
|
|
11820
12143
|
this.reconnectDelay = DEFAULT_RECONNECT_DELAY_MS;
|
|
11821
12144
|
}
|
|
12145
|
+
/**
|
|
12146
|
+
* Clear all active connection timers to prevent leaks.
|
|
12147
|
+
*/
|
|
12148
|
+
clearTimers() {
|
|
12149
|
+
if (this.wsPingTimer) {
|
|
12150
|
+
clearInterval(this.wsPingTimer);
|
|
12151
|
+
this.wsPingTimer = null;
|
|
12152
|
+
}
|
|
12153
|
+
if (this.wsPongTimeout) {
|
|
12154
|
+
clearTimeout(this.wsPongTimeout);
|
|
12155
|
+
this.wsPongTimeout = null;
|
|
12156
|
+
}
|
|
12157
|
+
if (this.wsHeartbeatTimer) {
|
|
12158
|
+
clearInterval(this.wsHeartbeatTimer);
|
|
12159
|
+
this.wsHeartbeatTimer = null;
|
|
12160
|
+
}
|
|
12161
|
+
if (this.inventorySyncTimer) {
|
|
12162
|
+
clearInterval(this.inventorySyncTimer);
|
|
12163
|
+
this.inventorySyncTimer = null;
|
|
12164
|
+
}
|
|
12165
|
+
if (this.inventorySyncKickTimer) {
|
|
12166
|
+
clearTimeout(this.inventorySyncKickTimer);
|
|
12167
|
+
this.inventorySyncKickTimer = null;
|
|
12168
|
+
}
|
|
12169
|
+
}
|
|
11822
12170
|
/**
|
|
11823
12171
|
* Start periodic timers (heartbeat, inventory sync, ping/pong).
|
|
11824
12172
|
*/
|
|
11825
12173
|
startTimers(onWsHeartbeat, onInventorySync) {
|
|
12174
|
+
this.clearTimers();
|
|
11826
12175
|
this.onInventorySync = onInventorySync;
|
|
11827
12176
|
this.wsHeartbeatTimer = setInterval(async () => {
|
|
11828
12177
|
if (!this.isReady()) return;
|
|
@@ -11833,7 +12182,19 @@ var ConnectionManager = class {
|
|
|
11833
12182
|
}, INVENTORY_SYNC_MS);
|
|
11834
12183
|
this.wsPingTimer = setInterval(() => {
|
|
11835
12184
|
if (!this.activeSocket || this.activeSocket.readyState !== import_ws3.default.OPEN) return;
|
|
11836
|
-
this.
|
|
12185
|
+
if (this.wsPongTimeout) {
|
|
12186
|
+
clearTimeout(this.wsPongTimeout);
|
|
12187
|
+
this.wsPongTimeout = null;
|
|
12188
|
+
}
|
|
12189
|
+
try {
|
|
12190
|
+
this.activeSocket.send(JSON.stringify({ type: "ping" }));
|
|
12191
|
+
} catch (err) {
|
|
12192
|
+
log20.error("failed to send JSON ping", { error: err.message });
|
|
12193
|
+
}
|
|
12194
|
+
try {
|
|
12195
|
+
this.activeSocket.ping();
|
|
12196
|
+
} catch {
|
|
12197
|
+
}
|
|
11837
12198
|
this.wsPongTimeout = setTimeout(() => {
|
|
11838
12199
|
log20.warn("no pong received within 10s; closing stale connection");
|
|
11839
12200
|
try {
|
|
@@ -11884,26 +12245,7 @@ var ConnectionManager = class {
|
|
|
11884
12245
|
*/
|
|
11885
12246
|
cleanup(opts = {}) {
|
|
11886
12247
|
if (opts.stopStreams) this.sessionStreamer.stopAll();
|
|
11887
|
-
|
|
11888
|
-
clearInterval(this.wsPingTimer);
|
|
11889
|
-
this.wsPingTimer = null;
|
|
11890
|
-
}
|
|
11891
|
-
if (this.wsPongTimeout) {
|
|
11892
|
-
clearTimeout(this.wsPongTimeout);
|
|
11893
|
-
this.wsPongTimeout = null;
|
|
11894
|
-
}
|
|
11895
|
-
if (this.wsHeartbeatTimer) {
|
|
11896
|
-
clearInterval(this.wsHeartbeatTimer);
|
|
11897
|
-
this.wsHeartbeatTimer = null;
|
|
11898
|
-
}
|
|
11899
|
-
if (this.inventorySyncTimer) {
|
|
11900
|
-
clearInterval(this.inventorySyncTimer);
|
|
11901
|
-
this.inventorySyncTimer = null;
|
|
11902
|
-
}
|
|
11903
|
-
if (this.inventorySyncKickTimer) {
|
|
11904
|
-
clearTimeout(this.inventorySyncKickTimer);
|
|
11905
|
-
this.inventorySyncKickTimer = null;
|
|
11906
|
-
}
|
|
12248
|
+
this.clearTimers();
|
|
11907
12249
|
if (this.activeSocket) {
|
|
11908
12250
|
this.activeSocket.removeAllListeners();
|
|
11909
12251
|
try {
|
|
@@ -12965,7 +13307,6 @@ var ControlDispatcher = class {
|
|
|
12965
13307
|
}
|
|
12966
13308
|
/** Handle input routing to the correct target. */
|
|
12967
13309
|
handleInput(data, sessionId) {
|
|
12968
|
-
this.connection.requestInventorySync(800);
|
|
12969
13310
|
if (!sessionId || sessionId.startsWith("pty:")) {
|
|
12970
13311
|
this.shell.write(data);
|
|
12971
13312
|
} else if (sessionId.startsWith("tmux:")) {
|
|
@@ -13936,7 +14277,6 @@ async function runAgent(rawOptions) {
|
|
|
13936
14277
|
const ptySessionId = `pty:${options.hostId}`;
|
|
13937
14278
|
const sessionStreamer = new SessionStreamer(
|
|
13938
14279
|
(sessionId, data, viewerId) => {
|
|
13939
|
-
conn.requestInventorySync();
|
|
13940
14280
|
if (!conn.isReady()) {
|
|
13941
14281
|
sessionStreamer.bufferOutput(sessionId, data);
|
|
13942
14282
|
return;
|
|
@@ -13968,7 +14308,6 @@ async function runAgent(rawOptions) {
|
|
|
13968
14308
|
);
|
|
13969
14309
|
const conn = new ConnectionManager(sessionStreamer, outputBuffer);
|
|
13970
14310
|
const sendOutput = (chunk) => {
|
|
13971
|
-
conn.requestInventorySync();
|
|
13972
14311
|
if (!conn.isReady()) {
|
|
13973
14312
|
outputBuffer.push(chunk);
|
|
13974
14313
|
return;
|
|
@@ -14220,6 +14559,10 @@ async function runAgent(rawOptions) {
|
|
|
14220
14559
|
} catch {
|
|
14221
14560
|
return;
|
|
14222
14561
|
}
|
|
14562
|
+
if (msg.type === "pong") {
|
|
14563
|
+
conn.onPong();
|
|
14564
|
+
return;
|
|
14565
|
+
}
|
|
14223
14566
|
if (msg.type === "message" && msg.group === groups.privateGroup) {
|
|
14224
14567
|
const payload = msg.data || {};
|
|
14225
14568
|
if (payload.type === "input") {
|
package/dist/sbom.json
CHANGED
|
@@ -1299,8 +1299,8 @@
|
|
|
1299
1299
|
{
|
|
1300
1300
|
"type": "library",
|
|
1301
1301
|
"name": "ragent-cli",
|
|
1302
|
-
"version": "1.11.
|
|
1303
|
-
"bom-ref": "ragent-live|ragent-cli@1.11.
|
|
1302
|
+
"version": "1.11.13",
|
|
1303
|
+
"bom-ref": "ragent-live|ragent-cli@1.11.13",
|
|
1304
1304
|
"author": "Intellimetrics",
|
|
1305
1305
|
"description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
|
|
1306
1306
|
"licenses": [
|
|
@@ -1311,7 +1311,7 @@
|
|
|
1311
1311
|
}
|
|
1312
1312
|
}
|
|
1313
1313
|
],
|
|
1314
|
-
"purl": "pkg:npm/ragent-cli@1.11.
|
|
1314
|
+
"purl": "pkg:npm/ragent-cli@1.11.13",
|
|
1315
1315
|
"externalReferences": [
|
|
1316
1316
|
{
|
|
1317
1317
|
"url": "https://github.com/chadlindell/ragent-live/issues",
|
|
@@ -1436,7 +1436,7 @@
|
|
|
1436
1436
|
"ragent-live|@emnapi/wasi-threads@1.2.1",
|
|
1437
1437
|
"ragent-live|@pkgjs/parseargs@0.11.0",
|
|
1438
1438
|
"ragent-live|@tybys/wasm-util@0.10.1",
|
|
1439
|
-
"ragent-live|ragent-cli@1.11.
|
|
1439
|
+
"ragent-live|ragent-cli@1.11.13"
|
|
1440
1440
|
]
|
|
1441
1441
|
},
|
|
1442
1442
|
{
|
|
@@ -1584,7 +1584,7 @@
|
|
|
1584
1584
|
]
|
|
1585
1585
|
},
|
|
1586
1586
|
{
|
|
1587
|
-
"ref": "ragent-live|ragent-cli@1.11.
|
|
1587
|
+
"ref": "ragent-live|ragent-cli@1.11.13",
|
|
1588
1588
|
"dependsOn": [
|
|
1589
1589
|
"ragent-live|@azure/web-pubsub-client@1.0.4",
|
|
1590
1590
|
"ragent-live|@openai/codex-sdk@0.130.0",
|