From 5e4244a8804bea8684aa06fccd337c0c048e76f9 Mon Sep 17 00:00:00 2001 From: Prateek Date: Tue, 7 Apr 2026 20:59:57 +0530 Subject: [PATCH] fix: prevent race condition marking new sessions as 'killed' When a session is freshly created, the lifecycle worker may poll before the claude process has started in the tmux session. findClaudeProcess() returns null, getActivityState() reports "exited", and the session gets marked as "killed" even though it's still starting up. Add a 60-second startup grace period: if the agent process is not found but the session was created less than 60 seconds ago, return "idle" instead of "exited" to give the agent time to launch. Fixes #989 --- packages/ao/package.json | 2 +- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/plugins/agent-aider/package.json | 2 +- packages/plugins/agent-claude-code/package.json | 2 +- .../src/__tests__/activity-detection.test.ts | 13 +++++++++++-- packages/plugins/agent-claude-code/src/index.ts | 12 +++++++++++- packages/plugins/agent-codex/package.json | 2 +- packages/plugins/agent-opencode/package.json | 2 +- packages/plugins/notifier-composio/package.json | 2 +- packages/plugins/notifier-desktop/package.json | 2 +- packages/plugins/notifier-discord/package.json | 2 +- packages/plugins/notifier-openclaw/package.json | 2 +- packages/plugins/notifier-slack/package.json | 2 +- packages/plugins/notifier-webhook/package.json | 2 +- packages/plugins/runtime-process/package.json | 2 +- packages/plugins/runtime-tmux/package.json | 2 +- packages/plugins/scm-github/package.json | 2 +- packages/plugins/scm-gitlab/package.json | 2 +- packages/plugins/terminal-iterm2/package.json | 2 +- packages/plugins/terminal-web/package.json | 2 +- packages/plugins/tracker-github/package.json | 2 +- packages/plugins/tracker-gitlab/package.json | 2 +- packages/plugins/tracker-linear/package.json | 2 +- packages/plugins/workspace-clone/package.json | 2 +- packages/plugins/workspace-worktree/package.json | 2 +- packages/web/package.json | 2 +- 27 files changed, 47 insertions(+), 28 deletions(-) diff --git a/packages/ao/package.json b/packages/ao/package.json index 686861d40..bef5e22fa 100644 --- a/packages/ao/package.json +++ b/packages/ao/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao", - "version": "0.2.2", + "version": "0.2.4", "description": "Orchestrate parallel AI coding agents — global CLI wrapper", "license": "MIT", "type": "module", diff --git a/packages/cli/package.json b/packages/cli/package.json index 1724be6a2..291a54e1c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-cli", - "version": "0.2.2", + "version": "0.2.4", "description": "CLI for agent-orchestrator — the `ao` command", "license": "MIT", "type": "module", diff --git a/packages/core/package.json b/packages/core/package.json index c32b6e0e8..9a0dce18b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-core", - "version": "0.2.0", + "version": "0.2.2", "description": "Core library — types, config, session manager, lifecycle manager, event bus", "license": "MIT", "type": "module", diff --git a/packages/plugins/agent-aider/package.json b/packages/plugins/agent-aider/package.json index 08cb5826f..aecc31ab3 100644 --- a/packages/plugins/agent-aider/package.json +++ b/packages/plugins/agent-aider/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-agent-aider", - "version": "0.2.0", + "version": "0.2.2", "description": "Agent plugin: Aider", "license": "MIT", "type": "module", diff --git a/packages/plugins/agent-claude-code/package.json b/packages/plugins/agent-claude-code/package.json index c5b8c4c58..e2a9e16b6 100644 --- a/packages/plugins/agent-claude-code/package.json +++ b/packages/plugins/agent-claude-code/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-agent-claude-code", - "version": "0.2.0", + "version": "0.2.2", "description": "Agent plugin: Claude Code", "license": "MIT", "type": "module", diff --git a/packages/plugins/agent-claude-code/src/__tests__/activity-detection.test.ts b/packages/plugins/agent-claude-code/src/__tests__/activity-detection.test.ts index a2be12ee6..5d8fcca51 100644 --- a/packages/plugins/agent-claude-code/src/__tests__/activity-detection.test.ts +++ b/packages/plugins/agent-claude-code/src/__tests__/activity-detection.test.ts @@ -116,10 +116,19 @@ describe("Claude Code Activity Detection", () => { // Process / handle edge cases // ----------------------------------------------------------------------- - it("returns 'exited' when process is not running", async () => { + it("returns 'idle' during startup grace period when process is not yet running", async () => { vi.spyOn(agent, "isProcessRunning").mockResolvedValue(false); writeJsonl([{ type: "assistant" }]); - expect((await agent.getActivityState(makeSession()))?.state).toBe("exited"); + // Session just created — within 60s grace period, so agent may still be starting + expect((await agent.getActivityState(makeSession()))?.state).toBe("idle"); + }); + + it("returns 'exited' when process is not running and session is old", async () => { + vi.spyOn(agent, "isProcessRunning").mockResolvedValue(false); + writeJsonl([{ type: "assistant" }]); + // Session created 2 minutes ago — well past grace period + const oldSession = makeSession({ createdAt: new Date(Date.now() - 120_000) }); + expect((await agent.getActivityState(oldSession))?.state).toBe("exited"); }); it("returns 'exited' when no runtimeHandle", async () => { diff --git a/packages/plugins/agent-claude-code/src/index.ts b/packages/plugins/agent-claude-code/src/index.ts index a6fe9e0e0..236bacb6e 100644 --- a/packages/plugins/agent-claude-code/src/index.ts +++ b/packages/plugins/agent-claude-code/src/index.ts @@ -720,7 +720,17 @@ function createClaudeCodeAgent(): Agent { const exitedAt = new Date(); if (!session.runtimeHandle) return { state: "exited", timestamp: exitedAt }; const running = await this.isProcessRunning(session.runtimeHandle); - if (!running) return { state: "exited", timestamp: exitedAt }; + if (!running) { + // Grace period: don't report "exited" for freshly created sessions + // where the agent process may not have launched yet (race between + // tmux session creation and the agent binary starting). + const STARTUP_GRACE_MS = 60_000; + const ageMs = Date.now() - session.createdAt.getTime(); + if (ageMs < STARTUP_GRACE_MS) { + return { state: "idle", timestamp: session.createdAt }; + } + return { state: "exited", timestamp: exitedAt }; + } // Process is running - check JSONL session file for activity if (!session.workspacePath) { diff --git a/packages/plugins/agent-codex/package.json b/packages/plugins/agent-codex/package.json index 4731133c4..befeccf4d 100644 --- a/packages/plugins/agent-codex/package.json +++ b/packages/plugins/agent-codex/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-agent-codex", - "version": "0.2.0", + "version": "0.2.2", "description": "Agent plugin: OpenAI Codex CLI", "license": "MIT", "type": "module", diff --git a/packages/plugins/agent-opencode/package.json b/packages/plugins/agent-opencode/package.json index 7ecccc8db..0507c215a 100644 --- a/packages/plugins/agent-opencode/package.json +++ b/packages/plugins/agent-opencode/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-agent-opencode", - "version": "0.2.0", + "version": "0.2.2", "description": "Agent plugin: OpenCode", "license": "MIT", "type": "module", diff --git a/packages/plugins/notifier-composio/package.json b/packages/plugins/notifier-composio/package.json index 30d48ad28..c0ef5e45c 100644 --- a/packages/plugins/notifier-composio/package.json +++ b/packages/plugins/notifier-composio/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-notifier-composio", - "version": "0.2.0", + "version": "0.2.2", "description": "Notifier plugin: Composio unified notifications (Slack, Discord, email)", "license": "MIT", "type": "module", diff --git a/packages/plugins/notifier-desktop/package.json b/packages/plugins/notifier-desktop/package.json index ac4bc2686..2a340c76e 100644 --- a/packages/plugins/notifier-desktop/package.json +++ b/packages/plugins/notifier-desktop/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-notifier-desktop", - "version": "0.2.0", + "version": "0.2.2", "description": "Notifier plugin: OS desktop notifications", "license": "MIT", "type": "module", diff --git a/packages/plugins/notifier-discord/package.json b/packages/plugins/notifier-discord/package.json index d84d1411e..f856d3667 100644 --- a/packages/plugins/notifier-discord/package.json +++ b/packages/plugins/notifier-discord/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-notifier-discord", - "version": "0.1.0", + "version": "0.1.2", "description": "Notifier plugin: Discord webhook notifications", "license": "MIT", "type": "module", diff --git a/packages/plugins/notifier-openclaw/package.json b/packages/plugins/notifier-openclaw/package.json index b6e7c82bc..435b0fd32 100644 --- a/packages/plugins/notifier-openclaw/package.json +++ b/packages/plugins/notifier-openclaw/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-notifier-openclaw", - "version": "0.1.1", + "version": "0.1.3", "description": "Notifier plugin: OpenClaw webhook notifications", "license": "MIT", "type": "module", diff --git a/packages/plugins/notifier-slack/package.json b/packages/plugins/notifier-slack/package.json index 2840e9166..767250ae3 100644 --- a/packages/plugins/notifier-slack/package.json +++ b/packages/plugins/notifier-slack/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-notifier-slack", - "version": "0.2.0", + "version": "0.2.2", "description": "Notifier plugin: Slack", "license": "MIT", "type": "module", diff --git a/packages/plugins/notifier-webhook/package.json b/packages/plugins/notifier-webhook/package.json index 64af60c67..5b9df27fe 100644 --- a/packages/plugins/notifier-webhook/package.json +++ b/packages/plugins/notifier-webhook/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-notifier-webhook", - "version": "0.2.0", + "version": "0.2.2", "description": "Notifier plugin: generic webhook", "license": "MIT", "type": "module", diff --git a/packages/plugins/runtime-process/package.json b/packages/plugins/runtime-process/package.json index a5ed169ec..861b872af 100644 --- a/packages/plugins/runtime-process/package.json +++ b/packages/plugins/runtime-process/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-runtime-process", - "version": "0.2.0", + "version": "0.2.2", "description": "Runtime plugin: child processes", "license": "MIT", "type": "module", diff --git a/packages/plugins/runtime-tmux/package.json b/packages/plugins/runtime-tmux/package.json index ecfe79cbe..027538e31 100644 --- a/packages/plugins/runtime-tmux/package.json +++ b/packages/plugins/runtime-tmux/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-runtime-tmux", - "version": "0.2.0", + "version": "0.2.2", "description": "Runtime plugin: tmux sessions", "license": "MIT", "type": "module", diff --git a/packages/plugins/scm-github/package.json b/packages/plugins/scm-github/package.json index 6c10804b3..d4ffeced8 100644 --- a/packages/plugins/scm-github/package.json +++ b/packages/plugins/scm-github/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-scm-github", - "version": "0.2.0", + "version": "0.2.2", "description": "SCM plugin: GitHub (PRs, CI, reviews)", "license": "MIT", "type": "module", diff --git a/packages/plugins/scm-gitlab/package.json b/packages/plugins/scm-gitlab/package.json index 8a5ca8a43..b71a8ccb5 100644 --- a/packages/plugins/scm-gitlab/package.json +++ b/packages/plugins/scm-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-scm-gitlab", - "version": "0.1.1", + "version": "0.1.3", "description": "SCM plugin: GitLab (MRs, CI, reviews)", "license": "MIT", "type": "module", diff --git a/packages/plugins/terminal-iterm2/package.json b/packages/plugins/terminal-iterm2/package.json index 9e8a43cf3..57153dec4 100644 --- a/packages/plugins/terminal-iterm2/package.json +++ b/packages/plugins/terminal-iterm2/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-terminal-iterm2", - "version": "0.2.0", + "version": "0.2.2", "description": "Terminal plugin: macOS iTerm2", "license": "MIT", "type": "module", diff --git a/packages/plugins/terminal-web/package.json b/packages/plugins/terminal-web/package.json index 50378d390..908a8a878 100644 --- a/packages/plugins/terminal-web/package.json +++ b/packages/plugins/terminal-web/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-terminal-web", - "version": "0.2.0", + "version": "0.2.2", "description": "Terminal plugin: xterm.js web terminal", "license": "MIT", "type": "module", diff --git a/packages/plugins/tracker-github/package.json b/packages/plugins/tracker-github/package.json index af71ad091..1a23d9144 100644 --- a/packages/plugins/tracker-github/package.json +++ b/packages/plugins/tracker-github/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-tracker-github", - "version": "0.2.0", + "version": "0.2.2", "description": "Tracker plugin: GitHub Issues", "license": "MIT", "type": "module", diff --git a/packages/plugins/tracker-gitlab/package.json b/packages/plugins/tracker-gitlab/package.json index 8632cf867..ea3717667 100644 --- a/packages/plugins/tracker-gitlab/package.json +++ b/packages/plugins/tracker-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-tracker-gitlab", - "version": "0.1.1", + "version": "0.1.3", "description": "Tracker plugin: GitLab Issues", "license": "MIT", "type": "module", diff --git a/packages/plugins/tracker-linear/package.json b/packages/plugins/tracker-linear/package.json index 36e2b47e2..a9f20d1b9 100644 --- a/packages/plugins/tracker-linear/package.json +++ b/packages/plugins/tracker-linear/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-tracker-linear", - "version": "0.2.0", + "version": "0.2.2", "description": "Tracker plugin: Linear", "license": "MIT", "type": "module", diff --git a/packages/plugins/workspace-clone/package.json b/packages/plugins/workspace-clone/package.json index 13661bcb9..248893e50 100644 --- a/packages/plugins/workspace-clone/package.json +++ b/packages/plugins/workspace-clone/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-workspace-clone", - "version": "0.2.0", + "version": "0.2.2", "description": "Workspace plugin: git clone", "license": "MIT", "type": "module", diff --git a/packages/plugins/workspace-worktree/package.json b/packages/plugins/workspace-worktree/package.json index d7e6fb309..840831c81 100644 --- a/packages/plugins/workspace-worktree/package.json +++ b/packages/plugins/workspace-worktree/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-plugin-workspace-worktree", - "version": "0.2.0", + "version": "0.2.2", "description": "Workspace plugin: git worktrees", "license": "MIT", "type": "module", diff --git a/packages/web/package.json b/packages/web/package.json index a0d3183fc..c4a11509c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "@aoagents/ao-web", - "version": "0.2.2", + "version": "0.2.4", "description": "Web dashboard for agent-orchestrator", "type": "module", "files": [