@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.
- package/canvas-dist/assets/{_basePickBy-BOTBlJNd.js → _basePickBy-CWoeT3J7.js} +1 -1
- package/canvas-dist/assets/{_baseUniq-EF6Y2_Wm.js → _baseUniq-Dtuvtwtn.js} +1 -1
- package/canvas-dist/assets/{arc-C_vIirh2.js → arc-YYWnrNJU.js} +1 -1
- package/canvas-dist/assets/{architectureDiagram-VXUJARFQ-EvM6tQ7I.js → architectureDiagram-VXUJARFQ-CegbV-RR.js} +1 -1
- package/canvas-dist/assets/{blockDiagram-VD42YOAC-B_rbZyqc.js → blockDiagram-VD42YOAC-C2e_j6ry.js} +1 -1
- package/canvas-dist/assets/{c4Diagram-YG6GDRKO-J9PHecY3.js → c4Diagram-YG6GDRKO-rIpnAud9.js} +1 -1
- package/canvas-dist/assets/channel-BzJVlie3.js +1 -0
- package/canvas-dist/assets/{chunk-4BX2VUAB-DjcN96Mk.js → chunk-4BX2VUAB-CpZGetnU.js} +1 -1
- package/canvas-dist/assets/{chunk-55IACEB6-CTdcUQSV.js → chunk-55IACEB6-L0OhcFdd.js} +1 -1
- package/canvas-dist/assets/{chunk-B4BG7PRW-Dcov7eRi.js → chunk-B4BG7PRW-Cv9vsAzg.js} +1 -1
- package/canvas-dist/assets/{chunk-DI55MBZ5-DUJCBZzM.js → chunk-DI55MBZ5-B3p1mU43.js} +1 -1
- package/canvas-dist/assets/{chunk-FMBD7UC4-EfGA9ufe.js → chunk-FMBD7UC4-JCLAHw5x.js} +1 -1
- package/canvas-dist/assets/{chunk-QN33PNHL-Cu6V1xBU.js → chunk-QN33PNHL-C9arKEVq.js} +1 -1
- package/canvas-dist/assets/{chunk-QZHKN3VN-avF3sH_r.js → chunk-QZHKN3VN-Bs1r3d9U.js} +1 -1
- package/canvas-dist/assets/{chunk-TZMSLE5B-CkWW-qpk.js → chunk-TZMSLE5B-_Ye6r84Y.js} +1 -1
- package/canvas-dist/assets/classDiagram-2ON5EDUG-BTs-zEmB.js +1 -0
- package/canvas-dist/assets/classDiagram-v2-WZHVMYZB-BTs-zEmB.js +1 -0
- package/canvas-dist/assets/clone-CXEfuXmc.js +1 -0
- package/canvas-dist/assets/{cose-bilkent-S5V4N54A-DDE4zf7X.js → cose-bilkent-S5V4N54A-2O2oovOj.js} +1 -1
- package/canvas-dist/assets/{dagre-6UL2VRFP-BD6MGb7B.js → dagre-6UL2VRFP-gRmGLrEW.js} +1 -1
- package/canvas-dist/assets/{diagram-PSM6KHXK-yyu-ytzf.js → diagram-PSM6KHXK-B7Li-xxw.js} +1 -1
- package/canvas-dist/assets/{diagram-QEK2KX5R-B_H957Uf.js → diagram-QEK2KX5R-B_NNUAm3.js} +1 -1
- package/canvas-dist/assets/{diagram-S2PKOQOG-DuebuBVv.js → diagram-S2PKOQOG-NcK-KHaA.js} +1 -1
- package/canvas-dist/assets/{erDiagram-Q2GNP2WA-AxqPt6IZ.js → erDiagram-Q2GNP2WA-CG7dqzk3.js} +1 -1
- package/canvas-dist/assets/{flowDiagram-NV44I4VS-mDhW3D3Q.js → flowDiagram-NV44I4VS-CBzCj5D6.js} +1 -1
- package/canvas-dist/assets/{ganttDiagram-JELNMOA3-sA8pHJPp.js → ganttDiagram-JELNMOA3-CHw-4qJC.js} +1 -1
- package/canvas-dist/assets/{gitGraphDiagram-V2S2FVAM-CvLzvhKr.js → gitGraphDiagram-V2S2FVAM-Dqrc4wUs.js} +1 -1
- package/canvas-dist/assets/{graph-BVZqMrwW.js → graph-X9Kzu-pf.js} +1 -1
- package/canvas-dist/assets/{index-CF3qc2Xb.js → index-BQFKo-II.js} +1 -1
- package/canvas-dist/assets/index-DJ49c6u-.js +426 -0
- package/canvas-dist/assets/{infoDiagram-HS3SLOUP-D1Kg3Q9d.js → infoDiagram-HS3SLOUP-CflnZPsm.js} +1 -1
- package/canvas-dist/assets/{journeyDiagram-XKPGCS4Q-D7ogbx9z.js → journeyDiagram-XKPGCS4Q-D2gkCipQ.js} +1 -1
- package/canvas-dist/assets/{kanban-definition-3W4ZIXB7-CDcnICM9.js → kanban-definition-3W4ZIXB7-CtLLz4o8.js} +1 -1
- package/canvas-dist/assets/{layout-CuaK7i3M.js → layout-CjvV_Dms.js} +1 -1
- package/canvas-dist/assets/{linear-CLSTOJ0g.js → linear-D3cIYHoS.js} +1 -1
- package/canvas-dist/assets/{mindmap-definition-VGOIOE7T-TrK7CIKt.js → mindmap-definition-VGOIOE7T-DSgjVg-P.js} +1 -1
- package/canvas-dist/assets/{pieDiagram-ADFJNKIX-BcIKTRbi.js → pieDiagram-ADFJNKIX-B_lYaGFj.js} +1 -1
- package/canvas-dist/assets/{quadrantDiagram-AYHSOK5B-EOHXFGoQ.js → quadrantDiagram-AYHSOK5B-DLZLTJe3.js} +1 -1
- package/canvas-dist/assets/{requirementDiagram-UZGBJVZJ-CJ8lImGs.js → requirementDiagram-UZGBJVZJ-CZE26rhL.js} +1 -1
- package/canvas-dist/assets/{sankeyDiagram-TZEHDZUN-4cANY87E.js → sankeyDiagram-TZEHDZUN-DQMRJAPV.js} +1 -1
- package/canvas-dist/assets/{sequenceDiagram-WL72ISMW-D9HrEsci.js → sequenceDiagram-WL72ISMW-BY723FEn.js} +1 -1
- package/canvas-dist/assets/{stateDiagram-FKZM4ZOC-qVbMjauZ.js → stateDiagram-FKZM4ZOC-C_UdOFhy.js} +1 -1
- package/canvas-dist/assets/stateDiagram-v2-4FDKWEC3-DXIiFh0L.js +1 -0
- package/canvas-dist/assets/{timeline-definition-IT6M3QCI-DDBlkydm.js → timeline-definition-IT6M3QCI-DkrJqww0.js} +1 -1
- package/canvas-dist/assets/{treemap-GDKQZRPO-D4a8udjO.js → treemap-GDKQZRPO-B-6bMZqD.js} +1 -1
- package/canvas-dist/assets/{xychartDiagram-PRI3JC2R-DteXAAAu.js → xychartDiagram-PRI3JC2R-DkBhUy_D.js} +1 -1
- package/canvas-dist/index.html +1 -1
- package/package.json +3 -2
- package/src/server.e2e.test.js +10 -41
- package/src/server.js +302 -186
- package/canvas-dist/assets/channel-saCUO1KA.js +0 -1
- package/canvas-dist/assets/classDiagram-2ON5EDUG-CBLbQwHx.js +0 -1
- package/canvas-dist/assets/classDiagram-v2-WZHVMYZB-CBLbQwHx.js +0 -1
- package/canvas-dist/assets/clone-DXnda9BY.js +0 -1
- package/canvas-dist/assets/index-DYNtb52W.js +0 -426
- package/canvas-dist/assets/stateDiagram-v2-4FDKWEC3-MT16RLO4.js +0 -1
- package/src/claude-session.js +0 -414
- 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};
|
package/src/claude-session.js
DELETED
|
@@ -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
|
-
}
|