@ai-devkit/agent-manager 0.29.0 → 0.30.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 (198) 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/migrations/004_codex_durable_sessions.sql +1 -0
  46. package/dist/durable/DurableAgent.d.ts +23 -3
  47. package/dist/durable/DurableAgent.d.ts.map +1 -1
  48. package/dist/durable/DurableAgent.js +12 -0
  49. package/dist/durable/DurableAgent.js.map +1 -1
  50. package/dist/durable/DurableAgentRepository.d.ts +6 -7
  51. package/dist/durable/DurableAgentRepository.d.ts.map +1 -1
  52. package/dist/durable/DurableAgentRepository.js +73 -42
  53. package/dist/durable/DurableAgentRepository.js.map +1 -1
  54. package/dist/durable/process.d.ts +8 -0
  55. package/dist/durable/process.d.ts.map +1 -0
  56. package/dist/durable/process.js +40 -0
  57. package/dist/durable/process.js.map +1 -0
  58. package/dist/durable/run.d.ts +32 -0
  59. package/dist/durable/run.d.ts.map +1 -0
  60. package/dist/durable/run.js +46 -0
  61. package/dist/durable/run.js.map +1 -0
  62. package/dist/durable/utils.d.ts +19 -0
  63. package/dist/durable/utils.d.ts.map +1 -0
  64. package/dist/durable/utils.js +52 -0
  65. package/dist/durable/utils.js.map +1 -0
  66. package/dist/index.d.ts +17 -13
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +9 -8
  69. package/dist/index.js.map +1 -1
  70. package/dist/providers/claude/ClaudeAgentMapper.d.ts +21 -0
  71. package/dist/providers/claude/ClaudeAgentMapper.d.ts.map +1 -0
  72. package/dist/providers/claude/ClaudeAgentMapper.js +42 -0
  73. package/dist/providers/claude/ClaudeAgentMapper.js.map +1 -0
  74. package/dist/providers/claude/ClaudeCodeAdapter.d.ts +29 -0
  75. package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -0
  76. package/dist/providers/claude/ClaudeCodeAdapter.js +129 -0
  77. package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -0
  78. package/dist/{adapters/ClaudeCodeAdapter.d.ts → providers/claude/ClaudeSessionLocator.d.ts} +68 -52
  79. package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -0
  80. package/dist/{adapters/ClaudeCodeAdapter.js → providers/claude/ClaudeSessionLocator.js} +71 -189
  81. package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -0
  82. package/dist/{utils → providers/claude}/ClaudeSessionParser.d.ts +2 -2
  83. package/dist/providers/claude/ClaudeSessionParser.d.ts.map +1 -0
  84. package/dist/{utils → providers/claude}/ClaudeSessionParser.js +1 -1
  85. package/dist/providers/claude/ClaudeSessionParser.js.map +1 -0
  86. package/dist/providers/claude/durable/ClaudeCliProbe.d.ts.map +1 -0
  87. package/dist/{durable → providers/claude/durable}/ClaudeCliProbe.js +4 -9
  88. package/dist/providers/claude/durable/ClaudeCliProbe.js.map +1 -0
  89. package/dist/{durable → providers/claude/durable}/ClaudePrintAgentService.d.ts +4 -9
  90. package/dist/providers/claude/durable/ClaudePrintAgentService.d.ts.map +1 -0
  91. package/dist/providers/claude/durable/ClaudePrintAgentService.js +45 -0
  92. package/dist/providers/claude/durable/ClaudePrintAgentService.js.map +1 -0
  93. package/dist/{durable → providers/claude/durable}/ClaudePrintRunner.d.ts +3 -3
  94. package/dist/providers/claude/durable/ClaudePrintRunner.d.ts.map +1 -0
  95. package/dist/providers/claude/durable/ClaudePrintRunner.js +75 -0
  96. package/dist/providers/claude/durable/ClaudePrintRunner.js.map +1 -0
  97. package/dist/providers/claude/durable/ClaudeStreamParser.d.ts +17 -0
  98. package/dist/providers/claude/durable/ClaudeStreamParser.d.ts.map +1 -0
  99. package/dist/providers/claude/durable/ClaudeStreamParser.js +69 -0
  100. package/dist/providers/claude/durable/ClaudeStreamParser.js.map +1 -0
  101. package/dist/providers/codex/durable/CodexCliProbe.d.ts +20 -0
  102. package/dist/providers/codex/durable/CodexCliProbe.d.ts.map +1 -0
  103. package/dist/providers/codex/durable/CodexCliProbe.js +61 -0
  104. package/dist/providers/codex/durable/CodexCliProbe.js.map +1 -0
  105. package/dist/providers/codex/durable/CodexPrintAgentService.d.ts +40 -0
  106. package/dist/providers/codex/durable/CodexPrintAgentService.d.ts.map +1 -0
  107. package/dist/providers/codex/durable/CodexPrintAgentService.js +50 -0
  108. package/dist/providers/codex/durable/CodexPrintAgentService.js.map +1 -0
  109. package/dist/providers/codex/durable/CodexPrintRunner.d.ts +34 -0
  110. package/dist/providers/codex/durable/CodexPrintRunner.d.ts.map +1 -0
  111. package/dist/providers/codex/durable/CodexPrintRunner.js +78 -0
  112. package/dist/providers/codex/durable/CodexPrintRunner.js.map +1 -0
  113. package/dist/providers/codex/durable/CodexStreamParser.d.ts +21 -0
  114. package/dist/providers/codex/durable/CodexStreamParser.d.ts.map +1 -0
  115. package/dist/providers/codex/durable/CodexStreamParser.js +86 -0
  116. package/dist/providers/codex/durable/CodexStreamParser.js.map +1 -0
  117. package/dist/providers/pi/durable/PiCliProbe.d.ts +20 -0
  118. package/dist/providers/pi/durable/PiCliProbe.d.ts.map +1 -0
  119. package/dist/providers/pi/durable/PiCliProbe.js +51 -0
  120. package/dist/providers/pi/durable/PiCliProbe.js.map +1 -0
  121. package/dist/providers/pi/durable/PiPrintAgentService.d.ts +39 -0
  122. package/dist/providers/pi/durable/PiPrintAgentService.d.ts.map +1 -0
  123. package/dist/providers/pi/durable/PiPrintAgentService.js +47 -0
  124. package/dist/providers/pi/durable/PiPrintAgentService.js.map +1 -0
  125. package/dist/providers/pi/durable/PiPrintProtocol.d.ts +4 -0
  126. package/dist/providers/pi/durable/PiPrintProtocol.d.ts.map +1 -0
  127. package/dist/providers/pi/durable/PiPrintProtocol.js +44 -0
  128. package/dist/providers/pi/durable/PiPrintProtocol.js.map +1 -0
  129. package/dist/providers/pi/durable/PiPrintRunner.d.ts +32 -0
  130. package/dist/providers/pi/durable/PiPrintRunner.d.ts.map +1 -0
  131. package/dist/providers/pi/durable/PiPrintRunner.js +61 -0
  132. package/dist/providers/pi/durable/PiPrintRunner.js.map +1 -0
  133. package/dist/providers/pi/durable/PiStreamParser.d.ts +21 -0
  134. package/dist/providers/pi/durable/PiStreamParser.d.ts.map +1 -0
  135. package/dist/providers/pi/durable/PiStreamParser.js +71 -0
  136. package/dist/providers/pi/durable/PiStreamParser.js.map +1 -0
  137. package/package.json +1 -1
  138. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +1 -307
  139. package/src/__tests__/database/DurableAgentsDatabase.test.ts +28 -3
  140. package/src/__tests__/durable/CodexCliProbe.test.ts +63 -0
  141. package/src/__tests__/durable/CodexDurableAgentRepository.test.ts +54 -0
  142. package/src/__tests__/durable/CodexPrintAgent.integration.test.ts +79 -0
  143. package/src/__tests__/durable/CodexPrintAgentService.test.ts +139 -0
  144. package/src/__tests__/durable/CodexPrintRunner.test.ts +189 -0
  145. package/src/__tests__/fixtures/fake-codex.cjs +45 -0
  146. package/src/__tests__/print/ClaudeCliProbe.test.ts +3 -7
  147. package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +11 -21
  148. package/src/__tests__/print/ClaudePrintAgentService.test.ts +33 -1
  149. package/src/__tests__/print/ClaudePrintRunner.test.ts +4 -10
  150. package/src/__tests__/providers/claude/ClaudeAgentMapper.test.ts +84 -0
  151. package/src/__tests__/providers/claude/ClaudeSessionLocator.test.ts +411 -0
  152. package/src/__tests__/providers/pi/durable/PiCliProbe.test.ts +38 -0
  153. package/src/__tests__/providers/pi/durable/PiDurableAgentRepository.test.ts +32 -0
  154. package/src/__tests__/providers/pi/durable/PiPrintAgentService.test.ts +69 -0
  155. package/src/__tests__/providers/pi/durable/PiPrintProtocol.test.ts +33 -0
  156. package/src/__tests__/providers/pi/durable/PiPrintRunner.test.ts +107 -0
  157. package/src/__tests__/providers/pi/durable/PiStreamParser.test.ts +24 -0
  158. package/src/__tests__/utils/ClaudeSessionParser.test.ts +2 -2
  159. package/src/adapters/index.ts +1 -1
  160. package/src/database/migrations/004_codex_durable_sessions.sql +1 -0
  161. package/src/durable/DurableAgent.ts +51 -3
  162. package/src/durable/DurableAgentRepository.ts +60 -31
  163. package/src/durable/process.ts +28 -0
  164. package/src/durable/run.ts +74 -0
  165. package/src/durable/utils.ts +61 -0
  166. package/src/index.ts +31 -15
  167. package/src/providers/claude/ClaudeAgentMapper.ts +62 -0
  168. package/src/providers/claude/ClaudeCodeAdapter.ts +160 -0
  169. package/src/{adapters/ClaudeCodeAdapter.ts → providers/claude/ClaudeSessionLocator.ts} +97 -239
  170. package/src/{utils → providers/claude}/ClaudeSessionParser.ts +2 -2
  171. package/src/{durable → providers/claude/durable}/ClaudeCliProbe.ts +4 -10
  172. package/src/providers/claude/durable/ClaudePrintAgentService.ts +66 -0
  173. package/src/providers/claude/durable/ClaudePrintRunner.ts +92 -0
  174. package/src/providers/claude/durable/ClaudeStreamParser.ts +79 -0
  175. package/src/providers/codex/durable/CodexCliProbe.ts +60 -0
  176. package/src/providers/codex/durable/CodexPrintAgentService.ts +65 -0
  177. package/src/providers/codex/durable/CodexPrintRunner.ts +94 -0
  178. package/src/providers/codex/durable/CodexStreamParser.ts +104 -0
  179. package/src/providers/pi/durable/PiCliProbe.ts +48 -0
  180. package/src/providers/pi/durable/PiPrintAgentService.ts +69 -0
  181. package/src/providers/pi/durable/PiPrintProtocol.ts +39 -0
  182. package/src/providers/pi/durable/PiPrintRunner.ts +88 -0
  183. package/src/providers/pi/durable/PiStreamParser.ts +84 -0
  184. package/dist/adapters/ClaudeCodeAdapter.d.ts.map +0 -1
  185. package/dist/adapters/ClaudeCodeAdapter.js.map +0 -1
  186. package/dist/durable/ClaudeCliProbe.d.ts.map +0 -1
  187. package/dist/durable/ClaudeCliProbe.js.map +0 -1
  188. package/dist/durable/ClaudePrintAgentService.d.ts.map +0 -1
  189. package/dist/durable/ClaudePrintAgentService.js +0 -66
  190. package/dist/durable/ClaudePrintAgentService.js.map +0 -1
  191. package/dist/durable/ClaudePrintRunner.d.ts.map +0 -1
  192. package/dist/durable/ClaudePrintRunner.js +0 -128
  193. package/dist/durable/ClaudePrintRunner.js.map +0 -1
  194. package/dist/utils/ClaudeSessionParser.d.ts.map +0 -1
  195. package/dist/utils/ClaudeSessionParser.js.map +0 -1
  196. package/src/durable/ClaudePrintAgentService.ts +0 -94
  197. package/src/durable/ClaudePrintRunner.ts +0 -139
  198. /package/dist/{durable → providers/claude/durable}/ClaudeCliProbe.d.ts +0 -0
