@shaykec/bridge 0.4.19 → 0.4.20

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 (58) hide show
  1. package/canvas-dist/assets/{_basePickBy-BOTBlJNd.js → _basePickBy-CWoeT3J7.js} +1 -1
  2. package/canvas-dist/assets/{_baseUniq-EF6Y2_Wm.js → _baseUniq-Dtuvtwtn.js} +1 -1
  3. package/canvas-dist/assets/{arc-C_vIirh2.js → arc-YYWnrNJU.js} +1 -1
  4. package/canvas-dist/assets/{architectureDiagram-VXUJARFQ-EvM6tQ7I.js → architectureDiagram-VXUJARFQ-CegbV-RR.js} +1 -1
  5. package/canvas-dist/assets/{blockDiagram-VD42YOAC-B_rbZyqc.js → blockDiagram-VD42YOAC-C2e_j6ry.js} +1 -1
  6. package/canvas-dist/assets/{c4Diagram-YG6GDRKO-J9PHecY3.js → c4Diagram-YG6GDRKO-rIpnAud9.js} +1 -1
  7. package/canvas-dist/assets/channel-BzJVlie3.js +1 -0
  8. package/canvas-dist/assets/{chunk-4BX2VUAB-DjcN96Mk.js → chunk-4BX2VUAB-CpZGetnU.js} +1 -1
  9. package/canvas-dist/assets/{chunk-55IACEB6-CTdcUQSV.js → chunk-55IACEB6-L0OhcFdd.js} +1 -1
  10. package/canvas-dist/assets/{chunk-B4BG7PRW-Dcov7eRi.js → chunk-B4BG7PRW-Cv9vsAzg.js} +1 -1
  11. package/canvas-dist/assets/{chunk-DI55MBZ5-DUJCBZzM.js → chunk-DI55MBZ5-B3p1mU43.js} +1 -1
  12. package/canvas-dist/assets/{chunk-FMBD7UC4-EfGA9ufe.js → chunk-FMBD7UC4-JCLAHw5x.js} +1 -1
  13. package/canvas-dist/assets/{chunk-QN33PNHL-Cu6V1xBU.js → chunk-QN33PNHL-C9arKEVq.js} +1 -1
  14. package/canvas-dist/assets/{chunk-QZHKN3VN-avF3sH_r.js → chunk-QZHKN3VN-Bs1r3d9U.js} +1 -1
  15. package/canvas-dist/assets/{chunk-TZMSLE5B-CkWW-qpk.js → chunk-TZMSLE5B-_Ye6r84Y.js} +1 -1
  16. package/canvas-dist/assets/classDiagram-2ON5EDUG-BTs-zEmB.js +1 -0
  17. package/canvas-dist/assets/classDiagram-v2-WZHVMYZB-BTs-zEmB.js +1 -0
  18. package/canvas-dist/assets/clone-CXEfuXmc.js +1 -0
  19. package/canvas-dist/assets/{cose-bilkent-S5V4N54A-DDE4zf7X.js → cose-bilkent-S5V4N54A-2O2oovOj.js} +1 -1
  20. package/canvas-dist/assets/{dagre-6UL2VRFP-BD6MGb7B.js → dagre-6UL2VRFP-gRmGLrEW.js} +1 -1
  21. package/canvas-dist/assets/{diagram-PSM6KHXK-yyu-ytzf.js → diagram-PSM6KHXK-B7Li-xxw.js} +1 -1
  22. package/canvas-dist/assets/{diagram-QEK2KX5R-B_H957Uf.js → diagram-QEK2KX5R-B_NNUAm3.js} +1 -1
  23. package/canvas-dist/assets/{diagram-S2PKOQOG-DuebuBVv.js → diagram-S2PKOQOG-NcK-KHaA.js} +1 -1
  24. package/canvas-dist/assets/{erDiagram-Q2GNP2WA-AxqPt6IZ.js → erDiagram-Q2GNP2WA-CG7dqzk3.js} +1 -1
  25. package/canvas-dist/assets/{flowDiagram-NV44I4VS-mDhW3D3Q.js → flowDiagram-NV44I4VS-CBzCj5D6.js} +1 -1
  26. package/canvas-dist/assets/{ganttDiagram-JELNMOA3-sA8pHJPp.js → ganttDiagram-JELNMOA3-CHw-4qJC.js} +1 -1
  27. package/canvas-dist/assets/{gitGraphDiagram-V2S2FVAM-CvLzvhKr.js → gitGraphDiagram-V2S2FVAM-Dqrc4wUs.js} +1 -1
  28. package/canvas-dist/assets/{graph-BVZqMrwW.js → graph-X9Kzu-pf.js} +1 -1
  29. package/canvas-dist/assets/{index-CF3qc2Xb.js → index-BQFKo-II.js} +1 -1
  30. package/canvas-dist/assets/index-DJ49c6u-.js +426 -0
  31. package/canvas-dist/assets/{infoDiagram-HS3SLOUP-D1Kg3Q9d.js → infoDiagram-HS3SLOUP-CflnZPsm.js} +1 -1
  32. package/canvas-dist/assets/{journeyDiagram-XKPGCS4Q-D7ogbx9z.js → journeyDiagram-XKPGCS4Q-D2gkCipQ.js} +1 -1
  33. package/canvas-dist/assets/{kanban-definition-3W4ZIXB7-CDcnICM9.js → kanban-definition-3W4ZIXB7-CtLLz4o8.js} +1 -1
  34. package/canvas-dist/assets/{layout-CuaK7i3M.js → layout-CjvV_Dms.js} +1 -1
  35. package/canvas-dist/assets/{linear-CLSTOJ0g.js → linear-D3cIYHoS.js} +1 -1
  36. package/canvas-dist/assets/{mindmap-definition-VGOIOE7T-TrK7CIKt.js → mindmap-definition-VGOIOE7T-DSgjVg-P.js} +1 -1
  37. package/canvas-dist/assets/{pieDiagram-ADFJNKIX-BcIKTRbi.js → pieDiagram-ADFJNKIX-B_lYaGFj.js} +1 -1
  38. package/canvas-dist/assets/{quadrantDiagram-AYHSOK5B-EOHXFGoQ.js → quadrantDiagram-AYHSOK5B-DLZLTJe3.js} +1 -1
  39. package/canvas-dist/assets/{requirementDiagram-UZGBJVZJ-CJ8lImGs.js → requirementDiagram-UZGBJVZJ-CZE26rhL.js} +1 -1
  40. package/canvas-dist/assets/{sankeyDiagram-TZEHDZUN-4cANY87E.js → sankeyDiagram-TZEHDZUN-DQMRJAPV.js} +1 -1
  41. package/canvas-dist/assets/{sequenceDiagram-WL72ISMW-D9HrEsci.js → sequenceDiagram-WL72ISMW-BY723FEn.js} +1 -1
  42. package/canvas-dist/assets/{stateDiagram-FKZM4ZOC-qVbMjauZ.js → stateDiagram-FKZM4ZOC-C_UdOFhy.js} +1 -1
  43. package/canvas-dist/assets/stateDiagram-v2-4FDKWEC3-DXIiFh0L.js +1 -0
  44. package/canvas-dist/assets/{timeline-definition-IT6M3QCI-DDBlkydm.js → timeline-definition-IT6M3QCI-DkrJqww0.js} +1 -1
  45. package/canvas-dist/assets/{treemap-GDKQZRPO-D4a8udjO.js → treemap-GDKQZRPO-B-6bMZqD.js} +1 -1
  46. package/canvas-dist/assets/{xychartDiagram-PRI3JC2R-DteXAAAu.js → xychartDiagram-PRI3JC2R-DkBhUy_D.js} +1 -1
  47. package/canvas-dist/index.html +1 -1
  48. package/package.json +3 -2
  49. package/src/server.e2e.test.js +10 -41
  50. package/src/server.js +302 -186
  51. package/canvas-dist/assets/channel-saCUO1KA.js +0 -1
  52. package/canvas-dist/assets/classDiagram-2ON5EDUG-CBLbQwHx.js +0 -1
  53. package/canvas-dist/assets/classDiagram-v2-WZHVMYZB-CBLbQwHx.js +0 -1
  54. package/canvas-dist/assets/clone-DXnda9BY.js +0 -1
  55. package/canvas-dist/assets/index-DYNtb52W.js +0 -426
  56. package/canvas-dist/assets/stateDiagram-v2-4FDKWEC3-MT16RLO4.js +0 -1
  57. package/src/claude-session.js +0 -414
  58. package/src/claude-session.test.js +0 -326
