@ai-devkit/agent-manager 0.29.0 → 0.31.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 (202) hide show
  1. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +1 -342
  2. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
  3. package/dist/__tests__/database/DurableAgentsDatabase.test.js +31 -3
  4. package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +1 -1
  5. package/dist/__tests__/durable/CodexCliProbe.test.js +129 -0
  6. package/dist/__tests__/durable/CodexCliProbe.test.js.map +1 -0
  7. package/dist/__tests__/durable/CodexDurableAgentRepository.test.js +84 -0
  8. package/dist/__tests__/durable/CodexDurableAgentRepository.test.js.map +1 -0
  9. package/dist/__tests__/durable/CodexPrintAgent.integration.test.js +126 -0
  10. package/dist/__tests__/durable/CodexPrintAgent.integration.test.js.map +1 -0
  11. package/dist/__tests__/durable/CodexPrintAgentService.test.js +271 -0
  12. package/dist/__tests__/durable/CodexPrintAgentService.test.js.map +1 -0
  13. package/dist/__tests__/durable/CodexPrintRunner.test.js +393 -0
  14. package/dist/__tests__/durable/CodexPrintRunner.test.js.map +1 -0
  15. package/dist/__tests__/print/ClaudeCliProbe.test.js +3 -7
  16. package/dist/__tests__/print/ClaudeCliProbe.test.js.map +1 -1
  17. package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +25 -28
  18. package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
  19. package/dist/__tests__/print/ClaudePrintAgentService.test.js +65 -0
  20. package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
  21. package/dist/__tests__/print/ClaudePrintRunner.test.js +4 -10
  22. package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
  23. package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js +79 -0
  24. package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js.map +1 -0
  25. package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js +436 -0
  26. package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js.map +1 -0
  27. package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js +76 -0
  28. package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js.map +1 -0
  29. package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js +43 -0
  30. package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js.map +1 -0
  31. package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js +202 -0
  32. package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js.map +1 -0
  33. package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js +119 -0
  34. package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js.map +1 -0
  35. package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js +386 -0
  36. package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js.map +1 -0
  37. package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js +34 -0
  38. package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js.map +1 -0
  39. package/dist/__tests__/utils/ClaudeSessionParser.test.js +2 -2
  40. package/dist/__tests__/utils/ClaudeSessionParser.test.js.map +1 -1
  41. package/dist/adapters/index.d.ts +1 -1
  42. package/dist/adapters/index.d.ts.map +1 -1
  43. package/dist/adapters/index.js +1 -1
  44. package/dist/adapters/index.js.map +1 -1
  45. package/dist/database/connection.d.ts.map +1 -1
  46. package/dist/database/connection.js +5 -1
  47. package/dist/database/connection.js.map +1 -1
  48. package/dist/database/migrations/004_codex_durable_sessions.sql +1 -0
  49. package/dist/durable/DurableAgent.d.ts +23 -3
  50. package/dist/durable/DurableAgent.d.ts.map +1 -1
  51. package/dist/durable/DurableAgent.js +12 -0
  52. package/dist/durable/DurableAgent.js.map +1 -1
  53. package/dist/durable/DurableAgentRepository.d.ts +6 -7
  54. package/dist/durable/DurableAgentRepository.d.ts.map +1 -1
  55. package/dist/durable/DurableAgentRepository.js +73 -42
  56. package/dist/durable/DurableAgentRepository.js.map +1 -1
  57. package/dist/durable/process.d.ts +8 -0
  58. package/dist/durable/process.d.ts.map +1 -0
  59. package/dist/durable/process.js +40 -0
  60. package/dist/durable/process.js.map +1 -0
  61. package/dist/durable/run.d.ts +32 -0
  62. package/dist/durable/run.d.ts.map +1 -0
  63. package/dist/durable/run.js +46 -0
  64. package/dist/durable/run.js.map +1 -0
  65. package/dist/durable/utils.d.ts +19 -0
  66. package/dist/durable/utils.d.ts.map +1 -0
  67. package/dist/durable/utils.js +52 -0
  68. package/dist/durable/utils.js.map +1 -0
  69. package/dist/index.d.ts +17 -13
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +9 -8
  72. package/dist/index.js.map +1 -1
  73. package/dist/providers/claude/ClaudeAgentMapper.d.ts +21 -0
  74. package/dist/providers/claude/ClaudeAgentMapper.d.ts.map +1 -0
  75. package/dist/providers/claude/ClaudeAgentMapper.js +42 -0
  76. package/dist/providers/claude/ClaudeAgentMapper.js.map +1 -0
  77. package/dist/providers/claude/ClaudeCodeAdapter.d.ts +29 -0
  78. package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -0
  79. package/dist/providers/claude/ClaudeCodeAdapter.js +129 -0
  80. package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -0
  81. package/dist/{adapters/ClaudeCodeAdapter.d.ts → providers/claude/ClaudeSessionLocator.d.ts} +68 -52
  82. package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -0
  83. package/dist/{adapters/ClaudeCodeAdapter.js → providers/claude/ClaudeSessionLocator.js} +71 -189
  84. package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -0
  85. package/dist/{utils → providers/claude}/ClaudeSessionParser.d.ts +2 -2
  86. package/dist/providers/claude/ClaudeSessionParser.d.ts.map +1 -0
  87. package/dist/{utils → providers/claude}/ClaudeSessionParser.js +1 -1
  88. package/dist/providers/claude/ClaudeSessionParser.js.map +1 -0
  89. package/dist/providers/claude/durable/ClaudeCliProbe.d.ts.map +1 -0
  90. package/dist/{durable → providers/claude/durable}/ClaudeCliProbe.js +4 -9
  91. package/dist/providers/claude/durable/ClaudeCliProbe.js.map +1 -0
  92. package/dist/{durable → providers/claude/durable}/ClaudePrintAgentService.d.ts +4 -9
  93. package/dist/providers/claude/durable/ClaudePrintAgentService.d.ts.map +1 -0
  94. package/dist/providers/claude/durable/ClaudePrintAgentService.js +45 -0
  95. package/dist/providers/claude/durable/ClaudePrintAgentService.js.map +1 -0
  96. package/dist/{durable → providers/claude/durable}/ClaudePrintRunner.d.ts +3 -3
  97. package/dist/providers/claude/durable/ClaudePrintRunner.d.ts.map +1 -0
  98. package/dist/providers/claude/durable/ClaudePrintRunner.js +75 -0
  99. package/dist/providers/claude/durable/ClaudePrintRunner.js.map +1 -0
  100. package/dist/providers/claude/durable/ClaudeStreamParser.d.ts +17 -0
  101. package/dist/providers/claude/durable/ClaudeStreamParser.d.ts.map +1 -0
  102. package/dist/providers/claude/durable/ClaudeStreamParser.js +69 -0
  103. package/dist/providers/claude/durable/ClaudeStreamParser.js.map +1 -0
  104. package/dist/providers/codex/durable/CodexCliProbe.d.ts +20 -0
  105. package/dist/providers/codex/durable/CodexCliProbe.d.ts.map +1 -0
  106. package/dist/providers/codex/durable/CodexCliProbe.js +61 -0
  107. package/dist/providers/codex/durable/CodexCliProbe.js.map +1 -0
  108. package/dist/providers/codex/durable/CodexPrintAgentService.d.ts +40 -0
  109. package/dist/providers/codex/durable/CodexPrintAgentService.d.ts.map +1 -0
  110. package/dist/providers/codex/durable/CodexPrintAgentService.js +50 -0
  111. package/dist/providers/codex/durable/CodexPrintAgentService.js.map +1 -0
  112. package/dist/providers/codex/durable/CodexPrintRunner.d.ts +34 -0
  113. package/dist/providers/codex/durable/CodexPrintRunner.d.ts.map +1 -0
  114. package/dist/providers/codex/durable/CodexPrintRunner.js +78 -0
  115. package/dist/providers/codex/durable/CodexPrintRunner.js.map +1 -0
  116. package/dist/providers/codex/durable/CodexStreamParser.d.ts +21 -0
  117. package/dist/providers/codex/durable/CodexStreamParser.d.ts.map +1 -0
  118. package/dist/providers/codex/durable/CodexStreamParser.js +86 -0
  119. package/dist/providers/codex/durable/CodexStreamParser.js.map +1 -0
  120. package/dist/providers/pi/durable/PiCliProbe.d.ts +20 -0
  121. package/dist/providers/pi/durable/PiCliProbe.d.ts.map +1 -0
  122. package/dist/providers/pi/durable/PiCliProbe.js +51 -0
  123. package/dist/providers/pi/durable/PiCliProbe.js.map +1 -0
  124. package/dist/providers/pi/durable/PiPrintAgentService.d.ts +39 -0
  125. package/dist/providers/pi/durable/PiPrintAgentService.d.ts.map +1 -0
  126. package/dist/providers/pi/durable/PiPrintAgentService.js +47 -0
  127. package/dist/providers/pi/durable/PiPrintAgentService.js.map +1 -0
  128. package/dist/providers/pi/durable/PiPrintProtocol.d.ts +4 -0
  129. package/dist/providers/pi/durable/PiPrintProtocol.d.ts.map +1 -0
  130. package/dist/providers/pi/durable/PiPrintProtocol.js +44 -0
  131. package/dist/providers/pi/durable/PiPrintProtocol.js.map +1 -0
  132. package/dist/providers/pi/durable/PiPrintRunner.d.ts +32 -0
  133. package/dist/providers/pi/durable/PiPrintRunner.d.ts.map +1 -0
  134. package/dist/providers/pi/durable/PiPrintRunner.js +61 -0
  135. package/dist/providers/pi/durable/PiPrintRunner.js.map +1 -0
  136. package/dist/providers/pi/durable/PiStreamParser.d.ts +21 -0
  137. package/dist/providers/pi/durable/PiStreamParser.d.ts.map +1 -0
  138. package/dist/providers/pi/durable/PiStreamParser.js +71 -0
  139. package/dist/providers/pi/durable/PiStreamParser.js.map +1 -0
  140. package/package.json +1 -1
  141. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +1 -307
  142. package/src/__tests__/database/DurableAgentsDatabase.test.ts +28 -3
  143. package/src/__tests__/durable/CodexCliProbe.test.ts +63 -0
  144. package/src/__tests__/durable/CodexDurableAgentRepository.test.ts +54 -0
  145. package/src/__tests__/durable/CodexPrintAgent.integration.test.ts +79 -0
  146. package/src/__tests__/durable/CodexPrintAgentService.test.ts +139 -0
  147. package/src/__tests__/durable/CodexPrintRunner.test.ts +189 -0
  148. package/src/__tests__/fixtures/fake-codex.cjs +45 -0
  149. package/src/__tests__/print/ClaudeCliProbe.test.ts +3 -7
  150. package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +11 -21
  151. package/src/__tests__/print/ClaudePrintAgentService.test.ts +33 -1
  152. package/src/__tests__/print/ClaudePrintRunner.test.ts +4 -10
  153. package/src/__tests__/providers/claude/ClaudeAgentMapper.test.ts +84 -0
  154. package/src/__tests__/providers/claude/ClaudeSessionLocator.test.ts +411 -0
  155. package/src/__tests__/providers/pi/durable/PiCliProbe.test.ts +38 -0
  156. package/src/__tests__/providers/pi/durable/PiDurableAgentRepository.test.ts +32 -0
  157. package/src/__tests__/providers/pi/durable/PiPrintAgentService.test.ts +69 -0
  158. package/src/__tests__/providers/pi/durable/PiPrintProtocol.test.ts +33 -0
  159. package/src/__tests__/providers/pi/durable/PiPrintRunner.test.ts +107 -0
  160. package/src/__tests__/providers/pi/durable/PiStreamParser.test.ts +24 -0
  161. package/src/__tests__/utils/ClaudeSessionParser.test.ts +2 -2
  162. package/src/adapters/index.ts +1 -1
  163. package/src/database/connection.ts +3 -1
  164. package/src/database/migrations/004_codex_durable_sessions.sql +1 -0
  165. package/src/durable/DurableAgent.ts +51 -3
  166. package/src/durable/DurableAgentRepository.ts +60 -31
  167. package/src/durable/process.ts +28 -0
  168. package/src/durable/run.ts +74 -0
  169. package/src/durable/utils.ts +61 -0
  170. package/src/index.ts +31 -15
  171. package/src/providers/claude/ClaudeAgentMapper.ts +62 -0
  172. package/src/providers/claude/ClaudeCodeAdapter.ts +160 -0
  173. package/src/{adapters/ClaudeCodeAdapter.ts → providers/claude/ClaudeSessionLocator.ts} +97 -239
  174. package/src/{utils → providers/claude}/ClaudeSessionParser.ts +2 -2
  175. package/src/{durable → providers/claude/durable}/ClaudeCliProbe.ts +4 -10
  176. package/src/providers/claude/durable/ClaudePrintAgentService.ts +66 -0
  177. package/src/providers/claude/durable/ClaudePrintRunner.ts +92 -0
  178. package/src/providers/claude/durable/ClaudeStreamParser.ts +79 -0
  179. package/src/providers/codex/durable/CodexCliProbe.ts +60 -0
  180. package/src/providers/codex/durable/CodexPrintAgentService.ts +65 -0
  181. package/src/providers/codex/durable/CodexPrintRunner.ts +94 -0
  182. package/src/providers/codex/durable/CodexStreamParser.ts +104 -0
  183. package/src/providers/pi/durable/PiCliProbe.ts +48 -0
  184. package/src/providers/pi/durable/PiPrintAgentService.ts +69 -0
  185. package/src/providers/pi/durable/PiPrintProtocol.ts +39 -0
  186. package/src/providers/pi/durable/PiPrintRunner.ts +88 -0
  187. package/src/providers/pi/durable/PiStreamParser.ts +84 -0
  188. package/dist/adapters/ClaudeCodeAdapter.d.ts.map +0 -1
  189. package/dist/adapters/ClaudeCodeAdapter.js.map +0 -1
  190. package/dist/durable/ClaudeCliProbe.d.ts.map +0 -1
  191. package/dist/durable/ClaudeCliProbe.js.map +0 -1
  192. package/dist/durable/ClaudePrintAgentService.d.ts.map +0 -1
  193. package/dist/durable/ClaudePrintAgentService.js +0 -66
  194. package/dist/durable/ClaudePrintAgentService.js.map +0 -1
  195. package/dist/durable/ClaudePrintRunner.d.ts.map +0 -1
  196. package/dist/durable/ClaudePrintRunner.js +0 -128
  197. package/dist/durable/ClaudePrintRunner.js.map +0 -1
  198. package/dist/utils/ClaudeSessionParser.d.ts.map +0 -1
  199. package/dist/utils/ClaudeSessionParser.js.map +0 -1
  200. package/src/durable/ClaudePrintAgentService.ts +0 -94
  201. package/src/durable/ClaudePrintRunner.ts +0 -139
  202. /package/dist/{durable → providers/claude/durable}/ClaudeCliProbe.d.ts +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/__tests__/durable/CodexPrintRunner.test.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { PassThrough, Writable } from 'node:stream';\nimport { describe, expect, it, vi } from 'vitest';\nimport type { CodexDurableAgent } from '../../index.js';\n\nconst SESSION = '22222222-2222-4222-8222-222222222222';\n\nfunction agent(providerSessionId: string | null = null): CodexDurableAgent {\n return {\n id: '11111111-1111-4111-8111-111111111111', name: 'reviewer', provider: 'codex', mode: 'durable',\n cwd: '/project', providerSessionId, state: 'running', sessionHealth: 'uninitialized',\n createdAt: '', updatedAt: '', lastActiveAt: null, lastResult: null, activeRun: null,\n };\n}\n\nfunction fakeSpawn(lines: string[], exitCode = 0, chunks = false) {\n const promptChunks: Buffer[] = [];\n const child = new EventEmitter() as any;\n child.pid = 4242;\n child.stdout = new PassThrough();\n child.stderr = new PassThrough();\n child.kill = vi.fn();\n child.stdin = new Writable({\n write(chunk, _encoding, callback) { promptChunks.push(Buffer.from(chunk)); callback(); },\n final(callback) {\n const output = lines.join('\\n');\n if (chunks) {\n const bytes = Buffer.from(output);\n child.stdout.write(bytes.subarray(0, 7));\n child.stdout.write(bytes.subarray(7));\n } else child.stdout.write(output);\n child.stdout.end();\n queueMicrotask(() => child.emit('close', exitCode, null));\n callback();\n },\n });\n const spawn = vi.fn(() => child);\n return { child, spawn, promptChunks };\n}\n\nfunction events(session = SESSION): string[] {\n return [\n JSON.stringify({ type: 'thread.started', thread_id: session }),\n JSON.stringify({ type: 'turn.started' }),\n JSON.stringify({ type: 'future.event', anything: true }),\n JSON.stringify({ type: 'item.completed', item: { type: 'agent_message', text: 'first' } }),\n JSON.stringify({ type: 'item.completed', item: { type: 'agent_message', text: 'final' } }),\n JSON.stringify({ type: 'turn.completed' }),\n '',\n ];\n}\n\nasync function runner(fixture: ReturnType<typeof fakeSpawn>, maxLineBytes?: number) {\n const api = await import('../../index.js') as Record<string, unknown>;\n expect(api).toHaveProperty('CodexPrintRunner');\n const Runner = api.CodexPrintRunner as new (options: unknown) => any;\n return new Runner({ spawn: fixture.spawn, maxLineBytes, processInspector: {\n getIdentity: () => ({ pid: 4242, startedAt: 'provider-start' }),\n } });\n}\n\ndescribe('CodexPrintRunner', () => {\n it('constructs default process dependencies without spawning', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const Runner = api.CodexPrintRunner as new () => any;\n expect(new Runner()).toBeDefined();\n });\n\n it('binds an initial thread before returning ordered assistant output', async () => {\n const fixture = fakeSpawn(events(), 0, true);\n const instance = await runner(fixture);\n const order: string[] = [];\n const result = await instance.run({\n agent: agent(), prompt: 'secret prompt', executable: 'fake-codex',\n onSpawn: async () => { expect(fixture.promptChunks).toHaveLength(0); order.push('spawn'); },\n onSession: async (id: string) => { expect(id).toBe(SESSION); order.push('session'); },\n });\n\n expect(order).toEqual(['spawn', 'session']);\n expect(fixture.spawn).toHaveBeenCalledWith('fake-codex', ['exec', '--json', '-'], expect.objectContaining({\n cwd: '/project', shell: false, stdio: ['pipe', 'pipe', 'pipe'],\n }));\n expect(JSON.stringify(fixture.spawn.mock.calls)).not.toContain('secret prompt');\n expect(Buffer.concat(fixture.promptChunks).toString()).toBe('secret prompt');\n expect(result).toEqual({ sessionId: SESSION, result: 'final', messages: ['first', 'final'], exitCode: 0 });\n });\n\n it('resumes the exact stored UUID and rejects a mismatch', async () => {\n const mismatch = '33333333-3333-4333-8333-333333333333';\n const fixture = fakeSpawn(events(mismatch));\n const instance = await runner(fixture);\n await expect(instance.run({ agent: agent(SESSION), prompt: 'later', onSpawn: vi.fn(), onSession: vi.fn() }))\n .rejects.toMatchObject({ code: 'CODEX_SESSION_MISMATCH' });\n expect(fixture.spawn.mock.calls[0]![1]).toEqual(['exec', 'resume', '--json', SESSION, '-']);\n });\n\n it.each([\n ['malformed JSON', ['{bad\\n'], 'CODEX_PROTOCOL'],\n ['non-object JSON', ['[]\\n'], 'CODEX_PROTOCOL'],\n ['truncated JSON', ['{}'], 'CODEX_PROTOCOL'],\n ['missing thread', [JSON.stringify({ type: 'item.completed', item: { type: 'agent_message', text: 'x' } }), JSON.stringify({ type: 'turn.completed' }), ''], 'CODEX_PROTOCOL'],\n ['missing assistant', [JSON.stringify({ type: 'thread.started', thread_id: SESSION }), JSON.stringify({ type: 'turn.completed' }), ''], 'CODEX_RESULT_MISSING'],\n ['missing completion', [JSON.stringify({ type: 'thread.started', thread_id: SESSION }), JSON.stringify({ type: 'item.completed', item: { type: 'agent_message', text: 'x' } }), ''], 'CODEX_PROTOCOL'],\n ])('rejects %s', async (_name, lines, code) => {\n const fixture = fakeSpawn(lines as string[]);\n await expect((await runner(fixture)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(), onSession: vi.fn(),\n })).rejects.toMatchObject({ code });\n });\n\n it('rejects oversized output, non-zero exit, and missing process identity without leaking stderr', async () => {\n const oversized = fakeSpawn([`${'x'.repeat(20)}\\n`]);\n await expect((await runner(oversized, 10)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(), onSession: vi.fn(),\n })).rejects.toMatchObject({ code: 'CODEX_PROTOCOL' });\n const oversizedWithoutNewline = fakeSpawn(['x'.repeat(20)]);\n await expect((await runner(oversizedWithoutNewline, 10)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(), onSession: vi.fn(),\n })).rejects.toMatchObject({ code: 'CODEX_PROTOCOL' });\n\n const failed = fakeSpawn(events(), 1);\n failed.child.stderr.end('secret-looking provider diagnostic');\n await expect((await runner(failed)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(), onSession: vi.fn(),\n })).rejects.toMatchObject({ code: 'CODEX_PROCESS' });\n\n const missing = fakeSpawn([]);\n missing.child.pid = undefined;\n await expect((await runner(missing)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(), onSession: vi.fn(),\n })).rejects.toMatchObject({ code: 'CODEX_PROCESS' });\n });\n\n it('rejects invalid or duplicate thread identities', async () => {\n for (const lines of [\n [JSON.stringify({ type: 'thread.started', thread_id: 'bad' }), ''],\n [\n JSON.stringify({ type: 'thread.started', thread_id: SESSION }),\n JSON.stringify({ type: 'thread.started', thread_id: SESSION }),\n '',\n ],\n ]) {\n const fixture = fakeSpawn(lines);\n await expect((await runner(fixture)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(), onSession: vi.fn(),\n })).rejects.toMatchObject({ code: 'CODEX_PROTOCOL' });\n }\n });\n\n it('classifies callback processing failure and kills when spawn persistence fails', async () => {\n const callbackFailure = fakeSpawn(events());\n await expect((await runner(callbackFailure)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(),\n onSession: vi.fn().mockRejectedValue(new Error('storage unavailable')),\n })).rejects.toMatchObject({ code: 'CODEX_PROTOCOL' });\n\n const spawnFailure = fakeSpawn([]);\n const failure = new Error('cannot persist process');\n await expect((await runner(spawnFailure)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn().mockRejectedValue(failure), onSession: vi.fn(),\n })).rejects.toBe(failure);\n expect(spawnFailure.child.kill).toHaveBeenCalledOnce();\n });\n\n it('rejects an unverifiable positive PID', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const fixture = fakeSpawn([]);\n const Runner = api.CodexPrintRunner as new (options: unknown) => any;\n const instance = new Runner({ spawn: fixture.spawn, processInspector: { getIdentity: () => null } });\n await expect(instance.run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(), onSession: vi.fn(),\n })).rejects.toMatchObject({ code: 'CODEX_PROCESS' });\n expect(fixture.child.kill).toHaveBeenCalledOnce();\n });\n\n it('classifies a child spawn error as a process failure', async () => {\n const fixture = fakeSpawn([]);\n fixture.child.stdin = new Writable({\n write(_chunk, _encoding, callback) { callback(); },\n final(callback) {\n queueMicrotask(() => fixture.child.emit('error', new Error('spawn failed')));\n callback();\n },\n });\n await expect((await runner(fixture)).run({\n agent: agent(), prompt: 'x', onSpawn: vi.fn(), onSession: vi.fn(),\n })).rejects.toMatchObject({ code: 'CODEX_PROCESS' });\n });\n});\n"],"names":["EventEmitter","PassThrough","Writable","describe","expect","it","vi","SESSION","agent","providerSessionId","id","name","provider","mode","cwd","state","sessionHealth","createdAt","updatedAt","lastActiveAt","lastResult","activeRun","fakeSpawn","lines","exitCode","chunks","promptChunks","child","pid","stdout","stderr","kill","fn","stdin","write","chunk","_encoding","callback","push","Buffer","from","final","output","join","bytes","subarray","end","queueMicrotask","emit","spawn","events","session","JSON","stringify","type","thread_id","anything","item","text","runner","fixture","maxLineBytes","api","toHaveProperty","Runner","CodexPrintRunner","processInspector","getIdentity","startedAt","toBeDefined","instance","order","result","run","prompt","executable","onSpawn","toHaveLength","onSession","toBe","toEqual","toHaveBeenCalledWith","objectContaining","shell","stdio","mock","calls","not","toContain","concat","toString","sessionId","messages","mismatch","rejects","toMatchObject","code","each","_name","oversized","repeat","oversizedWithoutNewline","failed","missing","undefined","callbackFailure","mockRejectedValue","Error","spawnFailure","failure","toHaveBeenCalledOnce","_chunk"],"mappings":"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"}
