refactor: rename container references to agent across codebase

- container-runner.ts → agent-runner.ts
- ContainerInput/Output → AgentInput/Output
- ContainerConfig → AgentConfig
- runContainerAgent → runAgentProcess
- CONTAINER_TIMEOUT → AGENT_TIMEOUT (with env fallback)
- MAX_CONCURRENT_CONTAINERS → MAX_CONCURRENT_AGENTS
- containerName → processName, isTaskContainer → isTaskProcess
- DB column container_config kept as-is (backwards compat)
This commit is contained in:
Eyejoker
2026-03-13 16:18:36 +09:00
parent 35277cea0a
commit bd7f4408ae
12 changed files with 144 additions and 133 deletions

View File

@@ -2,7 +2,7 @@ import { ChildProcess } from 'child_process';
import fs from 'fs';
import path from 'path';
import { DATA_DIR, MAX_CONCURRENT_CONTAINERS } from './config.js';
import { DATA_DIR, MAX_CONCURRENT_AGENTS } from './config.js';
import { logger } from './logger.js';
interface QueuedTask {
@@ -17,12 +17,12 @@ const BASE_RETRY_MS = 5000;
interface GroupState {
active: boolean;
idleWaiting: boolean;
isTaskContainer: boolean;
isTaskProcess: boolean;
runningTaskId: string | null;
pendingMessages: boolean;
pendingTasks: QueuedTask[];
process: ChildProcess | null;
containerName: string | null;
processName: string | null;
groupFolder: string | null;
retryCount: number;
}
@@ -41,12 +41,12 @@ export class GroupQueue {
state = {
active: false,
idleWaiting: false,
isTaskContainer: false,
isTaskProcess: false,
runningTaskId: null,
pendingMessages: false,
pendingTasks: [],
process: null,
containerName: null,
processName: null,
groupFolder: null,
retryCount: 0,
};
@@ -64,18 +64,18 @@ export class GroupQueue {
const state = this.getGroup(groupJid);
// Pre-set groupFolder so sendMessage can pipe IPC while container starts
// Pre-set groupFolder so sendMessage can pipe IPC while agent process starts
if (groupFolder && !state.groupFolder) {
state.groupFolder = groupFolder;
}
if (state.active) {
state.pendingMessages = true;
logger.debug({ groupJid }, 'Container active, message queued');
logger.debug({ groupJid }, 'Agent active, message queued');
return;
}
if (this.activeCount >= MAX_CONCURRENT_CONTAINERS) {
if (this.activeCount >= MAX_CONCURRENT_AGENTS) {
state.pendingMessages = true;
if (!this.waitingGroups.includes(groupJid)) {
this.waitingGroups.push(groupJid);
@@ -112,11 +112,11 @@ export class GroupQueue {
if (state.idleWaiting) {
this.closeStdin(groupJid);
}
logger.debug({ groupJid, taskId }, 'Container active, task queued');
logger.debug({ groupJid, taskId }, 'Agent active, task queued');
return;
}
if (this.activeCount >= MAX_CONCURRENT_CONTAINERS) {
if (this.activeCount >= MAX_CONCURRENT_AGENTS) {
state.pendingTasks.push({ id: taskId, groupJid, fn });
if (!this.waitingGroups.includes(groupJid)) {
this.waitingGroups.push(groupJid);
@@ -137,18 +137,18 @@ export class GroupQueue {
registerProcess(
groupJid: string,
proc: ChildProcess,
containerName: string,
processName: string,
groupFolder?: string,
): void {
const state = this.getGroup(groupJid);
state.process = proc;
state.containerName = containerName;
state.processName = processName;
if (groupFolder) state.groupFolder = groupFolder;
}
/**
* Mark the container as idle-waiting (finished work, waiting for IPC input).
* If tasks are pending, preempt the idle container immediately.
* Mark the agent process as idle-waiting (finished work, waiting for IPC input).
* If tasks are pending, preempt the idle agent process immediately.
*/
notifyIdle(groupJid: string): void {
const state = this.getGroup(groupJid);
@@ -159,12 +159,12 @@ export class GroupQueue {
}
/**
* Send a follow-up message to the active container via IPC file.
* Returns true if the message was written, false if no active container.
* Send a follow-up message to the active agent process via IPC file.
* Returns true if the message was written, false if no active agent process.
*/
sendMessage(groupJid: string, text: string): boolean {
const state = this.getGroup(groupJid);
if (!state.active || !state.groupFolder || state.isTaskContainer)
if (!state.active || !state.groupFolder || state.isTaskProcess)
return false;
state.idleWaiting = false; // Agent is about to receive work, no longer idle
@@ -183,7 +183,7 @@ export class GroupQueue {
}
/**
* Signal the active container to wind down by writing a close sentinel.
* Signal the active agent process to wind down by writing a close sentinel.
*/
closeStdin(groupJid: string): void {
const state = this.getGroup(groupJid);
@@ -205,13 +205,13 @@ export class GroupQueue {
const state = this.getGroup(groupJid);
state.active = true;
state.idleWaiting = false;
state.isTaskContainer = false;
state.isTaskProcess = false;
state.pendingMessages = false;
this.activeCount++;
logger.debug(
{ groupJid, reason, activeCount: this.activeCount },
'Starting container for group',
'Starting agent process for group',
);
try {
@@ -229,7 +229,7 @@ export class GroupQueue {
} finally {
state.active = false;
state.process = null;
state.containerName = null;
state.processName = null;
state.groupFolder = null;
this.activeCount--;
this.drainGroup(groupJid);
@@ -240,7 +240,7 @@ export class GroupQueue {
const state = this.getGroup(groupJid);
state.active = true;
state.idleWaiting = false;
state.isTaskContainer = true;
state.isTaskProcess = true;
state.runningTaskId = task.id;
this.activeCount++;
@@ -255,10 +255,10 @@ export class GroupQueue {
logger.error({ groupJid, taskId: task.id, err }, 'Error running task');
} finally {
state.active = false;
state.isTaskContainer = false;
state.isTaskProcess = false;
state.runningTaskId = null;
state.process = null;
state.containerName = null;
state.processName = null;
state.groupFolder = null;
this.activeCount--;
this.drainGroup(groupJid);
@@ -323,7 +323,7 @@ export class GroupQueue {
private drainWaiting(): void {
while (
this.waitingGroups.length > 0 &&
this.activeCount < MAX_CONCURRENT_CONTAINERS
this.activeCount < MAX_CONCURRENT_AGENTS
) {
const nextJid = this.waitingGroups.shift()!;
const state = this.getGroup(nextJid);
@@ -352,19 +352,19 @@ export class GroupQueue {
async shutdown(_gracePeriodMs: number): Promise<void> {
this.shuttingDown = true;
// Count active containers but don't kill them — they'll finish on their own
// via idle timeout or container timeout. The --rm flag cleans them up on exit.
// This prevents WhatsApp reconnection restarts from killing working agents.
const activeContainers: string[] = [];
// Count active agent processes but don't kill them — they'll finish on their own
// via idle timeout or agent timeout.
// This prevents reconnection restarts from killing working agents.
const activeProcesses: string[] = [];
for (const [jid, state] of this.groups) {
if (state.process && !state.process.killed && state.containerName) {
activeContainers.push(state.containerName);
if (state.process && !state.process.killed && state.processName) {
activeProcesses.push(state.processName);
}
}
logger.info(
{ activeCount: this.activeCount, detachedContainers: activeContainers },
'GroupQueue shutting down (containers detached, not killed)',
{ activeCount: this.activeCount, detachedProcesses: activeProcesses },
'GroupQueue shutting down (agent processes detached, not killed)',
);
}
}