@vibescope/mcp-server 0.0.1 → 0.1.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 (170) hide show
  1. package/README.md +113 -98
  2. package/dist/api-client.d.ts +1114 -0
  3. package/dist/api-client.js +698 -0
  4. package/dist/cli.d.ts +1 -6
  5. package/dist/cli.js +39 -240
  6. package/dist/config/tool-categories.d.ts +31 -0
  7. package/dist/config/tool-categories.js +253 -0
  8. package/dist/handlers/blockers.js +57 -58
  9. package/dist/handlers/bodies-of-work.d.ts +2 -0
  10. package/dist/handlers/bodies-of-work.js +106 -476
  11. package/dist/handlers/cost.d.ts +1 -0
  12. package/dist/handlers/cost.js +35 -113
  13. package/dist/handlers/decisions.d.ts +2 -0
  14. package/dist/handlers/decisions.js +28 -27
  15. package/dist/handlers/deployment.js +112 -828
  16. package/dist/handlers/discovery.js +31 -0
  17. package/dist/handlers/fallback.d.ts +2 -0
  18. package/dist/handlers/fallback.js +39 -134
  19. package/dist/handlers/findings.js +43 -67
  20. package/dist/handlers/git-issues.d.ts +9 -13
  21. package/dist/handlers/git-issues.js +80 -225
  22. package/dist/handlers/ideas.d.ts +3 -0
  23. package/dist/handlers/ideas.js +53 -134
  24. package/dist/handlers/index.d.ts +2 -0
  25. package/dist/handlers/index.js +6 -0
  26. package/dist/handlers/milestones.d.ts +2 -0
  27. package/dist/handlers/milestones.js +51 -98
  28. package/dist/handlers/organizations.js +79 -275
  29. package/dist/handlers/progress.d.ts +2 -0
  30. package/dist/handlers/progress.js +25 -123
  31. package/dist/handlers/project.js +42 -221
  32. package/dist/handlers/requests.d.ts +2 -0
  33. package/dist/handlers/requests.js +23 -83
  34. package/dist/handlers/session.js +99 -585
  35. package/dist/handlers/sprints.d.ts +32 -0
  36. package/dist/handlers/sprints.js +274 -0
  37. package/dist/handlers/tasks.d.ts +7 -10
  38. package/dist/handlers/tasks.js +230 -900
  39. package/dist/handlers/tool-docs.d.ts +8 -0
  40. package/dist/handlers/tool-docs.js +657 -0
  41. package/dist/handlers/types.d.ts +11 -3
  42. package/dist/handlers/validation.d.ts +1 -1
  43. package/dist/handlers/validation.js +26 -153
  44. package/dist/index.js +473 -160
  45. package/dist/knowledge.js +106 -9
  46. package/dist/tools.js +4 -0
  47. package/dist/validators.d.ts +21 -0
  48. package/dist/validators.js +91 -0
  49. package/package.json +2 -3
  50. package/src/api-client.ts +1752 -0
  51. package/src/cli.test.ts +128 -302
  52. package/src/cli.ts +41 -285
  53. package/src/handlers/__test-setup__.ts +210 -0
  54. package/src/handlers/__test-utils__.ts +4 -134
  55. package/src/handlers/blockers.test.ts +114 -124
  56. package/src/handlers/blockers.ts +68 -70
  57. package/src/handlers/bodies-of-work.test.ts +236 -831
  58. package/src/handlers/bodies-of-work.ts +194 -525
  59. package/src/handlers/cost.test.ts +149 -113
  60. package/src/handlers/cost.ts +44 -132
  61. package/src/handlers/decisions.test.ts +111 -209
  62. package/src/handlers/decisions.ts +35 -27
  63. package/src/handlers/deployment.test.ts +193 -239
  64. package/src/handlers/deployment.ts +140 -895
  65. package/src/handlers/discovery.test.ts +20 -67
  66. package/src/handlers/discovery.ts +32 -0
  67. package/src/handlers/fallback.test.ts +128 -361
  68. package/src/handlers/fallback.ts +62 -148
  69. package/src/handlers/findings.test.ts +127 -345
  70. package/src/handlers/findings.ts +49 -66
  71. package/src/handlers/git-issues.test.ts +623 -0
  72. package/src/handlers/git-issues.ts +174 -0
  73. package/src/handlers/ideas.test.ts +229 -343
  74. package/src/handlers/ideas.ts +69 -143
  75. package/src/handlers/index.ts +6 -0
  76. package/src/handlers/milestones.test.ts +167 -281
  77. package/src/handlers/milestones.ts +54 -93
  78. package/src/handlers/organizations.test.ts +275 -467
  79. package/src/handlers/organizations.ts +84 -294
  80. package/src/handlers/progress.test.ts +112 -218
  81. package/src/handlers/progress.ts +29 -142
  82. package/src/handlers/project.test.ts +203 -226
  83. package/src/handlers/project.ts +48 -238
  84. package/src/handlers/requests.test.ts +74 -342
  85. package/src/handlers/requests.ts +25 -83
  86. package/src/handlers/session.test.ts +241 -206
  87. package/src/handlers/session.ts +110 -657
  88. package/src/handlers/sprints.test.ts +711 -0
  89. package/src/handlers/sprints.ts +497 -0
  90. package/src/handlers/tasks.test.ts +608 -353
  91. package/src/handlers/tasks.ts +248 -1025
  92. package/src/handlers/types.ts +12 -4
  93. package/src/handlers/validation.test.ts +189 -572
  94. package/src/handlers/validation.ts +29 -166
  95. package/src/index.ts +473 -184
  96. package/src/knowledge.ts +107 -9
  97. package/src/tools.ts +2506 -0
  98. package/src/validators.test.ts +223 -223
  99. package/src/validators.ts +127 -0
  100. package/tsconfig.json +1 -1
  101. package/vitest.config.ts +14 -13
  102. package/dist/cli.test.d.ts +0 -1
  103. package/dist/cli.test.js +0 -367
  104. package/dist/handlers/__test-utils__.d.ts +0 -72
  105. package/dist/handlers/__test-utils__.js +0 -176
  106. package/dist/handlers/checkouts.d.ts +0 -37
  107. package/dist/handlers/checkouts.js +0 -377
  108. package/dist/handlers/knowledge-query.d.ts +0 -22
  109. package/dist/handlers/knowledge-query.js +0 -253
  110. package/dist/handlers/knowledge.d.ts +0 -12
  111. package/dist/handlers/knowledge.js +0 -108
  112. package/dist/handlers/roles.d.ts +0 -30
  113. package/dist/handlers/roles.js +0 -281
  114. package/dist/handlers/tasks.test.d.ts +0 -1
  115. package/dist/handlers/tasks.test.js +0 -431
  116. package/dist/utils.test.d.ts +0 -1
  117. package/dist/utils.test.js +0 -532
  118. package/dist/validators.test.d.ts +0 -1
  119. package/dist/validators.test.js +0 -176
  120. package/src/tmpclaude-0078-cwd +0 -1
  121. package/src/tmpclaude-0ee1-cwd +0 -1
  122. package/src/tmpclaude-2dd5-cwd +0 -1
  123. package/src/tmpclaude-344c-cwd +0 -1
  124. package/src/tmpclaude-3860-cwd +0 -1
  125. package/src/tmpclaude-4b63-cwd +0 -1
  126. package/src/tmpclaude-5c73-cwd +0 -1
  127. package/src/tmpclaude-5ee3-cwd +0 -1
  128. package/src/tmpclaude-6795-cwd +0 -1
  129. package/src/tmpclaude-709e-cwd +0 -1
  130. package/src/tmpclaude-9839-cwd +0 -1
  131. package/src/tmpclaude-d829-cwd +0 -1
  132. package/src/tmpclaude-e072-cwd +0 -1
  133. package/src/tmpclaude-f6ee-cwd +0 -1
  134. package/tmpclaude-0439-cwd +0 -1
  135. package/tmpclaude-132f-cwd +0 -1
  136. package/tmpclaude-15bb-cwd +0 -1
  137. package/tmpclaude-165a-cwd +0 -1
  138. package/tmpclaude-1ba9-cwd +0 -1
  139. package/tmpclaude-21a3-cwd +0 -1
  140. package/tmpclaude-2a38-cwd +0 -1
  141. package/tmpclaude-2adf-cwd +0 -1
  142. package/tmpclaude-2f56-cwd +0 -1
  143. package/tmpclaude-3626-cwd +0 -1
  144. package/tmpclaude-3727-cwd +0 -1
  145. package/tmpclaude-40bc-cwd +0 -1
  146. package/tmpclaude-436f-cwd +0 -1
  147. package/tmpclaude-4783-cwd +0 -1
  148. package/tmpclaude-4b6d-cwd +0 -1
  149. package/tmpclaude-4ba4-cwd +0 -1
  150. package/tmpclaude-51e6-cwd +0 -1
  151. package/tmpclaude-5ecf-cwd +0 -1
  152. package/tmpclaude-6f97-cwd +0 -1
  153. package/tmpclaude-7fb2-cwd +0 -1
  154. package/tmpclaude-825c-cwd +0 -1
  155. package/tmpclaude-8baf-cwd +0 -1
  156. package/tmpclaude-8d9f-cwd +0 -1
  157. package/tmpclaude-975c-cwd +0 -1
  158. package/tmpclaude-9983-cwd +0 -1
  159. package/tmpclaude-a045-cwd +0 -1
  160. package/tmpclaude-ac4a-cwd +0 -1
  161. package/tmpclaude-b593-cwd +0 -1
  162. package/tmpclaude-b891-cwd +0 -1
  163. package/tmpclaude-c032-cwd +0 -1
  164. package/tmpclaude-cf43-cwd +0 -1
  165. package/tmpclaude-d040-cwd +0 -1
  166. package/tmpclaude-dcdd-cwd +0 -1
  167. package/tmpclaude-dcee-cwd +0 -1
  168. package/tmpclaude-e16b-cwd +0 -1
  169. package/tmpclaude-ecd2-cwd +0 -1
  170. package/tmpclaude-f48d-cwd +0 -1
