@adhdev/daemon-core 0.6.79 → 0.7.1

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/dist/index.mjs CHANGED
@@ -2781,7 +2781,11 @@ var DaemonCdpManager = class {
2781
2781
  async attachToAgent(target) {
2782
2782
  if (!this.isConnected) return null;
2783
2783
  for (const [sid, t] of this.agentSessions) {
2784
- if (t.agentType === target.agentType) return sid;
2784
+ if (t.targetId === target.targetId) return sid;
2785
+ if (t.agentType === target.agentType && t.targetId !== target.targetId) {
2786
+ await this.detachAgent(sid).catch(() => {
2787
+ });
2788
+ }
2785
2789
  }
2786
2790
  try {
2787
2791
  const sendFn = this._browserConnected ? this.sendBrowser.bind(this) : this.sendInternal.bind(this);
@@ -2905,6 +2909,20 @@ var DaemonCdpManager = class {
2905
2909
  }
2906
2910
  async getCurrentPageWebviewUrls() {
2907
2911
  if (!this.isConnected) return /* @__PURE__ */ new Set();
2912
+ try {
2913
+ const urls = /* @__PURE__ */ new Set();
2914
+ const { frameTree } = await this.sendInternal("Page.getFrameTree", {}, 5e3);
2915
+ const visit = (node) => {
2916
+ const url = node?.frame?.url;
2917
+ if (typeof url === "string" && url.includes("vscode-webview")) {
2918
+ urls.add(url);
2919
+ }
2920
+ for (const child of node?.childFrames || []) visit(child);
2921
+ };
2922
+ if (frameTree) visit(frameTree);
2923
+ if (urls.size > 0) return urls;
2924
+ } catch {
2925
+ }
2908
2926
  try {
2909
2927
  const raw = await this.evaluate(
2910
2928
  `JSON.stringify(Array.from(document.querySelectorAll('iframe,webview'))
@@ -4056,7 +4074,7 @@ function registerExtensionProviders(providerLoader, manager, ideType) {
4056
4074
  manager.setExtensionProviders(enabledExtProviders);
4057
4075
  }
4058
4076
  async function setupIdeInstance(ctx, opts) {
4059
- const { providerLoader, instanceManager, instanceIdMap } = ctx;
4077
+ const { providerLoader, instanceManager, sessionRegistry } = ctx;
4060
4078
  const { ideType, manager, settings } = opts;
4061
4079
  const managerKey = opts.managerKey || ideType;
4062
4080
  registerExtensionProviders(providerLoader, manager, ideType);
@@ -4072,14 +4090,30 @@ async function setupIdeInstance(ctx, opts) {
4072
4090
  serverConn: ctx.serverConn,
4073
4091
  settings: resolvedSettings
4074
4092
  });
4075
- instanceIdMap.set(ideInstance.getInstanceId(), managerKey);
4093
+ sessionRegistry.register({
4094
+ sessionId: ideInstance.getInstanceId(),
4095
+ parentSessionId: null,
4096
+ providerType: ideType,
4097
+ providerCategory: "ide",
4098
+ transport: "cdp-page",
4099
+ cdpManagerKey: managerKey,
4100
+ instanceKey: `ide:${managerKey}`
4101
+ });
4076
4102
  const extensionProviders = providerLoader.getEnabledByCategory("extension", ideType);
4077
4103
  for (const extProvider of extensionProviders) {
4078
4104
  const extSettings = providerLoader.getSettings(extProvider.type);
4079
4105
  await ideInstance.addExtension(extProvider, extSettings);
4080
- for (const ext of ideInstance.getExtensionInstances()) {
4081
- instanceIdMap.set(ext.getInstanceId(), managerKey);
4082
- }
4106
+ }
4107
+ for (const ext of ideInstance.getExtensionInstances()) {
4108
+ sessionRegistry.register({
4109
+ sessionId: ext.getInstanceId(),
4110
+ parentSessionId: ideInstance.getInstanceId(),
4111
+ providerType: ext.type,
4112
+ providerCategory: "extension",
4113
+ transport: "cdp-webview",
4114
+ cdpManagerKey: managerKey,
4115
+ instanceKey: `ide:${managerKey}`
4116
+ });
4083
4117
  }
4084
4118
  return ideInstance;
4085
4119
  }
@@ -4296,6 +4330,7 @@ var DaemonCdpInitializer = class {
4296
4330
  async connectIdePort(port, ide) {
4297
4331
  const { providerLoader, cdpManagers } = this.config;
4298
4332
  const targets = await DaemonCdpManager.listAllTargets(port);
4333
+ await this.pruneStaleManagers(port, ide, targets);
4299
4334
  if (targets.length === 0) {
4300
4335
  if (cdpManagers.has(ide)) return;
4301
4336
  if (!await probeCdpPort(port)) return;
@@ -4348,6 +4383,34 @@ var DaemonCdpInitializer = class {
4348
4383
  }
4349
4384
  }
4350
4385
  }
4386
+ async pruneStaleManagers(port, ide, targets) {
4387
+ const trackedTargetIds = new Set(targets.map((target) => target.id));
4388
+ const removals = [];
4389
+ for (const [key, manager] of this.config.cdpManagers.entries()) {
4390
+ if (!(key === ide || key.startsWith(`${ide}_`))) continue;
4391
+ if (manager.getPort() !== port) continue;
4392
+ if (targets.length === 0) {
4393
+ removals.push({ key, manager, reason: "ide_closed" });
4394
+ continue;
4395
+ }
4396
+ if (manager.targetId && !trackedTargetIds.has(manager.targetId)) {
4397
+ removals.push({ key, manager, reason: "target_closed" });
4398
+ continue;
4399
+ }
4400
+ if (key === ide && !manager.targetId && targets.length > 1) {
4401
+ removals.push({ key, manager, reason: "target_rekeyed" });
4402
+ }
4403
+ }
4404
+ for (const { key, manager, reason } of removals) {
4405
+ try {
4406
+ manager.disconnect();
4407
+ } catch {
4408
+ }
4409
+ this.config.cdpManagers.delete(key);
4410
+ LOG.info("CDP", `Removed stale manager: ${key} (${reason})`);
4411
+ await this.config.onDisconnected?.(ide, manager, key, reason);
4412
+ }
4413
+ }
4351
4414
  // ─── Periodic scanning ───
4352
4415
  /**
4353
4416
  * Start periodic scanning for newly opened IDEs.
@@ -4451,95 +4514,152 @@ function isCdpConnected(cdpManagers, key) {
4451
4514
  const m = findCdpManager(cdpManagers, key);
4452
4515
  return m?.isConnected ?? false;
4453
4516
  }
4454
- function buildManagedIdes(ideStates, cdpManagers, opts) {
4455
- const result = [];
4456
- for (const state of ideStates) {
4457
- const cdpConnected = state.cdpConnected ?? isCdpConnected(cdpManagers, state.type);
4458
- result.push({
4459
- ideType: state.type,
4460
- ideVersion: "",
4461
- instanceId: state.instanceId || state.type,
4462
- workspace: state.workspace || null,
4463
- terminals: 0,
4464
- aiAgents: [],
4465
- activeChat: normalizeActiveChatData(state.activeChat),
4466
- chats: [],
4467
- agentStreams: state.extensions.map((ext) => ({
4468
- agentType: ext.type,
4469
- agentName: ext.name,
4470
- extensionId: ext.type,
4471
- status: normalizeManagedStatus(ext.status, { activeModal: ext.activeChat?.activeModal || null }),
4472
- messages: ext.activeChat?.messages || [],
4473
- inputContent: ext.activeChat?.inputContent || "",
4474
- activeModal: ext.activeChat?.activeModal || null
4475
- })),
4476
- cdpConnected,
4477
- currentModel: state.currentModel,
4478
- currentPlan: state.currentPlan,
4479
- currentAutoApprove: state.currentAutoApprove
4480
- });
4481
- }
4482
- if (opts?.detectedIdes) {
4483
- const coveredTypes = new Set(ideStates.map((s) => s.type));
4484
- for (const ide of opts.detectedIdes) {
4485
- if (!ide.installed || coveredTypes.has(ide.id)) continue;
4486
- if (!isCdpConnected(cdpManagers, ide.id)) continue;
4487
- result.push({
4488
- ideType: ide.id,
4489
- ideVersion: "",
4490
- instanceId: ide.id,
4491
- workspace: null,
4492
- terminals: 0,
4493
- aiAgents: [],
4494
- activeChat: null,
4495
- chats: [],
4496
- agentStreams: [],
4497
- cdpConnected: true,
4498
- currentModel: void 0,
4499
- currentPlan: void 0
4500
- });
4501
- }
4502
- }
4503
- return result;
4517
+ var IDE_SESSION_CAPABILITIES = [
4518
+ "read_chat",
4519
+ "send_message",
4520
+ "new_session",
4521
+ "list_sessions",
4522
+ "switch_session",
4523
+ "resolve_action",
4524
+ "change_model",
4525
+ "set_mode",
4526
+ "set_thought_level"
4527
+ ];
4528
+ var EXTENSION_SESSION_CAPABILITIES = [
4529
+ "read_chat",
4530
+ "send_message",
4531
+ "new_session",
4532
+ "list_sessions",
4533
+ "switch_session",
4534
+ "resolve_action",
4535
+ "change_model",
4536
+ "set_mode"
4537
+ ];
4538
+ var PTY_SESSION_CAPABILITIES = [
4539
+ "read_chat",
4540
+ "send_message",
4541
+ "resolve_action",
4542
+ "terminal_io",
4543
+ "resize_terminal"
4544
+ ];
4545
+ var ACP_SESSION_CAPABILITIES = [
4546
+ "read_chat",
4547
+ "send_message",
4548
+ "new_session",
4549
+ "resolve_action",
4550
+ "change_model",
4551
+ "set_mode",
4552
+ "set_thought_level"
4553
+ ];
4554
+ function buildIdeWorkspaceSession(state, cdpManagers) {
4555
+ const activeChat = normalizeActiveChatData(state.activeChat);
4556
+ const title = activeChat?.title || state.name;
4557
+ return {
4558
+ id: state.instanceId || state.type,
4559
+ parentId: null,
4560
+ providerType: state.type,
4561
+ providerName: state.name,
4562
+ kind: "workspace",
4563
+ transport: "cdp-page",
4564
+ status: normalizeManagedStatus(activeChat?.status || state.status, {
4565
+ activeModal: activeChat?.activeModal || null
4566
+ }),
4567
+ title,
4568
+ workspace: state.workspace || null,
4569
+ activeChat,
4570
+ capabilities: IDE_SESSION_CAPABILITIES,
4571
+ cdpConnected: state.cdpConnected ?? isCdpConnected(cdpManagers, state.type),
4572
+ currentModel: state.currentModel,
4573
+ currentPlan: state.currentPlan,
4574
+ currentAutoApprove: state.currentAutoApprove,
4575
+ errorMessage: state.errorMessage,
4576
+ errorReason: state.errorReason
4577
+ };
4504
4578
  }
4505
- function buildManagedClis(cliStates) {
4506
- return cliStates.map((s) => ({
4507
- id: s.instanceId,
4508
- instanceId: s.instanceId,
4509
- cliType: s.type,
4510
- cliName: s.name,
4511
- status: normalizeManagedStatus(s.status, { activeModal: s.activeChat?.activeModal || null }),
4512
- mode: "terminal",
4513
- workspace: s.workspace || "",
4514
- activeChat: normalizeActiveChatData(s.activeChat)
4515
- }));
4579
+ function buildExtensionAgentSession(parent, ext) {
4580
+ const activeChat = normalizeActiveChatData(ext.activeChat);
4581
+ return {
4582
+ id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
4583
+ parentId: parent.instanceId || parent.type,
4584
+ providerType: ext.type,
4585
+ providerName: ext.name,
4586
+ kind: "agent",
4587
+ transport: "cdp-webview",
4588
+ status: normalizeManagedStatus(activeChat?.status || ext.status, {
4589
+ activeModal: activeChat?.activeModal || null
4590
+ }),
4591
+ title: activeChat?.title || ext.name,
4592
+ workspace: parent.workspace || null,
4593
+ activeChat,
4594
+ capabilities: EXTENSION_SESSION_CAPABILITIES,
4595
+ currentModel: ext.currentModel,
4596
+ currentPlan: ext.currentPlan,
4597
+ errorMessage: ext.errorMessage,
4598
+ errorReason: ext.errorReason
4599
+ };
4516
4600
  }
4517
- function buildManagedAcps(acpStates) {
4518
- return acpStates.map((s) => ({
4519
- id: s.instanceId,
4520
- acpType: s.type,
4521
- acpName: s.name,
4522
- status: normalizeManagedStatus(s.status, { activeModal: s.activeChat?.activeModal || null }),
4523
- mode: "chat",
4524
- workspace: s.workspace || "",
4525
- activeChat: normalizeActiveChatData(s.activeChat),
4526
- currentModel: s.currentModel,
4527
- currentPlan: s.currentPlan,
4528
- acpConfigOptions: s.acpConfigOptions,
4529
- acpModes: s.acpModes,
4530
- errorMessage: s.errorMessage,
4531
- errorReason: s.errorReason
4532
- }));
4601
+ function buildCliSession(state) {
4602
+ const activeChat = normalizeActiveChatData(state.activeChat);
4603
+ return {
4604
+ id: state.instanceId,
4605
+ parentId: null,
4606
+ providerType: state.type,
4607
+ providerName: state.name,
4608
+ kind: "agent",
4609
+ transport: "pty",
4610
+ status: normalizeManagedStatus(activeChat?.status || state.status, {
4611
+ activeModal: activeChat?.activeModal || null
4612
+ }),
4613
+ title: activeChat?.title || state.name,
4614
+ workspace: state.workspace || null,
4615
+ activeChat,
4616
+ capabilities: PTY_SESSION_CAPABILITIES,
4617
+ errorMessage: state.errorMessage,
4618
+ errorReason: state.errorReason
4619
+ };
4533
4620
  }
4534
- function buildAllManagedEntries(allStates, cdpManagers, opts) {
4621
+ function buildAcpSession(state) {
4622
+ const activeChat = normalizeActiveChatData(state.activeChat);
4623
+ return {
4624
+ id: state.instanceId,
4625
+ parentId: null,
4626
+ providerType: state.type,
4627
+ providerName: state.name,
4628
+ kind: "agent",
4629
+ transport: "acp",
4630
+ status: normalizeManagedStatus(activeChat?.status || state.status, {
4631
+ activeModal: activeChat?.activeModal || null
4632
+ }),
4633
+ title: activeChat?.title || state.name,
4634
+ workspace: state.workspace || null,
4635
+ activeChat,
4636
+ capabilities: ACP_SESSION_CAPABILITIES,
4637
+ currentModel: state.currentModel,
4638
+ currentPlan: state.currentPlan,
4639
+ acpConfigOptions: state.acpConfigOptions,
4640
+ acpModes: state.acpModes,
4641
+ errorMessage: state.errorMessage,
4642
+ errorReason: state.errorReason
4643
+ };
4644
+ }
4645
+ function buildSessionEntries(allStates, cdpManagers) {
4646
+ const sessions = [];
4535
4647
  const ideStates = allStates.filter((s) => s.category === "ide");
4536
4648
  const cliStates = allStates.filter((s) => s.category === "cli");
4537
4649
  const acpStates = allStates.filter((s) => s.category === "acp");
4538
- return {
4539
- managedIdes: buildManagedIdes(ideStates, cdpManagers, opts),
4540
- managedClis: buildManagedClis(cliStates),
4541
- managedAcps: buildManagedAcps(acpStates)
4542
- };
4650
+ for (const state of ideStates) {
4651
+ sessions.push(buildIdeWorkspaceSession(state, cdpManagers));
4652
+ for (const ext of state.extensions) {
4653
+ sessions.push(buildExtensionAgentSession(state, ext));
4654
+ }
4655
+ }
4656
+ for (const state of cliStates) {
4657
+ sessions.push(buildCliSession(state));
4658
+ }
4659
+ for (const state of acpStates) {
4660
+ sessions.push(buildAcpSession(state));
4661
+ }
4662
+ return sessions;
4543
4663
  }
4544
4664
 
4545
4665
  // src/commands/handler.ts
@@ -4548,14 +4668,37 @@ init_logger();
4548
4668
 
4549
4669
  // src/commands/chat-commands.ts
4550
4670
  init_logger();
4671
+ var RECENT_SEND_WINDOW_MS = 1200;
4672
+ var recentSendByTarget = /* @__PURE__ */ new Map();
4673
+ function getCurrentProviderType(h, fallback = "") {
4674
+ return h.currentSession?.providerType || h.currentProviderType || fallback;
4675
+ }
4676
+ function getCurrentManagerKey(h) {
4677
+ return h.currentSession?.cdpManagerKey || h.currentManagerKey || "";
4678
+ }
4551
4679
  function getTargetedCliAdapter(h, args, providerType) {
4552
- return h.getCliAdapter(args?._targetInstance || h.currentIdeType || providerType);
4680
+ return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
4681
+ }
4682
+ function buildRecentSendKey(h, args, provider, text) {
4683
+ const target = args?.targetSessionId || args?.agentType || h.currentSession?.providerType || h.currentProviderType || h.currentManagerKey || "unknown";
4684
+ return `${provider?.category || "unknown"}:${target}:${text.trim()}`;
4685
+ }
4686
+ function isRecentDuplicateSend(key) {
4687
+ const now = Date.now();
4688
+ for (const [candidate, ts2] of recentSendByTarget.entries()) {
4689
+ if (now - ts2 > RECENT_SEND_WINDOW_MS) recentSendByTarget.delete(candidate);
4690
+ }
4691
+ const previous = recentSendByTarget.get(key);
4692
+ if (previous && now - previous <= RECENT_SEND_WINDOW_MS) return true;
4693
+ recentSendByTarget.set(key, now);
4694
+ return false;
4553
4695
  }
4554
4696
  async function handleChatHistory(h, args) {
4555
- const { agentType, offset, limit, instanceId } = args;
4697
+ const { agentType, offset, limit } = args;
4698
+ const instanceId = args?.targetSessionId;
4556
4699
  try {
4557
4700
  const provider = h.getProvider(agentType);
4558
- const agentStr = provider?.type || agentType || h.currentIdeType || "";
4701
+ const agentStr = provider?.type || agentType || getCurrentProviderType(h);
4559
4702
  const result = readChatHistory(agentStr, offset || 0, limit || 30, instanceId);
4560
4703
  return { success: true, ...result, agent: agentStr };
4561
4704
  } catch (e) {
@@ -4599,7 +4742,7 @@ async function handleReadChat(h, args) {
4599
4742
  provider.type || "unknown_extension",
4600
4743
  parsed.messages || [],
4601
4744
  parsed.title,
4602
- args?.instanceId
4745
+ args?.targetSessionId
4603
4746
  );
4604
4747
  return { success: true, ...parsed };
4605
4748
  }
@@ -4609,15 +4752,18 @@ async function handleReadChat(h, args) {
4609
4752
  }
4610
4753
  if (h.agentStream) {
4611
4754
  const cdp2 = h.getCdp();
4612
- if (cdp2 && h.currentIdeType) {
4613
- const streams = await h.agentStream.collectAgentStreams(cdp2, h.currentIdeType);
4614
- const stream = streams.find((s) => s.agentType === provider.type);
4755
+ const parentSessionId = h.currentSession?.parentSessionId;
4756
+ if (cdp2 && parentSessionId) {
4757
+ const stream = await h.agentStream.collectActiveSession(cdp2, parentSessionId);
4758
+ if (stream?.agentType !== provider.type) {
4759
+ return { success: true, messages: [], status: "idle" };
4760
+ }
4615
4761
  if (stream) {
4616
4762
  h.historyWriter.appendNewMessages(
4617
4763
  stream.agentType,
4618
4764
  stream.messages || [],
4619
4765
  void 0,
4620
- args?.instanceId
4766
+ args?.targetSessionId
4621
4767
  );
4622
4768
  return { success: true, messages: stream.messages || [], status: stream.status, agentType: stream.agentType };
4623
4769
  }
@@ -4644,10 +4790,10 @@ async function handleReadChat(h, args) {
4644
4790
  if (parsed && typeof parsed === "object") {
4645
4791
  _log(`Webview OK: ${parsed.messages?.length || 0} msgs`);
4646
4792
  h.historyWriter.appendNewMessages(
4647
- provider?.type || h.currentIdeType || "unknown_webview",
4793
+ provider?.type || getCurrentProviderType(h, "unknown_webview"),
4648
4794
  parsed.messages || [],
4649
4795
  parsed.title,
4650
- args?.instanceId
4796
+ args?.targetSessionId
4651
4797
  );
4652
4798
  return { success: true, ...parsed };
4653
4799
  }
@@ -4671,10 +4817,10 @@ async function handleReadChat(h, args) {
4671
4817
  if (parsed && typeof parsed === "object" && parsed.messages?.length > 0) {
4672
4818
  _log(`OK: ${parsed.messages?.length} msgs`);
4673
4819
  h.historyWriter.appendNewMessages(
4674
- provider?.type || h.currentIdeType || "unknown_ide",
4820
+ provider?.type || getCurrentProviderType(h, "unknown_ide"),
4675
4821
  parsed.messages || [],
4676
4822
  parsed.title,
4677
- args?.instanceId
4823
+ args?.targetSessionId
4678
4824
  );
4679
4825
  return { success: true, ...parsed };
4680
4826
  }
@@ -4689,16 +4835,21 @@ async function handleSendChat(h, args) {
4689
4835
  if (!text) return { success: false, error: "text required" };
4690
4836
  const _log = (msg) => LOG.debug("Command", `[send_chat] ${msg}`);
4691
4837
  const provider = h.getProvider(args?.agentType);
4838
+ const dedupeKey = buildRecentSendKey(h, args, provider, text);
4692
4839
  const _logSendSuccess = (method, targetAgent) => {
4693
4840
  h.historyWriter.appendNewMessages(
4694
- targetAgent || provider?.type || h.currentIdeType || "unknown_agent",
4841
+ targetAgent || provider?.type || getCurrentProviderType(h, "unknown_agent"),
4695
4842
  [{ role: "user", content: text, receivedAt: Date.now() }],
4696
4843
  void 0,
4697
4844
  // title
4698
- args?.instanceId
4845
+ args?.targetSessionId
4699
4846
  );
4700
4847
  return { success: true, sent: true, method, targetAgent };
4701
4848
  };
4849
+ if (isRecentDuplicateSend(dedupeKey)) {
4850
+ _log(`Suppressed duplicate send for ${dedupeKey}`);
4851
+ return { success: true, sent: false, deduplicated: true };
4852
+ }
4702
4853
  if (provider?.category === "cli" || provider?.category === "acp") {
4703
4854
  const adapter = getTargetedCliAdapter(h, args, provider.type);
4704
4855
  if (adapter) {
@@ -4734,8 +4885,9 @@ async function handleSendChat(h, args) {
4734
4885
  } catch (e) {
4735
4886
  _log(`Extension script error: ${e.message}`);
4736
4887
  }
4737
- if (h.agentStream && h.getCdp() && h.currentIdeType) {
4738
- const ok = await h.agentStream.sendToAgent(h.getCdp(), h.currentIdeType, provider.type, text, h.currentIdeType);
4888
+ const extensionSessionId = h.currentSession?.sessionId;
4889
+ if (h.agentStream && h.getCdp() && extensionSessionId) {
4890
+ const ok = await h.agentStream.sendToSession(h.getCdp(), extensionSessionId, text);
4739
4891
  if (ok) {
4740
4892
  _log(`AgentStreamManager sent OK`);
4741
4893
  return _logSendSuccess("agent-stream");
@@ -4745,45 +4897,11 @@ async function handleSendChat(h, args) {
4745
4897
  }
4746
4898
  const targetCdp = h.getCdp();
4747
4899
  if (!targetCdp?.isConnected) {
4748
- _log(`No CDP for ${h.currentIdeType}`);
4749
- return { success: false, error: `CDP for ${h.currentIdeType || "unknown"} not connected` };
4750
- }
4751
- _log(`Targeting IDE: ${h.currentIdeType}`);
4752
- if (provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
4753
- try {
4754
- const webviewScript = provider.scripts.webviewSendMessage(text);
4755
- if (webviewScript && targetCdp.evaluateInWebviewFrame) {
4756
- const matchText = provider.webviewMatchText;
4757
- const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
4758
- const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
4759
- let wvParsed = wvResult;
4760
- if (typeof wvResult === "string") {
4761
- try {
4762
- wvParsed = JSON.parse(wvResult);
4763
- } catch {
4764
- }
4765
- }
4766
- if (wvParsed?.sent) {
4767
- _log(`webviewSendMessage (priority) OK`);
4768
- return _logSendSuccess("webview-script-priority");
4769
- }
4770
- _log(`webviewSendMessage (priority) did not confirm sent, falling through`);
4771
- }
4772
- } catch (e) {
4773
- _log(`webviewSendMessage (priority) failed: ${e.message}, falling through`);
4774
- }
4775
- }
4776
- if (provider?.inputMethod === "cdp-type-and-send" && provider.inputSelector) {
4777
- try {
4778
- const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
4779
- if (sent) {
4780
- _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
4781
- return _logSendSuccess("typeAndSend-provider");
4782
- }
4783
- } catch (e) {
4784
- _log(`typeAndSend(provider) failed: ${e.message}`);
4785
- }
4900
+ const managerKey = getCurrentManagerKey(h);
4901
+ _log(`No CDP for ${managerKey}`);
4902
+ return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
4786
4903
  }
4904
+ _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
4787
4905
  const sendScript = h.getProviderScript("sendMessage", { MESSAGE: text });
4788
4906
  if (sendScript) {
4789
4907
  try {
@@ -4810,7 +4928,30 @@ async function handleSendChat(h, args) {
4810
4928
  _log(`typeAndSend(script.selector) failed: ${e.message}`);
4811
4929
  }
4812
4930
  }
4813
- if (parsed?.needsTypeAndSend && provider?.scripts?.webviewSendMessage) {
4931
+ if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
4932
+ try {
4933
+ const { x, y } = parsed.clickCoords;
4934
+ const sent = await targetCdp.typeAndSendAt(x, y, text);
4935
+ if (sent) {
4936
+ _log(`typeAndSendAt(${x},${y}) success`);
4937
+ return _logSendSuccess("typeAndSendAt-script");
4938
+ }
4939
+ } catch (e) {
4940
+ _log(`typeAndSendAt failed: ${e.message}`);
4941
+ }
4942
+ }
4943
+ if (parsed?.needsTypeAndSend && provider?.inputMethod === "cdp-type-and-send" && provider.inputSelector) {
4944
+ try {
4945
+ const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
4946
+ if (sent) {
4947
+ _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
4948
+ return _logSendSuccess("typeAndSend-provider");
4949
+ }
4950
+ } catch (e) {
4951
+ _log(`typeAndSend(provider) failed: ${e.message}`);
4952
+ }
4953
+ }
4954
+ if (parsed?.needsTypeAndSend && provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
4814
4955
  try {
4815
4956
  const webviewScript = provider.scripts.webviewSendMessage(text);
4816
4957
  if (webviewScript && targetCdp.evaluateInWebviewFrame) {
@@ -4833,20 +4974,44 @@ async function handleSendChat(h, args) {
4833
4974
  _log(`webviewSendMessage failed: ${e.message}`);
4834
4975
  }
4835
4976
  }
4836
- if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
4837
- try {
4838
- const { x, y } = parsed.clickCoords;
4839
- const sent = await targetCdp.typeAndSendAt(x, y, text);
4840
- if (sent) {
4841
- _log(`typeAndSendAt(${x},${y}) success`);
4842
- return _logSendSuccess("typeAndSendAt-script");
4977
+ return { success: false, error: parsed?.error || "Provider sendMessage did not confirm send" };
4978
+ } catch (e) {
4979
+ _log(`sendMessage script failed: ${e.message}`);
4980
+ return { success: false, error: `Provider sendMessage failed: ${e.message}` };
4981
+ }
4982
+ }
4983
+ if (provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
4984
+ try {
4985
+ const webviewScript = provider.scripts.webviewSendMessage(text);
4986
+ if (webviewScript && targetCdp.evaluateInWebviewFrame) {
4987
+ const matchText = provider.webviewMatchText;
4988
+ const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
4989
+ const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
4990
+ let wvParsed = wvResult;
4991
+ if (typeof wvResult === "string") {
4992
+ try {
4993
+ wvParsed = JSON.parse(wvResult);
4994
+ } catch {
4843
4995
  }
4844
- } catch (e) {
4845
- _log(`typeAndSendAt failed: ${e.message}`);
4996
+ }
4997
+ if (wvParsed?.sent) {
4998
+ _log(`webviewSendMessage OK`);
4999
+ return _logSendSuccess("webview-script");
4846
5000
  }
4847
5001
  }
4848
5002
  } catch (e) {
4849
- _log(`sendMessage script failed: ${e.message}`);
5003
+ _log(`webviewSendMessage failed: ${e.message}`);
5004
+ }
5005
+ }
5006
+ if (provider?.inputMethod === "cdp-type-and-send" && provider.inputSelector) {
5007
+ try {
5008
+ const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
5009
+ if (sent) {
5010
+ _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
5011
+ return _logSendSuccess("typeAndSend-provider");
5012
+ }
5013
+ } catch (e) {
5014
+ _log(`typeAndSend(provider) failed: ${e.message}`);
4850
5015
  }
4851
5016
  }
4852
5017
  _log("All methods failed");
@@ -4854,9 +5019,9 @@ async function handleSendChat(h, args) {
4854
5019
  }
4855
5020
  async function handleListChats(h, args) {
4856
5021
  const provider = h.getProvider(args?.agentType);
4857
- if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentIdeType) {
5022
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
4858
5023
  try {
4859
- const chats = await h.agentStream.listAgentChats(h.getCdp(), h.currentIdeType, provider.type);
5024
+ const chats = await h.agentStream.listSessionChats(h.getCdp(), h.currentSession.sessionId);
4860
5025
  LOG.info("Command", `[list_chats] Extension: ${chats.length} chats`);
4861
5026
  return { success: true, chats };
4862
5027
  } catch (e) {
@@ -4915,8 +5080,8 @@ async function handleNewChat(h, args) {
4915
5080
  }
4916
5081
  return { success: false, error: "new_chat not supported by this CLI provider" };
4917
5082
  }
4918
- if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentIdeType) {
4919
- const ok = await h.agentStream.newAgentSession(h.getCdp(), h.currentIdeType, provider.type, h.currentIdeType);
5083
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
5084
+ const ok = await h.agentStream.newSession(h.getCdp(), h.currentSession.sessionId);
4920
5085
  return { success: ok };
4921
5086
  }
4922
5087
  try {
@@ -4940,15 +5105,15 @@ async function handleNewChat(h, args) {
4940
5105
  }
4941
5106
  async function handleSwitchChat(h, args) {
4942
5107
  const provider = h.getProvider(args?.agentType);
4943
- const ideType = h.currentIdeType;
5108
+ const managerKey = getCurrentManagerKey(h);
4944
5109
  const sessionId = args?.sessionId || args?.id || args?.chatId;
4945
5110
  if (!sessionId) return { success: false, error: "sessionId required" };
4946
- LOG.info("Command", `[switch_chat] sessionId=${sessionId}, ideType=${ideType}`);
4947
- if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentIdeType) {
4948
- const ok = await h.agentStream.switchAgentSession(h.getCdp(), h.currentIdeType, provider.type, sessionId);
5111
+ LOG.info("Command", `[switch_chat] sessionId=${sessionId}, manager=${managerKey}`);
5112
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
5113
+ const ok = await h.agentStream.switchConversation(h.getCdp(), h.currentSession.sessionId, sessionId);
4949
5114
  return { success: ok, result: ok ? "switched" : "failed" };
4950
5115
  }
4951
- const cdp = h.getCdp(ideType);
5116
+ const cdp = h.getCdp(managerKey);
4952
5117
  if (!cdp?.isConnected) return { success: false, error: "CDP not connected" };
4953
5118
  try {
4954
5119
  const webviewScript = h.getProviderScript("webviewSwitchSession", { SESSION_ID: JSON.stringify(sessionId) });
@@ -5090,7 +5255,7 @@ async function handleSetMode(h, args) {
5090
5255
  async function handleChangeModel(h, args) {
5091
5256
  const provider = h.getProvider(args?.agentType);
5092
5257
  const model = args?.model;
5093
- LOG.info("Command", `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} ideType=${h.currentIdeType} providerType=${h.currentProviderType}`);
5258
+ LOG.info("Command", `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} manager=${getCurrentManagerKey(h)} providerType=${getCurrentProviderType(h)}`);
5094
5259
  if (provider?.category === "acp") {
5095
5260
  const adapter = getTargetedCliAdapter(h, args, provider.type);
5096
5261
  LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
@@ -5211,14 +5376,8 @@ async function handleResolveAction(h, args) {
5211
5376
  LOG.info("Command", `[resolveAction] CLI PTY \u2192 buttonIndex=${buttonIndex} "${buttons[buttonIndex] ?? "?"}"`);
5212
5377
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
5213
5378
  }
5214
- if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentIdeType) {
5215
- const ok = await h.agentStream.resolveAgentAction(
5216
- h.getCdp(),
5217
- h.currentIdeType,
5218
- provider.type,
5219
- action,
5220
- h.currentIdeType
5221
- );
5379
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
5380
+ const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
5222
5381
  return { success: ok };
5223
5382
  }
5224
5383
  if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
@@ -5578,157 +5737,37 @@ async function handleFileListBrowse(h, args) {
5578
5737
  // src/commands/stream-commands.ts
5579
5738
  init_config();
5580
5739
  init_logger();
5581
- async function handleAgentStreamSwitch(h, args) {
5582
- if (!h.agentStream || !h.getCdp() || !h.currentIdeType) return { success: false, error: "AgentStream or CDP not available" };
5583
- const agentType = args?.agentType || args?.agent || null;
5584
- await h.agentStream.switchActiveAgent(h.getCdp(), h.currentIdeType, agentType);
5585
- return { success: true, activeAgent: agentType };
5586
- }
5587
- async function handleAgentStreamRead(h, args) {
5588
- if (!h.agentStream || !h.getCdp() || !h.currentIdeType) return { success: false, error: "AgentStream or CDP not available" };
5589
- const streams = await h.agentStream.collectAgentStreams(h.getCdp(), h.currentIdeType);
5590
- return { success: true, streams };
5591
- }
5592
- async function handleAgentStreamSend(h, args) {
5593
- const agentType = args?.agentType || args?.agent;
5594
- const text = args?.text || args?.message;
5595
- if (!text) return { success: false, error: "text required" };
5596
- if (agentType && h.ctx.adapters) {
5597
- for (const [key, adapter] of h.ctx.adapters.entries()) {
5598
- if (adapter.cliType === agentType || key.includes(agentType)) {
5599
- LOG.info("Command", `[agent_stream_send] Routing to CLI adapter: ${adapter.cliType}`);
5600
- try {
5601
- await adapter.sendMessage(text);
5602
- return { success: true, sent: true, targetAgent: adapter.cliType };
5603
- } catch (e) {
5604
- LOG.info("Command", `[agent_stream_send] CLI adapter failed: ${e.message}`);
5605
- return { success: false, error: `CLI send failed: ${e.message}` };
5606
- }
5607
- }
5608
- }
5609
- }
5610
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5611
- const resolvedAgent = agentType || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5612
- if (!resolvedAgent) return { success: false, error: "agentType required" };
5613
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5614
- const ok = await h.agentStream.sendToAgent(h.getCdp(), h.currentIdeType, resolvedAgent, text, h.currentIdeType);
5615
- return { success: ok };
5616
- }
5617
- async function handleAgentStreamResolve(h, args) {
5618
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5619
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5620
- const action = args?.action || "approve";
5621
- if (!agentType) return { success: false, error: "agentType required" };
5622
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5623
- const ok = await h.agentStream.resolveAgentAction(h.getCdp(), h.currentIdeType, agentType, action, h.currentIdeType);
5624
- return { success: ok };
5625
- }
5626
- async function handleAgentStreamNew(h, args) {
5627
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5628
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5629
- if (!agentType) return { success: false, error: "agentType required" };
5630
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5631
- const ok = await h.agentStream.newAgentSession(h.getCdp(), h.currentIdeType, agentType, h.currentIdeType);
5632
- return { success: ok };
5633
- }
5634
- async function handleAgentStreamListChats(h, args) {
5635
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5636
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5637
- if (!agentType) return { success: false, error: "agentType required" };
5638
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5639
- const chats = await h.agentStream.listAgentChats(h.getCdp(), h.currentIdeType, agentType);
5640
- return { success: true, chats };
5641
- }
5642
- async function handleAgentStreamSwitchSession(h, args) {
5643
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5644
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5645
- const sessionId = args?.sessionId || args?.id;
5646
- if (!agentType || !sessionId) return { success: false, error: "agentType and sessionId required" };
5647
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5648
- const ok = await h.agentStream.switchAgentSession(h.getCdp(), h.currentIdeType, agentType, sessionId);
5649
- return { success: ok };
5650
- }
5651
- async function handleAgentStreamFocus(h, args) {
5740
+ async function handleFocusSession(h, args) {
5652
5741
  if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5653
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5654
- if (!agentType) return { success: false, error: "agentType required" };
5655
- await h.agentStream.ensureAgentPanelOpen(agentType, h.currentIdeType);
5656
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5657
- const ok = await h.agentStream.focusAgentEditor(h.getCdp(), h.currentIdeType, agentType);
5742
+ const sessionId = args?.targetSessionId || h.currentSession?.sessionId;
5743
+ if (!sessionId) return { success: false, error: "targetSessionId required" };
5744
+ const ok = await h.agentStream.focusSession(h.getCdp(), sessionId);
5658
5745
  return { success: ok };
5659
5746
  }
5660
5747
  function handlePtyInput(h, args) {
5661
- const { cliType, data } = args || {};
5748
+ const { cliType, data, targetSessionId } = args || {};
5662
5749
  if (!data) return { success: false, error: "data required" };
5663
- if (h.ctx.adapters) {
5664
- const targetCli = cliType || "";
5665
- if (!targetCli && h.ctx.adapters.size > 0) {
5666
- const first = h.ctx.adapters.values().next().value;
5667
- if (first && typeof first.writeRaw === "function") {
5668
- first.writeRaw(data);
5669
- return { success: true };
5670
- }
5671
- }
5672
- const directAdapter = h.ctx.adapters.get(targetCli);
5673
- if (directAdapter && typeof directAdapter.writeRaw === "function") {
5674
- directAdapter.writeRaw(data);
5675
- return { success: true };
5676
- }
5677
- for (const [, adapter] of h.ctx.adapters) {
5678
- if (adapter.cliType === targetCli && typeof adapter.writeRaw === "function") {
5679
- adapter.writeRaw(data);
5680
- return { success: true };
5681
- }
5682
- }
5683
- for (const [key, adapter] of h.ctx.adapters) {
5684
- if ((key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.writeRaw === "function") {
5685
- adapter.writeRaw(data);
5686
- return { success: true };
5687
- }
5688
- }
5750
+ const adapter = h.getCliAdapter(targetSessionId || cliType);
5751
+ if (!adapter || typeof adapter.writeRaw !== "function") {
5752
+ return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || "unknown"}` };
5689
5753
  }
5690
- return { success: false, error: `CLI adapter not found: ${cliType}` };
5754
+ adapter.writeRaw(data);
5755
+ return { success: true };
5691
5756
  }
5692
5757
  function handlePtyResize(h, args) {
5693
- const { cliType, cols, rows, force } = args || {};
5758
+ const { cliType, cols, rows, force, targetSessionId } = args || {};
5694
5759
  if (!cols || !rows) return { success: false, error: "cols and rows required" };
5695
- if (h.ctx.adapters) {
5696
- const targetCli = cliType || "";
5697
- if (!targetCli && h.ctx.adapters.size > 0) {
5698
- const first = h.ctx.adapters.values().next().value;
5699
- if (first && typeof first.resize === "function") {
5700
- if (force) {
5701
- first.resize(cols - 1, rows);
5702
- setTimeout(() => first.resize(cols, rows), 50);
5703
- } else {
5704
- first.resize(cols, rows);
5705
- }
5706
- return { success: true };
5707
- }
5708
- }
5709
- const directAdapter = h.ctx.adapters.get(targetCli);
5710
- if (directAdapter && typeof directAdapter.resize === "function") {
5711
- if (force) {
5712
- directAdapter.resize(cols - 1, rows);
5713
- setTimeout(() => directAdapter.resize(cols, rows), 50);
5714
- } else {
5715
- directAdapter.resize(cols, rows);
5716
- }
5717
- return { success: true };
5718
- }
5719
- for (const [key, adapter] of h.ctx.adapters) {
5720
- if ((adapter.cliType === targetCli || key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.resize === "function") {
5721
- if (force) {
5722
- adapter.resize(cols - 1, rows);
5723
- setTimeout(() => adapter.resize(cols, rows), 50);
5724
- } else {
5725
- adapter.resize(cols, rows);
5726
- }
5727
- return { success: true };
5728
- }
5729
- }
5760
+ const adapter = h.getCliAdapter(targetSessionId || cliType);
5761
+ if (!adapter || typeof adapter.resize !== "function") {
5762
+ return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || "unknown"}` };
5763
+ }
5764
+ if (force) {
5765
+ adapter.resize(cols - 1, rows);
5766
+ setTimeout(() => adapter.resize(cols, rows), 50);
5767
+ } else {
5768
+ adapter.resize(cols, rows);
5730
5769
  }
5731
- return { success: false, error: `CLI adapter not found: ${cliType}` };
5770
+ return { success: true };
5732
5771
  }
5733
5772
  function handleGetProviderSettings(h, args) {
5734
5773
  const loader = h.ctx.providerLoader;
@@ -5764,7 +5803,7 @@ function handleSetProviderSetting(h, args) {
5764
5803
  }
5765
5804
  async function handleExtensionScript(h, args, scriptName) {
5766
5805
  const { agentType, ideType } = args || {};
5767
- LOG.info("Command", `[ExtScript] ${scriptName} agentType=${agentType} ideType=${ideType} _currentIdeType=${h.currentIdeType}`);
5806
+ LOG.info("Command", `[ExtScript] ${scriptName} agentType=${agentType} ideType=${ideType} session=${h.currentSession?.sessionId || ""}`);
5768
5807
  if (!agentType) return { success: false, error: "agentType is required" };
5769
5808
  const loader = h.ctx.providerLoader;
5770
5809
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -5785,21 +5824,22 @@ async function handleExtensionScript(h, args, scriptName) {
5785
5824
  }
5786
5825
  const scriptCode = scriptFn(normalizedArgs);
5787
5826
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
5788
- const cdpKey = provider.category === "ide" ? h.currentIdeType || agentType : h.currentIdeType || ideType;
5827
+ const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType : h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType;
5789
5828
  LOG.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
5790
5829
  const cdp = h.getCdp(cdpKey);
5791
5830
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
5792
5831
  try {
5793
5832
  let result;
5794
5833
  if (provider.category === "extension") {
5795
- const sessions = cdp.getAgentSessions();
5796
- let targetSessionId = null;
5797
- for (const [sessionId, target] of sessions) {
5798
- if (target.agentType === agentType) {
5799
- targetSessionId = sessionId;
5800
- break;
5801
- }
5802
- }
5834
+ const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
5835
+ if (!runtimeSessionId) return { success: false, error: `No target session found for ${agentType}` };
5836
+ const parentSessionId = h.currentSession?.parentSessionId;
5837
+ if (parentSessionId) {
5838
+ await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
5839
+ await h.agentStream?.syncActiveSession(cdp, parentSessionId);
5840
+ }
5841
+ const managed = runtimeSessionId ? h.agentStream?.getManagedSession(runtimeSessionId) : null;
5842
+ const targetSessionId = managed?.cdpSessionId || null;
5803
5843
  const IDE_LEVEL_SCRIPTS = ["listModes", "setMode", "listModels", "setModel"];
5804
5844
  if (IDE_LEVEL_SCRIPTS.includes(scriptName)) {
5805
5845
  if (targetSessionId) {
@@ -5870,7 +5910,7 @@ function handleGetIdeExtensions(h, args) {
5870
5910
  enabled: config.ideSettings?.[ide]?.extensions?.[p.type]?.enabled === true
5871
5911
  }));
5872
5912
  }
5873
- return { success: true, ides: result };
5913
+ return { success: true, ideExtensions: result };
5874
5914
  }
5875
5915
  function handleSetIdeExtension(h, args) {
5876
5916
  const { ideType, extensionType, enabled } = args || {};
@@ -5982,10 +6022,8 @@ var DaemonCommandHandler = class {
5982
6022
  _agentStream = null;
5983
6023
  domHandlers;
5984
6024
  _historyWriter;
5985
- /** Current IDE type extracted from command args (per-request) */
5986
- _currentIdeType;
5987
- /** Current provider type — agentType priority, ideType use */
5988
- _currentProviderType;
6025
+ /** Current request route context */
6026
+ _currentRoute = {};
5989
6027
  constructor(ctx) {
5990
6028
  this._ctx = ctx;
5991
6029
  this.domHandlers = new CdpDomHandlers((ideType) => this.getCdp(ideType));
@@ -6001,20 +6039,25 @@ var DaemonCommandHandler = class {
6001
6039
  get historyWriter() {
6002
6040
  return this._historyWriter;
6003
6041
  }
6042
+ get currentManagerKey() {
6043
+ return this._currentRoute.managerKey;
6044
+ }
6004
6045
  get currentIdeType() {
6005
- return this._currentIdeType;
6046
+ return this._currentRoute.managerKey;
6006
6047
  }
6007
6048
  get currentProviderType() {
6008
- return this._currentProviderType;
6049
+ return this._currentRoute.providerType;
6050
+ }
6051
+ get currentSession() {
6052
+ return this._currentRoute.session;
6009
6053
  }
6010
- /** Get CDP manager for a specific ideType or managerKey.
6011
- * Supports exact match, multi-window prefix match, and instanceIdMap UUID lookup.
6012
- * Returns null if no match — never falls back to another IDE. */
6054
+ /** Get CDP manager for a specific session or manager key. */
6013
6055
  getCdp(ideType) {
6014
- const key = ideType || this._currentIdeType;
6015
- if (!key) return null;
6016
- const resolved = this._ctx.instanceIdMap?.get(key) || key;
6017
- const m = findCdpManager(this._ctx.cdpManagers, resolved);
6056
+ const requested = ideType || this._currentRoute.session?.sessionId || this._currentRoute.managerKey;
6057
+ if (!requested) return null;
6058
+ const session = this._ctx.sessionRegistry?.get(requested);
6059
+ const managerKey = session?.cdpManagerKey || requested;
6060
+ const m = findCdpManager(this._ctx.cdpManagers, managerKey);
6018
6061
  if (m?.isConnected) return m;
6019
6062
  return null;
6020
6063
  }
@@ -6022,7 +6065,7 @@ var DaemonCommandHandler = class {
6022
6065
  * Get provider module — _currentProviderType (agentType priority) use.
6023
6066
  */
6024
6067
  getProvider(overrideType) {
6025
- const key = overrideType || this._currentProviderType || this._currentIdeType;
6068
+ const key = overrideType || this._currentRoute.providerType || this._currentRoute.session?.providerType || this._currentRoute.managerKey;
6026
6069
  if (!key || !this._ctx.providerLoader) return void 0;
6027
6070
  const result = this._ctx.providerLoader.resolve(key);
6028
6071
  if (result) return result;
@@ -6055,14 +6098,22 @@ var DaemonCommandHandler = class {
6055
6098
  const cdp = this.getCdp();
6056
6099
  if (!cdp?.isConnected) return null;
6057
6100
  if (provider?.category === "extension") {
6058
- let sessionId = this.getExtensionSessionId(provider, this._currentIdeType);
6059
- if (!sessionId && this._agentStream && this._currentIdeType) {
6060
- await this._agentStream.switchActiveAgent(cdp, this._currentIdeType, provider.type);
6061
- await this._agentStream.syncAgentSessions(cdp, this._currentIdeType);
6062
- sessionId = this.getExtensionSessionId(provider, this._currentIdeType);
6101
+ let sessionId = this._currentRoute.session?.sessionId || null;
6102
+ if (!sessionId && this._currentRoute.session?.parentSessionId) {
6103
+ sessionId = this._agentStream?.resolveSessionForAgent(this._currentRoute.session.parentSessionId, provider.type) || null;
6104
+ }
6105
+ if (sessionId && this._agentStream) {
6106
+ const target = this._ctx.sessionRegistry?.get(sessionId);
6107
+ if (target?.parentSessionId) {
6108
+ await this._agentStream.setActiveSession(cdp, target.parentSessionId, sessionId);
6109
+ await this._agentStream.syncActiveSession(cdp, target.parentSessionId);
6110
+ }
6063
6111
  }
6064
6112
  if (!sessionId) return null;
6065
- const result2 = await cdp.evaluateInSessionFrame(sessionId, script, timeout);
6113
+ const managed = this._agentStream?.getManagedSession(sessionId);
6114
+ const cdpSessionId = managed?.cdpSessionId;
6115
+ if (!cdpSessionId) return null;
6116
+ const result2 = await cdp.evaluateInSessionFrame(cdpSessionId, script, timeout);
6066
6117
  return { result: result2, category: "extension" };
6067
6118
  }
6068
6119
  const result = await cdp.evaluate(script, timeout);
@@ -6070,57 +6121,37 @@ var DaemonCommandHandler = class {
6070
6121
  }
6071
6122
  /** CLI adapter search */
6072
6123
  getCliAdapter(type) {
6073
- const target = type || this._currentIdeType;
6124
+ const target = type || this._currentRoute.session?.sessionId || this._currentRoute.providerType || this._currentRoute.managerKey;
6074
6125
  if (!target || !this._ctx.adapters) return null;
6075
- let normalizedTarget = target;
6076
- const colonIdx = normalizedTarget.lastIndexOf(":");
6077
- if (colonIdx >= 0) normalizedTarget = normalizedTarget.substring(colonIdx + 1);
6078
- const direct = this._ctx.adapters.get(normalizedTarget);
6079
- if (direct) return direct;
6080
- for (const [key, adapter] of this._ctx.adapters.entries()) {
6081
- if (adapter.cliType === target || adapter.cliType === normalizedTarget || key === normalizedTarget || key.startsWith(target) || key.startsWith(normalizedTarget)) {
6082
- return adapter;
6083
- }
6126
+ const session = this._ctx.sessionRegistry?.get(target);
6127
+ if (session?.adapterKey) {
6128
+ return this._ctx.adapters.get(session.adapterKey) || null;
6084
6129
  }
6085
- return null;
6130
+ return this._ctx.adapters.get(target) || null;
6086
6131
  }
6087
6132
  // ─── Private helpers ──────────────────────────────
6088
- getExtensionSessionId(provider, scopeKey) {
6089
- if (provider.category !== "extension" || !this._agentStream || !scopeKey) return null;
6090
- const managed = this._agentStream.getManagedAgent(provider.type, scopeKey);
6091
- return managed?.sessionId || null;
6092
- }
6093
- resolveManagerKeyFromInstanceId(instanceId) {
6094
- const mapped = this._ctx.instanceIdMap?.get(instanceId);
6095
- if (mapped) return mapped;
6096
- const entries = this._ctx.instanceManager?.instances?.entries?.();
6097
- if (!entries) return void 0;
6098
- for (const [instanceKey, instance] of entries) {
6099
- if (typeof instanceKey !== "string" || !instanceKey.startsWith("ide:")) continue;
6100
- if (typeof instance?.getInstanceId === "function" && instance.getInstanceId() === instanceId) {
6101
- const managerKey = instanceKey.slice(4);
6102
- this._ctx.instanceIdMap?.set(instanceId, managerKey);
6103
- return managerKey;
6104
- }
6105
- if (typeof instance?.getExtensionInstances === "function") {
6106
- for (const ext of instance.getExtensionInstances() || []) {
6107
- if (typeof ext?.getInstanceId === "function" && ext.getInstanceId() === instanceId) {
6108
- const managerKey = instanceKey.slice(4);
6109
- this._ctx.instanceIdMap?.set(instanceId, managerKey);
6110
- return managerKey;
6111
- }
6112
- }
6113
- }
6114
- }
6115
- return void 0;
6116
- }
6117
- /** Extract ideType from _targetInstance or explicit ideType */
6133
+ inferProviderType(key) {
6134
+ if (!key) return void 0;
6135
+ const session = this._ctx.sessionRegistry?.get(key);
6136
+ if (session?.providerType) return session.providerType;
6137
+ return key.split("_")[0];
6138
+ }
6139
+ resolveRoute(args) {
6140
+ const session = this._ctx.sessionRegistry?.get(args?.targetSessionId);
6141
+ const managerKey = this.extractIdeType(args);
6142
+ const providerType = args?.agentType || args?.providerType || session?.providerType || this.inferProviderType(managerKey);
6143
+ return { session, managerKey, providerType };
6144
+ }
6145
+ /** Extract CDP scope key from target session or explicit ideType */
6118
6146
  extractIdeType(args) {
6147
+ if (args?.targetSessionId) {
6148
+ const target = this._ctx.sessionRegistry?.get(args.targetSessionId);
6149
+ if (target?.cdpManagerKey) return target.cdpManagerKey;
6150
+ if (this._ctx.cdpManagers.has(args.targetSessionId)) return args.targetSessionId;
6151
+ }
6119
6152
  if (args?.ideType) {
6120
- const mappedKey = this.resolveManagerKeyFromInstanceId(args.ideType);
6121
- if (mappedKey) {
6122
- return mappedKey;
6123
- }
6153
+ const target = this._ctx.sessionRegistry?.get(args.ideType);
6154
+ if (target?.cdpManagerKey) return target.cdpManagerKey;
6124
6155
  if (this._ctx.cdpManagers.has(args.ideType)) {
6125
6156
  return args.ideType;
6126
6157
  }
@@ -6131,34 +6162,6 @@ var DaemonCommandHandler = class {
6131
6162
  }
6132
6163
  }
6133
6164
  }
6134
- if (args?._targetInstance) {
6135
- let raw = args._targetInstance;
6136
- const ideMatch = raw.match(/:ide:(.+)$/);
6137
- const cliMatch = raw.match(/:cli:(.+)$/);
6138
- const acpMatch = raw.match(/:acp:(.+)$/);
6139
- if (ideMatch) raw = ideMatch[1];
6140
- else if (cliMatch) raw = cliMatch[1];
6141
- else if (acpMatch) raw = acpMatch[1];
6142
- const mappedKey = this.resolveManagerKeyFromInstanceId(raw);
6143
- if (mappedKey) {
6144
- return mappedKey;
6145
- }
6146
- if (this._ctx.cdpManagers.has(raw)) {
6147
- return raw;
6148
- }
6149
- const found = findCdpManager(this._ctx.cdpManagers, raw);
6150
- if (found) {
6151
- for (const [k, m] of this._ctx.cdpManagers.entries()) {
6152
- if (m === found) return k;
6153
- }
6154
- }
6155
- const lastUnderscore = raw.lastIndexOf("_");
6156
- if (lastUnderscore > 0) {
6157
- const stripped = raw.substring(0, lastUnderscore);
6158
- if (this._ctx.cdpManagers.has(stripped)) return stripped;
6159
- }
6160
- return raw;
6161
- }
6162
6165
  return void 0;
6163
6166
  }
6164
6167
  setAgentStreamManager(manager) {
@@ -6166,12 +6169,11 @@ var DaemonCommandHandler = class {
6166
6169
  }
6167
6170
  // ─── Command Dispatcher ──────────────────────────
6168
6171
  async handle(cmd, args) {
6169
- this._currentIdeType = this.extractIdeType(args);
6170
- this._currentProviderType = args?.agentType || args?.providerType || this._currentIdeType;
6171
- if (!this._currentIdeType && !this._currentProviderType) {
6172
+ this._currentRoute = this.resolveRoute(args);
6173
+ if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
6172
6174
  const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
6173
6175
  if (cdpCommands.includes(cmd)) {
6174
- return { success: false, error: "No ideType specified \u2014 cannot route command" };
6176
+ return { success: false, error: "No targetSessionId specified \u2014 cannot route command" };
6175
6177
  }
6176
6178
  }
6177
6179
  try {
@@ -6262,22 +6264,8 @@ var DaemonCommandHandler = class {
6262
6264
  case "refresh_scripts":
6263
6265
  return this.handleRefreshScripts(args);
6264
6266
  // ─── Stream commands (stream-commands.ts) ───────────
6265
- case "agent_stream_switch":
6266
- return handleAgentStreamSwitch(this, args);
6267
- case "agent_stream_read":
6268
- return handleAgentStreamRead(this, args);
6269
- case "agent_stream_send":
6270
- return handleAgentStreamSend(this, args);
6271
- case "agent_stream_resolve":
6272
- return handleAgentStreamResolve(this, args);
6273
- case "agent_stream_new":
6274
- return handleAgentStreamNew(this, args);
6275
- case "agent_stream_list_chats":
6276
- return handleAgentStreamListChats(this, args);
6277
- case "agent_stream_switch_session":
6278
- return handleAgentStreamSwitchSession(this, args);
6279
- case "agent_stream_focus":
6280
- return handleAgentStreamFocus(this, args);
6267
+ case "focus_session":
6268
+ return handleFocusSession(this, args);
6281
6269
  // ─── PTY Raw I/O (stream-commands.ts) ─────────
6282
6270
  case "pty_input":
6283
6271
  return handlePtyInput(this, args);
@@ -7925,8 +7913,7 @@ var CHAT_COMMANDS = [
7925
7913
  "new_chat",
7926
7914
  "switch_chat",
7927
7915
  "set_mode",
7928
- "change_model",
7929
- "agent_stream_send"
7916
+ "change_model"
7930
7917
  ];
7931
7918
  var DaemonCommandRouter = class {
7932
7919
  deps;
@@ -8164,6 +8151,7 @@ var DaemonCommandRouter = class {
8164
8151
  } catch {
8165
8152
  }
8166
8153
  this.deps.cdpManagers.delete(key);
8154
+ this.deps.sessionRegistry.unregisterByManagerKey(key);
8167
8155
  LOG.info("StopIDE", `CDP disconnected: ${key}`);
8168
8156
  }
8169
8157
  }
@@ -8176,14 +8164,6 @@ var DaemonCommandRouter = class {
8176
8164
  for (const instanceKey of keysToRemove) {
8177
8165
  const ideInstance = this.deps.instanceManager.getInstance(instanceKey);
8178
8166
  if (ideInstance) {
8179
- if (ideInstance.getInstanceId) {
8180
- this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
8181
- }
8182
- if (ideInstance.getExtensionInstances) {
8183
- for (const ext of ideInstance.getExtensionInstances()) {
8184
- if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
8185
- }
8186
- }
8187
8167
  this.deps.instanceManager.removeInstance(instanceKey);
8188
8168
  LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
8189
8169
  }
@@ -8192,14 +8172,6 @@ var DaemonCommandRouter = class {
8192
8172
  const instanceKey = `ide:${ideType}`;
8193
8173
  const ideInstance = this.deps.instanceManager.getInstance(instanceKey);
8194
8174
  if (ideInstance) {
8195
- if (ideInstance.getInstanceId) {
8196
- this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
8197
- }
8198
- if (ideInstance.getExtensionInstances) {
8199
- for (const ext of ideInstance.getExtensionInstances()) {
8200
- if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
8201
- }
8202
- }
8203
8175
  this.deps.instanceManager.removeInstance(instanceKey);
8204
8176
  LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
8205
8177
  }
@@ -8252,15 +8224,9 @@ function buildStatusSnapshot(options) {
8252
8224
  const cfg = loadConfig();
8253
8225
  const wsState = getWorkspaceState(cfg);
8254
8226
  const memSnap = getHostMemorySnapshot();
8255
- const { managedIdes, managedClis, managedAcps } = buildAllManagedEntries(
8227
+ const sessions = buildSessionEntries(
8256
8228
  options.allStates,
8257
- options.cdpManagers,
8258
- {
8259
- detectedIdes: options.detectedIdes.map((ide) => ({
8260
- id: ide.id,
8261
- installed: ide.installed !== false
8262
- }))
8263
- }
8229
+ options.cdpManagers
8264
8230
  );
8265
8231
  return {
8266
8232
  instanceId: options.instanceId,
@@ -8282,9 +8248,7 @@ function buildStatusSnapshot(options) {
8282
8248
  timestamp: options.timestamp ?? Date.now(),
8283
8249
  detectedIdes: buildDetectedIdeInfos(options.detectedIdes, options.cdpManagers),
8284
8250
  ...options.p2p ? { p2p: options.p2p } : {},
8285
- managedIdes,
8286
- managedClis,
8287
- managedAcps,
8251
+ sessions,
8288
8252
  workspaces: wsState.workspaces,
8289
8253
  defaultWorkspaceId: wsState.defaultWorkspaceId,
8290
8254
  defaultWorkspacePath: wsState.defaultWorkspacePath,
@@ -8396,7 +8360,7 @@ var DaemonStatusReporter = class {
8396
8360
  LOG.info("StatusReport", `\u2192${target} ${baseSummary}`);
8397
8361
  }
8398
8362
  }
8399
- const { managedIdes, managedClis, managedAcps } = buildAllManagedEntries(
8363
+ const sessions = buildSessionEntries(
8400
8364
  allStates,
8401
8365
  this.deps.cdpManagers
8402
8366
  );
@@ -8427,23 +8391,20 @@ var DaemonStatusReporter = class {
8427
8391
  if (opts?.p2pOnly) return;
8428
8392
  const wsPayload = {
8429
8393
  daemonMode: true,
8430
- // managedIdes: server only saves id, type, cdpConnected
8431
- managedIdes: managedIdes.map((ide) => ({
8432
- ideType: ide.ideType,
8433
- instanceId: ide.instanceId,
8434
- cdpConnected: ide.cdpConnected
8435
- })),
8436
- // managedClis: server only saves id, type, name
8437
- managedClis: managedClis.map((c) => ({
8438
- id: c.id,
8439
- cliType: c.cliType,
8440
- cliName: c.cliName
8441
- })),
8442
- // managedAcps: server only saves id, type, name
8443
- managedAcps: managedAcps?.map((a) => ({
8444
- id: a.id,
8445
- acpType: a.acpType,
8446
- acpName: a.acpName
8394
+ sessions: sessions.map((session) => ({
8395
+ id: session.id,
8396
+ parentId: session.parentId,
8397
+ providerType: session.providerType,
8398
+ providerName: session.providerName,
8399
+ kind: session.kind,
8400
+ transport: session.transport,
8401
+ status: session.status,
8402
+ workspace: session.workspace,
8403
+ title: session.title,
8404
+ cdpConnected: session.cdpConnected,
8405
+ currentModel: session.currentModel,
8406
+ currentPlan: session.currentPlan,
8407
+ currentAutoApprove: session.currentAutoApprove
8447
8408
  })),
8448
8409
  p2p: payload.p2p,
8449
8410
  timestamp: now
@@ -8879,6 +8840,9 @@ var AcpProviderInstance = class {
8879
8840
  );
8880
8841
  }
8881
8842
  }
8843
+ getInstanceId() {
8844
+ return this.instanceId;
8845
+ }
8882
8846
  // ─── ACP Config Options & Modes ─────────────────────
8883
8847
  parseConfigOptions(raw) {
8884
8848
  if (!Array.isArray(raw)) return;
@@ -9658,6 +9622,7 @@ var DaemonCliManager = class {
9658
9622
  const normalizedType = this.providerLoader.resolveAlias(cliType);
9659
9623
  const provider = this.providerLoader.getByAlias(cliType);
9660
9624
  const key = crypto4.randomUUID();
9625
+ const sessionRegistry = this.deps.getSessionRegistry?.() || null;
9661
9626
  if (provider && provider.category === "acp") {
9662
9627
  const instanceManager2 = this.deps.getInstanceManager();
9663
9628
  if (!instanceManager2) throw new Error("InstanceManager not available");
@@ -9681,6 +9646,16 @@ ${installInfo}`
9681
9646
  await instanceManager2.addInstance(key, acpInstance, {
9682
9647
  settings: this.providerLoader.getSettings(normalizedType)
9683
9648
  });
9649
+ const sessionId = acpInstance.getInstanceId();
9650
+ sessionRegistry?.register({
9651
+ sessionId,
9652
+ parentSessionId: null,
9653
+ providerType: normalizedType,
9654
+ providerCategory: "acp",
9655
+ transport: "acp",
9656
+ adapterKey: key,
9657
+ instanceKey: key
9658
+ });
9684
9659
  this.adapters.set(key, {
9685
9660
  cliType: normalizedType,
9686
9661
  workingDir: resolvedDir,
@@ -9738,9 +9713,18 @@ ${installInfo}`
9738
9713
  serverConn: this.deps.getServerConn(),
9739
9714
  settings: {},
9740
9715
  onPtyData: (data) => {
9741
- this.deps.getP2p()?.broadcastPtyOutput(key, data);
9716
+ this.deps.getP2p()?.broadcastPtyOutput(cliInstance.instanceId, data);
9742
9717
  }
9743
9718
  });
9719
+ sessionRegistry?.register({
9720
+ sessionId: cliInstance.instanceId,
9721
+ parentSessionId: null,
9722
+ providerType: normalizedType,
9723
+ providerCategory: "cli",
9724
+ transport: "pty",
9725
+ adapterKey: key,
9726
+ instanceKey: key
9727
+ });
9744
9728
  } catch (spawnErr) {
9745
9729
  LOG.error("CLI", `[${cliType}] Spawn failed: ${spawnErr?.message}`);
9746
9730
  instanceManager.removeInstance(key);
@@ -9762,6 +9746,7 @@ ${installInfo}`
9762
9746
  if (this.adapters.has(key)) {
9763
9747
  this.adapters.delete(key);
9764
9748
  this.deps.removeAgentTracking(key);
9749
+ sessionRegistry?.unregisterByInstanceKey(key);
9765
9750
  instanceManager.removeInstance(key);
9766
9751
  LOG.info("CLI", `\u{1F9F9} Auto-cleaned ${status.status} CLI: ${cliType}`);
9767
9752
  this.deps.onStatusChange();
@@ -9822,12 +9807,14 @@ ${installInfo}`
9822
9807
  }
9823
9808
  this.adapters.delete(key);
9824
9809
  this.deps.removeAgentTracking(key);
9810
+ this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
9825
9811
  this.deps.getInstanceManager()?.removeInstance(key);
9826
9812
  LOG.info("CLI", `\u{1F6D1} Agent stopped: ${adapter.cliType} in ${adapter.workingDir}`);
9827
9813
  this.deps.onStatusChange();
9828
9814
  } else {
9829
9815
  const im = this.deps.getInstanceManager();
9830
9816
  if (im) {
9817
+ this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
9831
9818
  im.removeInstance(key);
9832
9819
  this.deps.removeAgentTracking(key);
9833
9820
  LOG.warn("CLI", `\u{1F9F9} Force-removed orphan entry: ${key}`);
@@ -9842,7 +9829,7 @@ ${installInfo}`
9842
9829
  // ─── Adapter search ─────────────────────────────
9843
9830
  /**
9844
9831
  * Search for CLI adapter. Priority order:
9845
- * 0. instanceKey (UUID direct match) — extracted from _targetInstance / composite ID
9832
+ * 0. sessionId (UUID direct match)
9846
9833
  * 1. agentType + dir (iteration match)
9847
9834
  * 2. agentType fuzzy match (⚠ returns first match when multiple sessions exist)
9848
9835
  */
@@ -9910,7 +9897,7 @@ ${installInfo}`
9910
9897
  const cliType = args?.cliType;
9911
9898
  const dir = args?.dir || "";
9912
9899
  if (!cliType) throw new Error("cliType required");
9913
- const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
9900
+ const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
9914
9901
  if (found) {
9915
9902
  await this.stopSession(found.key);
9916
9903
  } else {
@@ -9942,7 +9929,7 @@ ${installInfo}`
9942
9929
  }
9943
9930
  const dir = rdir.path;
9944
9931
  if (!cliType) throw new Error("cliType required");
9945
- const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
9932
+ const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
9946
9933
  if (found) await this.stopSession(found.key);
9947
9934
  await this.startSession(cliType, dir);
9948
9935
  this.persistRecentDir(cliType, dir);
@@ -9954,7 +9941,7 @@ ${installInfo}`
9954
9941
  if (!agentType || !action) throw new Error("agentType and action required");
9955
9942
  const found = this.findAdapter(agentType, {
9956
9943
  dir: args?.dir,
9957
- instanceKey: args?._targetInstance
9944
+ instanceKey: args?.targetSessionId
9958
9945
  });
9959
9946
  if (!found) throw new Error(`CLI agent not running: ${agentType}`);
9960
9947
  const { adapter, key } = found;
@@ -10100,14 +10087,8 @@ var ProviderStreamAdapter = class {
10100
10087
  // src/agent-stream/manager.ts
10101
10088
  init_logger();
10102
10089
  var DaemonAgentStreamManager = class {
10103
- allAdapters = [];
10104
- managedByScope = /* @__PURE__ */ new Map();
10105
- enabled = true;
10106
- logFn;
10107
- lastDiscoveryTimeByScope = /* @__PURE__ */ new Map();
10108
- discoveryIntervalMsByScope = /* @__PURE__ */ new Map();
10109
- activeAgentTypeByScope = /* @__PURE__ */ new Map();
10110
- constructor(logFn, providerLoader) {
10090
+ constructor(logFn, providerLoader, sessionRegistry) {
10091
+ this.sessionRegistry = sessionRegistry;
10111
10092
  this.logFn = logFn || LOG.forComponent("AgentStream").asLogFn();
10112
10093
  if (providerLoader) {
10113
10094
  const allExtProviders = providerLoader.getByCategory("extension");
@@ -10115,257 +10096,278 @@ var DaemonAgentStreamManager = class {
10115
10096
  const resolved = providerLoader.resolve(p.type);
10116
10097
  if (!resolved) continue;
10117
10098
  const adapter = new ProviderStreamAdapter(resolved);
10118
- this.allAdapters.push(adapter);
10099
+ this.adaptersByType.set(p.type, adapter);
10119
10100
  this.logFn(`[AgentStream] Adapter created: ${p.type} (${p.name}) scripts=${Object.keys(resolved.scripts || {}).join(",") || "none"}`);
10120
10101
  }
10121
10102
  }
10122
10103
  }
10104
+ adaptersByType = /* @__PURE__ */ new Map();
10105
+ managedBySessionId = /* @__PURE__ */ new Map();
10106
+ enabled = true;
10107
+ logFn;
10108
+ lastDiscoveryTimeByParent = /* @__PURE__ */ new Map();
10109
+ discoveryIntervalMsByParent = /* @__PURE__ */ new Map();
10110
+ activeSessionIdByParent = /* @__PURE__ */ new Map();
10123
10111
  setEnabled(enabled) {
10124
10112
  this.enabled = enabled;
10125
10113
  }
10126
10114
  get isEnabled() {
10127
10115
  return this.enabled;
10128
10116
  }
10129
- getActiveAgentType(scopeKey) {
10130
- return this.activeAgentTypeByScope.get(scopeKey) || null;
10117
+ getActiveSessionId(parentSessionId) {
10118
+ return this.activeSessionIdByParent.get(parentSessionId) || null;
10131
10119
  }
10132
- getManagedScope(scopeKey) {
10133
- let managed = this.managedByScope.get(scopeKey);
10134
- if (!managed) {
10135
- managed = /* @__PURE__ */ new Map();
10136
- this.managedByScope.set(scopeKey, managed);
10137
- }
10138
- return managed;
10120
+ getSessionTarget(sessionId) {
10121
+ return this.sessionRegistry?.get(sessionId);
10139
10122
  }
10140
- resetScope(scopeKey) {
10141
- this.managedByScope.delete(scopeKey);
10142
- this.activeAgentTypeByScope.delete(scopeKey);
10143
- this.lastDiscoveryTimeByScope.delete(scopeKey);
10144
- this.discoveryIntervalMsByScope.delete(scopeKey);
10123
+ resetParentSession(parentSessionId) {
10124
+ const activeSessionId = this.activeSessionIdByParent.get(parentSessionId);
10125
+ if (activeSessionId) this.managedBySessionId.delete(activeSessionId);
10126
+ for (const child of this.sessionRegistry?.listChildren(parentSessionId) || []) {
10127
+ this.managedBySessionId.delete(child.sessionId);
10128
+ }
10129
+ this.activeSessionIdByParent.delete(parentSessionId);
10130
+ this.lastDiscoveryTimeByParent.delete(parentSessionId);
10131
+ this.discoveryIntervalMsByParent.delete(parentSessionId);
10145
10132
  }
10146
10133
  /** Panel focus based on provider.js focusPanel or extensionId (currently no-op) */
10147
- async ensureAgentPanelOpen(agentType, targetIdeType) {
10148
- }
10149
- async switchActiveAgent(cdp, scopeKey, agentType) {
10150
- const managed = this.getManagedScope(scopeKey);
10151
- const previousAgentType = this.getActiveAgentType(scopeKey);
10152
- if (previousAgentType === agentType) return;
10153
- if (previousAgentType) {
10154
- const prev = managed.get(previousAgentType);
10134
+ async ensureSessionPanelOpen(_sessionId) {
10135
+ }
10136
+ async setActiveSession(cdp, parentSessionId, sessionId) {
10137
+ const previousSessionId = this.getActiveSessionId(parentSessionId);
10138
+ if (previousSessionId === sessionId) return;
10139
+ if (previousSessionId) {
10140
+ const prev = this.managedBySessionId.get(previousSessionId);
10155
10141
  if (prev) {
10156
10142
  try {
10157
- await cdp.detachAgent(prev.sessionId);
10143
+ await cdp.detachAgent(prev.cdpSessionId);
10158
10144
  } catch {
10159
10145
  }
10160
- managed.delete(previousAgentType);
10161
- this.logFn(`[AgentStream] Deactivated: ${prev.adapter.agentName} (${scopeKey})`);
10162
- }
10163
- }
10164
- this.activeAgentTypeByScope.set(scopeKey, agentType);
10165
- this.lastDiscoveryTimeByScope.set(scopeKey, 0);
10166
- if (!agentType && managed.size === 0) {
10167
- this.managedByScope.delete(scopeKey);
10168
- }
10169
- this.logFn(`[AgentStream] Active agent (${scopeKey}): ${agentType || "none"}`);
10146
+ this.managedBySessionId.delete(previousSessionId);
10147
+ this.logFn(`[AgentStream] Deactivated: ${prev.adapter.agentName} (${parentSessionId})`);
10148
+ }
10149
+ }
10150
+ this.activeSessionIdByParent.set(parentSessionId, sessionId);
10151
+ this.lastDiscoveryTimeByParent.set(parentSessionId, 0);
10152
+ this.logFn(`[AgentStream] Active session (${parentSessionId}): ${sessionId || "none"}`);
10153
+ }
10154
+ resolveSessionIdForTarget(parentSessionId, agentType) {
10155
+ const child = (this.sessionRegistry?.listChildren(parentSessionId) || []).find((entry) => entry.providerCategory === "extension" && entry.providerType === agentType);
10156
+ return child?.sessionId || null;
10157
+ }
10158
+ async connectManagedSession(cdp, parentSessionId, runtimeSessionId) {
10159
+ const target = this.getSessionTarget(runtimeSessionId);
10160
+ if (!target || target.providerCategory !== "extension") return null;
10161
+ const adapter = this.adaptersByType.get(target.providerType);
10162
+ if (!adapter) return null;
10163
+ const targets = await cdp.discoverAgentWebviews();
10164
+ const activeTarget = targets.find((entry) => entry.agentType === target.providerType);
10165
+ if (!activeTarget) return null;
10166
+ const cdpSessionId = await cdp.attachToAgent(activeTarget);
10167
+ if (!cdpSessionId) return null;
10168
+ const managed = {
10169
+ adapter,
10170
+ runtimeSessionId,
10171
+ parentSessionId,
10172
+ cdpSessionId,
10173
+ target: activeTarget,
10174
+ lastState: null,
10175
+ lastError: null,
10176
+ lastHiddenCheckTime: 0
10177
+ };
10178
+ this.managedBySessionId.set(runtimeSessionId, managed);
10179
+ this.logFn(`[AgentStream] Connected: ${adapter.agentName} (${parentSessionId})`);
10180
+ return managed;
10170
10181
  }
10171
10182
  /** Agent webview discovery + session connection */
10172
- async syncAgentSessions(cdp, scopeKey) {
10173
- const activeAgentType = this.getActiveAgentType(scopeKey);
10174
- if (!this.enabled || !activeAgentType) return;
10183
+ async syncActiveSession(cdp, parentSessionId) {
10184
+ const activeSessionId = this.getActiveSessionId(parentSessionId);
10185
+ if (!this.enabled || !activeSessionId) return;
10175
10186
  const now = Date.now();
10176
- const managed = this.getManagedScope(scopeKey);
10177
- const lastDiscoveryTime = this.lastDiscoveryTimeByScope.get(scopeKey) || 0;
10178
- const discoveryIntervalMs = this.discoveryIntervalMsByScope.get(scopeKey) || 1e4;
10179
- if (managed.has(activeAgentType) && now - lastDiscoveryTime < discoveryIntervalMs) {
10187
+ const managed = this.managedBySessionId.get(activeSessionId);
10188
+ const lastDiscoveryTime = this.lastDiscoveryTimeByParent.get(parentSessionId) || 0;
10189
+ const discoveryIntervalMs = this.discoveryIntervalMsByParent.get(parentSessionId) || 1e4;
10190
+ if (managed && now - lastDiscoveryTime < discoveryIntervalMs) {
10180
10191
  return;
10181
10192
  }
10182
- this.lastDiscoveryTimeByScope.set(scopeKey, now);
10193
+ this.lastDiscoveryTimeByParent.set(parentSessionId, now);
10183
10194
  try {
10184
- const targets = await cdp.discoverAgentWebviews();
10185
- const activeTarget = targets.find((t) => t.agentType === activeAgentType);
10186
- if (activeTarget && !managed.has(activeAgentType)) {
10187
- const adapter = this.allAdapters.find((a) => a.agentType === activeAgentType);
10188
- if (adapter) {
10189
- const sessionId = await cdp.attachToAgent(activeTarget);
10190
- if (sessionId) {
10191
- managed.set(activeAgentType, {
10192
- adapter,
10193
- sessionId,
10194
- target: activeTarget,
10195
- lastState: null,
10196
- lastError: null,
10197
- lastHiddenCheckTime: 0
10198
- });
10199
- this.logFn(`[AgentStream] Connected: ${adapter.agentName} (${scopeKey})`);
10200
- }
10201
- }
10195
+ if (!managed) {
10196
+ await this.connectManagedSession(cdp, parentSessionId, activeSessionId);
10202
10197
  }
10203
- for (const [type, agent] of managed) {
10204
- if (type !== activeAgentType) {
10205
- await cdp.detachAgent(agent.sessionId);
10206
- managed.delete(type);
10207
- }
10198
+ this.discoveryIntervalMsByParent.set(parentSessionId, this.managedBySessionId.has(activeSessionId) ? 3e4 : 1e4);
10199
+ } catch (e) {
10200
+ this.logFn(`[AgentStream] sync error (${parentSessionId}): ${e.message}`);
10201
+ }
10202
+ }
10203
+ /** Collect active extension session state */
10204
+ async collectActiveSession(cdp, parentSessionId) {
10205
+ if (!this.enabled) return null;
10206
+ const activeSessionId = this.getActiveSessionId(parentSessionId);
10207
+ if (!activeSessionId) return null;
10208
+ let agent = this.managedBySessionId.get(activeSessionId);
10209
+ if (!agent) {
10210
+ agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
10211
+ }
10212
+ if (!agent) return null;
10213
+ const type = agent.adapter.agentType;
10214
+ const isHidden = agent.lastState?.status === "panel_hidden";
10215
+ const hiddenCacheFresh = isHidden && Date.now() - agent.lastHiddenCheckTime < 3e4;
10216
+ if (hiddenCacheFresh) return agent.lastState;
10217
+ try {
10218
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10219
+ const state = await agent.adapter.readChat(evaluate);
10220
+ LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${state.model || ""}${state.status === "error" ? " error=" + JSON.stringify(state.error || state._error || "unknown") : ""}`);
10221
+ agent.lastState = state;
10222
+ agent.lastError = null;
10223
+ if (state.status === "panel_hidden") {
10224
+ agent.lastHiddenCheckTime = Date.now();
10208
10225
  }
10209
- this.discoveryIntervalMsByScope.set(scopeKey, managed.has(activeAgentType) ? 3e4 : 1e4);
10226
+ return state;
10210
10227
  } catch (e) {
10211
- this.logFn(`[AgentStream] sync error (${scopeKey}): ${e.message}`);
10212
- }
10213
- }
10214
- /** Collect active agent status */
10215
- async collectAgentStreams(cdp, scopeKey) {
10216
- if (!this.enabled) return [];
10217
- const results = [];
10218
- const activeAgentType = this.getActiveAgentType(scopeKey);
10219
- const managed = this.managedByScope.get(scopeKey);
10220
- if (activeAgentType && managed?.has(activeAgentType)) {
10221
- const agent = managed.get(activeAgentType);
10222
- const type = activeAgentType;
10223
- const isHidden = agent.lastState?.status === "panel_hidden";
10224
- const hiddenCacheFresh = isHidden && Date.now() - agent.lastHiddenCheckTime < 3e4;
10225
- if (hiddenCacheFresh) {
10226
- results.push(agent.lastState);
10227
- } else {
10228
+ const errorMsg = e?.message || String(e);
10229
+ this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
10230
+ agent.lastError = errorMsg;
10231
+ if (errorMsg.includes("timeout") || errorMsg.includes("not connected") || errorMsg.includes("Session")) {
10228
10232
  try {
10229
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10230
- const state = await agent.adapter.readChat(evaluate);
10231
- LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${state.model || ""}${state.status === "error" ? " error=" + JSON.stringify(state.error || state._error || "unknown") : ""}`);
10232
- agent.lastState = state;
10233
- agent.lastError = null;
10234
- if (state.status === "panel_hidden") {
10235
- agent.lastHiddenCheckTime = Date.now();
10236
- }
10237
- results.push(state);
10238
- } catch (e) {
10239
- const errorMsg = e?.message || String(e);
10240
- this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
10241
- agent.lastError = errorMsg;
10242
- results.push({
10243
- agentType: type,
10244
- agentName: agent.adapter.agentName,
10245
- extensionId: agent.adapter.extensionId,
10246
- status: "disconnected",
10247
- messages: agent.lastState?.messages || [],
10248
- inputContent: ""
10249
- });
10250
- if (errorMsg.includes("timeout") || errorMsg.includes("not connected") || errorMsg.includes("Session")) {
10251
- try {
10252
- await cdp.detachAgent(agent.sessionId);
10253
- } catch {
10254
- }
10255
- managed.delete(type);
10256
- this.lastDiscoveryTimeByScope.set(scopeKey, 0);
10257
- }
10233
+ await cdp.detachAgent(agent.cdpSessionId);
10234
+ } catch {
10258
10235
  }
10236
+ this.managedBySessionId.delete(activeSessionId);
10237
+ this.lastDiscoveryTimeByParent.set(parentSessionId, 0);
10259
10238
  }
10239
+ return {
10240
+ agentType: type,
10241
+ agentName: agent.adapter.agentName,
10242
+ extensionId: agent.adapter.extensionId,
10243
+ status: "disconnected",
10244
+ messages: agent.lastState?.messages || [],
10245
+ inputContent: ""
10246
+ };
10260
10247
  }
10261
- return results;
10262
10248
  }
10263
- async sendToAgent(cdp, scopeKey, agentType, text, targetIdeType) {
10264
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10265
- const agent = this.getManagedAgent(agentType, scopeKey);
10249
+ async sendToSession(cdp, sessionId, text) {
10250
+ await this.ensureSessionPanelOpen(sessionId);
10251
+ const target = this.getSessionTarget(sessionId);
10252
+ if (!target?.parentSessionId) return false;
10253
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10254
+ await this.syncActiveSession(cdp, target.parentSessionId);
10255
+ const agent = this.managedBySessionId.get(sessionId);
10266
10256
  if (!agent) return false;
10267
10257
  try {
10268
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10258
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10269
10259
  await agent.adapter.sendMessage(evaluate, text);
10270
10260
  return true;
10271
10261
  } catch (e) {
10272
- this.logFn(`[AgentStream] sendToAgent(${agentType}) error: ${e.message}`);
10262
+ this.logFn(`[AgentStream] sendToSession(${sessionId}) error: ${e.message}`);
10273
10263
  return false;
10274
10264
  }
10275
10265
  }
10276
- async resolveAgentAction(cdp, scopeKey, agentType, action, targetIdeType) {
10277
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10278
- const agent = this.getManagedAgent(agentType, scopeKey);
10266
+ async resolveSessionAction(cdp, sessionId, action) {
10267
+ await this.ensureSessionPanelOpen(sessionId);
10268
+ const target = this.getSessionTarget(sessionId);
10269
+ if (!target?.parentSessionId) return false;
10270
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10271
+ await this.syncActiveSession(cdp, target.parentSessionId);
10272
+ const agent = this.managedBySessionId.get(sessionId);
10279
10273
  if (!agent) return false;
10280
10274
  try {
10281
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10275
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10282
10276
  return await agent.adapter.resolveAction(evaluate, action);
10283
10277
  } catch (e) {
10284
- this.logFn(`[AgentStream] resolveAction(${agentType}) error: ${e.message}`);
10278
+ this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
10285
10279
  return false;
10286
10280
  }
10287
10281
  }
10288
- async newAgentSession(cdp, scopeKey, agentType, targetIdeType) {
10289
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10290
- const agent = this.getManagedAgent(agentType, scopeKey);
10282
+ async newSession(cdp, sessionId) {
10283
+ await this.ensureSessionPanelOpen(sessionId);
10284
+ const target = this.getSessionTarget(sessionId);
10285
+ if (!target?.parentSessionId) return false;
10286
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10287
+ await this.syncActiveSession(cdp, target.parentSessionId);
10288
+ const agent = this.managedBySessionId.get(sessionId);
10291
10289
  if (!agent) return false;
10292
10290
  try {
10293
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10291
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10294
10292
  await agent.adapter.newSession(evaluate);
10295
10293
  return true;
10296
10294
  } catch (e) {
10297
- this.logFn(`[AgentStream] newSession(${agentType}) error: ${e.message}`);
10295
+ this.logFn(`[AgentStream] newSession(${sessionId}) error: ${e.message}`);
10298
10296
  return false;
10299
10297
  }
10300
10298
  }
10301
- async listAgentChats(cdp, scopeKey, agentType) {
10302
- let agent = this.getManagedAgent(agentType, scopeKey);
10303
- if (!agent) {
10304
- this.logFn(`[AgentStream] listChats: ${agentType} not managed in ${scopeKey}, trying on-demand activation`);
10305
- await this.switchActiveAgent(cdp, scopeKey, agentType);
10306
- await this.syncAgentSessions(cdp, scopeKey);
10307
- agent = this.getManagedAgent(agentType, scopeKey);
10308
- }
10299
+ async listSessionChats(cdp, sessionId) {
10300
+ const target = this.getSessionTarget(sessionId);
10301
+ if (!target?.parentSessionId) return [];
10302
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10303
+ await this.syncActiveSession(cdp, target.parentSessionId);
10304
+ const agent = this.managedBySessionId.get(sessionId);
10309
10305
  if (!agent || typeof agent.adapter.listChats !== "function") return [];
10310
10306
  try {
10311
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10307
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10312
10308
  return await agent.adapter.listChats(evaluate);
10313
10309
  } catch (e) {
10314
- this.logFn(`[AgentStream] listChats(${agentType}) error: ${e.message}`);
10310
+ this.logFn(`[AgentStream] listChats(${sessionId}) error: ${e.message}`);
10315
10311
  return [];
10316
10312
  }
10317
10313
  }
10318
- async switchAgentSession(cdp, scopeKey, agentType, sessionId) {
10319
- let agent = this.getManagedAgent(agentType, scopeKey);
10320
- if (!agent) {
10321
- this.logFn(`[AgentStream] switchSession: ${agentType} not managed in ${scopeKey}, trying on-demand activation`);
10322
- await this.switchActiveAgent(cdp, scopeKey, agentType);
10323
- await this.syncAgentSessions(cdp, scopeKey);
10324
- agent = this.getManagedAgent(agentType, scopeKey);
10325
- }
10314
+ async switchConversation(cdp, sessionId, conversationId) {
10315
+ const target = this.getSessionTarget(sessionId);
10316
+ if (!target?.parentSessionId) return false;
10317
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10318
+ await this.syncActiveSession(cdp, target.parentSessionId);
10319
+ const agent = this.managedBySessionId.get(sessionId);
10326
10320
  if (!agent || typeof agent.adapter.switchSession !== "function") return false;
10327
10321
  try {
10328
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10329
- return await agent.adapter.switchSession(evaluate, sessionId);
10322
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10323
+ return await agent.adapter.switchSession(evaluate, conversationId);
10330
10324
  } catch (e) {
10331
- this.logFn(`[AgentStream] switchSession(${agentType}) error: ${e.message}`);
10325
+ this.logFn(`[AgentStream] switchSession(${sessionId}) error: ${e.message}`);
10332
10326
  return false;
10333
10327
  }
10334
10328
  }
10335
- async focusAgentEditor(cdp, scopeKey, agentType) {
10336
- const agent = this.getManagedAgent(agentType, scopeKey);
10329
+ async focusSession(cdp, sessionId) {
10330
+ const target = this.getSessionTarget(sessionId);
10331
+ if (!target?.parentSessionId) return false;
10332
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10333
+ await this.syncActiveSession(cdp, target.parentSessionId);
10334
+ const agent = this.managedBySessionId.get(sessionId);
10337
10335
  if (!agent || typeof agent.adapter.focusEditor !== "function") return false;
10338
10336
  try {
10339
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10337
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10340
10338
  await agent.adapter.focusEditor(evaluate);
10341
10339
  return true;
10342
10340
  } catch (e) {
10343
- this.logFn(`[AgentStream] focusEditor(${agentType}) error: ${e.message}`);
10341
+ this.logFn(`[AgentStream] focusEditor(${sessionId}) error: ${e.message}`);
10344
10342
  return false;
10345
10343
  }
10346
10344
  }
10347
- getConnectedAgents(scopeKey) {
10348
- if (scopeKey) return Array.from((this.managedByScope.get(scopeKey) || /* @__PURE__ */ new Map()).keys());
10349
- return Array.from(this.managedByScope.values()).flatMap((scope) => Array.from(scope.keys()));
10345
+ getConnectedSessions(parentSessionId) {
10346
+ if (parentSessionId) {
10347
+ return [...this.managedBySessionId.values()].filter((entry) => entry.parentSessionId === parentSessionId).map((entry) => entry.runtimeSessionId);
10348
+ }
10349
+ return [...this.managedBySessionId.keys()];
10350
10350
  }
10351
- getManagedAgent(agentType, scopeKey) {
10352
- return this.managedByScope.get(scopeKey)?.get(agentType);
10351
+ getManagedSession(sessionId) {
10352
+ return this.managedBySessionId.get(sessionId);
10353
10353
  }
10354
10354
  async dispose(cdpManagers) {
10355
- for (const [scopeKey, managed] of this.managedByScope) {
10356
- const cdp = cdpManagers.get(scopeKey);
10355
+ for (const managed of this.managedBySessionId.values()) {
10356
+ const managerKey = this.getSessionTarget(managed.runtimeSessionId)?.cdpManagerKey;
10357
+ const cdp = managerKey ? cdpManagers.get(managerKey) : null;
10357
10358
  if (!cdp) continue;
10358
- for (const [, agent] of managed) {
10359
- try {
10360
- await cdp.detachAgent(agent.sessionId);
10361
- } catch {
10362
- }
10359
+ try {
10360
+ await cdp.detachAgent(managed.cdpSessionId);
10361
+ } catch {
10363
10362
  }
10364
10363
  }
10365
- this.managedByScope.clear();
10366
- this.activeAgentTypeByScope.clear();
10367
- this.lastDiscoveryTimeByScope.clear();
10368
- this.discoveryIntervalMsByScope.clear();
10364
+ this.managedBySessionId.clear();
10365
+ this.activeSessionIdByParent.clear();
10366
+ this.lastDiscoveryTimeByParent.clear();
10367
+ this.discoveryIntervalMsByParent.clear();
10368
+ }
10369
+ resolveSessionForAgent(parentSessionId, agentType) {
10370
+ return this.resolveSessionIdForTarget(parentSessionId, agentType);
10369
10371
  }
10370
10372
  };
10371
10373
 
@@ -10382,8 +10384,8 @@ var AgentStreamPoller = class {
10382
10384
  return null;
10383
10385
  }
10384
10386
  /** Reset active IDE tracking (e.g., when IDE is stopped) */
10385
- resetActiveIde(ideType) {
10386
- this.deps.agentStreamManager.resetScope(ideType);
10387
+ resetActiveIde(parentSessionId) {
10388
+ this.deps.agentStreamManager.resetParentSession(parentSessionId);
10387
10389
  }
10388
10390
  /** Start polling (idempotent — ignored if already started) */
10389
10391
  start(intervalMs = 5e3) {
@@ -10405,12 +10407,14 @@ var AgentStreamPoller = class {
10405
10407
  agentStreamManager,
10406
10408
  providerLoader,
10407
10409
  instanceManager,
10408
- cdpManagers
10410
+ cdpManagers,
10411
+ sessionRegistry
10409
10412
  } = this.deps;
10410
10413
  if (!agentStreamManager || cdpManagers.size === 0) return;
10411
10414
  for (const [ideType, cdp] of cdpManagers) {
10412
10415
  registerExtensionProviders(providerLoader, cdp, ideType);
10413
10416
  const ideInstance = instanceManager.getInstance(`ide:${ideType}`);
10417
+ const parentSessionId = ideInstance?.getInstanceId?.();
10414
10418
  if (ideInstance?.getExtensionTypes && ideInstance?.addExtension && ideInstance?.removeExtension) {
10415
10419
  const currentExtTypes = new Set(ideInstance.getExtensionTypes());
10416
10420
  const enabledExtTypes = new Set(
@@ -10418,6 +10422,10 @@ var AgentStreamPoller = class {
10418
10422
  );
10419
10423
  for (const extType of currentExtTypes) {
10420
10424
  if (!enabledExtTypes.has(extType)) {
10425
+ const extInstance = ideInstance.getExtension?.(extType);
10426
+ if (extInstance?.getInstanceId) {
10427
+ sessionRegistry.unregister(extInstance.getInstanceId());
10428
+ }
10421
10429
  ideInstance.removeExtension(extType);
10422
10430
  LOG.info("AgentStream", `Extension removed: ${extType} (disabled for ${ideType})`);
10423
10431
  }
@@ -10428,46 +10436,61 @@ var AgentStreamPoller = class {
10428
10436
  if (extProvider) {
10429
10437
  const extSettings = providerLoader.getSettings(extType);
10430
10438
  ideInstance.addExtension(extProvider, extSettings);
10439
+ const extInstance = ideInstance.getExtension?.(extType);
10440
+ if (parentSessionId && extInstance?.getInstanceId) {
10441
+ sessionRegistry.register({
10442
+ sessionId: extInstance.getInstanceId(),
10443
+ parentSessionId,
10444
+ providerType: extType,
10445
+ providerCategory: "extension",
10446
+ transport: "cdp-webview",
10447
+ cdpManagerKey: ideType,
10448
+ instanceKey: `ide:${ideType}`
10449
+ });
10450
+ }
10431
10451
  LOG.info("AgentStream", `Extension added: ${extType} (enabled for ${ideType})`);
10432
10452
  }
10433
10453
  }
10434
10454
  }
10435
10455
  }
10436
- const activeType = agentStreamManager.getActiveAgentType(ideType);
10437
- if (activeType) {
10438
- const enabledExtTypes = new Set(
10439
- providerLoader.getEnabledExtensionProviders(ideType).map((p) => p.type)
10440
- );
10441
- if (!enabledExtTypes.has(activeType)) {
10442
- LOG.info("AgentStream", `Active agent ${activeType} was disabled for ${ideType} \u2014 detaching`);
10443
- await agentStreamManager.switchActiveAgent(cdp, ideType, null);
10456
+ const activeSessionId = parentSessionId ? agentStreamManager.getActiveSessionId(parentSessionId) : null;
10457
+ if (activeSessionId) {
10458
+ const activeTarget = sessionRegistry.get(activeSessionId);
10459
+ const enabledExtTypes = new Set(providerLoader.getEnabledExtensionProviders(ideType).map((p) => p.type));
10460
+ if (!activeTarget || !enabledExtTypes.has(activeTarget.providerType)) {
10461
+ LOG.info("AgentStream", `Active agent ${activeTarget?.providerType || activeSessionId} was disabled for ${ideType} \u2014 detaching`);
10462
+ await agentStreamManager.setActiveSession(cdp, parentSessionId, null);
10444
10463
  this.deps.onStreamsUpdated?.(ideType, []);
10445
10464
  }
10446
10465
  }
10447
10466
  if (!cdp.isConnected) {
10448
- if (activeType) {
10449
- agentStreamManager.resetScope(ideType);
10467
+ if (parentSessionId && activeSessionId) {
10468
+ agentStreamManager.resetParentSession(parentSessionId);
10450
10469
  this.deps.onStreamsUpdated?.(ideType, []);
10451
10470
  }
10452
10471
  continue;
10453
10472
  }
10454
- let resolvedActiveType = activeType;
10455
- if (!resolvedActiveType) {
10473
+ let resolvedActiveSessionId = activeSessionId;
10474
+ if (!resolvedActiveSessionId && parentSessionId) {
10456
10475
  try {
10457
10476
  const discovered = await cdp.discoverAgentWebviews();
10458
- if (discovered.length > 0) {
10459
- resolvedActiveType = discovered[0].agentType;
10460
- await agentStreamManager.switchActiveAgent(cdp, ideType, resolvedActiveType);
10461
- LOG.info("AgentStream", `Auto-activated: ${resolvedActiveType} (${ideType})`);
10477
+ for (const target of discovered) {
10478
+ const sessionId = agentStreamManager.resolveSessionForAgent(parentSessionId, target.agentType);
10479
+ if (sessionId) {
10480
+ resolvedActiveSessionId = sessionId;
10481
+ await agentStreamManager.setActiveSession(cdp, parentSessionId, sessionId);
10482
+ LOG.info("AgentStream", `Auto-activated: ${target.agentType} (${ideType})`);
10483
+ break;
10484
+ }
10462
10485
  }
10463
10486
  } catch {
10464
10487
  }
10465
10488
  }
10466
- if (!resolvedActiveType) continue;
10489
+ if (!resolvedActiveSessionId || !parentSessionId) continue;
10467
10490
  try {
10468
- await agentStreamManager.syncAgentSessions(cdp, ideType);
10469
- const streams = await agentStreamManager.collectAgentStreams(cdp, ideType);
10470
- this.deps.onStreamsUpdated?.(ideType, streams);
10491
+ await agentStreamManager.syncActiveSession(cdp, parentSessionId);
10492
+ const stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
10493
+ this.deps.onStreamsUpdated?.(ideType, stream ? [stream] : []);
10471
10494
  } catch {
10472
10495
  }
10473
10496
  }
@@ -10556,6 +10579,7 @@ var ProviderInstanceManager = class {
10556
10579
  ...event,
10557
10580
  providerType: instance.type,
10558
10581
  instanceId: state.instanceId,
10582
+ targetSessionId: state.instanceId,
10559
10583
  providerCategory: state.category
10560
10584
  });
10561
10585
  }
@@ -14366,6 +14390,63 @@ function launchIDE(ide, workspacePath) {
14366
14390
  }
14367
14391
  }
14368
14392
 
14393
+ // src/sessions/registry.ts
14394
+ var SessionRegistry = class {
14395
+ bySessionId = /* @__PURE__ */ new Map();
14396
+ byManagerKey = /* @__PURE__ */ new Map();
14397
+ byInstanceKey = /* @__PURE__ */ new Map();
14398
+ byParentSessionId = /* @__PURE__ */ new Map();
14399
+ register(target) {
14400
+ this.unregister(target.sessionId);
14401
+ this.bySessionId.set(target.sessionId, target);
14402
+ if (target.cdpManagerKey) this.addIndex(this.byManagerKey, target.cdpManagerKey, target.sessionId);
14403
+ if (target.instanceKey) this.addIndex(this.byInstanceKey, target.instanceKey, target.sessionId);
14404
+ if (target.parentSessionId) this.addIndex(this.byParentSessionId, target.parentSessionId, target.sessionId);
14405
+ }
14406
+ get(sessionId) {
14407
+ if (!sessionId) return void 0;
14408
+ return this.bySessionId.get(sessionId);
14409
+ }
14410
+ unregister(sessionId) {
14411
+ if (!sessionId) return;
14412
+ const target = this.bySessionId.get(sessionId);
14413
+ if (!target) return;
14414
+ this.bySessionId.delete(sessionId);
14415
+ if (target.cdpManagerKey) this.removeIndex(this.byManagerKey, target.cdpManagerKey, sessionId);
14416
+ if (target.instanceKey) this.removeIndex(this.byInstanceKey, target.instanceKey, sessionId);
14417
+ if (target.parentSessionId) this.removeIndex(this.byParentSessionId, target.parentSessionId, sessionId);
14418
+ }
14419
+ unregisterByManagerKey(managerKey) {
14420
+ for (const sessionId of [...this.byManagerKey.get(managerKey) || []]) {
14421
+ this.unregister(sessionId);
14422
+ }
14423
+ }
14424
+ unregisterByInstanceKey(instanceKey) {
14425
+ for (const sessionId of [...this.byInstanceKey.get(instanceKey) || []]) {
14426
+ this.unregister(sessionId);
14427
+ }
14428
+ }
14429
+ listChildren(parentSessionId) {
14430
+ const ids = this.byParentSessionId.get(parentSessionId);
14431
+ if (!ids) return [];
14432
+ return [...ids].map((id) => this.bySessionId.get(id)).filter(Boolean);
14433
+ }
14434
+ addIndex(index, key, sessionId) {
14435
+ let set = index.get(key);
14436
+ if (!set) {
14437
+ set = /* @__PURE__ */ new Set();
14438
+ index.set(key, set);
14439
+ }
14440
+ set.add(sessionId);
14441
+ }
14442
+ removeIndex(index, key, sessionId) {
14443
+ const set = index.get(key);
14444
+ if (!set) return;
14445
+ set.delete(sessionId);
14446
+ if (set.size === 0) index.delete(key);
14447
+ }
14448
+ };
14449
+
14369
14450
  // src/boot/daemon-lifecycle.ts
14370
14451
  init_logger();
14371
14452
  init_config();
@@ -14405,11 +14486,14 @@ async function initDaemonComponents(config) {
14405
14486
  });
14406
14487
  const instanceManager = new ProviderInstanceManager();
14407
14488
  const cdpManagers = /* @__PURE__ */ new Map();
14408
- const instanceIdMap = /* @__PURE__ */ new Map();
14489
+ const sessionRegistry = new SessionRegistry();
14409
14490
  const detectedIdesRef = { value: [] };
14491
+ let agentStreamManager = null;
14492
+ let poller = null;
14410
14493
  const cliManager = new DaemonCliManager({
14411
14494
  ...config.cliManagerDeps,
14412
- getInstanceManager: () => instanceManager
14495
+ getInstanceManager: () => instanceManager,
14496
+ getSessionRegistry: () => sessionRegistry
14413
14497
  }, providerLoader);
14414
14498
  LOG.info("Init", "Detecting IDEs...");
14415
14499
  detectedIdesRef.value = await detectIDEs();
@@ -14419,7 +14503,7 @@ async function initDaemonComponents(config) {
14419
14503
  providerLoader,
14420
14504
  instanceManager,
14421
14505
  cdpManagers,
14422
- instanceIdMap
14506
+ sessionRegistry
14423
14507
  };
14424
14508
  const cdpInitializer = new DaemonCdpInitializer({
14425
14509
  providerLoader,
@@ -14428,6 +14512,19 @@ async function initDaemonComponents(config) {
14428
14512
  onConnected: async (ideType, manager, managerKey) => {
14429
14513
  await setupIdeInstance(cdpSetupContext, { ideType, manager, managerKey });
14430
14514
  await config.onCdpManagerSetup?.(ideType, manager, managerKey);
14515
+ },
14516
+ onDisconnected: async (_ideType, _manager, managerKey) => {
14517
+ sessionRegistry.unregisterByManagerKey(managerKey);
14518
+ const instanceKey = `ide:${managerKey}`;
14519
+ const ideInstance = instanceManager.getInstance(instanceKey);
14520
+ if (ideInstance) {
14521
+ instanceManager.removeInstance(instanceKey);
14522
+ LOG.info("CDP", `Instance removed after disconnect: ${instanceKey}`);
14523
+ }
14524
+ if (ideInstance?.getInstanceId) {
14525
+ agentStreamManager?.resetParentSession(ideInstance.getInstanceId());
14526
+ }
14527
+ config.onStatusChange?.();
14431
14528
  }
14432
14529
  });
14433
14530
  await cdpInitializer.connectAll(detectedIdesRef.value);
@@ -14439,14 +14536,14 @@ async function initDaemonComponents(config) {
14439
14536
  adapters: cliManager.adapters,
14440
14537
  providerLoader,
14441
14538
  instanceManager,
14442
- instanceIdMap
14539
+ sessionRegistry
14443
14540
  });
14444
- const agentStreamManager = new DaemonAgentStreamManager(
14541
+ agentStreamManager = new DaemonAgentStreamManager(
14445
14542
  LOG.forComponent("AgentStream").asLogFn(),
14446
- providerLoader
14543
+ providerLoader,
14544
+ sessionRegistry
14447
14545
  );
14448
14546
  commandHandler.setAgentStreamManager(agentStreamManager);
14449
- let poller;
14450
14547
  const router = new DaemonCommandRouter({
14451
14548
  commandHandler,
14452
14549
  cliManager,
@@ -14454,7 +14551,7 @@ async function initDaemonComponents(config) {
14454
14551
  providerLoader,
14455
14552
  instanceManager,
14456
14553
  detectedIdes: detectedIdesRef,
14457
- instanceIdMap,
14554
+ sessionRegistry,
14458
14555
  onCdpManagerCreated: async (ideType, manager) => {
14459
14556
  await setupIdeInstance(cdpSetupContext, { ideType, manager });
14460
14557
  await config.onCdpManagerSetup?.(ideType, manager, ideType);
@@ -14469,6 +14566,7 @@ async function initDaemonComponents(config) {
14469
14566
  providerLoader,
14470
14567
  instanceManager,
14471
14568
  cdpManagers,
14569
+ sessionRegistry,
14472
14570
  onStreamsUpdated: config.onStreamsUpdated
14473
14571
  });
14474
14572
  poller.start();
@@ -14483,7 +14581,7 @@ async function initDaemonComponents(config) {
14483
14581
  poller,
14484
14582
  cdpInitializer,
14485
14583
  cdpManagers,
14486
- instanceIdMap,
14584
+ sessionRegistry,
14487
14585
  detectedIdes: detectedIdesRef
14488
14586
  };
14489
14587
  }
@@ -14556,10 +14654,7 @@ export {
14556
14654
  ProviderLoader,
14557
14655
  VersionArchive,
14558
14656
  addCliHistory,
14559
- buildAllManagedEntries,
14560
- buildManagedAcps,
14561
- buildManagedClis,
14562
- buildManagedIdes,
14657
+ buildSessionEntries,
14563
14658
  buildStatusSnapshot,
14564
14659
  connectCdpManager,
14565
14660
  detectAllVersions,