@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.js CHANGED
@@ -1788,10 +1788,7 @@ __export(index_exports, {
1788
1788
  ProviderLoader: () => ProviderLoader,
1789
1789
  VersionArchive: () => VersionArchive,
1790
1790
  addCliHistory: () => addCliHistory,
1791
- buildAllManagedEntries: () => buildAllManagedEntries,
1792
- buildManagedAcps: () => buildManagedAcps,
1793
- buildManagedClis: () => buildManagedClis,
1794
- buildManagedIdes: () => buildManagedIdes,
1791
+ buildSessionEntries: () => buildSessionEntries,
1795
1792
  buildStatusSnapshot: () => buildStatusSnapshot,
1796
1793
  connectCdpManager: () => connectCdpManager,
1797
1794
  detectAllVersions: () => detectAllVersions,
@@ -2859,7 +2856,11 @@ var DaemonCdpManager = class {
2859
2856
  async attachToAgent(target) {
2860
2857
  if (!this.isConnected) return null;
2861
2858
  for (const [sid, t] of this.agentSessions) {
2862
- if (t.agentType === target.agentType) return sid;
2859
+ if (t.targetId === target.targetId) return sid;
2860
+ if (t.agentType === target.agentType && t.targetId !== target.targetId) {
2861
+ await this.detachAgent(sid).catch(() => {
2862
+ });
2863
+ }
2863
2864
  }
2864
2865
  try {
2865
2866
  const sendFn = this._browserConnected ? this.sendBrowser.bind(this) : this.sendInternal.bind(this);
@@ -2983,6 +2984,20 @@ var DaemonCdpManager = class {
2983
2984
  }
2984
2985
  async getCurrentPageWebviewUrls() {
2985
2986
  if (!this.isConnected) return /* @__PURE__ */ new Set();
2987
+ try {
2988
+ const urls = /* @__PURE__ */ new Set();
2989
+ const { frameTree } = await this.sendInternal("Page.getFrameTree", {}, 5e3);
2990
+ const visit = (node) => {
2991
+ const url = node?.frame?.url;
2992
+ if (typeof url === "string" && url.includes("vscode-webview")) {
2993
+ urls.add(url);
2994
+ }
2995
+ for (const child of node?.childFrames || []) visit(child);
2996
+ };
2997
+ if (frameTree) visit(frameTree);
2998
+ if (urls.size > 0) return urls;
2999
+ } catch {
3000
+ }
2986
3001
  try {
2987
3002
  const raw = await this.evaluate(
2988
3003
  `JSON.stringify(Array.from(document.querySelectorAll('iframe,webview'))
@@ -4134,7 +4149,7 @@ function registerExtensionProviders(providerLoader, manager, ideType) {
4134
4149
  manager.setExtensionProviders(enabledExtProviders);
4135
4150
  }
4136
4151
  async function setupIdeInstance(ctx, opts) {
4137
- const { providerLoader, instanceManager, instanceIdMap } = ctx;
4152
+ const { providerLoader, instanceManager, sessionRegistry } = ctx;
4138
4153
  const { ideType, manager, settings } = opts;
4139
4154
  const managerKey = opts.managerKey || ideType;
4140
4155
  registerExtensionProviders(providerLoader, manager, ideType);
@@ -4150,14 +4165,30 @@ async function setupIdeInstance(ctx, opts) {
4150
4165
  serverConn: ctx.serverConn,
4151
4166
  settings: resolvedSettings
4152
4167
  });
4153
- instanceIdMap.set(ideInstance.getInstanceId(), managerKey);
4168
+ sessionRegistry.register({
4169
+ sessionId: ideInstance.getInstanceId(),
4170
+ parentSessionId: null,
4171
+ providerType: ideType,
4172
+ providerCategory: "ide",
4173
+ transport: "cdp-page",
4174
+ cdpManagerKey: managerKey,
4175
+ instanceKey: `ide:${managerKey}`
4176
+ });
4154
4177
  const extensionProviders = providerLoader.getEnabledByCategory("extension", ideType);
4155
4178
  for (const extProvider of extensionProviders) {
4156
4179
  const extSettings = providerLoader.getSettings(extProvider.type);
4157
4180
  await ideInstance.addExtension(extProvider, extSettings);
4158
- for (const ext of ideInstance.getExtensionInstances()) {
4159
- instanceIdMap.set(ext.getInstanceId(), managerKey);
4160
- }
4181
+ }
4182
+ for (const ext of ideInstance.getExtensionInstances()) {
4183
+ sessionRegistry.register({
4184
+ sessionId: ext.getInstanceId(),
4185
+ parentSessionId: ideInstance.getInstanceId(),
4186
+ providerType: ext.type,
4187
+ providerCategory: "extension",
4188
+ transport: "cdp-webview",
4189
+ cdpManagerKey: managerKey,
4190
+ instanceKey: `ide:${managerKey}`
4191
+ });
4161
4192
  }
4162
4193
  return ideInstance;
4163
4194
  }
@@ -4374,6 +4405,7 @@ var DaemonCdpInitializer = class {
4374
4405
  async connectIdePort(port, ide) {
4375
4406
  const { providerLoader, cdpManagers } = this.config;
4376
4407
  const targets = await DaemonCdpManager.listAllTargets(port);
4408
+ await this.pruneStaleManagers(port, ide, targets);
4377
4409
  if (targets.length === 0) {
4378
4410
  if (cdpManagers.has(ide)) return;
4379
4411
  if (!await probeCdpPort(port)) return;
@@ -4426,6 +4458,34 @@ var DaemonCdpInitializer = class {
4426
4458
  }
4427
4459
  }
4428
4460
  }
4461
+ async pruneStaleManagers(port, ide, targets) {
4462
+ const trackedTargetIds = new Set(targets.map((target) => target.id));
4463
+ const removals = [];
4464
+ for (const [key, manager] of this.config.cdpManagers.entries()) {
4465
+ if (!(key === ide || key.startsWith(`${ide}_`))) continue;
4466
+ if (manager.getPort() !== port) continue;
4467
+ if (targets.length === 0) {
4468
+ removals.push({ key, manager, reason: "ide_closed" });
4469
+ continue;
4470
+ }
4471
+ if (manager.targetId && !trackedTargetIds.has(manager.targetId)) {
4472
+ removals.push({ key, manager, reason: "target_closed" });
4473
+ continue;
4474
+ }
4475
+ if (key === ide && !manager.targetId && targets.length > 1) {
4476
+ removals.push({ key, manager, reason: "target_rekeyed" });
4477
+ }
4478
+ }
4479
+ for (const { key, manager, reason } of removals) {
4480
+ try {
4481
+ manager.disconnect();
4482
+ } catch {
4483
+ }
4484
+ this.config.cdpManagers.delete(key);
4485
+ LOG.info("CDP", `Removed stale manager: ${key} (${reason})`);
4486
+ await this.config.onDisconnected?.(ide, manager, key, reason);
4487
+ }
4488
+ }
4429
4489
  // ─── Periodic scanning ───
4430
4490
  /**
4431
4491
  * Start periodic scanning for newly opened IDEs.
@@ -4529,95 +4589,152 @@ function isCdpConnected(cdpManagers, key) {
4529
4589
  const m = findCdpManager(cdpManagers, key);
4530
4590
  return m?.isConnected ?? false;
4531
4591
  }
4532
- function buildManagedIdes(ideStates, cdpManagers, opts) {
4533
- const result = [];
4534
- for (const state of ideStates) {
4535
- const cdpConnected = state.cdpConnected ?? isCdpConnected(cdpManagers, state.type);
4536
- result.push({
4537
- ideType: state.type,
4538
- ideVersion: "",
4539
- instanceId: state.instanceId || state.type,
4540
- workspace: state.workspace || null,
4541
- terminals: 0,
4542
- aiAgents: [],
4543
- activeChat: normalizeActiveChatData(state.activeChat),
4544
- chats: [],
4545
- agentStreams: state.extensions.map((ext) => ({
4546
- agentType: ext.type,
4547
- agentName: ext.name,
4548
- extensionId: ext.type,
4549
- status: normalizeManagedStatus(ext.status, { activeModal: ext.activeChat?.activeModal || null }),
4550
- messages: ext.activeChat?.messages || [],
4551
- inputContent: ext.activeChat?.inputContent || "",
4552
- activeModal: ext.activeChat?.activeModal || null
4553
- })),
4554
- cdpConnected,
4555
- currentModel: state.currentModel,
4556
- currentPlan: state.currentPlan,
4557
- currentAutoApprove: state.currentAutoApprove
4558
- });
4559
- }
4560
- if (opts?.detectedIdes) {
4561
- const coveredTypes = new Set(ideStates.map((s) => s.type));
4562
- for (const ide of opts.detectedIdes) {
4563
- if (!ide.installed || coveredTypes.has(ide.id)) continue;
4564
- if (!isCdpConnected(cdpManagers, ide.id)) continue;
4565
- result.push({
4566
- ideType: ide.id,
4567
- ideVersion: "",
4568
- instanceId: ide.id,
4569
- workspace: null,
4570
- terminals: 0,
4571
- aiAgents: [],
4572
- activeChat: null,
4573
- chats: [],
4574
- agentStreams: [],
4575
- cdpConnected: true,
4576
- currentModel: void 0,
4577
- currentPlan: void 0
4578
- });
4579
- }
4580
- }
4581
- return result;
4592
+ var IDE_SESSION_CAPABILITIES = [
4593
+ "read_chat",
4594
+ "send_message",
4595
+ "new_session",
4596
+ "list_sessions",
4597
+ "switch_session",
4598
+ "resolve_action",
4599
+ "change_model",
4600
+ "set_mode",
4601
+ "set_thought_level"
4602
+ ];
4603
+ var EXTENSION_SESSION_CAPABILITIES = [
4604
+ "read_chat",
4605
+ "send_message",
4606
+ "new_session",
4607
+ "list_sessions",
4608
+ "switch_session",
4609
+ "resolve_action",
4610
+ "change_model",
4611
+ "set_mode"
4612
+ ];
4613
+ var PTY_SESSION_CAPABILITIES = [
4614
+ "read_chat",
4615
+ "send_message",
4616
+ "resolve_action",
4617
+ "terminal_io",
4618
+ "resize_terminal"
4619
+ ];
4620
+ var ACP_SESSION_CAPABILITIES = [
4621
+ "read_chat",
4622
+ "send_message",
4623
+ "new_session",
4624
+ "resolve_action",
4625
+ "change_model",
4626
+ "set_mode",
4627
+ "set_thought_level"
4628
+ ];
4629
+ function buildIdeWorkspaceSession(state, cdpManagers) {
4630
+ const activeChat = normalizeActiveChatData(state.activeChat);
4631
+ const title = activeChat?.title || state.name;
4632
+ return {
4633
+ id: state.instanceId || state.type,
4634
+ parentId: null,
4635
+ providerType: state.type,
4636
+ providerName: state.name,
4637
+ kind: "workspace",
4638
+ transport: "cdp-page",
4639
+ status: normalizeManagedStatus(activeChat?.status || state.status, {
4640
+ activeModal: activeChat?.activeModal || null
4641
+ }),
4642
+ title,
4643
+ workspace: state.workspace || null,
4644
+ activeChat,
4645
+ capabilities: IDE_SESSION_CAPABILITIES,
4646
+ cdpConnected: state.cdpConnected ?? isCdpConnected(cdpManagers, state.type),
4647
+ currentModel: state.currentModel,
4648
+ currentPlan: state.currentPlan,
4649
+ currentAutoApprove: state.currentAutoApprove,
4650
+ errorMessage: state.errorMessage,
4651
+ errorReason: state.errorReason
4652
+ };
4582
4653
  }
4583
- function buildManagedClis(cliStates) {
4584
- return cliStates.map((s) => ({
4585
- id: s.instanceId,
4586
- instanceId: s.instanceId,
4587
- cliType: s.type,
4588
- cliName: s.name,
4589
- status: normalizeManagedStatus(s.status, { activeModal: s.activeChat?.activeModal || null }),
4590
- mode: "terminal",
4591
- workspace: s.workspace || "",
4592
- activeChat: normalizeActiveChatData(s.activeChat)
4593
- }));
4654
+ function buildExtensionAgentSession(parent, ext) {
4655
+ const activeChat = normalizeActiveChatData(ext.activeChat);
4656
+ return {
4657
+ id: ext.instanceId || `${parent.instanceId}:${ext.type}`,
4658
+ parentId: parent.instanceId || parent.type,
4659
+ providerType: ext.type,
4660
+ providerName: ext.name,
4661
+ kind: "agent",
4662
+ transport: "cdp-webview",
4663
+ status: normalizeManagedStatus(activeChat?.status || ext.status, {
4664
+ activeModal: activeChat?.activeModal || null
4665
+ }),
4666
+ title: activeChat?.title || ext.name,
4667
+ workspace: parent.workspace || null,
4668
+ activeChat,
4669
+ capabilities: EXTENSION_SESSION_CAPABILITIES,
4670
+ currentModel: ext.currentModel,
4671
+ currentPlan: ext.currentPlan,
4672
+ errorMessage: ext.errorMessage,
4673
+ errorReason: ext.errorReason
4674
+ };
4594
4675
  }
4595
- function buildManagedAcps(acpStates) {
4596
- return acpStates.map((s) => ({
4597
- id: s.instanceId,
4598
- acpType: s.type,
4599
- acpName: s.name,
4600
- status: normalizeManagedStatus(s.status, { activeModal: s.activeChat?.activeModal || null }),
4601
- mode: "chat",
4602
- workspace: s.workspace || "",
4603
- activeChat: normalizeActiveChatData(s.activeChat),
4604
- currentModel: s.currentModel,
4605
- currentPlan: s.currentPlan,
4606
- acpConfigOptions: s.acpConfigOptions,
4607
- acpModes: s.acpModes,
4608
- errorMessage: s.errorMessage,
4609
- errorReason: s.errorReason
4610
- }));
4676
+ function buildCliSession(state) {
4677
+ const activeChat = normalizeActiveChatData(state.activeChat);
4678
+ return {
4679
+ id: state.instanceId,
4680
+ parentId: null,
4681
+ providerType: state.type,
4682
+ providerName: state.name,
4683
+ kind: "agent",
4684
+ transport: "pty",
4685
+ status: normalizeManagedStatus(activeChat?.status || state.status, {
4686
+ activeModal: activeChat?.activeModal || null
4687
+ }),
4688
+ title: activeChat?.title || state.name,
4689
+ workspace: state.workspace || null,
4690
+ activeChat,
4691
+ capabilities: PTY_SESSION_CAPABILITIES,
4692
+ errorMessage: state.errorMessage,
4693
+ errorReason: state.errorReason
4694
+ };
4611
4695
  }
4612
- function buildAllManagedEntries(allStates, cdpManagers, opts) {
4696
+ function buildAcpSession(state) {
4697
+ const activeChat = normalizeActiveChatData(state.activeChat);
4698
+ return {
4699
+ id: state.instanceId,
4700
+ parentId: null,
4701
+ providerType: state.type,
4702
+ providerName: state.name,
4703
+ kind: "agent",
4704
+ transport: "acp",
4705
+ status: normalizeManagedStatus(activeChat?.status || state.status, {
4706
+ activeModal: activeChat?.activeModal || null
4707
+ }),
4708
+ title: activeChat?.title || state.name,
4709
+ workspace: state.workspace || null,
4710
+ activeChat,
4711
+ capabilities: ACP_SESSION_CAPABILITIES,
4712
+ currentModel: state.currentModel,
4713
+ currentPlan: state.currentPlan,
4714
+ acpConfigOptions: state.acpConfigOptions,
4715
+ acpModes: state.acpModes,
4716
+ errorMessage: state.errorMessage,
4717
+ errorReason: state.errorReason
4718
+ };
4719
+ }
4720
+ function buildSessionEntries(allStates, cdpManagers) {
4721
+ const sessions = [];
4613
4722
  const ideStates = allStates.filter((s) => s.category === "ide");
4614
4723
  const cliStates = allStates.filter((s) => s.category === "cli");
4615
4724
  const acpStates = allStates.filter((s) => s.category === "acp");
4616
- return {
4617
- managedIdes: buildManagedIdes(ideStates, cdpManagers, opts),
4618
- managedClis: buildManagedClis(cliStates),
4619
- managedAcps: buildManagedAcps(acpStates)
4620
- };
4725
+ for (const state of ideStates) {
4726
+ sessions.push(buildIdeWorkspaceSession(state, cdpManagers));
4727
+ for (const ext of state.extensions) {
4728
+ sessions.push(buildExtensionAgentSession(state, ext));
4729
+ }
4730
+ }
4731
+ for (const state of cliStates) {
4732
+ sessions.push(buildCliSession(state));
4733
+ }
4734
+ for (const state of acpStates) {
4735
+ sessions.push(buildAcpSession(state));
4736
+ }
4737
+ return sessions;
4621
4738
  }
4622
4739
 
4623
4740
  // src/commands/handler.ts
@@ -4626,14 +4743,37 @@ init_logger();
4626
4743
 
4627
4744
  // src/commands/chat-commands.ts
4628
4745
  init_logger();
4746
+ var RECENT_SEND_WINDOW_MS = 1200;
4747
+ var recentSendByTarget = /* @__PURE__ */ new Map();
4748
+ function getCurrentProviderType(h, fallback = "") {
4749
+ return h.currentSession?.providerType || h.currentProviderType || fallback;
4750
+ }
4751
+ function getCurrentManagerKey(h) {
4752
+ return h.currentSession?.cdpManagerKey || h.currentManagerKey || "";
4753
+ }
4629
4754
  function getTargetedCliAdapter(h, args, providerType) {
4630
- return h.getCliAdapter(args?._targetInstance || h.currentIdeType || providerType);
4755
+ return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
4756
+ }
4757
+ function buildRecentSendKey(h, args, provider, text) {
4758
+ const target = args?.targetSessionId || args?.agentType || h.currentSession?.providerType || h.currentProviderType || h.currentManagerKey || "unknown";
4759
+ return `${provider?.category || "unknown"}:${target}:${text.trim()}`;
4760
+ }
4761
+ function isRecentDuplicateSend(key) {
4762
+ const now = Date.now();
4763
+ for (const [candidate, ts2] of recentSendByTarget.entries()) {
4764
+ if (now - ts2 > RECENT_SEND_WINDOW_MS) recentSendByTarget.delete(candidate);
4765
+ }
4766
+ const previous = recentSendByTarget.get(key);
4767
+ if (previous && now - previous <= RECENT_SEND_WINDOW_MS) return true;
4768
+ recentSendByTarget.set(key, now);
4769
+ return false;
4631
4770
  }
4632
4771
  async function handleChatHistory(h, args) {
4633
- const { agentType, offset, limit, instanceId } = args;
4772
+ const { agentType, offset, limit } = args;
4773
+ const instanceId = args?.targetSessionId;
4634
4774
  try {
4635
4775
  const provider = h.getProvider(agentType);
4636
- const agentStr = provider?.type || agentType || h.currentIdeType || "";
4776
+ const agentStr = provider?.type || agentType || getCurrentProviderType(h);
4637
4777
  const result = readChatHistory(agentStr, offset || 0, limit || 30, instanceId);
4638
4778
  return { success: true, ...result, agent: agentStr };
4639
4779
  } catch (e) {
@@ -4677,7 +4817,7 @@ async function handleReadChat(h, args) {
4677
4817
  provider.type || "unknown_extension",
4678
4818
  parsed.messages || [],
4679
4819
  parsed.title,
4680
- args?.instanceId
4820
+ args?.targetSessionId
4681
4821
  );
4682
4822
  return { success: true, ...parsed };
4683
4823
  }
@@ -4687,15 +4827,18 @@ async function handleReadChat(h, args) {
4687
4827
  }
4688
4828
  if (h.agentStream) {
4689
4829
  const cdp2 = h.getCdp();
4690
- if (cdp2 && h.currentIdeType) {
4691
- const streams = await h.agentStream.collectAgentStreams(cdp2, h.currentIdeType);
4692
- const stream = streams.find((s) => s.agentType === provider.type);
4830
+ const parentSessionId = h.currentSession?.parentSessionId;
4831
+ if (cdp2 && parentSessionId) {
4832
+ const stream = await h.agentStream.collectActiveSession(cdp2, parentSessionId);
4833
+ if (stream?.agentType !== provider.type) {
4834
+ return { success: true, messages: [], status: "idle" };
4835
+ }
4693
4836
  if (stream) {
4694
4837
  h.historyWriter.appendNewMessages(
4695
4838
  stream.agentType,
4696
4839
  stream.messages || [],
4697
4840
  void 0,
4698
- args?.instanceId
4841
+ args?.targetSessionId
4699
4842
  );
4700
4843
  return { success: true, messages: stream.messages || [], status: stream.status, agentType: stream.agentType };
4701
4844
  }
@@ -4722,10 +4865,10 @@ async function handleReadChat(h, args) {
4722
4865
  if (parsed && typeof parsed === "object") {
4723
4866
  _log(`Webview OK: ${parsed.messages?.length || 0} msgs`);
4724
4867
  h.historyWriter.appendNewMessages(
4725
- provider?.type || h.currentIdeType || "unknown_webview",
4868
+ provider?.type || getCurrentProviderType(h, "unknown_webview"),
4726
4869
  parsed.messages || [],
4727
4870
  parsed.title,
4728
- args?.instanceId
4871
+ args?.targetSessionId
4729
4872
  );
4730
4873
  return { success: true, ...parsed };
4731
4874
  }
@@ -4749,10 +4892,10 @@ async function handleReadChat(h, args) {
4749
4892
  if (parsed && typeof parsed === "object" && parsed.messages?.length > 0) {
4750
4893
  _log(`OK: ${parsed.messages?.length} msgs`);
4751
4894
  h.historyWriter.appendNewMessages(
4752
- provider?.type || h.currentIdeType || "unknown_ide",
4895
+ provider?.type || getCurrentProviderType(h, "unknown_ide"),
4753
4896
  parsed.messages || [],
4754
4897
  parsed.title,
4755
- args?.instanceId
4898
+ args?.targetSessionId
4756
4899
  );
4757
4900
  return { success: true, ...parsed };
4758
4901
  }
@@ -4767,16 +4910,21 @@ async function handleSendChat(h, args) {
4767
4910
  if (!text) return { success: false, error: "text required" };
4768
4911
  const _log = (msg) => LOG.debug("Command", `[send_chat] ${msg}`);
4769
4912
  const provider = h.getProvider(args?.agentType);
4913
+ const dedupeKey = buildRecentSendKey(h, args, provider, text);
4770
4914
  const _logSendSuccess = (method, targetAgent) => {
4771
4915
  h.historyWriter.appendNewMessages(
4772
- targetAgent || provider?.type || h.currentIdeType || "unknown_agent",
4916
+ targetAgent || provider?.type || getCurrentProviderType(h, "unknown_agent"),
4773
4917
  [{ role: "user", content: text, receivedAt: Date.now() }],
4774
4918
  void 0,
4775
4919
  // title
4776
- args?.instanceId
4920
+ args?.targetSessionId
4777
4921
  );
4778
4922
  return { success: true, sent: true, method, targetAgent };
4779
4923
  };
4924
+ if (isRecentDuplicateSend(dedupeKey)) {
4925
+ _log(`Suppressed duplicate send for ${dedupeKey}`);
4926
+ return { success: true, sent: false, deduplicated: true };
4927
+ }
4780
4928
  if (provider?.category === "cli" || provider?.category === "acp") {
4781
4929
  const adapter = getTargetedCliAdapter(h, args, provider.type);
4782
4930
  if (adapter) {
@@ -4812,8 +4960,9 @@ async function handleSendChat(h, args) {
4812
4960
  } catch (e) {
4813
4961
  _log(`Extension script error: ${e.message}`);
4814
4962
  }
4815
- if (h.agentStream && h.getCdp() && h.currentIdeType) {
4816
- const ok = await h.agentStream.sendToAgent(h.getCdp(), h.currentIdeType, provider.type, text, h.currentIdeType);
4963
+ const extensionSessionId = h.currentSession?.sessionId;
4964
+ if (h.agentStream && h.getCdp() && extensionSessionId) {
4965
+ const ok = await h.agentStream.sendToSession(h.getCdp(), extensionSessionId, text);
4817
4966
  if (ok) {
4818
4967
  _log(`AgentStreamManager sent OK`);
4819
4968
  return _logSendSuccess("agent-stream");
@@ -4823,45 +4972,11 @@ async function handleSendChat(h, args) {
4823
4972
  }
4824
4973
  const targetCdp = h.getCdp();
4825
4974
  if (!targetCdp?.isConnected) {
4826
- _log(`No CDP for ${h.currentIdeType}`);
4827
- return { success: false, error: `CDP for ${h.currentIdeType || "unknown"} not connected` };
4828
- }
4829
- _log(`Targeting IDE: ${h.currentIdeType}`);
4830
- if (provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
4831
- try {
4832
- const webviewScript = provider.scripts.webviewSendMessage(text);
4833
- if (webviewScript && targetCdp.evaluateInWebviewFrame) {
4834
- const matchText = provider.webviewMatchText;
4835
- const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
4836
- const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
4837
- let wvParsed = wvResult;
4838
- if (typeof wvResult === "string") {
4839
- try {
4840
- wvParsed = JSON.parse(wvResult);
4841
- } catch {
4842
- }
4843
- }
4844
- if (wvParsed?.sent) {
4845
- _log(`webviewSendMessage (priority) OK`);
4846
- return _logSendSuccess("webview-script-priority");
4847
- }
4848
- _log(`webviewSendMessage (priority) did not confirm sent, falling through`);
4849
- }
4850
- } catch (e) {
4851
- _log(`webviewSendMessage (priority) failed: ${e.message}, falling through`);
4852
- }
4853
- }
4854
- if (provider?.inputMethod === "cdp-type-and-send" && provider.inputSelector) {
4855
- try {
4856
- const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
4857
- if (sent) {
4858
- _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
4859
- return _logSendSuccess("typeAndSend-provider");
4860
- }
4861
- } catch (e) {
4862
- _log(`typeAndSend(provider) failed: ${e.message}`);
4863
- }
4975
+ const managerKey = getCurrentManagerKey(h);
4976
+ _log(`No CDP for ${managerKey}`);
4977
+ return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
4864
4978
  }
4979
+ _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
4865
4980
  const sendScript = h.getProviderScript("sendMessage", { MESSAGE: text });
4866
4981
  if (sendScript) {
4867
4982
  try {
@@ -4888,7 +5003,30 @@ async function handleSendChat(h, args) {
4888
5003
  _log(`typeAndSend(script.selector) failed: ${e.message}`);
4889
5004
  }
4890
5005
  }
4891
- if (parsed?.needsTypeAndSend && provider?.scripts?.webviewSendMessage) {
5006
+ if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
5007
+ try {
5008
+ const { x, y } = parsed.clickCoords;
5009
+ const sent = await targetCdp.typeAndSendAt(x, y, text);
5010
+ if (sent) {
5011
+ _log(`typeAndSendAt(${x},${y}) success`);
5012
+ return _logSendSuccess("typeAndSendAt-script");
5013
+ }
5014
+ } catch (e) {
5015
+ _log(`typeAndSendAt failed: ${e.message}`);
5016
+ }
5017
+ }
5018
+ if (parsed?.needsTypeAndSend && provider?.inputMethod === "cdp-type-and-send" && provider.inputSelector) {
5019
+ try {
5020
+ const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
5021
+ if (sent) {
5022
+ _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
5023
+ return _logSendSuccess("typeAndSend-provider");
5024
+ }
5025
+ } catch (e) {
5026
+ _log(`typeAndSend(provider) failed: ${e.message}`);
5027
+ }
5028
+ }
5029
+ if (parsed?.needsTypeAndSend && provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
4892
5030
  try {
4893
5031
  const webviewScript = provider.scripts.webviewSendMessage(text);
4894
5032
  if (webviewScript && targetCdp.evaluateInWebviewFrame) {
@@ -4911,20 +5049,44 @@ async function handleSendChat(h, args) {
4911
5049
  _log(`webviewSendMessage failed: ${e.message}`);
4912
5050
  }
4913
5051
  }
4914
- if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
4915
- try {
4916
- const { x, y } = parsed.clickCoords;
4917
- const sent = await targetCdp.typeAndSendAt(x, y, text);
4918
- if (sent) {
4919
- _log(`typeAndSendAt(${x},${y}) success`);
4920
- return _logSendSuccess("typeAndSendAt-script");
5052
+ return { success: false, error: parsed?.error || "Provider sendMessage did not confirm send" };
5053
+ } catch (e) {
5054
+ _log(`sendMessage script failed: ${e.message}`);
5055
+ return { success: false, error: `Provider sendMessage failed: ${e.message}` };
5056
+ }
5057
+ }
5058
+ if (provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
5059
+ try {
5060
+ const webviewScript = provider.scripts.webviewSendMessage(text);
5061
+ if (webviewScript && targetCdp.evaluateInWebviewFrame) {
5062
+ const matchText = provider.webviewMatchText;
5063
+ const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
5064
+ const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
5065
+ let wvParsed = wvResult;
5066
+ if (typeof wvResult === "string") {
5067
+ try {
5068
+ wvParsed = JSON.parse(wvResult);
5069
+ } catch {
4921
5070
  }
4922
- } catch (e) {
4923
- _log(`typeAndSendAt failed: ${e.message}`);
5071
+ }
5072
+ if (wvParsed?.sent) {
5073
+ _log(`webviewSendMessage OK`);
5074
+ return _logSendSuccess("webview-script");
4924
5075
  }
4925
5076
  }
4926
5077
  } catch (e) {
4927
- _log(`sendMessage script failed: ${e.message}`);
5078
+ _log(`webviewSendMessage failed: ${e.message}`);
5079
+ }
5080
+ }
5081
+ if (provider?.inputMethod === "cdp-type-and-send" && provider.inputSelector) {
5082
+ try {
5083
+ const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
5084
+ if (sent) {
5085
+ _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
5086
+ return _logSendSuccess("typeAndSend-provider");
5087
+ }
5088
+ } catch (e) {
5089
+ _log(`typeAndSend(provider) failed: ${e.message}`);
4928
5090
  }
4929
5091
  }
4930
5092
  _log("All methods failed");
@@ -4932,9 +5094,9 @@ async function handleSendChat(h, args) {
4932
5094
  }
4933
5095
  async function handleListChats(h, args) {
4934
5096
  const provider = h.getProvider(args?.agentType);
4935
- if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentIdeType) {
5097
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
4936
5098
  try {
4937
- const chats = await h.agentStream.listAgentChats(h.getCdp(), h.currentIdeType, provider.type);
5099
+ const chats = await h.agentStream.listSessionChats(h.getCdp(), h.currentSession.sessionId);
4938
5100
  LOG.info("Command", `[list_chats] Extension: ${chats.length} chats`);
4939
5101
  return { success: true, chats };
4940
5102
  } catch (e) {
@@ -4993,8 +5155,8 @@ async function handleNewChat(h, args) {
4993
5155
  }
4994
5156
  return { success: false, error: "new_chat not supported by this CLI provider" };
4995
5157
  }
4996
- if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentIdeType) {
4997
- const ok = await h.agentStream.newAgentSession(h.getCdp(), h.currentIdeType, provider.type, h.currentIdeType);
5158
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
5159
+ const ok = await h.agentStream.newSession(h.getCdp(), h.currentSession.sessionId);
4998
5160
  return { success: ok };
4999
5161
  }
5000
5162
  try {
@@ -5018,15 +5180,15 @@ async function handleNewChat(h, args) {
5018
5180
  }
5019
5181
  async function handleSwitchChat(h, args) {
5020
5182
  const provider = h.getProvider(args?.agentType);
5021
- const ideType = h.currentIdeType;
5183
+ const managerKey = getCurrentManagerKey(h);
5022
5184
  const sessionId = args?.sessionId || args?.id || args?.chatId;
5023
5185
  if (!sessionId) return { success: false, error: "sessionId required" };
5024
- LOG.info("Command", `[switch_chat] sessionId=${sessionId}, ideType=${ideType}`);
5025
- if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentIdeType) {
5026
- const ok = await h.agentStream.switchAgentSession(h.getCdp(), h.currentIdeType, provider.type, sessionId);
5186
+ LOG.info("Command", `[switch_chat] sessionId=${sessionId}, manager=${managerKey}`);
5187
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
5188
+ const ok = await h.agentStream.switchConversation(h.getCdp(), h.currentSession.sessionId, sessionId);
5027
5189
  return { success: ok, result: ok ? "switched" : "failed" };
5028
5190
  }
5029
- const cdp = h.getCdp(ideType);
5191
+ const cdp = h.getCdp(managerKey);
5030
5192
  if (!cdp?.isConnected) return { success: false, error: "CDP not connected" };
5031
5193
  try {
5032
5194
  const webviewScript = h.getProviderScript("webviewSwitchSession", { SESSION_ID: JSON.stringify(sessionId) });
@@ -5168,7 +5330,7 @@ async function handleSetMode(h, args) {
5168
5330
  async function handleChangeModel(h, args) {
5169
5331
  const provider = h.getProvider(args?.agentType);
5170
5332
  const model = args?.model;
5171
- LOG.info("Command", `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} ideType=${h.currentIdeType} providerType=${h.currentProviderType}`);
5333
+ LOG.info("Command", `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} manager=${getCurrentManagerKey(h)} providerType=${getCurrentProviderType(h)}`);
5172
5334
  if (provider?.category === "acp") {
5173
5335
  const adapter = getTargetedCliAdapter(h, args, provider.type);
5174
5336
  LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
@@ -5289,14 +5451,8 @@ async function handleResolveAction(h, args) {
5289
5451
  LOG.info("Command", `[resolveAction] CLI PTY \u2192 buttonIndex=${buttonIndex} "${buttons[buttonIndex] ?? "?"}"`);
5290
5452
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
5291
5453
  }
5292
- if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentIdeType) {
5293
- const ok = await h.agentStream.resolveAgentAction(
5294
- h.getCdp(),
5295
- h.currentIdeType,
5296
- provider.type,
5297
- action,
5298
- h.currentIdeType
5299
- );
5454
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
5455
+ const ok = await h.agentStream.resolveSessionAction(h.getCdp(), h.currentSession.sessionId, action);
5300
5456
  return { success: ok };
5301
5457
  }
5302
5458
  if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
@@ -5656,157 +5812,37 @@ async function handleFileListBrowse(h, args) {
5656
5812
  // src/commands/stream-commands.ts
5657
5813
  init_config();
5658
5814
  init_logger();
5659
- async function handleAgentStreamSwitch(h, args) {
5660
- if (!h.agentStream || !h.getCdp() || !h.currentIdeType) return { success: false, error: "AgentStream or CDP not available" };
5661
- const agentType = args?.agentType || args?.agent || null;
5662
- await h.agentStream.switchActiveAgent(h.getCdp(), h.currentIdeType, agentType);
5663
- return { success: true, activeAgent: agentType };
5664
- }
5665
- async function handleAgentStreamRead(h, args) {
5666
- if (!h.agentStream || !h.getCdp() || !h.currentIdeType) return { success: false, error: "AgentStream or CDP not available" };
5667
- const streams = await h.agentStream.collectAgentStreams(h.getCdp(), h.currentIdeType);
5668
- return { success: true, streams };
5669
- }
5670
- async function handleAgentStreamSend(h, args) {
5671
- const agentType = args?.agentType || args?.agent;
5672
- const text = args?.text || args?.message;
5673
- if (!text) return { success: false, error: "text required" };
5674
- if (agentType && h.ctx.adapters) {
5675
- for (const [key, adapter] of h.ctx.adapters.entries()) {
5676
- if (adapter.cliType === agentType || key.includes(agentType)) {
5677
- LOG.info("Command", `[agent_stream_send] Routing to CLI adapter: ${adapter.cliType}`);
5678
- try {
5679
- await adapter.sendMessage(text);
5680
- return { success: true, sent: true, targetAgent: adapter.cliType };
5681
- } catch (e) {
5682
- LOG.info("Command", `[agent_stream_send] CLI adapter failed: ${e.message}`);
5683
- return { success: false, error: `CLI send failed: ${e.message}` };
5684
- }
5685
- }
5686
- }
5687
- }
5688
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5689
- const resolvedAgent = agentType || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5690
- if (!resolvedAgent) return { success: false, error: "agentType required" };
5691
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5692
- const ok = await h.agentStream.sendToAgent(h.getCdp(), h.currentIdeType, resolvedAgent, text, h.currentIdeType);
5693
- return { success: ok };
5694
- }
5695
- async function handleAgentStreamResolve(h, args) {
5696
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5697
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5698
- const action = args?.action || "approve";
5699
- if (!agentType) return { success: false, error: "agentType required" };
5700
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5701
- const ok = await h.agentStream.resolveAgentAction(h.getCdp(), h.currentIdeType, agentType, action, h.currentIdeType);
5702
- return { success: ok };
5703
- }
5704
- async function handleAgentStreamNew(h, args) {
5705
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5706
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5707
- if (!agentType) return { success: false, error: "agentType required" };
5708
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5709
- const ok = await h.agentStream.newAgentSession(h.getCdp(), h.currentIdeType, agentType, h.currentIdeType);
5710
- return { success: ok };
5711
- }
5712
- async function handleAgentStreamListChats(h, args) {
5713
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5714
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5715
- if (!agentType) return { success: false, error: "agentType required" };
5716
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5717
- const chats = await h.agentStream.listAgentChats(h.getCdp(), h.currentIdeType, agentType);
5718
- return { success: true, chats };
5719
- }
5720
- async function handleAgentStreamSwitchSession(h, args) {
5721
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5722
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5723
- const sessionId = args?.sessionId || args?.id;
5724
- if (!agentType || !sessionId) return { success: false, error: "agentType and sessionId required" };
5725
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5726
- const ok = await h.agentStream.switchAgentSession(h.getCdp(), h.currentIdeType, agentType, sessionId);
5727
- return { success: ok };
5728
- }
5729
- async function handleAgentStreamFocus(h, args) {
5815
+ async function handleFocusSession(h, args) {
5730
5816
  if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5731
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
5732
- if (!agentType) return { success: false, error: "agentType required" };
5733
- await h.agentStream.ensureAgentPanelOpen(agentType, h.currentIdeType);
5734
- if (!h.currentIdeType) return { success: false, error: "ideType required" };
5735
- const ok = await h.agentStream.focusAgentEditor(h.getCdp(), h.currentIdeType, agentType);
5817
+ const sessionId = args?.targetSessionId || h.currentSession?.sessionId;
5818
+ if (!sessionId) return { success: false, error: "targetSessionId required" };
5819
+ const ok = await h.agentStream.focusSession(h.getCdp(), sessionId);
5736
5820
  return { success: ok };
5737
5821
  }
5738
5822
  function handlePtyInput(h, args) {
5739
- const { cliType, data } = args || {};
5823
+ const { cliType, data, targetSessionId } = args || {};
5740
5824
  if (!data) return { success: false, error: "data required" };
5741
- if (h.ctx.adapters) {
5742
- const targetCli = cliType || "";
5743
- if (!targetCli && h.ctx.adapters.size > 0) {
5744
- const first = h.ctx.adapters.values().next().value;
5745
- if (first && typeof first.writeRaw === "function") {
5746
- first.writeRaw(data);
5747
- return { success: true };
5748
- }
5749
- }
5750
- const directAdapter = h.ctx.adapters.get(targetCli);
5751
- if (directAdapter && typeof directAdapter.writeRaw === "function") {
5752
- directAdapter.writeRaw(data);
5753
- return { success: true };
5754
- }
5755
- for (const [, adapter] of h.ctx.adapters) {
5756
- if (adapter.cliType === targetCli && typeof adapter.writeRaw === "function") {
5757
- adapter.writeRaw(data);
5758
- return { success: true };
5759
- }
5760
- }
5761
- for (const [key, adapter] of h.ctx.adapters) {
5762
- if ((key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.writeRaw === "function") {
5763
- adapter.writeRaw(data);
5764
- return { success: true };
5765
- }
5766
- }
5825
+ const adapter = h.getCliAdapter(targetSessionId || cliType);
5826
+ if (!adapter || typeof adapter.writeRaw !== "function") {
5827
+ return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || "unknown"}` };
5767
5828
  }
5768
- return { success: false, error: `CLI adapter not found: ${cliType}` };
5829
+ adapter.writeRaw(data);
5830
+ return { success: true };
5769
5831
  }
5770
5832
  function handlePtyResize(h, args) {
5771
- const { cliType, cols, rows, force } = args || {};
5833
+ const { cliType, cols, rows, force, targetSessionId } = args || {};
5772
5834
  if (!cols || !rows) return { success: false, error: "cols and rows required" };
5773
- if (h.ctx.adapters) {
5774
- const targetCli = cliType || "";
5775
- if (!targetCli && h.ctx.adapters.size > 0) {
5776
- const first = h.ctx.adapters.values().next().value;
5777
- if (first && typeof first.resize === "function") {
5778
- if (force) {
5779
- first.resize(cols - 1, rows);
5780
- setTimeout(() => first.resize(cols, rows), 50);
5781
- } else {
5782
- first.resize(cols, rows);
5783
- }
5784
- return { success: true };
5785
- }
5786
- }
5787
- const directAdapter = h.ctx.adapters.get(targetCli);
5788
- if (directAdapter && typeof directAdapter.resize === "function") {
5789
- if (force) {
5790
- directAdapter.resize(cols - 1, rows);
5791
- setTimeout(() => directAdapter.resize(cols, rows), 50);
5792
- } else {
5793
- directAdapter.resize(cols, rows);
5794
- }
5795
- return { success: true };
5796
- }
5797
- for (const [key, adapter] of h.ctx.adapters) {
5798
- if ((adapter.cliType === targetCli || key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.resize === "function") {
5799
- if (force) {
5800
- adapter.resize(cols - 1, rows);
5801
- setTimeout(() => adapter.resize(cols, rows), 50);
5802
- } else {
5803
- adapter.resize(cols, rows);
5804
- }
5805
- return { success: true };
5806
- }
5807
- }
5835
+ const adapter = h.getCliAdapter(targetSessionId || cliType);
5836
+ if (!adapter || typeof adapter.resize !== "function") {
5837
+ return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || "unknown"}` };
5838
+ }
5839
+ if (force) {
5840
+ adapter.resize(cols - 1, rows);
5841
+ setTimeout(() => adapter.resize(cols, rows), 50);
5842
+ } else {
5843
+ adapter.resize(cols, rows);
5808
5844
  }
5809
- return { success: false, error: `CLI adapter not found: ${cliType}` };
5845
+ return { success: true };
5810
5846
  }
5811
5847
  function handleGetProviderSettings(h, args) {
5812
5848
  const loader = h.ctx.providerLoader;
@@ -5842,7 +5878,7 @@ function handleSetProviderSetting(h, args) {
5842
5878
  }
5843
5879
  async function handleExtensionScript(h, args, scriptName) {
5844
5880
  const { agentType, ideType } = args || {};
5845
- LOG.info("Command", `[ExtScript] ${scriptName} agentType=${agentType} ideType=${ideType} _currentIdeType=${h.currentIdeType}`);
5881
+ LOG.info("Command", `[ExtScript] ${scriptName} agentType=${agentType} ideType=${ideType} session=${h.currentSession?.sessionId || ""}`);
5846
5882
  if (!agentType) return { success: false, error: "agentType is required" };
5847
5883
  const loader = h.ctx.providerLoader;
5848
5884
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -5863,21 +5899,22 @@ async function handleExtensionScript(h, args, scriptName) {
5863
5899
  }
5864
5900
  const scriptCode = scriptFn(normalizedArgs);
5865
5901
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
5866
- const cdpKey = provider.category === "ide" ? h.currentIdeType || agentType : h.currentIdeType || ideType;
5902
+ const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType : h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType;
5867
5903
  LOG.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
5868
5904
  const cdp = h.getCdp(cdpKey);
5869
5905
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
5870
5906
  try {
5871
5907
  let result;
5872
5908
  if (provider.category === "extension") {
5873
- const sessions = cdp.getAgentSessions();
5874
- let targetSessionId = null;
5875
- for (const [sessionId, target] of sessions) {
5876
- if (target.agentType === agentType) {
5877
- targetSessionId = sessionId;
5878
- break;
5879
- }
5880
- }
5909
+ const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
5910
+ if (!runtimeSessionId) return { success: false, error: `No target session found for ${agentType}` };
5911
+ const parentSessionId = h.currentSession?.parentSessionId;
5912
+ if (parentSessionId) {
5913
+ await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
5914
+ await h.agentStream?.syncActiveSession(cdp, parentSessionId);
5915
+ }
5916
+ const managed = runtimeSessionId ? h.agentStream?.getManagedSession(runtimeSessionId) : null;
5917
+ const targetSessionId = managed?.cdpSessionId || null;
5881
5918
  const IDE_LEVEL_SCRIPTS = ["listModes", "setMode", "listModels", "setModel"];
5882
5919
  if (IDE_LEVEL_SCRIPTS.includes(scriptName)) {
5883
5920
  if (targetSessionId) {
@@ -5948,7 +5985,7 @@ function handleGetIdeExtensions(h, args) {
5948
5985
  enabled: config.ideSettings?.[ide]?.extensions?.[p.type]?.enabled === true
5949
5986
  }));
5950
5987
  }
5951
- return { success: true, ides: result };
5988
+ return { success: true, ideExtensions: result };
5952
5989
  }
5953
5990
  function handleSetIdeExtension(h, args) {
5954
5991
  const { ideType, extensionType, enabled } = args || {};
@@ -6060,10 +6097,8 @@ var DaemonCommandHandler = class {
6060
6097
  _agentStream = null;
6061
6098
  domHandlers;
6062
6099
  _historyWriter;
6063
- /** Current IDE type extracted from command args (per-request) */
6064
- _currentIdeType;
6065
- /** Current provider type — agentType priority, ideType use */
6066
- _currentProviderType;
6100
+ /** Current request route context */
6101
+ _currentRoute = {};
6067
6102
  constructor(ctx) {
6068
6103
  this._ctx = ctx;
6069
6104
  this.domHandlers = new CdpDomHandlers((ideType) => this.getCdp(ideType));
@@ -6079,20 +6114,25 @@ var DaemonCommandHandler = class {
6079
6114
  get historyWriter() {
6080
6115
  return this._historyWriter;
6081
6116
  }
6117
+ get currentManagerKey() {
6118
+ return this._currentRoute.managerKey;
6119
+ }
6082
6120
  get currentIdeType() {
6083
- return this._currentIdeType;
6121
+ return this._currentRoute.managerKey;
6084
6122
  }
6085
6123
  get currentProviderType() {
6086
- return this._currentProviderType;
6124
+ return this._currentRoute.providerType;
6125
+ }
6126
+ get currentSession() {
6127
+ return this._currentRoute.session;
6087
6128
  }
6088
- /** Get CDP manager for a specific ideType or managerKey.
6089
- * Supports exact match, multi-window prefix match, and instanceIdMap UUID lookup.
6090
- * Returns null if no match — never falls back to another IDE. */
6129
+ /** Get CDP manager for a specific session or manager key. */
6091
6130
  getCdp(ideType) {
6092
- const key = ideType || this._currentIdeType;
6093
- if (!key) return null;
6094
- const resolved = this._ctx.instanceIdMap?.get(key) || key;
6095
- const m = findCdpManager(this._ctx.cdpManagers, resolved);
6131
+ const requested = ideType || this._currentRoute.session?.sessionId || this._currentRoute.managerKey;
6132
+ if (!requested) return null;
6133
+ const session = this._ctx.sessionRegistry?.get(requested);
6134
+ const managerKey = session?.cdpManagerKey || requested;
6135
+ const m = findCdpManager(this._ctx.cdpManagers, managerKey);
6096
6136
  if (m?.isConnected) return m;
6097
6137
  return null;
6098
6138
  }
@@ -6100,7 +6140,7 @@ var DaemonCommandHandler = class {
6100
6140
  * Get provider module — _currentProviderType (agentType priority) use.
6101
6141
  */
6102
6142
  getProvider(overrideType) {
6103
- const key = overrideType || this._currentProviderType || this._currentIdeType;
6143
+ const key = overrideType || this._currentRoute.providerType || this._currentRoute.session?.providerType || this._currentRoute.managerKey;
6104
6144
  if (!key || !this._ctx.providerLoader) return void 0;
6105
6145
  const result = this._ctx.providerLoader.resolve(key);
6106
6146
  if (result) return result;
@@ -6133,14 +6173,22 @@ var DaemonCommandHandler = class {
6133
6173
  const cdp = this.getCdp();
6134
6174
  if (!cdp?.isConnected) return null;
6135
6175
  if (provider?.category === "extension") {
6136
- let sessionId = this.getExtensionSessionId(provider, this._currentIdeType);
6137
- if (!sessionId && this._agentStream && this._currentIdeType) {
6138
- await this._agentStream.switchActiveAgent(cdp, this._currentIdeType, provider.type);
6139
- await this._agentStream.syncAgentSessions(cdp, this._currentIdeType);
6140
- sessionId = this.getExtensionSessionId(provider, this._currentIdeType);
6176
+ let sessionId = this._currentRoute.session?.sessionId || null;
6177
+ if (!sessionId && this._currentRoute.session?.parentSessionId) {
6178
+ sessionId = this._agentStream?.resolveSessionForAgent(this._currentRoute.session.parentSessionId, provider.type) || null;
6179
+ }
6180
+ if (sessionId && this._agentStream) {
6181
+ const target = this._ctx.sessionRegistry?.get(sessionId);
6182
+ if (target?.parentSessionId) {
6183
+ await this._agentStream.setActiveSession(cdp, target.parentSessionId, sessionId);
6184
+ await this._agentStream.syncActiveSession(cdp, target.parentSessionId);
6185
+ }
6141
6186
  }
6142
6187
  if (!sessionId) return null;
6143
- const result2 = await cdp.evaluateInSessionFrame(sessionId, script, timeout);
6188
+ const managed = this._agentStream?.getManagedSession(sessionId);
6189
+ const cdpSessionId = managed?.cdpSessionId;
6190
+ if (!cdpSessionId) return null;
6191
+ const result2 = await cdp.evaluateInSessionFrame(cdpSessionId, script, timeout);
6144
6192
  return { result: result2, category: "extension" };
6145
6193
  }
6146
6194
  const result = await cdp.evaluate(script, timeout);
@@ -6148,57 +6196,37 @@ var DaemonCommandHandler = class {
6148
6196
  }
6149
6197
  /** CLI adapter search */
6150
6198
  getCliAdapter(type) {
6151
- const target = type || this._currentIdeType;
6199
+ const target = type || this._currentRoute.session?.sessionId || this._currentRoute.providerType || this._currentRoute.managerKey;
6152
6200
  if (!target || !this._ctx.adapters) return null;
6153
- let normalizedTarget = target;
6154
- const colonIdx = normalizedTarget.lastIndexOf(":");
6155
- if (colonIdx >= 0) normalizedTarget = normalizedTarget.substring(colonIdx + 1);
6156
- const direct = this._ctx.adapters.get(normalizedTarget);
6157
- if (direct) return direct;
6158
- for (const [key, adapter] of this._ctx.adapters.entries()) {
6159
- if (adapter.cliType === target || adapter.cliType === normalizedTarget || key === normalizedTarget || key.startsWith(target) || key.startsWith(normalizedTarget)) {
6160
- return adapter;
6161
- }
6201
+ const session = this._ctx.sessionRegistry?.get(target);
6202
+ if (session?.adapterKey) {
6203
+ return this._ctx.adapters.get(session.adapterKey) || null;
6162
6204
  }
6163
- return null;
6205
+ return this._ctx.adapters.get(target) || null;
6164
6206
  }
6165
6207
  // ─── Private helpers ──────────────────────────────
6166
- getExtensionSessionId(provider, scopeKey) {
6167
- if (provider.category !== "extension" || !this._agentStream || !scopeKey) return null;
6168
- const managed = this._agentStream.getManagedAgent(provider.type, scopeKey);
6169
- return managed?.sessionId || null;
6170
- }
6171
- resolveManagerKeyFromInstanceId(instanceId) {
6172
- const mapped = this._ctx.instanceIdMap?.get(instanceId);
6173
- if (mapped) return mapped;
6174
- const entries = this._ctx.instanceManager?.instances?.entries?.();
6175
- if (!entries) return void 0;
6176
- for (const [instanceKey, instance] of entries) {
6177
- if (typeof instanceKey !== "string" || !instanceKey.startsWith("ide:")) continue;
6178
- if (typeof instance?.getInstanceId === "function" && instance.getInstanceId() === instanceId) {
6179
- const managerKey = instanceKey.slice(4);
6180
- this._ctx.instanceIdMap?.set(instanceId, managerKey);
6181
- return managerKey;
6182
- }
6183
- if (typeof instance?.getExtensionInstances === "function") {
6184
- for (const ext of instance.getExtensionInstances() || []) {
6185
- if (typeof ext?.getInstanceId === "function" && ext.getInstanceId() === instanceId) {
6186
- const managerKey = instanceKey.slice(4);
6187
- this._ctx.instanceIdMap?.set(instanceId, managerKey);
6188
- return managerKey;
6189
- }
6190
- }
6191
- }
6192
- }
6193
- return void 0;
6194
- }
6195
- /** Extract ideType from _targetInstance or explicit ideType */
6208
+ inferProviderType(key) {
6209
+ if (!key) return void 0;
6210
+ const session = this._ctx.sessionRegistry?.get(key);
6211
+ if (session?.providerType) return session.providerType;
6212
+ return key.split("_")[0];
6213
+ }
6214
+ resolveRoute(args) {
6215
+ const session = this._ctx.sessionRegistry?.get(args?.targetSessionId);
6216
+ const managerKey = this.extractIdeType(args);
6217
+ const providerType = args?.agentType || args?.providerType || session?.providerType || this.inferProviderType(managerKey);
6218
+ return { session, managerKey, providerType };
6219
+ }
6220
+ /** Extract CDP scope key from target session or explicit ideType */
6196
6221
  extractIdeType(args) {
6222
+ if (args?.targetSessionId) {
6223
+ const target = this._ctx.sessionRegistry?.get(args.targetSessionId);
6224
+ if (target?.cdpManagerKey) return target.cdpManagerKey;
6225
+ if (this._ctx.cdpManagers.has(args.targetSessionId)) return args.targetSessionId;
6226
+ }
6197
6227
  if (args?.ideType) {
6198
- const mappedKey = this.resolveManagerKeyFromInstanceId(args.ideType);
6199
- if (mappedKey) {
6200
- return mappedKey;
6201
- }
6228
+ const target = this._ctx.sessionRegistry?.get(args.ideType);
6229
+ if (target?.cdpManagerKey) return target.cdpManagerKey;
6202
6230
  if (this._ctx.cdpManagers.has(args.ideType)) {
6203
6231
  return args.ideType;
6204
6232
  }
@@ -6209,34 +6237,6 @@ var DaemonCommandHandler = class {
6209
6237
  }
6210
6238
  }
6211
6239
  }
6212
- if (args?._targetInstance) {
6213
- let raw = args._targetInstance;
6214
- const ideMatch = raw.match(/:ide:(.+)$/);
6215
- const cliMatch = raw.match(/:cli:(.+)$/);
6216
- const acpMatch = raw.match(/:acp:(.+)$/);
6217
- if (ideMatch) raw = ideMatch[1];
6218
- else if (cliMatch) raw = cliMatch[1];
6219
- else if (acpMatch) raw = acpMatch[1];
6220
- const mappedKey = this.resolveManagerKeyFromInstanceId(raw);
6221
- if (mappedKey) {
6222
- return mappedKey;
6223
- }
6224
- if (this._ctx.cdpManagers.has(raw)) {
6225
- return raw;
6226
- }
6227
- const found = findCdpManager(this._ctx.cdpManagers, raw);
6228
- if (found) {
6229
- for (const [k, m] of this._ctx.cdpManagers.entries()) {
6230
- if (m === found) return k;
6231
- }
6232
- }
6233
- const lastUnderscore = raw.lastIndexOf("_");
6234
- if (lastUnderscore > 0) {
6235
- const stripped = raw.substring(0, lastUnderscore);
6236
- if (this._ctx.cdpManagers.has(stripped)) return stripped;
6237
- }
6238
- return raw;
6239
- }
6240
6240
  return void 0;
6241
6241
  }
6242
6242
  setAgentStreamManager(manager) {
@@ -6244,12 +6244,11 @@ var DaemonCommandHandler = class {
6244
6244
  }
6245
6245
  // ─── Command Dispatcher ──────────────────────────
6246
6246
  async handle(cmd, args) {
6247
- this._currentIdeType = this.extractIdeType(args);
6248
- this._currentProviderType = args?.agentType || args?.providerType || this._currentIdeType;
6249
- if (!this._currentIdeType && !this._currentProviderType) {
6247
+ this._currentRoute = this.resolveRoute(args);
6248
+ if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
6250
6249
  const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
6251
6250
  if (cdpCommands.includes(cmd)) {
6252
- return { success: false, error: "No ideType specified \u2014 cannot route command" };
6251
+ return { success: false, error: "No targetSessionId specified \u2014 cannot route command" };
6253
6252
  }
6254
6253
  }
6255
6254
  try {
@@ -6340,22 +6339,8 @@ var DaemonCommandHandler = class {
6340
6339
  case "refresh_scripts":
6341
6340
  return this.handleRefreshScripts(args);
6342
6341
  // ─── Stream commands (stream-commands.ts) ───────────
6343
- case "agent_stream_switch":
6344
- return handleAgentStreamSwitch(this, args);
6345
- case "agent_stream_read":
6346
- return handleAgentStreamRead(this, args);
6347
- case "agent_stream_send":
6348
- return handleAgentStreamSend(this, args);
6349
- case "agent_stream_resolve":
6350
- return handleAgentStreamResolve(this, args);
6351
- case "agent_stream_new":
6352
- return handleAgentStreamNew(this, args);
6353
- case "agent_stream_list_chats":
6354
- return handleAgentStreamListChats(this, args);
6355
- case "agent_stream_switch_session":
6356
- return handleAgentStreamSwitchSession(this, args);
6357
- case "agent_stream_focus":
6358
- return handleAgentStreamFocus(this, args);
6342
+ case "focus_session":
6343
+ return handleFocusSession(this, args);
6359
6344
  // ─── PTY Raw I/O (stream-commands.ts) ─────────
6360
6345
  case "pty_input":
6361
6346
  return handlePtyInput(this, args);
@@ -8003,8 +7988,7 @@ var CHAT_COMMANDS = [
8003
7988
  "new_chat",
8004
7989
  "switch_chat",
8005
7990
  "set_mode",
8006
- "change_model",
8007
- "agent_stream_send"
7991
+ "change_model"
8008
7992
  ];
8009
7993
  var DaemonCommandRouter = class {
8010
7994
  deps;
@@ -8242,6 +8226,7 @@ var DaemonCommandRouter = class {
8242
8226
  } catch {
8243
8227
  }
8244
8228
  this.deps.cdpManagers.delete(key);
8229
+ this.deps.sessionRegistry.unregisterByManagerKey(key);
8245
8230
  LOG.info("StopIDE", `CDP disconnected: ${key}`);
8246
8231
  }
8247
8232
  }
@@ -8254,14 +8239,6 @@ var DaemonCommandRouter = class {
8254
8239
  for (const instanceKey of keysToRemove) {
8255
8240
  const ideInstance = this.deps.instanceManager.getInstance(instanceKey);
8256
8241
  if (ideInstance) {
8257
- if (ideInstance.getInstanceId) {
8258
- this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
8259
- }
8260
- if (ideInstance.getExtensionInstances) {
8261
- for (const ext of ideInstance.getExtensionInstances()) {
8262
- if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
8263
- }
8264
- }
8265
8242
  this.deps.instanceManager.removeInstance(instanceKey);
8266
8243
  LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
8267
8244
  }
@@ -8270,14 +8247,6 @@ var DaemonCommandRouter = class {
8270
8247
  const instanceKey = `ide:${ideType}`;
8271
8248
  const ideInstance = this.deps.instanceManager.getInstance(instanceKey);
8272
8249
  if (ideInstance) {
8273
- if (ideInstance.getInstanceId) {
8274
- this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
8275
- }
8276
- if (ideInstance.getExtensionInstances) {
8277
- for (const ext of ideInstance.getExtensionInstances()) {
8278
- if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
8279
- }
8280
- }
8281
8250
  this.deps.instanceManager.removeInstance(instanceKey);
8282
8251
  LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
8283
8252
  }
@@ -8330,15 +8299,9 @@ function buildStatusSnapshot(options) {
8330
8299
  const cfg = loadConfig();
8331
8300
  const wsState = getWorkspaceState(cfg);
8332
8301
  const memSnap = getHostMemorySnapshot();
8333
- const { managedIdes, managedClis, managedAcps } = buildAllManagedEntries(
8302
+ const sessions = buildSessionEntries(
8334
8303
  options.allStates,
8335
- options.cdpManagers,
8336
- {
8337
- detectedIdes: options.detectedIdes.map((ide) => ({
8338
- id: ide.id,
8339
- installed: ide.installed !== false
8340
- }))
8341
- }
8304
+ options.cdpManagers
8342
8305
  );
8343
8306
  return {
8344
8307
  instanceId: options.instanceId,
@@ -8360,9 +8323,7 @@ function buildStatusSnapshot(options) {
8360
8323
  timestamp: options.timestamp ?? Date.now(),
8361
8324
  detectedIdes: buildDetectedIdeInfos(options.detectedIdes, options.cdpManagers),
8362
8325
  ...options.p2p ? { p2p: options.p2p } : {},
8363
- managedIdes,
8364
- managedClis,
8365
- managedAcps,
8326
+ sessions,
8366
8327
  workspaces: wsState.workspaces,
8367
8328
  defaultWorkspaceId: wsState.defaultWorkspaceId,
8368
8329
  defaultWorkspacePath: wsState.defaultWorkspacePath,
@@ -8474,7 +8435,7 @@ var DaemonStatusReporter = class {
8474
8435
  LOG.info("StatusReport", `\u2192${target} ${baseSummary}`);
8475
8436
  }
8476
8437
  }
8477
- const { managedIdes, managedClis, managedAcps } = buildAllManagedEntries(
8438
+ const sessions = buildSessionEntries(
8478
8439
  allStates,
8479
8440
  this.deps.cdpManagers
8480
8441
  );
@@ -8505,23 +8466,20 @@ var DaemonStatusReporter = class {
8505
8466
  if (opts?.p2pOnly) return;
8506
8467
  const wsPayload = {
8507
8468
  daemonMode: true,
8508
- // managedIdes: server only saves id, type, cdpConnected
8509
- managedIdes: managedIdes.map((ide) => ({
8510
- ideType: ide.ideType,
8511
- instanceId: ide.instanceId,
8512
- cdpConnected: ide.cdpConnected
8513
- })),
8514
- // managedClis: server only saves id, type, name
8515
- managedClis: managedClis.map((c) => ({
8516
- id: c.id,
8517
- cliType: c.cliType,
8518
- cliName: c.cliName
8519
- })),
8520
- // managedAcps: server only saves id, type, name
8521
- managedAcps: managedAcps?.map((a) => ({
8522
- id: a.id,
8523
- acpType: a.acpType,
8524
- acpName: a.acpName
8469
+ sessions: sessions.map((session) => ({
8470
+ id: session.id,
8471
+ parentId: session.parentId,
8472
+ providerType: session.providerType,
8473
+ providerName: session.providerName,
8474
+ kind: session.kind,
8475
+ transport: session.transport,
8476
+ status: session.status,
8477
+ workspace: session.workspace,
8478
+ title: session.title,
8479
+ cdpConnected: session.cdpConnected,
8480
+ currentModel: session.currentModel,
8481
+ currentPlan: session.currentPlan,
8482
+ currentAutoApprove: session.currentAutoApprove
8525
8483
  })),
8526
8484
  p2p: payload.p2p,
8527
8485
  timestamp: now
@@ -8952,6 +8910,9 @@ var AcpProviderInstance = class {
8952
8910
  );
8953
8911
  }
8954
8912
  }
8913
+ getInstanceId() {
8914
+ return this.instanceId;
8915
+ }
8955
8916
  // ─── ACP Config Options & Modes ─────────────────────
8956
8917
  parseConfigOptions(raw) {
8957
8918
  if (!Array.isArray(raw)) return;
@@ -9731,6 +9692,7 @@ var DaemonCliManager = class {
9731
9692
  const normalizedType = this.providerLoader.resolveAlias(cliType);
9732
9693
  const provider = this.providerLoader.getByAlias(cliType);
9733
9694
  const key = crypto4.randomUUID();
9695
+ const sessionRegistry = this.deps.getSessionRegistry?.() || null;
9734
9696
  if (provider && provider.category === "acp") {
9735
9697
  const instanceManager2 = this.deps.getInstanceManager();
9736
9698
  if (!instanceManager2) throw new Error("InstanceManager not available");
@@ -9754,6 +9716,16 @@ ${installInfo}`
9754
9716
  await instanceManager2.addInstance(key, acpInstance, {
9755
9717
  settings: this.providerLoader.getSettings(normalizedType)
9756
9718
  });
9719
+ const sessionId = acpInstance.getInstanceId();
9720
+ sessionRegistry?.register({
9721
+ sessionId,
9722
+ parentSessionId: null,
9723
+ providerType: normalizedType,
9724
+ providerCategory: "acp",
9725
+ transport: "acp",
9726
+ adapterKey: key,
9727
+ instanceKey: key
9728
+ });
9757
9729
  this.adapters.set(key, {
9758
9730
  cliType: normalizedType,
9759
9731
  workingDir: resolvedDir,
@@ -9811,9 +9783,18 @@ ${installInfo}`
9811
9783
  serverConn: this.deps.getServerConn(),
9812
9784
  settings: {},
9813
9785
  onPtyData: (data) => {
9814
- this.deps.getP2p()?.broadcastPtyOutput(key, data);
9786
+ this.deps.getP2p()?.broadcastPtyOutput(cliInstance.instanceId, data);
9815
9787
  }
9816
9788
  });
9789
+ sessionRegistry?.register({
9790
+ sessionId: cliInstance.instanceId,
9791
+ parentSessionId: null,
9792
+ providerType: normalizedType,
9793
+ providerCategory: "cli",
9794
+ transport: "pty",
9795
+ adapterKey: key,
9796
+ instanceKey: key
9797
+ });
9817
9798
  } catch (spawnErr) {
9818
9799
  LOG.error("CLI", `[${cliType}] Spawn failed: ${spawnErr?.message}`);
9819
9800
  instanceManager.removeInstance(key);
@@ -9835,6 +9816,7 @@ ${installInfo}`
9835
9816
  if (this.adapters.has(key)) {
9836
9817
  this.adapters.delete(key);
9837
9818
  this.deps.removeAgentTracking(key);
9819
+ sessionRegistry?.unregisterByInstanceKey(key);
9838
9820
  instanceManager.removeInstance(key);
9839
9821
  LOG.info("CLI", `\u{1F9F9} Auto-cleaned ${status.status} CLI: ${cliType}`);
9840
9822
  this.deps.onStatusChange();
@@ -9895,12 +9877,14 @@ ${installInfo}`
9895
9877
  }
9896
9878
  this.adapters.delete(key);
9897
9879
  this.deps.removeAgentTracking(key);
9880
+ this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
9898
9881
  this.deps.getInstanceManager()?.removeInstance(key);
9899
9882
  LOG.info("CLI", `\u{1F6D1} Agent stopped: ${adapter.cliType} in ${adapter.workingDir}`);
9900
9883
  this.deps.onStatusChange();
9901
9884
  } else {
9902
9885
  const im = this.deps.getInstanceManager();
9903
9886
  if (im) {
9887
+ this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
9904
9888
  im.removeInstance(key);
9905
9889
  this.deps.removeAgentTracking(key);
9906
9890
  LOG.warn("CLI", `\u{1F9F9} Force-removed orphan entry: ${key}`);
@@ -9915,7 +9899,7 @@ ${installInfo}`
9915
9899
  // ─── Adapter search ─────────────────────────────
9916
9900
  /**
9917
9901
  * Search for CLI adapter. Priority order:
9918
- * 0. instanceKey (UUID direct match) — extracted from _targetInstance / composite ID
9902
+ * 0. sessionId (UUID direct match)
9919
9903
  * 1. agentType + dir (iteration match)
9920
9904
  * 2. agentType fuzzy match (⚠ returns first match when multiple sessions exist)
9921
9905
  */
@@ -9983,7 +9967,7 @@ ${installInfo}`
9983
9967
  const cliType = args?.cliType;
9984
9968
  const dir = args?.dir || "";
9985
9969
  if (!cliType) throw new Error("cliType required");
9986
- const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
9970
+ const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
9987
9971
  if (found) {
9988
9972
  await this.stopSession(found.key);
9989
9973
  } else {
@@ -10015,7 +9999,7 @@ ${installInfo}`
10015
9999
  }
10016
10000
  const dir = rdir.path;
10017
10001
  if (!cliType) throw new Error("cliType required");
10018
- const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
10002
+ const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
10019
10003
  if (found) await this.stopSession(found.key);
10020
10004
  await this.startSession(cliType, dir);
10021
10005
  this.persistRecentDir(cliType, dir);
@@ -10027,7 +10011,7 @@ ${installInfo}`
10027
10011
  if (!agentType || !action) throw new Error("agentType and action required");
10028
10012
  const found = this.findAdapter(agentType, {
10029
10013
  dir: args?.dir,
10030
- instanceKey: args?._targetInstance
10014
+ instanceKey: args?.targetSessionId
10031
10015
  });
10032
10016
  if (!found) throw new Error(`CLI agent not running: ${agentType}`);
10033
10017
  const { adapter, key } = found;
@@ -10173,14 +10157,8 @@ var ProviderStreamAdapter = class {
10173
10157
  // src/agent-stream/manager.ts
10174
10158
  init_logger();
10175
10159
  var DaemonAgentStreamManager = class {
10176
- allAdapters = [];
10177
- managedByScope = /* @__PURE__ */ new Map();
10178
- enabled = true;
10179
- logFn;
10180
- lastDiscoveryTimeByScope = /* @__PURE__ */ new Map();
10181
- discoveryIntervalMsByScope = /* @__PURE__ */ new Map();
10182
- activeAgentTypeByScope = /* @__PURE__ */ new Map();
10183
- constructor(logFn, providerLoader) {
10160
+ constructor(logFn, providerLoader, sessionRegistry) {
10161
+ this.sessionRegistry = sessionRegistry;
10184
10162
  this.logFn = logFn || LOG.forComponent("AgentStream").asLogFn();
10185
10163
  if (providerLoader) {
10186
10164
  const allExtProviders = providerLoader.getByCategory("extension");
@@ -10188,257 +10166,278 @@ var DaemonAgentStreamManager = class {
10188
10166
  const resolved = providerLoader.resolve(p.type);
10189
10167
  if (!resolved) continue;
10190
10168
  const adapter = new ProviderStreamAdapter(resolved);
10191
- this.allAdapters.push(adapter);
10169
+ this.adaptersByType.set(p.type, adapter);
10192
10170
  this.logFn(`[AgentStream] Adapter created: ${p.type} (${p.name}) scripts=${Object.keys(resolved.scripts || {}).join(",") || "none"}`);
10193
10171
  }
10194
10172
  }
10195
10173
  }
10174
+ adaptersByType = /* @__PURE__ */ new Map();
10175
+ managedBySessionId = /* @__PURE__ */ new Map();
10176
+ enabled = true;
10177
+ logFn;
10178
+ lastDiscoveryTimeByParent = /* @__PURE__ */ new Map();
10179
+ discoveryIntervalMsByParent = /* @__PURE__ */ new Map();
10180
+ activeSessionIdByParent = /* @__PURE__ */ new Map();
10196
10181
  setEnabled(enabled) {
10197
10182
  this.enabled = enabled;
10198
10183
  }
10199
10184
  get isEnabled() {
10200
10185
  return this.enabled;
10201
10186
  }
10202
- getActiveAgentType(scopeKey) {
10203
- return this.activeAgentTypeByScope.get(scopeKey) || null;
10187
+ getActiveSessionId(parentSessionId) {
10188
+ return this.activeSessionIdByParent.get(parentSessionId) || null;
10204
10189
  }
10205
- getManagedScope(scopeKey) {
10206
- let managed = this.managedByScope.get(scopeKey);
10207
- if (!managed) {
10208
- managed = /* @__PURE__ */ new Map();
10209
- this.managedByScope.set(scopeKey, managed);
10210
- }
10211
- return managed;
10190
+ getSessionTarget(sessionId) {
10191
+ return this.sessionRegistry?.get(sessionId);
10212
10192
  }
10213
- resetScope(scopeKey) {
10214
- this.managedByScope.delete(scopeKey);
10215
- this.activeAgentTypeByScope.delete(scopeKey);
10216
- this.lastDiscoveryTimeByScope.delete(scopeKey);
10217
- this.discoveryIntervalMsByScope.delete(scopeKey);
10193
+ resetParentSession(parentSessionId) {
10194
+ const activeSessionId = this.activeSessionIdByParent.get(parentSessionId);
10195
+ if (activeSessionId) this.managedBySessionId.delete(activeSessionId);
10196
+ for (const child of this.sessionRegistry?.listChildren(parentSessionId) || []) {
10197
+ this.managedBySessionId.delete(child.sessionId);
10198
+ }
10199
+ this.activeSessionIdByParent.delete(parentSessionId);
10200
+ this.lastDiscoveryTimeByParent.delete(parentSessionId);
10201
+ this.discoveryIntervalMsByParent.delete(parentSessionId);
10218
10202
  }
10219
10203
  /** Panel focus based on provider.js focusPanel or extensionId (currently no-op) */
10220
- async ensureAgentPanelOpen(agentType, targetIdeType) {
10221
- }
10222
- async switchActiveAgent(cdp, scopeKey, agentType) {
10223
- const managed = this.getManagedScope(scopeKey);
10224
- const previousAgentType = this.getActiveAgentType(scopeKey);
10225
- if (previousAgentType === agentType) return;
10226
- if (previousAgentType) {
10227
- const prev = managed.get(previousAgentType);
10204
+ async ensureSessionPanelOpen(_sessionId) {
10205
+ }
10206
+ async setActiveSession(cdp, parentSessionId, sessionId) {
10207
+ const previousSessionId = this.getActiveSessionId(parentSessionId);
10208
+ if (previousSessionId === sessionId) return;
10209
+ if (previousSessionId) {
10210
+ const prev = this.managedBySessionId.get(previousSessionId);
10228
10211
  if (prev) {
10229
10212
  try {
10230
- await cdp.detachAgent(prev.sessionId);
10213
+ await cdp.detachAgent(prev.cdpSessionId);
10231
10214
  } catch {
10232
10215
  }
10233
- managed.delete(previousAgentType);
10234
- this.logFn(`[AgentStream] Deactivated: ${prev.adapter.agentName} (${scopeKey})`);
10235
- }
10236
- }
10237
- this.activeAgentTypeByScope.set(scopeKey, agentType);
10238
- this.lastDiscoveryTimeByScope.set(scopeKey, 0);
10239
- if (!agentType && managed.size === 0) {
10240
- this.managedByScope.delete(scopeKey);
10241
- }
10242
- this.logFn(`[AgentStream] Active agent (${scopeKey}): ${agentType || "none"}`);
10216
+ this.managedBySessionId.delete(previousSessionId);
10217
+ this.logFn(`[AgentStream] Deactivated: ${prev.adapter.agentName} (${parentSessionId})`);
10218
+ }
10219
+ }
10220
+ this.activeSessionIdByParent.set(parentSessionId, sessionId);
10221
+ this.lastDiscoveryTimeByParent.set(parentSessionId, 0);
10222
+ this.logFn(`[AgentStream] Active session (${parentSessionId}): ${sessionId || "none"}`);
10223
+ }
10224
+ resolveSessionIdForTarget(parentSessionId, agentType) {
10225
+ const child = (this.sessionRegistry?.listChildren(parentSessionId) || []).find((entry) => entry.providerCategory === "extension" && entry.providerType === agentType);
10226
+ return child?.sessionId || null;
10227
+ }
10228
+ async connectManagedSession(cdp, parentSessionId, runtimeSessionId) {
10229
+ const target = this.getSessionTarget(runtimeSessionId);
10230
+ if (!target || target.providerCategory !== "extension") return null;
10231
+ const adapter = this.adaptersByType.get(target.providerType);
10232
+ if (!adapter) return null;
10233
+ const targets = await cdp.discoverAgentWebviews();
10234
+ const activeTarget = targets.find((entry) => entry.agentType === target.providerType);
10235
+ if (!activeTarget) return null;
10236
+ const cdpSessionId = await cdp.attachToAgent(activeTarget);
10237
+ if (!cdpSessionId) return null;
10238
+ const managed = {
10239
+ adapter,
10240
+ runtimeSessionId,
10241
+ parentSessionId,
10242
+ cdpSessionId,
10243
+ target: activeTarget,
10244
+ lastState: null,
10245
+ lastError: null,
10246
+ lastHiddenCheckTime: 0
10247
+ };
10248
+ this.managedBySessionId.set(runtimeSessionId, managed);
10249
+ this.logFn(`[AgentStream] Connected: ${adapter.agentName} (${parentSessionId})`);
10250
+ return managed;
10243
10251
  }
10244
10252
  /** Agent webview discovery + session connection */
10245
- async syncAgentSessions(cdp, scopeKey) {
10246
- const activeAgentType = this.getActiveAgentType(scopeKey);
10247
- if (!this.enabled || !activeAgentType) return;
10253
+ async syncActiveSession(cdp, parentSessionId) {
10254
+ const activeSessionId = this.getActiveSessionId(parentSessionId);
10255
+ if (!this.enabled || !activeSessionId) return;
10248
10256
  const now = Date.now();
10249
- const managed = this.getManagedScope(scopeKey);
10250
- const lastDiscoveryTime = this.lastDiscoveryTimeByScope.get(scopeKey) || 0;
10251
- const discoveryIntervalMs = this.discoveryIntervalMsByScope.get(scopeKey) || 1e4;
10252
- if (managed.has(activeAgentType) && now - lastDiscoveryTime < discoveryIntervalMs) {
10257
+ const managed = this.managedBySessionId.get(activeSessionId);
10258
+ const lastDiscoveryTime = this.lastDiscoveryTimeByParent.get(parentSessionId) || 0;
10259
+ const discoveryIntervalMs = this.discoveryIntervalMsByParent.get(parentSessionId) || 1e4;
10260
+ if (managed && now - lastDiscoveryTime < discoveryIntervalMs) {
10253
10261
  return;
10254
10262
  }
10255
- this.lastDiscoveryTimeByScope.set(scopeKey, now);
10263
+ this.lastDiscoveryTimeByParent.set(parentSessionId, now);
10256
10264
  try {
10257
- const targets = await cdp.discoverAgentWebviews();
10258
- const activeTarget = targets.find((t) => t.agentType === activeAgentType);
10259
- if (activeTarget && !managed.has(activeAgentType)) {
10260
- const adapter = this.allAdapters.find((a) => a.agentType === activeAgentType);
10261
- if (adapter) {
10262
- const sessionId = await cdp.attachToAgent(activeTarget);
10263
- if (sessionId) {
10264
- managed.set(activeAgentType, {
10265
- adapter,
10266
- sessionId,
10267
- target: activeTarget,
10268
- lastState: null,
10269
- lastError: null,
10270
- lastHiddenCheckTime: 0
10271
- });
10272
- this.logFn(`[AgentStream] Connected: ${adapter.agentName} (${scopeKey})`);
10273
- }
10274
- }
10265
+ if (!managed) {
10266
+ await this.connectManagedSession(cdp, parentSessionId, activeSessionId);
10275
10267
  }
10276
- for (const [type, agent] of managed) {
10277
- if (type !== activeAgentType) {
10278
- await cdp.detachAgent(agent.sessionId);
10279
- managed.delete(type);
10280
- }
10268
+ this.discoveryIntervalMsByParent.set(parentSessionId, this.managedBySessionId.has(activeSessionId) ? 3e4 : 1e4);
10269
+ } catch (e) {
10270
+ this.logFn(`[AgentStream] sync error (${parentSessionId}): ${e.message}`);
10271
+ }
10272
+ }
10273
+ /** Collect active extension session state */
10274
+ async collectActiveSession(cdp, parentSessionId) {
10275
+ if (!this.enabled) return null;
10276
+ const activeSessionId = this.getActiveSessionId(parentSessionId);
10277
+ if (!activeSessionId) return null;
10278
+ let agent = this.managedBySessionId.get(activeSessionId);
10279
+ if (!agent) {
10280
+ agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
10281
+ }
10282
+ if (!agent) return null;
10283
+ const type = agent.adapter.agentType;
10284
+ const isHidden = agent.lastState?.status === "panel_hidden";
10285
+ const hiddenCacheFresh = isHidden && Date.now() - agent.lastHiddenCheckTime < 3e4;
10286
+ if (hiddenCacheFresh) return agent.lastState;
10287
+ try {
10288
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10289
+ const state = await agent.adapter.readChat(evaluate);
10290
+ 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") : ""}`);
10291
+ agent.lastState = state;
10292
+ agent.lastError = null;
10293
+ if (state.status === "panel_hidden") {
10294
+ agent.lastHiddenCheckTime = Date.now();
10281
10295
  }
10282
- this.discoveryIntervalMsByScope.set(scopeKey, managed.has(activeAgentType) ? 3e4 : 1e4);
10296
+ return state;
10283
10297
  } catch (e) {
10284
- this.logFn(`[AgentStream] sync error (${scopeKey}): ${e.message}`);
10285
- }
10286
- }
10287
- /** Collect active agent status */
10288
- async collectAgentStreams(cdp, scopeKey) {
10289
- if (!this.enabled) return [];
10290
- const results = [];
10291
- const activeAgentType = this.getActiveAgentType(scopeKey);
10292
- const managed = this.managedByScope.get(scopeKey);
10293
- if (activeAgentType && managed?.has(activeAgentType)) {
10294
- const agent = managed.get(activeAgentType);
10295
- const type = activeAgentType;
10296
- const isHidden = agent.lastState?.status === "panel_hidden";
10297
- const hiddenCacheFresh = isHidden && Date.now() - agent.lastHiddenCheckTime < 3e4;
10298
- if (hiddenCacheFresh) {
10299
- results.push(agent.lastState);
10300
- } else {
10298
+ const errorMsg = e?.message || String(e);
10299
+ this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
10300
+ agent.lastError = errorMsg;
10301
+ if (errorMsg.includes("timeout") || errorMsg.includes("not connected") || errorMsg.includes("Session")) {
10301
10302
  try {
10302
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10303
- const state = await agent.adapter.readChat(evaluate);
10304
- 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") : ""}`);
10305
- agent.lastState = state;
10306
- agent.lastError = null;
10307
- if (state.status === "panel_hidden") {
10308
- agent.lastHiddenCheckTime = Date.now();
10309
- }
10310
- results.push(state);
10311
- } catch (e) {
10312
- const errorMsg = e?.message || String(e);
10313
- this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
10314
- agent.lastError = errorMsg;
10315
- results.push({
10316
- agentType: type,
10317
- agentName: agent.adapter.agentName,
10318
- extensionId: agent.adapter.extensionId,
10319
- status: "disconnected",
10320
- messages: agent.lastState?.messages || [],
10321
- inputContent: ""
10322
- });
10323
- if (errorMsg.includes("timeout") || errorMsg.includes("not connected") || errorMsg.includes("Session")) {
10324
- try {
10325
- await cdp.detachAgent(agent.sessionId);
10326
- } catch {
10327
- }
10328
- managed.delete(type);
10329
- this.lastDiscoveryTimeByScope.set(scopeKey, 0);
10330
- }
10303
+ await cdp.detachAgent(agent.cdpSessionId);
10304
+ } catch {
10331
10305
  }
10306
+ this.managedBySessionId.delete(activeSessionId);
10307
+ this.lastDiscoveryTimeByParent.set(parentSessionId, 0);
10332
10308
  }
10309
+ return {
10310
+ agentType: type,
10311
+ agentName: agent.adapter.agentName,
10312
+ extensionId: agent.adapter.extensionId,
10313
+ status: "disconnected",
10314
+ messages: agent.lastState?.messages || [],
10315
+ inputContent: ""
10316
+ };
10333
10317
  }
10334
- return results;
10335
10318
  }
10336
- async sendToAgent(cdp, scopeKey, agentType, text, targetIdeType) {
10337
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10338
- const agent = this.getManagedAgent(agentType, scopeKey);
10319
+ async sendToSession(cdp, sessionId, text) {
10320
+ await this.ensureSessionPanelOpen(sessionId);
10321
+ const target = this.getSessionTarget(sessionId);
10322
+ if (!target?.parentSessionId) return false;
10323
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10324
+ await this.syncActiveSession(cdp, target.parentSessionId);
10325
+ const agent = this.managedBySessionId.get(sessionId);
10339
10326
  if (!agent) return false;
10340
10327
  try {
10341
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10328
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10342
10329
  await agent.adapter.sendMessage(evaluate, text);
10343
10330
  return true;
10344
10331
  } catch (e) {
10345
- this.logFn(`[AgentStream] sendToAgent(${agentType}) error: ${e.message}`);
10332
+ this.logFn(`[AgentStream] sendToSession(${sessionId}) error: ${e.message}`);
10346
10333
  return false;
10347
10334
  }
10348
10335
  }
10349
- async resolveAgentAction(cdp, scopeKey, agentType, action, targetIdeType) {
10350
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10351
- const agent = this.getManagedAgent(agentType, scopeKey);
10336
+ async resolveSessionAction(cdp, sessionId, action) {
10337
+ await this.ensureSessionPanelOpen(sessionId);
10338
+ const target = this.getSessionTarget(sessionId);
10339
+ if (!target?.parentSessionId) return false;
10340
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10341
+ await this.syncActiveSession(cdp, target.parentSessionId);
10342
+ const agent = this.managedBySessionId.get(sessionId);
10352
10343
  if (!agent) return false;
10353
10344
  try {
10354
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10345
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10355
10346
  return await agent.adapter.resolveAction(evaluate, action);
10356
10347
  } catch (e) {
10357
- this.logFn(`[AgentStream] resolveAction(${agentType}) error: ${e.message}`);
10348
+ this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
10358
10349
  return false;
10359
10350
  }
10360
10351
  }
10361
- async newAgentSession(cdp, scopeKey, agentType, targetIdeType) {
10362
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10363
- const agent = this.getManagedAgent(agentType, scopeKey);
10352
+ async newSession(cdp, sessionId) {
10353
+ await this.ensureSessionPanelOpen(sessionId);
10354
+ const target = this.getSessionTarget(sessionId);
10355
+ if (!target?.parentSessionId) return false;
10356
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10357
+ await this.syncActiveSession(cdp, target.parentSessionId);
10358
+ const agent = this.managedBySessionId.get(sessionId);
10364
10359
  if (!agent) return false;
10365
10360
  try {
10366
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10361
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10367
10362
  await agent.adapter.newSession(evaluate);
10368
10363
  return true;
10369
10364
  } catch (e) {
10370
- this.logFn(`[AgentStream] newSession(${agentType}) error: ${e.message}`);
10365
+ this.logFn(`[AgentStream] newSession(${sessionId}) error: ${e.message}`);
10371
10366
  return false;
10372
10367
  }
10373
10368
  }
10374
- async listAgentChats(cdp, scopeKey, agentType) {
10375
- let agent = this.getManagedAgent(agentType, scopeKey);
10376
- if (!agent) {
10377
- this.logFn(`[AgentStream] listChats: ${agentType} not managed in ${scopeKey}, trying on-demand activation`);
10378
- await this.switchActiveAgent(cdp, scopeKey, agentType);
10379
- await this.syncAgentSessions(cdp, scopeKey);
10380
- agent = this.getManagedAgent(agentType, scopeKey);
10381
- }
10369
+ async listSessionChats(cdp, sessionId) {
10370
+ const target = this.getSessionTarget(sessionId);
10371
+ if (!target?.parentSessionId) return [];
10372
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10373
+ await this.syncActiveSession(cdp, target.parentSessionId);
10374
+ const agent = this.managedBySessionId.get(sessionId);
10382
10375
  if (!agent || typeof agent.adapter.listChats !== "function") return [];
10383
10376
  try {
10384
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10377
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10385
10378
  return await agent.adapter.listChats(evaluate);
10386
10379
  } catch (e) {
10387
- this.logFn(`[AgentStream] listChats(${agentType}) error: ${e.message}`);
10380
+ this.logFn(`[AgentStream] listChats(${sessionId}) error: ${e.message}`);
10388
10381
  return [];
10389
10382
  }
10390
10383
  }
10391
- async switchAgentSession(cdp, scopeKey, agentType, sessionId) {
10392
- let agent = this.getManagedAgent(agentType, scopeKey);
10393
- if (!agent) {
10394
- this.logFn(`[AgentStream] switchSession: ${agentType} not managed in ${scopeKey}, trying on-demand activation`);
10395
- await this.switchActiveAgent(cdp, scopeKey, agentType);
10396
- await this.syncAgentSessions(cdp, scopeKey);
10397
- agent = this.getManagedAgent(agentType, scopeKey);
10398
- }
10384
+ async switchConversation(cdp, sessionId, conversationId) {
10385
+ const target = this.getSessionTarget(sessionId);
10386
+ if (!target?.parentSessionId) return false;
10387
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10388
+ await this.syncActiveSession(cdp, target.parentSessionId);
10389
+ const agent = this.managedBySessionId.get(sessionId);
10399
10390
  if (!agent || typeof agent.adapter.switchSession !== "function") return false;
10400
10391
  try {
10401
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10402
- return await agent.adapter.switchSession(evaluate, sessionId);
10392
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10393
+ return await agent.adapter.switchSession(evaluate, conversationId);
10403
10394
  } catch (e) {
10404
- this.logFn(`[AgentStream] switchSession(${agentType}) error: ${e.message}`);
10395
+ this.logFn(`[AgentStream] switchSession(${sessionId}) error: ${e.message}`);
10405
10396
  return false;
10406
10397
  }
10407
10398
  }
10408
- async focusAgentEditor(cdp, scopeKey, agentType) {
10409
- const agent = this.getManagedAgent(agentType, scopeKey);
10399
+ async focusSession(cdp, sessionId) {
10400
+ const target = this.getSessionTarget(sessionId);
10401
+ if (!target?.parentSessionId) return false;
10402
+ await this.setActiveSession(cdp, target.parentSessionId, sessionId);
10403
+ await this.syncActiveSession(cdp, target.parentSessionId);
10404
+ const agent = this.managedBySessionId.get(sessionId);
10410
10405
  if (!agent || typeof agent.adapter.focusEditor !== "function") return false;
10411
10406
  try {
10412
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10407
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10413
10408
  await agent.adapter.focusEditor(evaluate);
10414
10409
  return true;
10415
10410
  } catch (e) {
10416
- this.logFn(`[AgentStream] focusEditor(${agentType}) error: ${e.message}`);
10411
+ this.logFn(`[AgentStream] focusEditor(${sessionId}) error: ${e.message}`);
10417
10412
  return false;
10418
10413
  }
10419
10414
  }
10420
- getConnectedAgents(scopeKey) {
10421
- if (scopeKey) return Array.from((this.managedByScope.get(scopeKey) || /* @__PURE__ */ new Map()).keys());
10422
- return Array.from(this.managedByScope.values()).flatMap((scope) => Array.from(scope.keys()));
10415
+ getConnectedSessions(parentSessionId) {
10416
+ if (parentSessionId) {
10417
+ return [...this.managedBySessionId.values()].filter((entry) => entry.parentSessionId === parentSessionId).map((entry) => entry.runtimeSessionId);
10418
+ }
10419
+ return [...this.managedBySessionId.keys()];
10423
10420
  }
10424
- getManagedAgent(agentType, scopeKey) {
10425
- return this.managedByScope.get(scopeKey)?.get(agentType);
10421
+ getManagedSession(sessionId) {
10422
+ return this.managedBySessionId.get(sessionId);
10426
10423
  }
10427
10424
  async dispose(cdpManagers) {
10428
- for (const [scopeKey, managed] of this.managedByScope) {
10429
- const cdp = cdpManagers.get(scopeKey);
10425
+ for (const managed of this.managedBySessionId.values()) {
10426
+ const managerKey = this.getSessionTarget(managed.runtimeSessionId)?.cdpManagerKey;
10427
+ const cdp = managerKey ? cdpManagers.get(managerKey) : null;
10430
10428
  if (!cdp) continue;
10431
- for (const [, agent] of managed) {
10432
- try {
10433
- await cdp.detachAgent(agent.sessionId);
10434
- } catch {
10435
- }
10429
+ try {
10430
+ await cdp.detachAgent(managed.cdpSessionId);
10431
+ } catch {
10436
10432
  }
10437
10433
  }
10438
- this.managedByScope.clear();
10439
- this.activeAgentTypeByScope.clear();
10440
- this.lastDiscoveryTimeByScope.clear();
10441
- this.discoveryIntervalMsByScope.clear();
10434
+ this.managedBySessionId.clear();
10435
+ this.activeSessionIdByParent.clear();
10436
+ this.lastDiscoveryTimeByParent.clear();
10437
+ this.discoveryIntervalMsByParent.clear();
10438
+ }
10439
+ resolveSessionForAgent(parentSessionId, agentType) {
10440
+ return this.resolveSessionIdForTarget(parentSessionId, agentType);
10442
10441
  }
10443
10442
  };
10444
10443
 
@@ -10455,8 +10454,8 @@ var AgentStreamPoller = class {
10455
10454
  return null;
10456
10455
  }
10457
10456
  /** Reset active IDE tracking (e.g., when IDE is stopped) */
10458
- resetActiveIde(ideType) {
10459
- this.deps.agentStreamManager.resetScope(ideType);
10457
+ resetActiveIde(parentSessionId) {
10458
+ this.deps.agentStreamManager.resetParentSession(parentSessionId);
10460
10459
  }
10461
10460
  /** Start polling (idempotent — ignored if already started) */
10462
10461
  start(intervalMs = 5e3) {
@@ -10478,12 +10477,14 @@ var AgentStreamPoller = class {
10478
10477
  agentStreamManager,
10479
10478
  providerLoader,
10480
10479
  instanceManager,
10481
- cdpManagers
10480
+ cdpManagers,
10481
+ sessionRegistry
10482
10482
  } = this.deps;
10483
10483
  if (!agentStreamManager || cdpManagers.size === 0) return;
10484
10484
  for (const [ideType, cdp] of cdpManagers) {
10485
10485
  registerExtensionProviders(providerLoader, cdp, ideType);
10486
10486
  const ideInstance = instanceManager.getInstance(`ide:${ideType}`);
10487
+ const parentSessionId = ideInstance?.getInstanceId?.();
10487
10488
  if (ideInstance?.getExtensionTypes && ideInstance?.addExtension && ideInstance?.removeExtension) {
10488
10489
  const currentExtTypes = new Set(ideInstance.getExtensionTypes());
10489
10490
  const enabledExtTypes = new Set(
@@ -10491,6 +10492,10 @@ var AgentStreamPoller = class {
10491
10492
  );
10492
10493
  for (const extType of currentExtTypes) {
10493
10494
  if (!enabledExtTypes.has(extType)) {
10495
+ const extInstance = ideInstance.getExtension?.(extType);
10496
+ if (extInstance?.getInstanceId) {
10497
+ sessionRegistry.unregister(extInstance.getInstanceId());
10498
+ }
10494
10499
  ideInstance.removeExtension(extType);
10495
10500
  LOG.info("AgentStream", `Extension removed: ${extType} (disabled for ${ideType})`);
10496
10501
  }
@@ -10501,46 +10506,61 @@ var AgentStreamPoller = class {
10501
10506
  if (extProvider) {
10502
10507
  const extSettings = providerLoader.getSettings(extType);
10503
10508
  ideInstance.addExtension(extProvider, extSettings);
10509
+ const extInstance = ideInstance.getExtension?.(extType);
10510
+ if (parentSessionId && extInstance?.getInstanceId) {
10511
+ sessionRegistry.register({
10512
+ sessionId: extInstance.getInstanceId(),
10513
+ parentSessionId,
10514
+ providerType: extType,
10515
+ providerCategory: "extension",
10516
+ transport: "cdp-webview",
10517
+ cdpManagerKey: ideType,
10518
+ instanceKey: `ide:${ideType}`
10519
+ });
10520
+ }
10504
10521
  LOG.info("AgentStream", `Extension added: ${extType} (enabled for ${ideType})`);
10505
10522
  }
10506
10523
  }
10507
10524
  }
10508
10525
  }
10509
- const activeType = agentStreamManager.getActiveAgentType(ideType);
10510
- if (activeType) {
10511
- const enabledExtTypes = new Set(
10512
- providerLoader.getEnabledExtensionProviders(ideType).map((p) => p.type)
10513
- );
10514
- if (!enabledExtTypes.has(activeType)) {
10515
- LOG.info("AgentStream", `Active agent ${activeType} was disabled for ${ideType} \u2014 detaching`);
10516
- await agentStreamManager.switchActiveAgent(cdp, ideType, null);
10526
+ const activeSessionId = parentSessionId ? agentStreamManager.getActiveSessionId(parentSessionId) : null;
10527
+ if (activeSessionId) {
10528
+ const activeTarget = sessionRegistry.get(activeSessionId);
10529
+ const enabledExtTypes = new Set(providerLoader.getEnabledExtensionProviders(ideType).map((p) => p.type));
10530
+ if (!activeTarget || !enabledExtTypes.has(activeTarget.providerType)) {
10531
+ LOG.info("AgentStream", `Active agent ${activeTarget?.providerType || activeSessionId} was disabled for ${ideType} \u2014 detaching`);
10532
+ await agentStreamManager.setActiveSession(cdp, parentSessionId, null);
10517
10533
  this.deps.onStreamsUpdated?.(ideType, []);
10518
10534
  }
10519
10535
  }
10520
10536
  if (!cdp.isConnected) {
10521
- if (activeType) {
10522
- agentStreamManager.resetScope(ideType);
10537
+ if (parentSessionId && activeSessionId) {
10538
+ agentStreamManager.resetParentSession(parentSessionId);
10523
10539
  this.deps.onStreamsUpdated?.(ideType, []);
10524
10540
  }
10525
10541
  continue;
10526
10542
  }
10527
- let resolvedActiveType = activeType;
10528
- if (!resolvedActiveType) {
10543
+ let resolvedActiveSessionId = activeSessionId;
10544
+ if (!resolvedActiveSessionId && parentSessionId) {
10529
10545
  try {
10530
10546
  const discovered = await cdp.discoverAgentWebviews();
10531
- if (discovered.length > 0) {
10532
- resolvedActiveType = discovered[0].agentType;
10533
- await agentStreamManager.switchActiveAgent(cdp, ideType, resolvedActiveType);
10534
- LOG.info("AgentStream", `Auto-activated: ${resolvedActiveType} (${ideType})`);
10547
+ for (const target of discovered) {
10548
+ const sessionId = agentStreamManager.resolveSessionForAgent(parentSessionId, target.agentType);
10549
+ if (sessionId) {
10550
+ resolvedActiveSessionId = sessionId;
10551
+ await agentStreamManager.setActiveSession(cdp, parentSessionId, sessionId);
10552
+ LOG.info("AgentStream", `Auto-activated: ${target.agentType} (${ideType})`);
10553
+ break;
10554
+ }
10535
10555
  }
10536
10556
  } catch {
10537
10557
  }
10538
10558
  }
10539
- if (!resolvedActiveType) continue;
10559
+ if (!resolvedActiveSessionId || !parentSessionId) continue;
10540
10560
  try {
10541
- await agentStreamManager.syncAgentSessions(cdp, ideType);
10542
- const streams = await agentStreamManager.collectAgentStreams(cdp, ideType);
10543
- this.deps.onStreamsUpdated?.(ideType, streams);
10561
+ await agentStreamManager.syncActiveSession(cdp, parentSessionId);
10562
+ const stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
10563
+ this.deps.onStreamsUpdated?.(ideType, stream ? [stream] : []);
10544
10564
  } catch {
10545
10565
  }
10546
10566
  }
@@ -10629,6 +10649,7 @@ var ProviderInstanceManager = class {
10629
10649
  ...event,
10630
10650
  providerType: instance.type,
10631
10651
  instanceId: state.instanceId,
10652
+ targetSessionId: state.instanceId,
10632
10653
  providerCategory: state.category
10633
10654
  });
10634
10655
  }
@@ -14439,6 +14460,63 @@ function launchIDE(ide, workspacePath) {
14439
14460
  }
14440
14461
  }
14441
14462
 
14463
+ // src/sessions/registry.ts
14464
+ var SessionRegistry = class {
14465
+ bySessionId = /* @__PURE__ */ new Map();
14466
+ byManagerKey = /* @__PURE__ */ new Map();
14467
+ byInstanceKey = /* @__PURE__ */ new Map();
14468
+ byParentSessionId = /* @__PURE__ */ new Map();
14469
+ register(target) {
14470
+ this.unregister(target.sessionId);
14471
+ this.bySessionId.set(target.sessionId, target);
14472
+ if (target.cdpManagerKey) this.addIndex(this.byManagerKey, target.cdpManagerKey, target.sessionId);
14473
+ if (target.instanceKey) this.addIndex(this.byInstanceKey, target.instanceKey, target.sessionId);
14474
+ if (target.parentSessionId) this.addIndex(this.byParentSessionId, target.parentSessionId, target.sessionId);
14475
+ }
14476
+ get(sessionId) {
14477
+ if (!sessionId) return void 0;
14478
+ return this.bySessionId.get(sessionId);
14479
+ }
14480
+ unregister(sessionId) {
14481
+ if (!sessionId) return;
14482
+ const target = this.bySessionId.get(sessionId);
14483
+ if (!target) return;
14484
+ this.bySessionId.delete(sessionId);
14485
+ if (target.cdpManagerKey) this.removeIndex(this.byManagerKey, target.cdpManagerKey, sessionId);
14486
+ if (target.instanceKey) this.removeIndex(this.byInstanceKey, target.instanceKey, sessionId);
14487
+ if (target.parentSessionId) this.removeIndex(this.byParentSessionId, target.parentSessionId, sessionId);
14488
+ }
14489
+ unregisterByManagerKey(managerKey) {
14490
+ for (const sessionId of [...this.byManagerKey.get(managerKey) || []]) {
14491
+ this.unregister(sessionId);
14492
+ }
14493
+ }
14494
+ unregisterByInstanceKey(instanceKey) {
14495
+ for (const sessionId of [...this.byInstanceKey.get(instanceKey) || []]) {
14496
+ this.unregister(sessionId);
14497
+ }
14498
+ }
14499
+ listChildren(parentSessionId) {
14500
+ const ids = this.byParentSessionId.get(parentSessionId);
14501
+ if (!ids) return [];
14502
+ return [...ids].map((id) => this.bySessionId.get(id)).filter(Boolean);
14503
+ }
14504
+ addIndex(index, key, sessionId) {
14505
+ let set = index.get(key);
14506
+ if (!set) {
14507
+ set = /* @__PURE__ */ new Set();
14508
+ index.set(key, set);
14509
+ }
14510
+ set.add(sessionId);
14511
+ }
14512
+ removeIndex(index, key, sessionId) {
14513
+ const set = index.get(key);
14514
+ if (!set) return;
14515
+ set.delete(sessionId);
14516
+ if (set.size === 0) index.delete(key);
14517
+ }
14518
+ };
14519
+
14442
14520
  // src/boot/daemon-lifecycle.ts
14443
14521
  init_logger();
14444
14522
  init_config();
@@ -14478,11 +14556,14 @@ async function initDaemonComponents(config) {
14478
14556
  });
14479
14557
  const instanceManager = new ProviderInstanceManager();
14480
14558
  const cdpManagers = /* @__PURE__ */ new Map();
14481
- const instanceIdMap = /* @__PURE__ */ new Map();
14559
+ const sessionRegistry = new SessionRegistry();
14482
14560
  const detectedIdesRef = { value: [] };
14561
+ let agentStreamManager = null;
14562
+ let poller = null;
14483
14563
  const cliManager = new DaemonCliManager({
14484
14564
  ...config.cliManagerDeps,
14485
- getInstanceManager: () => instanceManager
14565
+ getInstanceManager: () => instanceManager,
14566
+ getSessionRegistry: () => sessionRegistry
14486
14567
  }, providerLoader);
14487
14568
  LOG.info("Init", "Detecting IDEs...");
14488
14569
  detectedIdesRef.value = await detectIDEs();
@@ -14492,7 +14573,7 @@ async function initDaemonComponents(config) {
14492
14573
  providerLoader,
14493
14574
  instanceManager,
14494
14575
  cdpManagers,
14495
- instanceIdMap
14576
+ sessionRegistry
14496
14577
  };
14497
14578
  const cdpInitializer = new DaemonCdpInitializer({
14498
14579
  providerLoader,
@@ -14501,6 +14582,19 @@ async function initDaemonComponents(config) {
14501
14582
  onConnected: async (ideType, manager, managerKey) => {
14502
14583
  await setupIdeInstance(cdpSetupContext, { ideType, manager, managerKey });
14503
14584
  await config.onCdpManagerSetup?.(ideType, manager, managerKey);
14585
+ },
14586
+ onDisconnected: async (_ideType, _manager, managerKey) => {
14587
+ sessionRegistry.unregisterByManagerKey(managerKey);
14588
+ const instanceKey = `ide:${managerKey}`;
14589
+ const ideInstance = instanceManager.getInstance(instanceKey);
14590
+ if (ideInstance) {
14591
+ instanceManager.removeInstance(instanceKey);
14592
+ LOG.info("CDP", `Instance removed after disconnect: ${instanceKey}`);
14593
+ }
14594
+ if (ideInstance?.getInstanceId) {
14595
+ agentStreamManager?.resetParentSession(ideInstance.getInstanceId());
14596
+ }
14597
+ config.onStatusChange?.();
14504
14598
  }
14505
14599
  });
14506
14600
  await cdpInitializer.connectAll(detectedIdesRef.value);
@@ -14512,14 +14606,14 @@ async function initDaemonComponents(config) {
14512
14606
  adapters: cliManager.adapters,
14513
14607
  providerLoader,
14514
14608
  instanceManager,
14515
- instanceIdMap
14609
+ sessionRegistry
14516
14610
  });
14517
- const agentStreamManager = new DaemonAgentStreamManager(
14611
+ agentStreamManager = new DaemonAgentStreamManager(
14518
14612
  LOG.forComponent("AgentStream").asLogFn(),
14519
- providerLoader
14613
+ providerLoader,
14614
+ sessionRegistry
14520
14615
  );
14521
14616
  commandHandler.setAgentStreamManager(agentStreamManager);
14522
- let poller;
14523
14617
  const router = new DaemonCommandRouter({
14524
14618
  commandHandler,
14525
14619
  cliManager,
@@ -14527,7 +14621,7 @@ async function initDaemonComponents(config) {
14527
14621
  providerLoader,
14528
14622
  instanceManager,
14529
14623
  detectedIdes: detectedIdesRef,
14530
- instanceIdMap,
14624
+ sessionRegistry,
14531
14625
  onCdpManagerCreated: async (ideType, manager) => {
14532
14626
  await setupIdeInstance(cdpSetupContext, { ideType, manager });
14533
14627
  await config.onCdpManagerSetup?.(ideType, manager, ideType);
@@ -14542,6 +14636,7 @@ async function initDaemonComponents(config) {
14542
14636
  providerLoader,
14543
14637
  instanceManager,
14544
14638
  cdpManagers,
14639
+ sessionRegistry,
14545
14640
  onStreamsUpdated: config.onStreamsUpdated
14546
14641
  });
14547
14642
  poller.start();
@@ -14556,7 +14651,7 @@ async function initDaemonComponents(config) {
14556
14651
  poller,
14557
14652
  cdpInitializer,
14558
14653
  cdpManagers,
14559
- instanceIdMap,
14654
+ sessionRegistry,
14560
14655
  detectedIdes: detectedIdesRef
14561
14656
  };
14562
14657
  }
@@ -14630,10 +14725,7 @@ async function shutdownDaemonComponents(components) {
14630
14725
  ProviderLoader,
14631
14726
  VersionArchive,
14632
14727
  addCliHistory,
14633
- buildAllManagedEntries,
14634
- buildManagedAcps,
14635
- buildManagedClis,
14636
- buildManagedIdes,
14728
+ buildSessionEntries,
14637
14729
  buildStatusSnapshot,
14638
14730
  connectCdpManager,
14639
14731
  detectAllVersions,