You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Stale metadata accumulation: Currently ~/.agentctl/opencode-sessions/ has 20+ pending-*.json files for dead sessions. These files are only cleaned up during isSessionRunning() checks — if nobody queries those sessions, they persist forever.
PID recycling false positives: Despite start-time cross-checking, the metadata can match a wrong process if the start time check fails (e.g., unparseable ps output, clock skew).
Inconsistency with daemon state: The adapter meta may say a session is alive (PID exists) while daemon state.json says it's stopped (wrapper exited), or vice versa. reconcileAndEnrich() tries to merge these, but the merge logic is complex and error-prone.
No TTL: Unlike adapter native files which are naturally scoped, these metadata files have no expiration mechanism.
User-visible symptom
Ghost sessions in agentctl list from stale metadata files
Inconsistent status between agentctl list (daemon) and agentctl status <id> (adapter direct query)
Slow discover() due to scanning metadata directories
Proposed fix
The LaunchedSessionMeta exists because adapters need PID information that isn't in their native storage (the adapter launched a detached process but the native session file doesn't record which PID is running it). The correct fix:
Short-lived: Make metadata files self-cleaning with a hard TTL (e.g., 24h). If the process isn't alive after 24h, the metadata is definitely stale.
Minimal: Only store the PID and process start time — drop cwd, model, prompt, launchedAt which duplicate adapter native data.
Eventually: Explore whether adapters can determine PID association without agentctl's help (e.g., Claude Code's --continue flag includes sessionId in args, making ps-based matching reliable).
What state is duplicated
Each adapter persists its own
LaunchedSessionMetaJSON files alongside the adapter's native storage:~/.claude/agentctl/sessions/<sessionId>.json~/.agentctl/opencode-sessions/<sessionId>.json~/.codex/agentctl/sessions/<sessionId>.jsonThese files contain:
sessionId,pid,startTime,wrapperPid,cwd,model,prompt,launchedAt.This creates three layers of state for a single session:
LaunchedSessionMetafiles (shadow feat(phase-1): agent-ctl core + Claude Code adapter #1)state.jsonSessionRecord(shadow feat(openclaw): add OpenClaw gateway adapter #2, see Arch violation: Daemon state.json maintains full session registry that shadows adapter ground truth #110)Where is the ground truth?
kill(pid, 0)andps auxwith start time cross-referencinglsofon the actual processHow does it desync?
~/.agentctl/opencode-sessions/has 20+pending-*.jsonfiles for dead sessions. These files are only cleaned up duringisSessionRunning()checks — if nobody queries those sessions, they persist forever.reconcileAndEnrich()tries to merge these, but the merge logic is complex and error-prone.User-visible symptom
agentctl listfrom stale metadata filesagentctl list(daemon) andagentctl status <id>(adapter direct query)discover()due to scanning metadata directoriesProposed fix
The LaunchedSessionMeta exists because adapters need PID information that isn't in their native storage (the adapter launched a detached process but the native session file doesn't record which PID is running it). The correct fix:
--continueflag includes sessionId in args, making ps-based matching reliable).Related: #110