@@ -1,97 +1,8 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import type { SupabaseClient } from '@supabase/supabase-js';
3
- import type { HandlerContext, TokenUsage } from './types.js';
4
2
  import { getPendingRequests, acknowledgeRequest, answerQuestion } from './requests.js';
5
3
  import { ValidationError } from '../validators.js';
6
-
7
- // ============================================================================
8
- // Test Utilities
9
- // ============================================================================
10
-
11
- function createMockSupabase(overrides: {
12
- selectResult?: { data: unknown; error: unknown };
13
- updateResult?: { data: unknown; error: unknown };
14
- sessionsResult?: { data: unknown; error: unknown };
15
- } = {}) {
16
- const defaultResult = { data: null, error: null };
17
- // Use an object to track state so it persists across all mock function calls
18
- const state = {
19
- currentOperation: 'select' as string,
20
- currentTable: '' as string,
21
- updateCalled: false,
22
- };
23
-
24
- const mock = {
25
- from: vi.fn((table: string) => {
26
- state.currentTable = table;
27
- // Reset state for new query chain
28
- state.currentOperation = 'select';
29
- state.updateCalled = false;
30
- return mock;
31
- }),
32
- select: vi.fn(() => {
33
- // Don't reset updateCalled - we need to know if update was in this chain
34
- if (!state.updateCalled) {
35
- state.currentOperation = 'select';
36
- }
37
- return mock;
38
- }),
39
- update: vi.fn(() => {
40
- state.currentOperation = 'update';
41
- state.updateCalled = true;
42
- return mock;
43
- }),
44
- eq: vi.fn().mockReturnThis(),
45
- or: vi.fn().mockReturnThis(),
46
- order: vi.fn().mockReturnThis(),
47
- single: vi.fn(() => {
48
- // If update was called in this chain, return updateResult
49
- if (state.updateCalled) {
50
- return Promise.resolve(overrides.updateResult ?? defaultResult);
51
- }
52
- return Promise.resolve(overrides.selectResult ?? defaultResult);
53
- }),
54
- then: vi.fn((resolve: (value: unknown) => void) => {
55
- if (state.currentTable === 'agent_sessions') {
56
- return Promise.resolve(overrides.sessionsResult ?? { data: [], error: null }).then(resolve);
57
- }
58
- if (state.updateCalled) {
59
- return Promise.resolve(overrides.updateResult ?? defaultResult).then(resolve);
60
- }
61
- if (state.currentOperation === 'select') {
62
- return Promise.resolve(overrides.selectResult ?? defaultResult).then(resolve);
63
- }
64
- return Promise.resolve(defaultResult).then(resolve);
65
- }),
66
- };
67
-
68
- return mock as unknown as SupabaseClient;
69
- }
70
-
71
- function createMockContext(
72
- supabase: SupabaseClient,
73
- options: { sessionId?: string | null } = {}
74
- ): HandlerContext {
75
- const defaultTokenUsage: TokenUsage = {
76
- callCount: 5,
77
- totalTokens: 2500,
78
- byTool: {},
79
- };
80
-
81
- const sessionId = 'sessionId' in options ? options.sessionId : 'session-123';
82
-
83
- return {
84
- supabase,
85
- auth: { userId: 'user-123', apiKeyId: 'api-key-123' },
86
- session: {
87
- instanceId: 'instance-abc',
88
- currentSessionId: sessionId,
89
- currentPersona: 'Wave',
90
- tokenUsage: defaultTokenUsage,
91
- },
92
- updateSession: vi.fn(),
93
- };
94
- }
4
+ import { createMockContext } from './__test-utils__.js';
5
+ import { mockApiClient } from './__test-setup__.js';
95
6
 