@@ -0,0 +1,202 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ const SESSION = '22222222-2222-4222-8222-222222222222';
3
+ const base = {
4
+ id: 'id',
5
+ name: 'reviewer',
6
+ provider: 'pi',
7
+ providerSessionId: SESSION,
8
+ sessionHealth: 'uninitialized'
9
+ };
10
+ describe('PiPrintAgentService', ()=>{
11
+ it('probes before provider-aware create', async ()=>{
12
+ const api = await import('../../../../index.js');
13
+ expect(api).toHaveProperty('PiPrintAgentService');
14
+ const probe = {
15
+ validate: vi.fn()
16
+ };
17
+ const repository = {
18
+ create: vi.fn().mockResolvedValue(base),
19
+ list: vi.fn()
20
+ };
21
+ const runner = {
22
+ run: vi.fn()
23
+ };
24
+ const Service = api.PiPrintAgentService;
25
+ await new Service({
26
+ repository,
27
+ probe,
28
+ runner
29
+ }).create({
30
+ name: 'reviewer',
31
+ cwd: '/project'
32
+ });
33
+ expect(probe.validate).toHaveBeenCalledOnce();
34
+ expect(repository.create).toHaveBeenCalledWith({
35
+ name: 'reviewer',
36
+ cwd: '/project',
37
+ provider: 'pi'
38
+ });
39
+ });
40
+ it('records successful first and resumed sends', async ()=>{
41
+ const api = await import('../../../../index.js');
42
+ const repository = {
43
+ list: vi.fn(),
44
+ resolve: vi.fn().mockResolvedValue(base),
45
+ acquireRun: vi.fn().mockResolvedValueOnce({
46
+ agent: base,
47
+ token: 'one'
48
+ }).mockResolvedValueOnce({
49
+ agent: {
50
+ ...base,
51
+ sessionHealth: 'healthy'
52
+ },
53
+ token: 'two'
54
+ }),
55
+ recordProviderProcess: vi.fn(),
56
+ completeRun: vi.fn()
57
+ };
58
+ const runner = {
59
+ run: vi.fn(async (request)=>{
60
+ await request.onSpawn({
61
+ pid: 42,
62
+ startedAt: 'start'
63
+ });
64
+ return {
65
+ sessionId: SESSION,
66
+ result: 'answer',
67
+ messages: [
68
+ 'answer'
69
+ ],
70
+ exitCode: 0
71
+ };
72
+ })
73
+ };
74
+ const Service = api.PiPrintAgentService;
75
+ const service = new Service({
76
+ repository,
77
+ probe: {
78
+ validate: vi.fn()
79
+ },
80
+ runner
81
+ });
82
+ await service.send('reviewer', 'first');
83
+ await service.send('reviewer', 'later');
84
+ expect(repository.completeRun).toHaveBeenCalledWith('id', 'one', expect.objectContaining({
85
+ status: 'succeeded',
86
+ sessionHealth: 'healthy'
87
+ }));
88
+ });
89
+ it('records mismatches and rejects missing and ambiguous records', async ()=>{
90
+ const api = await import('../../../../index.js');
91
+ const ErrorType = api.PiPrintError;
92
+ const completeRun = vi.fn();
93
+ const repository = {
94
+ list: vi.fn(),
95
+ resolve: vi.fn().mockResolvedValue(base),
96
+ acquireRun: vi.fn().mockResolvedValue({
97
+ agent: base,
98
+ token: 'one'
99
+ }),
100
+ recordProviderProcess: vi.fn(),
101
+ completeRun
102
+ };
103
+ const Service = api.PiPrintAgentService;
104
+ await expect(new Service({
105
+ repository,
106
+ probe: {
107
+ validate: vi.fn()
108
+ },
109
+ runner: {
110
+ run: vi.fn().mockRejectedValue(new ErrorType('bad', 'PI_SESSION_MISMATCH'))
111
+ }
112
+ }).send('reviewer', 'x')).rejects.toMatchObject({
113
+ code: 'PI_SESSION_MISMATCH'
114
+ });
115
+ expect(completeRun).toHaveBeenCalledWith('id', 'one', expect.objectContaining({
116
+ sessionHealth: 'mismatch'
117
+ }));
118
+ const earlyRepository = {
119
+ ...repository,
120
+ resolve: vi.fn().mockResolvedValueOnce(null).mockResolvedValueOnce([
121
+ base,
122
+ base
123
+ ]),
124
+ acquireRun: vi.fn()
125
+ };
126
+ const early = new Service({
127
+ repository: earlyRepository,
128
+ probe: {
129
+ validate: vi.fn()
130
+ },
131
+ runner: {
132
+ run: vi.fn()
133
+ }
134
+ });
135
+ await expect(early.send('missing', 'x')).rejects.toMatchObject({
136
+ code: 'DURABLE_AGENT_NOT_FOUND'
137
+ });
138
+ await expect(early.send('many', 'x')).rejects.toMatchObject({
139
+ code: 'PI_UNSUPPORTED'
140
+ });
141
+ });
142
+ it('rejects a non-Pi target without acquiring or mutating it', async ()=>{
143
+ const api = await import('../../../../index.js');
144
+ const Service = api.PiPrintAgentService;
145
+ const repository = {
146
+ resolve: vi.fn().mockResolvedValue({
147
+ ...base,
148
+ provider: 'claude'
149
+ }),
150
+ acquireRun: vi.fn(),
151
+ recordProviderProcess: vi.fn(),
152
+ completeRun: vi.fn()
153
+ };
154
+ await expect(new Service({
155
+ repository,
156
+ probe: {
157
+ validate: vi.fn()
158
+ },
159
+ runner: {
160
+ run: vi.fn()
161
+ }
162
+ }).send('reviewer', 'x')).rejects.toMatchObject({
163
+ code: 'PI_UNSUPPORTED'
164
+ });
165
+ expect(repository.acquireRun).not.toHaveBeenCalled();
166
+ expect(repository.completeRun).not.toHaveBeenCalled();
167
+ });
168
+ it('does not turn a successful completion write failure into a second completion', async ()=>{
169
+ const api = await import('../../../../index.js');
170
+ const Service = api.PiPrintAgentService;
171
+ const completionFailure = new Error('completion failed');
172
+ const repository = {
173
+ resolve: vi.fn().mockResolvedValue(base),
174
+ acquireRun: vi.fn().mockResolvedValue({
175
+ agent: base,
176
+ token: 'one'
177
+ }),
178
+ recordProviderProcess: vi.fn(),
179
+ completeRun: vi.fn().mockRejectedValue(completionFailure)
180
+ };
181
+ const runner = {
182
+ run: vi.fn().mockResolvedValue({
183
+ sessionId: SESSION,
184
+ result: 'answer',
185
+ messages: [
186
+ 'answer'
187
+ ],
188
+ exitCode: 0
189
+ })
190
+ };
191
+ await expect(new Service({
192
+ repository,
193
+ probe: {
194
+ validate: vi.fn()
195
+ },
196
+ runner
197
+ }).send('reviewer', 'x')).rejects.toBe(completionFailure);
198
+ expect(repository.completeRun).toHaveBeenCalledOnce();
199
+ });
200
+ });
201
+
202
+ //# sourceMappingURL=PiPrintAgentService.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/__tests__/providers/pi/durable/PiPrintAgentService.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\nconst SESSION = '22222222-2222-4222-8222-222222222222';\nconst base = { id: 'id', name: 'reviewer', provider: 'pi', providerSessionId: SESSION, sessionHealth: 'uninitialized' };\n\ndescribe('PiPrintAgentService', () => {\n it('probes before provider-aware create', async () => {\n const api = await import('../../../../index.js') as Record<string, unknown>; expect(api).toHaveProperty('PiPrintAgentService');\n const probe = { validate: vi.fn() }; const repository = { create: vi.fn().mockResolvedValue(base), list: vi.fn() }; const runner = { run: vi.fn() };\n const Service = api.PiPrintAgentService as new (options: unknown) => any;\n await new Service({ repository, probe, runner }).create({ name: 'reviewer', cwd: '/project' });\n expect(probe.validate).toHaveBeenCalledOnce(); expect(repository.create).toHaveBeenCalledWith({ name: 'reviewer', cwd: '/project', provider: 'pi' });\n });\n\n it('records successful first and resumed sends', async () => {\n const api = await import('../../../../index.js') as Record<string, unknown>;\n const repository = { list: vi.fn(), resolve: vi.fn().mockResolvedValue(base), acquireRun: vi.fn()\n .mockResolvedValueOnce({ agent: base, token: 'one' }).mockResolvedValueOnce({ agent: { ...base, sessionHealth: 'healthy' }, token: 'two' }),\n recordProviderProcess: vi.fn(), completeRun: vi.fn() };\n const runner = { run: vi.fn(async (request) => { await request.onSpawn({ pid: 42, startedAt: 'start' }); return { sessionId: SESSION, result: 'answer', messages: ['answer'], exitCode: 0 }; }) };\n const Service = api.PiPrintAgentService as new (options: unknown) => any; const service = new Service({ repository, probe: { validate: vi.fn() }, runner });\n await service.send('reviewer', 'first'); await service.send('reviewer', 'later');\n expect(repository.completeRun).toHaveBeenCalledWith('id', 'one', expect.objectContaining({ status: 'succeeded', sessionHealth: 'healthy' }));\n });\n\n it('records mismatches and rejects missing and ambiguous records', async () => {\n const api = await import('../../../../index.js') as Record<string, unknown>; const ErrorType = api.PiPrintError as new (message: string, code: string) => Error;\n const completeRun = vi.fn(); const repository = { list: vi.fn(), resolve: vi.fn().mockResolvedValue(base), acquireRun: vi.fn().mockResolvedValue({ agent: base, token: 'one' }), recordProviderProcess: vi.fn(), completeRun };\n const Service = api.PiPrintAgentService as new (options: unknown) => any;\n await expect(new Service({ repository, probe: { validate: vi.fn() }, runner: { run: vi.fn().mockRejectedValue(new ErrorType('bad', 'PI_SESSION_MISMATCH')) } }).send('reviewer', 'x'))\n .rejects.toMatchObject({ code: 'PI_SESSION_MISMATCH' });\n expect(completeRun).toHaveBeenCalledWith('id', 'one', expect.objectContaining({ sessionHealth: 'mismatch' }));\n const earlyRepository = { ...repository, resolve: vi.fn().mockResolvedValueOnce(null).mockResolvedValueOnce([base, base]), acquireRun: vi.fn() };\n const early = new Service({ repository: earlyRepository, probe: { validate: vi.fn() }, runner: { run: vi.fn() } });\n await expect(early.send('missing', 'x')).rejects.toMatchObject({ code: 'DURABLE_AGENT_NOT_FOUND' });\n await expect(early.send('many', 'x')).rejects.toMatchObject({ code: 'PI_UNSUPPORTED' });\n });\n\n it('rejects a non-Pi target without acquiring or mutating it', async () => {\n const api = await import('../../../../index.js') as Record<string, unknown>;\n const Service = api.PiPrintAgentService as new (options: unknown) => any;\n const repository = {\n resolve: vi.fn().mockResolvedValue({ ...base, provider: 'claude' }),\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: 'PI_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.PiPrintAgentService as new (options: unknown) => any;\n const completionFailure = new Error('completion failed');\n const repository = {\n resolve: vi.fn().mockResolvedValue(base),\n acquireRun: vi.fn().mockResolvedValue({ agent: base, token: 'one' }),\n recordProviderProcess: vi.fn(), completeRun: vi.fn().mockRejectedValue(completionFailure),\n };\n const runner = { run: vi.fn().mockResolvedValue({\n sessionId: SESSION, result: 'answer', messages: ['answer'], exitCode: 0,\n }) };\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","SESSION","base","id","name","provider","providerSessionId","sessionHealth","api","toHaveProperty","probe","validate","fn","repository","create","mockResolvedValue","list","runner","run","Service","PiPrintAgentService","cwd","toHaveBeenCalledOnce","toHaveBeenCalledWith","resolve","acquireRun","mockResolvedValueOnce","agent","token","recordProviderProcess","completeRun","request","onSpawn","pid","startedAt","sessionId","result","messages","exitCode","service","send","objectContaining","status","ErrorType","PiPrintError","mockRejectedValue","rejects","toMatchObject","code","earlyRepository","early","not","toHaveBeenCalled","completionFailure","Error","toBe"],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { buildPiPrintArgs, readPiAssistantText, readPiSessionId } from '../../../../providers/pi/durable/PiPrintProtocol.js';
3
+ const SESSION = '22222222-2222-4222-8222-222222222222';
4
+ describe('Pi print pure protocol mapping', ()=>{
5
+ it('maps first and resumed runs to exact documented arguments', ()=>{
6
+ expect(buildPiPrintArgs(SESSION, true)).toEqual([
7
+ '--mode',
8
+ 'json',
9
+ '--session-id',
10
+ SESSION
11
+ ]);
12
+ expect(buildPiPrintArgs(SESSION, false)).toEqual([
13
+ '--mode',
14
+ 'json',
15
+ '--session',
16
+ SESSION
17
+ ]);
18
+ });
19
+ it('accepts one expected UUID and rejects invalid, duplicate, and mismatched identities', ()=>{
20
+ expect(readPiSessionId({
21
+ id: SESSION
22
+ }, null, SESSION)).toBe(SESSION);
23
+ expect(()=>readPiSessionId({}, null, SESSION)).toThrowError(expect.objectContaining({
24
+ code: 'PI_PROTOCOL'
25
+ }));
26
+ expect(()=>readPiSessionId({
27
+ id: [
28
+ SESSION
29
+ ]
30
+ }, null, SESSION)).toThrowError(expect.objectContaining({
31
+ code: 'PI_PROTOCOL'
32
+ }));
33
+ expect(()=>readPiSessionId({
34
+ id: SESSION
35
+ }, SESSION, SESSION)).toThrowError(expect.objectContaining({
36
+ code: 'PI_PROTOCOL'
37
+ }));
38
+ expect(()=>readPiSessionId({
39
+ id: '33333333-3333-4333-8333-333333333333'
40
+ }, null, SESSION)).toThrowError(expect.objectContaining({
41
+ code: 'PI_SESSION_MISMATCH'
42
+ }));
43
+ });
44
+ it('extracts only non-empty completed assistant text', ()=>{
45
+ expect(readPiAssistantText({
46
+ type: 'future'
47
+ })).toBeNull();
48
+ expect(readPiAssistantText({
49
+ type: 'message_end'
50
+ })).toBeNull();
51
+ expect(readPiAssistantText({
52
+ type: 'message_end',
53
+ message: []
54
+ })).toBeNull();
55
+ expect(readPiAssistantText({
56
+ type: 'message_end',
57
+ message: {
58
+ role: 'user',
59
+ content: 'no'
60
+ }
61
+ })).toBeNull();
62
+ expect(readPiAssistantText({
63
+ type: 'message_end',
64
+ message: {
65
+ role: 'assistant',
66
+ content: ' '
67
+ }
68
+ })).toBeNull();
69
+ expect(readPiAssistantText({
70
+ type: 'message_end',
71
+ message: {
72
+ role: 'assistant',
73
+ content: 'answer'
74
+ }
75
+ })).toBe('answer');
76
+ expect(readPiAssistantText({
77
+ type: 'message_end',
78
+ message: {
79
+ role: 'assistant',
80
+ content: null
81
+ }
82
+ })).toBeNull();
83
+ expect(readPiAssistantText({
84
+ type: 'message_end',
85
+ message: {
86
+ role: 'assistant',
87
+ content: [
88
+ null,
89
+ [],
90
+ {
91
+ type: 'thinking'
92
+ }
93
+ ]
94
+ }
95
+ })).toBeNull();
96
+ expect(readPiAssistantText({
97
+ type: 'message_end',
98
+ message: {
99
+ role: 'assistant',
100
+ content: [
101
+ {
102
+ type: 'text',
103
+ text: 'a'
104
+ },
105
+ {
106
+ type: 'text',
107
+ text: 1
108
+ },
109
+ {
110
+ type: 'text',
111
+ text: 'b'
112
+ }
113
+ ]
114
+ }
115
+ })).toBe('ab');
116
+ });
117
+ });
118
+
119
+ //# sourceMappingURL=PiPrintProtocol.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/__tests__/providers/pi/durable/PiPrintProtocol.test.ts"],"sourcesContent":["import { describe, expect, it } from 'vitest';\nimport { buildPiPrintArgs, readPiAssistantText, readPiSessionId } from '../../../../providers/pi/durable/PiPrintProtocol.js';\n\nconst SESSION = '22222222-2222-4222-8222-222222222222';\n\ndescribe('Pi print pure protocol mapping', () => {\n it('maps first and resumed runs to exact documented arguments', () => {\n expect(buildPiPrintArgs(SESSION, true)).toEqual(['--mode', 'json', '--session-id', SESSION]);\n expect(buildPiPrintArgs(SESSION, false)).toEqual(['--mode', 'json', '--session', SESSION]);\n });\n\n it('accepts one expected UUID and rejects invalid, duplicate, and mismatched identities', () => {\n expect(readPiSessionId({ id: SESSION }, null, SESSION)).toBe(SESSION);\n expect(() => readPiSessionId({}, null, SESSION)).toThrowError(expect.objectContaining({ code: 'PI_PROTOCOL' }));\n expect(() => readPiSessionId({ id: [SESSION] }, null, SESSION))\n .toThrowError(expect.objectContaining({ code: 'PI_PROTOCOL' }));\n expect(() => readPiSessionId({ id: SESSION }, SESSION, SESSION)).toThrowError(expect.objectContaining({ code: 'PI_PROTOCOL' }));\n expect(() => readPiSessionId({ id: '33333333-3333-4333-8333-333333333333' }, null, SESSION))\n .toThrowError(expect.objectContaining({ code: 'PI_SESSION_MISMATCH' }));\n });\n\n it('extracts only non-empty completed assistant text', () => {\n expect(readPiAssistantText({ type: 'future' })).toBeNull();\n expect(readPiAssistantText({ type: 'message_end' })).toBeNull();\n expect(readPiAssistantText({ type: 'message_end', message: [] })).toBeNull();\n expect(readPiAssistantText({ type: 'message_end', message: { role: 'user', content: 'no' } })).toBeNull();\n expect(readPiAssistantText({ type: 'message_end', message: { role: 'assistant', content: ' ' } })).toBeNull();\n expect(readPiAssistantText({ type: 'message_end', message: { role: 'assistant', content: 'answer' } })).toBe('answer');\n expect(readPiAssistantText({ type: 'message_end', message: { role: 'assistant', content: null } })).toBeNull();\n expect(readPiAssistantText({ type: 'message_end', message: { role: 'assistant', content: [null, [], { type: 'thinking' }] } })).toBeNull();\n expect(readPiAssistantText({ type: 'message_end', message: { role: 'assistant', content: [{ type: 'text', text: 'a' }, { type: 'text', text: 1 }, { type: 'text', text: 'b' }] } })).toBe('ab');\n });\n});\n"],"names":["describe","expect","it","buildPiPrintArgs","readPiAssistantText","readPiSessionId","SESSION","toEqual","id","toBe","toThrowError","objectContaining","code","type","toBeNull","message","role","content","text"],"mappings":"AAAA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,SAAS;AAC9C,SAASC,gBAAgB,EAAEC,mBAAmB,EAAEC,eAAe,QAAQ,sDAAsD;AAE7H,MAAMC,UAAU;AAEhBN,SAAS,kCAAkC;IACvCE,GAAG,6DAA6D;QAC5DD,OAAOE,iBAAiBG,SAAS,OAAOC,OAAO,CAAC;YAAC;YAAU;YAAQ;YAAgBD;SAAQ;QAC3FL,OAAOE,iBAAiBG,SAAS,QAAQC,OAAO,CAAC;YAAC;YAAU;YAAQ;YAAaD;SAAQ;IAC7F;IAEAJ,GAAG,uFAAuF;QACtFD,OAAOI,gBAAgB;YAAEG,IAAIF;QAAQ,GAAG,MAAMA,UAAUG,IAAI,CAACH;QAC7DL,OAAO,IAAMI,gBAAgB,CAAC,GAAG,MAAMC,UAAUI,YAAY,CAACT,OAAOU,gBAAgB,CAAC;YAAEC,MAAM;QAAc;QAC5GX,OAAO,IAAMI,gBAAgB;gBAAEG,IAAI;oBAACF;iBAAQ;YAAC,GAAG,MAAMA,UACjDI,YAAY,CAACT,OAAOU,gBAAgB,CAAC;YAAEC,MAAM;QAAc;QAChEX,OAAO,IAAMI,gBAAgB;gBAAEG,IAAIF;YAAQ,GAAGA,SAASA,UAAUI,YAAY,CAACT,OAAOU,gBAAgB,CAAC;YAAEC,MAAM;QAAc;QAC5HX,OAAO,IAAMI,gBAAgB;gBAAEG,IAAI;YAAuC,GAAG,MAAMF,UAC9EI,YAAY,CAACT,OAAOU,gBAAgB,CAAC;YAAEC,MAAM;QAAsB;IAC5E;IAEAV,GAAG,oDAAoD;QACnDD,OAAOG,oBAAoB;YAAES,MAAM;QAAS,IAAIC,QAAQ;QACxDb,OAAOG,oBAAoB;YAAES,MAAM;QAAc,IAAIC,QAAQ;QAC7Db,OAAOG,oBAAoB;YAAES,MAAM;YAAeE,SAAS,EAAE;QAAC,IAAID,QAAQ;QAC1Eb,OAAOG,oBAAoB;YAAES,MAAM;YAAeE,SAAS;gBAAEC,MAAM;gBAAQC,SAAS;YAAK;QAAE,IAAIH,QAAQ;QACvGb,OAAOG,oBAAoB;YAAES,MAAM;YAAeE,SAAS;gBAAEC,MAAM;gBAAaC,SAAS;YAAI;QAAE,IAAIH,QAAQ;QAC3Gb,OAAOG,oBAAoB;YAAES,MAAM;YAAeE,SAAS;gBAAEC,MAAM;gBAAaC,SAAS;YAAS;QAAE,IAAIR,IAAI,CAAC;QAC7GR,OAAOG,oBAAoB;YAAES,MAAM;YAAeE,SAAS;gBAAEC,MAAM;gBAAaC,SAAS;YAAK;QAAE,IAAIH,QAAQ;QAC5Gb,OAAOG,oBAAoB;YAAES,MAAM;YAAeE,SAAS;gBAAEC,MAAM;gBAAaC,SAAS;oBAAC;oBAAM,EAAE;oBAAE;wBAAEJ,MAAM;oBAAW;iBAAE;YAAC;QAAE,IAAIC,QAAQ;QACxIb,OAAOG,oBAAoB;YAAES,MAAM;YAAeE,SAAS;gBAAEC,MAAM;gBAAaC,SAAS;oBAAC;wBAAEJ,MAAM;wBAAQK,MAAM;oBAAI;oBAAG;wBAAEL,MAAM;wBAAQK,MAAM;oBAAE;oBAAG;wBAAEL,MAAM;wBAAQK,MAAM;oBAAI;iBAAE;YAAC;QAAE,IAAIT,IAAI,CAAC;IAC9L;AACJ"}