@adhdev/daemon-core 0.6.79 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@
5
5
  * the correct CDP manager or CLI adapter.
6
6
  *
7
7
  * Key concepts:
8
- * - extractIdeType(): determines target IDE from _targetInstance
8
+ * - extractIdeType(): determines target IDE from targetSessionId or ideType
9
9
  * - getCdp(): returns the DaemonCdpManager for current command
10
10
  * - getProvider(): returns the ProviderModule for current command
11
11
  * - handle(): main entry point, sets context then dispatches
@@ -20,6 +20,7 @@ import type { ProviderModule } from '../providers/contracts.js';
20
20
  import type { DaemonAgentStreamManager } from '../agent-stream/index.js';
21
21
  import { loadConfig } from '../config/config.js';
22
22
  import { ChatHistoryWriter } from '../config/chat-history.js';
23
+ import type { SessionRegistry, SessionRuntimeTarget } from '../sessions/registry.js';
23
24
  import { LOG } from '../logging/logger.js';
24
25
 
25
26
  // Sub-module imports
@@ -42,8 +43,7 @@ export interface CommandContext {
42
43
  providerLoader?: ProviderLoader;
43
44
  /** ProviderInstanceManager — for runtime settings propagation */
44
45
  instanceManager?: ProviderInstanceManager;
45
- /** UUID instanceId → CDP manager key (ideType) mapping */
46
- instanceIdMap?: Map<string, string>;
46
+ sessionRegistry?: SessionRegistry;
47
47
  }
48
48
 
49
49
  /**
@@ -56,8 +56,10 @@ export interface CommandHelpers {
56
56
  getProviderScript(scriptName: string, params?: Record<string, string>, ideType?: string): string | null;
57
57
  evaluateProviderScript(scriptName: string, params?: Record<string, string>, timeout?: number): Promise<{ result: any; category: string } | null>;
58
58
  getCliAdapter(type?: string): any | null;
59
+ readonly currentManagerKey: string | undefined;
59
60
  readonly currentIdeType: string | undefined;
60
61
  readonly currentProviderType: string | undefined;
62
+ readonly currentSession: SessionRuntimeTarget | undefined;
61
63
  readonly agentStream: DaemonAgentStreamManager | null;
62
64
  readonly ctx: CommandContext;
63
65
  readonly historyWriter: ChatHistoryWriter;
@@ -69,10 +71,12 @@ export class DaemonCommandHandler implements CommandHelpers {
69
71
  private domHandlers: CdpDomHandlers;
70
72
  private _historyWriter: ChatHistoryWriter;
71
73
 
72
- /** Current IDE type extracted from command args (per-request) */
73
- private _currentIdeType: string | undefined;
74
- /** Current provider type — agentType priority, ideType use */
75
- private _currentProviderType: string | undefined;
74
+ /** Current request route context */
75
+ private _currentRoute: {
76
+ session?: SessionRuntimeTarget;
77
+ managerKey?: string;
78
+ providerType?: string;
79
+ } = {};
76
80
 
