@ai-devkit/agent-manager 0.26.2 → 0.26.4

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.
Files changed (104) hide show
  1. package/dist/AgentManager.d.ts +5 -2
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +28 -10
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +289 -4
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +13 -4
  8. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
  9. package/dist/__tests__/adapters/CodexAdapter.test.js +13 -4
  10. package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
  11. package/dist/__tests__/adapters/CopilotAdapter.test.js +7 -2
  12. package/dist/__tests__/adapters/CopilotAdapter.test.js.map +1 -1
  13. package/dist/__tests__/adapters/GeminiCliAdapter.test.js +18 -5
  14. package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +1 -1
  15. package/dist/__tests__/adapters/GrokCliAdapter.test.js +7 -2
  16. package/dist/__tests__/adapters/GrokCliAdapter.test.js.map +1 -1
  17. package/dist/__tests__/adapters/OpenCodeAdapter.test.js +13 -4
  18. package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +1 -1
  19. package/dist/__tests__/adapters/PiAdapter.test.js +32 -4
  20. package/dist/__tests__/adapters/PiAdapter.test.js.map +1 -1
  21. package/dist/__tests__/utils/AgentRegistry.test.js +140 -0
  22. package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
  23. package/dist/__tests__/utils/process.test.js +90 -2
  24. package/dist/__tests__/utils/process.test.js.map +1 -1
  25. package/dist/adapters/AgentAdapter.d.ts +9 -3
  26. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  27. package/dist/adapters/AgentAdapter.js.map +1 -1
  28. package/dist/adapters/ClaudeCodeAdapter.d.ts +5 -4
  29. package/dist/adapters/ClaudeCodeAdapter.d.ts.map +1 -1
  30. package/dist/adapters/ClaudeCodeAdapter.js +11 -7
  31. package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
  32. package/dist/adapters/CodexAdapter.d.ts +5 -4
  33. package/dist/adapters/CodexAdapter.d.ts.map +1 -1
  34. package/dist/adapters/CodexAdapter.js +11 -7
  35. package/dist/adapters/CodexAdapter.js.map +1 -1
  36. package/dist/adapters/CopilotAdapter.d.ts +5 -4
  37. package/dist/adapters/CopilotAdapter.d.ts.map +1 -1
  38. package/dist/adapters/CopilotAdapter.js +11 -7
  39. package/dist/adapters/CopilotAdapter.js.map +1 -1
  40. package/dist/adapters/GeminiCliAdapter.d.ts +6 -5
  41. package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -1
  42. package/dist/adapters/GeminiCliAdapter.js +12 -8
  43. package/dist/adapters/GeminiCliAdapter.js.map +1 -1
  44. package/dist/adapters/GrokCliAdapter.d.ts +3 -2
  45. package/dist/adapters/GrokCliAdapter.d.ts.map +1 -1
  46. package/dist/adapters/GrokCliAdapter.js +10 -6
  47. package/dist/adapters/GrokCliAdapter.js.map +1 -1
  48. package/dist/adapters/OpenCodeAdapter.d.ts +5 -4
  49. package/dist/adapters/OpenCodeAdapter.d.ts.map +1 -1
  50. package/dist/adapters/OpenCodeAdapter.js +11 -7
  51. package/dist/adapters/OpenCodeAdapter.js.map +1 -1
  52. package/dist/adapters/PiAdapter.d.ts +3 -2
  53. package/dist/adapters/PiAdapter.d.ts.map +1 -1
  54. package/dist/adapters/PiAdapter.js +12 -9
  55. package/dist/adapters/PiAdapter.js.map +1 -1
  56. package/dist/adapters/index.d.ts +1 -1
  57. package/dist/adapters/index.d.ts.map +1 -1
  58. package/dist/adapters/index.js.map +1 -1
  59. package/dist/database/connection.d.ts +1 -1
  60. package/dist/database/connection.d.ts.map +1 -1
  61. package/dist/database/connection.js +1 -1
  62. package/dist/database/connection.js.map +1 -1
  63. package/dist/index.d.ts +3 -2
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +1 -0
  66. package/dist/index.js.map +1 -1
  67. package/dist/utils/AgentRegistry.d.ts +14 -1
  68. package/dist/utils/AgentRegistry.d.ts.map +1 -1
  69. package/dist/utils/AgentRegistry.js +65 -20
  70. package/dist/utils/AgentRegistry.js.map +1 -1
  71. package/dist/utils/index.d.ts +1 -1
  72. package/dist/utils/index.d.ts.map +1 -1
  73. package/dist/utils/index.js +1 -1
  74. package/dist/utils/index.js.map +1 -1
  75. package/dist/utils/process.d.ts +9 -1
  76. package/dist/utils/process.d.ts.map +1 -1
  77. package/dist/utils/process.js +155 -10
  78. package/dist/utils/process.js.map +1 -1
  79. package/package.json +1 -1
  80. package/src/AgentManager.ts +35 -8
  81. package/src/__tests__/AgentManager.test.ts +231 -4
  82. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +16 -5
  83. package/src/__tests__/adapters/CodexAdapter.test.ts +16 -5
  84. package/src/__tests__/adapters/CopilotAdapter.test.ts +8 -1
  85. package/src/__tests__/adapters/GeminiCliAdapter.test.ts +20 -4
  86. package/src/__tests__/adapters/GrokCliAdapter.test.ts +8 -1
  87. package/src/__tests__/adapters/OpenCodeAdapter.test.ts +16 -5
  88. package/src/__tests__/adapters/PiAdapter.test.ts +28 -5
  89. package/src/__tests__/utils/AgentRegistry.test.ts +119 -0
  90. package/src/__tests__/utils/process.test.ts +72 -1
  91. package/src/adapters/AgentAdapter.ts +11 -3
  92. package/src/adapters/ClaudeCodeAdapter.ts +10 -7
  93. package/src/adapters/CodexAdapter.ts +10 -7
  94. package/src/adapters/CopilotAdapter.ts +16 -7
  95. package/src/adapters/GeminiCliAdapter.ts +17 -8
  96. package/src/adapters/GrokCliAdapter.ts +9 -6
  97. package/src/adapters/OpenCodeAdapter.ts +10 -7
  98. package/src/adapters/PiAdapter.ts +10 -9
  99. package/src/adapters/index.ts +1 -1
  100. package/src/database/connection.ts +4 -2
  101. package/src/index.ts +3 -1
  102. package/src/utils/AgentRegistry.ts +85 -17
  103. package/src/utils/index.ts +1 -1
  104. package/src/utils/process.ts +160 -10
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/ClaudeCodeAdapter.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n} from './AgentAdapter.js';\nimport { AgentStatus } from './AgentAdapter.js';\nimport { listAgentProcesses, enrichProcesses } from '../utils/process.js';\nimport { batchGetSessionFileBirthtimes, isDirectory, listJsonl, safeReaddir, safeStat } from '../utils/session.js';\nimport type { SessionFile } from '../utils/session.js';\nimport { matchProcessesToSessions, generateAgentName } from '../utils/matching.js';\nimport { ClaudeSessionParser } from '../utils/ClaudeSessionParser.js';\nimport type { ClaudeSession } from '../utils/ClaudeSessionParser.js';\n\n/**\n * Entry in ~/.claude/sessions/<pid>.json written by Claude Code.\n * Maps a running process to its session file via PID.\n */\ninterface PidFileEntry {\n pid: number;\n sessionId: string;\n cwd: string;\n /** Epoch milliseconds when the Claude Code process started */\n startedAt: number;\n kind: string;\n entrypoint: string;\n /**\n * Authoritative live status published by the Claude Code process\n * (e.g., 'running', 'waiting', 'idle'). Preferred over JSONL-derived\n * status because trailing entries like 'permission-mode' / 'ai-title'\n * can mask the real conversational state.\n */\n status?: string;\n /** Short description of what the agent is waiting on (e.g., \"approve Read\"). */\n waitingFor?: string;\n}\n\n/**\n * A process directly matched to a session via PID file (authoritative path).\n *\n * When the matching PID file also exposes live status/waitingFor metadata,\n * those values are carried here so `mapSessionToAgent` can prefer them\n * over the JSONL-derived heuristic.\n */\ninterface DirectMatch {\n process: ProcessInfo;\n sessionFile: SessionFile;\n pidStatus?: AgentStatus;\n waitingFor?: string;\n}\n\n/** Maximum allowed delta (ms) between process start time and PID file startedAt. */\nconst PID_FILE_STALENESS_MS = 60000;\n\n/**\n * Claude Code Adapter\n *\n * Detects Claude Code agents by:\n * 1. Finding running claude processes via shared listAgentProcesses()\n * 2. Enriching with CWD and start times via shared enrichProcesses()\n * 3. Attempting authoritative PID-file matching via ~/.claude/sessions/<pid>.json\n * 4. Falling back to CWD+birthtime heuristic (matchProcessesToSessions) for processes without a PID file\n * 5. Extracting summary from last user message in session JSONL\n */\nexport class ClaudeCodeAdapter implements AgentAdapter {\n readonly type = 'claude' as const;\n\n private projectsDir: string;\n private sessionsDir: string;\n private parser: ClaudeSessionParser;\n\n constructor() {\n const homeDir = process.env.HOME || process.env.USERPROFILE || '';\n this.projectsDir = path.join(homeDir, '.claude', 'projects');\n this.sessionsDir = path.join(homeDir, '.claude', 'sessions');\n this.parser = new ClaudeSessionParser();\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n return this.isClaudeExecutable(processInfo.command);\n }\n\n private isClaudeExecutable(command: string): boolean {\n const executable = command.trim().split(/\\s+/)[0] || '';\n const base = path.basename(executable).toLowerCase();\n return base === 'claude' || base === 'claude.exe';\n }\n\n async detectAgents(): Promise<AgentInfo[]> {\n const processes = enrichProcesses(listAgentProcesses('claude'));\n if (processes.length === 0) {\n return [];\n }\n\n // Step 1: extract `--resume <id>` from command line — authoritative for\n // resumed sessions where the JSONL predates the process and PID-file/\n // birthtime heuristics can't match it.\n const { direct: resumeDirect, fallback: noResume } = this.tryResumeMatching(processes);\n\n // Step 2: try authoritative PID-file matching for the rest\n const { direct: pidDirect, fallback } = this.tryPidFileMatching(noResume);\n\n const direct = [...resumeDirect, ...pidDirect];\n\n // Step 3: run legacy CWD+birthtime matching only for processes without a PID file\n const legacySessions = this.discoverSessions(fallback);\n const legacyMatches =\n fallback.length > 0 && legacySessions.length > 0\n ? matchProcessesToSessions(fallback, legacySessions)\n : [];\n\n const matchedPids = new Set([\n ...direct.map((d) => d.process.pid),\n ...legacyMatches.map((m) => m.process.pid),\n ]);\n\n const agents: AgentInfo[] = [];\n\n // Build agents from direct (resume + PID-file) matches\n for (const match of direct) {\n const { process: proc, sessionFile } = match;\n const sessionData = this.parser.readSession(sessionFile.filePath, sessionFile.resolvedCwd);\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, proc, sessionFile, {\n pidStatus: match.pidStatus,\n waitingFor: match.waitingFor,\n }));\n } else {\n matchedPids.delete(proc.pid);\n }\n }\n\n // Build agents from legacy matches\n for (const match of legacyMatches) {\n const sessionData = this.parser.readSession(\n match.session.filePath,\n match.session.resolvedCwd,\n );\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, match.process, match.session));\n } else {\n matchedPids.delete(match.process.pid);\n }\n }\n\n // Any process with no match (direct or legacy) appears as IDLE\n for (const proc of processes) {\n if (!matchedPids.has(proc.pid)) {\n agents.push(this.mapProcessOnlyAgent(proc));\n }\n }\n\n return agents;\n }\n\n /**\n * Discover session files for the given processes.\n *\n * For each unique process CWD, encodes it to derive the expected\n * ~/.claude/projects/<encoded>/ directory, then gets session file birthtimes\n * via a single batched stat call across all directories.\n */\n private discoverSessions(processes: ProcessInfo[]): SessionFile[] {\n const dirToCwd = new Map<string, string>();\n\n for (const proc of processes) {\n if (!proc.cwd) continue;\n\n const projectDir = this.getProjectDir(proc.cwd);\n if (dirToCwd.has(projectDir)) continue;\n\n try {\n if (!fs.statSync(projectDir).isDirectory()) continue;\n } catch {\n continue;\n }\n\n dirToCwd.set(projectDir, proc.cwd);\n }\n\n if (dirToCwd.size === 0) return [];\n\n const files = batchGetSessionFileBirthtimes([...dirToCwd.keys()]);\n\n for (const file of files) {\n file.resolvedCwd = dirToCwd.get(file.projectDir) || '';\n }\n\n return files;\n }\n\n /**\n * Match processes via `claude --resume <uuid>` in their command line.\n * This works for resumed sessions, where the JSONL was created earlier\n * (so its birthtime is far from the process startTime and the legacy\n * matcher can't pair them) and the PID file may also be misaligned.\n */\n private tryResumeMatching(processes: ProcessInfo[]): {\n direct: DirectMatch[];\n fallback: ProcessInfo[];\n } {\n const direct: DirectMatch[] = [];\n const fallback: ProcessInfo[] = [];\n\n for (const proc of processes) {\n const sessionId = this.extractResumeSessionId(proc.command);\n if (!sessionId || !proc.cwd) {\n fallback.push(proc);\n continue;\n }\n\n const projectDir = this.getProjectDir(proc.cwd);\n const jsonlPath = path.join(projectDir, `${sessionId}.jsonl`);\n\n const stat = safeStat(jsonlPath);\n if (!stat) {\n fallback.push(proc);\n continue;\n }\n\n // Best-effort: the PID file (if present for this proc) is the\n // authoritative source of live status. We still match the session\n // via --resume, but we read the PID file alongside to capture\n // status/waitingFor.\n const pidEntry = this.readMatchingPidFile(proc.pid, proc.startTime);\n\n direct.push({\n process: proc,\n sessionFile: {\n sessionId,\n filePath: jsonlPath,\n projectDir,\n birthtimeMs: stat.birthtimeMs,\n resolvedCwd: proc.cwd,\n },\n pidStatus: this.mapPidStatus(pidEntry?.status),\n waitingFor: pidEntry?.waitingFor,\n });\n }\n\n return { direct, fallback };\n }\n\n private extractResumeSessionId(command: string): string | null {\n const match = command.match(/--resume\\s+([0-9a-f-]{36})/i);\n return match?.[1] ?? null;\n }\n\n /**\n * Read and parse ~/.claude/sessions/<pid>.json, returning null on any\n * I/O / parse failure or when the file is stale relative to the live\n * process.\n *\n * \"Stale\" means the PID file's startedAt diverges from the process's\n * start time by more than {@link PID_FILE_STALENESS_MS} — typically\n * a previous Claude Code process recycled the same PID without cleanup.\n */\n private readMatchingPidFile(pid: number, procStartTime?: Date): PidFileEntry | null {\n const pidFilePath = path.join(this.sessionsDir, `${pid}.json`);\n try {\n const entry = JSON.parse(\n fs.readFileSync(pidFilePath, 'utf-8'),\n ) as PidFileEntry;\n\n if (procStartTime) {\n const deltaMs = Math.abs(procStartTime.getTime() - entry.startedAt);\n if (deltaMs > PID_FILE_STALENESS_MS) {\n return null;\n }\n }\n\n return entry;\n } catch {\n return null;\n }\n }\n\n /**\n * Map the PID file's live status string to {@link AgentStatus}.\n *\n * Returns undefined for missing / unrecognized values so the caller\n * can fall back to JSONL-derived heuristics.\n */\n private mapPidStatus(status: string | undefined): AgentStatus | undefined {\n switch (status) {\n case 'running':\n return AgentStatus.RUNNING;\n case 'waiting':\n return AgentStatus.WAITING;\n case 'idle':\n return AgentStatus.IDLE;\n default:\n return undefined;\n }\n }\n\n /**\n * Attempt to match each process to its session via ~/.claude/sessions/<pid>.json.\n *\n * Returns:\n * direct — processes matched authoritatively via PID file\n * fallback — processes with no valid PID file (sent to legacy matching)\n *\n * Per-process fallback triggers on: file absent, malformed JSON,\n * stale startedAt (>60s from proc.startTime), or missing JSONL.\n */\n private tryPidFileMatching(processes: ProcessInfo[]): {\n direct: DirectMatch[];\n fallback: ProcessInfo[];\n } {\n const direct: DirectMatch[] = [];\n const fallback: ProcessInfo[] = [];\n\n for (const proc of processes) {\n const entry = this.readMatchingPidFile(proc.pid, proc.startTime);\n if (!entry) {\n fallback.push(proc);\n continue;\n }\n\n const projectDir = this.getProjectDir(entry.cwd);\n const jsonlPath = path.join(projectDir, `${entry.sessionId}.jsonl`);\n\n if (!fs.existsSync(jsonlPath)) {\n fallback.push(proc);\n continue;\n }\n\n direct.push({\n process: proc,\n sessionFile: {\n sessionId: entry.sessionId,\n filePath: jsonlPath,\n projectDir,\n birthtimeMs: entry.startedAt,\n resolvedCwd: entry.cwd,\n },\n pidStatus: this.mapPidStatus(entry.status),\n waitingFor: entry.waitingFor,\n });\n }\n\n return { direct, fallback };\n }\n\n /**\n * Derive the Claude Code project directory for a given CWD.\n *\n * Claude Code encodes paths by replacing every non-alphanumeric\n * character with '-', so '/', '_', '.', spaces, etc. all collapse:\n * /Users/foo/bar → -Users-foo-bar\n * /Users/foo/my_project → -Users-foo-my-project\n * /Users/foo/.worktrees/x → -Users-foo--worktrees-x\n *\n * The encoding is lossy — multiple real paths can collide on the\n * same encoded dir. Callers that need to disambiguate must read the\n * `cwd` field inside each session JSONL.\n */\n private getProjectDir(cwd: string): string {\n const encoded = cwd.replace(/[^a-zA-Z0-9]/g, '-');\n return path.join(this.projectsDir, encoded);\n }\n\n private mapSessionToAgent(\n session: ClaudeSession,\n processInfo: ProcessInfo,\n sessionFile: SessionFile,\n liveInfo?: { pidStatus?: AgentStatus; waitingFor?: string },\n ): AgentInfo {\n // Live PID-file status is authoritative when present — JSONL-derived\n // status mis-classifies sessions whose latest entry is a UI-state\n // event like `permission-mode` or `ai-title`.\n const status = liveInfo?.pidStatus ?? this.parser.determineStatus(session);\n const baseSummary = session.lastUserMessage || 'Session started';\n const summary = status === AgentStatus.WAITING && liveInfo?.waitingFor\n ? `${baseSummary} — waiting for ${liveInfo.waitingFor}`\n : baseSummary;\n\n return {\n name: generateAgentName(processInfo.cwd, processInfo.pid),\n type: this.type,\n status,\n summary,\n pid: processInfo.pid,\n projectPath: sessionFile.resolvedCwd || processInfo.cwd || '',\n sessionId: sessionFile.sessionId,\n lastActive: session.lastActive,\n sessionFilePath: sessionFile.filePath,\n };\n }\n\n private mapProcessOnlyAgent(processInfo: ProcessInfo): AgentInfo {\n return {\n name: generateAgentName(processInfo.cwd || '', processInfo.pid),\n type: this.type,\n status: AgentStatus.IDLE,\n summary: 'Unknown',\n pid: processInfo.pid,\n projectPath: processInfo.cwd || '',\n sessionId: `pid-${processInfo.pid}`,\n lastActive: new Date(),\n };\n }\n\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n return this.parser.getConversation(sessionFilePath, options);\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n const filterCwd = opts?.cwd;\n const candidates = this.discoverSessionFiles();\n const summaries: SessionSummary[] = [];\n\n for (const { filePath, defaultCwd } of candidates) {\n const session = this.parser.readSession(filePath, defaultCwd);\n if (!session) continue;\n\n // Drop sessions whose JSONL had no parseable conversation entries.\n // readSession is permissive (returns a shell record even when every\n // line fails to parse); listSessions needs at least one real entry\n // so we don't surface garbage files.\n if (!session.lastEntryType) continue;\n\n const recordedCwd = session.lastCwd || defaultCwd;\n if (filterCwd !== undefined && recordedCwd !== filterCwd) continue;\n\n const stat = safeStat(filePath);\n\n summaries.push({\n type: 'claude',\n sessionId: session.sessionId,\n cwd: recordedCwd,\n firstUserMessage: session.firstUserMessage || '',\n lastActive: session.lastActive ?? stat?.mtime ?? new Date(),\n startedAt: session.sessionStart ?? stat?.birthtime ?? stat?.mtime ?? new Date(),\n sessionFilePath: filePath,\n });\n }\n\n return summaries;\n }\n\n /**\n * Discover candidate session files for {@link listSessions}.\n *\n * Always walks every subdirectory of `projectsDir`. We can't use the\n * encoded-dir shortcut for the cwd-scoped path because Claude Code\n * indexes session files by where the *process was launched*, not by\n * the recorded `cwd` field inside the session — these diverge in\n * worktrees and similar setups. The cwd filter is applied later\n * against `session.lastCwd` so callers see exactly the sessions whose\n * recorded cwd matches.\n */\n private discoverSessionFiles(): Array<{ filePath: string; defaultCwd: string }> {\n const out: Array<{ filePath: string; defaultCwd: string }> = [];\n\n if (!isDirectory(this.projectsDir)) return out;\n\n for (const dirName of safeReaddir(this.projectsDir)) {\n const projectDir = path.join(this.projectsDir, dirName);\n if (!isDirectory(projectDir)) continue;\n\n // Best-effort decode for the rare case session content has no\n // recorded cwd: '-Users-foo-bar' → '/Users/foo/bar'. Lossy for\n // paths containing '-'; session content's lastCwd overrides\n // this when available.\n const decoded = dirName.replace(/-/g, '/');\n for (const name of listJsonl(projectDir)) {\n out.push({ filePath: path.join(projectDir, name), defaultCwd: decoded });\n }\n }\n\n return out;\n }\n}\n"],"names":["fs","path","AgentStatus","listAgentProcesses","enrichProcesses","batchGetSessionFileBirthtimes","isDirectory","listJsonl","safeReaddir","safeStat","matchProcessesToSessions","generateAgentName","ClaudeSessionParser","PID_FILE_STALENESS_MS","ClaudeCodeAdapter","type","projectsDir","sessionsDir","parser","homeDir","process","env","HOME","USERPROFILE","join","canHandle","processInfo","isClaudeExecutable","command","executable","trim","split","base","basename","toLowerCase","detectAgents","processes","length","direct","resumeDirect","fallback","noResume","tryResumeMatching","pidDirect","tryPidFileMatching","legacySessions","discoverSessions","legacyMatches","matchedPids","Set","map","d","pid","m","agents","match","proc","sessionFile","sessionData","readSession","filePath","resolvedCwd","push","mapSessionToAgent","pidStatus","waitingFor","delete","session","has","mapProcessOnlyAgent","dirToCwd","Map","cwd","projectDir","getProjectDir","statSync","set","size","files","keys","file","get","sessionId","extractResumeSessionId","jsonlPath","stat","pidEntry","readMatchingPidFile","startTime","birthtimeMs","mapPidStatus","status","procStartTime","pidFilePath","entry","JSON","parse","readFileSync","deltaMs","Math","abs","getTime","startedAt","RUNNING","WAITING","IDLE","undefined","existsSync","encoded","replace","liveInfo","determineStatus","baseSummary","lastUserMessage","summary","name","projectPath","lastActive","sessionFilePath","Date","getConversation","options","listSessions","opts","filterCwd","candidates","discoverSessionFiles","summaries","defaultCwd","lastEntryType","recordedCwd","lastCwd","firstUserMessage","mtime","sessionStart","birthtime","out","dirName","decoded"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/adapters/ClaudeCodeAdapter.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from './AgentAdapter.js';\nimport { AgentStatus } from './AgentAdapter.js';\nimport { captureProcessSnapshot, executableBasename, filterByProcessNames } from '../utils/process.js';\nimport { batchGetSessionFileBirthtimes, isDirectory, listJsonl, safeReaddir, safeStat } from '../utils/session.js';\nimport type { SessionFile } from '../utils/session.js';\nimport { matchProcessesToSessions, generateAgentName } from '../utils/matching.js';\nimport { ClaudeSessionParser } from '../utils/ClaudeSessionParser.js';\nimport type { ClaudeSession } from '../utils/ClaudeSessionParser.js';\n\n/**\n * Entry in ~/.claude/sessions/<pid>.json written by Claude Code.\n * Maps a running process to its session file via PID.\n */\ninterface PidFileEntry {\n pid: number;\n sessionId: string;\n cwd: string;\n /** Epoch milliseconds when the Claude Code process started */\n startedAt: number;\n kind: string;\n entrypoint: string;\n /**\n * Authoritative live status published by the Claude Code process\n * (e.g., 'running', 'waiting', 'idle'). Preferred over JSONL-derived\n * status because trailing entries like 'permission-mode' / 'ai-title'\n * can mask the real conversational state.\n */\n status?: string;\n /** Short description of what the agent is waiting on (e.g., \"approve Read\"). */\n waitingFor?: string;\n}\n\n/**\n * A process directly matched to a session via PID file (authoritative path).\n *\n * When the matching PID file also exposes live status/waitingFor metadata,\n * those values are carried here so `mapSessionToAgent` can prefer them\n * over the JSONL-derived heuristic.\n */\ninterface DirectMatch {\n process: ProcessInfo;\n sessionFile: SessionFile;\n pidStatus?: AgentStatus;\n waitingFor?: string;\n}\n\n/** Maximum allowed delta (ms) between process start time and PID file startedAt. */\nconst PID_FILE_STALENESS_MS = 60000;\n\n/**\n * Claude Code Adapter\n *\n * Detects Claude Code agents by:\n * 1. Filtering Claude processes from a shared asynchronous process snapshot\n * 2. Using snapshot CWD and start-time enrichment\n * 3. Attempting authoritative PID-file matching via ~/.claude/sessions/<pid>.json\n * 4. Falling back to CWD+birthtime heuristic (matchProcessesToSessions) for processes without a PID file\n * 5. Extracting summary from last user message in session JSONL\n */\nexport class ClaudeCodeAdapter implements AgentAdapter {\n readonly type = 'claude' as const;\n readonly processNames = ['claude'] as const;\n\n private projectsDir: string;\n private sessionsDir: string;\n private parser: ClaudeSessionParser;\n\n constructor() {\n const homeDir = process.env.HOME || process.env.USERPROFILE || '';\n this.projectsDir = path.join(homeDir, '.claude', 'projects');\n this.sessionsDir = path.join(homeDir, '.claude', 'sessions');\n this.parser = new ClaudeSessionParser();\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n return this.isClaudeExecutable(processInfo.command);\n }\n\n private isClaudeExecutable(command: string): boolean {\n const base = executableBasename(command);\n return base === 'claude' || base === 'claude.exe';\n }\n\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const snapshot = context?.processes ?? await captureProcessSnapshot(this.processNames);\n const relevant = filterByProcessNames(snapshot, this.processNames);\n const processes = relevant.filter((process) => this.canHandle(process));\n if (processes.length === 0) {\n return [];\n }\n\n // Step 1: extract `--resume <id>` from command line — authoritative for\n // resumed sessions where the JSONL predates the process and PID-file/\n // birthtime heuristics can't match it.\n const { direct: resumeDirect, fallback: noResume } = this.tryResumeMatching(processes);\n\n // Step 2: try authoritative PID-file matching for the rest\n const { direct: pidDirect, fallback } = this.tryPidFileMatching(noResume);\n\n const direct = [...resumeDirect, ...pidDirect];\n\n // Step 3: run legacy CWD+birthtime matching only for processes without a PID file\n const legacySessions = this.discoverSessions(fallback);\n const legacyMatches =\n fallback.length > 0 && legacySessions.length > 0\n ? matchProcessesToSessions(fallback, legacySessions)\n : [];\n\n const matchedPids = new Set([\n ...direct.map((d) => d.process.pid),\n ...legacyMatches.map((m) => m.process.pid),\n ]);\n\n const agents: AgentInfo[] = [];\n\n // Build agents from direct (resume + PID-file) matches\n for (const match of direct) {\n const { process: proc, sessionFile } = match;\n const sessionData = this.parser.readSession(sessionFile.filePath, sessionFile.resolvedCwd);\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, proc, sessionFile, {\n pidStatus: match.pidStatus,\n waitingFor: match.waitingFor,\n }));\n } else {\n matchedPids.delete(proc.pid);\n }\n }\n\n // Build agents from legacy matches\n for (const match of legacyMatches) {\n const sessionData = this.parser.readSession(\n match.session.filePath,\n match.session.resolvedCwd,\n );\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, match.process, match.session));\n } else {\n matchedPids.delete(match.process.pid);\n }\n }\n\n // Any process with no match (direct or legacy) appears as IDLE\n for (const proc of processes) {\n if (!matchedPids.has(proc.pid)) {\n agents.push(this.mapProcessOnlyAgent(proc));\n }\n }\n\n return agents;\n }\n\n /**\n * Discover session files for the given processes.\n *\n * For each unique process CWD, encodes it to derive the expected\n * ~/.claude/projects/<encoded>/ directory, then gets session file birthtimes\n * via a single batched stat call across all directories.\n */\n private discoverSessions(processes: ProcessInfo[]): SessionFile[] {\n const dirToCwd = new Map<string, string>();\n\n for (const proc of processes) {\n if (!proc.cwd) continue;\n\n const projectDir = this.getProjectDir(proc.cwd);\n if (dirToCwd.has(projectDir)) continue;\n\n try {\n if (!fs.statSync(projectDir).isDirectory()) continue;\n } catch {\n continue;\n }\n\n dirToCwd.set(projectDir, proc.cwd);\n }\n\n if (dirToCwd.size === 0) return [];\n\n const files = batchGetSessionFileBirthtimes([...dirToCwd.keys()]);\n\n for (const file of files) {\n file.resolvedCwd = dirToCwd.get(file.projectDir) || '';\n }\n\n return files;\n }\n\n /**\n * Match processes via `claude --resume <uuid>` in their command line.\n * This works for resumed sessions, where the JSONL was created earlier\n * (so its birthtime is far from the process startTime and the legacy\n * matcher can't pair them) and the PID file may also be misaligned.\n */\n private tryResumeMatching(processes: ProcessInfo[]): {\n direct: DirectMatch[];\n fallback: ProcessInfo[];\n } {\n const direct: DirectMatch[] = [];\n const fallback: ProcessInfo[] = [];\n\n for (const proc of processes) {\n const sessionId = this.extractResumeSessionId(proc.command);\n if (!sessionId || !proc.cwd) {\n fallback.push(proc);\n continue;\n }\n\n const projectDir = this.getProjectDir(proc.cwd);\n const jsonlPath = path.join(projectDir, `${sessionId}.jsonl`);\n\n const stat = safeStat(jsonlPath);\n if (!stat) {\n fallback.push(proc);\n continue;\n }\n\n // Best-effort: the PID file (if present for this proc) is the\n // authoritative source of live status. We still match the session\n // via --resume, but we read the PID file alongside to capture\n // status/waitingFor.\n const pidEntry = this.readMatchingPidFile(proc.pid, proc.startTime);\n\n direct.push({\n process: proc,\n sessionFile: {\n sessionId,\n filePath: jsonlPath,\n projectDir,\n birthtimeMs: stat.birthtimeMs,\n resolvedCwd: proc.cwd,\n },\n pidStatus: this.mapPidStatus(pidEntry?.status),\n waitingFor: pidEntry?.waitingFor,\n });\n }\n\n return { direct, fallback };\n }\n\n private extractResumeSessionId(command: string): string | null {\n const match = command.match(/--resume\\s+([0-9a-f-]{36})/i);\n return match?.[1] ?? null;\n }\n\n /**\n * Read and parse ~/.claude/sessions/<pid>.json, returning null on any\n * I/O / parse failure or when the file is stale relative to the live\n * process.\n *\n * \"Stale\" means the PID file's startedAt diverges from the process's\n * start time by more than {@link PID_FILE_STALENESS_MS} — typically\n * a previous Claude Code process recycled the same PID without cleanup.\n */\n private readMatchingPidFile(pid: number, procStartTime?: Date): PidFileEntry | null {\n const pidFilePath = path.join(this.sessionsDir, `${pid}.json`);\n try {\n const entry = JSON.parse(\n fs.readFileSync(pidFilePath, 'utf-8'),\n ) as PidFileEntry;\n\n if (procStartTime) {\n const deltaMs = Math.abs(procStartTime.getTime() - entry.startedAt);\n if (deltaMs > PID_FILE_STALENESS_MS) {\n return null;\n }\n }\n\n return entry;\n } catch {\n return null;\n }\n }\n\n /**\n * Map the PID file's live status string to {@link AgentStatus}.\n *\n * Returns undefined for missing / unrecognized values so the caller\n * can fall back to JSONL-derived heuristics.\n */\n private mapPidStatus(status: string | undefined): AgentStatus | undefined {\n switch (status) {\n case 'running':\n return AgentStatus.RUNNING;\n case 'waiting':\n return AgentStatus.WAITING;\n case 'idle':\n return AgentStatus.IDLE;\n default:\n return undefined;\n }\n }\n\n /**\n * Attempt to match each process to its session via ~/.claude/sessions/<pid>.json.\n *\n * Returns:\n * direct — processes matched authoritatively via PID file\n * fallback — processes with no valid PID file (sent to legacy matching)\n *\n * Per-process fallback triggers on: file absent, malformed JSON,\n * stale startedAt (>60s from proc.startTime), or missing JSONL.\n */\n private tryPidFileMatching(processes: ProcessInfo[]): {\n direct: DirectMatch[];\n fallback: ProcessInfo[];\n } {\n const direct: DirectMatch[] = [];\n const fallback: ProcessInfo[] = [];\n\n for (const proc of processes) {\n const entry = this.readMatchingPidFile(proc.pid, proc.startTime);\n if (!entry) {\n fallback.push(proc);\n continue;\n }\n\n const projectDir = this.getProjectDir(entry.cwd);\n const jsonlPath = path.join(projectDir, `${entry.sessionId}.jsonl`);\n\n if (!fs.existsSync(jsonlPath)) {\n fallback.push(proc);\n continue;\n }\n\n direct.push({\n process: proc,\n sessionFile: {\n sessionId: entry.sessionId,\n filePath: jsonlPath,\n projectDir,\n birthtimeMs: entry.startedAt,\n resolvedCwd: entry.cwd,\n },\n pidStatus: this.mapPidStatus(entry.status),\n waitingFor: entry.waitingFor,\n });\n }\n\n return { direct, fallback };\n }\n\n /**\n * Derive the Claude Code project directory for a given CWD.\n *\n * Claude Code encodes paths by replacing every non-alphanumeric\n * character with '-', so '/', '_', '.', spaces, etc. all collapse:\n * /Users/foo/bar → -Users-foo-bar\n * /Users/foo/my_project → -Users-foo-my-project\n * /Users/foo/.worktrees/x → -Users-foo--worktrees-x\n *\n * The encoding is lossy — multiple real paths can collide on the\n * same encoded dir. Callers that need to disambiguate must read the\n * `cwd` field inside each session JSONL.\n */\n private getProjectDir(cwd: string): string {\n const encoded = cwd.replace(/[^a-zA-Z0-9]/g, '-');\n return path.join(this.projectsDir, encoded);\n }\n\n private mapSessionToAgent(\n session: ClaudeSession,\n processInfo: ProcessInfo,\n sessionFile: SessionFile,\n liveInfo?: { pidStatus?: AgentStatus; waitingFor?: string },\n ): AgentInfo {\n // Live PID-file status is authoritative when present — JSONL-derived\n // status mis-classifies sessions whose latest entry is a UI-state\n // event like `permission-mode` or `ai-title`.\n const status = liveInfo?.pidStatus ?? this.parser.determineStatus(session);\n const baseSummary = session.lastUserMessage || 'Session started';\n const summary = status === AgentStatus.WAITING && liveInfo?.waitingFor\n ? `${baseSummary} — waiting for ${liveInfo.waitingFor}`\n : baseSummary;\n\n return {\n name: generateAgentName(processInfo.cwd, processInfo.pid),\n type: this.type,\n status,\n summary,\n pid: processInfo.pid,\n projectPath: sessionFile.resolvedCwd || processInfo.cwd || '',\n sessionId: sessionFile.sessionId,\n lastActive: session.lastActive,\n sessionFilePath: sessionFile.filePath,\n };\n }\n\n private mapProcessOnlyAgent(processInfo: ProcessInfo): AgentInfo {\n return {\n name: generateAgentName(processInfo.cwd || '', processInfo.pid),\n type: this.type,\n status: AgentStatus.IDLE,\n summary: 'Unknown',\n pid: processInfo.pid,\n projectPath: processInfo.cwd || '',\n sessionId: `pid-${processInfo.pid}`,\n lastActive: new Date(),\n };\n }\n\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n return this.parser.getConversation(sessionFilePath, options);\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n const filterCwd = opts?.cwd;\n const candidates = this.discoverSessionFiles();\n const summaries: SessionSummary[] = [];\n\n for (const { filePath, defaultCwd } of candidates) {\n const session = this.parser.readSession(filePath, defaultCwd);\n if (!session) continue;\n\n // Drop sessions whose JSONL had no parseable conversation entries.\n // readSession is permissive (returns a shell record even when every\n // line fails to parse); listSessions needs at least one real entry\n // so we don't surface garbage files.\n if (!session.lastEntryType) continue;\n\n const recordedCwd = session.lastCwd || defaultCwd;\n if (filterCwd !== undefined && recordedCwd !== filterCwd) continue;\n\n const stat = safeStat(filePath);\n\n summaries.push({\n type: 'claude',\n sessionId: session.sessionId,\n cwd: recordedCwd,\n firstUserMessage: session.firstUserMessage || '',\n lastActive: session.lastActive ?? stat?.mtime ?? new Date(),\n startedAt: session.sessionStart ?? stat?.birthtime ?? stat?.mtime ?? new Date(),\n sessionFilePath: filePath,\n });\n }\n\n return summaries;\n }\n\n /**\n * Discover candidate session files for {@link listSessions}.\n *\n * Always walks every subdirectory of `projectsDir`. We can't use the\n * encoded-dir shortcut for the cwd-scoped path because Claude Code\n * indexes session files by where the *process was launched*, not by\n * the recorded `cwd` field inside the session — these diverge in\n * worktrees and similar setups. The cwd filter is applied later\n * against `session.lastCwd` so callers see exactly the sessions whose\n * recorded cwd matches.\n */\n private discoverSessionFiles(): Array<{ filePath: string; defaultCwd: string }> {\n const out: Array<{ filePath: string; defaultCwd: string }> = [];\n\n if (!isDirectory(this.projectsDir)) return out;\n\n for (const dirName of safeReaddir(this.projectsDir)) {\n const projectDir = path.join(this.projectsDir, dirName);\n if (!isDirectory(projectDir)) continue;\n\n // Best-effort decode for the rare case session content has no\n // recorded cwd: '-Users-foo-bar' → '/Users/foo/bar'. Lossy for\n // paths containing '-'; session content's lastCwd overrides\n // this when available.\n const decoded = dirName.replace(/-/g, '/');\n for (const name of listJsonl(projectDir)) {\n out.push({ filePath: path.join(projectDir, name), defaultCwd: decoded });\n }\n }\n\n return out;\n }\n}\n"],"names":["fs","path","AgentStatus","captureProcessSnapshot","executableBasename","filterByProcessNames","batchGetSessionFileBirthtimes","isDirectory","listJsonl","safeReaddir","safeStat","matchProcessesToSessions","generateAgentName","ClaudeSessionParser","PID_FILE_STALENESS_MS","ClaudeCodeAdapter","type","processNames","projectsDir","sessionsDir","parser","homeDir","process","env","HOME","USERPROFILE","join","canHandle","processInfo","isClaudeExecutable","command","base","detectAgents","context","snapshot","processes","relevant","filter","length","direct","resumeDirect","fallback","noResume","tryResumeMatching","pidDirect","tryPidFileMatching","legacySessions","discoverSessions","legacyMatches","matchedPids","Set","map","d","pid","m","agents","match","proc","sessionFile","sessionData","readSession","filePath","resolvedCwd","push","mapSessionToAgent","pidStatus","waitingFor","delete","session","has","mapProcessOnlyAgent","dirToCwd","Map","cwd","projectDir","getProjectDir","statSync","set","size","files","keys","file","get","sessionId","extractResumeSessionId","jsonlPath","stat","pidEntry","readMatchingPidFile","startTime","birthtimeMs","mapPidStatus","status","procStartTime","pidFilePath","entry","JSON","parse","readFileSync","deltaMs","Math","abs","getTime","startedAt","RUNNING","WAITING","IDLE","undefined","existsSync","encoded","replace","liveInfo","determineStatus","baseSummary","lastUserMessage","summary","name","projectPath","lastActive","sessionFilePath","Date","getConversation","options","listSessions","opts","filterCwd","candidates","discoverSessionFiles","summaries","defaultCwd","lastEntryType","recordedCwd","lastCwd","firstUserMessage","mtime","sessionStart","birthtime","out","dirName","decoded"],"mappings":"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"}
@@ -2,18 +2,19 @@
2
2
  * Codex Adapter
3
3
  *
4
4
  * Detects running Codex agents by:
5
- * 1. Finding running codex processes via shared listAgentProcesses()
6
- * 2. Enriching with CWD and start times via shared enrichProcesses()
5
+ * 1. Filtering Codex processes from a shared asynchronous process snapshot
6
+ * 2. Using snapshot CWD and start-time enrichment
7
7
  * 3. Matching exact PID-to-session metadata from ~/.codex/ai-devkit/sessions.json
8
8
  * 4. Discovering session files from ~/.codex/sessions/YYYY/MM/DD/ via shared batchGetSessionFileBirthtimes()
9
9
  * 5. Setting resolvedCwd from session_meta first line
10
10
  * 6. Matching sessions to processes via shared matchProcessesToSessions()
11
11
  * 7. Extracting summary from last event entry in session JSONL
12
12
  */
13
- import type { AgentAdapter, AgentInfo, ProcessInfo, ConversationMessage, SessionSummary, ListSessionsOptions } from './AgentAdapter.js';
13
+ import type { AgentAdapter, AgentInfo, ProcessInfo, ConversationMessage, SessionSummary, ListSessionsOptions, AgentDetectionContext } from './AgentAdapter.js';
14
14
  import { AgentRegistry } from '../utils/AgentRegistry.js';
15
15
  export declare class CodexAdapter implements AgentAdapter {
16
16
  readonly type: "codex";
17
+ readonly processNames: readonly ["codex"];
17
18
  private static readonly IDLE_THRESHOLD_MINUTES;
18
19
  /** Include session files around process start day to recover long-lived processes. */
19
20
  private static readonly PROCESS_START_DAY_WINDOW_DAYS;
@@ -25,7 +26,7 @@ export declare class CodexAdapter implements AgentAdapter {
25
26
  /**
26
27
  * Detect running Codex agents
27
28
  */
28
- detectAgents(): Promise<AgentInfo[]>;
29
+ detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;
29
30
  private mapSessionMappingMatches;
30
31
  private matchFromSessionMapping;
31
32
  private readSessionMapping;
@@ -1 +1 @@
1
- {"version":3,"file":"CodexAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/CodexAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACR,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACtB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA4D1D,qBAAa,YAAa,YAAW,YAAY;IAC7C,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IAEjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAK;IACnD,sFAAsF;IACtF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,6BAA6B,CAAK;IAE1D,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAgB;gBAEpB,QAAQ,GAAE,aAAuC;IAO7D,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAI5C;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAmD1C,OAAO,CAAC,wBAAwB;IAgBhC,OAAO,CAAC,uBAAuB;IAuB/B,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,gBAAgB;IAiCxB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,mBAAmB;IAiC3B,OAAO,CAAC,wBAAwB;IAWhC,OAAO,CAAC,gBAAgB;IAgBxB;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAsCxB;;;OAGG;IACH,OAAO,CAAC,WAAW;IA4BnB,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,kBAAkB;IAW1B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAwDpB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,qBAAqB;IAmB7B,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,iBAAiB;IAMzB;;;;;OAKG;IACH,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IA8ChG,OAAO,CAAC,qBAAqB;IAwC7B,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,kBAAkB;IAYpB,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAgBzE;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IA0B9B,OAAO,CAAC,6BAA6B;IAarC;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;CA8D/B"}
1
+ {"version":3,"file":"CodexAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/CodexAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACR,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACxB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA4D1D,qBAAa,YAAa,YAAW,YAAY;IAC7C,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IACjC,QAAQ,CAAC,YAAY,qBAAsB;IAE3C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAK;IACnD,sFAAsF;IACtF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,6BAA6B,CAAK;IAE1D,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAgB;gBAEpB,QAAQ,GAAE,aAAuC;IAO7D,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAI5C;;OAEG;IACG,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAqDzE,OAAO,CAAC,wBAAwB;IAgBhC,OAAO,CAAC,uBAAuB;IAuB/B,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,KAAK;IAQb,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,gBAAgB;IAiCxB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,mBAAmB;IAiC3B,OAAO,CAAC,wBAAwB;IAWhC,OAAO,CAAC,gBAAgB;IAgBxB;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAsCxB;;;OAGG;IACH,OAAO,CAAC,WAAW;IA4BnB,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,kBAAkB;IAW1B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAwDpB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,qBAAqB;IAmB7B,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,iBAAiB;IAKzB;;;;;OAKG;IACH,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IA8ChG,OAAO,CAAC,qBAAqB;IAwC7B,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,kBAAkB;IAYpB,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAgBzE;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IA0B9B,OAAO,CAAC,6BAA6B;IAarC;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;CA8D/B"}
@@ -2,8 +2,8 @@
2
2
  * Codex Adapter
3
3
  *
4
4
  * Detects running Codex agents by:
5
- * 1. Finding running codex processes via shared listAgentProcesses()
6
- * 2. Enriching with CWD and start times via shared enrichProcesses()
5
+ * 1. Filtering Codex processes from a shared asynchronous process snapshot
6
+ * 2. Using snapshot CWD and start-time enrichment
7
7
  * 3. Matching exact PID-to-session metadata from ~/.codex/ai-devkit/sessions.json
8
8
  * 4. Discovering session files from ~/.codex/sessions/YYYY/MM/DD/ via shared batchGetSessionFileBirthtimes()
9
9
  * 5. Setting resolvedCwd from session_meta first line
@@ -12,12 +12,15 @@
12
12
  */ import * as fs from 'fs';
13
13
  import * as path from 'path';
14
14
  import { AgentStatus } from './AgentAdapter.js';
15
- import { listAgentProcesses, enrichProcesses } from '../utils/process.js';
15
+ import { captureProcessSnapshot, executableBasename, filterByProcessNames } from '../utils/process.js';
16
16
  import { batchGetSessionFileBirthtimes, isDirectory, safeReadFile, safeReaddir, safeStat } from '../utils/session.js';
17
17
  import { matchProcessesToSessions, generateAgentName } from '../utils/matching.js';
18
18
  import { AgentRegistry } from '../utils/AgentRegistry.js';
19
19
  export class CodexAdapter {
20
20
  type = 'codex';
21
+ processNames = [
22
+ 'codex'
23
+ ];
21
24
  static IDLE_THRESHOLD_MINUTES = 5;
22
25
  /** Include session files around process start day to recover long-lived processes. */ static PROCESS_START_DAY_WINDOW_DAYS = 1;
23
26
  codexSessionsDir;
@@ -34,8 +37,10 @@ export class CodexAdapter {
34
37
  }
35
38
  /**
36
39
  * Detect running Codex agents
37
- */ async detectAgents() {
38
- const processes = enrichProcesses(listAgentProcesses('codex'));
40
+ */ async detectAgents(context) {
41
+ const snapshot = context?.processes ?? await captureProcessSnapshot(this.processNames);
42
+ const relevant = filterByProcessNames(snapshot, this.processNames);
43
+ const processes = relevant.filter((process1)=>this.canHandle(process1));
39
44
  if (processes.length === 0) return [];
40
45
  const { cachedAgents, remaining } = this.tryRegistryCache(processes);
41
46
  if (remaining.length === 0) return cachedAgents;
@@ -495,8 +500,7 @@ export class CodexAdapter {
495
500
  return `${value.slice(0, maxLength - 3)}...`;
496
501
  }
497
502
  isCodexExecutable(command) {
498
- const executable = command.trim().split(/\s+/)[0] || '';
499
- const base = path.basename(executable).toLowerCase();
503
+ const base = executableBasename(command);
500
504
  return base === 'codex' || base === 'codex.exe';
501
505
  }
502
506
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/CodexAdapter.ts"],"sourcesContent":["/**\n * Codex Adapter\n *\n * Detects running Codex agents by:\n * 1. Finding running codex processes via shared listAgentProcesses()\n * 2. Enriching with CWD and start times via shared enrichProcesses()\n * 3. Matching exact PID-to-session metadata from ~/.codex/ai-devkit/sessions.json\n * 4. Discovering session files from ~/.codex/sessions/YYYY/MM/DD/ via shared batchGetSessionFileBirthtimes()\n * 5. Setting resolvedCwd from session_meta first line\n * 6. Matching sessions to processes via shared matchProcessesToSessions()\n * 7. Extracting summary from last event entry in session JSONL\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n} from './AgentAdapter.js';\nimport { AgentStatus } from './AgentAdapter.js';\nimport { listAgentProcesses, enrichProcesses } from '../utils/process.js';\nimport { batchGetSessionFileBirthtimes, isDirectory, safeReadFile, safeReaddir, safeStat } from '../utils/session.js';\nimport type { SessionFile } from '../utils/session.js';\nimport { matchProcessesToSessions, generateAgentName } from '../utils/matching.js';\nimport { AgentRegistry } from '../utils/AgentRegistry.js';\n\ninterface CodexEventEntry {\n timestamp?: string;\n type?: string;\n payload?: {\n type?: string;\n message?: string;\n id?: string;\n cwd?: string;\n timestamp?: string;\n role?: string;\n content?: CodexContent[];\n item?: CodexItem;\n turn_id?: string;\n internal_chat_message_metadata_passthrough?: {\n turn_id?: string;\n };\n };\n}\n\ninterface CodexContent {\n type?: string;\n text?: string;\n}\n\ninterface CodexItem {\n type?: string;\n content?: string | CodexContent[];\n}\n\ninterface CodexSession {\n sessionId: string;\n projectPath: string;\n summary: string;\n sessionStart: Date;\n lastActive: Date;\n lastPayloadType?: string;\n}\n\ninterface DirectMatch {\n process: ProcessInfo;\n sessionFile: SessionFile;\n}\n\ninterface DirectMatchResult {\n agents: AgentInfo[];\n failedProcesses: ProcessInfo[];\n}\n\ninterface MappingMatch {\n process: ProcessInfo;\n filePath: string;\n}\n\ninterface MappingMatchResult {\n agents: AgentInfo[];\n fallback: ProcessInfo[];\n}\n\nexport class CodexAdapter implements AgentAdapter {\n readonly type = 'codex' as const;\n\n private static readonly IDLE_THRESHOLD_MINUTES = 5;\n /** Include session files around process start day to recover long-lived processes. */\n private static readonly PROCESS_START_DAY_WINDOW_DAYS = 1;\n\n private codexSessionsDir: string;\n private sessionMappingPath: string;\n private registry: AgentRegistry;\n\n constructor(registry: AgentRegistry = AgentRegistry.default()) {\n const homeDir = process.env.HOME || process.env.USERPROFILE || '';\n this.codexSessionsDir = path.join(homeDir, '.codex', 'sessions');\n this.sessionMappingPath = path.join(homeDir, '.codex', 'ai-devkit', 'sessions.json');\n this.registry = registry;\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n return this.isCodexExecutable(processInfo.command);\n }\n\n /**\n * Detect running Codex agents\n */\n async detectAgents(): Promise<AgentInfo[]> {\n const processes = enrichProcesses(listAgentProcesses('codex'));\n if (processes.length === 0) return [];\n\n const { cachedAgents, remaining } = this.tryRegistryCache(processes);\n if (remaining.length === 0) return cachedAgents;\n\n const mappingResult = this.mapSessionMappingMatches(remaining);\n const { direct, fallback } = this.tryResumeMatching(mappingResult.fallback);\n const directResult = this.mapDirectMatches(direct);\n const { sessions, contentCache } = this.discoverSessions(fallback);\n if (sessions.length === 0) {\n return [\n ...cachedAgents,\n ...mappingResult.agents,\n ...directResult.agents,\n ...directResult.failedProcesses.map((p) => this.mapProcessOnlyAgent(p)),\n ...fallback.map((p) => this.mapProcessOnlyAgent(p)),\n ];\n }\n\n const matches = matchProcessesToSessions(fallback, sessions);\n const matchedPids = new Set([\n ...directResult.agents.map((a) => a.pid),\n ...matches.map((m) => m.process.pid),\n ]);\n const agents: AgentInfo[] = [...directResult.agents];\n\n for (const match of matches) {\n const cachedContent = contentCache.get(match.session.filePath);\n const sessionData = this.parseSession(cachedContent, match.session.filePath);\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, match.process, match.session.filePath));\n } else {\n matchedPids.delete(match.process.pid);\n }\n }\n\n for (const proc of fallback) {\n if (!matchedPids.has(proc.pid)) {\n agents.push(this.mapProcessOnlyAgent(proc));\n }\n }\n\n for (const proc of directResult.failedProcesses) {\n agents.push(this.mapProcessOnlyAgent(proc));\n }\n\n return [...cachedAgents, ...mappingResult.agents, ...agents];\n }\n\n private mapSessionMappingMatches(processes: ProcessInfo[]): MappingMatchResult {\n const { matches, fallback } = this.matchFromSessionMapping(processes);\n const agents: AgentInfo[] = [];\n\n for (const match of matches) {\n const sessionData = this.parseSession(undefined, match.filePath);\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, match.process, match.filePath));\n } else {\n fallback.push(match.process);\n }\n }\n\n return { agents, fallback };\n }\n\n private matchFromSessionMapping(processes: ProcessInfo[]): {\n matches: MappingMatch[];\n fallback: ProcessInfo[];\n } {\n const mapping = this.readSessionMapping();\n if (mapping.size === 0) return { matches: [], fallback: processes };\n\n const matches: MappingMatch[] = [];\n const fallback: ProcessInfo[] = [];\n\n for (const proc of processes) {\n const filePath = mapping.get(proc.pid);\n if (!filePath || !this.isTrustedSessionPath(filePath) || !fs.existsSync(filePath)) {\n fallback.push(proc);\n continue;\n }\n\n matches.push({ process: proc, filePath });\n }\n\n return { matches, fallback };\n }\n\n private readSessionMapping(): Map<number, string> {\n const content = safeReadFile(this.sessionMappingPath);\n if (content === undefined) return new Map();\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n return new Map();\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return new Map();\n\n const map = new Map<number, string>();\n for (const [key, value] of Object.entries(parsed)) {\n const pid = this.toPid(key);\n if (pid !== null && typeof value === 'string' && value) {\n map.set(pid, value);\n }\n }\n\n return map;\n }\n\n private toPid(value: unknown): number | null {\n if (typeof value === 'number' && Number.isInteger(value) && value > 0) return value;\n if (typeof value !== 'string' || !/^\\d+$/.test(value)) return null;\n\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n }\n\n private isTrustedSessionPath(filePath: string): boolean {\n const resolvedRoot = path.resolve(this.codexSessionsDir);\n const resolvedPath = path.resolve(filePath);\n return resolvedPath === resolvedRoot || resolvedPath.startsWith(`${resolvedRoot}${path.sep}`);\n }\n\n private tryRegistryCache(processes: ProcessInfo[]): {\n cachedAgents: AgentInfo[];\n remaining: ProcessInfo[];\n } {\n const cachedAgents: AgentInfo[] = [];\n const remaining: ProcessInfo[] = [];\n const byPid = new Map(this.registry.list().map((e) => [e.pid, e]));\n\n for (const proc of processes) {\n const entry = byPid.get(proc.pid);\n if (\n !entry ||\n entry.type !== this.type ||\n !entry.sessionFilePath ||\n !fs.existsSync(entry.sessionFilePath)\n ) {\n remaining.push(proc);\n continue;\n }\n\n const content = safeReadFile(entry.sessionFilePath);\n const sessionData = this.parseSession(content, entry.sessionFilePath);\n if (!sessionData) {\n remaining.push(proc);\n continue;\n }\n\n cachedAgents.push(this.mapSessionToAgent(sessionData, proc, entry.sessionFilePath));\n }\n\n return { cachedAgents, remaining };\n }\n\n /**\n * Match processes via `codex resume <uuid>` in their command line.\n * Resumed sessions predate the process, so birth-time proximity cannot\n * reliably pair them with the running PID.\n */\n private tryResumeMatching(processes: ProcessInfo[]): {\n direct: DirectMatch[];\n fallback: ProcessInfo[];\n } {\n const direct: DirectMatch[] = [];\n const fallback: ProcessInfo[] = [];\n\n for (const proc of processes) {\n const sessionId = this.extractResumeSessionId(proc.command);\n if (!sessionId) {\n fallback.push(proc);\n continue;\n }\n\n const sessionFile = this.findSessionFileById(sessionId);\n if (!sessionFile) {\n fallback.push(proc);\n continue;\n }\n\n direct.push({ process: proc, sessionFile });\n }\n\n return { direct, fallback };\n }\n\n private extractResumeSessionId(command: string): string | null {\n const match = command.match(/(?:^|\\s)resume\\s+([0-9a-f-]{36})(?:\\s|$)/i);\n return match?.[1] ?? null;\n }\n\n private findSessionFileById(sessionId: string): SessionFile | null {\n for (const filePath of this.getCandidateSessionFiles(sessionId)) {\n if (!path.basename(filePath).includes(sessionId)) continue;\n\n const content = safeReadFile(filePath);\n const firstLine = content?.split('\\n')[0]?.trim();\n if (!firstLine) continue;\n\n try {\n const parsed = JSON.parse(firstLine) as CodexEventEntry;\n if (parsed.type !== 'session_meta' || parsed.payload?.id !== sessionId) {\n continue;\n }\n\n const stat = safeStat(filePath);\n if (!stat) continue;\n const metaTimestampMs = this.parseMetaTimestampMs(parsed.payload?.timestamp);\n\n return {\n sessionId,\n filePath,\n projectDir: path.dirname(filePath),\n birthtimeMs: metaTimestampMs ?? stat.birthtimeMs,\n resolvedCwd: parsed.payload?.cwd || '',\n };\n } catch {\n continue;\n }\n }\n\n return null;\n }\n\n private getCandidateSessionFiles(sessionId: string): string[] {\n // Codex currently writes UUIDv7 session IDs, so the ID can narrow lookup\n // to the creation-date directory. Fall back for older or changed formats.\n const sessionDate = this.tryParseUuidV7Date(sessionId);\n if (!sessionDate) return this.collectAllSessionFiles();\n\n return this.collectSessionFilesInDateDirs(\n this.getDateDirsAroundDate(sessionDate, CodexAdapter.PROCESS_START_DAY_WINDOW_DAYS),\n );\n }\n\n private mapDirectMatches(matches: DirectMatch[]): DirectMatchResult {\n const agents: AgentInfo[] = [];\n const failedProcesses: ProcessInfo[] = [];\n\n for (const match of matches) {\n const sessionData = this.parseSession(undefined, match.sessionFile.filePath);\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, match.process, match.sessionFile.filePath));\n } else {\n failedProcesses.push(match.process);\n }\n }\n\n return { agents, failedProcesses };\n }\n\n /**\n * Discover session files for the given processes.\n *\n * Uses process start times to determine which YYYY/MM/DD date directories\n * to scan (±1 day window), then batches stat calls across all directories.\n * Reads each file once and caches content for later parsing by parseSession().\n * Sets resolvedCwd from session_meta first line.\n */\n private discoverSessions(processes: ProcessInfo[]): {\n sessions: SessionFile[];\n contentCache: Map<string, string>;\n } {\n const empty = { sessions: [], contentCache: new Map<string, string>() };\n if (!fs.existsSync(this.codexSessionsDir)) return empty;\n\n const dateDirs = this.getDateDirs(processes);\n if (dateDirs.length === 0) return empty;\n\n const files = batchGetSessionFileBirthtimes(dateDirs);\n const contentCache = new Map<string, string>();\n\n // Read each file once: extract CWD for matching, cache content for later parsing\n for (const file of files) {\n try {\n const content = fs.readFileSync(file.filePath, 'utf-8');\n contentCache.set(file.filePath, content);\n\n const firstLine = content.split('\\n')[0]?.trim();\n if (firstLine) {\n const parsed = JSON.parse(firstLine);\n if (parsed.type === 'session_meta') {\n file.resolvedCwd = parsed.payload?.cwd || '';\n const metaTimestampMs = this.parseMetaTimestampMs(parsed.payload?.timestamp);\n if (metaTimestampMs !== null) {\n file.birthtimeMs = metaTimestampMs;\n }\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n\n return { sessions: files, contentCache };\n }\n\n /**\n * Determine which date directories to scan based on process start times.\n * Returns only directories that actually exist.\n */\n private getDateDirs(processes: ProcessInfo[]): string[] {\n const dayKeys = new Set<string>();\n const window = CodexAdapter.PROCESS_START_DAY_WINDOW_DAYS;\n\n for (const proc of processes) {\n const startTime = proc.startTime || new Date();\n for (let offset = -window; offset <= window; offset++) {\n const day = new Date(startTime.getTime());\n day.setDate(day.getDate() + offset);\n dayKeys.add(this.toSessionDayKey(day));\n }\n }\n\n const dirs: string[] = [];\n for (const dayKey of dayKeys) {\n const dayDir = path.join(this.codexSessionsDir, dayKey);\n try {\n if (fs.statSync(dayDir).isDirectory()) {\n dirs.push(dayDir);\n }\n } catch {\n continue;\n }\n }\n\n return dirs;\n }\n\n private getDateDirsAroundDate(date: Date, windowDays: number): string[] {\n const dirs: string[] = [];\n\n for (let offset = -windowDays; offset <= windowDays; offset++) {\n const day = new Date(date.getTime());\n day.setDate(day.getDate() + offset);\n const dayDir = path.join(this.codexSessionsDir, this.toSessionDayKey(day));\n if (isDirectory(dayDir)) {\n dirs.push(dayDir);\n }\n }\n\n return dirs;\n }\n\n private toSessionDayKey(date: Date): string {\n const yyyy = String(date.getFullYear()).padStart(4, '0');\n const mm = String(date.getMonth() + 1).padStart(2, '0');\n const dd = String(date.getDate()).padStart(2, '0');\n return path.join(yyyy, mm, dd);\n }\n\n private tryParseUuidV7Date(sessionId: string): Date | null {\n const match = sessionId.match(/^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i);\n if (!match) return null;\n\n const timestampMs = Number.parseInt(`${match[1]}${match[2]}`, 16);\n if (!Number.isSafeInteger(timestampMs) || timestampMs <= 0) return null;\n\n const date = new Date(timestampMs);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n\n /**\n * Parse session file content into CodexSession.\n * Uses cached content if available, otherwise reads from disk.\n */\n private parseSession(cachedContent: string | undefined, filePath: string): CodexSession | null {\n let content: string;\n if (cachedContent !== undefined) {\n content = cachedContent;\n } else {\n try {\n content = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n }\n\n const allLines = content.trim().split('\\n');\n if (!allLines[0]) return null;\n\n let metaEntry: CodexEventEntry;\n try {\n metaEntry = JSON.parse(allLines[0]);\n } catch {\n return null;\n }\n\n if (metaEntry.type !== 'session_meta' || !metaEntry.payload?.id) {\n return null;\n }\n\n const entries: CodexEventEntry[] = [];\n for (const line of allLines) {\n try {\n entries.push(JSON.parse(line));\n } catch {\n continue;\n }\n }\n\n const lastEntry = this.findLastEventEntry(entries);\n const lastPayloadType = lastEntry ? this.normalizedPayloadType(lastEntry) : undefined;\n\n const lastActive =\n this.parseTimestamp(lastEntry?.timestamp) ||\n this.parseTimestamp(metaEntry.payload.timestamp) ||\n fs.statSync(filePath).mtime;\n const sessionStart =\n this.parseTimestamp(metaEntry.payload.timestamp) ||\n lastActive;\n\n return {\n sessionId: metaEntry.payload.id,\n projectPath: metaEntry.payload.cwd || '',\n summary: this.extractSummary(entries),\n sessionStart,\n lastActive,\n lastPayloadType,\n };\n }\n\n private mapSessionToAgent(session: CodexSession, processInfo: ProcessInfo, filePath: string): AgentInfo {\n return {\n name: generateAgentName(session.projectPath || processInfo.cwd || '', processInfo.pid),\n type: this.type,\n status: this.determineStatus(session),\n summary: session.summary || 'Codex session active',\n pid: processInfo.pid,\n projectPath: session.projectPath || processInfo.cwd || '',\n sessionId: session.sessionId,\n lastActive: session.lastActive,\n sessionFilePath: filePath,\n };\n }\n\n private mapProcessOnlyAgent(processInfo: ProcessInfo): AgentInfo {\n return {\n name: generateAgentName(processInfo.cwd || '', processInfo.pid),\n type: this.type,\n status: AgentStatus.RUNNING,\n summary: 'Codex process running',\n pid: processInfo.pid,\n projectPath: processInfo.cwd || '',\n sessionId: `pid-${processInfo.pid}`,\n lastActive: new Date(),\n };\n }\n\n private findLastEventEntry(entries: CodexEventEntry[]): CodexEventEntry | undefined {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry && typeof entry.type === 'string') {\n return entry;\n }\n }\n return undefined;\n }\n\n private parseTimestamp(value?: string): Date | null {\n if (!value) return null;\n const timestamp = new Date(value);\n return Number.isNaN(timestamp.getTime()) ? null : timestamp;\n }\n\n private parseMetaTimestampMs(value?: string): number | null {\n if (typeof value !== 'string') return null;\n\n const timestamp = this.parseTimestamp(value);\n if (!timestamp) return null;\n\n const timestampMs = timestamp.getTime();\n return Number.isFinite(timestampMs) ? timestampMs : null;\n }\n\n private determineStatus(session: CodexSession): AgentStatus {\n const diffMs = Date.now() - session.lastActive.getTime();\n const diffMinutes = diffMs / 60000;\n\n if (diffMinutes > CodexAdapter.IDLE_THRESHOLD_MINUTES) {\n return AgentStatus.IDLE;\n }\n\n if (\n session.lastPayloadType === 'agent_message' ||\n session.lastPayloadType === 'task_complete' ||\n session.lastPayloadType === 'turn_aborted'\n ) {\n return AgentStatus.WAITING;\n }\n\n return AgentStatus.RUNNING;\n }\n\n private extractSummary(entries: CodexEventEntry[]): string {\n for (let i = entries.length - 1; i >= 0; i--) {\n const message = this.extractEntryText(entries[i]);\n if (message) return this.truncate(message, 120);\n }\n\n return 'Codex session active';\n }\n\n private normalizedPayloadType(entry: CodexEventEntry): string | undefined {\n const payloadType = entry.payload?.type;\n\n if (entry.type === 'response_item' && payloadType === 'message') {\n if (entry.payload?.role === 'assistant') return 'agent_message';\n if (entry.payload?.role === 'user') return 'user_message';\n return payloadType;\n }\n\n if (entry.type === 'event_msg' && payloadType === 'item_completed') {\n const itemType = entry.payload?.item?.type;\n if (itemType === 'AgentMessage') return 'agent_message';\n if (itemType === 'UserMessage') return 'user_message';\n return itemType ?? payloadType;\n }\n\n return payloadType;\n }\n\n private extractEntryText(entry: CodexEventEntry | undefined): string {\n if (!entry) return '';\n\n const legacyMessage = entry.payload?.message;\n if (typeof legacyMessage === 'string' && legacyMessage.trim().length > 0) {\n return legacyMessage.trim();\n }\n\n const conversationMessage = this.toConversationMessage(entry, false);\n return conversationMessage?.content.trim() ?? '';\n }\n\n private truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n }\n\n private isCodexExecutable(command: string): boolean {\n const executable = command.trim().split(/\\s+/)[0] || '';\n const base = path.basename(executable).toLowerCase();\n return base === 'codex' || base === 'codex.exe';\n }\n\n /**\n * Read the full conversation from a Codex session JSONL file.\n *\n * Codex entries use either legacy payload.message fields or current\n * response_item/event_msg content arrays.\n */\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n const verbose = options?.verbose ?? false;\n\n const content = safeReadFile(sessionFilePath);\n if (content === undefined) return [];\n\n const lines = content.trim().split('\\n');\n const entries: CodexEventEntry[] = [];\n const messages: ConversationMessage[] = [];\n\n for (const line of lines) {\n try {\n entries.push(JSON.parse(line));\n } catch {\n continue;\n }\n }\n\n const responseItemMirrorKeys = new Set<string>();\n for (const entry of entries) {\n if (entry.type !== 'response_item') continue;\n\n const message = this.toConversationMessage(entry, verbose);\n const mirrorKey = message ? this.mirroredMessageKey(entry, message) : null;\n if (mirrorKey) responseItemMirrorKeys.add(mirrorKey);\n }\n\n for (const entry of entries) {\n const message = this.toConversationMessage(entry, verbose);\n if (!message) continue;\n\n const mirrorKey = this.mirroredMessageKey(entry, message);\n if (\n entry.type === 'event_msg' &&\n mirrorKey &&\n responseItemMirrorKeys.has(mirrorKey)\n ) {\n continue;\n }\n\n messages.push(message);\n }\n\n return messages;\n }\n\n private toConversationMessage(entry: CodexEventEntry, verbose: boolean): ConversationMessage | null {\n if (entry.type === 'session_meta') return null;\n\n const payloadType = entry.payload?.type;\n if (entry.type === 'response_item' && payloadType === 'message') {\n const role = this.mapCodexRole(entry.payload?.role, verbose);\n const text = this.extractContentText(entry.payload?.content);\n if (!role || !text) return null;\n\n return { role, content: text, timestamp: entry.timestamp };\n }\n\n if (entry.type === 'event_msg' && payloadType === 'item_completed') {\n const item = entry.payload?.item;\n const role = this.mapCodexItemRole(item?.type, verbose);\n const text = this.extractContentText(item?.content);\n if (!role || !text) return null;\n\n return { role, content: text, timestamp: entry.timestamp };\n }\n\n if (!payloadType) return null;\n\n let role: ConversationMessage['role'];\n if (payloadType === 'user_message') {\n role = 'user';\n } else if (payloadType === 'agent_message' || payloadType === 'task_complete') {\n role = 'assistant';\n } else if (verbose) {\n role = 'system';\n } else {\n return null;\n }\n\n const text = entry.payload?.message?.trim();\n if (!text) return null;\n\n return { role, content: text, timestamp: entry.timestamp };\n }\n\n private mapCodexRole(role: string | undefined, verbose: boolean): ConversationMessage['role'] | null {\n if (role === 'user') return 'user';\n if (role === 'assistant') return 'assistant';\n return verbose ? 'system' : null;\n }\n\n private mapCodexItemRole(itemType: string | undefined, verbose: boolean): ConversationMessage['role'] | null {\n if (itemType === 'AgentMessage') return 'assistant';\n if (itemType === 'UserMessage') return 'user';\n return verbose ? 'system' : null;\n }\n\n private mirroredMessageKey(entry: CodexEventEntry, message: ConversationMessage): string | null {\n const turnId =\n entry.payload?.turn_id ||\n entry.payload?.internal_chat_message_metadata_passthrough?.turn_id;\n\n if (!turnId) return null;\n return `${turnId}\\0${message.role}\\0${message.content}`;\n }\n\n private extractContentText(content: string | CodexContent[] | undefined): string {\n if (typeof content === 'string') return content.trim();\n if (!Array.isArray(content)) return '';\n\n return content\n .map((part) => part.text)\n .filter((text): text is string => typeof text === 'string' && text.trim().length > 0)\n .map((text) => text.trim())\n .join('\\n')\n .trim();\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n if (!isDirectory(this.codexSessionsDir)) return [];\n\n const files = this.collectAllSessionFiles();\n const summaries: SessionSummary[] = [];\n\n for (const filePath of files) {\n const summary = this.fileToSessionSummary(filePath);\n if (!summary) continue;\n if (opts?.cwd !== undefined && summary.cwd !== opts.cwd) continue;\n summaries.push(summary);\n }\n\n return summaries;\n }\n\n /**\n * Walk every YYYY/MM/DD directory under `codexSessionsDir` and return\n * absolute paths of `.jsonl` files. Tolerates malformed layouts\n * (skips entries that aren't directories at the expected depth).\n */\n private collectAllSessionFiles(): string[] {\n const out: string[] = [];\n\n for (const yearEntry of safeReaddir(this.codexSessionsDir)) {\n const yearDir = path.join(this.codexSessionsDir, yearEntry);\n if (!isDirectory(yearDir)) continue;\n\n for (const monthEntry of safeReaddir(yearDir)) {\n const monthDir = path.join(yearDir, monthEntry);\n if (!isDirectory(monthDir)) continue;\n\n for (const dayEntry of safeReaddir(monthDir)) {\n const dayDir = path.join(monthDir, dayEntry);\n if (!isDirectory(dayDir)) continue;\n\n for (const fileEntry of safeReaddir(dayDir)) {\n if (!fileEntry.endsWith('.jsonl')) continue;\n out.push(path.join(dayDir, fileEntry));\n }\n }\n }\n }\n\n return out;\n }\n\n private collectSessionFilesInDateDirs(dateDirs: string[]): string[] {\n const out: string[] = [];\n\n for (const dayDir of dateDirs) {\n for (const fileEntry of safeReaddir(dayDir)) {\n if (!fileEntry.endsWith('.jsonl')) continue;\n out.push(path.join(dayDir, fileEntry));\n }\n }\n\n return out;\n }\n\n /**\n * Read a Codex session JSONL file and produce a {@link SessionSummary}.\n * Returns null when the file is unreadable, has no `session_meta`, or\n * lacks a session id.\n */\n private fileToSessionSummary(filePath: string): SessionSummary | null {\n const content = safeReadFile(filePath);\n if (content === undefined) return null;\n\n const allLines = content.trim().split('\\n');\n if (!allLines[0]) return null;\n\n let metaEntry: CodexEventEntry;\n try {\n metaEntry = JSON.parse(allLines[0]);\n } catch {\n return null;\n }\n\n if (metaEntry.type !== 'session_meta' || !metaEntry.payload?.id) {\n return null;\n }\n\n let firstUserMessage = '';\n let lastTimestamp: Date | null = null;\n\n for (let i = 1; i < allLines.length; i++) {\n let entry: CodexEventEntry;\n try {\n entry = JSON.parse(allLines[i]);\n } catch {\n continue;\n }\n\n const ts = this.parseTimestamp(entry.timestamp);\n if (ts) lastTimestamp = ts;\n\n if (\n !firstUserMessage &&\n entry.payload?.type === 'user_message' &&\n typeof entry.payload.message === 'string' &&\n entry.payload.message.trim().length > 0\n ) {\n firstUserMessage = entry.payload.message.trim();\n }\n }\n\n const stat = safeStat(filePath);\n\n const startedAt =\n this.parseTimestamp(metaEntry.payload.timestamp) ||\n lastTimestamp ||\n stat?.birthtime ||\n stat?.mtime ||\n new Date();\n const lastActive = lastTimestamp || startedAt;\n\n return {\n type: 'codex',\n sessionId: metaEntry.payload.id,\n cwd: metaEntry.payload.cwd || '',\n firstUserMessage,\n lastActive,\n startedAt,\n sessionFilePath: filePath,\n };\n }\n}\n"],"names":["fs","path","AgentStatus","listAgentProcesses","enrichProcesses","batchGetSessionFileBirthtimes","isDirectory","safeReadFile","safeReaddir","safeStat","matchProcessesToSessions","generateAgentName","AgentRegistry","CodexAdapter","type","IDLE_THRESHOLD_MINUTES","PROCESS_START_DAY_WINDOW_DAYS","codexSessionsDir","sessionMappingPath","registry","default","homeDir","process","env","HOME","USERPROFILE","join","canHandle","processInfo","isCodexExecutable","command","detectAgents","processes","length","cachedAgents","remaining","tryRegistryCache","mappingResult","mapSessionMappingMatches","direct","fallback","tryResumeMatching","directResult","mapDirectMatches","sessions","contentCache","discoverSessions","agents","failedProcesses","map","p","mapProcessOnlyAgent","matches","matchedPids","Set","a","pid","m","match","cachedContent","get","session","filePath","sessionData","parseSession","push","mapSessionToAgent","delete","proc","has","matchFromSessionMapping","undefined","mapping","readSessionMapping","size","isTrustedSessionPath","existsSync","content","Map","parsed","JSON","parse","Array","isArray","key","value","Object","entries","toPid","set","Number","isInteger","test","isSafeInteger","resolvedRoot","resolve","resolvedPath","startsWith","sep","byPid","list","e","entry","sessionFilePath","sessionId","extractResumeSessionId","sessionFile","findSessionFileById","getCandidateSessionFiles","basename","includes","firstLine","split","trim","payload","id","stat","metaTimestampMs","parseMetaTimestampMs","timestamp","projectDir","dirname","birthtimeMs","resolvedCwd","cwd","sessionDate","tryParseUuidV7Date","collectAllSessionFiles","collectSessionFilesInDateDirs","getDateDirsAroundDate","empty","dateDirs","getDateDirs","files","file","readFileSync","dayKeys","window","startTime","Date","offset","day","getTime","setDate","getDate","add","toSessionDayKey","dirs","dayKey","dayDir","statSync","date","windowDays","yyyy","String","getFullYear","padStart","mm","getMonth","dd","timestampMs","parseInt","isNaN","allLines","metaEntry","line","lastEntry","findLastEventEntry","lastPayloadType","normalizedPayloadType","lastActive","parseTimestamp","mtime","sessionStart","projectPath","summary","extractSummary","name","status","determineStatus","RUNNING","i","isFinite","diffMs","now","diffMinutes","IDLE","WAITING","message","extractEntryText","truncate","payloadType","role","itemType","item","legacyMessage","conversationMessage","toConversationMessage","maxLength","slice","executable","base","toLowerCase","getConversation","options","verbose","lines","messages","responseItemMirrorKeys","mirrorKey","mirroredMessageKey","mapCodexRole","text","extractContentText","mapCodexItemRole","turnId","turn_id","internal_chat_message_metadata_passthrough","part","filter","listSessions","opts","summaries","fileToSessionSummary","out","yearEntry","yearDir","monthEntry","monthDir","dayEntry","fileEntry","endsWith","firstUserMessage","lastTimestamp","ts","startedAt","birthtime"],"mappings":"AAAA;;;;;;;;;;;CAWC,GAED,YAAYA,QAAQ,KAAK;AACzB,YAAYC,UAAU,OAAO;AAS7B,SAASC,WAAW,QAAQ,oBAAoB;AAChD,SAASC,kBAAkB,EAAEC,eAAe,QAAQ,sBAAsB;AAC1E,SAASC,6BAA6B,EAAEC,WAAW,EAAEC,YAAY,EAAEC,WAAW,EAAEC,QAAQ,QAAQ,sBAAsB;AAEtH,SAASC,wBAAwB,EAAEC,iBAAiB,QAAQ,uBAAuB;AACnF,SAASC,aAAa,QAAQ,4BAA4B;AA4D1D,OAAO,MAAMC;IACAC,OAAO,QAAiB;IAEjC,OAAwBC,yBAAyB,EAAE;IACnD,oFAAoF,GACpF,OAAwBC,gCAAgC,EAAE;IAElDC,iBAAyB;IACzBC,mBAA2B;IAC3BC,SAAwB;IAEhC,YAAYA,WAA0BP,cAAcQ,OAAO,EAAE,CAAE;QAC3D,MAAMC,UAAUC,QAAQC,GAAG,CAACC,IAAI,IAAIF,QAAQC,GAAG,CAACE,WAAW,IAAI;QAC/D,IAAI,CAACR,gBAAgB,GAAGhB,KAAKyB,IAAI,CAACL,SAAS,UAAU;QACrD,IAAI,CAACH,kBAAkB,GAAGjB,KAAKyB,IAAI,CAACL,SAAS,UAAU,aAAa;QACpE,IAAI,CAACF,QAAQ,GAAGA;IACpB;IAEAQ,UAAUC,WAAwB,EAAW;QACzC,OAAO,IAAI,CAACC,iBAAiB,CAACD,YAAYE,OAAO;IACrD;IAEA;;KAEC,GACD,MAAMC,eAAqC;QACvC,MAAMC,YAAY5B,gBAAgBD,mBAAmB;QACrD,IAAI6B,UAAUC,MAAM,KAAK,GAAG,OAAO,EAAE;QAErC,MAAM,EAAEC,YAAY,EAAEC,SAAS,EAAE,GAAG,IAAI,CAACC,gBAAgB,CAACJ;QAC1D,IAAIG,UAAUF,MAAM,KAAK,GAAG,OAAOC;QAEnC,MAAMG,gBAAgB,IAAI,CAACC,wBAAwB,CAACH;QACpD,MAAM,EAAEI,MAAM,EAAEC,QAAQ,EAAE,GAAG,IAAI,CAACC,iBAAiB,CAACJ,cAAcG,QAAQ;QAC1E,MAAME,eAAe,IAAI,CAACC,gBAAgB,CAACJ;QAC3C,MAAM,EAAEK,QAAQ,EAAEC,YAAY,EAAE,GAAG,IAAI,CAACC,gBAAgB,CAACN;QACzD,IAAII,SAASX,MAAM,KAAK,GAAG;YACvB,OAAO;mBACAC;mBACAG,cAAcU,MAAM;mBACpBL,aAAaK,MAAM;mBACnBL,aAAaM,eAAe,CAACC,GAAG,CAAC,CAACC,IAAM,IAAI,CAACC,mBAAmB,CAACD;mBACjEV,SAASS,GAAG,CAAC,CAACC,IAAM,IAAI,CAACC,mBAAmB,CAACD;aACnD;QACL;QAEA,MAAME,UAAU1C,yBAAyB8B,UAAUI;QACnD,MAAMS,cAAc,IAAIC,IAAI;eACrBZ,aAAaK,MAAM,CAACE,GAAG,CAAC,CAACM,IAAMA,EAAEC,GAAG;eACpCJ,QAAQH,GAAG,CAAC,CAACQ,IAAMA,EAAEnC,OAAO,CAACkC,GAAG;SACtC;QACD,MAAMT,SAAsB;eAAIL,aAAaK,MAAM;SAAC;QAEpD,KAAK,MAAMW,SAASN,QAAS;YACzB,MAAMO,gBAAgBd,aAAae,GAAG,CAACF,MAAMG,OAAO,CAACC,QAAQ;YAC7D,MAAMC,cAAc,IAAI,CAACC,YAAY,CAACL,eAAeD,MAAMG,OAAO,CAACC,QAAQ;YAC3E,IAAIC,aAAa;gBACbhB,OAAOkB,IAAI,CAAC,IAAI,CAACC,iBAAiB,CAACH,aAAaL,MAAMpC,OAAO,EAAEoC,MAAMG,OAAO,CAACC,QAAQ;YACzF,OAAO;gBACHT,YAAYc,MAAM,CAACT,MAAMpC,OAAO,CAACkC,GAAG;YACxC;QACJ;QAEA,KAAK,MAAMY,QAAQ5B,SAAU;YACzB,IAAI,CAACa,YAAYgB,GAAG,CAACD,KAAKZ,GAAG,GAAG;gBAC5BT,OAAOkB,IAAI,CAAC,IAAI,CAACd,mBAAmB,CAACiB;YACzC;QACJ;QAEA,KAAK,MAAMA,QAAQ1B,aAAaM,eAAe,CAAE;YAC7CD,OAAOkB,IAAI,CAAC,IAAI,CAACd,mBAAmB,CAACiB;QACzC;QAEA,OAAO;eAAIlC;eAAiBG,cAAcU,MAAM;eAAKA;SAAO;IAChE;IAEQT,yBAAyBN,SAAwB,EAAsB;QAC3E,MAAM,EAAEoB,OAAO,EAAEZ,QAAQ,EAAE,GAAG,IAAI,CAAC8B,uBAAuB,CAACtC;QAC3D,MAAMe,SAAsB,EAAE;QAE9B,KAAK,MAAMW,SAASN,QAAS;YACzB,MAAMW,cAAc,IAAI,CAACC,YAAY,CAACO,WAAWb,MAAMI,QAAQ;YAC/D,IAAIC,aAAa;gBACbhB,OAAOkB,IAAI,CAAC,IAAI,CAACC,iBAAiB,CAACH,aAAaL,MAAMpC,OAAO,EAAEoC,MAAMI,QAAQ;YACjF,OAAO;gBACHtB,SAASyB,IAAI,CAACP,MAAMpC,OAAO;YAC/B;QACJ;QAEA,OAAO;YAAEyB;YAAQP;QAAS;IAC9B;IAEQ8B,wBAAwBtC,SAAwB,EAGtD;QACE,MAAMwC,UAAU,IAAI,CAACC,kBAAkB;QACvC,IAAID,QAAQE,IAAI,KAAK,GAAG,OAAO;YAAEtB,SAAS,EAAE;YAAEZ,UAAUR;QAAU;QAElE,MAAMoB,UAA0B,EAAE;QAClC,MAAMZ,WAA0B,EAAE;QAElC,KAAK,MAAM4B,QAAQpC,UAAW;YAC1B,MAAM8B,WAAWU,QAAQZ,GAAG,CAACQ,KAAKZ,GAAG;YACrC,IAAI,CAACM,YAAY,CAAC,IAAI,CAACa,oBAAoB,CAACb,aAAa,CAAC9D,GAAG4E,UAAU,CAACd,WAAW;gBAC/EtB,SAASyB,IAAI,CAACG;gBACd;YACJ;YAEAhB,QAAQa,IAAI,CAAC;gBAAE3C,SAAS8C;gBAAMN;YAAS;QAC3C;QAEA,OAAO;YAAEV;YAASZ;QAAS;IAC/B;IAEQiC,qBAA0C;QAC9C,MAAMI,UAAUtE,aAAa,IAAI,CAACW,kBAAkB;QACpD,IAAI2D,YAAYN,WAAW,OAAO,IAAIO;QAEtC,IAAIC;QACJ,IAAI;YACAA,SAASC,KAAKC,KAAK,CAACJ;QACxB,EAAE,OAAM;YACJ,OAAO,IAAIC;QACf;QAEA,IAAI,CAACC,UAAU,OAAOA,WAAW,YAAYG,MAAMC,OAAO,CAACJ,SAAS,OAAO,IAAID;QAE/E,MAAM7B,MAAM,IAAI6B;QAChB,KAAK,MAAM,CAACM,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACR,QAAS;YAC/C,MAAMvB,MAAM,IAAI,CAACgC,KAAK,CAACJ;YACvB,IAAI5B,QAAQ,QAAQ,OAAO6B,UAAU,YAAYA,OAAO;gBACpDpC,IAAIwC,GAAG,CAACjC,KAAK6B;YACjB;QACJ;QAEA,OAAOpC;IACX;IAEQuC,MAAMH,KAAc,EAAiB;QACzC,IAAI,OAAOA,UAAU,YAAYK,OAAOC,SAAS,CAACN,UAAUA,QAAQ,GAAG,OAAOA;QAC9E,IAAI,OAAOA,UAAU,YAAY,CAAC,QAAQO,IAAI,CAACP,QAAQ,OAAO;QAE9D,MAAMN,SAASW,OAAOL;QACtB,OAAOK,OAAOG,aAAa,CAACd,WAAWA,SAAS,IAAIA,SAAS;IACjE;IAEQJ,qBAAqBb,QAAgB,EAAW;QACpD,MAAMgC,eAAe7F,KAAK8F,OAAO,CAAC,IAAI,CAAC9E,gBAAgB;QACvD,MAAM+E,eAAe/F,KAAK8F,OAAO,CAACjC;QAClC,OAAOkC,iBAAiBF,gBAAgBE,aAAaC,UAAU,CAAC,GAAGH,eAAe7F,KAAKiG,GAAG,EAAE;IAChG;IAEQ9D,iBAAiBJ,SAAwB,EAG/C;QACE,MAAME,eAA4B,EAAE;QACpC,MAAMC,YAA2B,EAAE;QACnC,MAAMgE,QAAQ,IAAIrB,IAAI,IAAI,CAAC3D,QAAQ,CAACiF,IAAI,GAAGnD,GAAG,CAAC,CAACoD,IAAM;gBAACA,EAAE7C,GAAG;gBAAE6C;aAAE;QAEhE,KAAK,MAAMjC,QAAQpC,UAAW;YAC1B,MAAMsE,QAAQH,MAAMvC,GAAG,CAACQ,KAAKZ,GAAG;YAChC,IACI,CAAC8C,SACDA,MAAMxF,IAAI,KAAK,IAAI,CAACA,IAAI,IACxB,CAACwF,MAAMC,eAAe,IACtB,CAACvG,GAAG4E,UAAU,CAAC0B,MAAMC,eAAe,GACtC;gBACEpE,UAAU8B,IAAI,CAACG;gBACf;YACJ;YAEA,MAAMS,UAAUtE,aAAa+F,MAAMC,eAAe;YAClD,MAAMxC,cAAc,IAAI,CAACC,YAAY,CAACa,SAASyB,MAAMC,eAAe;YACpE,IAAI,CAACxC,aAAa;gBACd5B,UAAU8B,IAAI,CAACG;gBACf;YACJ;YAEAlC,aAAa+B,IAAI,CAAC,IAAI,CAACC,iBAAiB,CAACH,aAAaK,MAAMkC,MAAMC,eAAe;QACrF;QAEA,OAAO;YAAErE;YAAcC;QAAU;IACrC;IAEA;;;;KAIC,GACD,AAAQM,kBAAkBT,SAAwB,EAGhD;QACE,MAAMO,SAAwB,EAAE;QAChC,MAAMC,WAA0B,EAAE;QAElC,KAAK,MAAM4B,QAAQpC,UAAW;YAC1B,MAAMwE,YAAY,IAAI,CAACC,sBAAsB,CAACrC,KAAKtC,OAAO;YAC1D,IAAI,CAAC0E,WAAW;gBACZhE,SAASyB,IAAI,CAACG;gBACd;YACJ;YAEA,MAAMsC,cAAc,IAAI,CAACC,mBAAmB,CAACH;YAC7C,IAAI,CAACE,aAAa;gBACdlE,SAASyB,IAAI,CAACG;gBACd;YACJ;YAEA7B,OAAO0B,IAAI,CAAC;gBAAE3C,SAAS8C;gBAAMsC;YAAY;QAC7C;QAEA,OAAO;YAAEnE;YAAQC;QAAS;IAC9B;IAEQiE,uBAAuB3E,OAAe,EAAiB;QAC3D,MAAM4B,QAAQ5B,QAAQ4B,KAAK,CAAC;QAC5B,OAAOA,OAAO,CAAC,EAAE,IAAI;IACzB;IAEQiD,oBAAoBH,SAAiB,EAAsB;QAC/D,KAAK,MAAM1C,YAAY,IAAI,CAAC8C,wBAAwB,CAACJ,WAAY;YAC7D,IAAI,CAACvG,KAAK4G,QAAQ,CAAC/C,UAAUgD,QAAQ,CAACN,YAAY;YAElD,MAAM3B,UAAUtE,aAAauD;YAC7B,MAAMiD,YAAYlC,SAASmC,MAAM,KAAK,CAAC,EAAE,EAAEC;YAC3C,IAAI,CAACF,WAAW;YAEhB,IAAI;gBACA,MAAMhC,SAASC,KAAKC,KAAK,CAAC8B;gBAC1B,IAAIhC,OAAOjE,IAAI,KAAK,kBAAkBiE,OAAOmC,OAAO,EAAEC,OAAOX,WAAW;oBACpE;gBACJ;gBAEA,MAAMY,OAAO3G,SAASqD;gBACtB,IAAI,CAACsD,MAAM;gBACX,MAAMC,kBAAkB,IAAI,CAACC,oBAAoB,CAACvC,OAAOmC,OAAO,EAAEK;gBAElE,OAAO;oBACHf;oBACA1C;oBACA0D,YAAYvH,KAAKwH,OAAO,CAAC3D;oBACzB4D,aAAaL,mBAAmBD,KAAKM,WAAW;oBAChDC,aAAa5C,OAAOmC,OAAO,EAAEU,OAAO;gBACxC;YACJ,EAAE,OAAM;gBACJ;YACJ;QACJ;QAEA,OAAO;IACX;IAEQhB,yBAAyBJ,SAAiB,EAAY;QAC1D,yEAAyE;QACzE,0EAA0E;QAC1E,MAAMqB,cAAc,IAAI,CAACC,kBAAkB,CAACtB;QAC5C,IAAI,CAACqB,aAAa,OAAO,IAAI,CAACE,sBAAsB;QAEpD,OAAO,IAAI,CAACC,6BAA6B,CACrC,IAAI,CAACC,qBAAqB,CAACJ,aAAahH,aAAaG,6BAA6B;IAE1F;IAEQ2B,iBAAiBS,OAAsB,EAAqB;QAChE,MAAML,SAAsB,EAAE;QAC9B,MAAMC,kBAAiC,EAAE;QAEzC,KAAK,MAAMU,SAASN,QAAS;YACzB,MAAMW,cAAc,IAAI,CAACC,YAAY,CAACO,WAAWb,MAAMgD,WAAW,CAAC5C,QAAQ;YAC3E,IAAIC,aAAa;gBACbhB,OAAOkB,IAAI,CAAC,IAAI,CAACC,iBAAiB,CAACH,aAAaL,MAAMpC,OAAO,EAAEoC,MAAMgD,WAAW,CAAC5C,QAAQ;YAC7F,OAAO;gBACHd,gBAAgBiB,IAAI,CAACP,MAAMpC,OAAO;YACtC;QACJ;QAEA,OAAO;YAAEyB;YAAQC;QAAgB;IACrC;IAEA;;;;;;;KAOC,GACD,AAAQF,iBAAiBd,SAAwB,EAG/C;QACE,MAAMkG,QAAQ;YAAEtF,UAAU,EAAE;YAAEC,cAAc,IAAIiC;QAAsB;QACtE,IAAI,CAAC9E,GAAG4E,UAAU,CAAC,IAAI,CAAC3D,gBAAgB,GAAG,OAAOiH;QAElD,MAAMC,WAAW,IAAI,CAACC,WAAW,CAACpG;QAClC,IAAImG,SAASlG,MAAM,KAAK,GAAG,OAAOiG;QAElC,MAAMG,QAAQhI,8BAA8B8H;QAC5C,MAAMtF,eAAe,IAAIiC;QAEzB,iFAAiF;QACjF,KAAK,MAAMwD,QAAQD,MAAO;YACtB,IAAI;gBACA,MAAMxD,UAAU7E,GAAGuI,YAAY,CAACD,KAAKxE,QAAQ,EAAE;gBAC/CjB,aAAa4C,GAAG,CAAC6C,KAAKxE,QAAQ,EAAEe;gBAEhC,MAAMkC,YAAYlC,QAAQmC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAEC;gBAC1C,IAAIF,WAAW;oBACX,MAAMhC,SAASC,KAAKC,KAAK,CAAC8B;oBAC1B,IAAIhC,OAAOjE,IAAI,KAAK,gBAAgB;wBAChCwH,KAAKX,WAAW,GAAG5C,OAAOmC,OAAO,EAAEU,OAAO;wBAC1C,MAAMP,kBAAkB,IAAI,CAACC,oBAAoB,CAACvC,OAAOmC,OAAO,EAAEK;wBAClE,IAAIF,oBAAoB,MAAM;4BAC1BiB,KAAKZ,WAAW,GAAGL;wBACvB;oBACJ;gBACJ;YACJ,EAAE,OAAM;YACJ,wBAAwB;YAC5B;QACJ;QAEA,OAAO;YAAEzE,UAAUyF;YAAOxF;QAAa;IAC3C;IAEA;;;KAGC,GACD,AAAQuF,YAAYpG,SAAwB,EAAY;QACpD,MAAMwG,UAAU,IAAIlF;QACpB,MAAMmF,SAAS5H,aAAaG,6BAA6B;QAEzD,KAAK,MAAMoD,QAAQpC,UAAW;YAC1B,MAAM0G,YAAYtE,KAAKsE,SAAS,IAAI,IAAIC;YACxC,IAAK,IAAIC,SAAS,CAACH,QAAQG,UAAUH,QAAQG,SAAU;gBACnD,MAAMC,MAAM,IAAIF,KAAKD,UAAUI,OAAO;gBACtCD,IAAIE,OAAO,CAACF,IAAIG,OAAO,KAAKJ;gBAC5BJ,QAAQS,GAAG,CAAC,IAAI,CAACC,eAAe,CAACL;YACrC;QACJ;QAEA,MAAMM,OAAiB,EAAE;QACzB,KAAK,MAAMC,UAAUZ,QAAS;YAC1B,MAAMa,SAASpJ,KAAKyB,IAAI,CAAC,IAAI,CAACT,gBAAgB,EAAEmI;YAChD,IAAI;gBACA,IAAIpJ,GAAGsJ,QAAQ,CAACD,QAAQ/I,WAAW,IAAI;oBACnC6I,KAAKlF,IAAI,CAACoF;gBACd;YACJ,EAAE,OAAM;gBACJ;YACJ;QACJ;QAEA,OAAOF;IACX;IAEQlB,sBAAsBsB,IAAU,EAAEC,UAAkB,EAAY;QACpE,MAAML,OAAiB,EAAE;QAEzB,IAAK,IAAIP,SAAS,CAACY,YAAYZ,UAAUY,YAAYZ,SAAU;YAC3D,MAAMC,MAAM,IAAIF,KAAKY,KAAKT,OAAO;YACjCD,IAAIE,OAAO,CAACF,IAAIG,OAAO,KAAKJ;YAC5B,MAAMS,SAASpJ,KAAKyB,IAAI,CAAC,IAAI,CAACT,gBAAgB,EAAE,IAAI,CAACiI,eAAe,CAACL;YACrE,IAAIvI,YAAY+I,SAAS;gBACrBF,KAAKlF,IAAI,CAACoF;YACd;QACJ;QAEA,OAAOF;IACX;IAEQD,gBAAgBK,IAAU,EAAU;QACxC,MAAME,OAAOC,OAAOH,KAAKI,WAAW,IAAIC,QAAQ,CAAC,GAAG;QACpD,MAAMC,KAAKH,OAAOH,KAAKO,QAAQ,KAAK,GAAGF,QAAQ,CAAC,GAAG;QACnD,MAAMG,KAAKL,OAAOH,KAAKP,OAAO,IAAIY,QAAQ,CAAC,GAAG;QAC9C,OAAO3J,KAAKyB,IAAI,CAAC+H,MAAMI,IAAIE;IAC/B;IAEQjC,mBAAmBtB,SAAiB,EAAe;QACvD,MAAM9C,QAAQ8C,UAAU9C,KAAK,CAAC;QAC9B,IAAI,CAACA,OAAO,OAAO;QAEnB,MAAMsG,cAActE,OAAOuE,QAAQ,CAAC,GAAGvG,KAAK,CAAC,EAAE,GAAGA,KAAK,CAAC,EAAE,EAAE,EAAE;QAC9D,IAAI,CAACgC,OAAOG,aAAa,CAACmE,gBAAgBA,eAAe,GAAG,OAAO;QAEnE,MAAMT,OAAO,IAAIZ,KAAKqB;QACtB,OAAOtE,OAAOwE,KAAK,CAACX,KAAKT,OAAO,MAAM,OAAOS;IACjD;IAEA;;;KAGC,GACD,AAAQvF,aAAaL,aAAiC,EAAEG,QAAgB,EAAuB;QAC3F,IAAIe;QACJ,IAAIlB,kBAAkBY,WAAW;YAC7BM,UAAUlB;QACd,OAAO;YACH,IAAI;gBACAkB,UAAU7E,GAAGuI,YAAY,CAACzE,UAAU;YACxC,EAAE,OAAM;gBACJ,OAAO;YACX;QACJ;QAEA,MAAMqG,WAAWtF,QAAQoC,IAAI,GAAGD,KAAK,CAAC;QACtC,IAAI,CAACmD,QAAQ,CAAC,EAAE,EAAE,OAAO;QAEzB,IAAIC;QACJ,IAAI;YACAA,YAAYpF,KAAKC,KAAK,CAACkF,QAAQ,CAAC,EAAE;QACtC,EAAE,OAAM;YACJ,OAAO;QACX;QAEA,IAAIC,UAAUtJ,IAAI,KAAK,kBAAkB,CAACsJ,UAAUlD,OAAO,EAAEC,IAAI;YAC7D,OAAO;QACX;QAEA,MAAM5B,UAA6B,EAAE;QACrC,KAAK,MAAM8E,QAAQF,SAAU;YACzB,IAAI;gBACA5E,QAAQtB,IAAI,CAACe,KAAKC,KAAK,CAACoF;YAC5B,EAAE,OAAM;gBACJ;YACJ;QACJ;QAEA,MAAMC,YAAY,IAAI,CAACC,kBAAkB,CAAChF;QAC1C,MAAMiF,kBAAkBF,YAAY,IAAI,CAACG,qBAAqB,CAACH,aAAa/F;QAE5E,MAAMmG,aACF,IAAI,CAACC,cAAc,CAACL,WAAW/C,cAC/B,IAAI,CAACoD,cAAc,CAACP,UAAUlD,OAAO,CAACK,SAAS,KAC/CvH,GAAGsJ,QAAQ,CAACxF,UAAU8G,KAAK;QAC/B,MAAMC,eACF,IAAI,CAACF,cAAc,CAACP,UAAUlD,OAAO,CAACK,SAAS,KAC/CmD;QAEJ,OAAO;YACHlE,WAAW4D,UAAUlD,OAAO,CAACC,EAAE;YAC/B2D,aAAaV,UAAUlD,OAAO,CAACU,GAAG,IAAI;YACtCmD,SAAS,IAAI,CAACC,cAAc,CAACzF;YAC7BsF;YACAH;YACAF;QACJ;IACJ;IAEQtG,kBAAkBL,OAAqB,EAAEjC,WAAwB,EAAEkC,QAAgB,EAAa;QACpG,OAAO;YACHmH,MAAMtK,kBAAkBkD,QAAQiH,WAAW,IAAIlJ,YAAYgG,GAAG,IAAI,IAAIhG,YAAY4B,GAAG;YACrF1C,MAAM,IAAI,CAACA,IAAI;YACfoK,QAAQ,IAAI,CAACC,eAAe,CAACtH;YAC7BkH,SAASlH,QAAQkH,OAAO,IAAI;YAC5BvH,KAAK5B,YAAY4B,GAAG;YACpBsH,aAAajH,QAAQiH,WAAW,IAAIlJ,YAAYgG,GAAG,IAAI;YACvDpB,WAAW3C,QAAQ2C,SAAS;YAC5BkE,YAAY7G,QAAQ6G,UAAU;YAC9BnE,iBAAiBzC;QACrB;IACJ;IAEQX,oBAAoBvB,WAAwB,EAAa;QAC7D,OAAO;YACHqJ,MAAMtK,kBAAkBiB,YAAYgG,GAAG,IAAI,IAAIhG,YAAY4B,GAAG;YAC9D1C,MAAM,IAAI,CAACA,IAAI;YACfoK,QAAQhL,YAAYkL,OAAO;YAC3BL,SAAS;YACTvH,KAAK5B,YAAY4B,GAAG;YACpBsH,aAAalJ,YAAYgG,GAAG,IAAI;YAChCpB,WAAW,CAAC,IAAI,EAAE5E,YAAY4B,GAAG,EAAE;YACnCkH,YAAY,IAAI/B;QACpB;IACJ;IAEQ4B,mBAAmBhF,OAA0B,EAA+B;QAChF,IAAK,IAAI8F,IAAI9F,QAAQtD,MAAM,GAAG,GAAGoJ,KAAK,GAAGA,IAAK;YAC1C,MAAM/E,QAAQf,OAAO,CAAC8F,EAAE;YACxB,IAAI/E,SAAS,OAAOA,MAAMxF,IAAI,KAAK,UAAU;gBACzC,OAAOwF;YACX;QACJ;QACA,OAAO/B;IACX;IAEQoG,eAAetF,KAAc,EAAe;QAChD,IAAI,CAACA,OAAO,OAAO;QACnB,MAAMkC,YAAY,IAAIoB,KAAKtD;QAC3B,OAAOK,OAAOwE,KAAK,CAAC3C,UAAUuB,OAAO,MAAM,OAAOvB;IACtD;IAEQD,qBAAqBjC,KAAc,EAAiB;QACxD,IAAI,OAAOA,UAAU,UAAU,OAAO;QAEtC,MAAMkC,YAAY,IAAI,CAACoD,cAAc,CAACtF;QACtC,IAAI,CAACkC,WAAW,OAAO;QAEvB,MAAMyC,cAAczC,UAAUuB,OAAO;QACrC,OAAOpD,OAAO4F,QAAQ,CAACtB,eAAeA,cAAc;IACxD;IAEQmB,gBAAgBtH,OAAqB,EAAe;QACxD,MAAM0H,SAAS5C,KAAK6C,GAAG,KAAK3H,QAAQ6G,UAAU,CAAC5B,OAAO;QACtD,MAAM2C,cAAcF,SAAS;QAE7B,IAAIE,cAAc5K,aAAaE,sBAAsB,EAAE;YACnD,OAAOb,YAAYwL,IAAI;QAC3B;QAEA,IACI7H,QAAQ2G,eAAe,KAAK,mBAC5B3G,QAAQ2G,eAAe,KAAK,mBAC5B3G,QAAQ2G,eAAe,KAAK,gBAC9B;YACE,OAAOtK,YAAYyL,OAAO;QAC9B;QAEA,OAAOzL,YAAYkL,OAAO;IAC9B;IAEQJ,eAAezF,OAA0B,EAAU;QACvD,IAAK,IAAI8F,IAAI9F,QAAQtD,MAAM,GAAG,GAAGoJ,KAAK,GAAGA,IAAK;YAC1C,MAAMO,UAAU,IAAI,CAACC,gBAAgB,CAACtG,OAAO,CAAC8F,EAAE;YAChD,IAAIO,SAAS,OAAO,IAAI,CAACE,QAAQ,CAACF,SAAS;QAC/C;QAEA,OAAO;IACX;IAEQnB,sBAAsBnE,KAAsB,EAAsB;QACtE,MAAMyF,cAAczF,MAAMY,OAAO,EAAEpG;QAEnC,IAAIwF,MAAMxF,IAAI,KAAK,mBAAmBiL,gBAAgB,WAAW;YAC7D,IAAIzF,MAAMY,OAAO,EAAE8E,SAAS,aAAa,OAAO;YAChD,IAAI1F,MAAMY,OAAO,EAAE8E,SAAS,QAAQ,OAAO;YAC3C,OAAOD;QACX;QAEA,IAAIzF,MAAMxF,IAAI,KAAK,eAAeiL,gBAAgB,kBAAkB;YAChE,MAAME,WAAW3F,MAAMY,OAAO,EAAEgF,MAAMpL;YACtC,IAAImL,aAAa,gBAAgB,OAAO;YACxC,IAAIA,aAAa,eAAe,OAAO;YACvC,OAAOA,YAAYF;QACvB;QAEA,OAAOA;IACX;IAEQF,iBAAiBvF,KAAkC,EAAU;QACjE,IAAI,CAACA,OAAO,OAAO;QAEnB,MAAM6F,gBAAgB7F,MAAMY,OAAO,EAAE0E;QACrC,IAAI,OAAOO,kBAAkB,YAAYA,cAAclF,IAAI,GAAGhF,MAAM,GAAG,GAAG;YACtE,OAAOkK,cAAclF,IAAI;QAC7B;QAEA,MAAMmF,sBAAsB,IAAI,CAACC,qBAAqB,CAAC/F,OAAO;QAC9D,OAAO8F,qBAAqBvH,QAAQoC,UAAU;IAClD;IAEQ6E,SAASzG,KAAa,EAAEiH,SAAiB,EAAU;QACvD,IAAIjH,MAAMpD,MAAM,IAAIqK,WAAW,OAAOjH;QACtC,OAAO,GAAGA,MAAMkH,KAAK,CAAC,GAAGD,YAAY,GAAG,GAAG,CAAC;IAChD;IAEQzK,kBAAkBC,OAAe,EAAW;QAChD,MAAM0K,aAAa1K,QAAQmF,IAAI,GAAGD,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI;QACrD,MAAMyF,OAAOxM,KAAK4G,QAAQ,CAAC2F,YAAYE,WAAW;QAClD,OAAOD,SAAS,WAAWA,SAAS;IACxC;IAEA;;;;;KAKC,GACDE,gBAAgBpG,eAAuB,EAAEqG,OAA+B,EAAyB;QAC7F,MAAMC,UAAUD,SAASC,WAAW;QAEpC,MAAMhI,UAAUtE,aAAagG;QAC7B,IAAI1B,YAAYN,WAAW,OAAO,EAAE;QAEpC,MAAMuI,QAAQjI,QAAQoC,IAAI,GAAGD,KAAK,CAAC;QACnC,MAAMzB,UAA6B,EAAE;QACrC,MAAMwH,WAAkC,EAAE;QAE1C,KAAK,MAAM1C,QAAQyC,MAAO;YACtB,IAAI;gBACAvH,QAAQtB,IAAI,CAACe,KAAKC,KAAK,CAACoF;YAC5B,EAAE,OAAM;gBACJ;YACJ;QACJ;QAEA,MAAM2C,yBAAyB,IAAI1J;QACnC,KAAK,MAAMgD,SAASf,QAAS;YACzB,IAAIe,MAAMxF,IAAI,KAAK,iBAAiB;YAEpC,MAAM8K,UAAU,IAAI,CAACS,qBAAqB,CAAC/F,OAAOuG;YAClD,MAAMI,YAAYrB,UAAU,IAAI,CAACsB,kBAAkB,CAAC5G,OAAOsF,WAAW;YACtE,IAAIqB,WAAWD,uBAAuB/D,GAAG,CAACgE;QAC9C;QAEA,KAAK,MAAM3G,SAASf,QAAS;YACzB,MAAMqG,UAAU,IAAI,CAACS,qBAAqB,CAAC/F,OAAOuG;YAClD,IAAI,CAACjB,SAAS;YAEd,MAAMqB,YAAY,IAAI,CAACC,kBAAkB,CAAC5G,OAAOsF;YACjD,IACItF,MAAMxF,IAAI,KAAK,eACfmM,aACAD,uBAAuB3I,GAAG,CAAC4I,YAC7B;gBACE;YACJ;YAEAF,SAAS9I,IAAI,CAAC2H;QAClB;QAEA,OAAOmB;IACX;IAEQV,sBAAsB/F,KAAsB,EAAEuG,OAAgB,EAA8B;QAChG,IAAIvG,MAAMxF,IAAI,KAAK,gBAAgB,OAAO;QAE1C,MAAMiL,cAAczF,MAAMY,OAAO,EAAEpG;QACnC,IAAIwF,MAAMxF,IAAI,KAAK,mBAAmBiL,gBAAgB,WAAW;YAC7D,MAAMC,OAAO,IAAI,CAACmB,YAAY,CAAC7G,MAAMY,OAAO,EAAE8E,MAAMa;YACpD,MAAMO,OAAO,IAAI,CAACC,kBAAkB,CAAC/G,MAAMY,OAAO,EAAErC;YACpD,IAAI,CAACmH,QAAQ,CAACoB,MAAM,OAAO;YAE3B,OAAO;gBAAEpB;gBAAMnH,SAASuI;gBAAM7F,WAAWjB,MAAMiB,SAAS;YAAC;QAC7D;QAEA,IAAIjB,MAAMxF,IAAI,KAAK,eAAeiL,gBAAgB,kBAAkB;YAChE,MAAMG,OAAO5F,MAAMY,OAAO,EAAEgF;YAC5B,MAAMF,OAAO,IAAI,CAACsB,gBAAgB,CAACpB,MAAMpL,MAAM+L;YAC/C,MAAMO,OAAO,IAAI,CAACC,kBAAkB,CAACnB,MAAMrH;YAC3C,IAAI,CAACmH,QAAQ,CAACoB,MAAM,OAAO;YAE3B,OAAO;gBAAEpB;gBAAMnH,SAASuI;gBAAM7F,WAAWjB,MAAMiB,SAAS;YAAC;QAC7D;QAEA,IAAI,CAACwE,aAAa,OAAO;QAEzB,IAAIC;QACJ,IAAID,gBAAgB,gBAAgB;YAChCC,OAAO;QACX,OAAO,IAAID,gBAAgB,mBAAmBA,gBAAgB,iBAAiB;YAC3EC,OAAO;QACX,OAAO,IAAIa,SAAS;YAChBb,OAAO;QACX,OAAO;YACH,OAAO;QACX;QAEA,MAAMoB,OAAO9G,MAAMY,OAAO,EAAE0E,SAAS3E;QACrC,IAAI,CAACmG,MAAM,OAAO;QAElB,OAAO;YAAEpB;YAAMnH,SAASuI;YAAM7F,WAAWjB,MAAMiB,SAAS;QAAC;IAC7D;IAEQ4F,aAAanB,IAAwB,EAAEa,OAAgB,EAAsC;QACjG,IAAIb,SAAS,QAAQ,OAAO;QAC5B,IAAIA,SAAS,aAAa,OAAO;QACjC,OAAOa,UAAU,WAAW;IAChC;IAEQS,iBAAiBrB,QAA4B,EAAEY,OAAgB,EAAsC;QACzG,IAAIZ,aAAa,gBAAgB,OAAO;QACxC,IAAIA,aAAa,eAAe,OAAO;QACvC,OAAOY,UAAU,WAAW;IAChC;IAEQK,mBAAmB5G,KAAsB,EAAEsF,OAA4B,EAAiB;QAC5F,MAAM2B,SACFjH,MAAMY,OAAO,EAAEsG,WACflH,MAAMY,OAAO,EAAEuG,4CAA4CD;QAE/D,IAAI,CAACD,QAAQ,OAAO;QACpB,OAAO,GAAGA,OAAO,EAAE,EAAE3B,QAAQI,IAAI,CAAC,EAAE,EAAEJ,QAAQ/G,OAAO,EAAE;IAC3D;IAEQwI,mBAAmBxI,OAA4C,EAAU;QAC7E,IAAI,OAAOA,YAAY,UAAU,OAAOA,QAAQoC,IAAI;QACpD,IAAI,CAAC/B,MAAMC,OAAO,CAACN,UAAU,OAAO;QAEpC,OAAOA,QACF5B,GAAG,CAAC,CAACyK,OAASA,KAAKN,IAAI,EACvBO,MAAM,CAAC,CAACP,OAAyB,OAAOA,SAAS,YAAYA,KAAKnG,IAAI,GAAGhF,MAAM,GAAG,GAClFgB,GAAG,CAAC,CAACmK,OAASA,KAAKnG,IAAI,IACvBvF,IAAI,CAAC,MACLuF,IAAI;IACb;IAEA,MAAM2G,aAAaC,IAA0B,EAA6B;QACtE,IAAI,CAACvN,YAAY,IAAI,CAACW,gBAAgB,GAAG,OAAO,EAAE;QAElD,MAAMoH,QAAQ,IAAI,CAACN,sBAAsB;QACzC,MAAM+F,YAA8B,EAAE;QAEtC,KAAK,MAAMhK,YAAYuE,MAAO;YAC1B,MAAM0C,UAAU,IAAI,CAACgD,oBAAoB,CAACjK;YAC1C,IAAI,CAACiH,SAAS;YACd,IAAI8C,MAAMjG,QAAQrD,aAAawG,QAAQnD,GAAG,KAAKiG,KAAKjG,GAAG,EAAE;YACzDkG,UAAU7J,IAAI,CAAC8G;QACnB;QAEA,OAAO+C;IACX;IAEA;;;;KAIC,GACD,AAAQ/F,yBAAmC;QACvC,MAAMiG,MAAgB,EAAE;QAExB,KAAK,MAAMC,aAAazN,YAAY,IAAI,CAACS,gBAAgB,EAAG;YACxD,MAAMiN,UAAUjO,KAAKyB,IAAI,CAAC,IAAI,CAACT,gBAAgB,EAAEgN;YACjD,IAAI,CAAC3N,YAAY4N,UAAU;YAE3B,KAAK,MAAMC,cAAc3N,YAAY0N,SAAU;gBAC3C,MAAME,WAAWnO,KAAKyB,IAAI,CAACwM,SAASC;gBACpC,IAAI,CAAC7N,YAAY8N,WAAW;gBAE5B,KAAK,MAAMC,YAAY7N,YAAY4N,UAAW;oBAC1C,MAAM/E,SAASpJ,KAAKyB,IAAI,CAAC0M,UAAUC;oBACnC,IAAI,CAAC/N,YAAY+I,SAAS;oBAE1B,KAAK,MAAMiF,aAAa9N,YAAY6I,QAAS;wBACzC,IAAI,CAACiF,UAAUC,QAAQ,CAAC,WAAW;wBACnCP,IAAI/J,IAAI,CAAChE,KAAKyB,IAAI,CAAC2H,QAAQiF;oBAC/B;gBACJ;YACJ;QACJ;QAEA,OAAON;IACX;IAEQhG,8BAA8BG,QAAkB,EAAY;QAChE,MAAM6F,MAAgB,EAAE;QAExB,KAAK,MAAM3E,UAAUlB,SAAU;YAC3B,KAAK,MAAMmG,aAAa9N,YAAY6I,QAAS;gBACzC,IAAI,CAACiF,UAAUC,QAAQ,CAAC,WAAW;gBACnCP,IAAI/J,IAAI,CAAChE,KAAKyB,IAAI,CAAC2H,QAAQiF;YAC/B;QACJ;QAEA,OAAON;IACX;IAEA;;;;KAIC,GACD,AAAQD,qBAAqBjK,QAAgB,EAAyB;QAClE,MAAMe,UAAUtE,aAAauD;QAC7B,IAAIe,YAAYN,WAAW,OAAO;QAElC,MAAM4F,WAAWtF,QAAQoC,IAAI,GAAGD,KAAK,CAAC;QACtC,IAAI,CAACmD,QAAQ,CAAC,EAAE,EAAE,OAAO;QAEzB,IAAIC;QACJ,IAAI;YACAA,YAAYpF,KAAKC,KAAK,CAACkF,QAAQ,CAAC,EAAE;QACtC,EAAE,OAAM;YACJ,OAAO;QACX;QAEA,IAAIC,UAAUtJ,IAAI,KAAK,kBAAkB,CAACsJ,UAAUlD,OAAO,EAAEC,IAAI;YAC7D,OAAO;QACX;QAEA,IAAIqH,mBAAmB;QACvB,IAAIC,gBAA6B;QAEjC,IAAK,IAAIpD,IAAI,GAAGA,IAAIlB,SAASlI,MAAM,EAAEoJ,IAAK;YACtC,IAAI/E;YACJ,IAAI;gBACAA,QAAQtB,KAAKC,KAAK,CAACkF,QAAQ,CAACkB,EAAE;YAClC,EAAE,OAAM;gBACJ;YACJ;YAEA,MAAMqD,KAAK,IAAI,CAAC/D,cAAc,CAACrE,MAAMiB,SAAS;YAC9C,IAAImH,IAAID,gBAAgBC;YAExB,IACI,CAACF,oBACDlI,MAAMY,OAAO,EAAEpG,SAAS,kBACxB,OAAOwF,MAAMY,OAAO,CAAC0E,OAAO,KAAK,YACjCtF,MAAMY,OAAO,CAAC0E,OAAO,CAAC3E,IAAI,GAAGhF,MAAM,GAAG,GACxC;gBACEuM,mBAAmBlI,MAAMY,OAAO,CAAC0E,OAAO,CAAC3E,IAAI;YACjD;QACJ;QAEA,MAAMG,OAAO3G,SAASqD;QAEtB,MAAM6K,YACF,IAAI,CAAChE,cAAc,CAACP,UAAUlD,OAAO,CAACK,SAAS,KAC/CkH,iBACArH,MAAMwH,aACNxH,MAAMwD,SACN,IAAIjC;QACR,MAAM+B,aAAa+D,iBAAiBE;QAEpC,OAAO;YACH7N,MAAM;YACN0F,WAAW4D,UAAUlD,OAAO,CAACC,EAAE;YAC/BS,KAAKwC,UAAUlD,OAAO,CAACU,GAAG,IAAI;YAC9B4G;YACA9D;YACAiE;YACApI,iBAAiBzC;QACrB;IACJ;AACJ"}
1
+ {"version":3,"sources":["../../src/adapters/CodexAdapter.ts"],"sourcesContent":["/**\n * Codex Adapter\n *\n * Detects running Codex agents by:\n * 1. Filtering Codex processes from a shared asynchronous process snapshot\n * 2. Using snapshot CWD and start-time enrichment\n * 3. Matching exact PID-to-session metadata from ~/.codex/ai-devkit/sessions.json\n * 4. Discovering session files from ~/.codex/sessions/YYYY/MM/DD/ via shared batchGetSessionFileBirthtimes()\n * 5. Setting resolvedCwd from session_meta first line\n * 6. Matching sessions to processes via shared matchProcessesToSessions()\n * 7. Extracting summary from last event entry in session JSONL\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from './AgentAdapter.js';\nimport { AgentStatus } from './AgentAdapter.js';\nimport { captureProcessSnapshot, executableBasename, filterByProcessNames } from '../utils/process.js';\nimport { batchGetSessionFileBirthtimes, isDirectory, safeReadFile, safeReaddir, safeStat } from '../utils/session.js';\nimport type { SessionFile } from '../utils/session.js';\nimport { matchProcessesToSessions, generateAgentName } from '../utils/matching.js';\nimport { AgentRegistry } from '../utils/AgentRegistry.js';\n\ninterface CodexEventEntry {\n timestamp?: string;\n type?: string;\n payload?: {\n type?: string;\n message?: string;\n id?: string;\n cwd?: string;\n timestamp?: string;\n role?: string;\n content?: CodexContent[];\n item?: CodexItem;\n turn_id?: string;\n internal_chat_message_metadata_passthrough?: {\n turn_id?: string;\n };\n };\n}\n\ninterface CodexContent {\n type?: string;\n text?: string;\n}\n\ninterface CodexItem {\n type?: string;\n content?: string | CodexContent[];\n}\n\ninterface CodexSession {\n sessionId: string;\n projectPath: string;\n summary: string;\n sessionStart: Date;\n lastActive: Date;\n lastPayloadType?: string;\n}\n\ninterface DirectMatch {\n process: ProcessInfo;\n sessionFile: SessionFile;\n}\n\ninterface DirectMatchResult {\n agents: AgentInfo[];\n failedProcesses: ProcessInfo[];\n}\n\ninterface MappingMatch {\n process: ProcessInfo;\n filePath: string;\n}\n\ninterface MappingMatchResult {\n agents: AgentInfo[];\n fallback: ProcessInfo[];\n}\n\nexport class CodexAdapter implements AgentAdapter {\n readonly type = 'codex' as const;\n readonly processNames = ['codex'] as const;\n\n private static readonly IDLE_THRESHOLD_MINUTES = 5;\n /** Include session files around process start day to recover long-lived processes. */\n private static readonly PROCESS_START_DAY_WINDOW_DAYS = 1;\n\n private codexSessionsDir: string;\n private sessionMappingPath: string;\n private registry: AgentRegistry;\n\n constructor(registry: AgentRegistry = AgentRegistry.default()) {\n const homeDir = process.env.HOME || process.env.USERPROFILE || '';\n this.codexSessionsDir = path.join(homeDir, '.codex', 'sessions');\n this.sessionMappingPath = path.join(homeDir, '.codex', 'ai-devkit', 'sessions.json');\n this.registry = registry;\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n return this.isCodexExecutable(processInfo.command);\n }\n\n /**\n * Detect running Codex agents\n */\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const snapshot = context?.processes ?? await captureProcessSnapshot(this.processNames);\n const relevant = filterByProcessNames(snapshot, this.processNames);\n const processes = relevant.filter((process) => this.canHandle(process));\n if (processes.length === 0) return [];\n\n const { cachedAgents, remaining } = this.tryRegistryCache(processes);\n if (remaining.length === 0) return cachedAgents;\n\n const mappingResult = this.mapSessionMappingMatches(remaining);\n const { direct, fallback } = this.tryResumeMatching(mappingResult.fallback);\n const directResult = this.mapDirectMatches(direct);\n const { sessions, contentCache } = this.discoverSessions(fallback);\n if (sessions.length === 0) {\n return [\n ...cachedAgents,\n ...mappingResult.agents,\n ...directResult.agents,\n ...directResult.failedProcesses.map((p) => this.mapProcessOnlyAgent(p)),\n ...fallback.map((p) => this.mapProcessOnlyAgent(p)),\n ];\n }\n\n const matches = matchProcessesToSessions(fallback, sessions);\n const matchedPids = new Set([\n ...directResult.agents.map((a) => a.pid),\n ...matches.map((m) => m.process.pid),\n ]);\n const agents: AgentInfo[] = [...directResult.agents];\n\n for (const match of matches) {\n const cachedContent = contentCache.get(match.session.filePath);\n const sessionData = this.parseSession(cachedContent, match.session.filePath);\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, match.process, match.session.filePath));\n } else {\n matchedPids.delete(match.process.pid);\n }\n }\n\n for (const proc of fallback) {\n if (!matchedPids.has(proc.pid)) {\n agents.push(this.mapProcessOnlyAgent(proc));\n }\n }\n\n for (const proc of directResult.failedProcesses) {\n agents.push(this.mapProcessOnlyAgent(proc));\n }\n\n return [...cachedAgents, ...mappingResult.agents, ...agents];\n }\n\n private mapSessionMappingMatches(processes: ProcessInfo[]): MappingMatchResult {\n const { matches, fallback } = this.matchFromSessionMapping(processes);\n const agents: AgentInfo[] = [];\n\n for (const match of matches) {\n const sessionData = this.parseSession(undefined, match.filePath);\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, match.process, match.filePath));\n } else {\n fallback.push(match.process);\n }\n }\n\n return { agents, fallback };\n }\n\n private matchFromSessionMapping(processes: ProcessInfo[]): {\n matches: MappingMatch[];\n fallback: ProcessInfo[];\n } {\n const mapping = this.readSessionMapping();\n if (mapping.size === 0) return { matches: [], fallback: processes };\n\n const matches: MappingMatch[] = [];\n const fallback: ProcessInfo[] = [];\n\n for (const proc of processes) {\n const filePath = mapping.get(proc.pid);\n if (!filePath || !this.isTrustedSessionPath(filePath) || !fs.existsSync(filePath)) {\n fallback.push(proc);\n continue;\n }\n\n matches.push({ process: proc, filePath });\n }\n\n return { matches, fallback };\n }\n\n private readSessionMapping(): Map<number, string> {\n const content = safeReadFile(this.sessionMappingPath);\n if (content === undefined) return new Map();\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(content);\n } catch {\n return new Map();\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return new Map();\n\n const map = new Map<number, string>();\n for (const [key, value] of Object.entries(parsed)) {\n const pid = this.toPid(key);\n if (pid !== null && typeof value === 'string' && value) {\n map.set(pid, value);\n }\n }\n\n return map;\n }\n\n private toPid(value: unknown): number | null {\n if (typeof value === 'number' && Number.isInteger(value) && value > 0) return value;\n if (typeof value !== 'string' || !/^\\d+$/.test(value)) return null;\n\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n }\n\n private isTrustedSessionPath(filePath: string): boolean {\n const resolvedRoot = path.resolve(this.codexSessionsDir);\n const resolvedPath = path.resolve(filePath);\n return resolvedPath === resolvedRoot || resolvedPath.startsWith(`${resolvedRoot}${path.sep}`);\n }\n\n private tryRegistryCache(processes: ProcessInfo[]): {\n cachedAgents: AgentInfo[];\n remaining: ProcessInfo[];\n } {\n const cachedAgents: AgentInfo[] = [];\n const remaining: ProcessInfo[] = [];\n const byPid = new Map(this.registry.list().map((e) => [e.pid, e]));\n\n for (const proc of processes) {\n const entry = byPid.get(proc.pid);\n if (\n !entry ||\n entry.type !== this.type ||\n !entry.sessionFilePath ||\n !fs.existsSync(entry.sessionFilePath)\n ) {\n remaining.push(proc);\n continue;\n }\n\n const content = safeReadFile(entry.sessionFilePath);\n const sessionData = this.parseSession(content, entry.sessionFilePath);\n if (!sessionData) {\n remaining.push(proc);\n continue;\n }\n\n cachedAgents.push(this.mapSessionToAgent(sessionData, proc, entry.sessionFilePath));\n }\n\n return { cachedAgents, remaining };\n }\n\n /**\n * Match processes via `codex resume <uuid>` in their command line.\n * Resumed sessions predate the process, so birth-time proximity cannot\n * reliably pair them with the running PID.\n */\n private tryResumeMatching(processes: ProcessInfo[]): {\n direct: DirectMatch[];\n fallback: ProcessInfo[];\n } {\n const direct: DirectMatch[] = [];\n const fallback: ProcessInfo[] = [];\n\n for (const proc of processes) {\n const sessionId = this.extractResumeSessionId(proc.command);\n if (!sessionId) {\n fallback.push(proc);\n continue;\n }\n\n const sessionFile = this.findSessionFileById(sessionId);\n if (!sessionFile) {\n fallback.push(proc);\n continue;\n }\n\n direct.push({ process: proc, sessionFile });\n }\n\n return { direct, fallback };\n }\n\n private extractResumeSessionId(command: string): string | null {\n const match = command.match(/(?:^|\\s)resume\\s+([0-9a-f-]{36})(?:\\s|$)/i);\n return match?.[1] ?? null;\n }\n\n private findSessionFileById(sessionId: string): SessionFile | null {\n for (const filePath of this.getCandidateSessionFiles(sessionId)) {\n if (!path.basename(filePath).includes(sessionId)) continue;\n\n const content = safeReadFile(filePath);\n const firstLine = content?.split('\\n')[0]?.trim();\n if (!firstLine) continue;\n\n try {\n const parsed = JSON.parse(firstLine) as CodexEventEntry;\n if (parsed.type !== 'session_meta' || parsed.payload?.id !== sessionId) {\n continue;\n }\n\n const stat = safeStat(filePath);\n if (!stat) continue;\n const metaTimestampMs = this.parseMetaTimestampMs(parsed.payload?.timestamp);\n\n return {\n sessionId,\n filePath,\n projectDir: path.dirname(filePath),\n birthtimeMs: metaTimestampMs ?? stat.birthtimeMs,\n resolvedCwd: parsed.payload?.cwd || '',\n };\n } catch {\n continue;\n }\n }\n\n return null;\n }\n\n private getCandidateSessionFiles(sessionId: string): string[] {\n // Codex currently writes UUIDv7 session IDs, so the ID can narrow lookup\n // to the creation-date directory. Fall back for older or changed formats.\n const sessionDate = this.tryParseUuidV7Date(sessionId);\n if (!sessionDate) return this.collectAllSessionFiles();\n\n return this.collectSessionFilesInDateDirs(\n this.getDateDirsAroundDate(sessionDate, CodexAdapter.PROCESS_START_DAY_WINDOW_DAYS),\n );\n }\n\n private mapDirectMatches(matches: DirectMatch[]): DirectMatchResult {\n const agents: AgentInfo[] = [];\n const failedProcesses: ProcessInfo[] = [];\n\n for (const match of matches) {\n const sessionData = this.parseSession(undefined, match.sessionFile.filePath);\n if (sessionData) {\n agents.push(this.mapSessionToAgent(sessionData, match.process, match.sessionFile.filePath));\n } else {\n failedProcesses.push(match.process);\n }\n }\n\n return { agents, failedProcesses };\n }\n\n /**\n * Discover session files for the given processes.\n *\n * Uses process start times to determine which YYYY/MM/DD date directories\n * to scan (±1 day window), then batches stat calls across all directories.\n * Reads each file once and caches content for later parsing by parseSession().\n * Sets resolvedCwd from session_meta first line.\n */\n private discoverSessions(processes: ProcessInfo[]): {\n sessions: SessionFile[];\n contentCache: Map<string, string>;\n } {\n const empty = { sessions: [], contentCache: new Map<string, string>() };\n if (!fs.existsSync(this.codexSessionsDir)) return empty;\n\n const dateDirs = this.getDateDirs(processes);\n if (dateDirs.length === 0) return empty;\n\n const files = batchGetSessionFileBirthtimes(dateDirs);\n const contentCache = new Map<string, string>();\n\n // Read each file once: extract CWD for matching, cache content for later parsing\n for (const file of files) {\n try {\n const content = fs.readFileSync(file.filePath, 'utf-8');\n contentCache.set(file.filePath, content);\n\n const firstLine = content.split('\\n')[0]?.trim();\n if (firstLine) {\n const parsed = JSON.parse(firstLine);\n if (parsed.type === 'session_meta') {\n file.resolvedCwd = parsed.payload?.cwd || '';\n const metaTimestampMs = this.parseMetaTimestampMs(parsed.payload?.timestamp);\n if (metaTimestampMs !== null) {\n file.birthtimeMs = metaTimestampMs;\n }\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n\n return { sessions: files, contentCache };\n }\n\n /**\n * Determine which date directories to scan based on process start times.\n * Returns only directories that actually exist.\n */\n private getDateDirs(processes: ProcessInfo[]): string[] {\n const dayKeys = new Set<string>();\n const window = CodexAdapter.PROCESS_START_DAY_WINDOW_DAYS;\n\n for (const proc of processes) {\n const startTime = proc.startTime || new Date();\n for (let offset = -window; offset <= window; offset++) {\n const day = new Date(startTime.getTime());\n day.setDate(day.getDate() + offset);\n dayKeys.add(this.toSessionDayKey(day));\n }\n }\n\n const dirs: string[] = [];\n for (const dayKey of dayKeys) {\n const dayDir = path.join(this.codexSessionsDir, dayKey);\n try {\n if (fs.statSync(dayDir).isDirectory()) {\n dirs.push(dayDir);\n }\n } catch {\n continue;\n }\n }\n\n return dirs;\n }\n\n private getDateDirsAroundDate(date: Date, windowDays: number): string[] {\n const dirs: string[] = [];\n\n for (let offset = -windowDays; offset <= windowDays; offset++) {\n const day = new Date(date.getTime());\n day.setDate(day.getDate() + offset);\n const dayDir = path.join(this.codexSessionsDir, this.toSessionDayKey(day));\n if (isDirectory(dayDir)) {\n dirs.push(dayDir);\n }\n }\n\n return dirs;\n }\n\n private toSessionDayKey(date: Date): string {\n const yyyy = String(date.getFullYear()).padStart(4, '0');\n const mm = String(date.getMonth() + 1).padStart(2, '0');\n const dd = String(date.getDate()).padStart(2, '0');\n return path.join(yyyy, mm, dd);\n }\n\n private tryParseUuidV7Date(sessionId: string): Date | null {\n const match = sessionId.match(/^([0-9a-f]{8})-([0-9a-f]{4})-7[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i);\n if (!match) return null;\n\n const timestampMs = Number.parseInt(`${match[1]}${match[2]}`, 16);\n if (!Number.isSafeInteger(timestampMs) || timestampMs <= 0) return null;\n\n const date = new Date(timestampMs);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n\n /**\n * Parse session file content into CodexSession.\n * Uses cached content if available, otherwise reads from disk.\n */\n private parseSession(cachedContent: string | undefined, filePath: string): CodexSession | null {\n let content: string;\n if (cachedContent !== undefined) {\n content = cachedContent;\n } else {\n try {\n content = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n }\n\n const allLines = content.trim().split('\\n');\n if (!allLines[0]) return null;\n\n let metaEntry: CodexEventEntry;\n try {\n metaEntry = JSON.parse(allLines[0]);\n } catch {\n return null;\n }\n\n if (metaEntry.type !== 'session_meta' || !metaEntry.payload?.id) {\n return null;\n }\n\n const entries: CodexEventEntry[] = [];\n for (const line of allLines) {\n try {\n entries.push(JSON.parse(line));\n } catch {\n continue;\n }\n }\n\n const lastEntry = this.findLastEventEntry(entries);\n const lastPayloadType = lastEntry ? this.normalizedPayloadType(lastEntry) : undefined;\n\n const lastActive =\n this.parseTimestamp(lastEntry?.timestamp) ||\n this.parseTimestamp(metaEntry.payload.timestamp) ||\n fs.statSync(filePath).mtime;\n const sessionStart =\n this.parseTimestamp(metaEntry.payload.timestamp) ||\n lastActive;\n\n return {\n sessionId: metaEntry.payload.id,\n projectPath: metaEntry.payload.cwd || '',\n summary: this.extractSummary(entries),\n sessionStart,\n lastActive,\n lastPayloadType,\n };\n }\n\n private mapSessionToAgent(session: CodexSession, processInfo: ProcessInfo, filePath: string): AgentInfo {\n return {\n name: generateAgentName(session.projectPath || processInfo.cwd || '', processInfo.pid),\n type: this.type,\n status: this.determineStatus(session),\n summary: session.summary || 'Codex session active',\n pid: processInfo.pid,\n projectPath: session.projectPath || processInfo.cwd || '',\n sessionId: session.sessionId,\n lastActive: session.lastActive,\n sessionFilePath: filePath,\n };\n }\n\n private mapProcessOnlyAgent(processInfo: ProcessInfo): AgentInfo {\n return {\n name: generateAgentName(processInfo.cwd || '', processInfo.pid),\n type: this.type,\n status: AgentStatus.RUNNING,\n summary: 'Codex process running',\n pid: processInfo.pid,\n projectPath: processInfo.cwd || '',\n sessionId: `pid-${processInfo.pid}`,\n lastActive: new Date(),\n };\n }\n\n private findLastEventEntry(entries: CodexEventEntry[]): CodexEventEntry | undefined {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry && typeof entry.type === 'string') {\n return entry;\n }\n }\n return undefined;\n }\n\n private parseTimestamp(value?: string): Date | null {\n if (!value) return null;\n const timestamp = new Date(value);\n return Number.isNaN(timestamp.getTime()) ? null : timestamp;\n }\n\n private parseMetaTimestampMs(value?: string): number | null {\n if (typeof value !== 'string') return null;\n\n const timestamp = this.parseTimestamp(value);\n if (!timestamp) return null;\n\n const timestampMs = timestamp.getTime();\n return Number.isFinite(timestampMs) ? timestampMs : null;\n }\n\n private determineStatus(session: CodexSession): AgentStatus {\n const diffMs = Date.now() - session.lastActive.getTime();\n const diffMinutes = diffMs / 60000;\n\n if (diffMinutes > CodexAdapter.IDLE_THRESHOLD_MINUTES) {\n return AgentStatus.IDLE;\n }\n\n if (\n session.lastPayloadType === 'agent_message' ||\n session.lastPayloadType === 'task_complete' ||\n session.lastPayloadType === 'turn_aborted'\n ) {\n return AgentStatus.WAITING;\n }\n\n return AgentStatus.RUNNING;\n }\n\n private extractSummary(entries: CodexEventEntry[]): string {\n for (let i = entries.length - 1; i >= 0; i--) {\n const message = this.extractEntryText(entries[i]);\n if (message) return this.truncate(message, 120);\n }\n\n return 'Codex session active';\n }\n\n private normalizedPayloadType(entry: CodexEventEntry): string | undefined {\n const payloadType = entry.payload?.type;\n\n if (entry.type === 'response_item' && payloadType === 'message') {\n if (entry.payload?.role === 'assistant') return 'agent_message';\n if (entry.payload?.role === 'user') return 'user_message';\n return payloadType;\n }\n\n if (entry.type === 'event_msg' && payloadType === 'item_completed') {\n const itemType = entry.payload?.item?.type;\n if (itemType === 'AgentMessage') return 'agent_message';\n if (itemType === 'UserMessage') return 'user_message';\n return itemType ?? payloadType;\n }\n\n return payloadType;\n }\n\n private extractEntryText(entry: CodexEventEntry | undefined): string {\n if (!entry) return '';\n\n const legacyMessage = entry.payload?.message;\n if (typeof legacyMessage === 'string' && legacyMessage.trim().length > 0) {\n return legacyMessage.trim();\n }\n\n const conversationMessage = this.toConversationMessage(entry, false);\n return conversationMessage?.content.trim() ?? '';\n }\n\n private truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n }\n\n private isCodexExecutable(command: string): boolean {\n const base = executableBasename(command);\n return base === 'codex' || base === 'codex.exe';\n }\n\n /**\n * Read the full conversation from a Codex session JSONL file.\n *\n * Codex entries use either legacy payload.message fields or current\n * response_item/event_msg content arrays.\n */\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n const verbose = options?.verbose ?? false;\n\n const content = safeReadFile(sessionFilePath);\n if (content === undefined) return [];\n\n const lines = content.trim().split('\\n');\n const entries: CodexEventEntry[] = [];\n const messages: ConversationMessage[] = [];\n\n for (const line of lines) {\n try {\n entries.push(JSON.parse(line));\n } catch {\n continue;\n }\n }\n\n const responseItemMirrorKeys = new Set<string>();\n for (const entry of entries) {\n if (entry.type !== 'response_item') continue;\n\n const message = this.toConversationMessage(entry, verbose);\n const mirrorKey = message ? this.mirroredMessageKey(entry, message) : null;\n if (mirrorKey) responseItemMirrorKeys.add(mirrorKey);\n }\n\n for (const entry of entries) {\n const message = this.toConversationMessage(entry, verbose);\n if (!message) continue;\n\n const mirrorKey = this.mirroredMessageKey(entry, message);\n if (\n entry.type === 'event_msg' &&\n mirrorKey &&\n responseItemMirrorKeys.has(mirrorKey)\n ) {\n continue;\n }\n\n messages.push(message);\n }\n\n return messages;\n }\n\n private toConversationMessage(entry: CodexEventEntry, verbose: boolean): ConversationMessage | null {\n if (entry.type === 'session_meta') return null;\n\n const payloadType = entry.payload?.type;\n if (entry.type === 'response_item' && payloadType === 'message') {\n const role = this.mapCodexRole(entry.payload?.role, verbose);\n const text = this.extractContentText(entry.payload?.content);\n if (!role || !text) return null;\n\n return { role, content: text, timestamp: entry.timestamp };\n }\n\n if (entry.type === 'event_msg' && payloadType === 'item_completed') {\n const item = entry.payload?.item;\n const role = this.mapCodexItemRole(item?.type, verbose);\n const text = this.extractContentText(item?.content);\n if (!role || !text) return null;\n\n return { role, content: text, timestamp: entry.timestamp };\n }\n\n if (!payloadType) return null;\n\n let role: ConversationMessage['role'];\n if (payloadType === 'user_message') {\n role = 'user';\n } else if (payloadType === 'agent_message' || payloadType === 'task_complete') {\n role = 'assistant';\n } else if (verbose) {\n role = 'system';\n } else {\n return null;\n }\n\n const text = entry.payload?.message?.trim();\n if (!text) return null;\n\n return { role, content: text, timestamp: entry.timestamp };\n }\n\n private mapCodexRole(role: string | undefined, verbose: boolean): ConversationMessage['role'] | null {\n if (role === 'user') return 'user';\n if (role === 'assistant') return 'assistant';\n return verbose ? 'system' : null;\n }\n\n private mapCodexItemRole(itemType: string | undefined, verbose: boolean): ConversationMessage['role'] | null {\n if (itemType === 'AgentMessage') return 'assistant';\n if (itemType === 'UserMessage') return 'user';\n return verbose ? 'system' : null;\n }\n\n private mirroredMessageKey(entry: CodexEventEntry, message: ConversationMessage): string | null {\n const turnId =\n entry.payload?.turn_id ||\n entry.payload?.internal_chat_message_metadata_passthrough?.turn_id;\n\n if (!turnId) return null;\n return `${turnId}\\0${message.role}\\0${message.content}`;\n }\n\n private extractContentText(content: string | CodexContent[] | undefined): string {\n if (typeof content === 'string') return content.trim();\n if (!Array.isArray(content)) return '';\n\n return content\n .map((part) => part.text)\n .filter((text): text is string => typeof text === 'string' && text.trim().length > 0)\n .map((text) => text.trim())\n .join('\\n')\n .trim();\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n if (!isDirectory(this.codexSessionsDir)) return [];\n\n const files = this.collectAllSessionFiles();\n const summaries: SessionSummary[] = [];\n\n for (const filePath of files) {\n const summary = this.fileToSessionSummary(filePath);\n if (!summary) continue;\n if (opts?.cwd !== undefined && summary.cwd !== opts.cwd) continue;\n summaries.push(summary);\n }\n\n return summaries;\n }\n\n /**\n * Walk every YYYY/MM/DD directory under `codexSessionsDir` and return\n * absolute paths of `.jsonl` files. Tolerates malformed layouts\n * (skips entries that aren't directories at the expected depth).\n */\n private collectAllSessionFiles(): string[] {\n const out: string[] = [];\n\n for (const yearEntry of safeReaddir(this.codexSessionsDir)) {\n const yearDir = path.join(this.codexSessionsDir, yearEntry);\n if (!isDirectory(yearDir)) continue;\n\n for (const monthEntry of safeReaddir(yearDir)) {\n const monthDir = path.join(yearDir, monthEntry);\n if (!isDirectory(monthDir)) continue;\n\n for (const dayEntry of safeReaddir(monthDir)) {\n const dayDir = path.join(monthDir, dayEntry);\n if (!isDirectory(dayDir)) continue;\n\n for (const fileEntry of safeReaddir(dayDir)) {\n if (!fileEntry.endsWith('.jsonl')) continue;\n out.push(path.join(dayDir, fileEntry));\n }\n }\n }\n }\n\n return out;\n }\n\n private collectSessionFilesInDateDirs(dateDirs: string[]): string[] {\n const out: string[] = [];\n\n for (const dayDir of dateDirs) {\n for (const fileEntry of safeReaddir(dayDir)) {\n if (!fileEntry.endsWith('.jsonl')) continue;\n out.push(path.join(dayDir, fileEntry));\n }\n }\n\n return out;\n }\n\n /**\n * Read a Codex session JSONL file and produce a {@link SessionSummary}.\n * Returns null when the file is unreadable, has no `session_meta`, or\n * lacks a session id.\n */\n private fileToSessionSummary(filePath: string): SessionSummary | null {\n const content = safeReadFile(filePath);\n if (content === undefined) return null;\n\n const allLines = content.trim().split('\\n');\n if (!allLines[0]) return null;\n\n let metaEntry: CodexEventEntry;\n try {\n metaEntry = JSON.parse(allLines[0]);\n } catch {\n return null;\n }\n\n if (metaEntry.type !== 'session_meta' || !metaEntry.payload?.id) {\n return null;\n }\n\n let firstUserMessage = '';\n let lastTimestamp: Date | null = null;\n\n for (let i = 1; i < allLines.length; i++) {\n let entry: CodexEventEntry;\n try {\n entry = JSON.parse(allLines[i]);\n } catch {\n continue;\n }\n\n const ts = this.parseTimestamp(entry.timestamp);\n if (ts) lastTimestamp = ts;\n\n if (\n !firstUserMessage &&\n entry.payload?.type === 'user_message' &&\n typeof entry.payload.message === 'string' &&\n entry.payload.message.trim().length > 0\n ) {\n firstUserMessage = entry.payload.message.trim();\n }\n }\n\n const stat = safeStat(filePath);\n\n const startedAt =\n this.parseTimestamp(metaEntry.payload.timestamp) ||\n lastTimestamp ||\n stat?.birthtime ||\n stat?.mtime ||\n new Date();\n const lastActive = lastTimestamp || startedAt;\n\n return {\n type: 'codex',\n sessionId: metaEntry.payload.id,\n cwd: metaEntry.payload.cwd || '',\n firstUserMessage,\n lastActive,\n startedAt,\n sessionFilePath: filePath,\n };\n }\n}\n"],"names":["fs","path","AgentStatus","captureProcessSnapshot","executableBasename","filterByProcessNames","batchGetSessionFileBirthtimes","isDirectory","safeReadFile","safeReaddir","safeStat","matchProcessesToSessions","generateAgentName","AgentRegistry","CodexAdapter","type","processNames","IDLE_THRESHOLD_MINUTES","PROCESS_START_DAY_WINDOW_DAYS","codexSessionsDir","sessionMappingPath","registry","default","homeDir","process","env","HOME","USERPROFILE","join","canHandle","processInfo","isCodexExecutable","command","detectAgents","context","snapshot","processes","relevant","filter","length","cachedAgents","remaining","tryRegistryCache","mappingResult","mapSessionMappingMatches","direct","fallback","tryResumeMatching","directResult","mapDirectMatches","sessions","contentCache","discoverSessions","agents","failedProcesses","map","p","mapProcessOnlyAgent","matches","matchedPids","Set","a","pid","m","match","cachedContent","get","session","filePath","sessionData","parseSession","push","mapSessionToAgent","delete","proc","has","matchFromSessionMapping","undefined","mapping","readSessionMapping","size","isTrustedSessionPath","existsSync","content","Map","parsed","JSON","parse","Array","isArray","key","value","Object","entries","toPid","set","Number","isInteger","test","isSafeInteger","resolvedRoot","resolve","resolvedPath","startsWith","sep","byPid","list","e","entry","sessionFilePath","sessionId","extractResumeSessionId","sessionFile","findSessionFileById","getCandidateSessionFiles","basename","includes","firstLine","split","trim","payload","id","stat","metaTimestampMs","parseMetaTimestampMs","timestamp","projectDir","dirname","birthtimeMs","resolvedCwd","cwd","sessionDate","tryParseUuidV7Date","collectAllSessionFiles","collectSessionFilesInDateDirs","getDateDirsAroundDate","empty","dateDirs","getDateDirs","files","file","readFileSync","dayKeys","window","startTime","Date","offset","day","getTime","setDate","getDate","add","toSessionDayKey","dirs","dayKey","dayDir","statSync","date","windowDays","yyyy","String","getFullYear","padStart","mm","getMonth","dd","timestampMs","parseInt","isNaN","allLines","metaEntry","line","lastEntry","findLastEventEntry","lastPayloadType","normalizedPayloadType","lastActive","parseTimestamp","mtime","sessionStart","projectPath","summary","extractSummary","name","status","determineStatus","RUNNING","i","isFinite","diffMs","now","diffMinutes","IDLE","WAITING","message","extractEntryText","truncate","payloadType","role","itemType","item","legacyMessage","conversationMessage","toConversationMessage","maxLength","slice","base","getConversation","options","verbose","lines","messages","responseItemMirrorKeys","mirrorKey","mirroredMessageKey","mapCodexRole","text","extractContentText","mapCodexItemRole","turnId","turn_id","internal_chat_message_metadata_passthrough","part","listSessions","opts","summaries","fileToSessionSummary","out","yearEntry","yearDir","monthEntry","monthDir","dayEntry","fileEntry","endsWith","firstUserMessage","lastTimestamp","ts","startedAt","birthtime"],"mappings":"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"}
@@ -2,16 +2,17 @@
2
2
  * Copilot Adapter
3
3
  *
4
4
  * Detects running GitHub Copilot CLI agents by:
5
- * 1. Finding running copilot processes via shared listAgentProcesses()
6
- * 2. Enriching with CWD and start times via shared enrichProcesses()
5
+ * 1. Filtering Copilot processes from a shared asynchronous process snapshot
6
+ * 2. Using snapshot CWD and start-time enrichment
7
7
  * 3. Mapping active ~/.copilot/session-state/{sessionId}/inuse.{pid}.lock files to processes
8
8
  * 4. Reading events.jsonl as the primary session/conversation source
9
9
  * 5. Reading workspace.yaml as a flat fallback metadata source
10
10
  */
11
- import type { AgentAdapter, AgentInfo, ConversationMessage, ListSessionsOptions, ProcessInfo, SessionSummary } from './AgentAdapter.js';
11
+ import type { AgentAdapter, AgentInfo, ConversationMessage, ListSessionsOptions, ProcessInfo, SessionSummary, AgentDetectionContext } from './AgentAdapter.js';
12
12
  import { AgentRegistry } from '../utils/AgentRegistry.js';
13
13
  export declare class CopilotAdapter implements AgentAdapter {
14
14
  readonly type: "copilot";
15
+ readonly processNames: readonly ["copilot"];
15
16
  private static readonly IDLE_THRESHOLD_MINUTES;
16
17
  private static readonly VERBOSE_SYSTEM_EVENTS;
17
18
  private static readonly WAITING_EVENTS;
@@ -19,7 +20,7 @@ export declare class CopilotAdapter implements AgentAdapter {
19
20
  private registry;
20
21
  constructor(registry?: AgentRegistry);
21
22
  canHandle(processInfo: ProcessInfo): boolean;
22
- detectAgents(): Promise<AgentInfo[]>;
23
+ detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]>;
23
24
  private applyWrapperRegistryName;
24
25
  getConversation(sessionFilePath: string, options?: {
25
26
  verbose?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"CopilotAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/CopilotAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EACR,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACjB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAC;AAiE9E,qBAAa,cAAe,YAAW,YAAY;IAC/C,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAU;IAEnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAK;IACnD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAQ1C;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAKnC;IAEH,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAgB;gBAEpB,QAAQ,GAAE,aAAuC;IAM7D,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAItC,YAAY,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAoC1C,OAAO,CAAC,wBAAwB;IAahC,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAwB1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAsBzE,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,mBAAmB;CAK9B"}
1
+ {"version":3,"file":"CopilotAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/CopilotAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EACR,YAAY,EACZ,SAAS,EACT,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,qBAAqB,EACxB,MAAM,mBAAmB,CAAC;AAW3B,OAAO,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAC;AAiE9E,qBAAa,cAAe,YAAW,YAAY;IAC/C,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAU;IACnC,QAAQ,CAAC,YAAY,uBAAwB;IAE7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAK;IACnD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAQ1C;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAKnC;IAEH,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAgB;gBAEpB,QAAQ,GAAE,aAAuC;IAM7D,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAItC,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAsCzE,OAAO,CAAC,wBAAwB;IAahC,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAwB1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAsBzE,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,mBAAmB;CAI9B"}
@@ -2,19 +2,22 @@
2
2
  * Copilot Adapter
3
3
  *
4
4
  * Detects running GitHub Copilot CLI agents by:
5
- * 1. Finding running copilot processes via shared listAgentProcesses()
6
- * 2. Enriching with CWD and start times via shared enrichProcesses()
5
+ * 1. Filtering Copilot processes from a shared asynchronous process snapshot
6
+ * 2. Using snapshot CWD and start-time enrichment
7
7
  * 3. Mapping active ~/.copilot/session-state/{sessionId}/inuse.{pid}.lock files to processes
8
8
  * 4. Reading events.jsonl as the primary session/conversation source
9
9
  * 5. Reading workspace.yaml as a flat fallback metadata source
10
10
  */ import * as path from 'path';
11
11
  import { AgentStatus } from './AgentAdapter.js';
12
- import { enrichProcesses, findWrapperProcess, findWrapperProcessPids, listAgentProcesses } from '../utils/process.js';
12
+ import { captureProcessSnapshot, executableBasename, filterByProcessNames, findWrapperProcess, findWrapperProcessPids } from '../utils/process.js';
13
13
  import { generateAgentName } from '../utils/matching.js';
14
14
  import { isDirectory, safeReadFile, safeReaddir, safeStat } from '../utils/session.js';
15
15
  import { AgentRegistry } from '../utils/AgentRegistry.js';
16
16
  export class CopilotAdapter {
17
17
  type = 'copilot';
18
+ processNames = [
19
+ 'copilot'
20
+ ];
18
21
  static IDLE_THRESHOLD_MINUTES = 5;
19
22
  static VERBOSE_SYSTEM_EVENTS = new Set([
20
23
  'system.message',
@@ -41,8 +44,10 @@ export class CopilotAdapter {
41
44
  canHandle(processInfo) {
42
45
  return this.isCopilotExecutable(processInfo.command);
43
46
  }
44
- async detectAgents() {
45
- const processes = enrichProcesses(listAgentProcesses('copilot'));
47
+ async detectAgents(context) {
48
+ const snapshot = context?.processes ?? await captureProcessSnapshot(this.processNames);
49
+ const relevant = filterByProcessNames(snapshot, this.processNames);
50
+ const processes = relevant.filter((process1)=>this.canHandle(process1));
46
51
  if (processes.length === 0) return [];
47
52
  const processByPid = new Map(processes.map((proc)=>[
48
53
  proc.pid,
@@ -316,8 +321,7 @@ export class CopilotAdapter {
316
321
  return `${value.slice(0, maxLength - 3)}...`;
317
322
  }
318
323
  isCopilotExecutable(command) {
319
- const executable = command.trim().split(/\s+/)[0] || '';
320
- const base = path.basename(executable).toLowerCase();
324
+ const base = executableBasename(command);
321
325
  return base === 'copilot' || base === 'copilot.exe';
322
326
  }
323
327
  }