@@ -1,326 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { ClaudeSessionManager } from './claude-session.js';
3
-
4
- describe('ClaudeSessionManager', () => {
5
- let manager;
6
- let emittedMessages;
7
- let mockSDK;
8
-
9
- function makeMockSession(id) {
10
- return {
11
- sessionId: id,
12
- send: vi.fn(),
13
- stream: vi.fn(),
14
- close: vi.fn(),
15
- };
16
- }
17
-
18
- beforeEach(() => {
19
- vi.clearAllMocks();
20
- emittedMessages = [];
21
- mockSDK = {
22
- unstable_v2_createSession: vi.fn(),
23
- unstable_v2_resumeSession: vi.fn(),
24
- listSessions: vi.fn(),
25
- };
26
- manager = new ClaudeSessionManager(mockSDK);
27
- });
28
-
29
- const mockOnMessage = (envelope) => {
30
- emittedMessages.push(envelope);
31
- };
32
-
33
- describe('createSession', () => {
34
- it('creates a session and emits started status', async () => {
35
- const mockSession = makeMockSession('sdk-id-1');
36
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
37
-
38
- const sessionId = await manager.createSession({ onMessage: mockOnMessage });
39
-
40
- // sessionId is a UUID we generate, not the SDK's
41
- expect(sessionId).toMatch(/^[0-9a-f-]{36}$/);
42
- expect(manager.isActive(sessionId)).toBe(true);
43
- expect(emittedMessages).toHaveLength(1);
44
- expect(emittedMessages[0].type).toBe('chat:status');
45
- expect(emittedMessages[0].payload.status).toBe('started');
46
- expect(emittedMessages[0].payload.sessionId).toBe(sessionId);
47
- });
48
-
49
- it('passes model, cwd and pluginDir to the SDK', async () => {
50
- const mockSession = makeMockSession('s1');
51
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
52
-
53
- await manager.createSession({
54
- cwd: '/test/dir',
55
- pluginDir: '/test/plugin',
56
- onMessage: mockOnMessage,
57
- });
58
-
59
- const opts = mockSDK.unstable_v2_createSession.mock.calls[0][0];
60
- expect(opts.model).toBe('claude-sonnet-4-6');
61
- expect(opts.cwd).toBe('/test/dir');
62
- expect(opts.plugins).toEqual([{ type: 'local', path: '/test/plugin' }]);
63
- });
64
-
65
- it('emits error status on SDK failure', async () => {
66
- mockSDK.unstable_v2_createSession.mockImplementation(() => {
67
- throw new Error('SDK init failed');
68
- });
69
-
70
- await expect(
71
- manager.createSession({ onMessage: mockOnMessage })
72
- ).rejects.toThrow('SDK init failed');
73
-
74
- expect(emittedMessages[0].type).toBe('chat:status');
75
- expect(emittedMessages[0].payload.status).toBe('error');
76
- });
77
- });
78
-
79
- describe('resumeSession', () => {
80
- it('resumes a session by ID', async () => {
81
- const mockSession = makeMockSession('existing-session');
82
- mockSDK.unstable_v2_resumeSession.mockReturnValue(mockSession);
83
-
84
- const sessionId = await manager.resumeSession('existing-session', { onMessage: mockOnMessage });
85
-
86
- expect(sessionId).toBe('existing-session');
87
- expect(mockSDK.unstable_v2_resumeSession).toHaveBeenCalledWith('existing-session', expect.any(Object));
88
- expect(emittedMessages[0].payload.status).toBe('resumed');
89
- });
90
- });
91
-
92
- describe('sendMessage', () => {
93
- it('throws if no active session', async () => {
94
- await expect(manager.sendMessage('nonexistent', 'hello')).rejects.toThrow('No active session');
95
- });
96
-
97
- it('sends a message and streams response', async () => {
98
- async function* fakeStream() {
99
- yield { type: 'assistant', message: { content: [{ type: 'text', text: 'Hello' }] } };
100
- yield { type: 'result' };
101
- }
102
-
103
- const mockSession = makeMockSession('stream-test');
104
- mockSession.stream.mockReturnValue(fakeStream());
105
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
106
- const sessionId = await manager.createSession({ onMessage: mockOnMessage });
107
-
108
- emittedMessages = [];
109
- await manager.sendMessage(sessionId, 'test message');
110
-
111
- expect(mockSession.send).toHaveBeenCalledWith('test message');
112
- expect(mockSession.stream).toHaveBeenCalled();
113
-
114
- const types = emittedMessages.map(m => m.type);
115
- expect(types).toContain('chat:status');
116
- expect(types).toContain('chat:stream');
117
- expect(types).toContain('chat:assistant');
118
- });
119
-
120
- it('captures SDK sessionId during streaming', async () => {
121
- async function* fakeStream() {
122
- yield { type: 'system' };
123
- yield { type: 'assistant', message: { content: [{ type: 'text', text: 'Hi' }] } };
124
- yield { type: 'result' };
125
- }
126
-
127
- const mockSession = makeMockSession('real-sdk-id');
128
- mockSession.stream.mockReturnValue(fakeStream());
129
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
130
- const sessionId = await manager.createSession({ onMessage: mockOnMessage });
131
-
132
- expect(manager.getSdkSessionId(sessionId)).toBeNull();
133
-
134
- await manager.sendMessage(sessionId, 'test');
135
-
136
- expect(manager.getSdkSessionId(sessionId)).toBe('real-sdk-id');
137
- });
138
-
139
- it('emits tool-use and tool-result events', async () => {
140
- async function* fakeStream() {
141
- yield { type: 'tool_use', name: 'Bash', id: 'tool-1', input: { command: 'ls' } };
142
- yield { type: 'tool_result', tool_use_id: 'tool-1', content: 'file1\nfile2' };
143
- yield { type: 'result' };
144
- }
145
-
146
- const mockSession = makeMockSession('tool-test');
147
- mockSession.stream.mockReturnValue(fakeStream());
148
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
149
- const sessionId = await manager.createSession({ onMessage: mockOnMessage });
150
-
151
- emittedMessages = [];
152
- await manager.sendMessage(sessionId, 'list files');
153
-
154
- const toolUse = emittedMessages.find(m => m.type === 'chat:tool-use');
155
- expect(toolUse).toBeDefined();
156
- expect(toolUse.payload.toolName).toBe('Bash');
157
-
158
- const toolResult = emittedMessages.find(m => m.type === 'chat:tool-result');
159
- expect(toolResult).toBeDefined();
160
- expect(toolResult.payload.toolId).toBe('tool-1');
161
- });
162
- });
163
-
164
- describe('multiple sessions', () => {
165
- it('supports multiple concurrent sessions', async () => {
166
- const session1 = makeMockSession('sdk-1');
167
- const session2 = makeMockSession('sdk-2');
168
- mockSDK.unstable_v2_createSession
169
- .mockReturnValueOnce(session1)
170
- .mockReturnValueOnce(session2);
171
-
172
- const messages1 = [];
173
- const messages2 = [];
174
-
175
- const id1 = await manager.createSession({ onMessage: (e) => messages1.push(e) });
176
- const id2 = await manager.createSession({ onMessage: (e) => messages2.push(e) });
177
-
178
- expect(id1).not.toBe(id2);
179
- expect(manager.isActive(id1)).toBe(true);
180
- expect(manager.isActive(id2)).toBe(true);
181
- expect(manager.getActiveSessionIds()).toHaveLength(2);
182
-
183
- // Status messages go to the right callback
184
- expect(messages1[0].payload.sessionId).toBe(id1);
185
- expect(messages2[0].payload.sessionId).toBe(id2);
186
- });
187
-
188
- it('closing one session does not affect others', async () => {
189
- const session1 = makeMockSession('sdk-1');
190
- const session2 = makeMockSession('sdk-2');
191
- mockSDK.unstable_v2_createSession
192
- .mockReturnValueOnce(session1)
193
- .mockReturnValueOnce(session2);
194
-
195
- const id1 = await manager.createSession({ onMessage: mockOnMessage });
196
- const id2 = await manager.createSession({ onMessage: mockOnMessage });
197
-
198
- await manager.closeSession(id1);
199
-
200
- expect(manager.isActive(id1)).toBe(false);
201
- expect(manager.isActive(id2)).toBe(true);
202
- });
203
-
204
- it('closeAll closes all sessions', async () => {
205
- const session1 = makeMockSession('sdk-1');
206
- const session2 = makeMockSession('sdk-2');
207
- mockSDK.unstable_v2_createSession
208
- .mockReturnValueOnce(session1)
209
- .mockReturnValueOnce(session2);
210
-
211
- await manager.createSession({ onMessage: mockOnMessage });
212
- await manager.createSession({ onMessage: mockOnMessage });
213
-
214
- await manager.closeAll();
215
-
216
- expect(manager.getActiveSessionIds()).toHaveLength(0);
217
- expect(session1.close).toHaveBeenCalled();
218
- expect(session2.close).toHaveBeenCalled();
219
- });
220
- });
221
-
222
- describe('stop', () => {
223
- it('is a no-op when not streaming', async () => {
224
- const mockSession = makeMockSession('s1');
225
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
226
- const sessionId = await manager.createSession({ onMessage: mockOnMessage });
227
-
228
- emittedMessages = [];
229
- await manager.stop(sessionId);
230
-
231
- // Session is still active since it wasn't streaming
232
- expect(manager.isActive(sessionId)).toBe(true);
233
- expect(emittedMessages).toHaveLength(0);
234
- });
235
- });
236
-
237
- describe('listSessions', () => {
238
- it('returns mapped session list', async () => {
239
- mockSDK.listSessions.mockResolvedValue([
240
- { sessionId: 's1', summary: 'Test', lastModified: 12345 },
241
- { sessionId: 's2', summary: 'Other', lastModified: 67890 },
242
- ]);
243
-
244
- const sessions = await manager.listSessions('/test');
245
- expect(sessions).toHaveLength(2);
246
- expect(sessions[0].sessionId).toBe('s1');
247
- });
248
-
249
- it('returns empty array on error', async () => {
250
- mockSDK.listSessions.mockRejectedValue(new Error('fail'));
251
- const sessions = await manager.listSessions('/test');
252
- expect(sessions).toEqual([]);
253
- });
254
- });
255
-
256
- describe('closeSession', () => {
257
- it('closes active session and removes from map', async () => {
258
- const mockSession = makeMockSession('s1');
259
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
260
- const sessionId = await manager.createSession({ onMessage: mockOnMessage });
261
-
262
- expect(manager.isActive(sessionId)).toBe(true);
263
- await manager.closeSession(sessionId);
264
- expect(manager.isActive(sessionId)).toBe(false);
265
- });
266
- });
267
-
268
- describe('isAvailable', () => {
269
- it('returns true when SDK is available', async () => {
270
- const available = await manager.isAvailable();
271
- expect(available).toBe(true);
272
- });
273
- });
274
-
275
- describe('sendMessage edge cases', () => {
276
- it('throws if already streaming', async () => {
277
- async function* slowStream() {
278
- yield { type: 'assistant', message: { content: [{ type: 'text', text: 'Hi' }] } };
279
- // never yields 'result' — hangs forever
280
- await new Promise(() => {});
281
- }
282
-
283
- const mockSession = makeMockSession('busy');
284
- mockSession.stream.mockReturnValue(slowStream());
285
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
286
- const sessionId = await manager.createSession({ onMessage: mockOnMessage });
287
-
288
- // Start streaming (don't await — it won't finish)
289
- const streamPromise = manager.sendMessage(sessionId, 'first');
290
-
291
- // Wait a tick for streaming flag to be set
292
- await new Promise(r => setTimeout(r, 10));
293
-
294
- await expect(manager.sendMessage(sessionId, 'second')).rejects.toThrow('Already streaming');
295
-
296
- // Clean up
297
- await manager.closeSession(sessionId);
298
- });
299
-
300
- it('emits error status on stream failure', async () => {
301
- async function* failStream() {
302
- throw new Error('stream broke');
303
- }
304
-
305
- const mockSession = makeMockSession('fail-stream');
306
- mockSession.stream.mockReturnValue(failStream());
307
- mockSDK.unstable_v2_createSession.mockReturnValue(mockSession);
308
- const sessionId = await manager.createSession({ onMessage: mockOnMessage });
309
-
310
- emittedMessages = [];
311
- await manager.sendMessage(sessionId, 'test');
312
-
313
- const errorStatus = emittedMessages.find(
314
- m => m.type === 'chat:status' && m.payload.status === 'error'
315
- );
316
- expect(errorStatus).toBeDefined();
317
- expect(errorStatus.payload.message).toBe('stream broke');
318
-
319
- // Should return to idle after error
320
- const idleStatus = emittedMessages.find(
321
- m => m.type === 'chat:status' && m.payload.status === 'idle'
322
- );
323
- expect(idleStatus).toBeDefined();
324
- });
325
- });
326
- });