@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 +0,0 @@
1
- import{s as t,b as r,a,S as s}from"./chunk-DI55MBZ5-DUJCBZzM.js";import{_ as i}from"./index-DYNtb52W.js";import"./chunk-55IACEB6-CTdcUQSV.js";import"./chunk-QN33PNHL-Cu6V1xBU.js";var l={parser:a,get db(){return new s(2)},renderer:r,styles:t,init:i(e=>{e.state||(e.state={}),e.state.arrowMarkerAbsolute=e.arrowMarkerAbsolute},"init")};export{l as diagram};
@@ -1,414 +0,0 @@
1
- /**
2
- * Claude session manager — wraps the Agent SDK to manage
3
- * multiple Claude Code session lifecycles for browser chat.
4
- *
5
- * Uses the V2 preview interface (unstable_v2_createSession / unstable_v2_resumeSession)
6
- * for clean send()/stream() multi-turn conversation support.
7
- *
8
- * The SDK is an optional dependency — if not installed, chat features
9
- * are gracefully disabled.
10
- *
11
- * Supports multiple concurrent sessions (one per browser tab).
12
- */
13
-
14
- import {
15
- createEnvelope,
16
- MSG_CHAT_ASSISTANT,
17
- MSG_CHAT_STREAM,
18
- MSG_CHAT_TOOL_USE,
19
- MSG_CHAT_TOOL_RESULT,
20
- MSG_CHAT_STATUS,
21
- } from '@shaykec/shared';
22
-
23
- import { randomUUID } from 'crypto';
24
- import { execSync } from 'child_process';
25
- import { createRequire } from 'module';
26
- import { dirname, join } from 'path';
27
- import { fileURLToPath } from 'url';
28
-
29
- // Resolve the package tree root (parent of node_modules/@shaykec/bridge)
30
- const __filename = fileURLToPath(import.meta.url);
31
- const BRIDGE_ROOT = join(dirname(__filename), '..');
32
- // Walk up: bridge/ -> @shaykec/ -> node_modules/ -> prefix/
33
- const NPM_PREFIX = join(BRIDGE_ROOT, '..', '..', '..');
34
-
35
- // Lazy-load the Agent SDK — auto-installs on first use if missing
36
- let _sdk = null;
37
- let _sdkLoadError = null;
38
-
39
- function tryRequireSDK() {
40
- const require = createRequire(import.meta.url);
41
- return require('@anthropic-ai/claude-agent-sdk');
42
- }
43
-
44
- async function getSDK() {
45
- if (_sdk) return _sdk;
46
- if (_sdkLoadError) throw _sdkLoadError;
47
- try {
48
- _sdk = tryRequireSDK();
49
- return _sdk;
50
- } catch {
51
- // SDK not installed — install it into the same node_modules tree
52
- console.log('[chat] Agent SDK not found, installing @anthropic-ai/claude-agent-sdk...');
53
- try {
54
- execSync(
55
- `npm install @anthropic-ai/claude-agent-sdk --registry https://registry.npmjs.org/ --prefix "${NPM_PREFIX}" --no-save`,
56
- { stdio: 'inherit', timeout: 60000 }
57
- );
58
- _sdk = tryRequireSDK();
59
- console.log('[chat] Agent SDK installed successfully.');
60
- return _sdk;
61
- } catch (installErr) {
62
- _sdkLoadError = new Error(
63
- 'Failed to install Claude Agent SDK. Chat features require @anthropic-ai/claude-agent-sdk. ' +
64
- 'Install manually: npm install @anthropic-ai/claude-agent-sdk --registry https://registry.npmjs.org/'
65
- );
66
- throw _sdkLoadError;
67
- }
68
- }
69
- }
70
-
71
- /**
72
- * @typedef {object} ChatSessionOptions
73
- * @property {string} [cwd] - Working directory for Claude Code
74
- * @property {string} [pluginDir] - Path to the ClaudeTeach plugin
75
- * @property {function} onMessage - Callback receiving protocol envelopes to broadcast
76
- */
77
-
78
- /**
79
- * @typedef {object} SessionEntry
80
- * @property {import('@anthropic-ai/claude-agent-sdk').SDKSession} session
81
- * @property {boolean} streaming
82
- * @property {function} onMessage
83
- */
84
-
85
- /**
86
- * Manages multiple Claude Code SDK sessions.
87
- */
88
- export class ClaudeSessionManager {
89
- /**
90
- * @param {object} [sdkOverride] - Injected SDK for testing
91
- */
92
- constructor(sdkOverride) {
93
- /** @type {Map<string, SessionEntry>} */
94
- this._sessions = new Map();
95
- this._sdkOverride = sdkOverride || null;
96
- }
97
-
98
- /** @private */
99
- async _getSDK() {
100
- return this._sdkOverride || getSDK();
101
- }
102
-
103
- /**
104
- * Check if the Agent SDK is available.
105
- * @returns {Promise<boolean>}
106
- */
107
- async isAvailable() {
108
- try {
109
- await this._getSDK();
110
- return true;
111
- } catch {
112
- return false;
113
- }
114
- }
115
-
116
- /**
117
- * Get a session entry by ID.
118
- * @param {string} sessionId
119
- * @returns {SessionEntry|undefined}
120
- */
121
- getSession(sessionId) {
122
- return this._sessions.get(sessionId);
123
- }
124
-
125
- /**
126
- * Check if a session exists and is active.
127
- * @param {string} sessionId
128
- * @returns {boolean}
129
- */
130
- isActive(sessionId) {
131
- return this._sessions.has(sessionId);
132
- }
133
-
134
- /**
135
- * Check if a specific session is currently streaming.
136
- * @param {string} sessionId
137
- * @returns {boolean}
138
- */
139
- isStreaming(sessionId) {
140
- const entry = this._sessions.get(sessionId);
141
- return entry ? entry.streaming : false;
142
- }
143
-
144
- /**
145
- * Get all active session IDs.
146
- * @returns {string[]}
147
- */
148
- getActiveSessionIds() {
149
- return [...this._sessions.keys()];
150
- }
151
-
152
- /**
153
- * Create a new chat session.
154
- *
155
- * The SDK's sessionId isn't available until after the first message
156
- * exchange, so we generate our own UUID to return immediately and
157
- * capture the real SDK sessionId lazily during sendMessage().
158
- *
159
- * @param {ChatSessionOptions} options
160
- * @returns {Promise<string>} sessionId (our UUID handle)
161
- */
162
- async createSession(options = {}) {
163
- const sdk = await this._getSDK();
164
- const onMessage = options.onMessage;
165
-
166
- const sessionOpts = {
167
- model: options.model || 'claude-sonnet-4-6',
168
- allowedTools: [
169
- 'Bash(*)', 'Read', 'Write', 'Edit', 'Glob', 'Grep',
170
- ],
171
- permissionMode: 'bypassPermissions',
172
- };
173
-
174
- if (options.cwd) sessionOpts.cwd = options.cwd;
175
-
176
- if (options.pluginDir) {
177
- sessionOpts.plugins = [{ type: 'local', path: options.pluginDir }];
178
- }
179
-
180
- try {
181
- const session = sdk.unstable_v2_createSession(sessionOpts);
182
- // session.sessionId throws until after first send()+stream()
183
- // so we use our own UUID as the map key
184
- const sessionId = randomUUID();
185
-
186
- this._sessions.set(sessionId, {
187
- session,
188
- streaming: false,
189
- onMessage,
190
- sdkSessionId: null, // captured after first message
191
- });
192
-
193
- this._emit(sessionId, MSG_CHAT_STATUS, { status: 'started', sessionId });
194
-
195
- return sessionId;
196
- } catch (err) {
197
- if (onMessage) {
198
- const envelope = createEnvelope(MSG_CHAT_STATUS, { status: 'error', message: err.message }, 'bridge');
199
- onMessage(envelope);
200
- }
201
- throw err;
202
- }
203
- }
204
-
205
- /**
206
- * Resume a previous session by ID.
207
- * @param {string} sessionId
208
- * @param {ChatSessionOptions} options
209
- * @returns {Promise<string>} sessionId
210
- */
211
- async resumeSession(sessionId, options = {}) {
212
- const sdk = await this._getSDK();
213
-
214
- // Close existing entry for this ID if present
215
- await this.closeSession(sessionId);
216
-
217
- const onMessage = options.onMessage;
218
-
219
- const sessionOpts = {
220
- model: options.model || 'claude-sonnet-4-6',
221
- allowedTools: [
222
- 'Bash(*)', 'Read', 'Write', 'Edit', 'Glob', 'Grep',
223
- ],
224
- permissionMode: 'bypassPermissions',
225
- };
226
-
227
- if (options.cwd) sessionOpts.cwd = options.cwd;
228
-
229
- if (options.pluginDir) {
230
- sessionOpts.plugins = [{ type: 'local', path: options.pluginDir }];
231
- }
232
-
233
- try {
234
- // For resumed sessions, sessionId is available immediately
235
- const session = sdk.unstable_v2_resumeSession(sessionId, sessionOpts);
236
-
237
- this._sessions.set(sessionId, {
238
- session,
239
- streaming: false,
240
- onMessage,
241
- sdkSessionId: sessionId,
242
- });
243
-
244
- this._emit(sessionId, MSG_CHAT_STATUS, { status: 'resumed', sessionId });
245
-
246
- return sessionId;
247
- } catch (err) {
248
- if (onMessage) {
249
- const envelope = createEnvelope(MSG_CHAT_STATUS, { status: 'error', message: err.message }, 'bridge');
250
- onMessage(envelope);
251
- }
252
- throw err;
253
- }
254
- }
255
-
256
- /**
257
- * Send a user message and stream the response for a specific session.
258
- * Emits protocol envelopes via the session's onMessage callback.
259
- * @param {string} sessionId
260
- * @param {string} text - User message text
261
- */
262
- async sendMessage(sessionId, text) {
263
- const entry = this._sessions.get(sessionId);
264
- if (!entry) {
265
- throw new Error(`No active session with ID "${sessionId}". Call createSession() or resumeSession() first.`);
266
- }
267
-
268
- if (entry.streaming) {
269
- throw new Error('Already streaming a response. Wait for completion or call stop().');
270
- }
271
-
272
- entry.streaming = true;
273
- this._emit(sessionId, MSG_CHAT_STATUS, { status: 'thinking' });
274
-
275
- try {
276
- await entry.session.send(text);
277
-
278
- let currentText = '';
279
-
280
- for await (const msg of entry.session.stream()) {
281
- // Capture the real SDK sessionId once it becomes available
282
- if (!entry.sdkSessionId) {
283
- try {
284
- entry.sdkSessionId = entry.session.sessionId;
285
- } catch { /* not ready yet */ }
286
- }
287
-
288
- if (msg.type === 'assistant') {
289
- const textBlocks = (msg.message?.content || []).filter(b => b.type === 'text');
290
- for (const block of textBlocks) {
291
- if (block.text && block.text !== currentText) {
292
- const delta = block.text.slice(currentText.length);
293
- if (delta) {
294
- this._emit(sessionId, MSG_CHAT_STREAM, { delta, fullText: block.text });
295
- }
296
- currentText = block.text;
297
- }
298
- }
299
- }
300
-
301
- if (msg.type === 'tool_use') {
302
- this._emit(sessionId, MSG_CHAT_TOOL_USE, {
303
- toolName: msg.tool_name || msg.name,
304
- toolId: msg.id,
305
- input: msg.input,
306
- });
307
- }
308
-
309
- if (msg.type === 'tool_result') {
310
- this._emit(sessionId, MSG_CHAT_TOOL_RESULT, {
311
- toolId: msg.tool_use_id,
312
- output: msg.content,
313
- });
314
- }
315
-
316
- if (msg.type === 'result') {
317
- if (currentText) {
318
- this._emit(sessionId, MSG_CHAT_ASSISTANT, { text: currentText, sessionId });
319
- }
320
- break;
321
- }
322
- }
323
- } catch (err) {
324
- this._emit(sessionId, MSG_CHAT_STATUS, { status: 'error', message: err.message });
325
- } finally {
326
- entry.streaming = false;
327
- this._emit(sessionId, MSG_CHAT_STATUS, { status: 'idle' });
328
- }
329
- }
330
-
331
- /**
332
- * Get the real SDK session ID for a session (available after first message).
333
- * @param {string} sessionId - Our UUID handle
334
- * @returns {string|null}
335
- */
336
- getSdkSessionId(sessionId) {
337
- const entry = this._sessions.get(sessionId);
338
- return entry?.sdkSessionId || null;
339
- }
340
-
341
- /**
342
- * Stop the current generation for a specific session.
343
- * @param {string} sessionId
344
- */
345
- async stop(sessionId) {
346
- const entry = this._sessions.get(sessionId);
347
- if (entry && entry.streaming) {
348
- try {
349
- entry.session.close();
350
- } catch { /* best effort */ }
351
- this._sessions.delete(sessionId);
352
- this._emit(sessionId, MSG_CHAT_STATUS, { status: 'stopped' });
353
- }
354
- }
355
-
356
- /**
357
- * List available sessions for the current project.
358
- * @param {string} [dir] - Project directory to filter by
359
- * @returns {Promise<Array>}
360
- */
361
- async listSessions(dir) {
362
- try {
363
- const sdk = await this._getSDK();
364
- const sessions = await sdk.listSessions({ dir, limit: 20 });
365
- return sessions.map(s => ({
366
- sessionId: s.sessionId,
367
- summary: s.summary,
368
- lastModified: s.lastModified,
369
- }));
370
- } catch {
371
- return [];
372
- }
373
- }
374
-
375
- /**
376
- * Close a specific session.
377
- * @param {string} sessionId
378
- */
379
- async closeSession(sessionId) {
380
- const entry = this._sessions.get(sessionId);
381
- if (entry) {
382
- try {
383
- entry.session.close();
384
- } catch { /* already closed */ }
385
- this._sessions.delete(sessionId);
386
- }
387
- }
388
-
389
- /**
390
- * Close all sessions and clean up resources.
391
- */
392
- async closeAll() {
393
- for (const [id, entry] of this._sessions) {
394
- try {
395
- entry.session.close();
396
- } catch { /* already closed */ }
397
- }
398
- this._sessions.clear();
399
- }
400
-
401
- /**
402
- * Emit a protocol envelope via the session's onMessage callback.
403
- * @param {string} sessionId
404
- * @param {string} type
405
- * @param {object} payload
406
- */
407
- _emit(sessionId, type, payload) {
408
- const entry = this._sessions.get(sessionId);
409
- if (entry?.onMessage) {
410
- const envelope = createEnvelope(type, payload, 'bridge');
411
- entry.onMessage(envelope);
412
- }
413
- }
414
- }