@gonzih/cc-discord 0.2.39 → 0.2.40
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/meta-agent-manager.d.ts +0 -15
- package/dist/meta-agent-manager.js +68 -2
- package/dist/notifier.js +0 -5
- package/package.json +1 -1
|
@@ -65,21 +65,6 @@ export interface MetaAgentManager {
|
|
|
65
65
|
/** Write a raw line to the stdin of the running session, if any. */
|
|
66
66
|
sendToSession: (ns: string, line: string) => void;
|
|
67
67
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Create a MetaAgentManager that maintains one persistent Claude process per namespace.
|
|
70
|
-
*
|
|
71
|
-
* On first message for a namespace:
|
|
72
|
-
* 1. ensureWorkspace + injectMcp
|
|
73
|
-
* 2. Drain any pending Redis queue entries to stdin
|
|
74
|
-
* 3. Write the new message to stdin
|
|
75
|
-
*
|
|
76
|
-
* On subsequent messages: write directly to stdin of the running process.
|
|
77
|
-
*
|
|
78
|
-
* On process exit: remove from sessions map. Next message triggers a respawn.
|
|
79
|
-
*
|
|
80
|
-
* The 3-second poll loop drains any messages that arrived while a session was
|
|
81
|
-
* starting up or temporarily unavailable.
|
|
82
|
-
*/
|
|
83
68
|
export declare function createMetaAgentManager(): MetaAgentManager;
|
|
84
69
|
/**
|
|
85
70
|
* Migrate the old cc-agent meta input key to the new cc-discord key.
|
|
@@ -293,7 +293,7 @@ export function spawnSession(ns, message, token, wire) {
|
|
|
293
293
|
* Stdout is wired to Redis via wireStdoutToRedis.
|
|
294
294
|
* Returns the ChildProcess.
|
|
295
295
|
*/
|
|
296
|
-
function spawnPersistentSession(ns, token, wire, onExit) {
|
|
296
|
+
function spawnPersistentSession(ns, token, wire, onExit, onOutput) {
|
|
297
297
|
const wsPath = workspacePath(ns);
|
|
298
298
|
const claudeBin = resolveClaude();
|
|
299
299
|
const env = buildEnv(token);
|
|
@@ -312,6 +312,9 @@ function spawnPersistentSession(ns, token, wire, onExit) {
|
|
|
312
312
|
], { cwd: wsPath, env, stdio: ["pipe", "pipe", "pipe"] });
|
|
313
313
|
proc.stdin.setDefaultEncoding("utf8");
|
|
314
314
|
wireStdoutToRedis(proc, ns, wire);
|
|
315
|
+
if (onOutput) {
|
|
316
|
+
proc.stdout.on("data", onOutput);
|
|
317
|
+
}
|
|
315
318
|
proc.on("exit", (code) => {
|
|
316
319
|
console.log(`[meta-agent-manager] persistent session exited (ns=${ns}, code=${code})`);
|
|
317
320
|
onExit();
|
|
@@ -337,15 +340,22 @@ function spawnPersistentSession(ns, token, wire, onExit) {
|
|
|
337
340
|
* The 3-second poll loop drains any messages that arrived while a session was
|
|
338
341
|
* starting up or temporarily unavailable.
|
|
339
342
|
*/
|
|
343
|
+
/** Kill a session after this many ms of no stdout (or no new stdin). */
|
|
344
|
+
const SESSION_INACTIVITY_MS = 5 * 60_000; // 5 minutes
|
|
340
345
|
export function createMetaAgentManager() {
|
|
341
346
|
let pollInterval = null;
|
|
347
|
+
let watchdogInterval = null;
|
|
342
348
|
/** One persistent ChildProcess per namespace. */
|
|
343
349
|
const sessions = new Map();
|
|
344
350
|
/** Namespaces currently being set up (workspace clone / first spawn). */
|
|
345
351
|
const startingUp = new Set();
|
|
352
|
+
/** Wire reference needed for watchdog notifications. Set in startPolling. */
|
|
353
|
+
let wireRef = null;
|
|
346
354
|
/**
|
|
347
355
|
* Write a line to the stdin of the persistent session for ns.
|
|
348
356
|
* Silently no-ops if no session exists.
|
|
357
|
+
* Resets the inactivity timer so the watchdog doesn't kill a session
|
|
358
|
+
* that just received a message but hasn't responded yet.
|
|
349
359
|
*/
|
|
350
360
|
function writeToStdin(ns, line) {
|
|
351
361
|
const session = sessions.get(ns);
|
|
@@ -354,11 +364,54 @@ export function createMetaAgentManager() {
|
|
|
354
364
|
try {
|
|
355
365
|
// Interactive mode: plain text line, Claude reads it as user input
|
|
356
366
|
session.proc.stdin.write(`${line}\n`);
|
|
367
|
+
// Reset inactivity timer — the session now has SESSION_INACTIVITY_MS to respond
|
|
368
|
+
session.lastOutputAt = Date.now();
|
|
357
369
|
}
|
|
358
370
|
catch (err) {
|
|
359
371
|
console.warn(`[meta-agent-manager] stdin write failed (ns=${ns}):`, err.message);
|
|
360
372
|
}
|
|
361
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Watchdog: scan all sessions every 60s. If any session has been silent
|
|
376
|
+
* (no stdout and no new stdin) for SESSION_INACTIVITY_MS, kill it and
|
|
377
|
+
* notify Discord so the user can see why the session went quiet.
|
|
378
|
+
*/
|
|
379
|
+
function startWatchdog() {
|
|
380
|
+
if (watchdogInterval)
|
|
381
|
+
return;
|
|
382
|
+
watchdogInterval = setInterval(() => {
|
|
383
|
+
if (!wireRef)
|
|
384
|
+
return;
|
|
385
|
+
const now = Date.now();
|
|
386
|
+
for (const [ns, session] of sessions) {
|
|
387
|
+
const idleMs = now - session.lastOutputAt;
|
|
388
|
+
if (idleMs < SESSION_INACTIVITY_MS)
|
|
389
|
+
continue;
|
|
390
|
+
const idleMin = Math.round(idleMs / 60_000);
|
|
391
|
+
console.warn(`[meta-agent-manager] watchdog: ns=${ns} idle ${idleMin}m, killing stuck session`);
|
|
392
|
+
// Notify Discord before killing so the user knows what happened
|
|
393
|
+
const alertMsg = {
|
|
394
|
+
id: crypto.randomUUID(),
|
|
395
|
+
source: "claude",
|
|
396
|
+
role: "assistant",
|
|
397
|
+
content: `⚠️ Session for **${ns}** was idle for ${idleMin} minutes (likely stuck on a tool call). Killed and removed — send any message to restart.`,
|
|
398
|
+
timestamp: new Date().toISOString(),
|
|
399
|
+
chatId: 0,
|
|
400
|
+
};
|
|
401
|
+
wireRef.discord.publishOutgoing(ns, alertMsg).catch((err) => {
|
|
402
|
+
console.warn(`[meta-agent-manager] watchdog publishOutgoing failed (ns=${ns}):`, err.message);
|
|
403
|
+
});
|
|
404
|
+
try {
|
|
405
|
+
session.proc.stdin.end();
|
|
406
|
+
session.proc.kill("SIGTERM");
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
// ignore kill errors
|
|
410
|
+
}
|
|
411
|
+
sessions.delete(ns);
|
|
412
|
+
}
|
|
413
|
+
}, 60_000);
|
|
414
|
+
}
|
|
362
415
|
/**
|
|
363
416
|
* Drain all pending messages from the Redis input queue into the session's stdin.
|
|
364
417
|
*/
|
|
@@ -405,12 +458,19 @@ export function createMetaAgentManager() {
|
|
|
405
458
|
const wsPath = workspacePath(ns);
|
|
406
459
|
await ensureWorkspace(ns, repoUrl);
|
|
407
460
|
injectMcp(ns, wsPath, token);
|
|
461
|
+
// Placeholder — filled in after proc is known so the onOutput closure can update it
|
|
462
|
+
let session;
|
|
408
463
|
const proc = spawnPersistentSession(ns, token, wire, () => {
|
|
409
464
|
// On exit: remove from map so next message triggers a respawn
|
|
410
465
|
sessions.delete(ns);
|
|
411
466
|
console.log(`[meta-agent-manager] session removed from map (ns=${ns})`);
|
|
467
|
+
}, () => {
|
|
468
|
+
// Reset inactivity timer on any stdout data
|
|
469
|
+
if (session)
|
|
470
|
+
session.lastOutputAt = Date.now();
|
|
412
471
|
});
|
|
413
|
-
|
|
472
|
+
session = { proc, ns, lastOutputAt: Date.now() };
|
|
473
|
+
sessions.set(ns, session);
|
|
414
474
|
// Drain any messages that arrived before this session started
|
|
415
475
|
await drainQueue(ns, wire);
|
|
416
476
|
// Write the triggering message last (after queue drain, in order)
|
|
@@ -443,6 +503,8 @@ export function createMetaAgentManager() {
|
|
|
443
503
|
startPolling(wire, getNamespaces, instanceId) {
|
|
444
504
|
if (pollInterval)
|
|
445
505
|
return; // already running
|
|
506
|
+
wireRef = wire;
|
|
507
|
+
startWatchdog();
|
|
446
508
|
pollInterval = setInterval(() => {
|
|
447
509
|
const namespaces = getNamespaces();
|
|
448
510
|
if (namespaces.length === 0)
|
|
@@ -499,6 +561,10 @@ export function createMetaAgentManager() {
|
|
|
499
561
|
pollInterval = null;
|
|
500
562
|
console.log("[meta-agent-manager] polling stopped");
|
|
501
563
|
}
|
|
564
|
+
if (watchdogInterval) {
|
|
565
|
+
clearInterval(watchdogInterval);
|
|
566
|
+
watchdogInterval = null;
|
|
567
|
+
}
|
|
502
568
|
// Kill all active sessions
|
|
503
569
|
for (const [ns, session] of sessions) {
|
|
504
570
|
try {
|
package/dist/notifier.js
CHANGED
|
@@ -279,11 +279,6 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
|
|
|
279
279
|
if (!buf || !buf.text.trim())
|
|
280
280
|
return;
|
|
281
281
|
const text = `← [${ns}] ` + stripAnsi(buf.text.trim());
|
|
282
|
-
if (text.length < 30) {
|
|
283
|
-
buf.text = "";
|
|
284
|
-
buf.timer = null;
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
282
|
buf.text = "";
|
|
288
283
|
buf.timer = null;
|
|
289
284
|
// During an active loop, route meta-agent output to the thread rather than main channel
|