@ai-devkit/agent-manager 0.33.1 → 0.34.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/__tests__/capacity/codex.test.js +2 -1
- package/dist/__tests__/capacity/codex.test.js.map +1 -1
- package/dist/__tests__/capacity/index.test.js +6 -3
- package/dist/__tests__/capacity/index.test.js.map +1 -1
- package/dist/__tests__/capacity/zai.test.js +213 -0
- package/dist/__tests__/capacity/zai.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js +189 -0
- package/dist/__tests__/providers/opencode/OpenCodeAdapter.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeAgentMapper.test.js +89 -0
- package/dist/__tests__/providers/opencode/OpenCodeAgentMapper.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeSessionLocator.test.js +94 -0
- package/dist/__tests__/providers/opencode/OpenCodeSessionLocator.test.js.map +1 -0
- package/dist/__tests__/providers/opencode/OpenCodeSessionParser.test.js +147 -0
- package/dist/__tests__/providers/opencode/OpenCodeSessionParser.test.js.map +1 -0
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +1 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/capacity/codex.d.ts +1 -0
- package/dist/capacity/codex.d.ts.map +1 -1
- package/dist/capacity/codex.js +9 -4
- package/dist/capacity/codex.js.map +1 -1
- package/dist/capacity/index.d.ts +8 -0
- package/dist/capacity/index.d.ts.map +1 -1
- package/dist/capacity/index.js +10 -9
- package/dist/capacity/index.js.map +1 -1
- package/dist/capacity/types.d.ts +8 -0
- package/dist/capacity/types.d.ts.map +1 -1
- package/dist/capacity/types.js.map +1 -1
- package/dist/capacity/zai.d.ts +14 -0
- package/dist/capacity/zai.d.ts.map +1 -0
- package/dist/capacity/zai.js +203 -0
- package/dist/capacity/zai.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/{adapters → providers/opencode}/OpenCodeAdapter.d.ts +5 -12
- package/dist/providers/opencode/OpenCodeAdapter.d.ts.map +1 -0
- package/dist/providers/opencode/OpenCodeAdapter.js +86 -0
- package/dist/providers/opencode/OpenCodeAdapter.js.map +1 -0
- package/dist/providers/opencode/OpenCodeAgentMapper.d.ts +11 -0
- package/dist/providers/opencode/OpenCodeAgentMapper.d.ts.map +1 -0
- package/dist/providers/opencode/OpenCodeAgentMapper.js +50 -0
- package/dist/providers/opencode/OpenCodeAgentMapper.js.map +1 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.d.ts +21 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.d.ts.map +1 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.js +99 -0
- package/dist/providers/opencode/OpenCodeSessionLocator.js.map +1 -0
- package/dist/providers/opencode/OpenCodeSessionParser.d.ts +18 -0
- package/dist/providers/opencode/OpenCodeSessionParser.d.ts.map +1 -0
- package/dist/providers/opencode/OpenCodeSessionParser.js +104 -0
- package/dist/providers/opencode/OpenCodeSessionParser.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/capacity/codex.test.ts +94 -26
- package/src/__tests__/capacity/fixtures/zai-auth.json +6 -0
- package/src/__tests__/capacity/fixtures/zai-quota-invalid.json +7 -0
- package/src/__tests__/capacity/fixtures/zai-quota.json +38 -0
- package/src/__tests__/capacity/index.test.ts +10 -3
- package/src/__tests__/capacity/zai.test.ts +186 -0
- package/src/__tests__/providers/opencode/OpenCodeAdapter.test.ts +197 -0
- package/src/__tests__/providers/opencode/OpenCodeAgentMapper.test.ts +83 -0
- package/src/__tests__/providers/opencode/OpenCodeSessionLocator.test.ts +84 -0
- package/src/__tests__/providers/opencode/OpenCodeSessionParser.test.ts +148 -0
- package/src/adapters/index.ts +1 -1
- package/src/capacity/codex.ts +96 -28
- package/src/capacity/index.ts +29 -11
- package/src/capacity/types.ts +8 -0
- package/src/capacity/zai.ts +237 -0
- package/src/index.ts +8 -3
- package/src/providers/opencode/OpenCodeAdapter.ts +112 -0
- package/src/providers/opencode/OpenCodeAgentMapper.ts +61 -0
- package/src/providers/opencode/OpenCodeSessionLocator.ts +123 -0
- package/src/providers/opencode/OpenCodeSessionParser.ts +145 -0
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js +0 -636
- package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +0 -1
- package/dist/adapters/OpenCodeAdapter.d.ts.map +0 -1
- package/dist/adapters/OpenCodeAdapter.js +0 -289
- package/dist/adapters/OpenCodeAdapter.js.map +0 -1
- package/src/__tests__/adapters/OpenCodeAdapter.test.ts +0 -544
- package/src/adapters/OpenCodeAdapter.ts +0 -373
|
@@ -1 +0,0 @@
|
|
|
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 {\n listAgentProcesses,\n enrichProcesses,\n captureProcessSnapshot,\n} from \"../../utils/process.js\";\nimport { generateAgentName } from \"../../utils/matching.js\";\nimport * as os from \"os\";\n\nvi.mock(\"../../utils/process.js\", async (importOriginal) => {\n const actual = (await importOriginal()) as typeof import(\"../../utils/process.js\");\n return {\n ...actual,\n listAgentProcesses: vi.fn(),\n enrichProcesses: vi.fn(),\n captureProcessSnapshot: vi.fn(),\n };\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 mockedCaptureProcessSnapshot = captureProcessSnapshot as MockedFunction<\n typeof captureProcessSnapshot\n>;\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: () =>\n (queries.session ?? []).map((r) => ({\n id: r.id,\n directory: r.directory,\n 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 (\n normalized.includes(\"from message\") &&\n !normalized.includes(\"'$.time.completed'\") &&\n !normalized.includes(\"order by p.time_created\")\n ) {\n return {\n get: () => queries.lastMessage ?? undefined,\n };\n }\n\n if (normalized.includes(\"'$.time.completed'\")) {\n return {\n get: () =>\n queries.lastAssistant === undefined ? undefined : (queries.lastAssistant ?? undefined),\n };\n }\n\n if (normalized.includes(\"json_extract(m.data, '$.role') = 'user'\")) {\n return {\n get: () =>\n queries.firstUserText === undefined ? undefined : (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 mockedCaptureProcessSnapshot.mockReset();\n mockedGenerateAgentName.mockReset();\n\n mockedEnrichProcesses.mockImplementation((procs) => procs);\n // Compatibility shim for standalone adapter discovery; the manager captures once and slices by name.\n mockedCaptureProcessSnapshot.mockImplementation(async (names) =>\n enrichProcesses(names.flatMap((name) => listAgentProcesses(name))),\n );\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 adapter.close();\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(\n true,\n );\n });\n\n it(\"returns true for full path opencode\", () => {\n expect(\n adapter.canHandle({\n pid: 2,\n command: \"/usr/local/bin/opencode serve\",\n cwd: \"/repo\",\n tty: \"ttys002\",\n }),\n ).toBe(true);\n });\n\n it(\"returns true for opencode.exe with unix-style path\", () => {\n expect(\n adapter.canHandle({\n pid: 3,\n command: \"/usr/bin/opencode.exe\",\n cwd: \"/repo\",\n tty: \"ttys003\",\n }),\n ).toBe(true);\n });\n\n it(\"returns false for non-opencode processes\", () => {\n expect(\n adapter.canHandle({ pid: 4, command: \"node server.js\", cwd: \"/repo\", tty: \"ttys004\" }),\n ).toBe(false);\n });\n\n it(\"returns false when opencode appears only in path args\", () => {\n expect(\n adapter.canHandle({\n pid: 5,\n command: \"node /projects/opencode-plugin/index.js\",\n cwd: \"/repo\",\n tty: \"ttys005\",\n }),\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 {\n role: \"user\",\n partData: JSON.stringify({ type: \"text\", text: \"Hello agent\" }),\n timeCreated: 1000,\n },\n {\n role: \"assistant\",\n partData: JSON.stringify({ type: \"text\", text: \"Hi, how can I help?\" }),\n timeCreated: 2000,\n },\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 {\n role: \"assistant\",\n partData: JSON.stringify({ type: \"reasoning\", reasoning: \"internal thought\" }),\n timeCreated: 1000,\n },\n {\n role: \"assistant\",\n partData: JSON.stringify({ type: \"text\", text: \"Answer\" }),\n timeCreated: 2000,\n },\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 {\n role: \"assistant\",\n partData: JSON.stringify({ type: \"reasoning\", reasoning: \"my thinking\" }),\n timeCreated: 1000,\n },\n {\n role: \"assistant\",\n partData: JSON.stringify({ type: \"tool\", tool: \"read_file\" }),\n timeCreated: 2000,\n },\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","captureProcessSnapshot","generateAgentName","os","vi","mock","importOriginal","actual","fn","matchProcessesToSessions","mockedListAgentProcesses","mockedEnrichProcesses","mockedCaptureProcessSnapshot","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","names","flatMap","name","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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"OpenCodeAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/OpenCodeAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAoC3B,qBAAa,eAAgB,YAAW,YAAY;IAClD,QAAQ,CAAC,IAAI,EAAG,UAAU,CAAU;IACpC,QAAQ,CAAC,YAAY,wBAAyB;IAE9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAK;IAEnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,EAAE,CAAkC;;IAS5C,KAAK,IAAI,IAAI;IAeb,OAAO,CAAC,MAAM,CAAC,aAAa;IAO5B,SAAS,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO;IAKtC,YAAY,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IA6BzE,eAAe,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,mBAAmB,EAAE;IAmD1F,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAyCzE,OAAO,CAAC,uBAAuB;IAsB/B,OAAO,CAAC,eAAe;IAwEvB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,MAAM;CAUf"}
|
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode Adapter
|
|
3
|
-
*
|
|
4
|
-
* Detects running OpenCode agents by:
|
|
5
|
-
* 1. Filtering OpenCode processes from a shared asynchronous process snapshot
|
|
6
|
-
* 2. Using snapshot CWD and start-time enrichment
|
|
7
|
-
* 3. Querying OpenCode's SQLite DB (~/.local/share/opencode/opencode.db) to
|
|
8
|
-
* find the session matching each process's CWD and read status from message.time.completed
|
|
9
|
-
*
|
|
10
|
-
* sessionFilePath encodes "<dbPath>::<sessionId>" so getConversation() can open the right
|
|
11
|
-
* DB row without extending the AgentAdapter interface.
|
|
12
|
-
*/ import * as fs from "fs";
|
|
13
|
-
import * as path from "path";
|
|
14
|
-
import Database from "better-sqlite3";
|
|
15
|
-
import { AgentStatus } from "./AgentAdapter.js";
|
|
16
|
-
import { captureProcessSnapshot, executableBasename, filterByProcessNames } from "../utils/process.js";
|
|
17
|
-
import { generateAgentName } from "../utils/matching.js";
|
|
18
|
-
const SESSION_REF_SEP = "::";
|
|
19
|
-
function encodeSessionRef(dbPath, sessionId) {
|
|
20
|
-
return `${dbPath}${SESSION_REF_SEP}${sessionId}`;
|
|
21
|
-
}
|
|
22
|
-
function decodeSessionRef(ref) {
|
|
23
|
-
const idx = ref.lastIndexOf(SESSION_REF_SEP);
|
|
24
|
-
if (idx === -1) return null;
|
|
25
|
-
return {
|
|
26
|
-
dbPath: ref.slice(0, idx),
|
|
27
|
-
sessionId: ref.slice(idx + SESSION_REF_SEP.length)
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
export class OpenCodeAdapter {
|
|
31
|
-
type = "opencode";
|
|
32
|
-
processNames = [
|
|
33
|
-
"opencode"
|
|
34
|
-
];
|
|
35
|
-
static IDLE_THRESHOLD_MINUTES = 5;
|
|
36
|
-
dbPath;
|
|
37
|
-
cleanup = ()=>this.close();
|
|
38
|
-
db = null;
|
|
39
|
-
constructor(){
|
|
40
|
-
this.dbPath = OpenCodeAdapter.resolveDbPath();
|
|
41
|
-
process.once("exit", this.cleanup);
|
|
42
|
-
process.once("SIGINT", this.cleanup);
|
|
43
|
-
process.once("SIGTERM", this.cleanup);
|
|
44
|
-
}
|
|
45
|
-
close() {
|
|
46
|
-
process.off("exit", this.cleanup);
|
|
47
|
-
process.off("SIGINT", this.cleanup);
|
|
48
|
-
process.off("SIGTERM", this.cleanup);
|
|
49
|
-
if (this.db) {
|
|
50
|
-
try {
|
|
51
|
-
this.db.close();
|
|
52
|
-
} catch {
|
|
53
|
-
/* ignore */ }
|
|
54
|
-
this.db = null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
static resolveDbPath() {
|
|
58
|
-
const xdg = process.env.XDG_DATA_HOME;
|
|
59
|
-
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
60
|
-
const base = xdg || path.join(home, ".local", "share");
|
|
61
|
-
return path.join(base, "opencode", "opencode.db");
|
|
62
|
-
}
|
|
63
|
-
canHandle(processInfo) {
|
|
64
|
-
const base = executableBasename(processInfo.command);
|
|
65
|
-
return base === "opencode" || base === "opencode.exe";
|
|
66
|
-
}
|
|
67
|
-
async detectAgents(context) {
|
|
68
|
-
const snapshot = context?.processes ?? await captureProcessSnapshot(this.processNames);
|
|
69
|
-
const relevant = filterByProcessNames(snapshot, this.processNames);
|
|
70
|
-
const processes = relevant.filter((process1)=>this.canHandle(process1));
|
|
71
|
-
if (processes.length === 0) return [];
|
|
72
|
-
const db = this.openDb();
|
|
73
|
-
if (!db) return processes.map((p)=>this.mapProcessOnlyAgent(p));
|
|
74
|
-
const agents = [];
|
|
75
|
-
for (const proc of processes){
|
|
76
|
-
if (!proc.cwd) {
|
|
77
|
-
agents.push(this.mapProcessOnlyAgent(proc));
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const session = this.findSessionForDirectory(db, proc.cwd);
|
|
81
|
-
if (!session) {
|
|
82
|
-
agents.push(this.mapProcessOnlyAgent(proc));
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
const stats = this.getSessionStats(db, session.sessionId);
|
|
86
|
-
agents.push(this.mapSessionToAgent(session, stats, proc));
|
|
87
|
-
}
|
|
88
|
-
return agents;
|
|
89
|
-
}
|
|
90
|
-
getConversation(sessionFilePath, options) {
|
|
91
|
-
const verbose = options?.verbose ?? false;
|
|
92
|
-
const ref = decodeSessionRef(sessionFilePath);
|
|
93
|
-
if (!ref) return [];
|
|
94
|
-
const db = this.openDb();
|
|
95
|
-
if (!db) return [];
|
|
96
|
-
try {
|
|
97
|
-
const rows = db.prepare(`
|
|
98
|
-
SELECT json_extract(m.data, '$.role') AS role,
|
|
99
|
-
p.data AS partData,
|
|
100
|
-
p.time_created AS timeCreated
|
|
101
|
-
FROM part p
|
|
102
|
-
JOIN message m ON p.message_id = m.id
|
|
103
|
-
WHERE p.session_id = ?
|
|
104
|
-
ORDER BY p.time_created ASC
|
|
105
|
-
`).all(ref.sessionId);
|
|
106
|
-
const messages = [];
|
|
107
|
-
for (const row of rows){
|
|
108
|
-
let partData = {};
|
|
109
|
-
try {
|
|
110
|
-
partData = JSON.parse(row.partData);
|
|
111
|
-
} catch {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
const role = row.role === "user" ? "user" : "assistant";
|
|
115
|
-
if (partData.type === "text" && partData.text) {
|
|
116
|
-
messages.push({
|
|
117
|
-
role,
|
|
118
|
-
content: partData.text
|
|
119
|
-
});
|
|
120
|
-
} else if (partData.type === "reasoning" && verbose) {
|
|
121
|
-
const text = partData.reasoning || partData.text || "";
|
|
122
|
-
if (text) messages.push({
|
|
123
|
-
role: "assistant",
|
|
124
|
-
content: `[thinking] ${text}`
|
|
125
|
-
});
|
|
126
|
-
} else if (partData.type === "tool" && verbose) {
|
|
127
|
-
const toolName = partData.tool || "tool";
|
|
128
|
-
messages.push({
|
|
129
|
-
role: "assistant",
|
|
130
|
-
content: `[tool: ${toolName}]`
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return messages;
|
|
135
|
-
} catch {
|
|
136
|
-
this.close();
|
|
137
|
-
return [];
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
async listSessions(opts) {
|
|
141
|
-
const db = this.openDb();
|
|
142
|
-
if (!db) return [];
|
|
143
|
-
try {
|
|
144
|
-
const rows = db.prepare(`
|
|
145
|
-
SELECT id, directory, time_created AS timeCreated
|
|
146
|
-
FROM session
|
|
147
|
-
ORDER BY time_created DESC
|
|
148
|
-
`).all();
|
|
149
|
-
const summaries = [];
|
|
150
|
-
for (const row of rows){
|
|
151
|
-
if (opts?.cwd !== undefined && row.directory !== opts.cwd) continue;
|
|
152
|
-
const stats = this.getSessionStats(db, row.id);
|
|
153
|
-
const lastActive = stats.lastTimeUpdated > 0 ? new Date(stats.lastTimeUpdated) : new Date(row.timeCreated);
|
|
154
|
-
const startedAt = new Date(row.timeCreated);
|
|
155
|
-
summaries.push({
|
|
156
|
-
type: "opencode",
|
|
157
|
-
sessionId: row.id,
|
|
158
|
-
cwd: row.directory,
|
|
159
|
-
firstUserMessage: stats.summary,
|
|
160
|
-
lastActive,
|
|
161
|
-
startedAt,
|
|
162
|
-
sessionFilePath: encodeSessionRef(this.dbPath, row.id)
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
return summaries;
|
|
166
|
-
} catch {
|
|
167
|
-
this.close();
|
|
168
|
-
return [];
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
findSessionForDirectory(db, directory) {
|
|
172
|
-
try {
|
|
173
|
-
const row = db.prepare(`
|
|
174
|
-
SELECT id, directory, time_created
|
|
175
|
-
FROM session
|
|
176
|
-
WHERE directory = ?
|
|
177
|
-
ORDER BY time_created DESC
|
|
178
|
-
LIMIT 1
|
|
179
|
-
`).get(directory);
|
|
180
|
-
if (!row) return null;
|
|
181
|
-
return {
|
|
182
|
-
sessionId: row.id,
|
|
183
|
-
directory: row.directory,
|
|
184
|
-
timeCreated: row.time_created
|
|
185
|
-
};
|
|
186
|
-
} catch {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
getSessionStats(db, sessionId) {
|
|
191
|
-
const empty = {
|
|
192
|
-
lastRole: null,
|
|
193
|
-
lastTimeUpdated: 0,
|
|
194
|
-
lastAssistantCompleted: false,
|
|
195
|
-
lastAssistantErrored: false,
|
|
196
|
-
summary: ""
|
|
197
|
-
};
|
|
198
|
-
try {
|
|
199
|
-
// Order by time_created — time_updated can lag when OpenCode appends
|
|
200
|
-
// metadata (e.g. summary diffs) to user messages after a turn finishes.
|
|
201
|
-
const last = db.prepare(`
|
|
202
|
-
SELECT json_extract(data, '$.role') AS role,
|
|
203
|
-
time_updated AS timeUpdated
|
|
204
|
-
FROM message
|
|
205
|
-
WHERE session_id = ?
|
|
206
|
-
ORDER BY time_created DESC
|
|
207
|
-
LIMIT 1
|
|
208
|
-
`).get(sessionId);
|
|
209
|
-
const heartbeat = db.prepare(`
|
|
210
|
-
SELECT MAX(time_updated) AS maxUpdated FROM message WHERE session_id = ?
|
|
211
|
-
`).get(sessionId);
|
|
212
|
-
const lastAssistant = db.prepare(`
|
|
213
|
-
SELECT json_extract(data, '$.time.completed') AS completed,
|
|
214
|
-
json_extract(data, '$.time.error') AS errored
|
|
215
|
-
FROM message
|
|
216
|
-
WHERE session_id = ? AND json_extract(data, '$.role') = 'assistant'
|
|
217
|
-
ORDER BY time_created DESC
|
|
218
|
-
LIMIT 1
|
|
219
|
-
`).get(sessionId);
|
|
220
|
-
const first = db.prepare(`
|
|
221
|
-
SELECT json_extract(p.data, '$.text') AS text
|
|
222
|
-
FROM part p
|
|
223
|
-
JOIN message m ON p.message_id = m.id
|
|
224
|
-
WHERE p.session_id = ?
|
|
225
|
-
AND json_extract(m.data, '$.role') = 'user'
|
|
226
|
-
AND json_extract(p.data, '$.type') = 'text'
|
|
227
|
-
AND json_extract(p.data, '$.text') IS NOT NULL
|
|
228
|
-
ORDER BY p.time_created ASC
|
|
229
|
-
LIMIT 1
|
|
230
|
-
`).get(sessionId);
|
|
231
|
-
return {
|
|
232
|
-
lastRole: last?.role ?? null,
|
|
233
|
-
lastTimeUpdated: heartbeat?.maxUpdated ?? last?.timeUpdated ?? 0,
|
|
234
|
-
lastAssistantCompleted: lastAssistant?.completed != null,
|
|
235
|
-
lastAssistantErrored: lastAssistant?.errored != null,
|
|
236
|
-
summary: first?.text?.trim() ?? ""
|
|
237
|
-
};
|
|
238
|
-
} catch {
|
|
239
|
-
return empty;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
mapSessionToAgent(session, stats, proc) {
|
|
243
|
-
const lastActive = stats.lastTimeUpdated > 0 ? new Date(stats.lastTimeUpdated) : new Date(session.timeCreated);
|
|
244
|
-
return {
|
|
245
|
-
name: generateAgentName(session.directory || proc.cwd || "", proc.pid),
|
|
246
|
-
type: this.type,
|
|
247
|
-
status: this.determineStatus(stats, lastActive),
|
|
248
|
-
summary: stats.summary || "OpenCode session active",
|
|
249
|
-
pid: proc.pid,
|
|
250
|
-
projectPath: session.directory || proc.cwd || "",
|
|
251
|
-
sessionId: session.sessionId,
|
|
252
|
-
lastActive,
|
|
253
|
-
sessionFilePath: encodeSessionRef(this.dbPath, session.sessionId)
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
mapProcessOnlyAgent(proc) {
|
|
257
|
-
return {
|
|
258
|
-
name: generateAgentName(proc.cwd || "", proc.pid),
|
|
259
|
-
type: this.type,
|
|
260
|
-
status: AgentStatus.RUNNING,
|
|
261
|
-
summary: "OpenCode process running",
|
|
262
|
-
pid: proc.pid,
|
|
263
|
-
projectPath: proc.cwd || "",
|
|
264
|
-
sessionId: `pid-${proc.pid}`,
|
|
265
|
-
lastActive: new Date()
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
determineStatus(stats, lastActive) {
|
|
269
|
-
const ageMin = (Date.now() - lastActive.getTime()) / 60000;
|
|
270
|
-
if (ageMin > OpenCodeAdapter.IDLE_THRESHOLD_MINUTES) return AgentStatus.IDLE;
|
|
271
|
-
if (stats.lastRole === "assistant" && !stats.lastAssistantCompleted) return AgentStatus.RUNNING;
|
|
272
|
-
if (stats.lastRole === "assistant") return AgentStatus.WAITING;
|
|
273
|
-
return AgentStatus.RUNNING;
|
|
274
|
-
}
|
|
275
|
-
openDb() {
|
|
276
|
-
if (this.db) return this.db;
|
|
277
|
-
if (!fs.existsSync(this.dbPath)) return null;
|
|
278
|
-
try {
|
|
279
|
-
this.db = new Database(this.dbPath, {
|
|
280
|
-
readonly: true
|
|
281
|
-
});
|
|
282
|
-
return this.db;
|
|
283
|
-
} catch {
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
//# sourceMappingURL=OpenCodeAdapter.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/OpenCodeAdapter.ts"],"sourcesContent":["/**\n * OpenCode Adapter\n *\n * Detects running OpenCode agents by:\n * 1. Filtering OpenCode processes from a shared asynchronous process snapshot\n * 2. Using snapshot CWD and start-time enrichment\n * 3. Querying OpenCode's SQLite DB (~/.local/share/opencode/opencode.db) to\n * find the session matching each process's CWD and read status from message.time.completed\n *\n * sessionFilePath encodes \"<dbPath>::<sessionId>\" so getConversation() can open the right\n * DB row without extending the AgentAdapter interface.\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport Database from \"better-sqlite3\";\nimport type {\n AgentAdapter,\n AgentInfo,\n ProcessInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n AgentDetectionContext,\n} from \"./AgentAdapter.js\";\nimport { AgentStatus } from \"./AgentAdapter.js\";\nimport {\n captureProcessSnapshot,\n executableBasename,\n filterByProcessNames,\n} from \"../utils/process.js\";\nimport { generateAgentName } from \"../utils/matching.js\";\n\nconst SESSION_REF_SEP = \"::\";\n\nfunction encodeSessionRef(dbPath: string, sessionId: string): string {\n return `${dbPath}${SESSION_REF_SEP}${sessionId}`;\n}\n\nfunction decodeSessionRef(ref: string): { dbPath: string; sessionId: string } | null {\n const idx = ref.lastIndexOf(SESSION_REF_SEP);\n if (idx === -1) return null;\n return { dbPath: ref.slice(0, idx), sessionId: ref.slice(idx + SESSION_REF_SEP.length) };\n}\n\ninterface OpenCodeSession {\n sessionId: string;\n directory: string;\n timeCreated: number;\n}\n\ninterface OpenCodeSessionStats {\n lastRole: string | null;\n lastTimeUpdated: number;\n /** OpenCode writes `time.completed` on the assistant message only when the turn finishes. */\n lastAssistantCompleted: boolean;\n lastAssistantErrored: boolean;\n summary: string;\n}\n\nexport class OpenCodeAdapter implements AgentAdapter {\n readonly type = \"opencode\" as const;\n readonly processNames = [\"opencode\"] as const;\n\n private static readonly IDLE_THRESHOLD_MINUTES = 5;\n\n private readonly dbPath: string;\n private readonly cleanup = (): void => this.close();\n private db: Database.Database | null = null;\n\n constructor() {\n this.dbPath = OpenCodeAdapter.resolveDbPath();\n process.once(\"exit\", this.cleanup);\n process.once(\"SIGINT\", this.cleanup);\n process.once(\"SIGTERM\", this.cleanup);\n }\n\n close(): void {\n process.off(\"exit\", this.cleanup);\n process.off(\"SIGINT\", this.cleanup);\n process.off(\"SIGTERM\", this.cleanup);\n\n if (this.db) {\n try {\n this.db.close();\n } catch {\n /* ignore */\n }\n this.db = null;\n }\n }\n\n private static resolveDbPath(): string {\n const xdg = process.env.XDG_DATA_HOME;\n const home = process.env.HOME || process.env.USERPROFILE || \"\";\n const base = xdg || path.join(home, \".local\", \"share\");\n return path.join(base, \"opencode\", \"opencode.db\");\n }\n\n canHandle(processInfo: ProcessInfo): boolean {\n const base = executableBasename(processInfo.command);\n return base === \"opencode\" || base === \"opencode.exe\";\n }\n\n async detectAgents(context?: AgentDetectionContext): Promise<AgentInfo[]> {\n const snapshot = context?.processes ?? (await captureProcessSnapshot(this.processNames));\n const relevant = filterByProcessNames(snapshot, this.processNames);\n const processes = relevant.filter((process) => this.canHandle(process));\n if (processes.length === 0) return [];\n\n const db = this.openDb();\n if (!db) return processes.map((p) => this.mapProcessOnlyAgent(p));\n\n const agents: AgentInfo[] = [];\n for (const proc of processes) {\n if (!proc.cwd) {\n agents.push(this.mapProcessOnlyAgent(proc));\n continue;\n }\n\n const session = this.findSessionForDirectory(db, proc.cwd);\n if (!session) {\n agents.push(this.mapProcessOnlyAgent(proc));\n continue;\n }\n\n const stats = this.getSessionStats(db, session.sessionId);\n agents.push(this.mapSessionToAgent(session, stats, proc));\n }\n\n return agents;\n }\n\n getConversation(sessionFilePath: string, options?: { verbose?: boolean }): ConversationMessage[] {\n const verbose = options?.verbose ?? false;\n const ref = decodeSessionRef(sessionFilePath);\n if (!ref) return [];\n\n const db = this.openDb();\n if (!db) return [];\n\n try {\n const rows = db\n .prepare<[string], { role: string; partData: string; timeCreated: number }>(`\n SELECT json_extract(m.data, '$.role') AS role,\n p.data AS partData,\n p.time_created AS timeCreated\n FROM part p\n JOIN message m ON p.message_id = m.id\n WHERE p.session_id = ?\n ORDER BY p.time_created ASC\n `)\n .all(ref.sessionId);\n\n const messages: ConversationMessage[] = [];\n\n for (const row of rows) {\n let partData: { type?: string; text?: string; reasoning?: string; tool?: string } = {};\n try {\n partData = JSON.parse(row.partData);\n } catch {\n continue;\n }\n\n const role = row.role === \"user\" ? \"user\" : \"assistant\";\n\n if (partData.type === \"text\" && partData.text) {\n messages.push({ role, content: partData.text });\n } else if (partData.type === \"reasoning\" && verbose) {\n const text = partData.reasoning || partData.text || \"\";\n if (text) messages.push({ role: \"assistant\", content: `[thinking] ${text}` });\n } else if (partData.type === \"tool\" && verbose) {\n const toolName = partData.tool || \"tool\";\n messages.push({ role: \"assistant\", content: `[tool: ${toolName}]` });\n }\n }\n\n return messages;\n } catch {\n this.close();\n return [];\n }\n }\n\n async listSessions(opts?: ListSessionsOptions): Promise<SessionSummary[]> {\n const db = this.openDb();\n if (!db) return [];\n\n try {\n const rows = db\n .prepare<[], { id: string; directory: string; timeCreated: number }>(`\n SELECT id, directory, time_created AS timeCreated\n FROM session\n ORDER BY time_created DESC\n `)\n .all();\n\n const summaries: SessionSummary[] = [];\n\n for (const row of rows) {\n if (opts?.cwd !== undefined && row.directory !== opts.cwd) continue;\n\n const stats = this.getSessionStats(db, row.id);\n const lastActive =\n stats.lastTimeUpdated > 0 ? new Date(stats.lastTimeUpdated) : new Date(row.timeCreated);\n const startedAt = new Date(row.timeCreated);\n\n summaries.push({\n type: \"opencode\",\n sessionId: row.id,\n cwd: row.directory,\n firstUserMessage: stats.summary,\n lastActive,\n startedAt,\n sessionFilePath: encodeSessionRef(this.dbPath, row.id),\n });\n }\n\n return summaries;\n } catch {\n this.close();\n return [];\n }\n }\n\n private findSessionForDirectory(\n db: Database.Database,\n directory: string,\n ): OpenCodeSession | null {\n try {\n const row = db\n .prepare<[string], { id: string; directory: string; time_created: number }>(`\n SELECT id, directory, time_created\n FROM session\n WHERE directory = ?\n ORDER BY time_created DESC\n LIMIT 1\n `)\n .get(directory);\n\n if (!row) return null;\n return { sessionId: row.id, directory: row.directory, timeCreated: row.time_created };\n } catch {\n return null;\n }\n }\n\n private getSessionStats(db: Database.Database, sessionId: string): OpenCodeSessionStats {\n const empty: OpenCodeSessionStats = {\n lastRole: null,\n lastTimeUpdated: 0,\n lastAssistantCompleted: false,\n lastAssistantErrored: false,\n summary: \"\",\n };\n\n try {\n // Order by time_created — time_updated can lag when OpenCode appends\n // metadata (e.g. summary diffs) to user messages after a turn finishes.\n const last = db\n .prepare<[string], { role: string; timeUpdated: number }>(`\n SELECT json_extract(data, '$.role') AS role,\n time_updated AS timeUpdated\n FROM message\n WHERE session_id = ?\n ORDER BY time_created DESC\n LIMIT 1\n `)\n .get(sessionId);\n\n const heartbeat = db\n .prepare<[string], { maxUpdated: number }>(`\n SELECT MAX(time_updated) AS maxUpdated FROM message WHERE session_id = ?\n `)\n .get(sessionId);\n\n const lastAssistant = db\n .prepare<\n [string],\n {\n completed: number | null;\n errored: number | null;\n }\n >(`\n SELECT json_extract(data, '$.time.completed') AS completed,\n json_extract(data, '$.time.error') AS errored\n FROM message\n WHERE session_id = ? AND json_extract(data, '$.role') = 'assistant'\n ORDER BY time_created DESC\n LIMIT 1\n `)\n .get(sessionId);\n\n const first = db\n .prepare<[string], { text: string }>(`\n SELECT json_extract(p.data, '$.text') AS text\n FROM part p\n JOIN message m ON p.message_id = m.id\n WHERE p.session_id = ?\n AND json_extract(m.data, '$.role') = 'user'\n AND json_extract(p.data, '$.type') = 'text'\n AND json_extract(p.data, '$.text') IS NOT NULL\n ORDER BY p.time_created ASC\n LIMIT 1\n `)\n .get(sessionId);\n\n return {\n lastRole: last?.role ?? null,\n lastTimeUpdated: heartbeat?.maxUpdated ?? last?.timeUpdated ?? 0,\n lastAssistantCompleted: lastAssistant?.completed != null,\n lastAssistantErrored: lastAssistant?.errored != null,\n summary: first?.text?.trim() ?? \"\",\n };\n } catch {\n return empty;\n }\n }\n\n private mapSessionToAgent(\n session: OpenCodeSession,\n stats: OpenCodeSessionStats,\n proc: ProcessInfo,\n ): AgentInfo {\n const lastActive =\n stats.lastTimeUpdated > 0 ? new Date(stats.lastTimeUpdated) : new Date(session.timeCreated);\n\n return {\n name: generateAgentName(session.directory || proc.cwd || \"\", proc.pid),\n type: this.type,\n status: this.determineStatus(stats, lastActive),\n summary: stats.summary || \"OpenCode session active\",\n pid: proc.pid,\n projectPath: session.directory || proc.cwd || \"\",\n sessionId: session.sessionId,\n lastActive,\n sessionFilePath: encodeSessionRef(this.dbPath, session.sessionId),\n };\n }\n\n private mapProcessOnlyAgent(proc: ProcessInfo): AgentInfo {\n return {\n name: generateAgentName(proc.cwd || \"\", proc.pid),\n type: this.type,\n status: AgentStatus.RUNNING,\n summary: \"OpenCode process running\",\n pid: proc.pid,\n projectPath: proc.cwd || \"\",\n sessionId: `pid-${proc.pid}`,\n lastActive: new Date(),\n };\n }\n\n private determineStatus(stats: OpenCodeSessionStats, lastActive: Date): AgentStatus {\n const ageMin = (Date.now() - lastActive.getTime()) / 60000;\n if (ageMin > OpenCodeAdapter.IDLE_THRESHOLD_MINUTES) return AgentStatus.IDLE;\n\n if (stats.lastRole === \"assistant\" && !stats.lastAssistantCompleted) return AgentStatus.RUNNING;\n if (stats.lastRole === \"assistant\") return AgentStatus.WAITING;\n return AgentStatus.RUNNING;\n }\n\n private openDb(): Database.Database | null {\n if (this.db) return this.db;\n if (!fs.existsSync(this.dbPath)) return null;\n try {\n this.db = new Database(this.dbPath, { readonly: true });\n return this.db;\n } catch {\n return null;\n }\n }\n}\n"],"names":["fs","path","Database","AgentStatus","captureProcessSnapshot","executableBasename","filterByProcessNames","generateAgentName","SESSION_REF_SEP","encodeSessionRef","dbPath","sessionId","decodeSessionRef","ref","idx","lastIndexOf","slice","length","OpenCodeAdapter","type","processNames","IDLE_THRESHOLD_MINUTES","cleanup","close","db","resolveDbPath","process","once","off","xdg","env","XDG_DATA_HOME","home","HOME","USERPROFILE","base","join","canHandle","processInfo","command","detectAgents","context","snapshot","processes","relevant","filter","openDb","map","p","mapProcessOnlyAgent","agents","proc","cwd","push","session","findSessionForDirectory","stats","getSessionStats","mapSessionToAgent","getConversation","sessionFilePath","options","verbose","rows","prepare","all","messages","row","partData","JSON","parse","role","text","content","reasoning","toolName","tool","listSessions","opts","summaries","undefined","directory","id","lastActive","lastTimeUpdated","Date","timeCreated","startedAt","firstUserMessage","summary","get","time_created","empty","lastRole","lastAssistantCompleted","lastAssistantErrored","last","heartbeat","lastAssistant","first","maxUpdated","timeUpdated","completed","errored","trim","name","pid","status","determineStatus","projectPath","RUNNING","ageMin","now","getTime","IDLE","WAITING","existsSync","readonly"],"mappings":"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"}
|