@adhdev/daemon-core 0.6.77 → 0.7.0

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,
@@ -1814,6 +1811,8 @@ __export(index_exports, {
1814
1811
  isCdpConnected: () => isCdpConnected,
1815
1812
  isExtensionInstalled: () => isExtensionInstalled,
1816
1813
  isIdeRunning: () => isIdeRunning,
1814
+ isManagedStatusWaiting: () => isManagedStatusWaiting,
1815
+ isManagedStatusWorking: () => isManagedStatusWorking,
1817
1816
  isSetupComplete: () => isSetupComplete,
1818
1817
  killIdeProcess: () => killIdeProcess,
1819
1818
  launchIDE: () => launchIDE,
@@ -1821,6 +1820,8 @@ __export(index_exports, {
1821
1820
  loadConfig: () => loadConfig,
1822
1821
  logCommand: () => logCommand,
1823
1822
  markSetupComplete: () => markSetupComplete,
1823
+ normalizeActiveChatData: () => normalizeActiveChatData,
1824
+ normalizeManagedStatus: () => normalizeManagedStatus,
1824
1825
  probeCdpPort: () => probeCdpPort,
1825
1826
  readChatHistory: () => readChatHistory,
1826
1827
  registerExtensionProviders: () => registerExtensionProviders,
@@ -2711,9 +2712,12 @@ var DaemonCdpManager = class {
2711
2712
  };
2712
2713
  try {
2713
2714
  const { targetInfos } = await sendWs("Target.getTargets");
2714
- const webviewIframes = (targetInfos || []).filter(
2715
- (t) => t.type === "iframe" && (t.url || "").includes("vscode-webview")
2716
- );
2715
+ const pageWebviewUrls = await this.getCurrentPageWebviewUrls();
2716
+ const webviewIframes = (targetInfos || []).filter((t) => {
2717
+ if (t.type !== "iframe" || !(t.url || "").includes("vscode-webview")) return false;
2718
+ if (pageWebviewUrls.size === 0) return true;
2719
+ return pageWebviewUrls.has(t.url || "");
2720
+ });
2717
2721
  if (webviewIframes.length === 0) {
2718
2722
  this.log("[CDP] evaluateInWebviewFrame: no webview iframes found");
2719
2723
  return null;
@@ -2800,6 +2804,7 @@ var DaemonCdpManager = class {
2800
2804
  const result = await this.sendInternal("Target.getTargets");
2801
2805
  allTargets = result?.targetInfos || [];
2802
2806
  }
2807
+ const pageWebviewUrls = await this.getCurrentPageWebviewUrls();
2803
2808
  const iframes = allTargets.filter((t) => t.type === "iframe");
2804
2809
  const typeMap = /* @__PURE__ */ new Map();
2805
2810
  for (const t of allTargets) {
@@ -2825,6 +2830,7 @@ var DaemonCdpManager = class {
2825
2830
  const url = target.url || "";
2826
2831
  const hasWebview = url.includes("vscode-webview");
2827
2832
  if (!hasWebview) continue;
2833
+ if (pageWebviewUrls.size > 0 && !pageWebviewUrls.has(url)) continue;
2828
2834
  for (const known of this.extensionProviders) {
2829
2835
  if (known.extensionIdPattern.test(url)) {
2830
2836
  agents.push({
@@ -2850,7 +2856,11 @@ var DaemonCdpManager = class {
2850
2856
  async attachToAgent(target) {
2851
2857
  if (!this.isConnected) return null;
2852
2858
  for (const [sid, t] of this.agentSessions) {
2853
- 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
+ }
2854
2864
  }
2855
2865
  try {
2856
2866
  const sendFn = this._browserConnected ? this.sendBrowser.bind(this) : this.sendInternal.bind(this);
@@ -2972,6 +2982,36 @@ var DaemonCdpManager = class {
2972
2982
  getAgentSessions() {
2973
2983
  return this.agentSessions;
2974
2984
  }
2985
+ async getCurrentPageWebviewUrls() {
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
+ }
3001
+ try {
3002
+ const raw = await this.evaluate(
3003
+ `JSON.stringify(Array.from(document.querySelectorAll('iframe,webview'))
3004
+ .map((el) => el.src || el.getAttribute('src') || '')
3005
+ .filter((src) => typeof src === 'string' && src.includes('vscode-webview')))`,
3006
+ 5e3
3007
+ );
3008
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
3009
+ if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
3010
+ return new Set(parsed.filter((src) => typeof src === "string" && src.length > 0));
3011
+ } catch {
3012
+ return /* @__PURE__ */ new Set();
3013
+ }
3014
+ }
2975
3015
  // ─── Screenshot ──────────────────────────────────────────
2976
3016
  async captureScreenshot(opts) {
2977
3017
  if (!this.isConnected) return null;
@@ -4109,7 +4149,7 @@ function registerExtensionProviders(providerLoader, manager, ideType) {
4109
4149
  manager.setExtensionProviders(enabledExtProviders);
4110
4150
  }
4111
4151
  async function setupIdeInstance(ctx, opts) {
4112
- const { providerLoader, instanceManager, instanceIdMap } = ctx;
4152
+ const { providerLoader, instanceManager, sessionRegistry } = ctx;
4113
4153
  const { ideType, manager, settings } = opts;
4114
4154
  const managerKey = opts.managerKey || ideType;
4115
4155
  registerExtensionProviders(providerLoader, manager, ideType);
@@ -4125,14 +4165,30 @@ async function setupIdeInstance(ctx, opts) {
4125
4165
  serverConn: ctx.serverConn,
4126
4166
  settings: resolvedSettings
4127
4167
  });
4128
- 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
+ });
4129
4177
  const extensionProviders = providerLoader.getEnabledByCategory("extension", ideType);
4130
4178
  for (const extProvider of extensionProviders) {
4131
4179
  const extSettings = providerLoader.getSettings(extProvider.type);
4132
4180
  await ideInstance.addExtension(extProvider, extSettings);
4133
- for (const ext of ideInstance.getExtensionInstances()) {
4134
- instanceIdMap.set(ext.getInstanceId(), managerKey);
4135
- }
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
+ });
4136
4192
  }
4137
4193
  return ideInstance;
4138
4194
  }
@@ -4349,6 +4405,7 @@ var DaemonCdpInitializer = class {
4349
4405
  async connectIdePort(port, ide) {
4350
4406
  const { providerLoader, cdpManagers } = this.config;
4351
4407
  const targets = await DaemonCdpManager.listAllTargets(port);
4408
+ await this.pruneStaleManagers(port, ide, targets);
4352
4409
  if (targets.length === 0) {
4353
4410
  if (cdpManagers.has(ide)) return;
4354
4411
  if (!await probeCdpPort(port)) return;
@@ -4401,6 +4458,34 @@ var DaemonCdpInitializer = class {
4401
4458
  }
4402
4459
  }
4403
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
+ }
4404
4489
  // ─── Periodic scanning ───
4405
4490
  /**
4406
4491
  * Start periodic scanning for newly opened IDEs.
@@ -4443,6 +4528,45 @@ var DaemonCdpInitializer = class {
4443
4528
  }
4444
4529
  };
4445
4530
 
4531
+ // src/status/normalize.ts
4532
+ var WORKING_STATUSES = /* @__PURE__ */ new Set([
4533
+ "generating",
4534
+ "streaming",
4535
+ "loading",
4536
+ "loading_reference",
4537
+ "thinking",
4538
+ "active"
4539
+ ]);
4540
+ function hasApprovalButtons(activeModal) {
4541
+ return (activeModal?.buttons?.length ?? 0) > 0;
4542
+ }
4543
+ function normalizeManagedStatus(status, opts) {
4544
+ if (hasApprovalButtons(opts?.activeModal)) return "waiting_approval";
4545
+ const normalized = String(status || "idle").trim().toLowerCase();
4546
+ if (normalized === "waiting_approval") return "waiting_approval";
4547
+ if (WORKING_STATUSES.has(normalized)) return "generating";
4548
+ if (normalized === "error") return "error";
4549
+ if (normalized === "stopped") return "stopped";
4550
+ if (normalized === "starting") return "starting";
4551
+ if (normalized === "panel_hidden") return "panel_hidden";
4552
+ if (normalized === "not_monitored") return "not_monitored";
4553
+ if (normalized === "disconnected") return "disconnected";
4554
+ return "idle";
4555
+ }
4556
+ function isManagedStatusWorking(status) {
4557
+ return normalizeManagedStatus(status) === "generating";
4558
+ }
4559
+ function isManagedStatusWaiting(status, opts) {
4560
+ return normalizeManagedStatus(status, opts) === "waiting_approval";
4561
+ }
4562
+ function normalizeActiveChatData(activeChat) {
4563
+ if (!activeChat) return activeChat;
4564
+ return {
4565
+ ...activeChat,
4566
+ status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal })
4567
+ };
4568
+ }
4569
+
4446
4570
  // src/status/builders.ts
4447
4571
  function findCdpManager(cdpManagers, key) {
4448
4572
  const exact = cdpManagers.get(key);
@@ -4465,95 +4589,152 @@ function isCdpConnected(cdpManagers, key) {
4465
4589
  const m = findCdpManager(cdpManagers, key);
4466
4590
  return m?.isConnected ?? false;
4467
4591
  }
4468
- function buildManagedIdes(ideStates, cdpManagers, opts) {
4469
- const result = [];
4470
- for (const state of ideStates) {
4471
- const cdpConnected = state.cdpConnected ?? isCdpConnected(cdpManagers, state.type);
4472
- result.push({
4473
- ideType: state.type,
4474
- ideVersion: "",
4475
- instanceId: state.instanceId || state.type,
4476
- workspace: state.workspace || null,
4477
- terminals: 0,
4478
- aiAgents: [],
4479
- activeChat: state.activeChat,
4480
- chats: [],
4481
- agentStreams: state.extensions.map((ext) => ({
4482
- agentType: ext.type,
4483
- agentName: ext.name,
4484
- extensionId: ext.type,
4485
- status: ext.status || "idle",
4486
- messages: ext.activeChat?.messages || [],
4487
- inputContent: ext.activeChat?.inputContent || "",
4488
- activeModal: ext.activeChat?.activeModal || null
4489
- })),
4490
- cdpConnected,
4491
- currentModel: state.currentModel,
4492
- currentPlan: state.currentPlan,
4493
- currentAutoApprove: state.currentAutoApprove
4494
- });
4495
- }
4496
- if (opts?.detectedIdes) {
4497
- const coveredTypes = new Set(ideStates.map((s) => s.type));
4498
- for (const ide of opts.detectedIdes) {
4499
- if (!ide.installed || coveredTypes.has(ide.id)) continue;
4500
- if (!isCdpConnected(cdpManagers, ide.id)) continue;
4501
- result.push({
4502
- ideType: ide.id,
4503
- ideVersion: "",
4504
- instanceId: ide.id,
4505
- workspace: null,
4506
- terminals: 0,
4507
- aiAgents: [],
4508
- activeChat: null,
4509
- chats: [],
4510
- agentStreams: [],
4511
- cdpConnected: true,
4512
- currentModel: void 0,
4513
- currentPlan: void 0
4514
- });
4515
- }
4516
- }
4517
- 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
+ };
4518
4653
  }
4519
- function buildManagedClis(cliStates) {
4520
- return cliStates.map((s) => ({
4521
- id: s.instanceId,
4522
- instanceId: s.instanceId,
4523
- cliType: s.type,
4524
- cliName: s.name,
4525
- status: s.status,
4526
- mode: "terminal",
4527
- workspace: s.workspace || "",
4528
- activeChat: s.activeChat
4529
- }));
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
+ };
4530
4675
  }
4531
- function buildManagedAcps(acpStates) {
4532
- return acpStates.map((s) => ({
4533
- id: s.instanceId,
4534
- acpType: s.type,
4535
- acpName: s.name,
4536
- status: s.status,
4537
- mode: "chat",
4538
- workspace: s.workspace || "",
4539
- activeChat: s.activeChat,
4540
- currentModel: s.currentModel,
4541
- currentPlan: s.currentPlan,
4542
- acpConfigOptions: s.acpConfigOptions,
4543
- acpModes: s.acpModes,
4544
- errorMessage: s.errorMessage,
4545
- errorReason: s.errorReason
4546
- }));
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
+ };
4695
+ }
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
+ };
4547
4719
  }
4548
- function buildAllManagedEntries(allStates, cdpManagers, opts) {
4720
+ function buildSessionEntries(allStates, cdpManagers) {
4721
+ const sessions = [];
4549
4722
  const ideStates = allStates.filter((s) => s.category === "ide");
4550
4723
  const cliStates = allStates.filter((s) => s.category === "cli");
4551
4724
  const acpStates = allStates.filter((s) => s.category === "acp");
4552
- return {
4553
- managedIdes: buildManagedIdes(ideStates, cdpManagers, opts),
4554
- managedClis: buildManagedClis(cliStates),
4555
- managedAcps: buildManagedAcps(acpStates)
4556
- };
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;
4557
4738
  }
4558
4739
 
4559
4740
  // src/commands/handler.ts
@@ -4562,14 +4743,37 @@ init_logger();
4562
4743
 
4563
4744
  // src/commands/chat-commands.ts
4564
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
+ }
4565
4754
  function getTargetedCliAdapter(h, args, providerType) {
4566
- 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;
4567
4770
  }
4568
4771
  async function handleChatHistory(h, args) {
4569
- const { agentType, offset, limit, instanceId } = args;
4772
+ const { agentType, offset, limit } = args;
4773
+ const instanceId = args?.targetSessionId;
4570
4774
  try {
4571
4775
  const provider = h.getProvider(agentType);
4572
- const agentStr = provider?.type || agentType || h.currentIdeType || "";
4776
+ const agentStr = provider?.type || agentType || getCurrentProviderType(h);
4573
4777
  const result = readChatHistory(agentStr, offset || 0, limit || 30, instanceId);
4574
4778
  return { success: true, ...result, agent: agentStr };
4575
4779
  } catch (e) {
@@ -4613,7 +4817,7 @@ async function handleReadChat(h, args) {
4613
4817
  provider.type || "unknown_extension",
4614
4818
  parsed.messages || [],
4615
4819
  parsed.title,
4616
- args?.instanceId
4820
+ args?.targetSessionId
4617
4821
  );
4618
4822
  return { success: true, ...parsed };
4619
4823
  }
@@ -4623,15 +4827,18 @@ async function handleReadChat(h, args) {
4623
4827
  }
4624
4828
  if (h.agentStream) {
4625
4829
  const cdp2 = h.getCdp();
4626
- if (cdp2) {
4627
- const streams = await h.agentStream.collectAgentStreams(cdp2);
4628
- 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
+ }
4629
4836
  if (stream) {
4630
4837
  h.historyWriter.appendNewMessages(
4631
4838
  stream.agentType,
4632
4839
  stream.messages || [],
4633
4840
  void 0,
4634
- args?.instanceId
4841
+ args?.targetSessionId
4635
4842
  );
4636
4843
  return { success: true, messages: stream.messages || [], status: stream.status, agentType: stream.agentType };
4637
4844
  }
@@ -4658,10 +4865,10 @@ async function handleReadChat(h, args) {
4658
4865
  if (parsed && typeof parsed === "object") {
4659
4866
  _log(`Webview OK: ${parsed.messages?.length || 0} msgs`);
4660
4867
  h.historyWriter.appendNewMessages(
4661
- provider?.type || h.currentIdeType || "unknown_webview",
4868
+ provider?.type || getCurrentProviderType(h, "unknown_webview"),
4662
4869
  parsed.messages || [],
4663
4870
  parsed.title,
4664
- args?.instanceId
4871
+ args?.targetSessionId
4665
4872
  );
4666
4873
  return { success: true, ...parsed };
4667
4874
  }
@@ -4685,10 +4892,10 @@ async function handleReadChat(h, args) {
4685
4892
  if (parsed && typeof parsed === "object" && parsed.messages?.length > 0) {
4686
4893
  _log(`OK: ${parsed.messages?.length} msgs`);
4687
4894
  h.historyWriter.appendNewMessages(
4688
- provider?.type || h.currentIdeType || "unknown_ide",
4895
+ provider?.type || getCurrentProviderType(h, "unknown_ide"),
4689
4896
  parsed.messages || [],
4690
4897
  parsed.title,
4691
- args?.instanceId
4898
+ args?.targetSessionId
4692
4899
  );
4693
4900
  return { success: true, ...parsed };
4694
4901
  }
@@ -4703,16 +4910,21 @@ async function handleSendChat(h, args) {
4703
4910
  if (!text) return { success: false, error: "text required" };
4704
4911
  const _log = (msg) => LOG.debug("Command", `[send_chat] ${msg}`);
4705
4912
  const provider = h.getProvider(args?.agentType);
4913
+ const dedupeKey = buildRecentSendKey(h, args, provider, text);
4706
4914
  const _logSendSuccess = (method, targetAgent) => {
4707
4915
  h.historyWriter.appendNewMessages(
4708
- targetAgent || provider?.type || h.currentIdeType || "unknown_agent",
4916
+ targetAgent || provider?.type || getCurrentProviderType(h, "unknown_agent"),
4709
4917
  [{ role: "user", content: text, receivedAt: Date.now() }],
4710
4918
  void 0,
4711
4919
  // title
4712
- args?.instanceId
4920
+ args?.targetSessionId
4713
4921
  );
4714
4922
  return { success: true, sent: true, method, targetAgent };
4715
4923
  };
4924
+ if (isRecentDuplicateSend(dedupeKey)) {
4925
+ _log(`Suppressed duplicate send for ${dedupeKey}`);
4926
+ return { success: true, sent: false, deduplicated: true };
4927
+ }
4716
4928
  if (provider?.category === "cli" || provider?.category === "acp") {
4717
4929
  const adapter = getTargetedCliAdapter(h, args, provider.type);
4718
4930
  if (adapter) {
@@ -4748,8 +4960,9 @@ async function handleSendChat(h, args) {
4748
4960
  } catch (e) {
4749
4961
  _log(`Extension script error: ${e.message}`);
4750
4962
  }
4751
- if (h.agentStream && h.getCdp()) {
4752
- const ok = await h.agentStream.sendToAgent(h.getCdp(), 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);
4753
4966
  if (ok) {
4754
4967
  _log(`AgentStreamManager sent OK`);
4755
4968
  return _logSendSuccess("agent-stream");
@@ -4759,45 +4972,11 @@ async function handleSendChat(h, args) {
4759
4972
  }
4760
4973
  const targetCdp = h.getCdp();
4761
4974
  if (!targetCdp?.isConnected) {
4762
- _log(`No CDP for ${h.currentIdeType}`);
4763
- return { success: false, error: `CDP for ${h.currentIdeType || "unknown"} not connected` };
4764
- }
4765
- _log(`Targeting IDE: ${h.currentIdeType}`);
4766
- if (provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
4767
- try {
4768
- const webviewScript = provider.scripts.webviewSendMessage(text);
4769
- if (webviewScript && targetCdp.evaluateInWebviewFrame) {
4770
- const matchText = provider.webviewMatchText;
4771
- const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
4772
- const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
4773
- let wvParsed = wvResult;
4774
- if (typeof wvResult === "string") {
4775
- try {
4776
- wvParsed = JSON.parse(wvResult);
4777
- } catch {
4778
- }
4779
- }
4780
- if (wvParsed?.sent) {
4781
- _log(`webviewSendMessage (priority) OK`);
4782
- return _logSendSuccess("webview-script-priority");
4783
- }
4784
- _log(`webviewSendMessage (priority) did not confirm sent, falling through`);
4785
- }
4786
- } catch (e) {
4787
- _log(`webviewSendMessage (priority) failed: ${e.message}, falling through`);
4788
- }
4789
- }
4790
- if (provider?.inputMethod === "cdp-type-and-send" && provider.inputSelector) {
4791
- try {
4792
- const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
4793
- if (sent) {
4794
- _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
4795
- return _logSendSuccess("typeAndSend-provider");
4796
- }
4797
- } catch (e) {
4798
- _log(`typeAndSend(provider) failed: ${e.message}`);
4799
- }
4975
+ const managerKey = getCurrentManagerKey(h);
4976
+ _log(`No CDP for ${managerKey}`);
4977
+ return { success: false, error: `CDP for ${managerKey || "unknown"} not connected` };
4800
4978
  }
4979
+ _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
4801
4980
  const sendScript = h.getProviderScript("sendMessage", { MESSAGE: text });
4802
4981
  if (sendScript) {
4803
4982
  try {
@@ -4824,7 +5003,30 @@ async function handleSendChat(h, args) {
4824
5003
  _log(`typeAndSend(script.selector) failed: ${e.message}`);
4825
5004
  }
4826
5005
  }
4827
- 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) {
4828
5030
  try {
4829
5031
  const webviewScript = provider.scripts.webviewSendMessage(text);
4830
5032
  if (webviewScript && targetCdp.evaluateInWebviewFrame) {
@@ -4847,20 +5049,44 @@ async function handleSendChat(h, args) {
4847
5049
  _log(`webviewSendMessage failed: ${e.message}`);
4848
5050
  }
4849
5051
  }
4850
- if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
4851
- try {
4852
- const { x, y } = parsed.clickCoords;
4853
- const sent = await targetCdp.typeAndSendAt(x, y, text);
4854
- if (sent) {
4855
- _log(`typeAndSendAt(${x},${y}) success`);
4856
- 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 {
4857
5070
  }
4858
- } catch (e) {
4859
- _log(`typeAndSendAt failed: ${e.message}`);
5071
+ }
5072
+ if (wvParsed?.sent) {
5073
+ _log(`webviewSendMessage OK`);
5074
+ return _logSendSuccess("webview-script");
4860
5075
  }
4861
5076
  }
4862
5077
  } catch (e) {
4863
- _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}`);
4864
5090
  }
4865
5091
  }
4866
5092
  _log("All methods failed");
@@ -4868,9 +5094,9 @@ async function handleSendChat(h, args) {
4868
5094
  }
4869
5095
  async function handleListChats(h, args) {
4870
5096
  const provider = h.getProvider(args?.agentType);
4871
- if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
5097
+ if (provider?.category === "extension" && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
4872
5098
  try {
4873
- const chats = await h.agentStream.listAgentChats(h.getCdp(), provider.type);
5099
+ const chats = await h.agentStream.listSessionChats(h.getCdp(), h.currentSession.sessionId);
4874
5100
  LOG.info("Command", `[list_chats] Extension: ${chats.length} chats`);
4875
5101
  return { success: true, chats };
4876
5102
  } catch (e) {
@@ -4929,8 +5155,8 @@ async function handleNewChat(h, args) {
4929
5155
  }
4930
5156
  return { success: false, error: "new_chat not supported by this CLI provider" };
4931
5157
  }
4932
- if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
4933
- const ok = await h.agentStream.newAgentSession(h.getCdp(), 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);
4934
5160
  return { success: ok };
4935
5161
  }
4936
5162
  try {
@@ -4954,15 +5180,15 @@ async function handleNewChat(h, args) {
4954
5180
  }
4955
5181
  async function handleSwitchChat(h, args) {
4956
5182
  const provider = h.getProvider(args?.agentType);
4957
- const ideType = h.currentIdeType;
5183
+ const managerKey = getCurrentManagerKey(h);
4958
5184
  const sessionId = args?.sessionId || args?.id || args?.chatId;
4959
5185
  if (!sessionId) return { success: false, error: "sessionId required" };
4960
- LOG.info("Command", `[switch_chat] sessionId=${sessionId}, ideType=${ideType}`);
4961
- if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
4962
- const ok = await h.agentStream.switchAgentSession(h.getCdp(), 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);
4963
5189
  return { success: ok, result: ok ? "switched" : "failed" };
4964
5190
  }
4965
- const cdp = h.getCdp(ideType);
5191
+ const cdp = h.getCdp(managerKey);
4966
5192
  if (!cdp?.isConnected) return { success: false, error: "CDP not connected" };
4967
5193
  try {
4968
5194
  const webviewScript = h.getProviderScript("webviewSwitchSession", { SESSION_ID: JSON.stringify(sessionId) });
@@ -5104,7 +5330,7 @@ async function handleSetMode(h, args) {
5104
5330
  async function handleChangeModel(h, args) {
5105
5331
  const provider = h.getProvider(args?.agentType);
5106
5332
  const model = args?.model;
5107
- 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)}`);
5108
5334
  if (provider?.category === "acp") {
5109
5335
  const adapter = getTargetedCliAdapter(h, args, provider.type);
5110
5336
  LOG.info("Command", `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
@@ -5225,13 +5451,8 @@ async function handleResolveAction(h, args) {
5225
5451
  LOG.info("Command", `[resolveAction] CLI PTY \u2192 buttonIndex=${buttonIndex} "${buttons[buttonIndex] ?? "?"}"`);
5226
5452
  return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
5227
5453
  }
5228
- if (provider?.category === "extension" && h.agentStream && h.getCdp()) {
5229
- const ok = await h.agentStream.resolveAgentAction(
5230
- h.getCdp(),
5231
- provider.type,
5232
- action,
5233
- h.currentIdeType
5234
- );
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);
5235
5456
  return { success: ok };
5236
5457
  }
5237
5458
  if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
@@ -5591,151 +5812,37 @@ async function handleFileListBrowse(h, args) {
5591
5812
  // src/commands/stream-commands.ts
5592
5813
  init_config();
5593
5814
  init_logger();
5594
- async function handleAgentStreamSwitch(h, args) {
5595
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5596
- const agentType = args?.agentType || args?.agent || null;
5597
- await h.agentStream.switchActiveAgent(h.getCdp(), agentType);
5598
- return { success: true, activeAgent: agentType };
5599
- }
5600
- async function handleAgentStreamRead(h, args) {
5815
+ async function handleFocusSession(h, args) {
5601
5816
  if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5602
- const streams = await h.agentStream.collectAgentStreams(h.getCdp());
5603
- return { success: true, streams };
5604
- }
5605
- async function handleAgentStreamSend(h, args) {
5606
- const agentType = args?.agentType || args?.agent;
5607
- const text = args?.text || args?.message;
5608
- if (!text) return { success: false, error: "text required" };
5609
- if (agentType && h.ctx.adapters) {
5610
- for (const [key, adapter] of h.ctx.adapters.entries()) {
5611
- if (adapter.cliType === agentType || key.includes(agentType)) {
5612
- LOG.info("Command", `[agent_stream_send] Routing to CLI adapter: ${adapter.cliType}`);
5613
- try {
5614
- await adapter.sendMessage(text);
5615
- return { success: true, sent: true, targetAgent: adapter.cliType };
5616
- } catch (e) {
5617
- LOG.info("Command", `[agent_stream_send] CLI adapter failed: ${e.message}`);
5618
- return { success: false, error: `CLI send failed: ${e.message}` };
5619
- }
5620
- }
5621
- }
5622
- }
5623
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5624
- const resolvedAgent = agentType || h.agentStream.activeAgentType;
5625
- if (!resolvedAgent) return { success: false, error: "agentType required" };
5626
- const ok = await h.agentStream.sendToAgent(h.getCdp(), resolvedAgent, text, h.currentIdeType);
5627
- return { success: ok };
5628
- }
5629
- async function handleAgentStreamResolve(h, args) {
5630
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5631
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
5632
- const action = args?.action || "approve";
5633
- if (!agentType) return { success: false, error: "agentType required" };
5634
- const ok = await h.agentStream.resolveAgentAction(h.getCdp(), agentType, action, h.currentIdeType);
5635
- return { success: ok };
5636
- }
5637
- async function handleAgentStreamNew(h, args) {
5638
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5639
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
5640
- if (!agentType) return { success: false, error: "agentType required" };
5641
- const ok = await h.agentStream.newAgentSession(h.getCdp(), agentType, h.currentIdeType);
5642
- return { success: ok };
5643
- }
5644
- async function handleAgentStreamListChats(h, args) {
5645
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5646
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
5647
- if (!agentType) return { success: false, error: "agentType required" };
5648
- const chats = await h.agentStream.listAgentChats(h.getCdp(), agentType);
5649
- return { success: true, chats };
5650
- }
5651
- async function handleAgentStreamSwitchSession(h, args) {
5652
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5653
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
5654
- const sessionId = args?.sessionId || args?.id;
5655
- if (!agentType || !sessionId) return { success: false, error: "agentType and sessionId required" };
5656
- const ok = await h.agentStream.switchAgentSession(h.getCdp(), agentType, sessionId);
5657
- return { success: ok };
5658
- }
5659
- async function handleAgentStreamFocus(h, args) {
5660
- if (!h.agentStream || !h.getCdp()) return { success: false, error: "AgentStream or CDP not available" };
5661
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
5662
- if (!agentType) return { success: false, error: "agentType required" };
5663
- await h.agentStream.ensureAgentPanelOpen(agentType, h.currentIdeType);
5664
- const ok = await h.agentStream.focusAgentEditor(h.getCdp(), 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);
5665
5820
  return { success: ok };
5666
5821
  }
5667
5822
  function handlePtyInput(h, args) {
5668
- const { cliType, data } = args || {};
5823
+ const { cliType, data, targetSessionId } = args || {};
5669
5824
  if (!data) return { success: false, error: "data required" };
5670
- if (h.ctx.adapters) {
5671
- const targetCli = cliType || "";
5672
- if (!targetCli && h.ctx.adapters.size > 0) {
5673
- const first = h.ctx.adapters.values().next().value;
5674
- if (first && typeof first.writeRaw === "function") {
5675
- first.writeRaw(data);
5676
- return { success: true };
5677
- }
5678
- }
5679
- const directAdapter = h.ctx.adapters.get(targetCli);
5680
- if (directAdapter && typeof directAdapter.writeRaw === "function") {
5681
- directAdapter.writeRaw(data);
5682
- return { success: true };
5683
- }
5684
- for (const [, adapter] of h.ctx.adapters) {
5685
- if (adapter.cliType === targetCli && typeof adapter.writeRaw === "function") {
5686
- adapter.writeRaw(data);
5687
- return { success: true };
5688
- }
5689
- }
5690
- for (const [key, adapter] of h.ctx.adapters) {
5691
- if ((key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.writeRaw === "function") {
5692
- adapter.writeRaw(data);
5693
- return { success: true };
5694
- }
5695
- }
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"}` };
5696
5828
  }
5697
- return { success: false, error: `CLI adapter not found: ${cliType}` };
5829
+ adapter.writeRaw(data);
5830
+ return { success: true };
5698
5831
  }
5699
5832
  function handlePtyResize(h, args) {
5700
- const { cliType, cols, rows, force } = args || {};
5833
+ const { cliType, cols, rows, force, targetSessionId } = args || {};
5701
5834
  if (!cols || !rows) return { success: false, error: "cols and rows required" };
5702
- if (h.ctx.adapters) {
5703
- const targetCli = cliType || "";
5704
- if (!targetCli && h.ctx.adapters.size > 0) {
5705
- const first = h.ctx.adapters.values().next().value;
5706
- if (first && typeof first.resize === "function") {
5707
- if (force) {
5708
- first.resize(cols - 1, rows);
5709
- setTimeout(() => first.resize(cols, rows), 50);
5710
- } else {
5711
- first.resize(cols, rows);
5712
- }
5713
- return { success: true };
5714
- }
5715
- }
5716
- const directAdapter = h.ctx.adapters.get(targetCli);
5717
- if (directAdapter && typeof directAdapter.resize === "function") {
5718
- if (force) {
5719
- directAdapter.resize(cols - 1, rows);
5720
- setTimeout(() => directAdapter.resize(cols, rows), 50);
5721
- } else {
5722
- directAdapter.resize(cols, rows);
5723
- }
5724
- return { success: true };
5725
- }
5726
- for (const [key, adapter] of h.ctx.adapters) {
5727
- if ((adapter.cliType === targetCli || key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.resize === "function") {
5728
- if (force) {
5729
- adapter.resize(cols - 1, rows);
5730
- setTimeout(() => adapter.resize(cols, rows), 50);
5731
- } else {
5732
- adapter.resize(cols, rows);
5733
- }
5734
- return { success: true };
5735
- }
5736
- }
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"}` };
5737
5838
  }
5738
- return { success: false, error: `CLI adapter not found: ${cliType}` };
5839
+ if (force) {
5840
+ adapter.resize(cols - 1, rows);
5841
+ setTimeout(() => adapter.resize(cols, rows), 50);
5842
+ } else {
5843
+ adapter.resize(cols, rows);
5844
+ }
5845
+ return { success: true };
5739
5846
  }
5740
5847
  function handleGetProviderSettings(h, args) {
5741
5848
  const loader = h.ctx.providerLoader;
@@ -5771,7 +5878,7 @@ function handleSetProviderSetting(h, args) {
5771
5878
  }
5772
5879
  async function handleExtensionScript(h, args, scriptName) {
5773
5880
  const { agentType, ideType } = args || {};
5774
- 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 || ""}`);
5775
5882
  if (!agentType) return { success: false, error: "agentType is required" };
5776
5883
  const loader = h.ctx.providerLoader;
5777
5884
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -5792,21 +5899,22 @@ async function handleExtensionScript(h, args, scriptName) {
5792
5899
  }
5793
5900
  const scriptCode = scriptFn(normalizedArgs);
5794
5901
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
5795
- 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;
5796
5903
  LOG.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
5797
5904
  const cdp = h.getCdp(cdpKey);
5798
5905
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
5799
5906
  try {
5800
5907
  let result;
5801
5908
  if (provider.category === "extension") {
5802
- const sessions = cdp.getAgentSessions();
5803
- let targetSessionId = null;
5804
- for (const [sessionId, target] of sessions) {
5805
- if (target.agentType === agentType) {
5806
- targetSessionId = sessionId;
5807
- break;
5808
- }
5809
- }
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;
5810
5918
  const IDE_LEVEL_SCRIPTS = ["listModes", "setMode", "listModels", "setModel"];
5811
5919
  if (IDE_LEVEL_SCRIPTS.includes(scriptName)) {
5812
5920
  if (targetSessionId) {
@@ -5877,7 +5985,7 @@ function handleGetIdeExtensions(h, args) {
5877
5985
  enabled: config.ideSettings?.[ide]?.extensions?.[p.type]?.enabled === true
5878
5986
  }));
5879
5987
  }
5880
- return { success: true, ides: result };
5988
+ return { success: true, ideExtensions: result };
5881
5989
  }
5882
5990
  function handleSetIdeExtension(h, args) {
5883
5991
  const { ideType, extensionType, enabled } = args || {};
@@ -5989,10 +6097,8 @@ var DaemonCommandHandler = class {
5989
6097
  _agentStream = null;
5990
6098
  domHandlers;
5991
6099
  _historyWriter;
5992
- /** Current IDE type extracted from command args (per-request) */
5993
- _currentIdeType;
5994
- /** Current provider type — agentType priority, ideType use */
5995
- _currentProviderType;
6100
+ /** Current request route context */
6101
+ _currentRoute = {};
5996
6102
  constructor(ctx) {
5997
6103
  this._ctx = ctx;
5998
6104
  this.domHandlers = new CdpDomHandlers((ideType) => this.getCdp(ideType));
@@ -6008,20 +6114,25 @@ var DaemonCommandHandler = class {
6008
6114
  get historyWriter() {
6009
6115
  return this._historyWriter;
6010
6116
  }
6117
+ get currentManagerKey() {
6118
+ return this._currentRoute.managerKey;
6119
+ }
6011
6120
  get currentIdeType() {
6012
- return this._currentIdeType;
6121
+ return this._currentRoute.managerKey;
6013
6122
  }
6014
6123
  get currentProviderType() {
6015
- return this._currentProviderType;
6124
+ return this._currentRoute.providerType;
6125
+ }
6126
+ get currentSession() {
6127
+ return this._currentRoute.session;
6016
6128
  }
6017
- /** Get CDP manager for a specific ideType or managerKey.
6018
- * Supports exact match, multi-window prefix match, and instanceIdMap UUID lookup.
6019
- * Returns null if no match — never falls back to another IDE. */
6129
+ /** Get CDP manager for a specific session or manager key. */
6020
6130
  getCdp(ideType) {
6021
- const key = ideType || this._currentIdeType;
6022
- if (!key) return null;
6023
- const resolved = this._ctx.instanceIdMap?.get(key) || key;
6024
- const m = findCdpManager(this._ctx.cdpManagers, resolved.toLowerCase());
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);
6025
6136
  if (m?.isConnected) return m;
6026
6137
  return null;
6027
6138
  }
@@ -6029,7 +6140,7 @@ var DaemonCommandHandler = class {
6029
6140
  * Get provider module — _currentProviderType (agentType priority) use.
6030
6141
  */
6031
6142
  getProvider(overrideType) {
6032
- const key = overrideType || this._currentProviderType || this._currentIdeType;
6143
+ const key = overrideType || this._currentRoute.providerType || this._currentRoute.session?.providerType || this._currentRoute.managerKey;
6033
6144
  if (!key || !this._ctx.providerLoader) return void 0;
6034
6145
  const result = this._ctx.providerLoader.resolve(key);
6035
6146
  if (result) return result;
@@ -6062,14 +6173,22 @@ var DaemonCommandHandler = class {
6062
6173
  const cdp = this.getCdp();
6063
6174
  if (!cdp?.isConnected) return null;
6064
6175
  if (provider?.category === "extension") {
6065
- let sessionId = this.getExtensionSessionId(provider);
6066
- if (!sessionId && this._agentStream) {
6067
- await this._agentStream.switchActiveAgent(cdp, provider.type);
6068
- await this._agentStream.syncAgentSessions(cdp);
6069
- sessionId = this.getExtensionSessionId(provider);
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
+ }
6070
6186
  }
6071
6187
  if (!sessionId) return null;
6072
- 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);
6073
6192
  return { result: result2, category: "extension" };
6074
6193
  }
6075
6194
  const result = await cdp.evaluate(script, timeout);
@@ -6077,33 +6196,37 @@ var DaemonCommandHandler = class {
6077
6196
  }
6078
6197
  /** CLI adapter search */
6079
6198
  getCliAdapter(type) {
6080
- const target = type || this._currentIdeType;
6199
+ const target = type || this._currentRoute.session?.sessionId || this._currentRoute.providerType || this._currentRoute.managerKey;
6081
6200
  if (!target || !this._ctx.adapters) return null;
6082
- let normalizedTarget = target;
6083
- const colonIdx = normalizedTarget.lastIndexOf(":");
6084
- if (colonIdx >= 0) normalizedTarget = normalizedTarget.substring(colonIdx + 1);
6085
- const direct = this._ctx.adapters.get(normalizedTarget);
6086
- if (direct) return direct;
6087
- for (const [key, adapter] of this._ctx.adapters.entries()) {
6088
- if (adapter.cliType === target || adapter.cliType === normalizedTarget || key === normalizedTarget || key.startsWith(target) || key.startsWith(normalizedTarget)) {
6089
- return adapter;
6090
- }
6201
+ const session = this._ctx.sessionRegistry?.get(target);
6202
+ if (session?.adapterKey) {
6203
+ return this._ctx.adapters.get(session.adapterKey) || null;
6091
6204
  }
6092
- return null;
6205
+ return this._ctx.adapters.get(target) || null;
6093
6206
  }
6094
6207
  // ─── Private helpers ──────────────────────────────
6095
- getExtensionSessionId(provider) {
6096
- if (provider.category !== "extension" || !this._agentStream) return null;
6097
- const managed = this._agentStream.getManagedAgent(provider.type);
6098
- return managed?.sessionId || null;
6099
- }
6100
- /** 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 */
6101
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
+ }
6102
6227
  if (args?.ideType) {
6103
- const mappedKey = this._ctx.instanceIdMap?.get(args.ideType);
6104
- if (mappedKey) {
6105
- return mappedKey;
6106
- }
6228
+ const target = this._ctx.sessionRegistry?.get(args.ideType);
6229
+ if (target?.cdpManagerKey) return target.cdpManagerKey;
6107
6230
  if (this._ctx.cdpManagers.has(args.ideType)) {
6108
6231
  return args.ideType;
6109
6232
  }
@@ -6114,33 +6237,6 @@ var DaemonCommandHandler = class {
6114
6237
  }
6115
6238
  }
6116
6239
  }
6117
- if (args?._targetInstance) {
6118
- let raw = args._targetInstance;
6119
- const ideMatch = raw.match(/:ide:(.+)$/);
6120
- const cliMatch = raw.match(/:cli:(.+)$/);
6121
- const acpMatch = raw.match(/:acp:(.+)$/);
6122
- if (ideMatch) raw = ideMatch[1];
6123
- else if (cliMatch) raw = cliMatch[1];
6124
- else if (acpMatch) raw = acpMatch[1];
6125
- if (this._ctx.instanceIdMap?.has(raw)) {
6126
- return this._ctx.instanceIdMap.get(raw);
6127
- }
6128
- if (this._ctx.cdpManagers.has(raw)) {
6129
- return raw;
6130
- }
6131
- const found = findCdpManager(this._ctx.cdpManagers, raw);
6132
- if (found) {
6133
- for (const [k, m] of this._ctx.cdpManagers.entries()) {
6134
- if (m === found) return k;
6135
- }
6136
- }
6137
- const lastUnderscore = raw.lastIndexOf("_");
6138
- if (lastUnderscore > 0) {
6139
- const stripped = raw.substring(0, lastUnderscore);
6140
- if (this._ctx.cdpManagers.has(stripped)) return stripped;
6141
- }
6142
- return raw;
6143
- }
6144
6240
  return void 0;
6145
6241
  }
6146
6242
  setAgentStreamManager(manager) {
@@ -6148,12 +6244,11 @@ var DaemonCommandHandler = class {
6148
6244
  }
6149
6245
  // ─── Command Dispatcher ──────────────────────────
6150
6246
  async handle(cmd, args) {
6151
- this._currentIdeType = this.extractIdeType(args);
6152
- this._currentProviderType = args?.agentType || args?.providerType || this._currentIdeType;
6153
- if (!this._currentIdeType && !this._currentProviderType) {
6247
+ this._currentRoute = this.resolveRoute(args);
6248
+ if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
6154
6249
  const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
6155
6250
  if (cdpCommands.includes(cmd)) {
6156
- return { success: false, error: "No ideType specified \u2014 cannot route command" };
6251
+ return { success: false, error: "No targetSessionId specified \u2014 cannot route command" };
6157
6252
  }
6158
6253
  }
6159
6254
  try {
@@ -6244,22 +6339,8 @@ var DaemonCommandHandler = class {
6244
6339
  case "refresh_scripts":
6245
6340
  return this.handleRefreshScripts(args);
6246
6341
  // ─── Stream commands (stream-commands.ts) ───────────
6247
- case "agent_stream_switch":
6248
- return handleAgentStreamSwitch(this, args);
6249
- case "agent_stream_read":
6250
- return handleAgentStreamRead(this, args);
6251
- case "agent_stream_send":
6252
- return handleAgentStreamSend(this, args);
6253
- case "agent_stream_resolve":
6254
- return handleAgentStreamResolve(this, args);
6255
- case "agent_stream_new":
6256
- return handleAgentStreamNew(this, args);
6257
- case "agent_stream_list_chats":
6258
- return handleAgentStreamListChats(this, args);
6259
- case "agent_stream_switch_session":
6260
- return handleAgentStreamSwitchSession(this, args);
6261
- case "agent_stream_focus":
6262
- return handleAgentStreamFocus(this, args);
6342
+ case "focus_session":
6343
+ return handleFocusSession(this, args);
6263
6344
  // ─── PTY Raw I/O (stream-commands.ts) ─────────
6264
6345
  case "pty_input":
6265
6346
  return handlePtyInput(this, args);
@@ -7907,8 +7988,7 @@ var CHAT_COMMANDS = [
7907
7988
  "new_chat",
7908
7989
  "switch_chat",
7909
7990
  "set_mode",
7910
- "change_model",
7911
- "agent_stream_send"
7991
+ "change_model"
7912
7992
  ];
7913
7993
  var DaemonCommandRouter = class {
7914
7994
  deps;
@@ -8146,6 +8226,7 @@ var DaemonCommandRouter = class {
8146
8226
  } catch {
8147
8227
  }
8148
8228
  this.deps.cdpManagers.delete(key);
8229
+ this.deps.sessionRegistry.unregisterByManagerKey(key);
8149
8230
  LOG.info("StopIDE", `CDP disconnected: ${key}`);
8150
8231
  }
8151
8232
  }
@@ -8158,14 +8239,6 @@ var DaemonCommandRouter = class {
8158
8239
  for (const instanceKey of keysToRemove) {
8159
8240
  const ideInstance = this.deps.instanceManager.getInstance(instanceKey);
8160
8241
  if (ideInstance) {
8161
- if (ideInstance.getInstanceId) {
8162
- this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
8163
- }
8164
- if (ideInstance.getExtensionInstances) {
8165
- for (const ext of ideInstance.getExtensionInstances()) {
8166
- if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
8167
- }
8168
- }
8169
8242
  this.deps.instanceManager.removeInstance(instanceKey);
8170
8243
  LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
8171
8244
  }
@@ -8174,14 +8247,6 @@ var DaemonCommandRouter = class {
8174
8247
  const instanceKey = `ide:${ideType}`;
8175
8248
  const ideInstance = this.deps.instanceManager.getInstance(instanceKey);
8176
8249
  if (ideInstance) {
8177
- if (ideInstance.getInstanceId) {
8178
- this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
8179
- }
8180
- if (ideInstance.getExtensionInstances) {
8181
- for (const ext of ideInstance.getExtensionInstances()) {
8182
- if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
8183
- }
8184
- }
8185
8250
  this.deps.instanceManager.removeInstance(instanceKey);
8186
8251
  LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
8187
8252
  }
@@ -8234,15 +8299,9 @@ function buildStatusSnapshot(options) {
8234
8299
  const cfg = loadConfig();
8235
8300
  const wsState = getWorkspaceState(cfg);
8236
8301
  const memSnap = getHostMemorySnapshot();
8237
- const { managedIdes, managedClis, managedAcps } = buildAllManagedEntries(
8302
+ const sessions = buildSessionEntries(
8238
8303
  options.allStates,
8239
- options.cdpManagers,
8240
- {
8241
- detectedIdes: options.detectedIdes.map((ide) => ({
8242
- id: ide.id,
8243
- installed: ide.installed !== false
8244
- }))
8245
- }
8304
+ options.cdpManagers
8246
8305
  );
8247
8306
  return {
8248
8307
  instanceId: options.instanceId,
@@ -8264,9 +8323,7 @@ function buildStatusSnapshot(options) {
8264
8323
  timestamp: options.timestamp ?? Date.now(),
8265
8324
  detectedIdes: buildDetectedIdeInfos(options.detectedIdes, options.cdpManagers),
8266
8325
  ...options.p2p ? { p2p: options.p2p } : {},
8267
- managedIdes,
8268
- managedClis,
8269
- managedAcps,
8326
+ sessions,
8270
8327
  workspaces: wsState.workspaces,
8271
8328
  defaultWorkspaceId: wsState.defaultWorkspaceId,
8272
8329
  defaultWorkspacePath: wsState.defaultWorkspacePath,
@@ -8378,7 +8435,7 @@ var DaemonStatusReporter = class {
8378
8435
  LOG.info("StatusReport", `\u2192${target} ${baseSummary}`);
8379
8436
  }
8380
8437
  }
8381
- const { managedIdes, managedClis, managedAcps } = buildAllManagedEntries(
8438
+ const sessions = buildSessionEntries(
8382
8439
  allStates,
8383
8440
  this.deps.cdpManagers
8384
8441
  );
@@ -8409,23 +8466,20 @@ var DaemonStatusReporter = class {
8409
8466
  if (opts?.p2pOnly) return;
8410
8467
  const wsPayload = {
8411
8468
  daemonMode: true,
8412
- // managedIdes: server only saves id, type, cdpConnected
8413
- managedIdes: managedIdes.map((ide) => ({
8414
- ideType: ide.ideType,
8415
- instanceId: ide.instanceId,
8416
- cdpConnected: ide.cdpConnected
8417
- })),
8418
- // managedClis: server only saves id, type, name
8419
- managedClis: managedClis.map((c) => ({
8420
- id: c.id,
8421
- cliType: c.cliType,
8422
- cliName: c.cliName
8423
- })),
8424
- // managedAcps: server only saves id, type, name
8425
- managedAcps: managedAcps?.map((a) => ({
8426
- id: a.id,
8427
- acpType: a.acpType,
8428
- 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
8429
8483
  })),
8430
8484
  p2p: payload.p2p,
8431
8485
  timestamp: now
@@ -8856,6 +8910,9 @@ var AcpProviderInstance = class {
8856
8910
  );
8857
8911
  }
8858
8912
  }
8913
+ getInstanceId() {
8914
+ return this.instanceId;
8915
+ }
8859
8916
  // ─── ACP Config Options & Modes ─────────────────────
8860
8917
  parseConfigOptions(raw) {
8861
8918
  if (!Array.isArray(raw)) return;
@@ -9635,6 +9692,7 @@ var DaemonCliManager = class {
9635
9692
  const normalizedType = this.providerLoader.resolveAlias(cliType);
9636
9693
  const provider = this.providerLoader.getByAlias(cliType);
9637
9694
  const key = crypto4.randomUUID();
9695
+ const sessionRegistry = this.deps.getSessionRegistry?.() || null;
9638
9696
  if (provider && provider.category === "acp") {
9639
9697
  const instanceManager2 = this.deps.getInstanceManager();
9640
9698
  if (!instanceManager2) throw new Error("InstanceManager not available");
@@ -9658,6 +9716,16 @@ ${installInfo}`
9658
9716
  await instanceManager2.addInstance(key, acpInstance, {
9659
9717
  settings: this.providerLoader.getSettings(normalizedType)
9660
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
+ });
9661
9729
  this.adapters.set(key, {
9662
9730
  cliType: normalizedType,
9663
9731
  workingDir: resolvedDir,
@@ -9715,9 +9783,18 @@ ${installInfo}`
9715
9783
  serverConn: this.deps.getServerConn(),
9716
9784
  settings: {},
9717
9785
  onPtyData: (data) => {
9718
- this.deps.getP2p()?.broadcastPtyOutput(key, data);
9786
+ this.deps.getP2p()?.broadcastPtyOutput(cliInstance.instanceId, data);
9719
9787
  }
9720
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
+ });
9721
9798
  } catch (spawnErr) {
9722
9799
  LOG.error("CLI", `[${cliType}] Spawn failed: ${spawnErr?.message}`);
9723
9800
  instanceManager.removeInstance(key);
@@ -9739,6 +9816,7 @@ ${installInfo}`
9739
9816
  if (this.adapters.has(key)) {
9740
9817
  this.adapters.delete(key);
9741
9818
  this.deps.removeAgentTracking(key);
9819
+ sessionRegistry?.unregisterByInstanceKey(key);
9742
9820
  instanceManager.removeInstance(key);
9743
9821
  LOG.info("CLI", `\u{1F9F9} Auto-cleaned ${status.status} CLI: ${cliType}`);
9744
9822
  this.deps.onStatusChange();
@@ -9799,12 +9877,14 @@ ${installInfo}`
9799
9877
  }
9800
9878
  this.adapters.delete(key);
9801
9879
  this.deps.removeAgentTracking(key);
9880
+ this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
9802
9881
  this.deps.getInstanceManager()?.removeInstance(key);
9803
9882
  LOG.info("CLI", `\u{1F6D1} Agent stopped: ${adapter.cliType} in ${adapter.workingDir}`);
9804
9883
  this.deps.onStatusChange();
9805
9884
  } else {
9806
9885
  const im = this.deps.getInstanceManager();
9807
9886
  if (im) {
9887
+ this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
9808
9888
  im.removeInstance(key);
9809
9889
  this.deps.removeAgentTracking(key);
9810
9890
  LOG.warn("CLI", `\u{1F9F9} Force-removed orphan entry: ${key}`);
@@ -9819,7 +9899,7 @@ ${installInfo}`
9819
9899
  // ─── Adapter search ─────────────────────────────
9820
9900
  /**
9821
9901
  * Search for CLI adapter. Priority order:
9822
- * 0. instanceKey (UUID direct match) — extracted from _targetInstance / composite ID
9902
+ * 0. sessionId (UUID direct match)
9823
9903
  * 1. agentType + dir (iteration match)
9824
9904
  * 2. agentType fuzzy match (⚠ returns first match when multiple sessions exist)
9825
9905
  */
@@ -9887,7 +9967,7 @@ ${installInfo}`
9887
9967
  const cliType = args?.cliType;
9888
9968
  const dir = args?.dir || "";
9889
9969
  if (!cliType) throw new Error("cliType required");
9890
- const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
9970
+ const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
9891
9971
  if (found) {
9892
9972
  await this.stopSession(found.key);
9893
9973
  } else {
@@ -9919,7 +9999,7 @@ ${installInfo}`
9919
9999
  }
9920
10000
  const dir = rdir.path;
9921
10001
  if (!cliType) throw new Error("cliType required");
9922
- const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
10002
+ const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
9923
10003
  if (found) await this.stopSession(found.key);
9924
10004
  await this.startSession(cliType, dir);
9925
10005
  this.persistRecentDir(cliType, dir);
@@ -9931,7 +10011,7 @@ ${installInfo}`
9931
10011
  if (!agentType || !action) throw new Error("agentType and action required");
9932
10012
  const found = this.findAdapter(agentType, {
9933
10013
  dir: args?.dir,
9934
- instanceKey: args?._targetInstance
10014
+ instanceKey: args?.targetSessionId
9935
10015
  });
9936
10016
  if (!found) throw new Error(`CLI agent not running: ${agentType}`);
9937
10017
  const { adapter, key } = found;
@@ -10077,14 +10157,8 @@ var ProviderStreamAdapter = class {
10077
10157
  // src/agent-stream/manager.ts
10078
10158
  init_logger();
10079
10159
  var DaemonAgentStreamManager = class {
10080
- allAdapters = [];
10081
- managed = /* @__PURE__ */ new Map();
10082
- enabled = true;
10083
- logFn;
10084
- lastDiscoveryTime = 0;
10085
- discoveryIntervalMs = 1e4;
10086
- _activeAgentType = null;
10087
- constructor(logFn, providerLoader) {
10160
+ constructor(logFn, providerLoader, sessionRegistry) {
10161
+ this.sessionRegistry = sessionRegistry;
10088
10162
  this.logFn = logFn || LOG.forComponent("AgentStream").asLogFn();
10089
10163
  if (providerLoader) {
10090
10164
  const allExtProviders = providerLoader.getByCategory("extension");
@@ -10092,224 +10166,278 @@ var DaemonAgentStreamManager = class {
10092
10166
  const resolved = providerLoader.resolve(p.type);
10093
10167
  if (!resolved) continue;
10094
10168
  const adapter = new ProviderStreamAdapter(resolved);
10095
- this.allAdapters.push(adapter);
10169
+ this.adaptersByType.set(p.type, adapter);
10096
10170
  this.logFn(`[AgentStream] Adapter created: ${p.type} (${p.name}) scripts=${Object.keys(resolved.scripts || {}).join(",") || "none"}`);
10097
10171
  }
10098
10172
  }
10099
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();
10100
10181
  setEnabled(enabled) {
10101
10182
  this.enabled = enabled;
10102
10183
  }
10103
10184
  get isEnabled() {
10104
10185
  return this.enabled;
10105
10186
  }
10106
- get activeAgentType() {
10107
- return this._activeAgentType;
10187
+ getActiveSessionId(parentSessionId) {
10188
+ return this.activeSessionIdByParent.get(parentSessionId) || null;
10189
+ }
10190
+ getSessionTarget(sessionId) {
10191
+ return this.sessionRegistry?.get(sessionId);
10192
+ }
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);
10108
10202
  }
10109
10203
  /** Panel focus based on provider.js focusPanel or extensionId (currently no-op) */
10110
- async ensureAgentPanelOpen(agentType, targetIdeType) {
10204
+ async ensureSessionPanelOpen(_sessionId) {
10111
10205
  }
10112
- async switchActiveAgent(cdp, agentType) {
10113
- if (this._activeAgentType === agentType) return;
10114
- if (this._activeAgentType) {
10115
- const prev = this.managed.get(this._activeAgentType);
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);
10116
10211
  if (prev) {
10117
10212
  try {
10118
- await cdp.detachAgent(prev.sessionId);
10213
+ await cdp.detachAgent(prev.cdpSessionId);
10119
10214
  } catch {
10120
10215
  }
10121
- this.managed.delete(this._activeAgentType);
10122
- this.logFn(`[AgentStream] Deactivated: ${prev.adapter.agentName}`);
10123
- }
10124
- }
10125
- this._activeAgentType = agentType;
10126
- this.lastDiscoveryTime = 0;
10127
- this.logFn(`[AgentStream] Active agent: ${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;
10128
10251
  }
10129
10252
  /** Agent webview discovery + session connection */
10130
- async syncAgentSessions(cdp) {
10131
- if (!this.enabled || !this._activeAgentType) return;
10253
+ async syncActiveSession(cdp, parentSessionId) {
10254
+ const activeSessionId = this.getActiveSessionId(parentSessionId);
10255
+ if (!this.enabled || !activeSessionId) return;
10132
10256
  const now = Date.now();
10133
- if (this.managed.has(this._activeAgentType) && now - this.lastDiscoveryTime < this.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) {
10134
10261
  return;
10135
10262
  }
10136
- this.lastDiscoveryTime = now;
10263
+ this.lastDiscoveryTimeByParent.set(parentSessionId, now);
10137
10264
  try {
10138
- const targets = await cdp.discoverAgentWebviews();
10139
- const activeTarget = targets.find((t) => t.agentType === this._activeAgentType);
10140
- if (activeTarget && !this.managed.has(this._activeAgentType)) {
10141
- const adapter = this.allAdapters.find((a) => a.agentType === this._activeAgentType);
10142
- if (adapter) {
10143
- const sessionId = await cdp.attachToAgent(activeTarget);
10144
- if (sessionId) {
10145
- this.managed.set(this._activeAgentType, {
10146
- adapter,
10147
- sessionId,
10148
- target: activeTarget,
10149
- lastState: null,
10150
- lastError: null,
10151
- lastHiddenCheckTime: 0
10152
- });
10153
- this.logFn(`[AgentStream] Connected: ${adapter.agentName}`);
10154
- }
10155
- }
10265
+ if (!managed) {
10266
+ await this.connectManagedSession(cdp, parentSessionId, activeSessionId);
10156
10267
  }
10157
- for (const [type, agent] of this.managed) {
10158
- if (type !== this._activeAgentType) {
10159
- await cdp.detachAgent(agent.sessionId);
10160
- this.managed.delete(type);
10161
- }
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();
10162
10295
  }
10163
- this.discoveryIntervalMs = this.managed.has(this._activeAgentType) ? 3e4 : 1e4;
10296
+ return state;
10164
10297
  } catch (e) {
10165
- this.logFn(`[AgentStream] sync error: ${e.message}`);
10166
- }
10167
- }
10168
- /** Collect active agent status */
10169
- async collectAgentStreams(cdp) {
10170
- if (!this.enabled) return [];
10171
- const results = [];
10172
- if (this._activeAgentType && this.managed.has(this._activeAgentType)) {
10173
- const agent = this.managed.get(this._activeAgentType);
10174
- const type = this._activeAgentType;
10175
- const isHidden = agent.lastState?.status === "panel_hidden";
10176
- const hiddenCacheFresh = isHidden && Date.now() - agent.lastHiddenCheckTime < 3e4;
10177
- if (hiddenCacheFresh) {
10178
- results.push(agent.lastState);
10179
- } 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")) {
10180
10302
  try {
10181
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10182
- const state = await agent.adapter.readChat(evaluate);
10183
- 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") : ""}`);
10184
- agent.lastState = state;
10185
- agent.lastError = null;
10186
- if (state.status === "panel_hidden") {
10187
- agent.lastHiddenCheckTime = Date.now();
10188
- }
10189
- results.push(state);
10190
- } catch (e) {
10191
- const errorMsg = e?.message || String(e);
10192
- this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
10193
- agent.lastError = errorMsg;
10194
- results.push({
10195
- agentType: type,
10196
- agentName: agent.adapter.agentName,
10197
- extensionId: agent.adapter.extensionId,
10198
- status: "disconnected",
10199
- messages: agent.lastState?.messages || [],
10200
- inputContent: ""
10201
- });
10202
- if (errorMsg.includes("timeout") || errorMsg.includes("not connected") || errorMsg.includes("Session")) {
10203
- try {
10204
- await cdp.detachAgent(agent.sessionId);
10205
- } catch {
10206
- }
10207
- this.managed.delete(type);
10208
- this.lastDiscoveryTime = 0;
10209
- }
10303
+ await cdp.detachAgent(agent.cdpSessionId);
10304
+ } catch {
10210
10305
  }
10306
+ this.managedBySessionId.delete(activeSessionId);
10307
+ this.lastDiscoveryTimeByParent.set(parentSessionId, 0);
10211
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
+ };
10212
10317
  }
10213
- return results;
10214
10318
  }
10215
- async sendToAgent(cdp, agentType, text, targetIdeType) {
10216
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10217
- const agent = this.managed.get(agentType);
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);
10218
10326
  if (!agent) return false;
10219
10327
  try {
10220
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10328
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10221
10329
  await agent.adapter.sendMessage(evaluate, text);
10222
10330
  return true;
10223
10331
  } catch (e) {
10224
- this.logFn(`[AgentStream] sendToAgent(${agentType}) error: ${e.message}`);
10332
+ this.logFn(`[AgentStream] sendToSession(${sessionId}) error: ${e.message}`);
10225
10333
  return false;
10226
10334
  }
10227
10335
  }
10228
- async resolveAgentAction(cdp, agentType, action, targetIdeType) {
10229
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10230
- const agent = this.managed.get(agentType);
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);
10231
10343
  if (!agent) return false;
10232
10344
  try {
10233
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10345
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10234
10346
  return await agent.adapter.resolveAction(evaluate, action);
10235
10347
  } catch (e) {
10236
- this.logFn(`[AgentStream] resolveAction(${agentType}) error: ${e.message}`);
10348
+ this.logFn(`[AgentStream] resolveAction(${sessionId}) error: ${e.message}`);
10237
10349
  return false;
10238
10350
  }
10239
10351
  }
10240
- async newAgentSession(cdp, agentType, targetIdeType) {
10241
- await this.ensureAgentPanelOpen(agentType, targetIdeType);
10242
- const agent = this.managed.get(agentType);
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);
10243
10359
  if (!agent) return false;
10244
10360
  try {
10245
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10361
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10246
10362
  await agent.adapter.newSession(evaluate);
10247
10363
  return true;
10248
10364
  } catch (e) {
10249
- this.logFn(`[AgentStream] newSession(${agentType}) error: ${e.message}`);
10365
+ this.logFn(`[AgentStream] newSession(${sessionId}) error: ${e.message}`);
10250
10366
  return false;
10251
10367
  }
10252
10368
  }
10253
- async listAgentChats(cdp, agentType) {
10254
- let agent = this.managed.get(agentType);
10255
- if (!agent) {
10256
- this.logFn(`[AgentStream] listChats: ${agentType} not managed, trying on-demand activation`);
10257
- await this.switchActiveAgent(cdp, agentType);
10258
- await this.syncAgentSessions(cdp);
10259
- agent = this.managed.get(agentType);
10260
- }
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);
10261
10375
  if (!agent || typeof agent.adapter.listChats !== "function") return [];
10262
10376
  try {
10263
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10377
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10264
10378
  return await agent.adapter.listChats(evaluate);
10265
10379
  } catch (e) {
10266
- this.logFn(`[AgentStream] listChats(${agentType}) error: ${e.message}`);
10380
+ this.logFn(`[AgentStream] listChats(${sessionId}) error: ${e.message}`);
10267
10381
  return [];
10268
10382
  }
10269
10383
  }
10270
- async switchAgentSession(cdp, agentType, sessionId) {
10271
- let agent = this.managed.get(agentType);
10272
- if (!agent) {
10273
- this.logFn(`[AgentStream] switchSession: ${agentType} not managed, trying on-demand activation`);
10274
- await this.switchActiveAgent(cdp, agentType);
10275
- await this.syncAgentSessions(cdp);
10276
- agent = this.managed.get(agentType);
10277
- }
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);
10278
10390
  if (!agent || typeof agent.adapter.switchSession !== "function") return false;
10279
10391
  try {
10280
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10281
- 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);
10282
10394
  } catch (e) {
10283
- this.logFn(`[AgentStream] switchSession(${agentType}) error: ${e.message}`);
10395
+ this.logFn(`[AgentStream] switchSession(${sessionId}) error: ${e.message}`);
10284
10396
  return false;
10285
10397
  }
10286
10398
  }
10287
- async focusAgentEditor(cdp, agentType) {
10288
- const agent = this.managed.get(agentType);
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);
10289
10405
  if (!agent || typeof agent.adapter.focusEditor !== "function") return false;
10290
10406
  try {
10291
- const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.sessionId, expr, timeout);
10407
+ const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
10292
10408
  await agent.adapter.focusEditor(evaluate);
10293
10409
  return true;
10294
10410
  } catch (e) {
10295
- this.logFn(`[AgentStream] focusEditor(${agentType}) error: ${e.message}`);
10411
+ this.logFn(`[AgentStream] focusEditor(${sessionId}) error: ${e.message}`);
10296
10412
  return false;
10297
10413
  }
10298
10414
  }
10299
- getConnectedAgents() {
10300
- return Array.from(this.managed.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()];
10301
10420
  }
10302
- getManagedAgent(agentType) {
10303
- return this.managed.get(agentType);
10421
+ getManagedSession(sessionId) {
10422
+ return this.managedBySessionId.get(sessionId);
10304
10423
  }
10305
- async dispose(cdp) {
10306
- for (const [, agent] of this.managed) {
10424
+ async dispose(cdpManagers) {
10425
+ for (const managed of this.managedBySessionId.values()) {
10426
+ const managerKey = this.getSessionTarget(managed.runtimeSessionId)?.cdpManagerKey;
10427
+ const cdp = managerKey ? cdpManagers.get(managerKey) : null;
10428
+ if (!cdp) continue;
10307
10429
  try {
10308
- await cdp.detachAgent(agent.sessionId);
10430
+ await cdp.detachAgent(managed.cdpSessionId);
10309
10431
  } catch {
10310
10432
  }
10311
10433
  }
10312
- this.managed.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);
10313
10441
  }
10314
10442
  };
10315
10443
 
@@ -10317,20 +10445,17 @@ var DaemonAgentStreamManager = class {
10317
10445
  init_logger();
10318
10446
  var AgentStreamPoller = class {
10319
10447
  deps;
10320
- _activeIdeType = null;
10321
10448
  timer = null;
10322
10449
  constructor(deps) {
10323
10450
  this.deps = deps;
10324
10451
  }
10325
10452
  /** Currently active IDE type for agent streaming */
10326
10453
  get activeIde() {
10327
- return this._activeIdeType;
10454
+ return null;
10328
10455
  }
10329
10456
  /** Reset active IDE tracking (e.g., when IDE is stopped) */
10330
- resetActiveIde(ideType) {
10331
- if (this._activeIdeType === ideType) {
10332
- this._activeIdeType = null;
10333
- }
10457
+ resetActiveIde(parentSessionId) {
10458
+ this.deps.agentStreamManager.resetParentSession(parentSessionId);
10334
10459
  }
10335
10460
  /** Start polling (idempotent — ignored if already started) */
10336
10461
  start(intervalMs = 5e3) {
@@ -10352,12 +10477,14 @@ var AgentStreamPoller = class {
10352
10477
  agentStreamManager,
10353
10478
  providerLoader,
10354
10479
  instanceManager,
10355
- cdpManagers
10480
+ cdpManagers,
10481
+ sessionRegistry
10356
10482
  } = this.deps;
10357
10483
  if (!agentStreamManager || cdpManagers.size === 0) return;
10358
10484
  for (const [ideType, cdp] of cdpManagers) {
10359
10485
  registerExtensionProviders(providerLoader, cdp, ideType);
10360
10486
  const ideInstance = instanceManager.getInstance(`ide:${ideType}`);
10487
+ const parentSessionId = ideInstance?.getInstanceId?.();
10361
10488
  if (ideInstance?.getExtensionTypes && ideInstance?.addExtension && ideInstance?.removeExtension) {
10362
10489
  const currentExtTypes = new Set(ideInstance.getExtensionTypes());
10363
10490
  const enabledExtTypes = new Set(
@@ -10365,6 +10492,10 @@ var AgentStreamPoller = class {
10365
10492
  );
10366
10493
  for (const extType of currentExtTypes) {
10367
10494
  if (!enabledExtTypes.has(extType)) {
10495
+ const extInstance = ideInstance.getExtension?.(extType);
10496
+ if (extInstance?.getInstanceId) {
10497
+ sessionRegistry.unregister(extInstance.getInstanceId());
10498
+ }
10368
10499
  ideInstance.removeExtension(extType);
10369
10500
  LOG.info("AgentStream", `Extension removed: ${extType} (disabled for ${ideType})`);
10370
10501
  }
@@ -10375,54 +10506,63 @@ var AgentStreamPoller = class {
10375
10506
  if (extProvider) {
10376
10507
  const extSettings = providerLoader.getSettings(extType);
10377
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
+ }
10378
10521
  LOG.info("AgentStream", `Extension added: ${extType} (enabled for ${ideType})`);
10379
10522
  }
10380
10523
  }
10381
10524
  }
10382
10525
  }
10383
- if (this._activeIdeType === ideType && agentStreamManager.activeAgentType) {
10384
- const activeType = agentStreamManager.activeAgentType;
10385
- const enabledExtTypes = new Set(
10386
- providerLoader.getEnabledExtensionProviders(ideType).map((p) => p.type)
10387
- );
10388
- if (!enabledExtTypes.has(activeType)) {
10389
- LOG.info("AgentStream", `Active agent ${activeType} was disabled for ${ideType} \u2014 detaching`);
10390
- await agentStreamManager.switchActiveAgent(cdp, null);
10391
- this._activeIdeType = 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);
10392
10533
  this.deps.onStreamsUpdated?.(ideType, []);
10393
10534
  }
10394
10535
  }
10395
- }
10396
- if (this._activeIdeType) {
10397
- const cdp = cdpManagers.get(this._activeIdeType);
10398
- if (cdp?.isConnected) {
10399
- try {
10400
- await agentStreamManager.syncAgentSessions(cdp);
10401
- const streams = await agentStreamManager.collectAgentStreams(cdp);
10402
- this.deps.onStreamsUpdated?.(this._activeIdeType, streams);
10403
- } catch {
10536
+ if (!cdp.isConnected) {
10537
+ if (parentSessionId && activeSessionId) {
10538
+ agentStreamManager.resetParentSession(parentSessionId);
10539
+ this.deps.onStreamsUpdated?.(ideType, []);
10404
10540
  }
10405
- return;
10541
+ continue;
10406
10542
  }
10407
- this._activeIdeType = null;
10408
- }
10409
- if (!agentStreamManager.activeAgentType) {
10410
- for (const [ideType, cdp] of cdpManagers) {
10411
- if (!cdp.isConnected) continue;
10543
+ let resolvedActiveSessionId = activeSessionId;
10544
+ if (!resolvedActiveSessionId && parentSessionId) {
10412
10545
  try {
10413
10546
  const discovered = await cdp.discoverAgentWebviews();
10414
- if (discovered.length > 0) {
10415
- this._activeIdeType = ideType;
10416
- await agentStreamManager.switchActiveAgent(cdp, discovered[0].agentType);
10417
- LOG.info("AgentStream", `Auto-activated: ${discovered[0].agentType} (${ideType})`);
10418
- await agentStreamManager.syncAgentSessions(cdp);
10419
- const streams = await agentStreamManager.collectAgentStreams(cdp);
10420
- this.deps.onStreamsUpdated?.(ideType, streams);
10421
- return;
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
+ }
10422
10555
  }
10423
10556
  } catch {
10424
10557
  }
10425
10558
  }
10559
+ if (!resolvedActiveSessionId || !parentSessionId) continue;
10560
+ try {
10561
+ await agentStreamManager.syncActiveSession(cdp, parentSessionId);
10562
+ const stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
10563
+ this.deps.onStreamsUpdated?.(ideType, stream ? [stream] : []);
10564
+ } catch {
10565
+ }
10426
10566
  }
10427
10567
  }
10428
10568
  };
@@ -10509,6 +10649,7 @@ var ProviderInstanceManager = class {
10509
10649
  ...event,
10510
10650
  providerType: instance.type,
10511
10651
  instanceId: state.instanceId,
10652
+ targetSessionId: state.instanceId,
10512
10653
  providerCategory: state.category
10513
10654
  });
10514
10655
  }
@@ -14319,6 +14460,63 @@ function launchIDE(ide, workspacePath) {
14319
14460
  }
14320
14461
  }
14321
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
+
14322
14520
  // src/boot/daemon-lifecycle.ts
14323
14521
  init_logger();
14324
14522
  init_config();
@@ -14358,11 +14556,14 @@ async function initDaemonComponents(config) {
14358
14556
  });
14359
14557
  const instanceManager = new ProviderInstanceManager();
14360
14558
  const cdpManagers = /* @__PURE__ */ new Map();
14361
- const instanceIdMap = /* @__PURE__ */ new Map();
14559
+ const sessionRegistry = new SessionRegistry();
14362
14560
  const detectedIdesRef = { value: [] };
14561
+ let agentStreamManager = null;
14562
+ let poller = null;
14363
14563
  const cliManager = new DaemonCliManager({
14364
14564
  ...config.cliManagerDeps,
14365
- getInstanceManager: () => instanceManager
14565
+ getInstanceManager: () => instanceManager,
14566
+ getSessionRegistry: () => sessionRegistry
14366
14567
  }, providerLoader);
14367
14568
  LOG.info("Init", "Detecting IDEs...");
14368
14569
  detectedIdesRef.value = await detectIDEs();
@@ -14372,7 +14573,7 @@ async function initDaemonComponents(config) {
14372
14573
  providerLoader,
14373
14574
  instanceManager,
14374
14575
  cdpManagers,
14375
- instanceIdMap
14576
+ sessionRegistry
14376
14577
  };
14377
14578
  const cdpInitializer = new DaemonCdpInitializer({
14378
14579
  providerLoader,
@@ -14381,6 +14582,19 @@ async function initDaemonComponents(config) {
14381
14582
  onConnected: async (ideType, manager, managerKey) => {
14382
14583
  await setupIdeInstance(cdpSetupContext, { ideType, manager, managerKey });
14383
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?.();
14384
14598
  }
14385
14599
  });
14386
14600
  await cdpInitializer.connectAll(detectedIdesRef.value);
@@ -14392,14 +14606,14 @@ async function initDaemonComponents(config) {
14392
14606
  adapters: cliManager.adapters,
14393
14607
  providerLoader,
14394
14608
  instanceManager,
14395
- instanceIdMap
14609
+ sessionRegistry
14396
14610
  });
14397
- const agentStreamManager = new DaemonAgentStreamManager(
14611
+ agentStreamManager = new DaemonAgentStreamManager(
14398
14612
  LOG.forComponent("AgentStream").asLogFn(),
14399
- providerLoader
14613
+ providerLoader,
14614
+ sessionRegistry
14400
14615
  );
14401
14616
  commandHandler.setAgentStreamManager(agentStreamManager);
14402
- let poller;
14403
14617
  const router = new DaemonCommandRouter({
14404
14618
  commandHandler,
14405
14619
  cliManager,
@@ -14407,7 +14621,7 @@ async function initDaemonComponents(config) {
14407
14621
  providerLoader,
14408
14622
  instanceManager,
14409
14623
  detectedIdes: detectedIdesRef,
14410
- instanceIdMap,
14624
+ sessionRegistry,
14411
14625
  onCdpManagerCreated: async (ideType, manager) => {
14412
14626
  await setupIdeInstance(cdpSetupContext, { ideType, manager });
14413
14627
  await config.onCdpManagerSetup?.(ideType, manager, ideType);
@@ -14422,6 +14636,7 @@ async function initDaemonComponents(config) {
14422
14636
  providerLoader,
14423
14637
  instanceManager,
14424
14638
  cdpManagers,
14639
+ sessionRegistry,
14425
14640
  onStreamsUpdated: config.onStreamsUpdated
14426
14641
  });
14427
14642
  poller.start();
@@ -14436,7 +14651,7 @@ async function initDaemonComponents(config) {
14436
14651
  poller,
14437
14652
  cdpInitializer,
14438
14653
  cdpManagers,
14439
- instanceIdMap,
14654
+ sessionRegistry,
14440
14655
  detectedIdes: detectedIdesRef
14441
14656
  };
14442
14657
  }
@@ -14464,9 +14679,8 @@ async function shutdownDaemonComponents(components) {
14464
14679
  poller.stop();
14465
14680
  cdpInitializer.stop();
14466
14681
  try {
14467
- const anyCdp = [...cdpManagers.values()].find((m) => m.isConnected);
14468
- if (agentStreamManager && anyCdp) {
14469
- await agentStreamManager.dispose(anyCdp);
14682
+ if (agentStreamManager) {
14683
+ await agentStreamManager.dispose(cdpManagers);
14470
14684
  }
14471
14685
  } catch (e) {
14472
14686
  LOG.warn("Shutdown", `AgentStream dispose: ${e?.message}`);
@@ -14511,10 +14725,7 @@ async function shutdownDaemonComponents(components) {
14511
14725
  ProviderLoader,
14512
14726
  VersionArchive,
14513
14727
  addCliHistory,
14514
- buildAllManagedEntries,
14515
- buildManagedAcps,
14516
- buildManagedClis,
14517
- buildManagedIdes,
14728
+ buildSessionEntries,
14518
14729
  buildStatusSnapshot,
14519
14730
  connectCdpManager,
14520
14731
  detectAllVersions,
@@ -14537,6 +14748,8 @@ async function shutdownDaemonComponents(components) {
14537
14748
  isCdpConnected,
14538
14749
  isExtensionInstalled,
14539
14750
  isIdeRunning,
14751
+ isManagedStatusWaiting,
14752
+ isManagedStatusWorking,
14540
14753
  isSetupComplete,
14541
14754
  killIdeProcess,
14542
14755
  launchIDE,
@@ -14544,6 +14757,8 @@ async function shutdownDaemonComponents(components) {
14544
14757
  loadConfig,
14545
14758
  logCommand,
14546
14759
  markSetupComplete,
14760
+ normalizeActiveChatData,
14761
+ normalizeManagedStatus,
14547
14762
  probeCdpPort,
14548
14763
  readChatHistory,
14549
14764
  registerExtensionProviders,