@ai-devkit/agent-manager 0.32.0 → 0.33.0
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.
- package/dist/AgentManager.d.ts +3 -3
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +80 -79
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +413 -388
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +642 -642
- package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
- package/dist/__tests__/adapters/CodexAdapter.test.js +322 -2321
- package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
- package/dist/__tests__/adapters/CopilotAdapter.test.js +254 -248
- package/dist/__tests__/adapters/CopilotAdapter.test.js.map +1 -1
- package/dist/__tests__/adapters/GeminiCliAdapter.test.js +564 -558
- package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +1 -1
- package/dist/__tests__/adapters/GrokCliAdapter.test.js +114 -114
- package/dist/__tests__/adapters/GrokCliAdapter.test.js.map +1 -1
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js +169 -169
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +1 -1
- package/dist/__tests__/capacity/codex.test.js +93 -93
- package/dist/__tests__/capacity/codex.test.js.map +1 -1
- package/dist/__tests__/capacity/index.test.js +17 -17
- package/dist/__tests__/capacity/index.test.js.map +1 -1
- package/dist/__tests__/durable/CodexCliProbe.test.js +60 -60
- package/dist/__tests__/durable/CodexCliProbe.test.js.map +1 -1
- package/dist/__tests__/durable/CodexDurableAgentRepository.test.js +28 -28
- package/dist/__tests__/durable/CodexDurableAgentRepository.test.js.map +1 -1
- package/dist/__tests__/durable/CodexPrintAgent.integration.test.js +30 -30
- package/dist/__tests__/durable/CodexPrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/durable/CodexPrintAgentService.test.js +71 -71
- package/dist/__tests__/durable/CodexPrintAgentService.test.js.map +1 -1
- package/dist/__tests__/durable/CodexPrintRunner.test.js +125 -125
- package/dist/__tests__/durable/CodexPrintRunner.test.js.map +1 -1
- package/dist/__tests__/print/ClaudeCliProbe.test.js +20 -20
- package/dist/__tests__/print/ClaudeCliProbe.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +20 -20
- package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintAgentService.test.js +58 -58
- package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
- package/dist/__tests__/print/ClaudePrintRunner.test.js +55 -55
- package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
- package/dist/__tests__/print/DurableAgent.test.js +9 -9
- package/dist/__tests__/print/DurableAgent.test.js.map +1 -1
- package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js +40 -40
- package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js.map +1 -1
- package/dist/__tests__/print/DurableAgentRepository.test.js +66 -66
- package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -1
- package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js +34 -34
- package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js.map +1 -1
- package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js +132 -132
- package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js.map +1 -1
- package/dist/__tests__/providers/codex/CodexSessionLocator.test.js +194 -0
- package/dist/__tests__/providers/codex/CodexSessionLocator.test.js.map +1 -0
- package/dist/__tests__/providers/codex/CodexSessionMapping.test.js +86 -0
- package/dist/__tests__/providers/codex/CodexSessionMapping.test.js.map +1 -0
- package/dist/__tests__/providers/codex/CodexSessionParser.test.js +254 -0
- package/dist/__tests__/providers/codex/CodexSessionParser.test.js.map +1 -0
- package/dist/__tests__/providers/pi/PiAdapter.test.js +337 -0
- package/dist/__tests__/providers/pi/PiAdapter.test.js.map +1 -0
- package/dist/__tests__/providers/pi/PiSessionParser.test.js +246 -0
- package/dist/__tests__/providers/pi/PiSessionParser.test.js.map +1 -0
- package/dist/__tests__/providers/pi/PiSessionTracker.test.js +108 -0
- package/dist/__tests__/providers/pi/PiSessionTracker.test.js.map +1 -0
- package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js +33 -33
- package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js.map +1 -1
- package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js +16 -16
- package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js.map +1 -1
- package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js +52 -52
- package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js.map +1 -1
- package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js +44 -44
- package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js.map +1 -1
- package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js +126 -126
- package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js.map +1 -1
- package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js +14 -14
- package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js.map +1 -1
- package/dist/__tests__/readiness/AgentReadiness.test.js +134 -134
- package/dist/__tests__/readiness/AgentReadiness.test.js.map +1 -1
- package/dist/__tests__/runtime/AgentRuntime.test.js +13 -0
- package/dist/__tests__/runtime/AgentRuntime.test.js.map +1 -0
- package/dist/__tests__/runtime/HerdrAgentRuntime.test.js +452 -0
- package/dist/__tests__/runtime/HerdrAgentRuntime.test.js.map +1 -0
- package/dist/__tests__/runtime/ManagedAgentRuntime.test.js +588 -0
- package/dist/__tests__/runtime/ManagedAgentRuntime.test.js.map +1 -0
- package/dist/__tests__/terminal/TerminalFocusManager.test.js +100 -100
- package/dist/__tests__/terminal/TerminalFocusManager.test.js.map +1 -1
- package/dist/__tests__/terminal/TmuxManager.test.js +101 -101
- package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -1
- package/dist/__tests__/terminal/TtyWriter.test.js +185 -185
- package/dist/__tests__/terminal/TtyWriter.test.js.map +1 -1
- package/dist/__tests__/utils/AgentRegistry.test.js +335 -217
- package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
- package/dist/__tests__/utils/ClaudeSessionParser.test.js +103 -101
- package/dist/__tests__/utils/ClaudeSessionParser.test.js.map +1 -1
- package/dist/__tests__/utils/agent-requests.test.js +43 -43
- package/dist/__tests__/utils/agent-requests.test.js.map +1 -1
- package/dist/__tests__/utils/agents.test.js +17 -17
- package/dist/__tests__/utils/agents.test.js.map +1 -1
- package/dist/__tests__/utils/matching.test.js +92 -92
- package/dist/__tests__/utils/matching.test.js.map +1 -1
- package/dist/__tests__/utils/process.test.js +108 -108
- package/dist/__tests__/utils/process.test.js.map +1 -1
- package/dist/__tests__/utils/session.test.js +53 -53
- package/dist/__tests__/utils/session.test.js.map +1 -1
- package/dist/adapters/AgentAdapter.d.ts +2 -2
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/AgentAdapter.js +1 -1
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/adapters/CopilotAdapter.d.ts +2 -2
- package/dist/adapters/CopilotAdapter.d.ts.map +1 -1
- package/dist/adapters/CopilotAdapter.js +51 -51
- package/dist/adapters/CopilotAdapter.js.map +1 -1
- package/dist/adapters/GeminiCliAdapter.d.ts +2 -2
- package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -1
- package/dist/adapters/GeminiCliAdapter.js +86 -86
- package/dist/adapters/GeminiCliAdapter.js.map +1 -1
- package/dist/adapters/GrokCliAdapter.d.ts +1 -1
- package/dist/adapters/GrokCliAdapter.d.ts.map +1 -1
- package/dist/adapters/GrokCliAdapter.js +80 -80
- package/dist/adapters/GrokCliAdapter.js.map +1 -1
- package/dist/adapters/OpenCodeAdapter.d.ts +1 -1
- package/dist/adapters/OpenCodeAdapter.d.ts.map +1 -1
- package/dist/adapters/OpenCodeAdapter.js +37 -36
- package/dist/adapters/OpenCodeAdapter.js.map +1 -1
- package/dist/adapters/index.d.ts +9 -9
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +8 -8
- package/dist/adapters/index.js.map +1 -1
- package/dist/capacity/codex.d.ts +3 -3
- package/dist/capacity/codex.d.ts.map +1 -1
- package/dist/capacity/codex.js +57 -57
- package/dist/capacity/codex.js.map +1 -1
- package/dist/capacity/index.d.ts +3 -3
- package/dist/capacity/index.d.ts.map +1 -1
- package/dist/capacity/index.js +8 -8
- package/dist/capacity/index.js.map +1 -1
- package/dist/capacity/types.d.ts +1 -1
- package/dist/capacity/types.js.map +1 -1
- package/dist/database/connection.d.ts +1 -1
- package/dist/database/connection.d.ts.map +1 -1
- package/dist/database/connection.js +15 -15
- package/dist/database/connection.js.map +1 -1
- package/dist/database/index.d.ts +3 -3
- package/dist/database/index.d.ts.map +1 -1
- package/dist/database/index.js +2 -2
- package/dist/database/index.js.map +1 -1
- package/dist/database/migrations/005_agent_runtime.sql +2 -0
- package/dist/database/schema.d.ts +1 -1
- package/dist/database/schema.d.ts.map +1 -1
- package/dist/database/schema.js +7 -7
- package/dist/database/schema.js.map +1 -1
- package/dist/durable/DurableAgent.d.ts +9 -9
- package/dist/durable/DurableAgent.d.ts.map +1 -1
- package/dist/durable/DurableAgent.js +16 -16
- package/dist/durable/DurableAgent.js.map +1 -1
- package/dist/durable/DurableAgentRepository.d.ts +4 -4
- package/dist/durable/DurableAgentRepository.d.ts.map +1 -1
- package/dist/durable/DurableAgentRepository.js +49 -48
- package/dist/durable/DurableAgentRepository.js.map +1 -1
- package/dist/durable/process.d.ts +1 -1
- package/dist/durable/process.d.ts.map +1 -1
- package/dist/durable/process.js +14 -14
- package/dist/durable/process.js.map +1 -1
- package/dist/durable/run.d.ts +2 -2
- package/dist/durable/run.d.ts.map +1 -1
- package/dist/durable/run.js +6 -6
- package/dist/durable/run.js.map +1 -1
- package/dist/durable/utils.d.ts +1 -1
- package/dist/durable/utils.d.ts.map +1 -1
- package/dist/durable/utils.js +4 -4
- package/dist/durable/utils.js.map +1 -1
- package/dist/index.d.ts +46 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -26
- package/dist/index.js.map +1 -1
- package/dist/providers/claude/ClaudeAgentMapper.d.ts +4 -4
- package/dist/providers/claude/ClaudeAgentMapper.d.ts.map +1 -1
- package/dist/providers/claude/ClaudeAgentMapper.js +10 -10
- package/dist/providers/claude/ClaudeAgentMapper.js.map +1 -1
- package/dist/providers/claude/ClaudeCodeAdapter.d.ts +1 -1
- package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -1
- package/dist/providers/claude/ClaudeCodeAdapter.js +14 -14
- package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -1
- package/dist/providers/claude/ClaudeSessionLocator.d.ts +4 -4
- package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -1
- package/dist/providers/claude/ClaudeSessionLocator.js +79 -79
- package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -1
- package/dist/providers/claude/ClaudeSessionParser.d.ts +2 -2
- package/dist/providers/claude/ClaudeSessionParser.d.ts.map +1 -1
- package/dist/providers/claude/ClaudeSessionParser.js +99 -99
- package/dist/providers/claude/ClaudeSessionParser.js.map +1 -1
- package/dist/providers/claude/durable/ClaudeCliProbe.d.ts.map +1 -1
- package/dist/providers/claude/durable/ClaudeCliProbe.js +16 -16
- package/dist/providers/claude/durable/ClaudeCliProbe.js.map +1 -1
- package/dist/providers/claude/durable/ClaudePrintAgentService.d.ts +5 -5
- package/dist/providers/claude/durable/ClaudePrintAgentService.d.ts.map +1 -1
- package/dist/providers/claude/durable/ClaudePrintAgentService.js +10 -10
- package/dist/providers/claude/durable/ClaudePrintAgentService.js.map +1 -1
- package/dist/providers/claude/durable/ClaudePrintRunner.d.ts +4 -4
- package/dist/providers/claude/durable/ClaudePrintRunner.d.ts.map +1 -1
- package/dist/providers/claude/durable/ClaudePrintRunner.js +18 -18
- package/dist/providers/claude/durable/ClaudePrintRunner.js.map +1 -1
- package/dist/providers/claude/durable/ClaudeStreamParser.d.ts +2 -2
- package/dist/providers/claude/durable/ClaudeStreamParser.d.ts.map +1 -1
- package/dist/providers/claude/durable/ClaudeStreamParser.js +17 -17
- package/dist/providers/claude/durable/ClaudeStreamParser.js.map +1 -1
- package/dist/providers/codex/CodexAdapter.d.ts +27 -0
- package/dist/providers/codex/CodexAdapter.d.ts.map +1 -0
- package/dist/providers/codex/CodexAdapter.js +162 -0
- package/dist/providers/codex/CodexAdapter.js.map +1 -0
- package/dist/providers/codex/CodexAgentMapper.d.ts +9 -0
- package/dist/providers/codex/CodexAgentMapper.d.ts.map +1 -0
- package/dist/providers/codex/CodexAgentMapper.js +35 -0
- package/dist/providers/codex/CodexAgentMapper.js.map +1 -0
- package/dist/providers/codex/CodexSessionLocator.d.ts +44 -0
- package/dist/providers/codex/CodexSessionLocator.d.ts.map +1 -0
- package/dist/providers/codex/CodexSessionLocator.js +207 -0
- package/dist/providers/codex/CodexSessionLocator.js.map +1 -0
- package/dist/providers/codex/CodexSessionMapping.d.ts +23 -0
- package/dist/providers/codex/CodexSessionMapping.d.ts.map +1 -0
- package/dist/providers/codex/CodexSessionMapping.js +68 -0
- package/dist/providers/codex/CodexSessionMapping.js.map +1 -0
- package/dist/providers/codex/CodexSessionParser.d.ts +58 -0
- package/dist/providers/codex/CodexSessionParser.d.ts.map +1 -0
- package/dist/providers/codex/CodexSessionParser.js +262 -0
- package/dist/providers/codex/CodexSessionParser.js.map +1 -0
- package/dist/providers/codex/durable/CodexCliProbe.d.ts.map +1 -1
- package/dist/providers/codex/durable/CodexCliProbe.js +22 -22
- package/dist/providers/codex/durable/CodexCliProbe.js.map +1 -1
- package/dist/providers/codex/durable/CodexPrintAgentService.d.ts +6 -6
- package/dist/providers/codex/durable/CodexPrintAgentService.d.ts.map +1 -1
- package/dist/providers/codex/durable/CodexPrintAgentService.js +10 -10
- package/dist/providers/codex/durable/CodexPrintAgentService.js.map +1 -1
- package/dist/providers/codex/durable/CodexPrintRunner.d.ts +4 -4
- package/dist/providers/codex/durable/CodexPrintRunner.d.ts.map +1 -1
- package/dist/providers/codex/durable/CodexPrintRunner.js +20 -20
- package/dist/providers/codex/durable/CodexPrintRunner.js.map +1 -1
- package/dist/providers/codex/durable/CodexStreamParser.d.ts +2 -2
- package/dist/providers/codex/durable/CodexStreamParser.d.ts.map +1 -1
- package/dist/providers/codex/durable/CodexStreamParser.js +21 -21
- package/dist/providers/codex/durable/CodexStreamParser.js.map +1 -1
- package/dist/providers/pi/PiAdapter.d.ts +28 -0
- package/dist/providers/pi/PiAdapter.d.ts.map +1 -0
- package/dist/providers/pi/PiAdapter.js +153 -0
- package/dist/providers/pi/PiAdapter.js.map +1 -0
- package/dist/providers/pi/PiAgentMapper.d.ts +9 -0
- package/dist/providers/pi/PiAgentMapper.d.ts.map +1 -0
- package/dist/providers/pi/PiAgentMapper.js +37 -0
- package/dist/providers/pi/PiAgentMapper.js.map +1 -0
- package/dist/providers/pi/PiSessionLocator.d.ts +23 -0
- package/dist/providers/pi/PiSessionLocator.d.ts.map +1 -0
- package/dist/providers/pi/PiSessionLocator.js +79 -0
- package/dist/providers/pi/PiSessionLocator.js.map +1 -0
- package/dist/providers/pi/PiSessionParser.d.ts +36 -0
- package/dist/providers/pi/PiSessionParser.d.ts.map +1 -0
- package/dist/providers/pi/PiSessionParser.js +191 -0
- package/dist/providers/pi/PiSessionParser.js.map +1 -0
- package/dist/providers/pi/PiSessionTracker.d.ts +23 -0
- package/dist/providers/pi/PiSessionTracker.d.ts.map +1 -0
- package/dist/providers/pi/PiSessionTracker.js +69 -0
- package/dist/providers/pi/PiSessionTracker.js.map +1 -0
- package/dist/providers/pi/durable/PiCliProbe.d.ts.map +1 -1
- package/dist/providers/pi/durable/PiCliProbe.js +15 -15
- package/dist/providers/pi/durable/PiCliProbe.js.map +1 -1
- package/dist/providers/pi/durable/PiPrintAgentService.d.ts +6 -6
- package/dist/providers/pi/durable/PiPrintAgentService.d.ts.map +1 -1
- package/dist/providers/pi/durable/PiPrintAgentService.js +10 -10
- package/dist/providers/pi/durable/PiPrintAgentService.js.map +1 -1
- package/dist/providers/pi/durable/PiPrintProtocol.d.ts.map +1 -1
- package/dist/providers/pi/durable/PiPrintProtocol.js +18 -18
- package/dist/providers/pi/durable/PiPrintProtocol.js.map +1 -1
- package/dist/providers/pi/durable/PiPrintRunner.d.ts +4 -4
- package/dist/providers/pi/durable/PiPrintRunner.d.ts.map +1 -1
- package/dist/providers/pi/durable/PiPrintRunner.js +15 -15
- package/dist/providers/pi/durable/PiPrintRunner.js.map +1 -1
- package/dist/providers/pi/durable/PiStreamParser.d.ts +1 -1
- package/dist/providers/pi/durable/PiStreamParser.d.ts.map +1 -1
- package/dist/providers/pi/durable/PiStreamParser.js +16 -16
- package/dist/providers/pi/durable/PiStreamParser.js.map +1 -1
- package/dist/readiness/AgentReadiness.d.ts +6 -6
- package/dist/readiness/AgentReadiness.d.ts.map +1 -1
- package/dist/readiness/AgentReadiness.js +130 -130
- package/dist/readiness/AgentReadiness.js.map +1 -1
- package/dist/runtime/ManagedAgentRuntime.d.ts +69 -0
- package/dist/runtime/ManagedAgentRuntime.d.ts.map +1 -0
- package/dist/runtime/ManagedAgentRuntime.js +156 -0
- package/dist/runtime/ManagedAgentRuntime.js.map +1 -0
- package/dist/runtime/RuntimeFactory.d.ts +4 -0
- package/dist/runtime/RuntimeFactory.d.ts.map +1 -0
- package/dist/runtime/RuntimeFactory.js +11 -0
- package/dist/runtime/RuntimeFactory.js.map +1 -0
- package/dist/runtime/errors.d.ts +27 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +42 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/herdr/HerdrAgentRuntime.d.ts +31 -0
- package/dist/runtime/herdr/HerdrAgentRuntime.d.ts.map +1 -0
- package/dist/runtime/herdr/HerdrAgentRuntime.js +184 -0
- package/dist/runtime/herdr/HerdrAgentRuntime.js.map +1 -0
- package/dist/runtime/herdr/HerdrCliClient.d.ts +13 -0
- package/dist/runtime/herdr/HerdrCliClient.d.ts.map +1 -0
- package/dist/runtime/herdr/HerdrCliClient.js +60 -0
- package/dist/runtime/herdr/HerdrCliClient.js.map +1 -0
- package/dist/runtime/herdr/HerdrErrors.d.ts +7 -0
- package/dist/runtime/herdr/HerdrErrors.d.ts.map +1 -0
- package/dist/runtime/herdr/HerdrErrors.js +10 -0
- package/dist/runtime/herdr/HerdrErrors.js.map +1 -0
- package/dist/runtime/herdr/HerdrResponseParsers.d.ts +14 -0
- package/dist/runtime/herdr/HerdrResponseParsers.d.ts.map +1 -0
- package/dist/runtime/herdr/HerdrResponseParsers.js +96 -0
- package/dist/runtime/herdr/HerdrResponseParsers.js.map +1 -0
- package/dist/runtime/herdr/HerdrRuntimeRef.d.ts +9 -0
- package/dist/runtime/herdr/HerdrRuntimeRef.d.ts.map +1 -0
- package/dist/runtime/herdr/HerdrRuntimeRef.js +33 -0
- package/dist/runtime/herdr/HerdrRuntimeRef.js.map +1 -0
- package/dist/runtime/tmux/PidPolling.d.ts +5 -0
- package/dist/runtime/tmux/PidPolling.d.ts.map +1 -0
- package/dist/runtime/tmux/PidPolling.js +24 -0
- package/dist/runtime/tmux/PidPolling.js.map +1 -0
- package/dist/runtime/tmux/TmuxAgentRuntime.d.ts +17 -0
- package/dist/runtime/tmux/TmuxAgentRuntime.d.ts.map +1 -0
- package/dist/runtime/tmux/TmuxAgentRuntime.js +80 -0
- package/dist/runtime/tmux/TmuxAgentRuntime.js.map +1 -0
- package/dist/runtime/tmux/TmuxRuntimeRef.d.ts +5 -0
- package/dist/runtime/tmux/TmuxRuntimeRef.d.ts.map +1 -0
- package/dist/runtime/tmux/TmuxRuntimeRef.js +9 -0
- package/dist/runtime/tmux/TmuxRuntimeRef.js.map +1 -0
- package/dist/runtime/types.d.ts +54 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +3 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
- package/dist/terminal/TerminalFocusManager.js +50 -50
- package/dist/terminal/TerminalFocusManager.js.map +1 -1
- package/dist/terminal/TmuxManager.d.ts.map +1 -1
- package/dist/terminal/TmuxManager.js +47 -47
- package/dist/terminal/TmuxManager.js.map +1 -1
- package/dist/terminal/TtyWriter.d.ts +1 -1
- package/dist/terminal/TtyWriter.d.ts.map +1 -1
- package/dist/terminal/TtyWriter.js +89 -89
- package/dist/terminal/TtyWriter.js.map +1 -1
- package/dist/terminal/index.d.ts +4 -4
- package/dist/terminal/index.js +3 -3
- package/dist/terminal/index.js.map +1 -1
- package/dist/utils/AgentRegistry.d.ts +10 -2
- package/dist/utils/AgentRegistry.d.ts.map +1 -1
- package/dist/utils/AgentRegistry.js +57 -24
- package/dist/utils/AgentRegistry.js.map +1 -1
- package/dist/utils/agent-requests.d.ts.map +1 -1
- package/dist/utils/agent-requests.js +5 -5
- package/dist/utils/agent-requests.js.map +1 -1
- package/dist/utils/agents.d.ts +2 -2
- package/dist/utils/agents.d.ts.map +1 -1
- package/dist/utils/agents.js +15 -15
- package/dist/utils/agents.js.map +1 -1
- package/dist/utils/applescript.js +1 -1
- package/dist/utils/applescript.js.map +1 -1
- package/dist/utils/index.d.ts +6 -6
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -4
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/matching.d.ts +2 -2
- package/dist/utils/matching.d.ts.map +1 -1
- package/dist/utils/matching.js +4 -4
- package/dist/utils/matching.js.map +1 -1
- package/dist/utils/process.d.ts +1 -1
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +74 -74
- package/dist/utils/process.js.map +1 -1
- package/dist/utils/session.d.ts +1 -1
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +7 -7
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/sortAgents.d.ts +2 -2
- package/dist/utils/sortAgents.d.ts.map +1 -1
- package/dist/utils/sortAgents.js +5 -5
- package/dist/utils/sortAgents.js.map +1 -1
- package/package.json +18 -19
- package/src/AgentManager.ts +283 -281
- package/src/__tests__/AgentManager.test.ts +917 -907
- package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +1657 -1368
- package/src/__tests__/adapters/CodexAdapter.test.ts +365 -1698
- package/src/__tests__/adapters/CopilotAdapter.test.ts +576 -481
- package/src/__tests__/adapters/GeminiCliAdapter.test.ts +1189 -1157
- package/src/__tests__/adapters/GrokCliAdapter.test.ts +325 -272
- package/src/__tests__/adapters/OpenCodeAdapter.test.ts +510 -455
- package/src/__tests__/capacity/codex.test.ts +178 -91
- package/src/__tests__/capacity/index.test.ts +27 -18
- package/src/__tests__/durable/CodexCliProbe.test.ts +75 -53
- package/src/__tests__/durable/CodexDurableAgentRepository.test.ts +51 -40
- package/src/__tests__/durable/CodexPrintAgent.integration.test.ts +89 -64
- package/src/__tests__/durable/CodexPrintAgentService.test.ts +196 -118
- package/src/__tests__/durable/CodexPrintRunner.test.ts +282 -162
- package/src/__tests__/fixtures/fake-claude.cjs +20 -13
- package/src/__tests__/fixtures/fake-codex.cjs +41 -30
- package/src/__tests__/print/ClaudeCliProbe.test.ts +27 -21
- package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +48 -40
- package/src/__tests__/print/ClaudePrintAgentService.test.ts +118 -67
- package/src/__tests__/print/ClaudePrintRunner.test.ts +136 -83
- package/src/__tests__/print/DurableAgent.test.ts +19 -16
- package/src/__tests__/print/DurableAgentRepository.sqlite.test.ts +109 -86
- package/src/__tests__/print/DurableAgentRepository.test.ts +170 -137
- package/src/__tests__/providers/claude/ClaudeAgentMapper.test.ts +65 -65
- package/src/__tests__/providers/claude/ClaudeSessionLocator.test.ts +400 -377
- package/src/__tests__/providers/codex/CodexSessionLocator.test.ts +182 -0
- package/src/__tests__/providers/codex/CodexSessionMapping.test.ts +63 -0
- package/src/__tests__/providers/codex/CodexSessionParser.test.ts +192 -0
- package/src/__tests__/providers/pi/PiAdapter.test.ts +310 -0
- package/src/__tests__/providers/pi/PiSessionParser.test.ts +190 -0
- package/src/__tests__/providers/pi/PiSessionTracker.test.ts +79 -0
- package/src/__tests__/providers/pi/durable/PiCliProbe.test.ts +48 -32
- package/src/__tests__/providers/pi/durable/PiDurableAgentRepository.test.ts +24 -23
- package/src/__tests__/providers/pi/durable/PiPrintAgentService.test.ts +131 -59
- package/src/__tests__/providers/pi/durable/PiPrintProtocol.test.ts +66 -28
- package/src/__tests__/providers/pi/durable/PiPrintRunner.test.ts +236 -90
- package/src/__tests__/providers/pi/durable/PiStreamParser.test.ts +20 -18
- package/src/__tests__/readiness/AgentReadiness.test.ts +217 -169
- package/src/__tests__/runtime/AgentRuntime.test.ts +8 -0
- package/src/__tests__/runtime/HerdrAgentRuntime.test.ts +358 -0
- package/src/__tests__/runtime/ManagedAgentRuntime.test.ts +555 -0
- package/src/__tests__/terminal/TerminalFocusManager.test.ts +254 -253
- package/src/__tests__/terminal/TmuxManager.test.ts +157 -157
- package/src/__tests__/terminal/TtyWriter.test.ts +423 -419
- package/src/__tests__/utils/AgentRegistry.test.ts +506 -402
- package/src/__tests__/utils/ClaudeSessionParser.test.ts +276 -242
- package/src/__tests__/utils/agent-requests.test.ts +76 -56
- package/src/__tests__/utils/agents.test.ts +20 -20
- package/src/__tests__/utils/matching.test.ts +206 -171
- package/src/__tests__/utils/process.test.ts +276 -262
- package/src/__tests__/utils/session.test.ts +133 -139
- package/src/adapters/AgentAdapter.ts +134 -126
- package/src/adapters/CopilotAdapter.ts +405 -395
- package/src/adapters/GeminiCliAdapter.ts +598 -600
- package/src/adapters/GrokCliAdapter.ts +353 -342
- package/src/adapters/OpenCodeAdapter.ts +285 -259
- package/src/adapters/index.ts +15 -9
- package/src/capacity/codex.ts +137 -84
- package/src/capacity/index.ts +18 -13
- package/src/capacity/types.ts +1 -1
- package/src/database/connection.ts +73 -68
- package/src/database/index.ts +6 -6
- package/src/database/migrations/005_agent_runtime.sql +2 -0
- package/src/database/schema.ts +40 -38
- package/src/durable/DurableAgent.ts +85 -85
- package/src/durable/DurableAgentRepository.ts +409 -260
- package/src/durable/process.ts +22 -21
- package/src/durable/run.ts +61 -51
- package/src/durable/utils.ts +46 -41
- package/src/index.ts +144 -103
- package/src/providers/claude/ClaudeAgentMapper.ts +51 -50
- package/src/providers/claude/ClaudeCodeAdapter.ts +144 -136
- package/src/providers/claude/ClaudeSessionLocator.ts +291 -293
- package/src/providers/claude/ClaudeSessionParser.ts +322 -338
- package/src/providers/claude/durable/ClaudeCliProbe.ts +42 -37
- package/src/providers/claude/durable/ClaudePrintAgentService.ts +63 -50
- package/src/providers/claude/durable/ClaudePrintRunner.ts +79 -69
- package/src/providers/claude/durable/ClaudeStreamParser.ts +91 -62
- package/src/providers/codex/CodexAdapter.ts +205 -0
- package/src/providers/codex/CodexAgentMapper.ts +35 -0
- package/src/providers/codex/CodexSessionLocator.ts +279 -0
- package/src/providers/codex/CodexSessionMapping.ts +89 -0
- package/src/providers/codex/CodexSessionParser.ts +385 -0
- package/src/providers/codex/durable/CodexCliProbe.ts +50 -42
- package/src/providers/codex/durable/CodexPrintAgentService.ts +63 -49
- package/src/providers/codex/durable/CodexPrintRunner.ts +79 -69
- package/src/providers/codex/durable/CodexStreamParser.ts +97 -84
- package/src/providers/pi/PiAdapter.ts +183 -0
- package/src/providers/pi/PiAgentMapper.ts +36 -0
- package/src/providers/pi/PiSessionLocator.ts +95 -0
- package/src/providers/pi/PiSessionParser.ts +284 -0
- package/src/providers/pi/PiSessionTracker.ts +87 -0
- package/src/providers/pi/durable/PiCliProbe.ts +42 -34
- package/src/providers/pi/durable/PiPrintAgentService.ts +61 -53
- package/src/providers/pi/durable/PiPrintProtocol.ts +31 -29
- package/src/providers/pi/durable/PiPrintRunner.ts +75 -68
- package/src/providers/pi/durable/PiStreamParser.ts +82 -72
- package/src/readiness/AgentReadiness.ts +585 -513
- package/src/runtime/ManagedAgentRuntime.ts +266 -0
- package/src/runtime/RuntimeFactory.ts +13 -0
- package/src/runtime/errors.ts +50 -0
- package/src/runtime/herdr/HerdrAgentRuntime.ts +190 -0
- package/src/runtime/herdr/HerdrCliClient.ts +78 -0
- package/src/runtime/herdr/HerdrErrors.ts +9 -0
- package/src/runtime/herdr/HerdrResponseParsers.ts +76 -0
- package/src/runtime/herdr/HerdrRuntimeRef.ts +41 -0
- package/src/runtime/tmux/PidPolling.ts +34 -0
- package/src/runtime/tmux/TmuxAgentRuntime.ts +113 -0
- package/src/runtime/tmux/TmuxRuntimeRef.ts +9 -0
- package/src/runtime/types.ts +56 -0
- package/src/terminal/TerminalFocusManager.ts +215 -212
- package/src/terminal/TmuxManager.ts +99 -92
- package/src/terminal/TtyWriter.ts +203 -194
- package/src/terminal/index.ts +4 -4
- package/src/utils/AgentRegistry.ts +299 -238
- package/src/utils/agent-requests.ts +16 -16
- package/src/utils/agents.ts +41 -38
- package/src/utils/applescript.ts +4 -4
- package/src/utils/index.ts +12 -6
- package/src/utils/matching.ts +43 -43
- package/src/utils/process.ts +261 -258
- package/src/utils/session.ts +66 -66
- package/src/utils/sortAgents.ts +35 -33
- package/tsconfig.json +2 -8
- package/vitest.config.ts +16 -16
- package/.eslintrc.json +0 -31
- package/dist/__tests__/adapters/PiAdapter.test.js +0 -618
- package/dist/__tests__/adapters/PiAdapter.test.js.map +0 -1
- package/dist/__tests__/database/DurableAgentsDatabase.test.js +0 -128
- package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +0 -1
- package/dist/adapters/CodexAdapter.d.ts +0 -109
- package/dist/adapters/CodexAdapter.d.ts.map +0 -1
- package/dist/adapters/CodexAdapter.js +0 -710
- package/dist/adapters/CodexAdapter.js.map +0 -1
- package/dist/adapters/PiAdapter.d.ts +0 -63
- package/dist/adapters/PiAdapter.d.ts.map +0 -1
- package/dist/adapters/PiAdapter.js +0 -453
- package/dist/adapters/PiAdapter.js.map +0 -1
- package/src/__tests__/adapters/PiAdapter.test.ts +0 -458
- package/src/__tests__/database/DurableAgentsDatabase.test.ts +0 -102
- package/src/adapters/CodexAdapter.ts +0 -923
- package/src/adapters/PiAdapter.ts +0 -598
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for AgentManager
|
|
3
|
-
*/ import fs from
|
|
4
|
-
import os from
|
|
5
|
-
import path from
|
|
6
|
-
import { AgentManager } from
|
|
7
|
-
import { AgentStatus } from
|
|
8
|
-
import { AgentRegistry } from
|
|
3
|
+
*/ import fs from "fs";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { AgentManager } from "../AgentManager.js";
|
|
7
|
+
import { AgentStatus } from "../adapters/AgentAdapter.js";
|
|
8
|
+
import { AgentRegistry } from "../utils/AgentRegistry.js";
|
|
9
9
|
// Mock adapter for testing
|
|
10
10
|
let MockAdapter = class MockAdapter {
|
|
11
11
|
type;
|
|
@@ -56,23 +56,23 @@ let MockAdapter = class MockAdapter {
|
|
|
56
56
|
// Helper to create mock agent
|
|
57
57
|
function createMockAgent(overrides = {}) {
|
|
58
58
|
return {
|
|
59
|
-
name:
|
|
60
|
-
type:
|
|
59
|
+
name: "test-agent",
|
|
60
|
+
type: "claude",
|
|
61
61
|
status: AgentStatus.RUNNING,
|
|
62
|
-
summary:
|
|
62
|
+
summary: "Test summary",
|
|
63
63
|
pid: 12345,
|
|
64
|
-
projectPath:
|
|
65
|
-
sessionId:
|
|
64
|
+
projectPath: "/test/path",
|
|
65
|
+
sessionId: "test-session-id",
|
|
66
66
|
lastActive: new Date(),
|
|
67
67
|
...overrides
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
describe(
|
|
70
|
+
describe("AgentManager", ()=>{
|
|
71
71
|
let manager;
|
|
72
72
|
let tmpDir;
|
|
73
73
|
beforeEach(()=>{
|
|
74
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(),
|
|
75
|
-
manager = new AgentManager(new AgentRegistry(path.join(tmpDir,
|
|
74
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-manager-"));
|
|
75
|
+
manager = new AgentManager(new AgentRegistry(path.join(tmpDir, "agents.json")));
|
|
76
76
|
});
|
|
77
77
|
afterEach(()=>{
|
|
78
78
|
fs.rmSync(tmpDir, {
|
|
@@ -80,51 +80,51 @@ describe('AgentManager', ()=>{
|
|
|
80
80
|
force: true
|
|
81
81
|
});
|
|
82
82
|
});
|
|
83
|
-
describe(
|
|
84
|
-
it(
|
|
85
|
-
const adapter = new MockAdapter(
|
|
83
|
+
describe("registerAdapter", ()=>{
|
|
84
|
+
it("should register a new adapter", ()=>{
|
|
85
|
+
const adapter = new MockAdapter("claude");
|
|
86
86
|
manager.registerAdapter(adapter);
|
|
87
|
-
expect(manager.hasAdapter(
|
|
87
|
+
expect(manager.hasAdapter("claude")).toBe(true);
|
|
88
88
|
expect(manager.getAdapterCount()).toBe(1);
|
|
89
89
|
});
|
|
90
|
-
it(
|
|
91
|
-
const adapter1 = new MockAdapter(
|
|
92
|
-
const adapter2 = new MockAdapter(
|
|
90
|
+
it("should throw error when registering duplicate adapter type", ()=>{
|
|
91
|
+
const adapter1 = new MockAdapter("claude");
|
|
92
|
+
const adapter2 = new MockAdapter("claude");
|
|
93
93
|
manager.registerAdapter(adapter1);
|
|
94
94
|
expect(()=>manager.registerAdapter(adapter2)).toThrow('Adapter for type "claude" is already registered');
|
|
95
95
|
});
|
|
96
|
-
it(
|
|
97
|
-
const adapter1 = new MockAdapter(
|
|
98
|
-
const adapter2 = new MockAdapter(
|
|
96
|
+
it("should allow registering multiple different adapter types", ()=>{
|
|
97
|
+
const adapter1 = new MockAdapter("claude");
|
|
98
|
+
const adapter2 = new MockAdapter("gemini_cli");
|
|
99
99
|
manager.registerAdapter(adapter1);
|
|
100
100
|
manager.registerAdapter(adapter2);
|
|
101
101
|
expect(manager.getAdapterCount()).toBe(2);
|
|
102
|
-
expect(manager.hasAdapter(
|
|
103
|
-
expect(manager.hasAdapter(
|
|
102
|
+
expect(manager.hasAdapter("claude")).toBe(true);
|
|
103
|
+
expect(manager.hasAdapter("gemini_cli")).toBe(true);
|
|
104
104
|
});
|
|
105
105
|
});
|
|
106
|
-
describe(
|
|
107
|
-
it(
|
|
108
|
-
const adapter = new MockAdapter(
|
|
106
|
+
describe("unregisterAdapter", ()=>{
|
|
107
|
+
it("should unregister an existing adapter", ()=>{
|
|
108
|
+
const adapter = new MockAdapter("claude");
|
|
109
109
|
manager.registerAdapter(adapter);
|
|
110
|
-
const removed = manager.unregisterAdapter(
|
|
110
|
+
const removed = manager.unregisterAdapter("claude");
|
|
111
111
|
expect(removed).toBe(true);
|
|
112
|
-
expect(manager.hasAdapter(
|
|
112
|
+
expect(manager.hasAdapter("claude")).toBe(false);
|
|
113
113
|
expect(manager.getAdapterCount()).toBe(0);
|
|
114
114
|
});
|
|
115
|
-
it(
|
|
116
|
-
const removed = manager.unregisterAdapter(
|
|
115
|
+
it("should return false when unregistering non-existent adapter", ()=>{
|
|
116
|
+
const removed = manager.unregisterAdapter("NonExistent");
|
|
117
117
|
expect(removed).toBe(false);
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
|
-
describe(
|
|
121
|
-
it(
|
|
120
|
+
describe("getAdapters", ()=>{
|
|
121
|
+
it("should return empty array when no adapters registered", ()=>{
|
|
122
122
|
const adapters = manager.getAdapters();
|
|
123
123
|
expect(adapters).toEqual([]);
|
|
124
124
|
});
|
|
125
|
-
it(
|
|
126
|
-
const adapter1 = new MockAdapter(
|
|
127
|
-
const adapter2 = new MockAdapter(
|
|
125
|
+
it("should return all registered adapters", ()=>{
|
|
126
|
+
const adapter1 = new MockAdapter("claude");
|
|
127
|
+
const adapter2 = new MockAdapter("gemini_cli");
|
|
128
128
|
manager.registerAdapter(adapter1);
|
|
129
129
|
manager.registerAdapter(adapter2);
|
|
130
130
|
const adapters = manager.getAdapters();
|
|
@@ -133,20 +133,20 @@ describe('AgentManager', ()=>{
|
|
|
133
133
|
expect(adapters).toContain(adapter2);
|
|
134
134
|
});
|
|
135
135
|
});
|
|
136
|
-
describe(
|
|
137
|
-
it(
|
|
136
|
+
describe("listAgents", ()=>{
|
|
137
|
+
it("shares one process capture while giving each adapter only its declared executables", async ()=>{
|
|
138
138
|
const processes = [
|
|
139
139
|
{
|
|
140
140
|
pid: 101,
|
|
141
|
-
command:
|
|
142
|
-
cwd:
|
|
143
|
-
tty:
|
|
141
|
+
command: "claude",
|
|
142
|
+
cwd: "/claude",
|
|
143
|
+
tty: "s001"
|
|
144
144
|
},
|
|
145
145
|
{
|
|
146
146
|
pid: 202,
|
|
147
|
-
command:
|
|
148
|
-
cwd:
|
|
149
|
-
tty:
|
|
147
|
+
command: "node /bin/pi",
|
|
148
|
+
cwd: "/pi",
|
|
149
|
+
tty: "s002"
|
|
150
150
|
}
|
|
151
151
|
];
|
|
152
152
|
const captureSnapshot = vi.fn(async ()=>processes);
|
|
@@ -158,22 +158,22 @@ describe('AgentManager', ()=>{
|
|
|
158
158
|
getConversation: ()=>[],
|
|
159
159
|
listSessions: async ()=>[]
|
|
160
160
|
});
|
|
161
|
-
const claude = createSnapshotAdapter(
|
|
162
|
-
|
|
161
|
+
const claude = createSnapshotAdapter("claude", [
|
|
162
|
+
"claude"
|
|
163
163
|
]);
|
|
164
|
-
const pi = createSnapshotAdapter(
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
const pi = createSnapshotAdapter("pi", [
|
|
165
|
+
"pi",
|
|
166
|
+
"node"
|
|
167
167
|
]);
|
|
168
|
-
const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir,
|
|
168
|
+
const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, "snapshot-agents.json")), captureSnapshot);
|
|
169
169
|
snapshotManager.registerAdapter(claude);
|
|
170
170
|
snapshotManager.registerAdapter(pi);
|
|
171
171
|
await snapshotManager.listAgents();
|
|
172
172
|
expect(captureSnapshot).toHaveBeenCalledTimes(1);
|
|
173
173
|
expect(captureSnapshot).toHaveBeenCalledWith([
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
"claude",
|
|
175
|
+
"pi",
|
|
176
|
+
"node"
|
|
177
177
|
]);
|
|
178
178
|
expect(claude.detectAgents).toHaveBeenCalledWith({
|
|
179
179
|
processes: [
|
|
@@ -186,31 +186,31 @@ describe('AgentManager', ()=>{
|
|
|
186
186
|
]
|
|
187
187
|
});
|
|
188
188
|
});
|
|
189
|
-
it(
|
|
189
|
+
it("does not expose foreign command arguments to broad Pi and Gemini matchers", async ()=>{
|
|
190
190
|
const processes = [
|
|
191
191
|
{
|
|
192
192
|
pid: 100,
|
|
193
|
-
command:
|
|
194
|
-
cwd:
|
|
195
|
-
tty:
|
|
193
|
+
command: "node /usr/local/lib/gemini.js",
|
|
194
|
+
cwd: "/g",
|
|
195
|
+
tty: "s001"
|
|
196
196
|
},
|
|
197
197
|
{
|
|
198
198
|
pid: 200,
|
|
199
|
-
command:
|
|
200
|
-
cwd:
|
|
201
|
-
tty:
|
|
199
|
+
command: "codex exec --cd /Users/x/repos/gemini",
|
|
200
|
+
cwd: "/c",
|
|
201
|
+
tty: "s002"
|
|
202
202
|
},
|
|
203
203
|
{
|
|
204
204
|
pid: 300,
|
|
205
|
-
command:
|
|
206
|
-
cwd:
|
|
207
|
-
tty:
|
|
205
|
+
command: "node /usr/local/lib/pi.js",
|
|
206
|
+
cwd: "/p",
|
|
207
|
+
tty: "s003"
|
|
208
208
|
},
|
|
209
209
|
{
|
|
210
210
|
pid: 400,
|
|
211
|
-
command:
|
|
212
|
-
cwd:
|
|
213
|
-
tty:
|
|
211
|
+
command: "claude --resume /Users/x/pi/session.jsonl",
|
|
212
|
+
cwd: "/a",
|
|
213
|
+
tty: "s004"
|
|
214
214
|
}
|
|
215
215
|
];
|
|
216
216
|
const captureSnapshot = vi.fn(async ()=>processes);
|
|
@@ -222,20 +222,20 @@ describe('AgentManager', ()=>{
|
|
|
222
222
|
getConversation: ()=>[],
|
|
223
223
|
listSessions: async ()=>[]
|
|
224
224
|
});
|
|
225
|
-
const gemini = createSnapshotAdapter(
|
|
226
|
-
|
|
225
|
+
const gemini = createSnapshotAdapter("gemini_cli", [
|
|
226
|
+
"node"
|
|
227
227
|
]);
|
|
228
|
-
const codex = createSnapshotAdapter(
|
|
229
|
-
|
|
228
|
+
const codex = createSnapshotAdapter("codex", [
|
|
229
|
+
"codex"
|
|
230
230
|
]);
|
|
231
|
-
const pi = createSnapshotAdapter(
|
|
232
|
-
|
|
233
|
-
|
|
231
|
+
const pi = createSnapshotAdapter("pi", [
|
|
232
|
+
"pi",
|
|
233
|
+
"node"
|
|
234
234
|
]);
|
|
235
|
-
const claude = createSnapshotAdapter(
|
|
236
|
-
|
|
235
|
+
const claude = createSnapshotAdapter("claude", [
|
|
236
|
+
"claude"
|
|
237
237
|
]);
|
|
238
|
-
const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir,
|
|
238
|
+
const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, "filtered-snapshot-agents.json")), captureSnapshot);
|
|
239
239
|
snapshotManager.registerAdapter(gemini);
|
|
240
240
|
snapshotManager.registerAdapter(codex);
|
|
241
241
|
snapshotManager.registerAdapter(pi);
|
|
@@ -243,10 +243,10 @@ describe('AgentManager', ()=>{
|
|
|
243
243
|
await snapshotManager.listAgents();
|
|
244
244
|
expect(captureSnapshot).toHaveBeenCalledTimes(1);
|
|
245
245
|
expect(captureSnapshot).toHaveBeenCalledWith([
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
246
|
+
"node",
|
|
247
|
+
"codex",
|
|
248
|
+
"pi",
|
|
249
|
+
"claude"
|
|
250
250
|
]);
|
|
251
251
|
expect(gemini.detectAgents).toHaveBeenCalledWith({
|
|
252
252
|
processes: [
|
|
@@ -271,107 +271,107 @@ describe('AgentManager', ()=>{
|
|
|
271
271
|
]
|
|
272
272
|
});
|
|
273
273
|
});
|
|
274
|
-
it(
|
|
274
|
+
it("does not pass a snapshot context to legacy adapters", async ()=>{
|
|
275
275
|
const captureSnapshot = vi.fn(async ()=>[]);
|
|
276
|
-
const legacy = new MockAdapter(
|
|
277
|
-
const detect = vi.spyOn(legacy,
|
|
278
|
-
const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir,
|
|
276
|
+
const legacy = new MockAdapter("claude");
|
|
277
|
+
const detect = vi.spyOn(legacy, "detectAgents");
|
|
278
|
+
const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, "legacy-agents.json")), captureSnapshot);
|
|
279
279
|
snapshotManager.registerAdapter(legacy);
|
|
280
280
|
await snapshotManager.listAgents();
|
|
281
281
|
expect(captureSnapshot).not.toHaveBeenCalled();
|
|
282
282
|
expect(detect).toHaveBeenCalledWith();
|
|
283
283
|
});
|
|
284
|
-
it(
|
|
284
|
+
it("should return empty array when no adapters registered", async ()=>{
|
|
285
285
|
const agents = await manager.listAgents();
|
|
286
286
|
expect(agents).toEqual([]);
|
|
287
287
|
});
|
|
288
|
-
it(
|
|
288
|
+
it("should return agents from single adapter", async ()=>{
|
|
289
289
|
const mockAgents = [
|
|
290
290
|
createMockAgent({
|
|
291
|
-
name:
|
|
291
|
+
name: "agent1"
|
|
292
292
|
}),
|
|
293
293
|
createMockAgent({
|
|
294
|
-
name:
|
|
294
|
+
name: "agent2"
|
|
295
295
|
})
|
|
296
296
|
];
|
|
297
|
-
const adapter = new MockAdapter(
|
|
297
|
+
const adapter = new MockAdapter("claude", mockAgents);
|
|
298
298
|
manager.registerAdapter(adapter);
|
|
299
299
|
const agents = await manager.listAgents();
|
|
300
300
|
expect(agents).toHaveLength(2);
|
|
301
|
-
expect(agents[0].name).toBe(
|
|
302
|
-
expect(agents[1].name).toBe(
|
|
301
|
+
expect(agents[0].name).toBe("agent1");
|
|
302
|
+
expect(agents[1].name).toBe("agent2");
|
|
303
303
|
});
|
|
304
|
-
it(
|
|
304
|
+
it("should aggregate agents from multiple adapters", async ()=>{
|
|
305
305
|
const claudeAgents = [
|
|
306
306
|
createMockAgent({
|
|
307
|
-
name:
|
|
308
|
-
type:
|
|
307
|
+
name: "claude-agent",
|
|
308
|
+
type: "claude"
|
|
309
309
|
})
|
|
310
310
|
];
|
|
311
311
|
const geminiAgents = [
|
|
312
312
|
createMockAgent({
|
|
313
|
-
name:
|
|
314
|
-
type:
|
|
313
|
+
name: "gemini-agent",
|
|
314
|
+
type: "gemini_cli"
|
|
315
315
|
})
|
|
316
316
|
];
|
|
317
|
-
manager.registerAdapter(new MockAdapter(
|
|
318
|
-
manager.registerAdapter(new MockAdapter(
|
|
317
|
+
manager.registerAdapter(new MockAdapter("claude", claudeAgents));
|
|
318
|
+
manager.registerAdapter(new MockAdapter("gemini_cli", geminiAgents));
|
|
319
319
|
const agents = await manager.listAgents();
|
|
320
320
|
expect(agents).toHaveLength(2);
|
|
321
|
-
expect(agents.find((a)=>a.name ===
|
|
322
|
-
expect(agents.find((a)=>a.name ===
|
|
321
|
+
expect(agents.find((a)=>a.name === "claude-agent")).toBeDefined();
|
|
322
|
+
expect(agents.find((a)=>a.name === "gemini-agent")).toBeDefined();
|
|
323
323
|
});
|
|
324
|
-
it(
|
|
324
|
+
it("should sort agents by status priority (waiting first)", async ()=>{
|
|
325
325
|
const mockAgents = [
|
|
326
326
|
createMockAgent({
|
|
327
|
-
name:
|
|
327
|
+
name: "idle-agent",
|
|
328
328
|
status: AgentStatus.IDLE
|
|
329
329
|
}),
|
|
330
330
|
createMockAgent({
|
|
331
|
-
name:
|
|
331
|
+
name: "waiting-agent",
|
|
332
332
|
status: AgentStatus.WAITING
|
|
333
333
|
}),
|
|
334
334
|
createMockAgent({
|
|
335
|
-
name:
|
|
335
|
+
name: "running-agent",
|
|
336
336
|
status: AgentStatus.RUNNING
|
|
337
337
|
}),
|
|
338
338
|
createMockAgent({
|
|
339
|
-
name:
|
|
339
|
+
name: "unknown-agent",
|
|
340
340
|
status: AgentStatus.UNKNOWN
|
|
341
341
|
})
|
|
342
342
|
];
|
|
343
|
-
const adapter = new MockAdapter(
|
|
343
|
+
const adapter = new MockAdapter("claude", mockAgents);
|
|
344
344
|
manager.registerAdapter(adapter);
|
|
345
345
|
const agents = await manager.listAgents();
|
|
346
|
-
expect(agents[0].name).toBe(
|
|
347
|
-
expect(agents[1].name).toBe(
|
|
348
|
-
expect(agents[2].name).toBe(
|
|
349
|
-
expect(agents[3].name).toBe(
|
|
346
|
+
expect(agents[0].name).toBe("waiting-agent");
|
|
347
|
+
expect(agents[1].name).toBe("running-agent");
|
|
348
|
+
expect(agents[2].name).toBe("idle-agent");
|
|
349
|
+
expect(agents[3].name).toBe("unknown-agent");
|
|
350
350
|
});
|
|
351
|
-
it(
|
|
352
|
-
const goodAdapter = new MockAdapter(
|
|
351
|
+
it("should handle adapter errors gracefully", async ()=>{
|
|
352
|
+
const goodAdapter = new MockAdapter("claude", [
|
|
353
353
|
createMockAgent({
|
|
354
|
-
name:
|
|
354
|
+
name: "good-agent"
|
|
355
355
|
})
|
|
356
356
|
]);
|
|
357
|
-
const badAdapter = new MockAdapter(
|
|
357
|
+
const badAdapter = new MockAdapter("gemini_cli", [], true); // Will fail
|
|
358
358
|
manager.registerAdapter(goodAdapter);
|
|
359
359
|
manager.registerAdapter(badAdapter);
|
|
360
360
|
// Should not throw, should return results from working adapter
|
|
361
361
|
const agents = await manager.listAgents();
|
|
362
362
|
expect(agents).toHaveLength(1);
|
|
363
|
-
expect(agents[0].name).toBe(
|
|
363
|
+
expect(agents[0].name).toBe("good-agent");
|
|
364
364
|
});
|
|
365
|
-
it(
|
|
366
|
-
const adapter1 = new MockAdapter(
|
|
367
|
-
const adapter2 = new MockAdapter(
|
|
365
|
+
it("should return empty array when all adapters fail", async ()=>{
|
|
366
|
+
const adapter1 = new MockAdapter("claude", [], true);
|
|
367
|
+
const adapter2 = new MockAdapter("gemini_cli", [], true);
|
|
368
368
|
manager.registerAdapter(adapter1);
|
|
369
369
|
manager.registerAdapter(adapter2);
|
|
370
370
|
const agents = await manager.listAgents();
|
|
371
371
|
expect(agents).toEqual([]);
|
|
372
372
|
});
|
|
373
373
|
});
|
|
374
|
-
describe(
|
|
374
|
+
describe("listAgents — registry persistence", ()=>{
|
|
375
375
|
let tmpDir;
|
|
376
376
|
let regPath;
|
|
377
377
|
let registry;
|
|
@@ -379,9 +379,9 @@ describe('AgentManager', ()=>{
|
|
|
379
379
|
let nowMs;
|
|
380
380
|
let databaseOperations;
|
|
381
381
|
beforeEach(()=>{
|
|
382
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(),
|
|
383
|
-
regPath = path.join(tmpDir,
|
|
384
|
-
nowMs = Date.parse(
|
|
382
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-manager-"));
|
|
383
|
+
regPath = path.join(tmpDir, "agents.json");
|
|
384
|
+
nowMs = Date.parse("2026-08-14T10:00:00.000Z");
|
|
385
385
|
databaseOperations = [];
|
|
386
386
|
registry = new AgentRegistry(regPath, {
|
|
387
387
|
now: ()=>new Date(nowMs),
|
|
@@ -398,149 +398,167 @@ describe('AgentManager', ()=>{
|
|
|
398
398
|
force: true
|
|
399
399
|
});
|
|
400
400
|
});
|
|
401
|
-
it(
|
|
402
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
401
|
+
it("persists every detected agent to the registry", async ()=>{
|
|
402
|
+
scopedManager.registerAdapter(new MockAdapter("claude", [
|
|
403
403
|
createMockAgent({
|
|
404
|
-
name:
|
|
404
|
+
name: "a",
|
|
405
405
|
pid: process.pid,
|
|
406
|
-
sessionId:
|
|
407
|
-
sessionFilePath:
|
|
408
|
-
projectPath:
|
|
406
|
+
sessionId: "sid-a",
|
|
407
|
+
sessionFilePath: "/path/a.jsonl",
|
|
408
|
+
projectPath: "/cwd/a"
|
|
409
409
|
})
|
|
410
410
|
]));
|
|
411
411
|
await scopedManager.listAgents();
|
|
412
412
|
const entries = registry.list();
|
|
413
413
|
expect(entries).toHaveLength(1);
|
|
414
414
|
expect(entries[0]).toMatchObject({
|
|
415
|
-
name:
|
|
416
|
-
type:
|
|
415
|
+
name: "a",
|
|
416
|
+
type: "claude",
|
|
417
417
|
pid: process.pid,
|
|
418
|
-
cwd:
|
|
419
|
-
sessionId:
|
|
420
|
-
sessionFilePath:
|
|
421
|
-
|
|
418
|
+
cwd: "/cwd/a",
|
|
419
|
+
sessionId: "sid-a",
|
|
420
|
+
sessionFilePath: "/path/a.jsonl",
|
|
421
|
+
runtime: "tmux",
|
|
422
|
+
runtimeRef: null
|
|
422
423
|
});
|
|
423
424
|
expect(entries[0].startedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
424
425
|
});
|
|
425
|
-
it(
|
|
426
|
+
it("prunes entries for dead pids", async ()=>{
|
|
426
427
|
registry.register({
|
|
427
|
-
name:
|
|
428
|
-
type:
|
|
428
|
+
name: "dead",
|
|
429
|
+
type: "claude",
|
|
429
430
|
pid: 999999,
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
431
|
+
runtime: "tmux",
|
|
432
|
+
runtimeRef: null,
|
|
433
|
+
cwd: "/cwd/dead",
|
|
434
|
+
startedAt: "2026-05-30T00:00:00.000Z",
|
|
435
|
+
sessionId: "sid-dead",
|
|
436
|
+
sessionFilePath: "/path/dead.jsonl"
|
|
435
437
|
});
|
|
436
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
438
|
+
scopedManager.registerAdapter(new MockAdapter("claude", [
|
|
437
439
|
createMockAgent({
|
|
438
|
-
name:
|
|
440
|
+
name: "live",
|
|
439
441
|
pid: process.pid
|
|
440
442
|
})
|
|
441
443
|
]));
|
|
442
444
|
await scopedManager.listAgents();
|
|
443
445
|
const entries = registry.list();
|
|
444
446
|
expect(entries.map((e)=>e.name)).toEqual([
|
|
445
|
-
|
|
447
|
+
"live"
|
|
446
448
|
]);
|
|
447
449
|
});
|
|
448
450
|
it('preserves an existing name (e.g. user-set "merry") across cycles', async ()=>{
|
|
449
451
|
registry.register({
|
|
450
|
-
name:
|
|
451
|
-
type:
|
|
452
|
+
name: "merry",
|
|
453
|
+
type: "claude",
|
|
452
454
|
pid: process.pid,
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
455
|
+
runtime: "tmux",
|
|
456
|
+
runtimeRef: {
|
|
457
|
+
session: "merry"
|
|
458
|
+
},
|
|
459
|
+
cwd: "/cwd/merry",
|
|
460
|
+
startedAt: "2026-05-30T00:00:00.000Z",
|
|
461
|
+
sessionId: "sid-merry",
|
|
462
|
+
sessionFilePath: "/path/merry.jsonl"
|
|
458
463
|
});
|
|
459
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
464
|
+
scopedManager.registerAdapter(new MockAdapter("claude", [
|
|
460
465
|
createMockAgent({
|
|
461
|
-
name:
|
|
466
|
+
name: "default-name",
|
|
462
467
|
pid: process.pid
|
|
463
468
|
})
|
|
464
469
|
]));
|
|
465
470
|
const agents = await scopedManager.listAgents();
|
|
466
|
-
expect(agents[0].name).toBe(
|
|
467
|
-
expect(registry.list()[0].name).toBe(
|
|
468
|
-
expect(registry.list()[0].
|
|
469
|
-
|
|
471
|
+
expect(agents[0].name).toBe("merry");
|
|
472
|
+
expect(registry.list()[0].name).toBe("merry");
|
|
473
|
+
expect(registry.list()[0].runtimeRef).toEqual({
|
|
474
|
+
session: "merry"
|
|
475
|
+
});
|
|
476
|
+
expect(registry.list()[0].startedAt).toBe("2026-05-30T00:00:00.000Z");
|
|
470
477
|
});
|
|
471
|
-
it(
|
|
478
|
+
it("preserves custom name and tmux runtime ref across two EPERM refresh cycles", async ()=>{
|
|
472
479
|
registry.register({
|
|
473
|
-
name:
|
|
474
|
-
type:
|
|
480
|
+
name: "merry",
|
|
481
|
+
type: "claude",
|
|
475
482
|
pid: process.pid,
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
483
|
+
runtime: "tmux",
|
|
484
|
+
runtimeRef: {
|
|
485
|
+
session: "merry-tmux"
|
|
486
|
+
},
|
|
487
|
+
cwd: "/cwd/merry",
|
|
488
|
+
startedAt: "2026-05-30T00:00:00.000Z",
|
|
489
|
+
sessionId: "sid-merry",
|
|
490
|
+
sessionFilePath: "/path/merry.jsonl"
|
|
481
491
|
});
|
|
482
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
492
|
+
scopedManager.registerAdapter(new MockAdapter("claude", [
|
|
483
493
|
createMockAgent({
|
|
484
494
|
name: `ai-devkit-${process.pid}`,
|
|
485
495
|
pid: process.pid
|
|
486
496
|
})
|
|
487
497
|
]));
|
|
488
|
-
vi.spyOn(process,
|
|
489
|
-
throw Object.assign(new Error(
|
|
490
|
-
code:
|
|
498
|
+
vi.spyOn(process, "kill").mockImplementation(()=>{
|
|
499
|
+
throw Object.assign(new Error("operation not permitted"), {
|
|
500
|
+
code: "EPERM"
|
|
491
501
|
});
|
|
492
502
|
});
|
|
493
503
|
const firstRefresh = await scopedManager.listAgents();
|
|
494
504
|
const secondRefresh = await scopedManager.listAgents();
|
|
495
|
-
expect(firstRefresh[0].name).toBe(
|
|
496
|
-
expect(secondRefresh[0].name).toBe(
|
|
497
|
-
expect(registry.lookup(
|
|
498
|
-
name:
|
|
499
|
-
|
|
505
|
+
expect(firstRefresh[0].name).toBe("merry");
|
|
506
|
+
expect(secondRefresh[0].name).toBe("merry");
|
|
507
|
+
expect(registry.lookup("merry")).toMatchObject({
|
|
508
|
+
name: "merry",
|
|
509
|
+
runtimeRef: {
|
|
510
|
+
session: "merry-tmux"
|
|
511
|
+
}
|
|
500
512
|
});
|
|
501
513
|
});
|
|
502
|
-
it(
|
|
514
|
+
it("preserves a user-managed name when a fallback row was written later for the same pid", async ()=>{
|
|
503
515
|
registry.register({
|
|
504
|
-
name:
|
|
505
|
-
type:
|
|
516
|
+
name: "agent-list-debug",
|
|
517
|
+
type: "codex",
|
|
506
518
|
pid: process.pid,
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
519
|
+
runtime: "tmux",
|
|
520
|
+
runtimeRef: {
|
|
521
|
+
session: "agent-list-debug"
|
|
522
|
+
},
|
|
523
|
+
cwd: "/cwd/debug",
|
|
524
|
+
startedAt: "2026-05-30T00:00:00.000Z",
|
|
525
|
+
sessionId: "pid-debug",
|
|
526
|
+
sessionFilePath: ""
|
|
512
527
|
});
|
|
513
528
|
registry.register({
|
|
514
529
|
name: `ai-devkit-${process.pid}`,
|
|
515
|
-
type:
|
|
530
|
+
type: "codex",
|
|
516
531
|
pid: process.pid,
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
532
|
+
runtime: "tmux",
|
|
533
|
+
runtimeRef: null,
|
|
534
|
+
cwd: "/cwd/debug",
|
|
535
|
+
startedAt: "2026-05-31T00:00:00.000Z",
|
|
536
|
+
sessionId: "pid-debug",
|
|
537
|
+
sessionFilePath: ""
|
|
522
538
|
});
|
|
523
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
539
|
+
scopedManager.registerAdapter(new MockAdapter("codex", [
|
|
524
540
|
createMockAgent({
|
|
525
541
|
name: `ai-devkit-${process.pid}`,
|
|
526
|
-
type:
|
|
542
|
+
type: "codex",
|
|
527
543
|
pid: process.pid
|
|
528
544
|
})
|
|
529
545
|
]));
|
|
530
546
|
const agents = await scopedManager.listAgents();
|
|
531
|
-
expect(agents[0].name).toBe(
|
|
547
|
+
expect(agents[0].name).toBe("agent-list-debug");
|
|
532
548
|
expect(registry.list()).toHaveLength(1);
|
|
533
549
|
expect(registry.list()[0]).toMatchObject({
|
|
534
|
-
name:
|
|
550
|
+
name: "agent-list-debug",
|
|
535
551
|
pid: process.pid,
|
|
536
|
-
|
|
552
|
+
runtimeRef: {
|
|
553
|
+
session: "agent-list-debug"
|
|
554
|
+
}
|
|
537
555
|
});
|
|
538
556
|
});
|
|
539
|
-
it(
|
|
557
|
+
it("writes a fresh startedAt for new entries", async ()=>{
|
|
540
558
|
const before = new Date().toISOString();
|
|
541
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
559
|
+
scopedManager.registerAdapter(new MockAdapter("claude", [
|
|
542
560
|
createMockAgent({
|
|
543
|
-
name:
|
|
561
|
+
name: "new",
|
|
544
562
|
pid: process.pid
|
|
545
563
|
})
|
|
546
564
|
]));
|
|
@@ -548,36 +566,36 @@ describe('AgentManager', ()=>{
|
|
|
548
566
|
const entry = registry.list()[0];
|
|
549
567
|
expect(entry.startedAt >= before).toBe(true);
|
|
550
568
|
});
|
|
551
|
-
it(
|
|
552
|
-
const spy = vi.spyOn(registry,
|
|
553
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
569
|
+
it("batches the write — a single registerBatch call per listAgents", async ()=>{
|
|
570
|
+
const spy = vi.spyOn(registry, "registerBatch");
|
|
571
|
+
scopedManager.registerAdapter(new MockAdapter("claude", [
|
|
554
572
|
createMockAgent({
|
|
555
|
-
name:
|
|
573
|
+
name: "a",
|
|
556
574
|
pid: process.pid
|
|
557
575
|
})
|
|
558
576
|
]));
|
|
559
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
577
|
+
scopedManager.registerAdapter(new MockAdapter("codex", [
|
|
560
578
|
createMockAgent({
|
|
561
|
-
name:
|
|
562
|
-
type:
|
|
579
|
+
name: "b",
|
|
580
|
+
type: "codex",
|
|
563
581
|
pid: process.pid + 1
|
|
564
582
|
})
|
|
565
583
|
]));
|
|
566
584
|
await scopedManager.listAgents();
|
|
567
585
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
568
586
|
expect(spy.mock.calls[0][0].map((e)=>e.name).sort()).toEqual([
|
|
569
|
-
|
|
570
|
-
|
|
587
|
+
"a",
|
|
588
|
+
"b"
|
|
571
589
|
]);
|
|
572
590
|
});
|
|
573
|
-
it(
|
|
574
|
-
const adapter = new MockAdapter(
|
|
591
|
+
it("performs zero database writes on an unchanged refresh", async ()=>{
|
|
592
|
+
const adapter = new MockAdapter("claude", [
|
|
575
593
|
createMockAgent({
|
|
576
|
-
name:
|
|
594
|
+
name: "stable",
|
|
577
595
|
pid: process.pid,
|
|
578
|
-
projectPath:
|
|
579
|
-
sessionId:
|
|
580
|
-
sessionFilePath:
|
|
596
|
+
projectPath: "/cwd/stable",
|
|
597
|
+
sessionId: "stable-session",
|
|
598
|
+
sessionFilePath: "/sessions/stable.jsonl"
|
|
581
599
|
})
|
|
582
600
|
]);
|
|
583
601
|
scopedManager.registerAdapter(adapter);
|
|
@@ -587,66 +605,66 @@ describe('AgentManager', ()=>{
|
|
|
587
605
|
const writes = databaseOperations.filter((sql)=>/^\s*(BEGIN|COMMIT|INSERT|UPDATE|DELETE)/i.test(sql));
|
|
588
606
|
expect(writes).toEqual([]);
|
|
589
607
|
});
|
|
590
|
-
it(
|
|
591
|
-
const adapter = new MockAdapter(
|
|
608
|
+
it("exposes a persisted pin and preserves it across a changed poll refresh", async ()=>{
|
|
609
|
+
const adapter = new MockAdapter("claude", [
|
|
592
610
|
createMockAgent({
|
|
593
|
-
name:
|
|
611
|
+
name: "pinned",
|
|
594
612
|
pid: process.pid,
|
|
595
|
-
sessionId:
|
|
613
|
+
sessionId: "before"
|
|
596
614
|
})
|
|
597
615
|
]);
|
|
598
616
|
scopedManager.registerAdapter(adapter);
|
|
599
617
|
await scopedManager.listAgents();
|
|
600
|
-
registry.togglePin(
|
|
618
|
+
registry.togglePin("claude", process.pid);
|
|
601
619
|
adapter.setAgents([
|
|
602
620
|
createMockAgent({
|
|
603
|
-
name:
|
|
621
|
+
name: "pinned",
|
|
604
622
|
pid: process.pid,
|
|
605
|
-
sessionId:
|
|
623
|
+
sessionId: "after"
|
|
606
624
|
})
|
|
607
625
|
]);
|
|
608
626
|
const agents = await scopedManager.listAgents();
|
|
609
627
|
expect(agents[0].pinned).toBe(true);
|
|
610
|
-
expect(registry.lookup(
|
|
611
|
-
sessionId:
|
|
628
|
+
expect(registry.lookup("pinned")).toMatchObject({
|
|
629
|
+
sessionId: "after",
|
|
612
630
|
pinned: true
|
|
613
631
|
});
|
|
614
632
|
});
|
|
615
|
-
it(
|
|
616
|
-
const adapter = new MockAdapter(
|
|
633
|
+
it("uses registry updated_at as lastActive for pinned recency ordering", async ()=>{
|
|
634
|
+
const adapter = new MockAdapter("claude", [
|
|
617
635
|
createMockAgent({
|
|
618
|
-
name:
|
|
636
|
+
name: "recently-pinned",
|
|
619
637
|
pid: process.pid,
|
|
620
|
-
lastActive: new Date(
|
|
638
|
+
lastActive: new Date("2026-01-01T00:00:00.000Z")
|
|
621
639
|
})
|
|
622
640
|
]);
|
|
623
641
|
scopedManager.registerAdapter(adapter);
|
|
624
642
|
await scopedManager.listAgents();
|
|
625
643
|
nowMs += 60_000;
|
|
626
|
-
scopedManager.togglePin(
|
|
644
|
+
scopedManager.togglePin("recently-pinned");
|
|
627
645
|
const agents = await scopedManager.listAgents();
|
|
628
646
|
expect(agents[0].pinned).toBe(true);
|
|
629
|
-
expect(agents[0].lastActive.toISOString()).toBe(
|
|
647
|
+
expect(agents[0].lastActive.toISOString()).toBe("2026-08-14T10:01:00.000Z");
|
|
630
648
|
});
|
|
631
|
-
it(
|
|
632
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
649
|
+
it("preserves adapter lastActive for unpinned agents", async ()=>{
|
|
650
|
+
scopedManager.registerAdapter(new MockAdapter("claude", [
|
|
633
651
|
createMockAgent({
|
|
634
|
-
name:
|
|
652
|
+
name: "unpinned",
|
|
635
653
|
pid: process.pid,
|
|
636
|
-
lastActive: new Date(
|
|
654
|
+
lastActive: new Date("2026-01-01T00:00:00.000Z")
|
|
637
655
|
})
|
|
638
656
|
]));
|
|
639
657
|
await scopedManager.listAgents();
|
|
640
658
|
const agents = await scopedManager.listAgents();
|
|
641
659
|
expect(agents[0].pinned).toBe(false);
|
|
642
|
-
expect(agents[0].lastActive.toISOString()).toBe(
|
|
660
|
+
expect(agents[0].lastActive.toISOString()).toBe("2026-01-01T00:00:00.000Z");
|
|
643
661
|
});
|
|
644
|
-
it(
|
|
645
|
-
const adapter = new MockAdapter(
|
|
662
|
+
it("persists changed fields once in one write transaction", async ()=>{
|
|
663
|
+
const adapter = new MockAdapter("claude", [
|
|
646
664
|
createMockAgent({
|
|
647
|
-
name:
|
|
665
|
+
name: "changing",
|
|
648
666
|
pid: process.pid,
|
|
649
|
-
projectPath:
|
|
667
|
+
projectPath: "/cwd/before"
|
|
650
668
|
})
|
|
651
669
|
]);
|
|
652
670
|
scopedManager.registerAdapter(adapter);
|
|
@@ -655,9 +673,9 @@ describe('AgentManager', ()=>{
|
|
|
655
673
|
nowMs += 1_000;
|
|
656
674
|
adapter.setAgents([
|
|
657
675
|
createMockAgent({
|
|
658
|
-
name:
|
|
676
|
+
name: "changing",
|
|
659
677
|
pid: process.pid,
|
|
660
|
-
projectPath:
|
|
678
|
+
projectPath: "/cwd/after"
|
|
661
679
|
})
|
|
662
680
|
]);
|
|
663
681
|
await scopedManager.listAgents();
|
|
@@ -666,339 +684,346 @@ describe('AgentManager', ()=>{
|
|
|
666
684
|
expect(upserts).toHaveLength(1);
|
|
667
685
|
expect(upserts[0]).toContain("'2026-08-14T10:00:01.000Z'");
|
|
668
686
|
expect(transactions).toHaveLength(2);
|
|
669
|
-
expect(registry.lookup(
|
|
687
|
+
expect(registry.lookup("changing")?.cwd).toBe("/cwd/after");
|
|
670
688
|
});
|
|
671
|
-
it(
|
|
689
|
+
it("prunes newly dead entries only when the passive cadence is due", async ()=>{
|
|
672
690
|
registry.register({
|
|
673
|
-
name:
|
|
674
|
-
type:
|
|
691
|
+
name: "cadenced",
|
|
692
|
+
type: "claude",
|
|
675
693
|
pid: process.pid,
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
694
|
+
runtime: "tmux",
|
|
695
|
+
runtimeRef: null,
|
|
696
|
+
cwd: "/cwd/cadenced",
|
|
697
|
+
startedAt: "2026-05-30T00:00:00.000Z",
|
|
698
|
+
sessionId: "sid-cadenced",
|
|
699
|
+
sessionFilePath: ""
|
|
681
700
|
});
|
|
682
|
-
const alive = vi.spyOn(registry,
|
|
701
|
+
const alive = vi.spyOn(registry, "isAlive").mockReturnValue(true);
|
|
683
702
|
await scopedManager.listAgents();
|
|
684
703
|
expect(alive).toHaveBeenCalledTimes(1);
|
|
685
704
|
alive.mockReturnValue(false);
|
|
686
705
|
nowMs += 29_999;
|
|
687
706
|
await scopedManager.listAgents();
|
|
688
707
|
expect(alive).toHaveBeenCalledTimes(1);
|
|
689
|
-
expect(registry.lookup(
|
|
708
|
+
expect(registry.lookup("cadenced")).not.toBeNull();
|
|
690
709
|
nowMs += 1;
|
|
691
710
|
await scopedManager.listAgents();
|
|
692
711
|
expect(alive).toHaveBeenCalledTimes(2);
|
|
693
|
-
expect(registry.lookup(
|
|
712
|
+
expect(registry.lookup("cadenced")).toBeNull();
|
|
694
713
|
});
|
|
695
|
-
it(
|
|
714
|
+
it("does not inherit a name when the same pid is reused by another agent type", async ()=>{
|
|
696
715
|
registry.register({
|
|
697
|
-
name:
|
|
698
|
-
type:
|
|
716
|
+
name: "old-claude",
|
|
717
|
+
type: "claude",
|
|
699
718
|
pid: process.pid,
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
719
|
+
runtime: "tmux",
|
|
720
|
+
runtimeRef: {
|
|
721
|
+
session: "old-claude"
|
|
722
|
+
},
|
|
723
|
+
cwd: "/cwd/old",
|
|
724
|
+
startedAt: "2026-05-30T00:00:00.000Z",
|
|
725
|
+
sessionId: "old-session",
|
|
726
|
+
sessionFilePath: ""
|
|
705
727
|
});
|
|
706
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
728
|
+
scopedManager.registerAdapter(new MockAdapter("codex", [
|
|
707
729
|
createMockAgent({
|
|
708
|
-
name:
|
|
709
|
-
type:
|
|
730
|
+
name: "new-codex",
|
|
731
|
+
type: "codex",
|
|
710
732
|
pid: process.pid,
|
|
711
|
-
projectPath:
|
|
712
|
-
sessionId:
|
|
733
|
+
projectPath: "/cwd/new",
|
|
734
|
+
sessionId: "new-session"
|
|
713
735
|
})
|
|
714
736
|
]));
|
|
715
737
|
const agents = await scopedManager.listAgents();
|
|
716
|
-
expect(agents[0].name).toBe(
|
|
717
|
-
expect(registry.lookup(
|
|
718
|
-
expect(registry.lookup(
|
|
719
|
-
type:
|
|
738
|
+
expect(agents[0].name).toBe("new-codex");
|
|
739
|
+
expect(registry.lookup("old-claude")).toBeNull();
|
|
740
|
+
expect(registry.lookup("new-codex")).toMatchObject({
|
|
741
|
+
type: "codex",
|
|
720
742
|
pid: process.pid
|
|
721
743
|
});
|
|
722
744
|
});
|
|
723
|
-
it(
|
|
724
|
-
const writeSpy = vi.spyOn(registry,
|
|
725
|
-
const pruneSpy = vi.spyOn(registry,
|
|
726
|
-
scopedManager.registerAdapter(new MockAdapter(
|
|
745
|
+
it("skips registerBatch when no agents are detected and prune is not due", async ()=>{
|
|
746
|
+
const writeSpy = vi.spyOn(registry, "registerBatch");
|
|
747
|
+
const pruneSpy = vi.spyOn(registry, "pruneIfDue");
|
|
748
|
+
scopedManager.registerAdapter(new MockAdapter("claude", []));
|
|
727
749
|
await scopedManager.listAgents();
|
|
728
750
|
await scopedManager.listAgents();
|
|
729
751
|
expect(writeSpy).not.toHaveBeenCalled();
|
|
730
752
|
expect(pruneSpy).toHaveBeenCalledTimes(2);
|
|
731
753
|
});
|
|
732
754
|
});
|
|
733
|
-
describe(
|
|
734
|
-
it(
|
|
735
|
-
const registry = new AgentRegistry(path.join(tmpDir,
|
|
755
|
+
describe("togglePin", ()=>{
|
|
756
|
+
it("resolves the agent name to its process identity and toggles the pin", ()=>{
|
|
757
|
+
const registry = new AgentRegistry(path.join(tmpDir, "toggle.json"));
|
|
736
758
|
const scopedManager = new AgentManager(registry);
|
|
737
759
|
registry.register({
|
|
738
|
-
name:
|
|
739
|
-
type:
|
|
760
|
+
name: "renamed-agent",
|
|
761
|
+
type: "claude",
|
|
740
762
|
pid: process.pid,
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
763
|
+
runtime: "tmux",
|
|
764
|
+
runtimeRef: null,
|
|
765
|
+
cwd: "/tmp",
|
|
766
|
+
startedAt: "2026-08-16T00:00:00.000Z",
|
|
767
|
+
sessionId: "session",
|
|
768
|
+
sessionFilePath: "",
|
|
746
769
|
pinned: false
|
|
747
770
|
});
|
|
748
|
-
expect(scopedManager.togglePin(
|
|
749
|
-
expect(registry.lookup(
|
|
771
|
+
expect(scopedManager.togglePin("renamed-agent")).toBe(true);
|
|
772
|
+
expect(registry.lookup("renamed-agent")?.pinned).toBe(true);
|
|
750
773
|
});
|
|
751
|
-
it(
|
|
752
|
-
expect(()=>manager.togglePin(
|
|
774
|
+
it("reports when the agent is no longer running", ()=>{
|
|
775
|
+
expect(()=>manager.togglePin("missing")).toThrow(/no longer running/i);
|
|
753
776
|
});
|
|
754
|
-
it(
|
|
755
|
-
const registry = new AgentRegistry(path.join(tmpDir,
|
|
777
|
+
it("rejects a dead process and prunes its row", ()=>{
|
|
778
|
+
const registry = new AgentRegistry(path.join(tmpDir, "dead-toggle.json"));
|
|
756
779
|
const scopedManager = new AgentManager(registry);
|
|
757
780
|
registry.register({
|
|
758
|
-
name:
|
|
759
|
-
type:
|
|
781
|
+
name: "dead",
|
|
782
|
+
type: "claude",
|
|
760
783
|
pid: 999999,
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
784
|
+
runtime: "tmux",
|
|
785
|
+
runtimeRef: null,
|
|
786
|
+
cwd: "/tmp",
|
|
787
|
+
startedAt: "2026-08-16T00:00:00.000Z",
|
|
788
|
+
sessionId: "session",
|
|
789
|
+
sessionFilePath: "",
|
|
766
790
|
pinned: false
|
|
767
791
|
});
|
|
768
|
-
expect(()=>scopedManager.togglePin(
|
|
769
|
-
expect(registry.lookup(
|
|
792
|
+
expect(()=>scopedManager.togglePin("dead")).toThrow(/no longer running/i);
|
|
793
|
+
expect(registry.lookup("dead")).toBeNull();
|
|
770
794
|
});
|
|
771
|
-
it(
|
|
772
|
-
const regPath = path.join(tmpDir,
|
|
795
|
+
it("surfaces a clear readonly mutation error", ()=>{
|
|
796
|
+
const regPath = path.join(tmpDir, "readonly-toggle.json");
|
|
773
797
|
const writable = new AgentRegistry(regPath);
|
|
774
798
|
writable.register({
|
|
775
|
-
name:
|
|
776
|
-
type:
|
|
799
|
+
name: "readonly-agent",
|
|
800
|
+
type: "claude",
|
|
777
801
|
pid: process.pid,
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
802
|
+
runtime: "tmux",
|
|
803
|
+
runtimeRef: null,
|
|
804
|
+
cwd: "/tmp",
|
|
805
|
+
startedAt: "2026-08-16T00:00:00.000Z",
|
|
806
|
+
sessionId: "session",
|
|
807
|
+
sessionFilePath: "",
|
|
783
808
|
pinned: false
|
|
784
809
|
});
|
|
785
810
|
const readonlyManager = new AgentManager(new AgentRegistry(regPath, {
|
|
786
811
|
readonly: true
|
|
787
812
|
}));
|
|
788
|
-
expect(()=>readonlyManager.togglePin(
|
|
813
|
+
expect(()=>readonlyManager.togglePin("readonly-agent")).toThrow("Agent registry is readonly; cannot toggle pin.");
|
|
789
814
|
});
|
|
790
815
|
});
|
|
791
|
-
describe(
|
|
792
|
-
it(
|
|
793
|
-
manager.registerAdapter(new MockAdapter(
|
|
794
|
-
manager.registerAdapter(new MockAdapter(
|
|
816
|
+
describe("clear", ()=>{
|
|
817
|
+
it("should remove all adapters", ()=>{
|
|
818
|
+
manager.registerAdapter(new MockAdapter("claude"));
|
|
819
|
+
manager.registerAdapter(new MockAdapter("gemini_cli"));
|
|
795
820
|
manager.clear();
|
|
796
821
|
expect(manager.getAdapterCount()).toBe(0);
|
|
797
822
|
expect(manager.getAdapters()).toEqual([]);
|
|
798
823
|
});
|
|
799
824
|
});
|
|
800
|
-
describe(
|
|
825
|
+
describe("listSessions", ()=>{
|
|
801
826
|
function createMockSession(overrides = {}) {
|
|
802
827
|
return {
|
|
803
|
-
type:
|
|
804
|
-
sessionId:
|
|
805
|
-
cwd:
|
|
806
|
-
firstUserMessage:
|
|
807
|
-
lastActive: new Date(
|
|
808
|
-
startedAt: new Date(
|
|
809
|
-
sessionFilePath:
|
|
828
|
+
type: "claude",
|
|
829
|
+
sessionId: "session-1",
|
|
830
|
+
cwd: "/repo",
|
|
831
|
+
firstUserMessage: "hello",
|
|
832
|
+
lastActive: new Date("2025-01-01T00:00:00Z"),
|
|
833
|
+
startedAt: new Date("2025-01-01T00:00:00Z"),
|
|
834
|
+
sessionFilePath: "/tmp/session-1.jsonl",
|
|
810
835
|
...overrides
|
|
811
836
|
};
|
|
812
837
|
}
|
|
813
|
-
it(
|
|
838
|
+
it("returns empty array when no adapters are registered", async ()=>{
|
|
814
839
|
const result = await manager.listSessions();
|
|
815
840
|
expect(result).toEqual([]);
|
|
816
841
|
});
|
|
817
|
-
it(
|
|
842
|
+
it("merges sessions from every registered adapter", async ()=>{
|
|
818
843
|
const claudeSession = createMockSession({
|
|
819
|
-
type:
|
|
820
|
-
sessionId:
|
|
844
|
+
type: "claude",
|
|
845
|
+
sessionId: "c1"
|
|
821
846
|
});
|
|
822
847
|
const codexSession = createMockSession({
|
|
823
|
-
type:
|
|
824
|
-
sessionId:
|
|
848
|
+
type: "codex",
|
|
849
|
+
sessionId: "cx1"
|
|
825
850
|
});
|
|
826
|
-
manager.registerAdapter(new MockAdapter(
|
|
851
|
+
manager.registerAdapter(new MockAdapter("claude", [], false, [
|
|
827
852
|
claudeSession
|
|
828
853
|
]));
|
|
829
|
-
manager.registerAdapter(new MockAdapter(
|
|
854
|
+
manager.registerAdapter(new MockAdapter("codex", [], false, [
|
|
830
855
|
codexSession
|
|
831
856
|
]));
|
|
832
857
|
const result = await manager.listSessions();
|
|
833
858
|
expect(result).toHaveLength(2);
|
|
834
859
|
expect(result.map((s)=>s.sessionId).sort()).toEqual([
|
|
835
|
-
|
|
836
|
-
|
|
860
|
+
"c1",
|
|
861
|
+
"cx1"
|
|
837
862
|
]);
|
|
838
863
|
});
|
|
839
|
-
it(
|
|
864
|
+
it("sorts merged sessions by lastActive descending", async ()=>{
|
|
840
865
|
const older = createMockSession({
|
|
841
|
-
sessionId:
|
|
842
|
-
lastActive: new Date(
|
|
866
|
+
sessionId: "older",
|
|
867
|
+
lastActive: new Date("2025-01-01T00:00:00Z")
|
|
843
868
|
});
|
|
844
869
|
const newer = createMockSession({
|
|
845
|
-
type:
|
|
846
|
-
sessionId:
|
|
847
|
-
lastActive: new Date(
|
|
870
|
+
type: "codex",
|
|
871
|
+
sessionId: "newer",
|
|
872
|
+
lastActive: new Date("2025-06-01T00:00:00Z")
|
|
848
873
|
});
|
|
849
|
-
manager.registerAdapter(new MockAdapter(
|
|
874
|
+
manager.registerAdapter(new MockAdapter("claude", [], false, [
|
|
850
875
|
older
|
|
851
876
|
]));
|
|
852
|
-
manager.registerAdapter(new MockAdapter(
|
|
877
|
+
manager.registerAdapter(new MockAdapter("codex", [], false, [
|
|
853
878
|
newer
|
|
854
879
|
]));
|
|
855
880
|
const result = await manager.listSessions();
|
|
856
881
|
expect(result.map((s)=>s.sessionId)).toEqual([
|
|
857
|
-
|
|
858
|
-
|
|
882
|
+
"newer",
|
|
883
|
+
"older"
|
|
859
884
|
]);
|
|
860
885
|
});
|
|
861
|
-
it(
|
|
862
|
-
const claudeAdapter = new MockAdapter(
|
|
886
|
+
it("skips adapters whose type does not match opts.type", async ()=>{
|
|
887
|
+
const claudeAdapter = new MockAdapter("claude", [], false, [
|
|
863
888
|
createMockSession({
|
|
864
|
-
type:
|
|
865
|
-
sessionId:
|
|
889
|
+
type: "claude",
|
|
890
|
+
sessionId: "c1"
|
|
866
891
|
})
|
|
867
892
|
]);
|
|
868
|
-
const codexAdapter = new MockAdapter(
|
|
893
|
+
const codexAdapter = new MockAdapter("codex", [], false, [
|
|
869
894
|
createMockSession({
|
|
870
|
-
type:
|
|
871
|
-
sessionId:
|
|
895
|
+
type: "codex",
|
|
896
|
+
sessionId: "cx1"
|
|
872
897
|
})
|
|
873
898
|
]);
|
|
874
899
|
manager.registerAdapter(claudeAdapter);
|
|
875
900
|
manager.registerAdapter(codexAdapter);
|
|
876
901
|
const result = await manager.listSessions({
|
|
877
|
-
type:
|
|
902
|
+
type: "claude"
|
|
878
903
|
});
|
|
879
904
|
expect(result).toHaveLength(1);
|
|
880
|
-
expect(result[0].sessionId).toBe(
|
|
905
|
+
expect(result[0].sessionId).toBe("c1");
|
|
881
906
|
// Codex adapter must not have been called
|
|
882
907
|
expect(codexAdapter.lastListSessionsOpts).toBeUndefined();
|
|
883
908
|
expect(claudeAdapter.lastListSessionsOpts).toEqual({
|
|
884
|
-
type:
|
|
909
|
+
type: "claude"
|
|
885
910
|
});
|
|
886
911
|
});
|
|
887
|
-
it(
|
|
912
|
+
it("tolerates an adapter that throws and still returns the others", async ()=>{
|
|
888
913
|
const goodSession = createMockSession({
|
|
889
|
-
sessionId:
|
|
914
|
+
sessionId: "good"
|
|
890
915
|
});
|
|
891
|
-
manager.registerAdapter(new MockAdapter(
|
|
916
|
+
manager.registerAdapter(new MockAdapter("claude", [], false, [
|
|
892
917
|
goodSession
|
|
893
918
|
]));
|
|
894
|
-
manager.registerAdapter(new MockAdapter(
|
|
895
|
-
const consoleErrorSpy = vi.spyOn(console,
|
|
919
|
+
manager.registerAdapter(new MockAdapter("codex", [], false, [], true));
|
|
920
|
+
const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(()=>undefined);
|
|
896
921
|
try {
|
|
897
922
|
const result = await manager.listSessions();
|
|
898
923
|
expect(result).toHaveLength(1);
|
|
899
|
-
expect(result[0].sessionId).toBe(
|
|
924
|
+
expect(result[0].sessionId).toBe("good");
|
|
900
925
|
expect(consoleErrorSpy).toHaveBeenCalled();
|
|
901
926
|
} finally{
|
|
902
927
|
consoleErrorSpy.mockRestore();
|
|
903
928
|
}
|
|
904
929
|
});
|
|
905
|
-
it(
|
|
906
|
-
const a = new MockAdapter(
|
|
907
|
-
const b = new MockAdapter(
|
|
930
|
+
it("passes the same opts to every called adapter", async ()=>{
|
|
931
|
+
const a = new MockAdapter("claude", [], false, []);
|
|
932
|
+
const b = new MockAdapter("codex", [], false, []);
|
|
908
933
|
manager.registerAdapter(a);
|
|
909
934
|
manager.registerAdapter(b);
|
|
910
935
|
await manager.listSessions({
|
|
911
|
-
cwd:
|
|
936
|
+
cwd: "/Users/test/proj"
|
|
912
937
|
});
|
|
913
938
|
expect(a.lastListSessionsOpts).toEqual({
|
|
914
|
-
cwd:
|
|
939
|
+
cwd: "/Users/test/proj"
|
|
915
940
|
});
|
|
916
941
|
expect(b.lastListSessionsOpts).toEqual({
|
|
917
|
-
cwd:
|
|
942
|
+
cwd: "/Users/test/proj"
|
|
918
943
|
});
|
|
919
944
|
});
|
|
920
945
|
});
|
|
921
|
-
describe(
|
|
922
|
-
it(
|
|
946
|
+
describe("resolveAgent", ()=>{
|
|
947
|
+
it("should return null for empty input or empty agents list", ()=>{
|
|
923
948
|
const agent = createMockAgent({
|
|
924
|
-
name:
|
|
949
|
+
name: "test-agent"
|
|
925
950
|
});
|
|
926
|
-
expect(manager.resolveAgent(
|
|
951
|
+
expect(manager.resolveAgent("", [
|
|
927
952
|
agent
|
|
928
953
|
])).toBeNull();
|
|
929
|
-
expect(manager.resolveAgent(
|
|
954
|
+
expect(manager.resolveAgent("test", [])).toBeNull();
|
|
930
955
|
});
|
|
931
|
-
it(
|
|
956
|
+
it("should resolve exact match (case-insensitive)", ()=>{
|
|
932
957
|
const agent = createMockAgent({
|
|
933
|
-
name:
|
|
958
|
+
name: "My-Agent"
|
|
934
959
|
});
|
|
935
960
|
const agents = [
|
|
936
961
|
agent,
|
|
937
962
|
createMockAgent({
|
|
938
|
-
name:
|
|
963
|
+
name: "Other"
|
|
939
964
|
})
|
|
940
965
|
];
|
|
941
966
|
// Exact match
|
|
942
|
-
expect(manager.resolveAgent(
|
|
967
|
+
expect(manager.resolveAgent("My-Agent", agents)).toBe(agent);
|
|
943
968
|
// Case-insensitive
|
|
944
|
-
expect(manager.resolveAgent(
|
|
969
|
+
expect(manager.resolveAgent("my-agent", agents)).toBe(agent);
|
|
945
970
|
});
|
|
946
|
-
it(
|
|
971
|
+
it("should resolve unique partial match", ()=>{
|
|
947
972
|
const agent = createMockAgent({
|
|
948
|
-
name:
|
|
973
|
+
name: "ai-devkit"
|
|
949
974
|
});
|
|
950
975
|
const agents = [
|
|
951
976
|
agent,
|
|
952
977
|
createMockAgent({
|
|
953
|
-
name:
|
|
978
|
+
name: "other-project"
|
|
954
979
|
})
|
|
955
980
|
];
|
|
956
|
-
const result = manager.resolveAgent(
|
|
981
|
+
const result = manager.resolveAgent("dev", agents);
|
|
957
982
|
expect(result).toBe(agent);
|
|
958
983
|
});
|
|
959
|
-
it(
|
|
984
|
+
it("should return array for ambiguous partial match", ()=>{
|
|
960
985
|
const agent1 = createMockAgent({
|
|
961
|
-
name:
|
|
986
|
+
name: "my-website"
|
|
962
987
|
});
|
|
963
988
|
const agent2 = createMockAgent({
|
|
964
|
-
name:
|
|
989
|
+
name: "my-app"
|
|
965
990
|
});
|
|
966
991
|
const agents = [
|
|
967
992
|
agent1,
|
|
968
993
|
agent2,
|
|
969
994
|
createMockAgent({
|
|
970
|
-
name:
|
|
995
|
+
name: "other"
|
|
971
996
|
})
|
|
972
997
|
];
|
|
973
|
-
const result = manager.resolveAgent(
|
|
998
|
+
const result = manager.resolveAgent("my", agents);
|
|
974
999
|
expect(Array.isArray(result)).toBe(true);
|
|
975
1000
|
const matches = result;
|
|
976
1001
|
expect(matches).toHaveLength(2);
|
|
977
1002
|
expect(matches).toContain(agent1);
|
|
978
1003
|
expect(matches).toContain(agent2);
|
|
979
1004
|
});
|
|
980
|
-
it(
|
|
1005
|
+
it("should return null for no match", ()=>{
|
|
981
1006
|
const agents = [
|
|
982
1007
|
createMockAgent({
|
|
983
|
-
name:
|
|
1008
|
+
name: "ai-devkit"
|
|
984
1009
|
})
|
|
985
1010
|
];
|
|
986
|
-
expect(manager.resolveAgent(
|
|
1011
|
+
expect(manager.resolveAgent("xyz", agents)).toBeNull();
|
|
987
1012
|
});
|
|
988
|
-
it(
|
|
1013
|
+
it("should prefer exact match over partial matches", ()=>{
|
|
989
1014
|
// Edge case: "test" matches "test" (exact) and "testing" (partial)
|
|
990
1015
|
// Should return exact "test"
|
|
991
1016
|
const exact = createMockAgent({
|
|
992
|
-
name:
|
|
1017
|
+
name: "test"
|
|
993
1018
|
});
|
|
994
1019
|
const partial = createMockAgent({
|
|
995
|
-
name:
|
|
1020
|
+
name: "testing"
|
|
996
1021
|
});
|
|
997
1022
|
const agents = [
|
|
998
1023
|
exact,
|
|
999
1024
|
partial
|
|
1000
1025
|
];
|
|
1001
|
-
expect(manager.resolveAgent(
|
|
1026
|
+
expect(manager.resolveAgent("test", agents)).toBe(exact);
|
|
1002
1027
|
});
|
|
1003
1028
|
});
|
|
1004
1029
|
});
|