77
81
  constructor(ctx: CommandContext) {
78
82
  this._ctx = ctx;
@@ -85,19 +89,18 @@ export class DaemonCommandHandler implements CommandHelpers {
85
89
  get ctx(): CommandContext { return this._ctx; }
86
90
  get agentStream(): DaemonAgentStreamManager | null { return this._agentStream; }
87
91
  get historyWriter(): ChatHistoryWriter { return this._historyWriter; }
88
- get currentIdeType(): string | undefined { return this._currentIdeType; }
89
- get currentProviderType(): string | undefined { return this._currentProviderType; }
92
+ get currentManagerKey(): string | undefined { return this._currentRoute.managerKey; }
93
+ get currentIdeType(): string | undefined { return this._currentRoute.managerKey; }
94
+ get currentProviderType(): string | undefined { return this._currentRoute.providerType; }
95
+ get currentSession(): SessionRuntimeTarget | undefined { return this._currentRoute.session; }
90
96
 
91
- /** Get CDP manager for a specific ideType or managerKey.
92
- * Supports exact match, multi-window prefix match, and instanceIdMap UUID lookup.
93
- * Returns null if no match — never falls back to another IDE. */
97
+ /** Get CDP manager for a specific session or manager key. */
94
98
  getCdp(ideType?: string): DaemonCdpManager | null {
95
- const key = ideType || this._currentIdeType;
96
- if (!key) return null;
97
- // 1. Try instanceIdMap (UUID → managerKey)
98
- const resolved = this._ctx.instanceIdMap?.get(key) || key;
99
- // 2. Use findCdpManager (exact + prefix match)
100
- const m = findCdpManager(this._ctx.cdpManagers, resolved);
99
+ const requested = ideType || this._currentRoute.session?.sessionId || this._currentRoute.managerKey;
100
+ if (!requested) return null;
101
+ const session = this._ctx.sessionRegistry?.get(requested);
102
+ const managerKey = session?.cdpManagerKey || requested;
103
+ const m = findCdpManager(this._ctx.cdpManagers, managerKey);
101
104
  if (m?.isConnected) return m;
102
105
  return null;
103
106
  }
@@ -106,7 +109,7 @@ export class DaemonCommandHandler implements CommandHelpers {
106
109
  * Get provider module — _currentProviderType (agentType priority) use.
107
110
  */
108
111
  getProvider(overrideType?: string): ProviderModule | undefined {
109
- const key = overrideType || this._currentProviderType || this._currentIdeType;
112
+ const key = overrideType || this._currentRoute.providerType || this._currentRoute.session?.providerType || this._currentRoute.managerKey;
110
113
  if (!key || !this._ctx.providerLoader) return undefined;
111
114
  const result = this._ctx.providerLoader.resolve(key);
112
115
  if (result) return result;
@@ -148,14 +151,22 @@ export class DaemonCommandHandler implements CommandHelpers {
148
151
 
149
152
  // Extension: evaluateInSession
150
153
  if (provider?.category === 'extension') {
151
- let sessionId = this.getExtensionSessionId(provider, this._currentIdeType);
152
- if (!sessionId && this._agentStream && this._currentIdeType) {
153
- await this._agentStream.switchActiveAgent(cdp, this._currentIdeType, provider.type);
154
- await this._agentStream.syncAgentSessions(cdp, this._currentIdeType);
155
- sessionId = this.getExtensionSessionId(provider, this._currentIdeType);
154
+ let sessionId: string | null = this._currentRoute.session?.sessionId || null;
155
+ if (!sessionId && this._currentRoute.session?.parentSessionId) {
156
+ sessionId = this._agentStream?.resolveSessionForAgent(this._currentRoute.session.parentSessionId, provider.type) || null;
157
+ }
158
+ if (sessionId && this._agentStream) {
159
+ const target = this._ctx.sessionRegistry?.get(sessionId);
160
+ if (target?.parentSessionId) {
161
+ await this._agentStream.setActiveSession(cdp, target.parentSessionId, sessionId);
162
+ await this._agentStream.syncActiveSession(cdp, target.parentSessionId);
163
+ }
156
164
  }
157
165
  if (!sessionId) return null;
158
- const result = await cdp.evaluateInSessionFrame(sessionId, script, timeout);
166
+ const managed = this._agentStream?.getManagedSession(sessionId);
167
+ const cdpSessionId = managed?.cdpSessionId;
168
+ if (!cdpSessionId) return null;
169
+ const result = await cdp.evaluateInSessionFrame(cdpSessionId, script, timeout);
159
170
  return { result, category: 'extension' };
160
171
  }
161
172
 
@@ -166,79 +177,47 @@ export class DaemonCommandHandler implements CommandHelpers {
166
177
 
167
178
  /** CLI adapter search */
168
179
  getCliAdapter(type?: string): any | null {
169
- const target = type || this._currentIdeType;
180
+ const target = type || this._currentRoute.session?.sessionId || this._currentRoute.providerType || this._currentRoute.managerKey;
170
181
  if (!target || !this._ctx.adapters) return null;
171
- // Normalize composite transport IDs:
172
- // standalone_xxx:cli:<uuid> -> <uuid>
173
- // daemon:acp:<uuid> -> <uuid>
174
- let normalizedTarget = target;
175
- const colonIdx = normalizedTarget.lastIndexOf(':');
176
- if (colonIdx >= 0) normalizedTarget = normalizedTarget.substring(colonIdx + 1);
177
-
178
- const direct = this._ctx.adapters.get(normalizedTarget);
179
- if (direct) return direct;
180
-
181
- for (const [key, adapter] of this._ctx.adapters.entries()) {
182
- if (
183
- (adapter as any).cliType === target
184
- || (adapter as any).cliType === normalizedTarget
185
- || key === normalizedTarget
186
- || key.startsWith(target)
187
- || key.startsWith(normalizedTarget)
188
- ) {
189
- return adapter;
190
- }
182
+ const session = this._ctx.sessionRegistry?.get(target);
183
+ if (session?.adapterKey) {
184
+ return this._ctx.adapters.get(session.adapterKey) || null;
191
185
  }
192
- return null;
186
+ return this._ctx.adapters.get(target) || null;
193
187
  }
194
188
 
195
189
  // ─── Private helpers ──────────────────────────────
196
190
 
197
- private getExtensionSessionId(provider: ProviderModule, scopeKey?: string): string | null {
198
- if (provider.category !== 'extension' || !this._agentStream || !scopeKey) return null;
199
- const managed = this._agentStream.getManagedAgent(provider.type, scopeKey);
200
- return managed?.sessionId || null;
191
+ private inferProviderType(key: string | undefined): string | undefined {
192
+ if (!key) return undefined;
193
+ const session = this._ctx.sessionRegistry?.get(key);
194
+ if (session?.providerType) return session.providerType;
195
+ return key.split('_')[0];
201
196
  }
202
197
 
203
- private resolveManagerKeyFromInstanceId(instanceId: string): string | undefined {
204
- const mapped = this._ctx.instanceIdMap?.get(instanceId);
205
- if (mapped) return mapped;
206
-
207
- const entries = (this._ctx.instanceManager as any)?.instances?.entries?.();
208
- if (!entries) return undefined;
209
-
210
- for (const [instanceKey, instance] of entries as Iterable<[string, any]>) {
211
- if (typeof instanceKey !== 'string' || !instanceKey.startsWith('ide:')) continue;
212
-
213
- if (typeof instance?.getInstanceId === 'function' && instance.getInstanceId() === instanceId) {
214
- const managerKey = instanceKey.slice(4);
215
- this._ctx.instanceIdMap?.set(instanceId, managerKey);
216
- return managerKey;
217
- }
218
-
219
- if (typeof instance?.getExtensionInstances === 'function') {
220
- for (const ext of instance.getExtensionInstances() || []) {
221
- if (typeof ext?.getInstanceId === 'function' && ext.getInstanceId() === instanceId) {
222
- const managerKey = instanceKey.slice(4);
223
- this._ctx.instanceIdMap?.set(instanceId, managerKey);
224
- return managerKey;
225
- }
226
- }
227
- }
228
- }
229
-
230
- return undefined;
198
+ private resolveRoute(args: any): { session?: SessionRuntimeTarget; managerKey?: string; providerType?: string } {
199
+ const session = this._ctx.sessionRegistry?.get(args?.targetSessionId);
200
+ const managerKey = this.extractIdeType(args);
201
+ const providerType =
202
+ args?.agentType
203
+ || args?.providerType
204
+ || session?.providerType
205
+ || this.inferProviderType(managerKey);
206
+ return { session, managerKey, providerType };
231
207
  }
232
208
 
233
- /** Extract ideType from _targetInstance or explicit ideType */
209
+ /** Extract CDP scope key from target session or explicit ideType */
234
210
  private extractIdeType(args: any): string | undefined {
211
+ if (args?.targetSessionId) {
212
+ const target = this._ctx.sessionRegistry?.get(args.targetSessionId);
213
+ if (target?.cdpManagerKey) return target.cdpManagerKey;
214
+ if (this._ctx.cdpManagers.has(args.targetSessionId)) return args.targetSessionId;
215
+ }
216
+
235
217
  // Also accept explicit ideType from args (P2P input, agentType for extensions)
236
218
  if (args?.ideType) {
237
- // UUID managerKey via instanceIdMap (P2P sends UUID instance IDs)
238
- const mappedKey = this.resolveManagerKeyFromInstanceId(args.ideType);
239
- if (mappedKey) {
240
- return mappedKey;
241
- }
219
+ const target = this._ctx.sessionRegistry?.get(args.ideType);
220
+ if (target?.cdpManagerKey) return target.cdpManagerKey;
242
221
  // Exact match first
243
222
  if (this._ctx.cdpManagers.has(args.ideType)) {
244
223
  return args.ideType;
@@ -253,46 +232,6 @@ export class DaemonCommandHandler implements CommandHelpers {
253
232
  }
254
233
  }
255
234
 
256
- if (args?._targetInstance) {
257
- let raw = args._targetInstance as string;
258
- const ideMatch = raw.match(/:ide:(.+)$/);
259
- const cliMatch = raw.match(/:cli:(.+)$/);
260
- const acpMatch = raw.match(/:acp:(.+)$/);
261
- if (ideMatch) raw = ideMatch[1];
262
- else if (cliMatch) raw = cliMatch[1];
263
- else if (acpMatch) raw = acpMatch[1];
264
-
265
- const mappedKey = this.resolveManagerKeyFromInstanceId(raw);
266
- if (mappedKey) {
267
- return mappedKey;
268
- }
269
-
270
- // Direct CDP manager key match (e.g. "cursor", "cursor_remote_vs")
271
- if (this._ctx.cdpManagers.has(raw)) {
272
- return raw;
273
- }
274
-
275
- // Prefix match for multi-window keys
276
- const found = findCdpManager(this._ctx.cdpManagers, raw);
277
- if (found) {
278
- for (const [k, m] of this._ctx.cdpManagers.entries()) {
279
- if (m === found) return k;
280
- }
281
- }
282
-
283
- // Fallback removed: returning first-connected CDP was the root cause of
284
- // input routing to wrong IDE (e.g. screenshot shows Cursor but input goes
285
- // to Antigravity). If no match is found, return undefined so the caller
286
- // gets an explicit error rather than silently routing to the wrong IDE.
287
-
288
- // Legacy: strip trailing _N suffix (e.g. "cursor_1" → "cursor")
289
- const lastUnderscore = raw.lastIndexOf('_');
290
- if (lastUnderscore > 0) {
291
- const stripped = raw.substring(0, lastUnderscore);
292
- if (this._ctx.cdpManagers.has(stripped)) return stripped;
293
- }
294
- return raw;
295
- }
296
235
  return undefined;
297
236
  }
298
237
 
@@ -303,15 +242,14 @@ export class DaemonCommandHandler implements CommandHelpers {
303
242
  // ─── Command Dispatcher ──────────────────────────
304
243
 
305
244
  async handle(cmd: string, args: any): Promise<CommandResult> {
306
- // Per-request: extract target IDE/provider type from args
307
- this._currentIdeType = this.extractIdeType(args);
308
- this._currentProviderType = args?.agentType || args?.providerType || this._currentIdeType;
245
+ // Per-request: extract target session / CDP scope / provider type from args
246
+ this._currentRoute = this.resolveRoute(args);
309
247
 
310
248
  // Commands without ideType CDP silently fail (prevent P2P retry spam)
311
- if (!this._currentIdeType && !this._currentProviderType) {
249
+ if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
312
250
  const cdpCommands = ['send_chat', 'read_chat', 'list_chats', 'new_chat', 'switch_chat', 'set_mode', 'change_model', 'set_thought_level', 'resolve_action'];
313
251
  if (cdpCommands.includes(cmd)) {
314
- return { success: false, error: 'No ideType specified — cannot route command' };
252
+ return { success: false, error: 'No targetSessionId specified — cannot route command' };
315
253
  }
316
254
  }
317
255
 
@@ -382,14 +320,7 @@ export class DaemonCommandHandler implements CommandHelpers {
382
320
  case 'refresh_scripts': return this.handleRefreshScripts(args);
383
321
 
384
322
  // ─── Stream commands (stream-commands.ts) ───────────
385
- case 'agent_stream_switch': return Stream.handleAgentStreamSwitch(this, args);
386
- case 'agent_stream_read': return Stream.handleAgentStreamRead(this, args);
387
- case 'agent_stream_send': return Stream.handleAgentStreamSend(this, args);
388
- case 'agent_stream_resolve': return Stream.handleAgentStreamResolve(this, args);
389
- case 'agent_stream_new': return Stream.handleAgentStreamNew(this, args);
390
- case 'agent_stream_list_chats': return Stream.handleAgentStreamListChats(this, args);
391
- case 'agent_stream_switch_session': return Stream.handleAgentStreamSwitchSession(this, args);
392
- case 'agent_stream_focus': return Stream.handleAgentStreamFocus(this, args);
323
+ case 'focus_session': return Stream.handleFocusSession(this, args);
393
324
 
394
325
  // ─── PTY Raw I/O (stream-commands.ts) ─────────
395
326
  case 'pty_input': return Stream.handlePtyInput(this, args);
@@ -21,6 +21,7 @@ import { resolveIdeLaunchWorkspace } from '../config/workspaces.js';
21
21
  import { appendWorkspaceActivity } from '../config/workspace-activity.js';
22
22
  import { addCliHistory } from '../config/config.js';
23
23
  import { detectIDEs } from '../detection/ide-detector.js';
24
+ import { SessionRegistry } from '../sessions/registry.js';
24
25
  import { LOG } from '../logging/logger.js';
25
26
  import { logCommand } from '../logging/command-log.js';
26
27
  import { getRecentLogs, LOG_PATH } from '../logging/logger.js';
@@ -36,8 +37,7 @@ export interface CommandRouterDeps {
36
37
  instanceManager: ProviderInstanceManager;
37
38
  /** Reference to detected IDEs array (mutable — router updates it) */
38
39
  detectedIdes: { value: any[] };
39
- /** UUID instanceId → CDP manager key mapping */
40
- instanceIdMap: Map<string, string>;
40
+ sessionRegistry: SessionRegistry;
41
41
  /** Callback for CDP manager creation after launch_ide */
42
42
  onCdpManagerCreated?: (ideType: string, manager: DaemonCdpManager) => void;
43
43
  /** Callback after IDE connected (e.g., startAgentStreamPolling) */
@@ -60,7 +60,7 @@ export interface CommandRouterResult {
60
60
  // Commands that trigger post-chat status updates
61
61
  const CHAT_COMMANDS = [
62
62
  'send_chat', 'new_chat', 'switch_chat', 'set_mode',
63
- 'change_model', 'agent_stream_send',
63
+ 'change_model',
64
64
  ];
65
65
 
66
66
  export class DaemonCommandRouter {
@@ -344,6 +344,7 @@ export class DaemonCommandRouter {
344
344
  if (cdp) {
345
345
  try { cdp.disconnect(); } catch { /* noop */ }
346
346
  this.deps.cdpManagers.delete(key);
347
+ this.deps.sessionRegistry.unregisterByManagerKey(key);
347
348
  LOG.info('StopIDE', `CDP disconnected: ${key}`);
348
349
  }
349
350
  }
@@ -358,15 +359,6 @@ export class DaemonCommandRouter {
358
359
  for (const instanceKey of keysToRemove) {
359
360
  const ideInstance = this.deps.instanceManager.getInstance(instanceKey) as any;
360
361
  if (ideInstance) {
361
- // Remove IDE and child Extension UUIDs from instanceIdMap
362
- if (ideInstance.getInstanceId) {
363
- this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
364
- }
365
- if (ideInstance.getExtensionInstances) {
366
- for (const ext of ideInstance.getExtensionInstances()) {
367
- if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
368
- }
369
- }
370
362
  this.deps.instanceManager.removeInstance(instanceKey);
371
363
  LOG.info('StopIDE', `Instance removed: ${instanceKey}`);
372
364
  }
@@ -376,14 +368,6 @@ export class DaemonCommandRouter {
376
368
  const instanceKey = `ide:${ideType}`;
377
369
  const ideInstance = this.deps.instanceManager.getInstance(instanceKey) as any;
378
370
  if (ideInstance) {
379
- if (ideInstance.getInstanceId) {
380
- this.deps.instanceIdMap.delete(ideInstance.getInstanceId());
381
- }
382
- if (ideInstance.getExtensionInstances) {
383
- for (const ext of ideInstance.getExtensionInstances()) {
384
- if (ext.getInstanceId) this.deps.instanceIdMap.delete(ext.getInstanceId());
385
- }
386
- }
387
371
  this.deps.instanceManager.removeInstance(instanceKey);
388
372
  LOG.info('StopIDE', `Instance removed: ${instanceKey}`);
389
373
  }
@@ -8,172 +8,41 @@ import type { ProviderLoader } from '../providers/provider-loader.js';
8
8
  import { loadConfig } from '../config/config.js';
9
9
  import { LOG } from '../logging/logger.js';
10
10
 
11
- // ─── Agent Stream commands ───────────────────────
12
-
13
- export async function handleAgentStreamSwitch(h: CommandHelpers, args: any): Promise<CommandResult> {
14
- if (!h.agentStream || !h.getCdp() || !h.currentIdeType) return { success: false, error: 'AgentStream or CDP not available' };
15
- const agentType = args?.agentType || args?.agent || null;
16
- await h.agentStream.switchActiveAgent(h.getCdp()!, h.currentIdeType, agentType);
17
- return { success: true, activeAgent: agentType };
18
- }
19
-
20
- export async function handleAgentStreamRead(h: CommandHelpers, args: any): Promise<CommandResult> {
21
- if (!h.agentStream || !h.getCdp() || !h.currentIdeType) return { success: false, error: 'AgentStream or CDP not available' };
22
- const streams = await h.agentStream.collectAgentStreams(h.getCdp()!, h.currentIdeType);
23
- return { success: true, streams };
24
- }
25
-
26
- export async function handleAgentStreamSend(h: CommandHelpers, args: any): Promise<CommandResult> {
27
- const agentType = args?.agentType || args?.agent;
28
- const text = args?.text || args?.message;
29
- if (!text) return { success: false, error: 'text required' };
30
-
31
- // CLI adapter routing
32
- if (agentType && h.ctx.adapters) {
33
- for (const [key, adapter] of h.ctx.adapters.entries()) {
34
- if (adapter.cliType === agentType || key.includes(agentType)) {
35
- LOG.info('Command', `[agent_stream_send] Routing to CLI adapter: ${adapter.cliType}`);
36
- try {
37
- await adapter.sendMessage(text);
38
- return { success: true, sent: true, targetAgent: adapter.cliType };
39
- } catch (e: any) {
40
- LOG.info('Command', `[agent_stream_send] CLI adapter failed: ${e.message}`);
41
- return { success: false, error: `CLI send failed: ${e.message}` };
42
- }
43
- }
44
- }
45
- }
46
-
47
- // CDP-based IDE agent routing
48
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
49
- const resolvedAgent = agentType || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
50
- if (!resolvedAgent) return { success: false, error: 'agentType required' };
51
- if (!h.currentIdeType) return { success: false, error: 'ideType required' };
52
- const ok = await h.agentStream.sendToAgent(h.getCdp()!, h.currentIdeType, resolvedAgent, text, h.currentIdeType);
53
- return { success: ok };
54
- }
55
-
56
- export async function handleAgentStreamResolve(h: CommandHelpers, args: any): Promise<CommandResult> {
57
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
58
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
59
- const action = args?.action as 'approve' | 'reject' || 'approve';
60
- if (!agentType) return { success: false, error: 'agentType required' };
61
- if (!h.currentIdeType) return { success: false, error: 'ideType required' };
62
- const ok = await h.agentStream.resolveAgentAction(h.getCdp()!, h.currentIdeType, agentType, action, h.currentIdeType);
63
- return { success: ok };
64
- }
65
-
66
- export async function handleAgentStreamNew(h: CommandHelpers, args: any): Promise<CommandResult> {
67
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
68
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
69
- if (!agentType) return { success: false, error: 'agentType required' };
70
- if (!h.currentIdeType) return { success: false, error: 'ideType required' };
71
- const ok = await h.agentStream.newAgentSession(h.getCdp()!, h.currentIdeType, agentType, h.currentIdeType);
72
- return { success: ok };
73
- }
74
-
75
- export async function handleAgentStreamListChats(h: CommandHelpers, args: any): Promise<CommandResult> {
76
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
77
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
78
- if (!agentType) return { success: false, error: 'agentType required' };
79
- if (!h.currentIdeType) return { success: false, error: 'ideType required' };
80
- const chats = await h.agentStream.listAgentChats(h.getCdp()!, h.currentIdeType, agentType);
81
- return { success: true, chats };
82
- }
83
-
84
- export async function handleAgentStreamSwitchSession(h: CommandHelpers, args: any): Promise<CommandResult> {
11
+ export async function handleFocusSession(h: CommandHelpers, args: any): Promise<CommandResult> {
85
12
  if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
86
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
87
- const sessionId = args?.sessionId || args?.id;
88
- if (!agentType || !sessionId) return { success: false, error: 'agentType and sessionId required' };
89
- if (!h.currentIdeType) return { success: false, error: 'ideType required' };
90
- const ok = await h.agentStream.switchAgentSession(h.getCdp()!, h.currentIdeType, agentType, sessionId);
91
- return { success: ok };
92
- }
93
-
94
- export async function handleAgentStreamFocus(h: CommandHelpers, args: any): Promise<CommandResult> {
95
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
96
- const agentType = args?.agentType || args?.agent || (h.currentIdeType ? h.agentStream.getActiveAgentType(h.currentIdeType) : null);
97
- if (!agentType) return { success: false, error: 'agentType required' };
98
- await h.agentStream.ensureAgentPanelOpen(agentType, h.currentIdeType);
99
- if (!h.currentIdeType) return { success: false, error: 'ideType required' };
100
- const ok = await h.agentStream.focusAgentEditor(h.getCdp()!, h.currentIdeType, agentType);
13
+ const sessionId = args?.targetSessionId || h.currentSession?.sessionId;
14
+ if (!sessionId) return { success: false, error: 'targetSessionId required' };
15
+ const ok = await h.agentStream.focusSession(h.getCdp()!, sessionId);
101
16
  return { success: ok };
102
17
  }
103
18
 
104
19
  // ─── PTY Raw I/O ──────────────────────────────────
105
20
 
106
21
  export function handlePtyInput(h: CommandHelpers, args: any): CommandResult {
107
- const { cliType, data } = args || {};
22
+ const { cliType, data, targetSessionId } = args || {};
108
23
  if (!data) return { success: false, error: 'data required' };
109
-
110
- if (h.ctx.adapters) {
111
- const targetCli = cliType || '';
112
- if (!targetCli && h.ctx.adapters.size > 0) {
113
- const first = h.ctx.adapters.values().next().value;
114
- if (first && typeof first.writeRaw === 'function') {
115
- first.writeRaw(data);
116
- return { success: true };
117
- }
118
- }
119
- const directAdapter = h.ctx.adapters.get(targetCli);
120
- if (directAdapter && typeof directAdapter.writeRaw === 'function') {
121
- directAdapter.writeRaw(data);
122
- return { success: true };
123
- }
124
- for (const [, adapter] of h.ctx.adapters) {
125
- if (adapter.cliType === targetCli && typeof adapter.writeRaw === 'function') {
126
- adapter.writeRaw(data);
127
- return { success: true };
128
- }
129
- }
130
- for (const [key, adapter] of h.ctx.adapters) {
131
- if ((key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.writeRaw === 'function') {
132
- adapter.writeRaw(data);
133
- return { success: true };
134
- }
135
- }
24
+ const adapter = h.getCliAdapter(targetSessionId || cliType);
25
+ if (!adapter || typeof adapter.writeRaw !== 'function') {
26
+ return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || 'unknown'}` };
136
27
  }
137
- return { success: false, error: `CLI adapter not found: ${cliType}` };
28
+ adapter.writeRaw(data);
29
+ return { success: true };
138
30
  }
139
31
 
140
32
  export function handlePtyResize(h: CommandHelpers, args: any): CommandResult {
141
- const { cliType, cols, rows, force } = args || {};
33
+ const { cliType, cols, rows, force, targetSessionId } = args || {};
142
34
  if (!cols || !rows) return { success: false, error: 'cols and rows required' };
143
-
144
- if (h.ctx.adapters) {
145
- const targetCli = cliType || '';
146
- if (!targetCli && h.ctx.adapters.size > 0) {
147
- const first = h.ctx.adapters.values().next().value;
148
- if (first && typeof first.resize === 'function') {
149
- if (force) { first.resize(cols - 1, rows); setTimeout(() => first.resize(cols, rows), 50); }
150
- else { first.resize(cols, rows); }
151
- return { success: true };
152
- }
153
- }
154
- const directAdapter = h.ctx.adapters.get(targetCli);
155
- if (directAdapter && typeof directAdapter.resize === 'function') {
156
- if (force) {
157
- directAdapter.resize(cols - 1, rows);
158
- setTimeout(() => directAdapter.resize(cols, rows), 50);
159
- } else {
160
- directAdapter.resize(cols, rows);
161
- }
162
- return { success: true };
163
- }
164
- for (const [key, adapter] of h.ctx.adapters) {
165
- if ((adapter.cliType === targetCli || key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.resize === 'function') {
166
- if (force) {
167
- adapter.resize(cols - 1, rows);
168
- setTimeout(() => adapter.resize(cols, rows), 50);
169
- } else {
170
- adapter.resize(cols, rows);
171
- }
172
- return { success: true };
173
- }
174
- }
35
+ const adapter = h.getCliAdapter(targetSessionId || cliType);
36
+ if (!adapter || typeof adapter.resize !== 'function') {
37
+ return { success: false, error: `CLI adapter not found: ${targetSessionId || cliType || 'unknown'}` };
38
+ }
39
+ if (force) {
40
+ adapter.resize(cols - 1, rows);
41
+ setTimeout(() => adapter.resize(cols, rows), 50);
42
+ } else {
43
+ adapter.resize(cols, rows);
175
44
  }
176
- return { success: false, error: `CLI adapter not found: ${cliType}` };
45
+ return { success: true };
177
46
  }
178
47
 
179
48
  // ─── Provider Settings ────────────────────────
@@ -216,7 +85,7 @@ export function handleSetProviderSetting(h: CommandHelpers, args: any): CommandR
216
85
 
217
86
  export async function handleExtensionScript(h: CommandHelpers, args: any, scriptName: string): Promise<CommandResult> {
218
87
  const { agentType, ideType } = args || {};
219
- LOG.info('Command', `[ExtScript] ${scriptName} agentType=${agentType} ideType=${ideType} _currentIdeType=${h.currentIdeType}`);
88
+ LOG.info('Command', `[ExtScript] ${scriptName} agentType=${agentType} ideType=${ideType} session=${h.currentSession?.sessionId || ''}`);
220
89
  if (!agentType) return { success: false, error: 'agentType is required' };
221
90
 
222
91
  const loader = h.ctx.providerLoader;
@@ -246,7 +115,9 @@ export async function handleExtensionScript(h: CommandHelpers, args: any, script
246
115
  const scriptCode = scriptFn(normalizedArgs);
247
116
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
248
117
 
249
- const cdpKey = provider.category === 'ide' ? (h.currentIdeType || agentType) : (h.currentIdeType || ideType);
118
+ const cdpKey = provider.category === 'ide'
119
+ ? (h.currentSession?.cdpManagerKey || h.currentManagerKey || agentType)
120
+ : (h.currentSession?.cdpManagerKey || h.currentManagerKey || ideType);
250
121
  LOG.info('Command', `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
251
122
  const cdp = h.getCdp(cdpKey);
252
123
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || 'any'}` };
@@ -255,14 +126,15 @@ export async function handleExtensionScript(h: CommandHelpers, args: any, script
255
126
  let result: unknown;
256
127
 
257
128
  if (provider.category === 'extension') {
258
- const sessions = cdp.getAgentSessions();
259
- let targetSessionId: string | null = null;
260
- for (const [sessionId, target] of sessions) {
261
- if (target.agentType === agentType) {
262
- targetSessionId = sessionId;
263
- break;
264
- }
129
+ const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
130
+ if (!runtimeSessionId) return { success: false, error: `No target session found for ${agentType}` };
131
+ const parentSessionId = h.currentSession?.parentSessionId;
132
+ if (parentSessionId) {
133
+ await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
134
+ await h.agentStream?.syncActiveSession(cdp, parentSessionId);
265
135
  }
136
+ const managed = runtimeSessionId ? h.agentStream?.getManagedSession(runtimeSessionId) : null;
137
+ const targetSessionId = managed?.cdpSessionId || null;
266
138
 
267
139
  // IDE-level scripts (model/mode) — try session frame first, fallback to main page
268
140
  const IDE_LEVEL_SCRIPTS = ['listModes', 'setMode', 'listModels', 'setModel'];
@@ -344,7 +216,7 @@ export function handleGetIdeExtensions(h: CommandHelpers, args: any): CommandRes
344
216
  enabled: config.ideSettings?.[ide]?.extensions?.[p.type]?.enabled === true,
345
217
  }));
346
218
  }
347
- return { success: true, ides: result };
219
+ return { success: true, ideExtensions: result };
348
220
  }
349
221
 
350
222
  export function handleSetIdeExtension(h: CommandHelpers, args: any): CommandResult {
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { StatusResponse, CommandResult, DaemonEvent } from './types.js';
9
- import type { ManagedIdeEntry, ManagedCliEntry, ManagedAcpEntry } from './shared-types.js';
9
+ import type { SessionEntry } from './shared-types.js';
10
10
 
11
11
  export interface DaemonCoreOptions {
12
12
  /** Data directory for config, logs */
@@ -40,12 +40,6 @@ export interface IDaemonCore {
40
40
  /** Execute a command (send_chat, new_session, etc.) */
41
41
  executeCommand(type: string, payload: any, target?: string): Promise<CommandResult>;
42
42
 
43
- /** Get currently detected/managed IDEs */
44
- getManagedIdes(): ManagedIdeEntry[];
45
-
46
- /** Get currently detected/managed CLIs */
47
- getManagedClis(): ManagedCliEntry[];
48
-
49
- /** Get currently detected/managed ACP agents */
50
- getManagedAcps(): ManagedAcpEntry[];
43
+ /** Get current canonical runtime sessions */
44
+ getSessions(): SessionEntry[];
51
45
  }