96
7
  // ============================================================================
97
8
  // getPendingRequests Tests
@@ -101,15 +12,13 @@ describe('getPendingRequests', () => {
101
12
  beforeEach(() => vi.clearAllMocks());
102
13
 
103
14
  it('should throw error for missing project_id', async () => {
104
- const supabase = createMockSupabase();
105
- const ctx = createMockContext(supabase);
15
+ const ctx = createMockContext();
106
16
 
107
17
  await expect(getPendingRequests({}, ctx)).rejects.toThrow(ValidationError);
108
18
  });
109
19
 
110
20
  it('should throw error for invalid project_id UUID', async () => {
111
- const supabase = createMockSupabase();
112
- const ctx = createMockContext(supabase);
21
+ const ctx = createMockContext();
113
22
 
114
23
  await expect(
115
24
  getPendingRequests({ project_id: 'invalid' }, ctx)
@@ -117,11 +26,11 @@ describe('getPendingRequests', () => {
117
26
  });
118
27
 
119
28
  it('should return empty list when no requests', async () => {
120
- const supabase = createMockSupabase({
121
- selectResult: { data: [], error: null },
122
- sessionsResult: { data: [], error: null },
29
+ mockApiClient.getPendingRequests.mockResolvedValue({
30
+ ok: true,
31
+ data: { requests: [] },
123
32
  });
124
- const ctx = createMockContext(supabase);
33
+ const ctx = createMockContext();
125
34
 
126
35
  const result = await getPendingRequests(
127
36
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -131,7 +40,6 @@ describe('getPendingRequests', () => {
131
40
  expect(result.result).toMatchObject({
132
41
  requests: [],
133
42
  count: 0,
134
- questions_count: 0,
135
43
  });
136
44
  });
137
45
 
@@ -141,181 +49,57 @@ describe('getPendingRequests', () => {
141
49
  id: 'r1',
142
50
  request_type: 'task',
143
51
  content: 'Please do this',
144
- session_id: null, // broadcast
145
- acknowledged_at: null,
146
- created_at: '2025-01-14T10:00:00Z',
147
- },
148
- ];
149
-
150
- const supabase = createMockSupabase({
151
- selectResult: { data: mockRequests, error: null },
152
- sessionsResult: { data: [], error: null },
153
- });
154
- const ctx = createMockContext(supabase);
155
-
156
- const result = await getPendingRequests(
157
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
158
- ctx
159
- );
160
-
161
- expect((result.result as { count: number }).count).toBe(1);
162
- });
163
-
164
- it('should filter broadcast requests (session_id is null)', async () => {
165
- const mockRequests = [
166
- {
167
- id: 'r1',
168
- request_type: 'task',
169
- content: 'Broadcast request',
170
52
  session_id: null,
171
53
  acknowledged_at: null,
172
54
  created_at: '2025-01-14T10:00:00Z',
173
55
  },
174
56
  ];
175
57
 
176
- const supabase = createMockSupabase({
177
- selectResult: { data: mockRequests, error: null },
178
- sessionsResult: { data: [], error: null },
58
+ mockApiClient.getPendingRequests.mockResolvedValue({
59
+ ok: true,
60
+ data: { requests: mockRequests },
179
61
  });
180
- const ctx = createMockContext(supabase);
62
+ const ctx = createMockContext();
181
63
 
182
64
  const result = await getPendingRequests(
183
65
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
184
66
  ctx
185
67
  );
186
68
 
187
- // Broadcast request should be included
188
69
  expect((result.result as { count: number }).count).toBe(1);
189
70
  });
190
71
 
191
- it('should include requests targeted to current session', async () => {
192
- const mockRequests = [
193
- {
194
- id: 'r1',
195
- request_type: 'task',
196
- content: 'Targeted request',
197
- session_id: 'session-123',
198
- acknowledged_at: null,
199
- created_at: '2025-01-14T10:00:00Z',
200
- },
201
- ];
202
-
203
- const supabase = createMockSupabase({
204
- selectResult: { data: mockRequests, error: null },
205
- sessionsResult: { data: [], error: null },
206
- });
207
- const ctx = createMockContext(supabase, { sessionId: 'session-123' });
208
-
209
- const result = await getPendingRequests(
210
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
211
- ctx
212
- );
213
-
214
- expect((result.result as { count: number }).count).toBe(1);
215
- });
216
-
217
- it('should exclude requests targeted to other sessions', async () => {
218
- const mockRequests = [
219
- {
220
- id: 'r1',
221
- request_type: 'task',
222
- content: 'Other session request',
223
- session_id: 'other-session',
224
- acknowledged_at: null,
225
- created_at: '2025-01-14T10:00:00Z',
226
- },
227
- ];
228
-
229
- const supabase = createMockSupabase({
230
- selectResult: { data: mockRequests, error: null },
231
- sessionsResult: { data: [{ id: 'other-session' }], error: null },
72
+ it('should call API client with project_id and session_id', async () => {
73
+ mockApiClient.getPendingRequests.mockResolvedValue({
74
+ ok: true,
75
+ data: { requests: [] },
232
76
  });
233
- const ctx = createMockContext(supabase, { sessionId: 'session-123' });
77
+ const ctx = createMockContext({ sessionId: 'my-session' });
234
78
 
235
- const result = await getPendingRequests(
236
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
237
- ctx
238
- );
239
-
240
- // Should be excluded because targeted to an active other session
241
- expect((result.result as { count: number }).count).toBe(0);
242
- });
243
-
244
- it('should count unanswered questions', async () => {
245
- const mockRequests = [
246
- {
247
- id: 'r1',
248
- request_type: 'question',
249
- content: 'What is this?',
250
- session_id: null,
251
- acknowledged_at: null,
252
- answered_at: null,
253
- created_at: '2025-01-14T10:00:00Z',
254
- },
255
- {
256
- id: 'r2',
257
- request_type: 'task',
258
- content: 'Do this',
259
- session_id: null,
260
- acknowledged_at: null,
261
- created_at: '2025-01-14T11:00:00Z',
262
- },
263
- ];
264
-
265
- const supabase = createMockSupabase({
266
- selectResult: { data: mockRequests, error: null },
267
- sessionsResult: { data: [], error: null },
268
- });
269
- const ctx = createMockContext(supabase);
270
-
271
- const result = await getPendingRequests(
79
+ await getPendingRequests(
272
80
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
273
81
  ctx
274
82
  );
275
83
 
276
- expect((result.result as { questions_count: number }).questions_count).toBe(1);
277
- });
278
-
279
- it('should add wait_minutes to requests', async () => {
280
- const mockRequests = [
281
- {
282
- id: 'r1',
283
- request_type: 'task',
284
- content: 'Request',
285
- session_id: null,
286
- acknowledged_at: null,
287
- created_at: new Date(Date.now() - 5 * 60000).toISOString(), // 5 minutes ago
288
- },
289
- ];
290
-
291
- const supabase = createMockSupabase({
292
- selectResult: { data: mockRequests, error: null },
293
- sessionsResult: { data: [], error: null },
294
- });
295
- const ctx = createMockContext(supabase);
296
-
297
- const result = await getPendingRequests(
298
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
299
- ctx
84
+ expect(mockApiClient.getPendingRequests).toHaveBeenCalledWith(
85
+ '123e4567-e89b-12d3-a456-426614174000',
86
+ 'my-session'
300
87
  );
301
-
302
- const requests = (result.result as { requests: { wait_minutes: number }[] }).requests;
303
- expect(requests[0].wait_minutes).toBeGreaterThanOrEqual(4); // Allow some margin
304
88
  });
305
89
 
306
- it('should query agent_requests table', async () => {
307
- const supabase = createMockSupabase({
308
- selectResult: { data: [], error: null },
309
- sessionsResult: { data: [], error: null },
90
+ it('should throw error when API call fails', async () => {
91
+ mockApiClient.getPendingRequests.mockResolvedValue({
92
+ ok: false,
93
+ error: 'Query failed',
310
94
  });
311
- const ctx = createMockContext(supabase);
312
-
313
- await getPendingRequests(
314
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
315
- ctx
316
- );
95
+ const ctx = createMockContext();
317
96
 
318
- expect(supabase.from).toHaveBeenCalledWith('agent_requests');
97
+ await expect(
98
+ getPendingRequests(
99
+ { project_id: '123e4567-e89b-12d3-a456-426614174000' },
100
+ ctx
101
+ )
102
+ ).rejects.toThrow('Failed to get pending requests: Query failed');
319
103
  });
320
104
  });
321
105
 
@@ -327,15 +111,13 @@ describe('acknowledgeRequest', () => {
327
111
  beforeEach(() => vi.clearAllMocks());
328
112
 
329
113
  it('should throw error for missing request_id', async () => {
330
- const supabase = createMockSupabase();
331
- const ctx = createMockContext(supabase);
114
+ const ctx = createMockContext();
332
115
 
333
116
  await expect(acknowledgeRequest({}, ctx)).rejects.toThrow(ValidationError);
334
117
  });
335
118
 
336
119
  it('should throw error for invalid request_id UUID', async () => {
337
- const supabase = createMockSupabase();
338
- const ctx = createMockContext(supabase);
120
+ const ctx = createMockContext();
339
121
 
340
122
  await expect(
341
123
  acknowledgeRequest({ request_id: 'invalid' }, ctx)
@@ -343,17 +125,11 @@ describe('acknowledgeRequest', () => {
343
125
  });
344
126
 
345
127
  it('should acknowledge request successfully', async () => {
346
- const mockRequest = {
347
- id: 'r1',
348
- request_type: 'task',
349
- content: 'Do this',
350
- acknowledged_at: '2025-01-14T12:00:00Z',
351
- };
352
-
353
- const supabase = createMockSupabase({
354
- updateResult: { data: mockRequest, error: null },
128
+ mockApiClient.acknowledgeRequest.mockResolvedValue({
129
+ ok: true,
130
+ data: { success: true },
355
131
  });
356
- const ctx = createMockContext(supabase);
132
+ const ctx = createMockContext();
357
133
 
358
134
  const result = await acknowledgeRequest(
359
135
  { request_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -362,52 +138,37 @@ describe('acknowledgeRequest', () => {
362
138
 
363
139
  expect(result.result).toMatchObject({
364
140
  success: true,
365
- request: mockRequest,
366
141
  });
367
142
  });
368
143
 
369
- it('should set acknowledged_at and acknowledged_by_session_id', async () => {
370
- const supabase = createMockSupabase({
371
- updateResult: { data: { id: 'r1' }, error: null },
144
+ it('should call API client with request_id and session_id', async () => {
145
+ mockApiClient.acknowledgeRequest.mockResolvedValue({
146
+ ok: true,
147
+ data: { success: true },
372
148
  });
373
- const ctx = createMockContext(supabase, { sessionId: 'my-session' });
149
+ const ctx = createMockContext({ sessionId: 'my-session' });
374
150
 
375
151
  await acknowledgeRequest(
376
152
  { request_id: '123e4567-e89b-12d3-a456-426614174000' },
377
153
  ctx
378
154
  );
379
155
 
380
- expect(supabase.update).toHaveBeenCalledWith(
381
- expect.objectContaining({
382
- acknowledged_at: expect.any(String),
383
- acknowledged_by_session_id: 'my-session',
384
- })
385
- );
386
- });
387
-
388
- it('should query agent_requests table', async () => {
389
- const supabase = createMockSupabase({
390
- updateResult: { data: { id: 'r1' }, error: null },
391
- });
392
- const ctx = createMockContext(supabase);
393
-
394
- await acknowledgeRequest(
395
- { request_id: '123e4567-e89b-12d3-a456-426614174000' },
396
- ctx
156
+ expect(mockApiClient.acknowledgeRequest).toHaveBeenCalledWith(
157
+ '123e4567-e89b-12d3-a456-426614174000',
158
+ 'my-session'
397
159
  );
398
-
399
- expect(supabase.from).toHaveBeenCalledWith('agent_requests');
400
160
  });
401
161
 
402
- it('should throw error when update fails', async () => {
403
- const supabase = createMockSupabase({
404
- updateResult: { data: null, error: { message: 'Update failed' } },
162
+ it('should throw error when API call fails', async () => {
163
+ mockApiClient.acknowledgeRequest.mockResolvedValue({
164
+ ok: false,
165
+ error: 'Update failed',
405
166
  });
406
- const ctx = createMockContext(supabase);
167
+ const ctx = createMockContext();
407
168
 
408
169
  await expect(
409
170
  acknowledgeRequest({ request_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
410
- ).rejects.toThrow();
171
+ ).rejects.toThrow('Failed to acknowledge request: Update failed');
411
172
  });
412
173
  });
413
174
 
@@ -419,8 +180,7 @@ describe('answerQuestion', () => {
419
180
  beforeEach(() => vi.clearAllMocks());
420
181
 
421
182
  it('should throw error for missing request_id', async () => {
422
- const supabase = createMockSupabase();
423
- const ctx = createMockContext(supabase);
183
+ const ctx = createMockContext();
424
184
 
425
185
  await expect(
426
186
  answerQuestion({ answer: 'The answer' }, ctx)
@@ -428,8 +188,7 @@ describe('answerQuestion', () => {
428
188
  });
429
189
 
430
190
  it('should throw error for missing answer', async () => {
431
- const supabase = createMockSupabase();
432
- const ctx = createMockContext(supabase);
191
+ const ctx = createMockContext();
433
192
 
434
193
  await expect(
435
194
  answerQuestion({ request_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
@@ -437,8 +196,7 @@ describe('answerQuestion', () => {
437
196
  });
438
197
 
439
198
  it('should throw error for invalid request_id UUID', async () => {
440
- const supabase = createMockSupabase();
441
- const ctx = createMockContext(supabase);
199
+ const ctx = createMockContext();
442
200
 
443
201
  await expect(
444
202
  answerQuestion({ request_id: 'invalid', answer: 'The answer' }, ctx)
@@ -446,18 +204,11 @@ describe('answerQuestion', () => {
446
204
  });
447
205
 
448
206
  it('should answer question successfully', async () => {
449
- const mockRequest = {
450
- id: 'r1',
451
- request_type: 'question',
452
- content: 'What is this?',
453
- answer: 'This is the answer',
454
- answered_at: '2025-01-14T12:00:00Z',
455
- };
456
-
457
- const supabase = createMockSupabase({
458
- updateResult: { data: mockRequest, error: null },
207
+ mockApiClient.answerQuestion.mockResolvedValue({
208
+ ok: true,
209
+ data: { success: true },
459
210
  });
460
- const ctx = createMockContext(supabase);
211
+ const ctx = createMockContext();
461
212
 
462
213
  const result = await answerQuestion(
463
214
  {
@@ -470,15 +221,15 @@ describe('answerQuestion', () => {
470
221
  expect(result.result).toMatchObject({
471
222
  success: true,
472
223
  message: 'Question answered successfully',
473
- request: mockRequest,
474
224
  });
475
225
  });
476
226
 
477
- it('should set answer, answered_at, and acknowledgment fields', async () => {
478
- const supabase = createMockSupabase({
479
- updateResult: { data: { id: 'r1' }, error: null },
227
+ it('should call API client with request_id, answer, and session_id', async () => {
228
+ mockApiClient.answerQuestion.mockResolvedValue({
229
+ ok: true,
230
+ data: { success: true },
480
231
  });
481
- const ctx = createMockContext(supabase, { sessionId: 'my-session' });
232
+ const ctx = createMockContext({ sessionId: 'my-session' });
482
233
 
483
234
  await answerQuestion(
484
235
  {
@@ -488,44 +239,25 @@ describe('answerQuestion', () => {
488
239
  ctx
489
240
  );
490
241
 
491
- expect(supabase.update).toHaveBeenCalledWith(
492
- expect.objectContaining({
493
- answer: 'The answer to the question',
494
- answered_at: expect.any(String),
495
- acknowledged_at: expect.any(String),
496
- acknowledged_by_session_id: 'my-session',
497
- })
242
+ expect(mockApiClient.answerQuestion).toHaveBeenCalledWith(
243
+ '123e4567-e89b-12d3-a456-426614174000',
244
+ 'The answer to the question',
245
+ 'my-session'
498
246
  );
499
247
  });
500
248
 
501
- it('should query agent_requests table', async () => {
502
- const supabase = createMockSupabase({
503
- updateResult: { data: { id: 'r1' }, error: null },
504
- });
505
- const ctx = createMockContext(supabase);
506
-
507
- await answerQuestion(
508
- {
509
- request_id: '123e4567-e89b-12d3-a456-426614174000',
510
- answer: 'Answer',
511
- },
512
- ctx
513
- );
514
-
515
- expect(supabase.from).toHaveBeenCalledWith('agent_requests');
516
- });
517
-
518
- it('should throw error when update fails', async () => {
519
- const supabase = createMockSupabase({
520
- updateResult: { data: null, error: { message: 'Update failed' } },
249
+ it('should throw error when API call fails', async () => {
250
+ mockApiClient.answerQuestion.mockResolvedValue({
251
+ ok: false,
252
+ error: 'Update failed',
521
253
  });
522
- const ctx = createMockContext(supabase);
254
+ const ctx = createMockContext();
523
255
 
524
256
  await expect(
525
257
  answerQuestion({
526
258
  request_id: '123e4567-e89b-12d3-a456-426614174000',
527
259
  answer: 'Answer',
528
260
  }, ctx)
529
- ).rejects.toThrow();
261
+ ).rejects.toThrow('Failed to answer question: Update failed');
530
262
  });
531
263
  });