@ai-devkit/agent-manager 0.16.0 → 0.18.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.
Files changed (105) hide show
  1. package/.swcrc +27 -0
  2. package/dist/AgentManager.js +72 -74
  3. package/dist/AgentManager.js.map +1 -1
  4. package/dist/__tests__/AgentManager.test.js +564 -0
  5. package/dist/__tests__/AgentManager.test.js.map +1 -0
  6. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +2157 -0
  7. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -0
  8. package/dist/__tests__/adapters/CodexAdapter.test.js +1419 -0
  9. package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -0
  10. package/dist/__tests__/adapters/CopilotAdapter.test.js +500 -0
  11. package/dist/__tests__/adapters/CopilotAdapter.test.js.map +1 -0
  12. package/dist/__tests__/adapters/GeminiCliAdapter.test.js +1229 -0
  13. package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +1 -0
  14. package/dist/__tests__/adapters/OpenCodeAdapter.test.js +626 -0
  15. package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +1 -0
  16. package/dist/__tests__/adapters/PiAdapter.test.js +590 -0
  17. package/dist/__tests__/adapters/PiAdapter.test.js.map +1 -0
  18. package/dist/__tests__/terminal/TmuxManager.test.js +159 -0
  19. package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -0
  20. package/dist/__tests__/terminal/TtyWriter.test.js +162 -0
  21. package/dist/__tests__/terminal/TtyWriter.test.js.map +1 -0
  22. package/dist/__tests__/utils/AgentRegistry.test.js +282 -0
  23. package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -0
  24. package/dist/__tests__/utils/ClaudeSessionParser.test.js +312 -0
  25. package/dist/__tests__/utils/ClaudeSessionParser.test.js.map +1 -0
  26. package/dist/__tests__/utils/matching.test.js +324 -0
  27. package/dist/__tests__/utils/matching.test.js.map +1 -0
  28. package/dist/__tests__/utils/process.test.js +177 -0
  29. package/dist/__tests__/utils/process.test.js.map +1 -0
  30. package/dist/__tests__/utils/session.test.js +158 -0
  31. package/dist/__tests__/utils/session.test.js.map +1 -0
  32. package/dist/adapters/AgentAdapter.d.ts +1 -1
  33. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  34. package/dist/adapters/AgentAdapter.js +8 -7
  35. package/dist/adapters/AgentAdapter.js.map +1 -1
  36. package/dist/adapters/ClaudeCodeAdapter.js +66 -77
  37. package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
  38. package/dist/adapters/CodexAdapter.d.ts +13 -0
  39. package/dist/adapters/CodexAdapter.d.ts.map +1 -1
  40. package/dist/adapters/CodexAdapter.js +225 -141
  41. package/dist/adapters/CodexAdapter.js.map +1 -1
  42. package/dist/adapters/CopilotAdapter.d.ts +44 -0
  43. package/dist/adapters/CopilotAdapter.d.ts.map +1 -0
  44. package/dist/adapters/CopilotAdapter.js +314 -0
  45. package/dist/adapters/CopilotAdapter.js.map +1 -0
  46. package/dist/adapters/GeminiCliAdapter.d.ts +2 -0
  47. package/dist/adapters/GeminiCliAdapter.d.ts.map +1 -1
  48. package/dist/adapters/GeminiCliAdapter.js +138 -156
  49. package/dist/adapters/GeminiCliAdapter.js.map +1 -1
  50. package/dist/adapters/OpenCodeAdapter.js +60 -69
  51. package/dist/adapters/OpenCodeAdapter.js.map +1 -1
  52. package/dist/adapters/PiAdapter.d.ts +62 -0
  53. package/dist/adapters/PiAdapter.d.ts.map +1 -0
  54. package/dist/adapters/PiAdapter.js +450 -0
  55. package/dist/adapters/PiAdapter.js.map +1 -0
  56. package/dist/adapters/index.d.ts +2 -0
  57. package/dist/adapters/index.d.ts.map +1 -1
  58. package/dist/adapters/index.js +3 -0
  59. package/dist/adapters/index.js.map +1 -1
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +3 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/terminal/TerminalFocusManager.js +62 -49
  65. package/dist/terminal/TerminalFocusManager.js.map +1 -1
  66. package/dist/terminal/TmuxManager.js +59 -34
  67. package/dist/terminal/TmuxManager.js.map +1 -1
  68. package/dist/terminal/TtyWriter.js +37 -16
  69. package/dist/terminal/TtyWriter.js.map +1 -1
  70. package/dist/terminal/index.js +1 -0
  71. package/dist/terminal/index.js.map +1 -1
  72. package/dist/utils/AgentRegistry.js +41 -32
  73. package/dist/utils/AgentRegistry.js.map +1 -1
  74. package/dist/utils/ClaudeSessionParser.js +46 -87
  75. package/dist/utils/ClaudeSessionParser.js.map +1 -1
  76. package/dist/utils/agents.d.ts +1 -1
  77. package/dist/utils/agents.d.ts.map +1 -1
  78. package/dist/utils/agents.js +22 -11
  79. package/dist/utils/agents.js.map +1 -1
  80. package/dist/utils/applescript.js +3 -6
  81. package/dist/utils/applescript.js.map +1 -1
  82. package/dist/utils/index.js +1 -0
  83. package/dist/utils/index.js.map +1 -1
  84. package/dist/utils/matching.js +21 -29
  85. package/dist/utils/matching.js.map +1 -1
  86. package/dist/utils/process.js +77 -56
  87. package/dist/utils/process.js.map +1 -1
  88. package/dist/utils/session.js +20 -34
  89. package/dist/utils/session.js.map +1 -1
  90. package/dist/utils/sortAgents.js +6 -8
  91. package/dist/utils/sortAgents.js.map +1 -1
  92. package/package.json +11 -7
  93. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +2 -0
  94. package/src/__tests__/adapters/CodexAdapter.test.ts +145 -0
  95. package/src/__tests__/adapters/CopilotAdapter.test.ts +411 -0
  96. package/src/__tests__/adapters/GeminiCliAdapter.test.ts +74 -1
  97. package/src/__tests__/adapters/PiAdapter.test.ts +435 -0
  98. package/src/adapters/AgentAdapter.ts +1 -1
  99. package/src/adapters/CodexAdapter.ts +164 -6
  100. package/src/adapters/CopilotAdapter.ts +453 -0
  101. package/src/adapters/GeminiCliAdapter.ts +42 -3
  102. package/src/adapters/PiAdapter.ts +597 -0
  103. package/src/adapters/index.ts +2 -0
  104. package/src/index.ts +2 -0
  105. package/src/utils/agents.ts +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/__tests__/adapters/OpenCodeAdapter.test.ts"],"sourcesContent":["/**\n * Tests for OpenCodeAdapter\n */\n\nimport type { MockedFunction } from 'vitest';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nimport { OpenCodeAdapter } from '../../adapters/OpenCodeAdapter.js';\nimport type { ProcessInfo } from '../../adapters/AgentAdapter.js';\nimport { AgentStatus } from '../../adapters/AgentAdapter.js';\nimport { listAgentProcesses, enrichProcesses } from '../../utils/process.js';\nimport { generateAgentName } from '../../utils/matching.js';\nimport * as os from 'os';\n\nvi.mock('../../utils/process.js', () => ({\n listAgentProcesses: vi.fn(),\n enrichProcesses: vi.fn(),\n}));\n\nvi.mock('../../utils/matching.js', () => ({\n generateAgentName: vi.fn(),\n matchProcessesToSessions: vi.fn(),\n}));\n\nconst mockedListAgentProcesses = listAgentProcesses as MockedFunction<typeof listAgentProcesses>;\nconst mockedEnrichProcesses = enrichProcesses as MockedFunction<typeof enrichProcesses>;\nconst mockedGenerateAgentName = generateAgentName as MockedFunction<typeof generateAgentName>;\n\nfunction makeDb(queries: {\n session?: Array<{ id: string; directory: string; time_created: number }>;\n lastMessage?: { role: string; timeUpdated: number } | null;\n lastAssistant?: { completed: number | null; errored: number | null } | null;\n firstUserText?: { text: string } | null;\n parts?: Array<{ role: string; partData: string; timeCreated: number }>;\n}) {\n const prepareImpl = (sql: string) => {\n const normalized = sql.replace(/\\s+/g, ' ').trim().toLowerCase();\n\n if (normalized.includes('from session')) {\n return {\n all: () => (queries.session ?? []).map((r) => ({\n id: r.id, directory: r.directory, timeCreated: r.time_created,\n })),\n get: (dir: string) => {\n const match = (queries.session ?? []).find((r) => r.directory === dir);\n if (!match) return undefined;\n return { id: match.id, directory: match.directory, time_created: match.time_created };\n },\n };\n }\n\n if (normalized.includes('max(time_updated)')) {\n return {\n get: () => ({ maxUpdated: queries.lastMessage?.timeUpdated ?? 0 }),\n };\n }\n\n if (normalized.includes('from message') && !normalized.includes(\"'$.time.completed'\") && !normalized.includes('order by p.time_created')) {\n return {\n get: () => queries.lastMessage ?? undefined,\n };\n }\n\n if (normalized.includes(\"'$.time.completed'\")) {\n return {\n get: () => queries.lastAssistant === undefined\n ? undefined\n : queries.lastAssistant ?? undefined,\n };\n }\n\n if (normalized.includes(\"json_extract(m.data, '$.role') = 'user'\")) {\n return {\n get: () => queries.firstUserText === undefined\n ? undefined\n : queries.firstUserText ?? undefined,\n };\n }\n\n if (normalized.includes('order by p.time_created asc')) {\n return {\n all: () => queries.parts ?? [],\n };\n }\n\n return { all: () => [], get: () => undefined };\n };\n\n return { prepare: prepareImpl, close: vi.fn() };\n}\n\nfunction makeDbConstructor(db: ReturnType<typeof makeDb>) {\n return vi.fn().mockReturnValue(db);\n}\n\ndescribe('OpenCodeAdapter', () => {\n let adapter: OpenCodeAdapter;\n let tmpDir: string;\n let dbPath: string;\n\n beforeEach(() => {\n adapter = new OpenCodeAdapter();\n\n mockedListAgentProcesses.mockReset();\n mockedEnrichProcesses.mockReset();\n mockedGenerateAgentName.mockReset();\n\n mockedEnrichProcesses.mockImplementation((procs) => procs);\n mockedGenerateAgentName.mockImplementation((cwd, pid) => {\n const folder = path.basename(cwd) || 'unknown';\n return `${folder}-${pid}`;\n });\n\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'opencode-test-'));\n dbPath = path.join(tmpDir, 'opencode.db');\n (adapter as any).dbPath = dbPath;\n (adapter as any).db = null;\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n vi.restoreAllMocks();\n });\n\n describe('type', () => {\n it('exposes opencode type', () => {\n expect(adapter.type).toBe('opencode');\n });\n });\n\n describe('canHandle', () => {\n it('returns true for opencode command', () => {\n expect(adapter.canHandle({ pid: 1, command: 'opencode', cwd: '/repo', tty: 'ttys001' })).toBe(true);\n });\n\n it('returns true for full path opencode', () => {\n expect(adapter.canHandle({ pid: 2, command: '/usr/local/bin/opencode serve', cwd: '/repo', tty: 'ttys002' })).toBe(true);\n });\n\n it('returns true for opencode.exe with unix-style path', () => {\n expect(adapter.canHandle({ pid: 3, command: '/usr/bin/opencode.exe', cwd: '/repo', tty: 'ttys003' })).toBe(true);\n });\n\n it('returns false for non-opencode processes', () => {\n expect(adapter.canHandle({ pid: 4, command: 'node server.js', cwd: '/repo', tty: 'ttys004' })).toBe(false);\n });\n\n it('returns false when opencode appears only in path args', () => {\n expect(adapter.canHandle({\n pid: 5,\n command: 'node /projects/opencode-plugin/index.js',\n cwd: '/repo',\n tty: 'ttys005',\n })).toBe(false);\n });\n });\n\n describe('detectAgents', () => {\n it('returns empty list when no opencode processes running', async () => {\n mockedListAgentProcesses.mockReturnValue([]);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toEqual([]);\n expect(mockedListAgentProcesses).toHaveBeenCalledWith('opencode');\n });\n\n it('returns process-only agent when DB does not exist', async () => {\n const procs: ProcessInfo[] = [\n { pid: 100, command: 'opencode', cwd: '/repo', tty: 'ttys001' },\n ];\n mockedListAgentProcesses.mockReturnValue(procs);\n mockedEnrichProcesses.mockReturnValue(procs);\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: 'opencode',\n status: AgentStatus.RUNNING,\n pid: 100,\n projectPath: '/repo',\n sessionId: 'pid-100',\n summary: 'OpenCode process running',\n });\n });\n\n it('returns process-only agent when no session matches CWD', async () => {\n const procs: ProcessInfo[] = [\n { pid: 100, command: 'opencode', cwd: '/repo', tty: 'ttys001' },\n ];\n mockedListAgentProcesses.mockReturnValue(procs);\n mockedEnrichProcesses.mockReturnValue(procs);\n\n fs.writeFileSync(dbPath, ''); // file exists but empty → sqlite throws\n const db = makeDb({ session: [] }); // no matching session\n vi.doMock('better-sqlite3', () => makeDbConstructor(db));\n (adapter as any).db = db;\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: 'opencode',\n status: AgentStatus.RUNNING,\n sessionId: 'pid-100',\n });\n });\n\n it('returns waiting when assistant turn has time.completed set', async () => {\n const now = Date.now();\n const procs: ProcessInfo[] = [\n { pid: 200, command: 'opencode', cwd: '/my-project', tty: 'ttys002' },\n ];\n mockedListAgentProcesses.mockReturnValue(procs);\n mockedEnrichProcesses.mockReturnValue(procs);\n\n const db = makeDb({\n session: [{ id: 'sess-001', directory: '/my-project', time_created: now - 60000 }],\n lastMessage: { role: 'assistant', timeUpdated: now - 60_000 },\n lastAssistant: { completed: now - 30_000, errored: null },\n firstUserText: { text: 'Refactor the auth module' },\n });\n (adapter as any).db = db;\n\n const agents = await adapter.detectAgents();\n\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: 'opencode',\n status: AgentStatus.WAITING,\n sessionId: 'sess-001',\n summary: 'Refactor the auth module',\n });\n });\n\n it('returns running when assistant turn has no time.completed (in-progress, any age)', async () => {\n const now = Date.now();\n const procs: ProcessInfo[] = [\n { pid: 250, command: 'opencode', cwd: '/proj', tty: 'ttys004' },\n ];\n mockedListAgentProcesses.mockReturnValue(procs);\n mockedEnrichProcesses.mockReturnValue(procs);\n\n const db = makeDb({\n session: [{ id: 'sess-tool', directory: '/proj', time_created: now - 180_000 }],\n lastMessage: { role: 'assistant', timeUpdated: now - 120_000 },\n lastAssistant: { completed: null, errored: null },\n firstUserText: { text: 'Run the build' },\n });\n (adapter as any).db = db;\n\n const agents = await adapter.detectAgents();\n\n expect(agents[0]).toMatchObject({\n status: AgentStatus.RUNNING,\n sessionId: 'sess-tool',\n });\n });\n\n it('returns running between steps (the bug fix) — no time.completed even during quiet moment', async () => {\n const now = Date.now();\n const procs: ProcessInfo[] = [\n { pid: 260, command: 'opencode', cwd: '/proj-b', tty: 'ttys005' },\n ];\n mockedListAgentProcesses.mockReturnValue(procs);\n mockedEnrichProcesses.mockReturnValue(procs);\n\n const db = makeDb({\n session: [{ id: 'sess-mid', directory: '/proj-b', time_created: now - 60_000 }],\n lastMessage: { role: 'assistant', timeUpdated: now - 45_000 },\n lastAssistant: { completed: null, errored: null },\n firstUserText: null,\n });\n (adapter as any).db = db;\n\n const agents = await adapter.detectAgents();\n expect(agents[0].status).toBe(AgentStatus.RUNNING);\n });\n\n it('returns waiting even when latest user message was metadata-updated after assistant completion', async () => {\n // Regression: OpenCode updates user.message.time_updated when appending\n // summary diffs after a turn finishes. Ordering by time_created (not\n // time_updated) keeps the assistant message correctly identified as latest.\n const now = Date.now();\n const procs: ProcessInfo[] = [\n { pid: 270, command: 'opencode', cwd: '/proj-c', tty: 'ttys006' },\n ];\n mockedListAgentProcesses.mockReturnValue(procs);\n mockedEnrichProcesses.mockReturnValue(procs);\n\n // makeDb returns lastMessage from the time_created-ordered query — supply the assistant.\n const db = makeDb({\n session: [{ id: 'sess-meta', directory: '/proj-c', time_created: now - 120_000 }],\n lastMessage: { role: 'assistant', timeUpdated: now - 30_000 },\n lastAssistant: { completed: now - 30_000, errored: null },\n firstUserText: null,\n });\n (adapter as any).db = db;\n\n const agents = await adapter.detectAgents();\n expect(agents[0].status).toBe(AgentStatus.WAITING);\n });\n\n it('returns running agent when last role is user (no assistant message yet)', async () => {\n const now = Date.now();\n const procs: ProcessInfo[] = [\n { pid: 300, command: 'opencode', cwd: '/work', tty: 'ttys003' },\n ];\n mockedListAgentProcesses.mockReturnValue(procs);\n mockedEnrichProcesses.mockReturnValue(procs);\n\n const db = makeDb({\n session: [{ id: 'sess-002', directory: '/work', time_created: now - 30000 }],\n lastMessage: { role: 'user', timeUpdated: now - 30_000 },\n lastAssistant: null,\n firstUserText: { text: 'Add unit tests' },\n });\n (adapter as any).db = db;\n\n const agents = await adapter.detectAgents();\n\n expect(agents[0]).toMatchObject({\n status: AgentStatus.RUNNING,\n sessionId: 'sess-002',\n });\n });\n\n it('returns idle agent when last activity exceeds threshold', async () => {\n const now = Date.now();\n const staleTime = now - 10 * 60 * 1000; // 10 minutes ago\n const procs: ProcessInfo[] = [\n { pid: 400, command: 'opencode', cwd: '/old-work', tty: 'ttys004' },\n ];\n mockedListAgentProcesses.mockReturnValue(procs);\n mockedEnrichProcesses.mockReturnValue(procs);\n\n const db = makeDb({\n session: [{ id: 'sess-003', directory: '/old-work', time_created: staleTime }],\n lastMessage: { role: 'assistant', timeUpdated: staleTime },\n lastAssistant: { completed: staleTime, errored: null },\n firstUserText: null,\n });\n (adapter as any).db = db;\n\n const agents = await adapter.detectAgents();\n\n expect(agents[0]).toMatchObject({\n status: AgentStatus.IDLE,\n sessionId: 'sess-003',\n });\n });\n });\n\n describe('getConversation', () => {\n it('returns empty array for invalid session ref', () => {\n const messages = adapter.getConversation('/no-separator-here');\n expect(messages).toEqual([]);\n });\n\n it('returns text messages from session parts', () => {\n const db = makeDb({\n parts: [\n { role: 'user', partData: JSON.stringify({ type: 'text', text: 'Hello agent' }), timeCreated: 1000 },\n { role: 'assistant', partData: JSON.stringify({ type: 'text', text: 'Hi, how can I help?' }), timeCreated: 2000 },\n ],\n });\n (adapter as any).db = db;\n\n const ref = `${dbPath}::sess-abc`;\n const messages = adapter.getConversation(ref);\n\n expect(messages).toHaveLength(2);\n expect(messages[0]).toEqual({ role: 'user', content: 'Hello agent' });\n expect(messages[1]).toEqual({ role: 'assistant', content: 'Hi, how can I help?' });\n });\n\n it('skips reasoning parts when verbose is false', () => {\n const db = makeDb({\n parts: [\n { role: 'assistant', partData: JSON.stringify({ type: 'reasoning', reasoning: 'internal thought' }), timeCreated: 1000 },\n { role: 'assistant', partData: JSON.stringify({ type: 'text', text: 'Answer' }), timeCreated: 2000 },\n ],\n });\n (adapter as any).db = db;\n\n const messages = adapter.getConversation(`${dbPath}::sess-x`);\n expect(messages).toHaveLength(1);\n expect(messages[0].content).toBe('Answer');\n });\n\n it('includes reasoning and tool parts when verbose is true', () => {\n const db = makeDb({\n parts: [\n { role: 'assistant', partData: JSON.stringify({ type: 'reasoning', reasoning: 'my thinking' }), timeCreated: 1000 },\n { role: 'assistant', partData: JSON.stringify({ type: 'tool', tool: 'read_file' }), timeCreated: 2000 },\n ],\n });\n (adapter as any).db = db;\n\n const messages = adapter.getConversation(`${dbPath}::sess-y`, { verbose: true });\n expect(messages).toHaveLength(2);\n expect(messages[0].content).toContain('my thinking');\n expect(messages[1].content).toContain('read_file');\n });\n\n it('returns empty array when DB cannot be opened', () => {\n (adapter as any).db = null;\n const messages = adapter.getConversation(`${dbPath}::sess-z`);\n expect(messages).toEqual([]);\n });\n });\n\n describe('listSessions', () => {\n it('returns empty array when DB does not exist', async () => {\n const sessions = await adapter.listSessions();\n expect(sessions).toEqual([]);\n });\n\n it('returns all sessions from DB', async () => {\n const now = Date.now();\n const db = makeDb({\n session: [\n { id: 'sess-a', directory: '/proj-a', time_created: now - 3000 },\n { id: 'sess-b', directory: '/proj-b', time_created: now - 6000 },\n ],\n lastMessage: null,\n lastAssistant: null,\n firstUserText: { text: 'Build the feature' },\n });\n (adapter as any).db = db;\n\n const sessions = await adapter.listSessions();\n\n expect(sessions).toHaveLength(2);\n expect(sessions[0]).toMatchObject({ type: 'opencode', sessionId: 'sess-a', cwd: '/proj-a' });\n expect(sessions[1]).toMatchObject({ type: 'opencode', sessionId: 'sess-b', cwd: '/proj-b' });\n expect(sessions[0].sessionFilePath).toContain('sess-a');\n });\n\n it('filters sessions by cwd when opts.cwd is set', async () => {\n const now = Date.now();\n const db = makeDb({\n session: [\n { id: 'sess-a', directory: '/proj-a', time_created: now - 3000 },\n { id: 'sess-b', directory: '/proj-b', time_created: now - 6000 },\n ],\n lastMessage: null,\n lastAssistant: null,\n firstUserText: null,\n });\n (adapter as any).db = db;\n\n const sessions = await adapter.listSessions({ cwd: '/proj-a' });\n\n expect(sessions).toHaveLength(1);\n expect(sessions[0].sessionId).toBe('sess-a');\n });\n\n it('uses session time_created as startedAt and lastActive when no parts exist', async () => {\n const timeCreated = Date.now() - 120000;\n const db = makeDb({\n session: [{ id: 'sess-c', directory: '/repo', time_created: timeCreated }],\n lastMessage: null,\n lastAssistant: null,\n firstUserText: null,\n });\n (adapter as any).db = db;\n\n const [session] = await adapter.listSessions();\n\n expect(session.startedAt.getTime()).toBeCloseTo(timeCreated, -2);\n expect(session.lastActive.getTime()).toBeCloseTo(timeCreated, -2);\n });\n });\n});\n"],"names":["fs","path","OpenCodeAdapter","AgentStatus","listAgentProcesses","enrichProcesses","generateAgentName","os","vi","mock","fn","matchProcessesToSessions","mockedListAgentProcesses","mockedEnrichProcesses","mockedGenerateAgentName","makeDb","queries","prepareImpl","sql","normalized","replace","trim","toLowerCase","includes","all","session","map","r","id","directory","timeCreated","time_created","get","dir","match","find","undefined","maxUpdated","lastMessage","timeUpdated","lastAssistant","firstUserText","parts","prepare","close","makeDbConstructor","db","mockReturnValue","describe","adapter","tmpDir","dbPath","beforeEach","mockReset","mockImplementation","procs","cwd","pid","folder","basename","mkdtempSync","join","tmpdir","afterEach","rmSync","recursive","force","restoreAllMocks","it","expect","type","toBe","canHandle","command","tty","agents","detectAgents","toEqual","toHaveBeenCalledWith","toHaveLength","toMatchObject","status","RUNNING","projectPath","sessionId","summary","writeFileSync","doMock","now","Date","role","completed","errored","text","WAITING","staleTime","IDLE","messages","getConversation","partData","JSON","stringify","ref","content","reasoning","tool","verbose","toContain","sessions","listSessions","sessionFilePath","startedAt","getTime","toBeCloseTo","lastActive"],"mappings":"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"}
@@ -0,0 +1,590 @@
1
+ /**
2
+ * Tests for PiAdapter
3
+ */ import * as fs from 'fs';
4
+ import * as os from 'os';
5
+ import * as path from 'path';
6
+ import { PiAdapter } from '../../adapters/PiAdapter.js';
7
+ import { AgentStatus } from '../../adapters/AgentAdapter.js';
8
+ import { AgentRegistry } from '../../utils/AgentRegistry.js';
9
+ import { listAgentProcesses, enrichProcesses } from '../../utils/process.js';
10
+ import { matchProcessesToSessions, generateAgentName } from '../../utils/matching.js';
11
+ vi.mock('../../utils/process.js', ()=>({
12
+ listAgentProcesses: vi.fn(),
13
+ enrichProcesses: vi.fn()
14
+ }));
15
+ vi.mock('../../utils/matching.js', ()=>({
16
+ matchProcessesToSessions: vi.fn(),
17
+ generateAgentName: vi.fn()
18
+ }));
19
+ const mockedListAgentProcesses = listAgentProcesses;
20
+ const mockedEnrichProcesses = enrichProcesses;
21
+ const mockedMatchProcessesToSessions = matchProcessesToSessions;
22
+ const mockedGenerateAgentName = generateAgentName;
23
+ describe('PiAdapter', ()=>{
24
+ let adapter;
25
+ let tmpHome;
26
+ let sessionsDir;
27
+ beforeEach(()=>{
28
+ tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'pi-adapter-test-'));
29
+ process.env.HOME = tmpHome;
30
+ sessionsDir = path.join(tmpHome, '.pi', 'agent', 'sessions');
31
+ fs.mkdirSync(sessionsDir, {
32
+ recursive: true
33
+ });
34
+ adapter = new PiAdapter(new AgentRegistry(path.join(tmpHome, 'agents.json')));
35
+ mockedListAgentProcesses.mockReset();
36
+ mockedEnrichProcesses.mockReset();
37
+ mockedMatchProcessesToSessions.mockReset();
38
+ mockedGenerateAgentName.mockReset();
39
+ mockedEnrichProcesses.mockImplementation((procs)=>procs);
40
+ mockedMatchProcessesToSessions.mockReturnValue([]);
41
+ mockedGenerateAgentName.mockImplementation((cwd, pid)=>{
42
+ const folder = path.basename(cwd) || 'unknown';
43
+ return `${folder} (${pid})`;
44
+ });
45
+ });
46
+ afterEach(()=>{
47
+ fs.rmSync(tmpHome, {
48
+ recursive: true,
49
+ force: true
50
+ });
51
+ });
52
+ it('exposes pi type', ()=>{
53
+ expect(adapter.type).toBe('pi');
54
+ });
55
+ it('identifies Pi commands without matching unrelated paths', ()=>{
56
+ expect(adapter.canHandle({
57
+ pid: 1,
58
+ command: 'pi',
59
+ cwd: '/repo',
60
+ tty: 'ttys001'
61
+ })).toBe(true);
62
+ expect(adapter.canHandle({
63
+ pid: 2,
64
+ command: '/usr/local/bin/PI --model x',
65
+ cwd: '/repo',
66
+ tty: 'ttys002'
67
+ })).toBe(true);
68
+ expect(adapter.canHandle({
69
+ pid: 3,
70
+ command: 'node /opt/pi/bin/pi.js',
71
+ cwd: '/repo',
72
+ tty: 'ttys003'
73
+ })).toBe(true);
74
+ expect(adapter.canHandle({
75
+ pid: 4,
76
+ command: 'node /repo/feature-pi-adapter/script.js',
77
+ cwd: '/repo',
78
+ tty: 'ttys004'
79
+ })).toBe(false);
80
+ });
81
+ it('maps a running Pi process to the tracker session for its PID', async ()=>{
82
+ const cwd = '/repo/project-a';
83
+ const proc = makeProcess({
84
+ pid: 101,
85
+ cwd
86
+ });
87
+ const sessionFile = writePiSession(cwd, [
88
+ {
89
+ type: 'session_meta',
90
+ timestamp: '2026-06-10T08:58:20.754Z',
91
+ sessionId: 'sess-101',
92
+ cwd
93
+ },
94
+ {
95
+ role: 'user',
96
+ timestamp: '2026-06-10T08:58:21.000Z',
97
+ content: 'implement Pi adapter'
98
+ },
99
+ {
100
+ role: 'assistant',
101
+ timestamp: new Date().toISOString(),
102
+ content: 'working on it'
103
+ }
104
+ ]);
105
+ fs.writeFileSync(path.join(tmpHome, '.pi', 'agent', 'sessions.json'), JSON.stringify({
106
+ 101: sessionFile
107
+ }));
108
+ mockedListAgentProcesses.mockReturnValue([
109
+ proc
110
+ ]);
111
+ const agents = await adapter.detectAgents();
112
+ expect(agents).toHaveLength(1);
113
+ expect(agents[0]).toMatchObject({
114
+ type: 'pi',
115
+ pid: 101,
116
+ projectPath: cwd,
117
+ sessionId: 'sess-101',
118
+ summary: 'implement Pi adapter',
119
+ status: AgentStatus.WAITING,
120
+ sessionFilePath: sessionFile
121
+ });
122
+ expect(mockedMatchProcessesToSessions).not.toHaveBeenCalled();
123
+ });
124
+ it('truncates long user prompts in detected agent summaries', async ()=>{
125
+ const cwd = '/repo/project-long-summary';
126
+ const proc = makeProcess({
127
+ pid: 112,
128
+ cwd
129
+ });
130
+ const longPrompt = 'x'.repeat(140);
131
+ const sessionFile = writePiSession(cwd, [
132
+ {
133
+ type: 'session',
134
+ timestamp: '2026-06-10T08:58:20.754Z',
135
+ id: 'sess-long',
136
+ cwd
137
+ },
138
+ {
139
+ role: 'user',
140
+ timestamp: '2026-06-10T08:58:21.000Z',
141
+ content: longPrompt
142
+ }
143
+ ]);
144
+ fs.writeFileSync(path.join(tmpHome, '.pi', 'agent', 'sessions.json'), JSON.stringify({
145
+ 112: sessionFile
146
+ }));
147
+ mockedListAgentProcesses.mockReturnValue([
148
+ proc
149
+ ]);
150
+ const agents = await adapter.detectAgents();
151
+ expect(agents[0].summary).toHaveLength(120);
152
+ expect(agents[0].summary.endsWith('...')).toBe(true);
153
+ });
154
+ it('uses the filename session id fallback and reports running when the latest message is from the user', async ()=>{
155
+ const cwd = '/repo/project-filename-fallback';
156
+ const proc = makeProcess({
157
+ pid: 113,
158
+ cwd
159
+ });
160
+ const sessionFile = writePiSessionWithFileName(cwd, 'plain-session.jsonl', [
161
+ {
162
+ role: 'user',
163
+ timestamp: new Date().toISOString(),
164
+ content: 'still working'
165
+ }
166
+ ]);
167
+ fs.writeFileSync(path.join(tmpHome, '.pi', 'agent', 'sessions.json'), JSON.stringify({
168
+ 113: sessionFile
169
+ }));
170
+ mockedListAgentProcesses.mockReturnValue([
171
+ proc
172
+ ]);
173
+ const agents = await adapter.detectAgents();
174
+ expect(agents[0]).toMatchObject({
175
+ sessionId: 'plain-session',
176
+ summary: 'still working',
177
+ status: AgentStatus.RUNNING
178
+ });
179
+ });
180
+ it('falls back to legacy matching when sessions.json is missing', async ()=>{
181
+ const cwd = '/repo/project-b';
182
+ const proc = makeProcess({
183
+ pid: 202,
184
+ cwd
185
+ });
186
+ const sessionFile = writePiSession(cwd, [
187
+ {
188
+ timestamp: '2026-06-10T08:58:20.754Z',
189
+ sessionId: 'sess-202'
190
+ },
191
+ {
192
+ role: 'user',
193
+ timestamp: '2026-06-10T08:58:21.000Z',
194
+ content: 'fallback matching please'
195
+ }
196
+ ]);
197
+ mockedListAgentProcesses.mockReturnValue([
198
+ proc
199
+ ]);
200
+ mockedMatchProcessesToSessions.mockReturnValue([
201
+ {
202
+ process: proc,
203
+ session: {
204
+ sessionId: 'sess-202',
205
+ filePath: sessionFile,
206
+ projectDir: path.dirname(sessionFile),
207
+ birthtimeMs: Date.now(),
208
+ resolvedCwd: cwd
209
+ },
210
+ deltaMs: 0
211
+ }
212
+ ]);
213
+ const agents = await adapter.detectAgents();
214
+ expect(agents).toHaveLength(1);
215
+ expect(agents[0]).toMatchObject({
216
+ type: 'pi',
217
+ pid: 202,
218
+ projectPath: cwd,
219
+ sessionId: 'sess-202',
220
+ summary: 'fallback matching please'
221
+ });
222
+ expect(mockedMatchProcessesToSessions).toHaveBeenCalledWith([
223
+ proc
224
+ ], expect.arrayContaining([
225
+ expect.objectContaining({
226
+ filePath: sessionFile,
227
+ resolvedCwd: cwd
228
+ })
229
+ ]));
230
+ });
231
+ it('ignores malformed tracker metadata and still falls back to legacy matching', async ()=>{
232
+ const cwd = '/repo/project-c';
233
+ const proc = makeProcess({
234
+ pid: 303,
235
+ cwd
236
+ });
237
+ const sessionFile = writePiSession(cwd, [
238
+ {
239
+ timestamp: '2026-06-10T08:58:20.754Z',
240
+ sessionId: 'sess-303',
241
+ cwd
242
+ },
243
+ {
244
+ role: 'user',
245
+ timestamp: '2026-06-10T08:58:21.000Z',
246
+ content: 'recover from bad tracker'
247
+ }
248
+ ]);
249
+ fs.writeFileSync(path.join(tmpHome, '.pi', 'agent', 'sessions.json'), '{bad json');
250
+ mockedListAgentProcesses.mockReturnValue([
251
+ proc
252
+ ]);
253
+ mockedMatchProcessesToSessions.mockReturnValue([
254
+ {
255
+ process: proc,
256
+ session: {
257
+ sessionId: 'sess-303',
258
+ filePath: sessionFile,
259
+ projectDir: path.dirname(sessionFile),
260
+ birthtimeMs: Date.now(),
261
+ resolvedCwd: cwd
262
+ },
263
+ deltaMs: 0
264
+ }
265
+ ]);
266
+ const agents = await adapter.detectAgents();
267
+ expect(agents).toHaveLength(1);
268
+ expect(agents[0].sessionId).toBe('sess-303');
269
+ });
270
+ it('falls back to legacy matching when a trusted tracker session is unparseable', async ()=>{
271
+ const cwd = '/repo/project-bad-tracker-session';
272
+ const proc = makeProcess({
273
+ pid: 304,
274
+ cwd
275
+ });
276
+ const badSessionFile = writePiSessionWithFileName(cwd, 'bad.jsonl', [
277
+ '{not json'
278
+ ]);
279
+ const fallbackSessionFile = writePiSession(cwd, [
280
+ {
281
+ timestamp: '2026-06-10T08:58:20.754Z',
282
+ sessionId: 'sess-304',
283
+ cwd
284
+ },
285
+ {
286
+ role: 'user',
287
+ timestamp: '2026-06-10T08:58:21.000Z',
288
+ content: 'fallback after bad tracker session'
289
+ }
290
+ ]);
291
+ fs.writeFileSync(path.join(tmpHome, '.pi', 'agent', 'sessions.json'), JSON.stringify({
292
+ 304: badSessionFile
293
+ }));
294
+ mockedListAgentProcesses.mockReturnValue([
295
+ proc
296
+ ]);
297
+ mockedMatchProcessesToSessions.mockReturnValue([
298
+ {
299
+ process: proc,
300
+ session: {
301
+ sessionId: 'sess-304',
302
+ filePath: fallbackSessionFile,
303
+ projectDir: path.dirname(fallbackSessionFile),
304
+ birthtimeMs: Date.now(),
305
+ resolvedCwd: cwd
306
+ },
307
+ deltaMs: 0
308
+ }
309
+ ]);
310
+ const agents = await adapter.detectAgents();
311
+ expect(agents).toHaveLength(1);
312
+ expect(agents[0]).toMatchObject({
313
+ sessionId: 'sess-304',
314
+ summary: 'fallback after bad tracker session',
315
+ sessionFilePath: fallbackSessionFile
316
+ });
317
+ expect(mockedMatchProcessesToSessions).toHaveBeenCalled();
318
+ });
319
+ it('does not trust tracker paths outside the Pi sessions directory', async ()=>{
320
+ const cwd = '/repo/project-d';
321
+ const proc = makeProcess({
322
+ pid: 404,
323
+ cwd
324
+ });
325
+ const outside = path.join(tmpHome, 'outside.jsonl');
326
+ fs.writeFileSync(outside, JSON.stringify({
327
+ role: 'user',
328
+ content: 'nope'
329
+ }));
330
+ fs.writeFileSync(path.join(tmpHome, '.pi', 'agent', 'sessions.json'), JSON.stringify({
331
+ 404: outside
332
+ }));
333
+ mockedListAgentProcesses.mockReturnValue([
334
+ proc
335
+ ]);
336
+ const agents = await adapter.detectAgents();
337
+ expect(agents).toHaveLength(1);
338
+ expect(agents[0]).toMatchObject({
339
+ type: 'pi',
340
+ pid: 404,
341
+ sessionId: 'pid-404',
342
+ summary: 'Pi process running'
343
+ });
344
+ });
345
+ it('returns a process-only agent when no session can be matched', async ()=>{
346
+ const proc = makeProcess({
347
+ pid: 505,
348
+ cwd: '/repo/project-e'
349
+ });
350
+ mockedListAgentProcesses.mockReturnValue([
351
+ proc
352
+ ]);
353
+ const agents = await adapter.detectAgents();
354
+ expect(agents).toHaveLength(1);
355
+ expect(agents[0]).toMatchObject({
356
+ type: 'pi',
357
+ status: AgentStatus.RUNNING,
358
+ pid: 505,
359
+ projectPath: '/repo/project-e',
360
+ sessionId: 'pid-505',
361
+ summary: 'Pi process running'
362
+ });
363
+ });
364
+ it('reads user and assistant conversation messages from JSONL', ()=>{
365
+ const cwd = '/repo/project-f';
366
+ const sessionFile = writePiSession(cwd, [
367
+ {
368
+ role: 'system',
369
+ timestamp: '2026-06-10T08:58:20.000Z',
370
+ content: 'hidden'
371
+ },
372
+ {
373
+ role: 'user',
374
+ timestamp: '2026-06-10T08:58:21.000Z',
375
+ content: 'hello pi'
376
+ },
377
+ {
378
+ type: 'assistant',
379
+ timestamp: '2026-06-10T08:58:22.000Z',
380
+ message: {
381
+ content: 'hello human'
382
+ }
383
+ },
384
+ '{not json'
385
+ ]);
386
+ expect(adapter.getConversation(sessionFile)).toEqual([
387
+ {
388
+ role: 'user',
389
+ content: 'hello pi',
390
+ timestamp: '2026-06-10T08:58:21.000Z'
391
+ },
392
+ {
393
+ role: 'assistant',
394
+ content: 'hello human',
395
+ timestamp: '2026-06-10T08:58:22.000Z'
396
+ }
397
+ ]);
398
+ });
399
+ it('includes system entries only in verbose conversation mode', ()=>{
400
+ const cwd = '/repo/project-verbose';
401
+ const sessionFile = writePiSession(cwd, [
402
+ {
403
+ role: 'system',
404
+ timestamp: '2026-06-10T08:58:20.000Z',
405
+ content: 'model changed'
406
+ },
407
+ {
408
+ role: 'user',
409
+ timestamp: '2026-06-10T08:58:21.000Z',
410
+ content: 'visible'
411
+ }
412
+ ]);
413
+ expect(adapter.getConversation(sessionFile)).toEqual([
414
+ {
415
+ role: 'user',
416
+ content: 'visible',
417
+ timestamp: '2026-06-10T08:58:21.000Z'
418
+ }
419
+ ]);
420
+ expect(adapter.getConversation(sessionFile, {
421
+ verbose: true
422
+ })).toEqual([
423
+ {
424
+ role: 'system',
425
+ content: 'model changed',
426
+ timestamp: '2026-06-10T08:58:20.000Z'
427
+ },
428
+ {
429
+ role: 'user',
430
+ content: 'visible',
431
+ timestamp: '2026-06-10T08:58:21.000Z'
432
+ }
433
+ ]);
434
+ });
435
+ it('reads real Pi message entries with nested role and text parts', async ()=>{
436
+ const cwd = '/repo/project-real';
437
+ const proc = makeProcess({
438
+ pid: 606,
439
+ cwd
440
+ });
441
+ const sessionFile = writePiSession(cwd, [
442
+ {
443
+ type: 'session',
444
+ version: 3,
445
+ id: 'sess-real',
446
+ timestamp: '2026-06-10T13:27:17.581Z',
447
+ cwd
448
+ },
449
+ {
450
+ type: 'model_change',
451
+ id: 'model-1',
452
+ timestamp: '2026-06-10T13:27:17.655Z',
453
+ modelId: 'claude-sonnet-4-6'
454
+ },
455
+ {
456
+ type: 'message',
457
+ id: 'msg-user',
458
+ timestamp: '2026-06-10T13:27:37.975Z',
459
+ message: {
460
+ role: 'user',
461
+ content: [
462
+ {
463
+ type: 'text',
464
+ text: 'hello'
465
+ }
466
+ ],
467
+ timestamp: 1781098057974
468
+ }
469
+ },
470
+ {
471
+ type: 'message',
472
+ id: 'msg-assistant',
473
+ timestamp: '2026-06-10T13:27:40.161Z',
474
+ message: {
475
+ role: 'assistant',
476
+ content: [
477
+ {
478
+ type: 'text',
479
+ text: 'Hello! How can I help you today?'
480
+ }
481
+ ],
482
+ provider: 'anthropic',
483
+ model: 'claude-sonnet-4-6',
484
+ timestamp: 1781098058012
485
+ }
486
+ }
487
+ ]);
488
+ fs.writeFileSync(path.join(tmpHome, '.pi', 'agent', 'sessions.json'), JSON.stringify({
489
+ 606: sessionFile
490
+ }));
491
+ mockedListAgentProcesses.mockReturnValue([
492
+ proc
493
+ ]);
494
+ expect(adapter.getConversation(sessionFile)).toEqual([
495
+ {
496
+ role: 'user',
497
+ content: 'hello',
498
+ timestamp: '2026-06-10T13:27:37.975Z'
499
+ },
500
+ {
501
+ role: 'assistant',
502
+ content: 'Hello! How can I help you today?',
503
+ timestamp: '2026-06-10T13:27:40.161Z'
504
+ }
505
+ ]);
506
+ const agents = await adapter.detectAgents();
507
+ expect(agents[0]).toMatchObject({
508
+ sessionId: 'sess-real',
509
+ summary: 'hello',
510
+ lastActive: new Date('2026-06-10T13:27:40.161Z')
511
+ });
512
+ });
513
+ it('lists historical sessions and applies cwd filtering', async ()=>{
514
+ const matchingCwd = '/repo/project-g';
515
+ const otherCwd = '/repo/project-h';
516
+ const matchingSession = writePiSession(matchingCwd, [
517
+ {
518
+ timestamp: '2026-06-10T08:58:20.754Z',
519
+ sessionId: 'sess-g',
520
+ cwd: matchingCwd
521
+ },
522
+ {
523
+ role: 'user',
524
+ timestamp: '2026-06-10T08:58:21.000Z',
525
+ content: 'first matching message'
526
+ }
527
+ ]);
528
+ writePiSession(otherCwd, [
529
+ {
530
+ timestamp: '2026-06-10T08:58:20.754Z',
531
+ sessionId: 'sess-h',
532
+ cwd: otherCwd
533
+ },
534
+ {
535
+ role: 'user',
536
+ timestamp: '2026-06-10T08:58:21.000Z',
537
+ content: 'other message'
538
+ }
539
+ ]);
540
+ const sessions = await adapter.listSessions({
541
+ cwd: matchingCwd
542
+ });
543
+ expect(sessions).toEqual([
544
+ expect.objectContaining({
545
+ type: 'pi',
546
+ sessionId: 'sess-g',
547
+ cwd: matchingCwd,
548
+ firstUserMessage: 'first matching message',
549
+ sessionFilePath: matchingSession
550
+ })
551
+ ]);
552
+ });
553
+ function makeProcess(overrides) {
554
+ return {
555
+ pid: 1,
556
+ command: 'pi',
557
+ cwd: '/repo',
558
+ tty: 'ttys001',
559
+ startTime: new Date('2026-06-10T08:58:20.000Z'),
560
+ ...overrides
561
+ };
562
+ }
563
+ function writePiSession(cwd, entries) {
564
+ const projectDir = path.join(sessionsDir, encodeProjectDir(cwd));
565
+ fs.mkdirSync(projectDir, {
566
+ recursive: true
567
+ });
568
+ const sessionId = entries.map((entry)=>typeof entry === 'string' ? undefined : entry.sessionId).find((value)=>typeof value === 'string') ?? cryptoRandomSessionId();
569
+ const filePath = path.join(projectDir, `2026-06-10T08-58-20-754Z_${sessionId}.jsonl`);
570
+ fs.writeFileSync(filePath, entries.map((entry)=>typeof entry === 'string' ? entry : JSON.stringify(entry)).join('\n'));
571
+ return filePath;
572
+ }
573
+ function writePiSessionWithFileName(cwd, fileName, entries) {
574
+ const projectDir = path.join(sessionsDir, encodeProjectDir(cwd));
575
+ fs.mkdirSync(projectDir, {
576
+ recursive: true
577
+ });
578
+ const filePath = path.join(projectDir, fileName);
579
+ fs.writeFileSync(filePath, entries.map((entry)=>typeof entry === 'string' ? entry : JSON.stringify(entry)).join('\n'));
580
+ return filePath;
581
+ }
582
+ function encodeProjectDir(cwd) {
583
+ return cwd.replace(/\//g, '-').replace(/^-?/, '--') + '--';
584
+ }
585
+ function cryptoRandomSessionId() {
586
+ return `019eb0c1-06d2-71ed-90ee-${Math.random().toString(16).slice(2, 14).padEnd(12, '0')}`;
587
+ }
588
+ });
589
+
590
+ //# sourceMappingURL=PiAdapter.test.js.map