fix: prevent agents from running indefinitely after app quit
Agents kept running after closing AgentCompany because worker processes auto-recreated on exit, retry timers were never cleared, child CLI processes weren't terminated, and orchestration had no quit guards. Changes: - Add isQuitting guards to all orchestration/automation entry points - Track and clear all setTimeout/setInterval timers on quit - Send cancel-run to all active CLI processes before killing worker - Add destroyed flag to prevent worker recreation after intentional destroy - Add quit confirmation dialog: Stop All & Quit / Minimize to Background / Cancel - Bump version to 0.4.96 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
222c80e544
commit
05c57be98f
7 files changed
+190
-19
No files matched your search
@@ -60,6 +60,7 @@ interface OrchestrationDependencies {
|
||||
dispatchAutomation: (trigger: string, ctx: AutomationDispatch) => void;
|
||||
hasWorkerReady: () => boolean;
|
||||
enqueueWorkerRun: (payload: EnqueueRunPayload) => void;
|
||||
isQuitting: () => boolean;
|
||||
}
|
||||
|
||||
export function createOrchestrationService({
|
||||
@@ -72,6 +73,7 @@ export function createOrchestrationService({
|
||||
dispatchAutomation,
|
||||
hasWorkerReady,
|
||||
enqueueWorkerRun,
|
||||
isQuitting,
|
||||
}: OrchestrationDependencies) {
|
||||
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
||||
const scheduleLastFiredMap = new Map<string, number>();
|
||||
@@ -182,6 +184,9 @@ export function createOrchestrationService({
|
||||
trigger: string;
|
||||
}) {
|
||||
const { runId, companyId, agent, task, connector, workspace, workspaceMode, companyName, companyDescription, snapshot, trigger } = params;
|
||||
if (isQuitting()) {
|
||||
throw new Error("Application is shutting down — refusing to dispatch new run.");
|
||||
}
|
||||
if (!hasWorkerReady()) {
|
||||
throw new Error("Orchestration worker is not ready.");
|
||||
}
|
||||
@@ -311,6 +316,7 @@ export function createOrchestrationService({
|
||||
}
|
||||
|
||||
function startAgentHeartbeatRun(agentId: string, companyId: string, trigger: string): string | null {
|
||||
if (isQuitting()) return null;
|
||||
if (!hasWorkerReady()) return null;
|
||||
const companyStatus = db.getCompanyStatus(companyId);
|
||||
if (companyStatus !== "active") {
|
||||
@@ -448,7 +454,7 @@ export function createOrchestrationService({
|
||||
}
|
||||
|
||||
function wakeAgentIfPossible(agentId: string | null | undefined, companyId: string, trigger: string) {
|
||||
if (!agentId) return null;
|
||||
if (!agentId || isQuitting()) return null;
|
||||
try {
|
||||
const agent = db.getAgent(agentId);
|
||||
if (agent.companyId !== companyId) {
|
||||
@@ -473,6 +479,9 @@ export function createOrchestrationService({
|
||||
}
|
||||
|
||||
function queueTaskRun(taskId: string, trigger: string, actor: string) {
|
||||
if (isQuitting()) {
|
||||
return fail("APP_SHUTTING_DOWN", "Application is shutting down.");
|
||||
}
|
||||
if (!hasWorkerReady()) {
|
||||
logger.warn(`[run] Worker not ready — cannot queue run for task ${taskId} (trigger: ${trigger})`);
|
||||
return fail("WORKER_UNAVAILABLE", "Orchestration worker is not ready.");
|
||||
@@ -556,7 +565,7 @@ export function createOrchestrationService({
|
||||
}
|
||||
|
||||
function resumeQueuedRuns() {
|
||||
if (!hasWorkerReady()) {
|
||||
if (isQuitting() || !hasWorkerReady()) {
|
||||
return;
|
||||
}
|
||||
const snapshot = db.listSnapshot();
|
||||
@@ -610,7 +619,7 @@ export function createOrchestrationService({
|
||||
|
||||
heartbeatTimer = setInterval(() => {
|
||||
try {
|
||||
if (!hasWorkerReady()) return;
|
||||
if (isQuitting() || !hasWorkerReady()) return;
|
||||
const activeCompanies = db.listActiveCompanyIds();
|
||||
const now = Date.now();
|
||||
|
||||
|
||||
Reference in new issue
Block a user