@@ -1,8 +1,7 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
+ import { ClaudeCliProbe } from '../../providers/claude/durable/ClaudeCliProbe.js';
2
3
  describe('ClaudeCliProbe', ()=>{
3
4
  it('validates only version/help and requires the print session flags', async ()=>{
4
- const api = await import('../../index.js');
5
- expect(api).toHaveProperty('ClaudeCliProbe');
6
5
  const exec = vi.fn().mockResolvedValueOnce({
7
6
  stdout: '2.1.220\n',
8
7
  stderr: ''
@@ -10,8 +9,7 @@ describe('ClaudeCliProbe', ()=>{
10
9
  stdout: '--print --session-id --resume --output-format stream-json',
11
10
  stderr: ''
12
11
  });
13
- const Probe = api.ClaudeCliProbe;
14
- await expect(new Probe({
12
+ await expect(new ClaudeCliProbe({
15
13
  exec
16
14
  }).validate()).resolves.toEqual({
17
15
  executable: 'claude',
@@ -33,7 +31,6 @@ describe('ClaudeCliProbe', ()=>{
33
31
  ]);
34
32
  });
35
33
  it('rejects a CLI missing a required capability', async ()=>{
36
- const api = await import('../../index.js');
37
34
  const exec = vi.fn().mockResolvedValueOnce({
38
35
  stdout: 'old',
39
36
  stderr: ''
@@ -41,8 +38,7 @@ describe('ClaudeCliProbe', ()=>{
41
38
  stdout: '--print only',
42
39
  stderr: ''
43
40
  });
44
- const Probe = api.ClaudeCliProbe;
45
- await expect(new Probe({
41
+ await expect(new ClaudeCliProbe({
46
42
  exec
47
43
  }).validate()).rejects.toMatchObject({
48
44
  code: 'CLAUDE_CLI_UNSUPPORTED'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/print/ClaudeCliProbe.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\n\ndescribe('ClaudeCliProbe', () => {\n it('validates only version/help and requires the print session flags', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n expect(api).toHaveProperty('ClaudeCliProbe');\n const exec = vi.fn()\n .mockResolvedValueOnce({ stdout: '2.1.220\\n', stderr: '' })\n .mockResolvedValueOnce({\n stdout: '--print --session-id --resume --output-format stream-json', stderr: '',\n });\n const Probe = api.ClaudeCliProbe as new (options: unknown) => { validate(): Promise<unknown> };\n\n await expect(new Probe({ exec }).validate()).resolves.toEqual({\n executable: 'claude', version: '2.1.220',\n });\n expect(exec.mock.calls).toEqual([\n ['claude', ['--version']],\n ['claude', ['--help']],\n ]);\n });\n\n it('rejects a CLI missing a required capability', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const exec = vi.fn()\n .mockResolvedValueOnce({ stdout: 'old', stderr: '' })\n .mockResolvedValueOnce({ stdout: '--print only', stderr: '' });\n const Probe = api.ClaudeCliProbe as new (options: unknown) => { validate(): Promise<unknown> };\n\n await expect(new Probe({ exec }).validate()).rejects.toMatchObject({ code: 'CLAUDE_CLI_UNSUPPORTED' });\n });\n});\n"],"names":["describe","expect","it","vi","api","toHaveProperty","exec","fn","mockResolvedValueOnce","stdout","stderr","Probe","ClaudeCliProbe","validate","resolves","toEqual","executable","version","mock","calls","rejects","toMatchObject","code"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAS;AAElDH,SAAS,kBAAkB;IACvBE,GAAG,oEAAoE;QACnE,MAAME,MAAM,MAAM,MAAM,CAAC;QACzBH,OAAOG,KAAKC,cAAc,CAAC;QAC3B,MAAMC,OAAOH,GAAGI,EAAE,GACbC,qBAAqB,CAAC;YAAEC,QAAQ;YAAaC,QAAQ;QAAG,GACxDF,qBAAqB,CAAC;YACnBC,QAAQ;YAA6DC,QAAQ;QACjF;QACJ,MAAMC,QAAQP,IAAIQ,cAAc;QAEhC,MAAMX,OAAO,IAAIU,MAAM;YAAEL;QAAK,GAAGO,QAAQ,IAAIC,QAAQ,CAACC,OAAO,CAAC;YAC1DC,YAAY;YAAUC,SAAS;QACnC;QACAhB,OAAOK,KAAKY,IAAI,CAACC,KAAK,EAAEJ,OAAO,CAAC;YAC5B;gBAAC;gBAAU;oBAAC;iBAAY;aAAC;YACzB;gBAAC;gBAAU;oBAAC;iBAAS;aAAC;SACzB;IACL;IAEAb,GAAG,+CAA+C;QAC9C,MAAME,MAAM,MAAM,MAAM,CAAC;QACzB,MAAME,OAAOH,GAAGI,EAAE,GACbC,qBAAqB,CAAC;YAAEC,QAAQ;YAAOC,QAAQ;QAAG,GAClDF,qBAAqB,CAAC;YAAEC,QAAQ;YAAgBC,QAAQ;QAAG;QAChE,MAAMC,QAAQP,IAAIQ,cAAc;QAEhC,MAAMX,OAAO,IAAIU,MAAM;YAAEL;QAAK,GAAGO,QAAQ,IAAIO,OAAO,CAACC,aAAa,CAAC;YAAEC,MAAM;QAAyB;IACxG;AACJ"}
1
+ {"version":3,"sources":["../../../src/__tests__/print/ClaudeCliProbe.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\nimport { ClaudeCliProbe } from '../../providers/claude/durable/ClaudeCliProbe.js';\n\ndescribe('ClaudeCliProbe', () => {\n it('validates only version/help and requires the print session flags', async () => {\n const exec = vi.fn()\n .mockResolvedValueOnce({ stdout: '2.1.220\\n', stderr: '' })\n .mockResolvedValueOnce({\n stdout: '--print --session-id --resume --output-format stream-json', stderr: '',\n });\n\n await expect(new ClaudeCliProbe({ exec }).validate()).resolves.toEqual({\n executable: 'claude', version: '2.1.220',\n });\n expect(exec.mock.calls).toEqual([\n ['claude', ['--version']],\n ['claude', ['--help']],\n ]);\n });\n\n it('rejects a CLI missing a required capability', async () => {\n const exec = vi.fn()\n .mockResolvedValueOnce({ stdout: 'old', stderr: '' })\n .mockResolvedValueOnce({ stdout: '--print only', stderr: '' });\n\n await expect(new ClaudeCliProbe({ exec }).validate()).rejects.toMatchObject({ code: 'CLAUDE_CLI_UNSUPPORTED' });\n });\n});\n"],"names":["describe","expect","it","vi","ClaudeCliProbe","exec","fn","mockResolvedValueOnce","stdout","stderr","validate","resolves","toEqual","executable","version","mock","calls","rejects","toMatchObject","code"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAS;AAClD,SAASC,cAAc,QAAQ,mDAAmD;AAElFJ,SAAS,kBAAkB;IACvBE,GAAG,oEAAoE;QACnE,MAAMG,OAAOF,GAAGG,EAAE,GACbC,qBAAqB,CAAC;YAAEC,QAAQ;YAAaC,QAAQ;QAAG,GACxDF,qBAAqB,CAAC;YACnBC,QAAQ;YAA6DC,QAAQ;QACjF;QAEJ,MAAMR,OAAO,IAAIG,eAAe;YAAEC;QAAK,GAAGK,QAAQ,IAAIC,QAAQ,CAACC,OAAO,CAAC;YACnEC,YAAY;YAAUC,SAAS;QACnC;QACAb,OAAOI,KAAKU,IAAI,CAACC,KAAK,EAAEJ,OAAO,CAAC;YAC5B;gBAAC;gBAAU;oBAAC;iBAAY;aAAC;YACzB;gBAAC;gBAAU;oBAAC;iBAAS;aAAC;SACzB;IACL;IAEAV,GAAG,+CAA+C;QAC9C,MAAMG,OAAOF,GAAGG,EAAE,GACbC,qBAAqB,CAAC;YAAEC,QAAQ;YAAOC,QAAQ;QAAG,GAClDF,qBAAqB,CAAC;YAAEC,QAAQ;YAAgBC,QAAQ;QAAG;QAEhE,MAAMR,OAAO,IAAIG,eAAe;YAAEC;QAAK,GAAGK,QAAQ,IAAIO,OAAO,CAACC,aAAa,CAAC;YAAEC,MAAM;QAAyB;IACjH;AACJ"}
@@ -1,14 +1,10 @@
1
1
  import fs from 'node:fs';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import { afterEach, describe, expect, it } from 'vitest';
6
- import { ClaudeCliProbe, ClaudePrintAgentService, ClaudePrintRunner, DurableAgentRepository } from '../../index.js';
4
+ import { afterEach, describe, expect, it, vi } from 'vitest';
5
+ import { ClaudePrintAgentService, DurableAgentRepository } from '../../index.js';
7
6
  const roots = [];
8
- const originalCapture = process.env.AI_DEVKIT_FAKE_CLAUDE_CAPTURE;
9
7
  afterEach(()=>{
10
- if (originalCapture === undefined) delete process.env.AI_DEVKIT_FAKE_CLAUDE_CAPTURE;
11
- else process.env.AI_DEVKIT_FAKE_CLAUDE_CAPTURE = originalCapture;
12
8
  for (const root of roots.splice(0))fs.rmSync(root, {
13
9
  recursive: true,
14
10
  force: true
@@ -20,9 +16,6 @@ describe('Claude durable-agent fake-provider journey', ()=>{
20
16
  roots.push(root);
21
17
  const cwd = path.join(root, 'project');
22
18
  fs.mkdirSync(cwd);
23
- const capture = path.join(root, 'capture.jsonl');
24
- process.env.AI_DEVKIT_FAKE_CLAUDE_CAPTURE = capture;
25
- const executable = fileURLToPath(new URL('../fixtures/fake-claude.cjs', import.meta.url));
26
19
  const processInspector = {
27
20
  getIdentity: (pid)=>({
28
21
  pid,
@@ -33,40 +26,44 @@ describe('Claude durable-agent fake-provider journey', ()=>{
33
26
  dbPath: path.join(root, 'state', 'agents.db'),
34
27
  processInspector
35
28
  });
29
+ const runner = {
30
+ run: vi.fn().mockImplementation(async (request)=>{
31
+ await request.onSpawn({
32
+ pid: 42,
33
+ startedAt: 'process-42'
34
+ });
35
+ return {
36
+ sessionId: request.agent.providerSessionId,
37
+ result: `answer:${request.prompt}`,
38
+ exitCode: 0
39
+ };
40
+ })
41
+ };
36
42
  const service = new ClaudePrintAgentService({
37
43
  repository,
38
- probe: new ClaudeCliProbe({
39
- executable
40
- }),
41
- runner: new ClaudePrintRunner({
42
- processInspector
43
- }),
44
- executable
44
+ probe: {
45
+ validate: vi.fn()
46
+ },
47
+ runner
45
48
  });
46
49
  const created = await service.create({
47
50
  name: 'reviewer',
48
51
  cwd
49
52
  });
50
- expect(fs.existsSync(capture)).toBe(false);
51
53
  await expect(service.send(created.id, 'first secret')).resolves.toMatchObject({
52
54
  result: 'answer:first secret'
53
55
  });
54
56
  await expect(service.send(created.id, 'follow up')).resolves.toMatchObject({
55
57
  result: 'answer:follow up'
56
58
  });
57
- const invocations = fs.readFileSync(capture, 'utf8').trim().split('\n').map((line)=>JSON.parse(line));
58
- expect(invocations[0]).toMatchObject({
59
- prompt: 'first secret',
60
- cwd: fs.realpathSync(cwd)
59
+ expect(runner.run.mock.calls[0][0]).toMatchObject({
60
+ firstRun: true,
61
+ prompt: 'first secret'
61
62
  });
62
- expect(invocations[0].args).toContain('--session-id');
63
- expect(invocations[0].args).not.toContain('first secret');
64
- expect(invocations[1]).toMatchObject({
65
- prompt: 'follow up',
66
- cwd: fs.realpathSync(cwd)
63
+ expect(runner.run.mock.calls[1][0]).toMatchObject({
64
+ firstRun: false,
65
+ prompt: 'follow up'
67
66
  });
68
- expect(invocations[1].args).toContain('--resume');
69
- expect(invocations[1].args[invocations[1].args.indexOf('--resume') + 1]).toBe(created.providerSessionId);
70
67
  const persisted = await repository.getById(created.id);
71
68
  expect(persisted).toMatchObject({
72
69
  state: 'ready',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/print/ClaudePrintAgent.integration.test.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { afterEach, describe, expect, it } from 'vitest';\nimport {\n ClaudeCliProbe,\n ClaudePrintAgentService,\n ClaudePrintRunner,\n DurableAgentRepository,\n type ProcessInspector,\n} from '../../index.js';\n\nconst roots: string[] = [];\nconst originalCapture = process.env.AI_DEVKIT_FAKE_CLAUDE_CAPTURE;\n\nafterEach(() => {\n if (originalCapture === undefined) delete process.env.AI_DEVKIT_FAKE_CLAUDE_CAPTURE;\n else process.env.AI_DEVKIT_FAKE_CLAUDE_CAPTURE = originalCapture;\n for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });\n});\n\ndescribe('Claude durable-agent fake-provider journey', () => {\n it('creates without invocation, then starts and resumes the same session through stdin', async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), 'durable-agent-integration-'));\n roots.push(root);\n const cwd = path.join(root, 'project');\n fs.mkdirSync(cwd);\n const capture = path.join(root, 'capture.jsonl');\n process.env.AI_DEVKIT_FAKE_CLAUDE_CAPTURE = capture;\n const executable = fileURLToPath(new URL('../fixtures/fake-claude.cjs', import.meta.url));\n const processInspector: ProcessInspector = {\n getIdentity: (pid) => ({ pid, startedAt: `process-${pid}` }),\n };\n const repository = new DurableAgentRepository({\n dbPath: path.join(root, 'state', 'agents.db'),\n processInspector,\n });\n const service = new ClaudePrintAgentService({\n repository,\n probe: new ClaudeCliProbe({ executable }),\n runner: new ClaudePrintRunner({ processInspector }),\n executable,\n });\n\n const created = await service.create({ name: 'reviewer', cwd });\n expect(fs.existsSync(capture)).toBe(false);\n\n await expect(service.send(created.id, 'first secret')).resolves.toMatchObject({ result: 'answer:first secret' });\n await expect(service.send(created.id, 'follow up')).resolves.toMatchObject({ result: 'answer:follow up' });\n\n const invocations = fs.readFileSync(capture, 'utf8').trim().split('\\n').map((line) => JSON.parse(line));\n expect(invocations[0]).toMatchObject({ prompt: 'first secret', cwd: fs.realpathSync(cwd) });\n expect(invocations[0].args).toContain('--session-id');\n expect(invocations[0].args).not.toContain('first secret');\n expect(invocations[1]).toMatchObject({ prompt: 'follow up', cwd: fs.realpathSync(cwd) });\n expect(invocations[1].args).toContain('--resume');\n expect(invocations[1].args[invocations[1].args.indexOf('--resume') + 1]).toBe(created.providerSessionId);\n\n const persisted = await repository.getById(created.id);\n expect(persisted).toMatchObject({ state: 'ready', sessionHealth: 'healthy' });\n });\n});\n"],"names":["fs","os","path","fileURLToPath","afterEach","describe","expect","it","ClaudeCliProbe","ClaudePrintAgentService","ClaudePrintRunner","DurableAgentRepository","roots","originalCapture","process","env","AI_DEVKIT_FAKE_CLAUDE_CAPTURE","undefined","root","splice","rmSync","recursive","force","mkdtempSync","join","tmpdir","push","cwd","mkdirSync","capture","executable","URL","url","processInspector","getIdentity","pid","startedAt","repository","dbPath","service","probe","runner","created","create","name","existsSync","toBe","send","id","resolves","toMatchObject","result","invocations","readFileSync","trim","split","map","line","JSON","parse","prompt","realpathSync","args","toContain","not","indexOf","providerSessionId","persisted","getById","state","sessionHealth"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/__tests__/print/ClaudePrintAgent.integration.test.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport {\n ClaudePrintAgentService,\n DurableAgentRepository,\n type ProcessInspector,\n} from '../../index.js';\n\nconst roots: string[] = [];\n\nafterEach(() => {\n for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });\n});\n\ndescribe('Claude durable-agent fake-provider journey', () => {\n it('creates without invocation, then starts and resumes the same session through stdin', async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), 'durable-agent-integration-'));\n roots.push(root);\n const cwd = path.join(root, 'project');\n fs.mkdirSync(cwd);\n const processInspector: ProcessInspector = {\n getIdentity: (pid) => ({ pid, startedAt: `process-${pid}` }),\n };\n const repository = new DurableAgentRepository({\n dbPath: path.join(root, 'state', 'agents.db'),\n processInspector,\n });\n const runner = {\n run: vi.fn().mockImplementation(async (request) => {\n await request.onSpawn({ pid: 42, startedAt: 'process-42' });\n return { sessionId: request.agent.providerSessionId, result: `answer:${request.prompt}`, exitCode: 0 };\n }),\n };\n const service = new ClaudePrintAgentService({\n repository,\n probe: { validate: vi.fn() },\n runner,\n });\n\n const created = await service.create({ name: 'reviewer', cwd });\n\n await expect(service.send(created.id, 'first secret')).resolves.toMatchObject({ result: 'answer:first secret' });\n await expect(service.send(created.id, 'follow up')).resolves.toMatchObject({ result: 'answer:follow up' });\n\n expect(runner.run.mock.calls[0][0]).toMatchObject({ firstRun: true, prompt: 'first secret' });\n expect(runner.run.mock.calls[1][0]).toMatchObject({ firstRun: false, prompt: 'follow up' });\n\n const persisted = await repository.getById(created.id);\n expect(persisted).toMatchObject({ state: 'ready', sessionHealth: 'healthy' });\n });\n});\n"],"names":["fs","os","path","afterEach","describe","expect","it","vi","ClaudePrintAgentService","DurableAgentRepository","roots","root","splice","rmSync","recursive","force","mkdtempSync","join","tmpdir","push","cwd","mkdirSync","processInspector","getIdentity","pid","startedAt","repository","dbPath","runner","run","fn","mockImplementation","request","onSpawn","sessionId","agent","providerSessionId","result","prompt","exitCode","service","probe","validate","created","create","name","send","id","resolves","toMatchObject","mock","calls","firstRun","persisted","getById","state","sessionHealth"],"mappings":"AAAA,OAAOA,QAAQ,UAAU;AACzB,OAAOC,QAAQ,UAAU;AACzB,OAAOC,UAAU,YAAY;AAC7B,SAASC,SAAS,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAS;AAC7D,SACIC,uBAAuB,EACvBC,sBAAsB,QAEnB,iBAAiB;AAExB,MAAMC,QAAkB,EAAE;AAE1BP,UAAU;IACN,KAAK,MAAMQ,QAAQD,MAAME,MAAM,CAAC,GAAIZ,GAAGa,MAAM,CAACF,MAAM;QAAEG,WAAW;QAAMC,OAAO;IAAK;AACvF;AAEAX,SAAS,8CAA8C;IACnDE,GAAG,sFAAsF;QACrF,MAAMK,OAAOX,GAAGgB,WAAW,CAACd,KAAKe,IAAI,CAAChB,GAAGiB,MAAM,IAAI;QACnDR,MAAMS,IAAI,CAACR;QACX,MAAMS,MAAMlB,KAAKe,IAAI,CAACN,MAAM;QAC5BX,GAAGqB,SAAS,CAACD;QACb,MAAME,mBAAqC;YACvCC,aAAa,CAACC,MAAS,CAAA;oBAAEA;oBAAKC,WAAW,CAAC,QAAQ,EAAED,KAAK;gBAAC,CAAA;QAC9D;QACA,MAAME,aAAa,IAAIjB,uBAAuB;YAC1CkB,QAAQzB,KAAKe,IAAI,CAACN,MAAM,SAAS;YACjCW;QACJ;QACA,MAAMM,SAAS;YACXC,KAAKtB,GAAGuB,EAAE,GAAGC,kBAAkB,CAAC,OAAOC;gBACnC,MAAMA,QAAQC,OAAO,CAAC;oBAAET,KAAK;oBAAIC,WAAW;gBAAa;gBACzD,OAAO;oBAAES,WAAWF,QAAQG,KAAK,CAACC,iBAAiB;oBAAEC,QAAQ,CAAC,OAAO,EAAEL,QAAQM,MAAM,EAAE;oBAAEC,UAAU;gBAAE;YACzG;QACJ;QACA,MAAMC,UAAU,IAAIhC,wBAAwB;YACxCkB;YACAe,OAAO;gBAAEC,UAAUnC,GAAGuB,EAAE;YAAG;YAC3BF;QACJ;QAEA,MAAMe,UAAU,MAAMH,QAAQI,MAAM,CAAC;YAAEC,MAAM;YAAYzB;QAAI;QAE7D,MAAMf,OAAOmC,QAAQM,IAAI,CAACH,QAAQI,EAAE,EAAE,iBAAiBC,QAAQ,CAACC,aAAa,CAAC;YAAEZ,QAAQ;QAAsB;QAC9G,MAAMhC,OAAOmC,QAAQM,IAAI,CAACH,QAAQI,EAAE,EAAE,cAAcC,QAAQ,CAACC,aAAa,CAAC;YAAEZ,QAAQ;QAAmB;QAExGhC,OAAOuB,OAAOC,GAAG,CAACqB,IAAI,CAACC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAEF,aAAa,CAAC;YAAEG,UAAU;YAAMd,QAAQ;QAAe;QAC3FjC,OAAOuB,OAAOC,GAAG,CAACqB,IAAI,CAACC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAEF,aAAa,CAAC;YAAEG,UAAU;YAAOd,QAAQ;QAAY;QAEzF,MAAMe,YAAY,MAAM3B,WAAW4B,OAAO,CAACX,QAAQI,EAAE;QACrD1C,OAAOgD,WAAWJ,aAAa,CAAC;YAAEM,OAAO;YAASC,eAAe;QAAU;IAC/E;AACJ"}
@@ -41,6 +41,7 @@ describe('ClaudePrintAgentService', ()=>{
41
41
  const base = {
42
42
  id: 'id',
43
43
  name: 'reviewer',
44
+ provider: 'claude',
44
45
  providerSessionId: 'session',
45
46
  sessionHealth: 'uninitialized'
46
47
  };
@@ -103,6 +104,70 @@ describe('ClaudePrintAgentService', ()=>{
103
104
  sessionHealth: 'healthy'
104
105
  }));
105
106
  });
107
+ it('rejects a non-Claude target without acquiring or mutating it', async ()=>{
108
+ const api = await import('../../index.js');
109
+ const Service = api.ClaudePrintAgentService;
110
+ const target = {
111
+ id: 'id',
112
+ name: 'reviewer',
113
+ provider: 'pi'
114
+ };
115
+ const repository = {
116
+ resolve: vi.fn().mockResolvedValue(target),
117
+ acquireRun: vi.fn(),
118
+ recordProviderProcess: vi.fn(),
119
+ completeRun: vi.fn()
120
+ };
121
+ await expect(new Service({
122
+ repository,
123
+ probe: {
124
+ validate: vi.fn()
125
+ },
126
+ runner: {
127
+ run: vi.fn()
128
+ }
129
+ }).send('reviewer', 'x')).rejects.toMatchObject({
130
+ code: 'CLAUDE_PRINT_UNSUPPORTED'
131
+ });
132
+ expect(repository.acquireRun).not.toHaveBeenCalled();
133
+ expect(repository.completeRun).not.toHaveBeenCalled();
134
+ });
135
+ it('does not turn a successful completion write failure into a second completion', async ()=>{
136
+ const api = await import('../../index.js');
137
+ const Service = api.ClaudePrintAgentService;
138
+ const target = {
139
+ id: 'id',
140
+ name: 'reviewer',
141
+ provider: 'claude',
142
+ providerSessionId: 'session',
143
+ sessionHealth: 'healthy'
144
+ };
145
+ const completionFailure = new Error('completion failed');
146
+ const repository = {
147
+ resolve: vi.fn().mockResolvedValue(target),
148
+ acquireRun: vi.fn().mockResolvedValue({
149
+ agent: target,
150
+ token: 'one'
151
+ }),
152
+ recordProviderProcess: vi.fn(),
153
+ completeRun: vi.fn().mockRejectedValue(completionFailure)
154
+ };
155
+ const runner = {
156
+ run: vi.fn().mockResolvedValue({
157
+ sessionId: 'session',
158
+ result: 'answer',
159
+ exitCode: 0
160
+ })
161
+ };
162
+ await expect(new Service({
163
+ repository,
164
+ probe: {
165
+ validate: vi.fn()
166
+ },
167
+ runner
168
+ }).send('reviewer', 'x')).rejects.toBe(completionFailure);
169
+ expect(repository.completeRun).toHaveBeenCalledOnce();
170
+ });
106
171
  });
107
172
 
108
173
  //# sourceMappingURL=ClaudePrintAgentService.test.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/print/ClaudePrintAgentService.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\n\ndescribe('ClaudePrintAgentService', () => {\n it('validates before create and does not run Claude', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n expect(api).toHaveProperty('ClaudePrintAgentService');\n const probe = { validate: vi.fn().mockResolvedValue({ executable: 'claude', version: '2.1.220' }) };\n const repository = { create: vi.fn().mockResolvedValue({ id: 'agent-id', name: 'reviewer' }) };\n const runner = { run: vi.fn() };\n const Service = api.ClaudePrintAgentService as new (options: unknown) => any;\n\n await expect(new Service({ repository, probe, runner }).create({ name: 'reviewer', cwd: '/project' }))\n .resolves.toMatchObject({ id: 'agent-id' });\n expect(probe.validate).toHaveBeenCalledOnce();\n expect(repository.create).toHaveBeenCalledWith({ name: 'reviewer', cwd: '/project' });\n expect(runner.run).not.toHaveBeenCalled();\n });\n\n it('runs first and resumed sends and records provider identity/results', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const base = { id: 'id', name: 'reviewer', providerSessionId: 'session', sessionHealth: 'uninitialized' };\n const repository = {\n resolve: vi.fn().mockResolvedValue(base),\n acquireRun: vi.fn()\n .mockResolvedValueOnce({ agent: base, token: 'one' })\n .mockResolvedValueOnce({ agent: { ...base, sessionHealth: 'healthy' }, token: 'two' }),\n recordProviderProcess: vi.fn(), completeRun: vi.fn().mockResolvedValue({}),\n };\n const runner = { run: vi.fn().mockImplementation(async (request) => {\n await request.onSpawn({ pid: 42, startedAt: 'start' });\n return { sessionId: 'session', result: 'answer', exitCode: 0 };\n }) };\n const Service = api.ClaudePrintAgentService as new (options: unknown) => any;\n const service = new Service({ repository, probe: { validate: vi.fn() }, runner, executable: 'fake-claude' });\n\n await service.send('reviewer', 'first');\n await service.send('id', 'later');\n\n expect(runner.run.mock.calls[0][0]).toMatchObject({ prompt: 'first', firstRun: true, executable: 'fake-claude' });\n expect(runner.run.mock.calls[1][0]).toMatchObject({ prompt: 'later', firstRun: false, executable: 'fake-claude' });\n expect(repository.recordProviderProcess).toHaveBeenCalledWith('id', 'one', { pid: 42, startedAt: 'start' });\n expect(repository.completeRun).toHaveBeenCalledWith('id', 'one', expect.objectContaining({\n status: 'succeeded', exitCode: 0, sessionHealth: 'healthy',\n }));\n });\n});\n"],"names":["describe","expect","it","vi","api","toHaveProperty","probe","validate","fn","mockResolvedValue","executable","version","repository","create","id","name","runner","run","Service","ClaudePrintAgentService","cwd","resolves","toMatchObject","toHaveBeenCalledOnce","toHaveBeenCalledWith","not","toHaveBeenCalled","base","providerSessionId","sessionHealth","resolve","acquireRun","mockResolvedValueOnce","agent","token","recordProviderProcess","completeRun","mockImplementation","request","onSpawn","pid","startedAt","sessionId","result","exitCode","service","send","mock","calls","prompt","firstRun","objectContaining","status"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAS;AAElDH,SAAS,2BAA2B;IAChCE,GAAG,mDAAmD;QAClD,MAAME,MAAM,MAAM,MAAM,CAAC;QACzBH,OAAOG,KAAKC,cAAc,CAAC;QAC3B,MAAMC,QAAQ;YAAEC,UAAUJ,GAAGK,EAAE,GAAGC,iBAAiB,CAAC;gBAAEC,YAAY;gBAAUC,SAAS;YAAU;QAAG;QAClG,MAAMC,aAAa;YAAEC,QAAQV,GAAGK,EAAE,GAAGC,iBAAiB,CAAC;gBAAEK,IAAI;gBAAYC,MAAM;YAAW;QAAG;QAC7F,MAAMC,SAAS;YAAEC,KAAKd,GAAGK,EAAE;QAAG;QAC9B,MAAMU,UAAUd,IAAIe,uBAAuB;QAE3C,MAAMlB,OAAO,IAAIiB,QAAQ;YAAEN;YAAYN;YAAOU;QAAO,GAAGH,MAAM,CAAC;YAAEE,MAAM;YAAYK,KAAK;QAAW,IAC9FC,QAAQ,CAACC,aAAa,CAAC;YAAER,IAAI;QAAW;QAC7Cb,OAAOK,MAAMC,QAAQ,EAAEgB,oBAAoB;QAC3CtB,OAAOW,WAAWC,MAAM,EAAEW,oBAAoB,CAAC;YAAET,MAAM;YAAYK,KAAK;QAAW;QACnFnB,OAAOe,OAAOC,GAAG,EAAEQ,GAAG,CAACC,gBAAgB;IAC3C;IAEAxB,GAAG,sEAAsE;QACrE,MAAME,MAAM,MAAM,MAAM,CAAC;QACzB,MAAMuB,OAAO;YAAEb,IAAI;YAAMC,MAAM;YAAYa,mBAAmB;YAAWC,eAAe;QAAgB;QACxG,MAAMjB,aAAa;YACfkB,SAAS3B,GAAGK,EAAE,GAAGC,iBAAiB,CAACkB;YACnCI,YAAY5B,GAAGK,EAAE,GACZwB,qBAAqB,CAAC;gBAAEC,OAAON;gBAAMO,OAAO;YAAM,GAClDF,qBAAqB,CAAC;gBAAEC,OAAO;oBAAE,GAAGN,IAAI;oBAAEE,eAAe;gBAAU;gBAAGK,OAAO;YAAM;YACxFC,uBAAuBhC,GAAGK,EAAE;YAAI4B,aAAajC,GAAGK,EAAE,GAAGC,iBAAiB,CAAC,CAAC;QAC5E;QACA,MAAMO,SAAS;YAAEC,KAAKd,GAAGK,EAAE,GAAG6B,kBAAkB,CAAC,OAAOC;gBACpD,MAAMA,QAAQC,OAAO,CAAC;oBAAEC,KAAK;oBAAIC,WAAW;gBAAQ;gBACpD,OAAO;oBAAEC,WAAW;oBAAWC,QAAQ;oBAAUC,UAAU;gBAAE;YACjE;QAAG;QACH,MAAM1B,UAAUd,IAAIe,uBAAuB;QAC3C,MAAM0B,UAAU,IAAI3B,QAAQ;YAAEN;YAAYN,OAAO;gBAAEC,UAAUJ,GAAGK,EAAE;YAAG;YAAGQ;YAAQN,YAAY;QAAc;QAE1G,MAAMmC,QAAQC,IAAI,CAAC,YAAY;QAC/B,MAAMD,QAAQC,IAAI,CAAC,MAAM;QAEzB7C,OAAOe,OAAOC,GAAG,CAAC8B,IAAI,CAACC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE1B,aAAa,CAAC;YAAE2B,QAAQ;YAASC,UAAU;YAAMxC,YAAY;QAAc;QAC/GT,OAAOe,OAAOC,GAAG,CAAC8B,IAAI,CAACC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE1B,aAAa,CAAC;YAAE2B,QAAQ;YAASC,UAAU;YAAOxC,YAAY;QAAc;QAChHT,OAAOW,WAAWuB,qBAAqB,EAAEX,oBAAoB,CAAC,MAAM,OAAO;YAAEgB,KAAK;YAAIC,WAAW;QAAQ;QACzGxC,OAAOW,WAAWwB,WAAW,EAAEZ,oBAAoB,CAAC,MAAM,OAAOvB,OAAOkD,gBAAgB,CAAC;YACrFC,QAAQ;YAAaR,UAAU;YAAGf,eAAe;QACrD;IACJ;AACJ"}
1
+ {"version":3,"sources":["../../../src/__tests__/print/ClaudePrintAgentService.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\n\ndescribe('ClaudePrintAgentService', () => {\n it('validates before create and does not run Claude', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n expect(api).toHaveProperty('ClaudePrintAgentService');\n const probe = { validate: vi.fn().mockResolvedValue({ executable: 'claude', version: '2.1.220' }) };\n const repository = { create: vi.fn().mockResolvedValue({ id: 'agent-id', name: 'reviewer' }) };\n const runner = { run: vi.fn() };\n const Service = api.ClaudePrintAgentService as new (options: unknown) => any;\n\n await expect(new Service({ repository, probe, runner }).create({ name: 'reviewer', cwd: '/project' }))\n .resolves.toMatchObject({ id: 'agent-id' });\n expect(probe.validate).toHaveBeenCalledOnce();\n expect(repository.create).toHaveBeenCalledWith({ name: 'reviewer', cwd: '/project' });\n expect(runner.run).not.toHaveBeenCalled();\n });\n\n it('runs first and resumed sends and records provider identity/results', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const base = { id: 'id', name: 'reviewer', provider: 'claude', providerSessionId: 'session', sessionHealth: 'uninitialized' };\n const repository = {\n resolve: vi.fn().mockResolvedValue(base),\n acquireRun: vi.fn()\n .mockResolvedValueOnce({ agent: base, token: 'one' })\n .mockResolvedValueOnce({ agent: { ...base, sessionHealth: 'healthy' }, token: 'two' }),\n recordProviderProcess: vi.fn(), completeRun: vi.fn().mockResolvedValue({}),\n };\n const runner = { run: vi.fn().mockImplementation(async (request) => {\n await request.onSpawn({ pid: 42, startedAt: 'start' });\n return { sessionId: 'session', result: 'answer', exitCode: 0 };\n }) };\n const Service = api.ClaudePrintAgentService as new (options: unknown) => any;\n const service = new Service({ repository, probe: { validate: vi.fn() }, runner, executable: 'fake-claude' });\n\n await service.send('reviewer', 'first');\n await service.send('id', 'later');\n\n expect(runner.run.mock.calls[0][0]).toMatchObject({ prompt: 'first', firstRun: true, executable: 'fake-claude' });\n expect(runner.run.mock.calls[1][0]).toMatchObject({ prompt: 'later', firstRun: false, executable: 'fake-claude' });\n expect(repository.recordProviderProcess).toHaveBeenCalledWith('id', 'one', { pid: 42, startedAt: 'start' });\n expect(repository.completeRun).toHaveBeenCalledWith('id', 'one', expect.objectContaining({\n status: 'succeeded', exitCode: 0, sessionHealth: 'healthy',\n }));\n });\n\n it('rejects a non-Claude target without acquiring or mutating it', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const Service = api.ClaudePrintAgentService as new (options: unknown) => any;\n const target = { id: 'id', name: 'reviewer', provider: 'pi' };\n const repository = {\n resolve: vi.fn().mockResolvedValue(target),\n acquireRun: vi.fn(), recordProviderProcess: vi.fn(), completeRun: vi.fn(),\n };\n\n await expect(new Service({ repository, probe: { validate: vi.fn() }, runner: { run: vi.fn() } })\n .send('reviewer', 'x')).rejects.toMatchObject({ code: 'CLAUDE_PRINT_UNSUPPORTED' });\n expect(repository.acquireRun).not.toHaveBeenCalled();\n expect(repository.completeRun).not.toHaveBeenCalled();\n });\n\n it('does not turn a successful completion write failure into a second completion', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const Service = api.ClaudePrintAgentService as new (options: unknown) => any;\n const target = { id: 'id', name: 'reviewer', provider: 'claude', providerSessionId: 'session', sessionHealth: 'healthy' };\n const completionFailure = new Error('completion failed');\n const repository = {\n resolve: vi.fn().mockResolvedValue(target),\n acquireRun: vi.fn().mockResolvedValue({ agent: target, token: 'one' }),\n recordProviderProcess: vi.fn(), completeRun: vi.fn().mockRejectedValue(completionFailure),\n };\n const runner = { run: vi.fn().mockResolvedValue({ sessionId: 'session', result: 'answer', exitCode: 0 }) };\n\n await expect(new Service({ repository, probe: { validate: vi.fn() }, runner })\n .send('reviewer', 'x')).rejects.toBe(completionFailure);\n expect(repository.completeRun).toHaveBeenCalledOnce();\n });\n});\n"],"names":["describe","expect","it","vi","api","toHaveProperty","probe","validate","fn","mockResolvedValue","executable","version","repository","create","id","name","runner","run","Service","ClaudePrintAgentService","cwd","resolves","toMatchObject","toHaveBeenCalledOnce","toHaveBeenCalledWith","not","toHaveBeenCalled","base","provider","providerSessionId","sessionHealth","resolve","acquireRun","mockResolvedValueOnce","agent","token","recordProviderProcess","completeRun","mockImplementation","request","onSpawn","pid","startedAt","sessionId","result","exitCode","service","send","mock","calls","prompt","firstRun","objectContaining","status","target","rejects","code","completionFailure","Error","mockRejectedValue","toBe"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import { PassThrough, Writable } from 'node:stream';
3
3
  import { describe, expect, it, vi } from 'vitest';
4
+ import { ClaudePrintRunner } from '../../providers/claude/durable/ClaudePrintRunner.js';
4
5
  function agent() {
5
6
  return {
6
7
  id: '11111111-1111-4111-8111-111111111111',
@@ -49,8 +50,6 @@ function fakeSpawn(events, exitCode = 0) {
49
50
  }
50
51
  describe('ClaudePrintRunner', ()=>{
51
52
  it('starts a caller-assigned session and persists provider identity before stdin', async ()=>{
52
- const api = await import('../../index.js');
53
- expect(api).toHaveProperty('ClaudePrintRunner');
54
53
  const fixture = fakeSpawn([
55
54
  {
56
55
  type: 'system',
@@ -64,8 +63,7 @@ describe('ClaudePrintRunner', ()=>{
64
63
  }
65
64
  ]);
66
65
  let persisted = false;
67
- const Runner = api.ClaudePrintRunner;
68
- const runner = new Runner({
66
+ const runner = new ClaudePrintRunner({
69
67
  spawn: fixture.spawn,
70
68
  processInspector: {
71
69
  getIdentity: ()=>({
@@ -114,7 +112,6 @@ describe('ClaudePrintRunner', ()=>{
114
112
  });
115
113
  });
116
114
  it('uses exact resume and rejects a mismatched result session', async ()=>{
117
- const api = await import('../../index.js');
118
115
  const fixture = fakeSpawn([
119
116
  {
120
117
  type: 'result',
@@ -122,8 +119,7 @@ describe('ClaudePrintRunner', ()=>{
122
119
  result: 'nope'
123
120
  }
124
121
  ]);
125
- const Runner = api.ClaudePrintRunner;
126
- const runner = new Runner({
122
+ const runner = new ClaudePrintRunner({
127
123
  spawn: fixture.spawn,
128
124
  processInspector: {
129
125
  getIdentity: ()=>({
@@ -151,10 +147,8 @@ describe('ClaudePrintRunner', ()=>{
151
147
  ]);
152
148
  });
153
149
  it('does not disclose provider stderr in a failed-run error', async ()=>{
154
- const api = await import('../../index.js');
155
150
  const fixture = fakeSpawn([], 1);
156
- const Runner = api.ClaudePrintRunner;
157
- const runner = new Runner({
151
+ const runner = new ClaudePrintRunner({
158
152
  spawn: fixture.spawn,
159
153
  processInspector: {
160
154
  getIdentity: ()=>({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/print/ClaudePrintRunner.test.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { PassThrough, Writable } from 'node:stream';\nimport { describe, expect, it, vi } from 'vitest';\nimport type { DurableAgent } from '../../index.js';\n\nfunction agent(): DurableAgent {\n return {\n id: '11111111-1111-4111-8111-111111111111', name: 'reviewer', provider: 'claude', mode: 'durable',\n cwd: '/project', providerSessionId: '22222222-2222-4222-8222-222222222222', state: 'running',\n sessionHealth: 'uninitialized', createdAt: '', updatedAt: '', lastActiveAt: null, lastResult: null,\n activeRun: null,\n };\n}\n\nfunction fakeSpawn(events: object[], exitCode = 0) {\n const calls: unknown[][] = [];\n const promptChunks: Buffer[] = [];\n const child = new EventEmitter() as any;\n child.pid = 4242;\n child.stdout = new PassThrough();\n child.stderr = new PassThrough();\n child.stdin = new Writable({\n write(chunk, _encoding, callback) { promptChunks.push(Buffer.from(chunk)); callback(); },\n final(callback) {\n for (const event of events) child.stdout.write(`${JSON.stringify(event)}\\n`);\n child.stdout.end();\n queueMicrotask(() => child.emit('close', exitCode, null));\n callback();\n },\n });\n const spawn = vi.fn((...args: unknown[]) => { calls.push(args); return child; });\n return { spawn, calls, promptChunks };\n}\n\ndescribe('ClaudePrintRunner', () => {\n it('starts a caller-assigned session and persists provider identity before stdin', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n expect(api).toHaveProperty('ClaudePrintRunner');\n const fixture = fakeSpawn([\n { type: 'system', subtype: 'init', session_id: agent().providerSessionId },\n { type: 'result', session_id: agent().providerSessionId, result: 'done' },\n ]);\n let persisted = false;\n const Runner = api.ClaudePrintRunner as new (options: unknown) => any;\n const runner = new Runner({ spawn: fixture.spawn, processInspector: {\n getIdentity: () => ({ pid: 4242, startedAt: 'provider-start' }),\n } });\n\n const result = await runner.run({\n agent: agent(), prompt: 'secret prompt', executable: 'claude-test', firstRun: true,\n onSpawn: async () => { expect(fixture.promptChunks).toHaveLength(0); persisted = true; },\n });\n\n expect(persisted).toBe(true);\n expect(fixture.calls[0]).toEqual([\n 'claude-test',\n ['-p', '--session-id', agent().providerSessionId, '--output-format', 'stream-json', '--verbose'],\n expect.objectContaining({ cwd: '/project', shell: false, stdio: ['pipe', 'pipe', 'pipe'] }),\n ]);\n expect(JSON.stringify(fixture.calls)).not.toContain('secret prompt');\n expect(Buffer.concat(fixture.promptChunks).toString()).toBe('secret prompt');\n expect(result).toEqual({ sessionId: agent().providerSessionId, result: 'done', exitCode: 0 });\n });\n\n it('uses exact resume and rejects a mismatched result session', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const fixture = fakeSpawn([{ type: 'result', session_id: 'wrong', result: 'nope' }]);\n const Runner = api.ClaudePrintRunner as new (options: unknown) => any;\n const runner = new Runner({ spawn: fixture.spawn, processInspector: {\n getIdentity: () => ({ pid: 4242, startedAt: 'provider-start' }),\n } });\n\n await expect(runner.run({\n agent: agent(), prompt: 'followup', executable: 'claude', firstRun: false, onSpawn: vi.fn(),\n })).rejects.toMatchObject({ code: 'CLAUDE_SESSION_MISMATCH' });\n expect(fixture.calls[0]![1]).toEqual([\n '-p', '--resume', agent().providerSessionId, '--output-format', 'stream-json', '--verbose',\n ]);\n });\n\n it('does not disclose provider stderr in a failed-run error', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const fixture = fakeSpawn([], 1);\n const Runner = api.ClaudePrintRunner as new (options: unknown) => any;\n const runner = new Runner({ spawn: fixture.spawn, processInspector: {\n getIdentity: () => ({ pid: 4242, startedAt: 'provider-start' }),\n } });\n fixture.spawn.mockImplementationOnce((...args: unknown[]) => {\n const child = (fakeSpawn([], 1).spawn as any)(...args);\n child.stdin = new Writable({\n final(callback) {\n child.stderr.write('secret prompt echoed by provider');\n child.stderr.end();\n queueMicrotask(() => child.emit('close', 1, null));\n callback();\n },\n });\n return child;\n });\n\n await expect(runner.run({\n agent: agent(), prompt: 'secret prompt', firstRun: true, onSpawn: vi.fn(),\n })).rejects.not.toThrow(/secret prompt/);\n });\n});\n"],"names":["EventEmitter","PassThrough","Writable","describe","expect","it","vi","agent","id","name","provider","mode","cwd","providerSessionId","state","sessionHealth","createdAt","updatedAt","lastActiveAt","lastResult","activeRun","fakeSpawn","events","exitCode","calls","promptChunks","child","pid","stdout","stderr","stdin","write","chunk","_encoding","callback","push","Buffer","from","final","event","JSON","stringify","end","queueMicrotask","emit","spawn","fn","args","api","toHaveProperty","fixture","type","subtype","session_id","result","persisted","Runner","ClaudePrintRunner","runner","processInspector","getIdentity","startedAt","run","prompt","executable","firstRun","onSpawn","toHaveLength","toBe","toEqual","objectContaining","shell","stdio","not","toContain","concat","toString","sessionId","rejects","toMatchObject","code","mockImplementationOnce","toThrow"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/__tests__/print/ClaudePrintRunner.test.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { PassThrough, Writable } from 'node:stream';\nimport { describe, expect, it, vi } from 'vitest';\nimport type { DurableAgent } from '../../index.js';\nimport { ClaudePrintRunner } from '../../providers/claude/durable/ClaudePrintRunner.js';\n\nfunction agent(): DurableAgent {\n return {\n id: '11111111-1111-4111-8111-111111111111', name: 'reviewer', provider: 'claude', mode: 'durable',\n cwd: '/project', providerSessionId: '22222222-2222-4222-8222-222222222222', state: 'running',\n sessionHealth: 'uninitialized', createdAt: '', updatedAt: '', lastActiveAt: null, lastResult: null,\n activeRun: null,\n };\n}\n\nfunction fakeSpawn(events: object[], exitCode = 0) {\n const calls: unknown[][] = [];\n const promptChunks: Buffer[] = [];\n const child = new EventEmitter() as any;\n child.pid = 4242;\n child.stdout = new PassThrough();\n child.stderr = new PassThrough();\n child.stdin = new Writable({\n write(chunk, _encoding, callback) { promptChunks.push(Buffer.from(chunk)); callback(); },\n final(callback) {\n for (const event of events) child.stdout.write(`${JSON.stringify(event)}\\n`);\n child.stdout.end();\n queueMicrotask(() => child.emit('close', exitCode, null));\n callback();\n },\n });\n const spawn = vi.fn((...args: unknown[]) => { calls.push(args); return child; });\n return { spawn, calls, promptChunks };\n}\n\ndescribe('ClaudePrintRunner', () => {\n it('starts a caller-assigned session and persists provider identity before stdin', async () => {\n const fixture = fakeSpawn([\n { type: 'system', subtype: 'init', session_id: agent().providerSessionId },\n { type: 'result', session_id: agent().providerSessionId, result: 'done' },\n ]);\n let persisted = false;\n const runner = new ClaudePrintRunner({ spawn: fixture.spawn, processInspector: {\n getIdentity: () => ({ pid: 4242, startedAt: 'provider-start' }),\n } });\n\n const result = await runner.run({\n agent: agent(), prompt: 'secret prompt', executable: 'claude-test', firstRun: true,\n onSpawn: async () => { expect(fixture.promptChunks).toHaveLength(0); persisted = true; },\n });\n\n expect(persisted).toBe(true);\n expect(fixture.calls[0]).toEqual([\n 'claude-test',\n ['-p', '--session-id', agent().providerSessionId, '--output-format', 'stream-json', '--verbose'],\n expect.objectContaining({ cwd: '/project', shell: false, stdio: ['pipe', 'pipe', 'pipe'] }),\n ]);\n expect(JSON.stringify(fixture.calls)).not.toContain('secret prompt');\n expect(Buffer.concat(fixture.promptChunks).toString()).toBe('secret prompt');\n expect(result).toEqual({ sessionId: agent().providerSessionId, result: 'done', exitCode: 0 });\n });\n\n it('uses exact resume and rejects a mismatched result session', async () => {\n const fixture = fakeSpawn([{ type: 'result', session_id: 'wrong', result: 'nope' }]);\n const runner = new ClaudePrintRunner({ spawn: fixture.spawn, processInspector: {\n getIdentity: () => ({ pid: 4242, startedAt: 'provider-start' }),\n } });\n\n await expect(runner.run({\n agent: agent(), prompt: 'followup', executable: 'claude', firstRun: false, onSpawn: vi.fn(),\n })).rejects.toMatchObject({ code: 'CLAUDE_SESSION_MISMATCH' });\n expect(fixture.calls[0]![1]).toEqual([\n '-p', '--resume', agent().providerSessionId, '--output-format', 'stream-json', '--verbose',\n ]);\n });\n\n it('does not disclose provider stderr in a failed-run error', async () => {\n const fixture = fakeSpawn([], 1);\n const runner = new ClaudePrintRunner({ spawn: fixture.spawn, processInspector: {\n getIdentity: () => ({ pid: 4242, startedAt: 'provider-start' }),\n } });\n fixture.spawn.mockImplementationOnce((...args: unknown[]) => {\n const child = (fakeSpawn([], 1).spawn as any)(...args);\n child.stdin = new Writable({\n final(callback) {\n child.stderr.write('secret prompt echoed by provider');\n child.stderr.end();\n queueMicrotask(() => child.emit('close', 1, null));\n callback();\n },\n });\n return child;\n });\n\n await expect(runner.run({\n agent: agent(), prompt: 'secret prompt', firstRun: true, onSpawn: vi.fn(),\n })).rejects.not.toThrow(/secret prompt/);\n });\n});\n"],"names":["EventEmitter","PassThrough","Writable","describe","expect","it","vi","ClaudePrintRunner","agent","id","name","provider","mode","cwd","providerSessionId","state","sessionHealth","createdAt","updatedAt","lastActiveAt","lastResult","activeRun","fakeSpawn","events","exitCode","calls","promptChunks","child","pid","stdout","stderr","stdin","write","chunk","_encoding","callback","push","Buffer","from","final","event","JSON","stringify","end","queueMicrotask","emit","spawn","fn","args","fixture","type","subtype","session_id","result","persisted","runner","processInspector","getIdentity","startedAt","run","prompt","executable","firstRun","onSpawn","toHaveLength","toBe","toEqual","objectContaining","shell","stdio","not","toContain","concat","toString","sessionId","rejects","toMatchObject","code","mockImplementationOnce","toThrow"],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { AgentStatus } from '../../../adapters/AgentAdapter.js';
3
+ import { ClaudeAgentMapper } from '../../../providers/claude/ClaudeAgentMapper.js';
4
+ function makeProcess(overrides = {}) {
5
+ return {
6
+ pid: 123,
7
+ command: 'claude',
8
+ cwd: '/repo/my-app',
9
+ tty: 'ttys001',
10
+ ...overrides
11
+ };
12
+ }
13
+ function makeSession(overrides = {}) {
14
+ return {
15
+ sessionId: 'session-1',
16
+ projectPath: '/repo/my-app',
17
+ sessionStart: new Date('2026-08-23T10:00:00.000Z'),
18
+ lastActive: new Date('2026-08-23T10:05:00.000Z'),
19
+ lastEntryType: 'assistant',
20
+ isInterrupted: false,
21
+ lastUserMessage: 'Review this change',
22
+ ...overrides
23
+ };
24
+ }
25
+ function makeSessionFile(overrides = {}) {
26
+ return {
27
+ sessionId: 'session-1',
28
+ filePath: '/home/.claude/projects/-repo-my-app/session-1.jsonl',
29
+ projectDir: '/home/.claude/projects/-repo-my-app',
30
+ birthtimeMs: new Date('2026-08-23T10:00:00.000Z').getTime(),
31
+ resolvedCwd: '/repo/my-app',
32
+ ...overrides
33
+ };
34
+ }
35
+ describe('ClaudeAgentMapper', ()=>{
36
+ it('prefers live PID-file status and waiting reason over JSONL-derived status', ()=>{
37
+ const mapper = new ClaudeAgentMapper();
38
+ const agent = mapper.mapSessionToAgent({
39
+ session: makeSession({
40
+ lastEntryType: 'assistant'
41
+ }),
42
+ processInfo: makeProcess(),
43
+ sessionFile: makeSessionFile(),
44
+ liveInfo: {
45
+ pidStatus: AgentStatus.WAITING,
46
+ waitingFor: 'approve Read'
47
+ }
48
+ });
49
+ expect(agent).toMatchObject({
50
+ name: 'my-app-123',
51
+ type: 'claude',
52
+ status: AgentStatus.WAITING,
53
+ summary: 'Review this change — waiting for approve Read',
54
+ pid: 123,
55
+ projectPath: '/repo/my-app',
56
+ sessionId: 'session-1',
57
+ sessionFilePath: '/home/.claude/projects/-repo-my-app/session-1.jsonl'
58
+ });
59
+ });
60
+ it('maps unmatched processes to the existing process-only fallback shape', ()=>{
61
+ const mapper = new ClaudeAgentMapper();
62
+ const agent = mapper.mapProcessOnlyAgent(makeProcess({
63
+ pid: 456,
64
+ cwd: '/repo/tooling'
65
+ }));
66
+ expect(agent).toMatchObject({
67
+ name: 'tooling-456',
68
+ type: 'claude',
69
+ status: AgentStatus.IDLE,
70
+ summary: 'Unknown',
71
+ pid: 456,
72
+ projectPath: '/repo/tooling',
73
+ sessionId: 'pid-456'
74
+ });
75
+ expect(agent.lastActive).toBeInstanceOf(Date);
76
+ });
77
+ });
78
+
79
+ //# sourceMappingURL=ClaudeAgentMapper.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/__tests__/providers/claude/ClaudeAgentMapper.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\n\nimport { AgentStatus, type ProcessInfo } from '../../../adapters/AgentAdapter.js';\nimport { ClaudeAgentMapper } from '../../../providers/claude/ClaudeAgentMapper.js';\nimport type { ClaudeSession } from '../../../providers/claude/ClaudeSessionParser.js';\nimport type { SessionFile } from '../../../utils/session.js';\n\nfunction makeProcess(overrides: Partial<ProcessInfo> = {}): ProcessInfo {\n return {\n pid: 123,\n command: 'claude',\n cwd: '/repo/my-app',\n tty: 'ttys001',\n ...overrides,\n };\n}\n\nfunction makeSession(overrides: Partial<ClaudeSession> = {}): ClaudeSession {\n return {\n sessionId: 'session-1',\n projectPath: '/repo/my-app',\n sessionStart: new Date('2026-08-23T10:00:00.000Z'),\n lastActive: new Date('2026-08-23T10:05:00.000Z'),\n lastEntryType: 'assistant',\n isInterrupted: false,\n lastUserMessage: 'Review this change',\n ...overrides,\n };\n}\n\nfunction makeSessionFile(overrides: Partial<SessionFile> = {}): SessionFile {\n return {\n sessionId: 'session-1',\n filePath: '/home/.claude/projects/-repo-my-app/session-1.jsonl',\n projectDir: '/home/.claude/projects/-repo-my-app',\n birthtimeMs: new Date('2026-08-23T10:00:00.000Z').getTime(),\n resolvedCwd: '/repo/my-app',\n ...overrides,\n };\n}\n\ndescribe('ClaudeAgentMapper', () => {\n it('prefers live PID-file status and waiting reason over JSONL-derived status', () => {\n const mapper = new ClaudeAgentMapper();\n\n const agent = mapper.mapSessionToAgent({\n session: makeSession({ lastEntryType: 'assistant' }),\n processInfo: makeProcess(),\n sessionFile: makeSessionFile(),\n liveInfo: {\n pidStatus: AgentStatus.WAITING,\n waitingFor: 'approve Read',\n },\n });\n\n expect(agent).toMatchObject({\n name: 'my-app-123',\n type: 'claude',\n status: AgentStatus.WAITING,\n summary: 'Review this change — waiting for approve Read',\n pid: 123,\n projectPath: '/repo/my-app',\n sessionId: 'session-1',\n sessionFilePath: '/home/.claude/projects/-repo-my-app/session-1.jsonl',\n });\n });\n\n it('maps unmatched processes to the existing process-only fallback shape', () => {\n const mapper = new ClaudeAgentMapper();\n\n const agent = mapper.mapProcessOnlyAgent(makeProcess({ pid: 456, cwd: '/repo/tooling' }));\n\n expect(agent).toMatchObject({\n name: 'tooling-456',\n type: 'claude',\n status: AgentStatus.IDLE,\n summary: 'Unknown',\n pid: 456,\n projectPath: '/repo/tooling',\n sessionId: 'pid-456',\n });\n expect(agent.lastActive).toBeInstanceOf(Date);\n });\n});\n"],"names":["describe","expect","it","AgentStatus","ClaudeAgentMapper","makeProcess","overrides","pid","command","cwd","tty","makeSession","sessionId","projectPath","sessionStart","Date","lastActive","lastEntryType","isInterrupted","lastUserMessage","makeSessionFile","filePath","projectDir","birthtimeMs","getTime","resolvedCwd","mapper","agent","mapSessionToAgent","session","processInfo","sessionFile","liveInfo","pidStatus","WAITING","waitingFor","toMatchObject","name","type","status","summary","sessionFilePath","mapProcessOnlyAgent","IDLE","toBeInstanceOf"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAS;AAE9C,SAASC,WAAW,QAA0B,oCAAoC;AAClF,SAASC,iBAAiB,QAAQ,iDAAiD;AAInF,SAASC,YAAYC,YAAkC,CAAC,CAAC;IACrD,OAAO;QACHC,KAAK;QACLC,SAAS;QACTC,KAAK;QACLC,KAAK;QACL,GAAGJ,SAAS;IAChB;AACJ;AAEA,SAASK,YAAYL,YAAoC,CAAC,CAAC;IACvD,OAAO;QACHM,WAAW;QACXC,aAAa;QACbC,cAAc,IAAIC,KAAK;QACvBC,YAAY,IAAID,KAAK;QACrBE,eAAe;QACfC,eAAe;QACfC,iBAAiB;QACjB,GAAGb,SAAS;IAChB;AACJ;AAEA,SAASc,gBAAgBd,YAAkC,CAAC,CAAC;IACzD,OAAO;QACHM,WAAW;QACXS,UAAU;QACVC,YAAY;QACZC,aAAa,IAAIR,KAAK,4BAA4BS,OAAO;QACzDC,aAAa;QACb,GAAGnB,SAAS;IAChB;AACJ;AAEAN,SAAS,qBAAqB;IAC1BE,GAAG,6EAA6E;QAC5E,MAAMwB,SAAS,IAAItB;QAEnB,MAAMuB,QAAQD,OAAOE,iBAAiB,CAAC;YACnCC,SAASlB,YAAY;gBAAEM,eAAe;YAAY;YAClDa,aAAazB;YACb0B,aAAaX;YACbY,UAAU;gBACNC,WAAW9B,YAAY+B,OAAO;gBAC9BC,YAAY;YAChB;QACJ;QAEAlC,OAAO0B,OAAOS,aAAa,CAAC;YACxBC,MAAM;YACNC,MAAM;YACNC,QAAQpC,YAAY+B,OAAO;YAC3BM,SAAS;YACTjC,KAAK;YACLM,aAAa;YACbD,WAAW;YACX6B,iBAAiB;QACrB;IACJ;IAEAvC,GAAG,wEAAwE;QACvE,MAAMwB,SAAS,IAAItB;QAEnB,MAAMuB,QAAQD,OAAOgB,mBAAmB,CAACrC,YAAY;YAAEE,KAAK;YAAKE,KAAK;QAAgB;QAEtFR,OAAO0B,OAAOS,aAAa,CAAC;YACxBC,MAAM;YACNC,MAAM;YACNC,QAAQpC,YAAYwC,IAAI;YACxBH,SAAS;YACTjC,KAAK;YACLM,aAAa;YACbD,WAAW;QACf;QACAX,OAAO0B,MAAMX,UAAU,EAAE4B,cAAc,CAAC7B;IAC5C;AACJ"}