@chrisromp/copilot-bridge 0.8.0 → 0.8.2
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/README.md +1 -1
- package/config.sample.json +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/core/bridge.d.ts +5 -0
- package/dist/core/bridge.d.ts.map +1 -1
- package/dist/core/bridge.js +5 -0
- package/dist/core/bridge.js.map +1 -1
- package/dist/core/command-handler.d.ts +3 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +26 -33
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/inter-agent.d.ts.map +1 -1
- package/dist/core/inter-agent.js +7 -2
- package/dist/core/inter-agent.js.map +1 -1
- package/dist/core/session-manager.d.ts +9 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +82 -26
- package/dist/core/session-manager.js.map +1 -1
- package/dist/index.js +73 -21
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/check.ts +54 -1
- package/scripts/com.copilot-bridge.plist +5 -2
- package/scripts/install-service.ts +32 -1
- package/scripts/lib/service.ts +27 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/core/session-manager.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EACV,cAAc,EACf,MAAM,aAAa,CAAC;AAIrB,4GAA4G;AAC5G,eAAO,MAAM,mBAAmB,UAA8D,CAAC;AAE/F,KAAK,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;AAsNtF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAU/D;
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/core/session-manager.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAK3B,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EACV,cAAc,EACf,MAAM,aAAa,CAAC;AAIrB,4GAA4G;AAC5G,eAAO,MAAM,mBAAmB,UAA8D,CAAC;AAE/F,KAAK,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;AAsNtF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAU/D;AAyCD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,mBAAmB,CAAiC;IAC5D,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,UAAU,CAAsB;IAGxC,OAAO,CAAC,kBAAkB,CAA0C;IAEpE,OAAO,CAAC,gBAAgB,CAAyC;IAEjE,OAAO,CAAC,YAAY,CAAoE;IACxF,OAAO,CAAC,kBAAkB,CAA6B;IAEvD,OAAO,CAAC,iBAAiB,CAAkC;IAE3D,OAAO,CAAC,gBAAgB,CAAkC;IAE1D,OAAO,CAAC,eAAe,CAA6F;IACpH,OAAO,CAAC,oBAAoB,CAA+D;gBAE/E,MAAM,EAAE,aAAa;IAMjC,0EAA0E;IAC1E,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAIlD,sDAAsD;IACtD,UAAU,CAAC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI;IAIrG,sDAAsD;IACtD,YAAY,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,cAAc,GAAG,IAAI,GAAG,IAAI;IAI1E;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAkBzB,8FAA8F;IAC9F,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE;IA2BpD,8GAA8G;IAC9G,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE;IAmC3G,qEAAqE;IAC/D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAYpH,6CAA6C;IACvC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAwBtF,uEAAuE;IACjE,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBpD,+FAA+F;IACzF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuCvD,4CAA4C;IACtC,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA2ClF,6FAA6F;IACvF,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA0F/J;+DAC2D;IACrD,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAapF,gGAAgG;IAChG,OAAO,CAAC,uBAAuB;IAoB/B,gDAAgD;IAC1C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYlE,gDAAgD;IAC1C,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAczE,sFAAsF;IACtF,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,eAAe,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,SAAS,GAAG,KAAK,CAAA;KAAE;IAc1K,wEAAwE;IAClE,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IASxD,iCAAiC;IAC3B,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAIlC,iDAAiD;IAC3C,aAAa,IAAI,OAAO,CAAC;QAAE,eAAe,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAQpG,6DAA6D;IAC7D,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO;IA4CjF,6DAA6D;IAC7D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAyB5D,yDAAyD;IACzD,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAKhD,yDAAyD;IACzD,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAInD,sDAAsD;IAChD,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpD,yDAAyD;IACzD,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAK/C,wDAAwD;IACxD,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAOpG,qDAAqD;IACrD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAIxF;;;OAGG;IACG,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQhF,+DAA+D;IACzD,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAC;QAAC,YAAY,EAAE,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAe9J,sGAAsG;IACtG,OAAO,CAAC,uBAAuB;YAWjB,gBAAgB;YA2EhB,aAAa;IA8B3B;;;OAGG;IACG,oBAAoB,CAAC,IAAI,EAAE;QAC/B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,iBAAiB,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IA0GpG,gGAAgG;IAChG,OAAO,CAAC,kBAAkB;IAgD1B,yFAAyF;IACzF,OAAO,CAAC,+BAA+B;IAqEvC,+DAA+D;IAC/D,OAAO,CAAC,mBAAmB;IAY3B,kDAAkD;IAClD,OAAO,CAAC,kBAAkB;IAU1B;0GACsG;IACtG,OAAO,CAAC,oBAAoB;IAqF5B,qEAAqE;IACrE,OAAO,CAAC,gBAAgB;IAkXxB,gFAAgF;IAChF,OAAO,CAAC,oBAAoB;IAiH5B,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,uBAAuB;IAmH/B,OAAO,CAAC,sBAAsB;IAqCxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CA8BhC"}
|
|
@@ -230,15 +230,18 @@ export function extractCommandPatterns(input) {
|
|
|
230
230
|
/**
|
|
231
231
|
* Discover skill directories following Copilot CLI conventions:
|
|
232
232
|
* - ~/.copilot/skills/ (user-level)
|
|
233
|
+
* - ~/.agents/skills/ (user-level)
|
|
233
234
|
* - <workspace>/.github/skills/ (project-level)
|
|
234
|
-
* - <workspace>/.agents/skills/ (project-level
|
|
235
|
+
* - <workspace>/.agents/skills/ (project-level)
|
|
235
236
|
*/
|
|
236
237
|
function discoverSkillDirectories(workingDirectory) {
|
|
237
238
|
const home = process.env.HOME;
|
|
238
239
|
const roots = [];
|
|
239
240
|
// User-level skills
|
|
240
|
-
if (home)
|
|
241
|
+
if (home) {
|
|
241
242
|
roots.push(path.join(home, '.copilot', 'skills'));
|
|
243
|
+
roots.push(path.join(home, '.agents', 'skills'));
|
|
244
|
+
}
|
|
242
245
|
// Project-level skills (standard)
|
|
243
246
|
roots.push(path.join(workingDirectory, '.github', 'skills'));
|
|
244
247
|
// Project-level skills (legacy)
|
|
@@ -275,6 +278,10 @@ export class SessionManager {
|
|
|
275
278
|
// Cached context usage from session.usage_info events
|
|
276
279
|
contextUsage = new Map();
|
|
277
280
|
lastMessageUserIds = new Map(); // channelId → userId of last message sender
|
|
281
|
+
// MCP server names that were passed to the session at creation/resume time
|
|
282
|
+
sessionMcpServers = new Map(); // channelId → server names
|
|
283
|
+
// Skill directories that were passed to the session at creation/resume time
|
|
284
|
+
sessionSkillDirs = new Map(); // channelId → skill dir paths
|
|
278
285
|
// Handler for send_file tool (set by index.ts, calls adapter.sendFile)
|
|
279
286
|
sendFileHandler = null;
|
|
280
287
|
getAdapterForChannel = null;
|
|
@@ -320,20 +327,21 @@ export class SessionManager {
|
|
|
320
327
|
const workingDirectory = this.resolveWorkingDirectory(channelId);
|
|
321
328
|
const { servers: workspaceServers } = loadWorkspaceMcpServers(workingDirectory);
|
|
322
329
|
const globalNames = new Set(Object.keys(this.mcpServers));
|
|
330
|
+
const sessionServers = this.sessionMcpServers.get(channelId);
|
|
323
331
|
const result = [];
|
|
324
332
|
// All user-level servers — mark project overrides accordingly
|
|
325
333
|
for (const name of globalNames) {
|
|
326
334
|
if (name in workspaceServers) {
|
|
327
|
-
result.push({ name, source: 'workspace (override)' });
|
|
335
|
+
result.push({ name, source: 'workspace (override)', pending: sessionServers ? !sessionServers.has(name) : undefined });
|
|
328
336
|
}
|
|
329
337
|
else {
|
|
330
|
-
result.push({ name, source: 'user' });
|
|
338
|
+
result.push({ name, source: 'user', pending: sessionServers ? !sessionServers.has(name) : undefined });
|
|
331
339
|
}
|
|
332
340
|
}
|
|
333
341
|
// Project-only servers (not in user-level)
|
|
334
342
|
for (const name of Object.keys(workspaceServers)) {
|
|
335
343
|
if (!globalNames.has(name)) {
|
|
336
|
-
result.push({ name, source: 'workspace' });
|
|
344
|
+
result.push({ name, source: 'workspace', pending: sessionServers ? !sessionServers.has(name) : undefined });
|
|
337
345
|
}
|
|
338
346
|
}
|
|
339
347
|
return result.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -342,7 +350,9 @@ export class SessionManager {
|
|
|
342
350
|
getSkillInfo(channelId) {
|
|
343
351
|
const workingDirectory = this.resolveWorkingDirectory(channelId);
|
|
344
352
|
const dirs = discoverSkillDirectories(workingDirectory);
|
|
353
|
+
const sessionDirs = this.sessionSkillDirs.get(channelId);
|
|
345
354
|
const skills = [];
|
|
355
|
+
const home = process.env.HOME;
|
|
346
356
|
for (const dir of dirs) {
|
|
347
357
|
const name = path.basename(dir);
|
|
348
358
|
const skillFile = path.join(dir, 'SKILL.md');
|
|
@@ -352,6 +362,8 @@ export class SessionManager {
|
|
|
352
362
|
const normalized = dir.split(path.sep).join('/');
|
|
353
363
|
if (normalized.includes('.copilot/skills'))
|
|
354
364
|
source = 'user';
|
|
365
|
+
else if (home && normalized.startsWith(home.split(path.sep).join('/') + '/.agents/skills'))
|
|
366
|
+
source = 'user';
|
|
355
367
|
else if (normalized.includes('.github/skills'))
|
|
356
368
|
source = 'workspace';
|
|
357
369
|
else if (normalized.includes('.agents/skills'))
|
|
@@ -366,10 +378,24 @@ export class SessionManager {
|
|
|
366
378
|
}
|
|
367
379
|
catch { /* skip */ }
|
|
368
380
|
}
|
|
369
|
-
skills.push({ name, description, source });
|
|
381
|
+
skills.push({ name, description, source, pending: sessionDirs ? !sessionDirs.has(dir) : undefined });
|
|
370
382
|
}
|
|
371
383
|
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
372
384
|
}
|
|
385
|
+
/** List tools the SDK CLI process has available (via server RPC). */
|
|
386
|
+
async listSessionTools(channelId) {
|
|
387
|
+
// Ensure there's an active session so the CLI process is running
|
|
388
|
+
const sessionId = this.channelSessions.get(channelId);
|
|
389
|
+
if (!sessionId)
|
|
390
|
+
return [];
|
|
391
|
+
try {
|
|
392
|
+
return await this.bridge.listTools();
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
log.warn(`Failed to list tools for channel ${channelId}:`, err);
|
|
396
|
+
return [];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
373
399
|
/** Get or create a session for a channel. */
|
|
374
400
|
async ensureSession(channelId) {
|
|
375
401
|
// Check in-memory cache first
|
|
@@ -404,13 +430,15 @@ export class SessionManager {
|
|
|
404
430
|
this.sessionUnsubscribes.delete(existingId);
|
|
405
431
|
}
|
|
406
432
|
try {
|
|
407
|
-
this.bridge.destroySession(existingId);
|
|
433
|
+
await this.bridge.destroySession(existingId);
|
|
408
434
|
}
|
|
409
435
|
catch { /* best-effort */ }
|
|
410
436
|
this.channelSessions.delete(channelId);
|
|
411
437
|
this.sessionChannels.delete(existingId);
|
|
412
438
|
this.contextUsage.delete(channelId);
|
|
413
439
|
this.lastMessageUserIds.delete(channelId);
|
|
440
|
+
this.sessionMcpServers.delete(channelId);
|
|
441
|
+
this.sessionSkillDirs.delete(channelId);
|
|
414
442
|
}
|
|
415
443
|
clearChannelSession(channelId);
|
|
416
444
|
return this.createNewSession(channelId);
|
|
@@ -422,16 +450,24 @@ export class SessionManager {
|
|
|
422
450
|
// No session to reload — just create one
|
|
423
451
|
return this.createNewSession(channelId);
|
|
424
452
|
}
|
|
425
|
-
// Detach event listeners and
|
|
453
|
+
// Detach event listeners and disconnect so the CLI subprocess tears down
|
|
454
|
+
// in-memory state (including MCP connections), allowing a clean re-init.
|
|
426
455
|
const unsub = this.sessionUnsubscribes.get(existingId);
|
|
427
456
|
if (unsub) {
|
|
428
457
|
unsub();
|
|
429
458
|
this.sessionUnsubscribes.delete(existingId);
|
|
430
459
|
}
|
|
431
|
-
|
|
460
|
+
try {
|
|
461
|
+
await this.bridge.destroySession(existingId);
|
|
462
|
+
}
|
|
463
|
+
catch { /* best-effort */ }
|
|
464
|
+
// Re-read global MCP servers so /reload picks up user-level config changes
|
|
465
|
+
this.mcpServers = loadMcpServers();
|
|
432
466
|
// Re-attach the same session (re-reads workspace config, AGENTS.md, MCP, etc.)
|
|
433
467
|
this.contextUsage.delete(channelId);
|
|
434
468
|
this.lastMessageUserIds.delete(channelId);
|
|
469
|
+
this.sessionMcpServers.delete(channelId);
|
|
470
|
+
this.sessionSkillDirs.delete(channelId);
|
|
435
471
|
try {
|
|
436
472
|
await this.attachSession(channelId, existingId);
|
|
437
473
|
log.info(`Reloaded session ${existingId} for channel ${channelId}`);
|
|
@@ -444,6 +480,8 @@ export class SessionManager {
|
|
|
444
480
|
this.sessionChannels.delete(existingId);
|
|
445
481
|
this.contextUsage.delete(channelId);
|
|
446
482
|
this.lastMessageUserIds.delete(channelId);
|
|
483
|
+
this.sessionMcpServers.delete(channelId);
|
|
484
|
+
this.sessionSkillDirs.delete(channelId);
|
|
447
485
|
clearChannelSession(channelId);
|
|
448
486
|
return this.createNewSession(channelId);
|
|
449
487
|
}
|
|
@@ -462,13 +500,18 @@ export class SessionManager {
|
|
|
462
500
|
unsub();
|
|
463
501
|
this.sessionUnsubscribes.delete(existingId);
|
|
464
502
|
}
|
|
465
|
-
|
|
503
|
+
try {
|
|
504
|
+
await this.bridge.destroySession(existingId);
|
|
505
|
+
}
|
|
506
|
+
catch { /* best-effort */ }
|
|
466
507
|
this.channelSessions.delete(channelId);
|
|
467
508
|
this.sessionChannels.delete(existingId);
|
|
468
509
|
this.contextUsage.delete(channelId);
|
|
469
510
|
this.lastMessageUserIds.delete(channelId);
|
|
511
|
+
this.sessionMcpServers.delete(channelId);
|
|
512
|
+
this.sessionSkillDirs.delete(channelId);
|
|
470
513
|
}
|
|
471
|
-
// If target session is active on another channel,
|
|
514
|
+
// If target session is active on another channel, disconnect it first
|
|
472
515
|
const otherChannel = this.sessionChannels.get(targetSessionId);
|
|
473
516
|
if (otherChannel) {
|
|
474
517
|
const unsub = this.sessionUnsubscribes.get(targetSessionId);
|
|
@@ -476,10 +519,16 @@ export class SessionManager {
|
|
|
476
519
|
unsub();
|
|
477
520
|
this.sessionUnsubscribes.delete(targetSessionId);
|
|
478
521
|
}
|
|
479
|
-
|
|
522
|
+
try {
|
|
523
|
+
await this.bridge.destroySession(targetSessionId);
|
|
524
|
+
}
|
|
525
|
+
catch { /* best-effort */ }
|
|
480
526
|
this.channelSessions.delete(otherChannel);
|
|
481
527
|
this.sessionChannels.delete(targetSessionId);
|
|
482
528
|
this.contextUsage.delete(otherChannel);
|
|
529
|
+
this.lastMessageUserIds.delete(otherChannel);
|
|
530
|
+
this.sessionMcpServers.delete(otherChannel);
|
|
531
|
+
this.sessionSkillDirs.delete(otherChannel);
|
|
483
532
|
clearChannelSession(otherChannel);
|
|
484
533
|
}
|
|
485
534
|
// Attach to the target session — fail hard if it doesn't exist
|
|
@@ -553,12 +602,15 @@ export class SessionManager {
|
|
|
553
602
|
// Try to reconnect to the same session (CLI subprocess may have restarted)
|
|
554
603
|
try {
|
|
555
604
|
log.info(`Attempting to re-attach session ${sessionId}...`);
|
|
556
|
-
this.bridge.releaseSession(sessionId);
|
|
557
605
|
const unsub = this.sessionUnsubscribes.get(sessionId);
|
|
558
606
|
if (unsub) {
|
|
559
607
|
unsub();
|
|
560
608
|
this.sessionUnsubscribes.delete(sessionId);
|
|
561
609
|
}
|
|
610
|
+
try {
|
|
611
|
+
await this.bridge.destroySession(sessionId);
|
|
612
|
+
}
|
|
613
|
+
catch { /* best-effort */ }
|
|
562
614
|
await this.attachSession(channelId, sessionId);
|
|
563
615
|
const reconnected = this.bridge.getSession(sessionId);
|
|
564
616
|
if (reconnected) {
|
|
@@ -626,20 +678,18 @@ export class SessionManager {
|
|
|
626
678
|
}
|
|
627
679
|
/** Switch the agent for a channel's session. */
|
|
628
680
|
async switchAgent(channelId, agent) {
|
|
629
|
-
const sessionId = this.
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
await this.bridge.selectAgent(sessionId, agent);
|
|
634
|
-
}
|
|
635
|
-
else {
|
|
636
|
-
await this.bridge.deselectAgent(sessionId);
|
|
637
|
-
}
|
|
681
|
+
const { sessionId } = await this.ensureSession(channelId);
|
|
682
|
+
try {
|
|
683
|
+
if (agent) {
|
|
684
|
+
await this.bridge.selectAgent(sessionId, agent);
|
|
638
685
|
}
|
|
639
|
-
|
|
640
|
-
|
|
686
|
+
else {
|
|
687
|
+
await this.bridge.deselectAgent(sessionId);
|
|
641
688
|
}
|
|
642
689
|
}
|
|
690
|
+
catch (err) {
|
|
691
|
+
log.warn(`RPC agent switch failed:`, err);
|
|
692
|
+
}
|
|
643
693
|
setChannelPrefs(channelId, { agent });
|
|
644
694
|
}
|
|
645
695
|
/** Get effective preferences for a channel (config merged with runtime overrides). */
|
|
@@ -834,13 +884,14 @@ export class SessionManager {
|
|
|
834
884
|
catch {
|
|
835
885
|
log.warn('Failed to fetch model list for fallback resolution');
|
|
836
886
|
}
|
|
887
|
+
const resolvedMcpServers = this.resolveMcpServers(workingDirectory);
|
|
837
888
|
const createWithModel = async (model) => {
|
|
838
889
|
return withWorkspaceEnv(workingDirectory, () => this.bridge.createSession({
|
|
839
890
|
model,
|
|
840
891
|
workingDirectory,
|
|
841
892
|
configDir: defaultConfigDir,
|
|
842
893
|
reasoningEffort: reasoningEffort ?? undefined,
|
|
843
|
-
mcpServers:
|
|
894
|
+
mcpServers: resolvedMcpServers,
|
|
844
895
|
skillDirectories: skillDirectories.length > 0 ? skillDirectories : undefined,
|
|
845
896
|
onPermissionRequest: (request, invocation) => this.handlePermissionRequest(channelId, request, invocation),
|
|
846
897
|
onUserInputRequest: (request, invocation) => this.handleUserInputRequest(channelId, request, invocation),
|
|
@@ -848,6 +899,8 @@ export class SessionManager {
|
|
|
848
899
|
}));
|
|
849
900
|
};
|
|
850
901
|
const { result: session, usedModel, didFallback } = await tryWithFallback(prefs.model, availableModels, configFallbacks, createWithModel);
|
|
902
|
+
this.sessionMcpServers.set(channelId, new Set(Object.keys(resolvedMcpServers)));
|
|
903
|
+
this.sessionSkillDirs.set(channelId, new Set(skillDirectories));
|
|
851
904
|
if (didFallback) {
|
|
852
905
|
log.info(`Model fallback: "${prefs.model}" → "${usedModel}" for channel ${channelId}`);
|
|
853
906
|
setChannelPrefs(channelId, { model: usedModel });
|
|
@@ -874,16 +927,19 @@ export class SessionManager {
|
|
|
874
927
|
const reasoningEffort = prefs.reasoningEffort;
|
|
875
928
|
const skillDirectories = discoverSkillDirectories(workingDirectory);
|
|
876
929
|
const customTools = this.buildCustomTools(channelId);
|
|
930
|
+
const mcpServers = this.resolveMcpServers(workingDirectory);
|
|
877
931
|
const session = await withWorkspaceEnv(workingDirectory, () => this.bridge.resumeSession(sessionId, {
|
|
878
932
|
onPermissionRequest: (request, invocation) => this.handlePermissionRequest(channelId, request, invocation),
|
|
879
933
|
onUserInputRequest: (request, invocation) => this.handleUserInputRequest(channelId, request, invocation),
|
|
880
934
|
configDir: defaultConfigDir,
|
|
881
935
|
workingDirectory,
|
|
882
936
|
reasoningEffort: reasoningEffort ?? undefined,
|
|
883
|
-
mcpServers
|
|
937
|
+
mcpServers,
|
|
884
938
|
skillDirectories: skillDirectories.length > 0 ? skillDirectories : undefined,
|
|
885
939
|
tools: customTools.length > 0 ? customTools : undefined,
|
|
886
940
|
}));
|
|
941
|
+
this.sessionMcpServers.set(channelId, new Set(Object.keys(mcpServers)));
|
|
942
|
+
this.sessionSkillDirs.set(channelId, new Set(skillDirectories));
|
|
887
943
|
this.channelSessions.set(channelId, sessionId);
|
|
888
944
|
this.sessionChannels.set(sessionId, channelId);
|
|
889
945
|
this.attachSessionEvents(session, channelId);
|