@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.
@@ -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.toLowerCase());
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);
152
- if (!sessionId && this._agentStream) {
153
- await this._agentStream.switchActiveAgent(cdp, provider.type);
154
- await this._agentStream.syncAgentSessions(cdp);
155
- sessionId = this.getExtensionSessionId(provider);
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,49 +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): string | null {
198
- if (provider.category !== 'extension' || !this._agentStream) return null;
199
- const managed = this._agentStream.getManagedAgent(provider.type);
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];
196
+ }
197
+
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 };
201
207
  }
202
208
 
203
- /** Extract ideType from _targetInstance or explicit ideType */
209
+ /** Extract CDP scope key from target session or explicit ideType */
204
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
+
205
217
  // Also accept explicit ideType from args (P2P input, agentType for extensions)
206
218
  if (args?.ideType) {
207
- // UUID managerKey via instanceIdMap (P2P sends UUID instance IDs)
208
- const mappedKey = this._ctx.instanceIdMap?.get(args.ideType);
209
- if (mappedKey) {
210
- return mappedKey;
211
- }
219
+ const target = this._ctx.sessionRegistry?.get(args.ideType);
220
+ if (target?.cdpManagerKey) return target.cdpManagerKey;
212
221
  // Exact match first
213
222
  if (this._ctx.cdpManagers.has(args.ideType)) {
214
223
  return args.ideType;
@@ -223,45 +232,6 @@ export class DaemonCommandHandler implements CommandHelpers {
223
232
  }
224
233
  }
225
234
 
226
- if (args?._targetInstance) {
227
- let raw = args._targetInstance as string;
228
- const ideMatch = raw.match(/:ide:(.+)$/);
229
- const cliMatch = raw.match(/:cli:(.+)$/);
230
- const acpMatch = raw.match(/:acp:(.+)$/);
231
- if (ideMatch) raw = ideMatch[1];
232
- else if (cliMatch) raw = cliMatch[1];
233
- else if (acpMatch) raw = acpMatch[1];
234
-
235
- if (this._ctx.instanceIdMap?.has(raw)) {
236
- return this._ctx.instanceIdMap.get(raw)!;
237
- }
238
-
239
- // Direct CDP manager key match (e.g. "cursor", "cursor_remote_vs")
240
- if (this._ctx.cdpManagers.has(raw)) {
241
- return raw;
242
- }
243
-
244
- // Prefix match for multi-window keys
245
- const found = findCdpManager(this._ctx.cdpManagers, raw);
246
- if (found) {
247
- for (const [k, m] of this._ctx.cdpManagers.entries()) {
248
- if (m === found) return k;
249
- }
250
- }
251
-
252
- // Fallback removed: returning first-connected CDP was the root cause of
253
- // input routing to wrong IDE (e.g. screenshot shows Cursor but input goes
254
- // to Antigravity). If no match is found, return undefined so the caller
255
- // gets an explicit error rather than silently routing to the wrong IDE.
256
-
257
- // Legacy: strip trailing _N suffix (e.g. "cursor_1" → "cursor")
258
- const lastUnderscore = raw.lastIndexOf('_');
259
- if (lastUnderscore > 0) {
260
- const stripped = raw.substring(0, lastUnderscore);
261
- if (this._ctx.cdpManagers.has(stripped)) return stripped;
262
- }
263
- return raw;
264
- }
265
235
  return undefined;
266
236
  }
267
237
 
@@ -272,15 +242,14 @@ export class DaemonCommandHandler implements CommandHelpers {
272
242
  // ─── Command Dispatcher ──────────────────────────
273
243
 
274
244
  async handle(cmd: string, args: any): Promise<CommandResult> {
275
- // Per-request: extract target IDE/provider type from args
276
- this._currentIdeType = this.extractIdeType(args);
277
- 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);
278
247
 
279
248
  // Commands without ideType CDP silently fail (prevent P2P retry spam)
280
- if (!this._currentIdeType && !this._currentProviderType) {
249
+ if (!this._currentRoute.session && !this._currentRoute.managerKey && !this._currentRoute.providerType) {
281
250
  const cdpCommands = ['send_chat', 'read_chat', 'list_chats', 'new_chat', 'switch_chat', 'set_mode', 'change_model', 'set_thought_level', 'resolve_action'];
282
251
  if (cdpCommands.includes(cmd)) {
283
- return { success: false, error: 'No ideType specified — cannot route command' };
252
+ return { success: false, error: 'No targetSessionId specified — cannot route command' };
284
253
  }
285
254
  }
286
255
 
@@ -351,14 +320,7 @@ export class DaemonCommandHandler implements CommandHelpers {
351
320
  case 'refresh_scripts': return this.handleRefreshScripts(args);
352
321
 
353
322
  // ─── Stream commands (stream-commands.ts) ───────────
354
- case 'agent_stream_switch': return Stream.handleAgentStreamSwitch(this, args);
355
- case 'agent_stream_read': return Stream.handleAgentStreamRead(this, args);
356
- case 'agent_stream_send': return Stream.handleAgentStreamSend(this, args);
357
- case 'agent_stream_resolve': return Stream.handleAgentStreamResolve(this, args);
358
- case 'agent_stream_new': return Stream.handleAgentStreamNew(this, args);
359
- case 'agent_stream_list_chats': return Stream.handleAgentStreamListChats(this, args);
360
- case 'agent_stream_switch_session': return Stream.handleAgentStreamSwitchSession(this, args);
361
- case 'agent_stream_focus': return Stream.handleAgentStreamFocus(this, args);
323
+ case 'focus_session': return Stream.handleFocusSession(this, args);
362
324
 
363
325
  // ─── PTY Raw I/O (stream-commands.ts) ─────────
364
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,166 +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()) return { success: false, error: 'AgentStream or CDP not available' };
15
- const agentType = args?.agentType || args?.agent || null;
16
- await h.agentStream.switchActiveAgent(h.getCdp()!, 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()) return { success: false, error: 'AgentStream or CDP not available' };
22
- const streams = await h.agentStream.collectAgentStreams(h.getCdp()!);
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.agentStream.activeAgentType;
50
- if (!resolvedAgent) return { success: false, error: 'agentType required' };
51
- const ok = await h.agentStream.sendToAgent(h.getCdp()!, resolvedAgent, text, h.currentIdeType);
52
- return { success: ok };
53
- }
54
-
55
- export async function handleAgentStreamResolve(h: CommandHelpers, args: any): Promise<CommandResult> {
56
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
57
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
58
- const action = args?.action as 'approve' | 'reject' || 'approve';
59
- if (!agentType) return { success: false, error: 'agentType required' };
60
- const ok = await h.agentStream.resolveAgentAction(h.getCdp()!, agentType, action, h.currentIdeType);
61
- return { success: ok };
62
- }
63
-
64
- export async function handleAgentStreamNew(h: CommandHelpers, args: any): Promise<CommandResult> {
65
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
66
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
67
- if (!agentType) return { success: false, error: 'agentType required' };
68
- const ok = await h.agentStream.newAgentSession(h.getCdp()!, agentType, h.currentIdeType);
69
- return { success: ok };
70
- }
71
-
72
- export async function handleAgentStreamListChats(h: CommandHelpers, args: any): Promise<CommandResult> {
73
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
74
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
75
- if (!agentType) return { success: false, error: 'agentType required' };
76
- const chats = await h.agentStream.listAgentChats(h.getCdp()!, agentType);
77
- return { success: true, chats };
78
- }
79
-
80
- export async function handleAgentStreamSwitchSession(h: CommandHelpers, args: any): Promise<CommandResult> {
11
+ export async function handleFocusSession(h: CommandHelpers, args: any): Promise<CommandResult> {
81
12
  if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
82
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
83
- const sessionId = args?.sessionId || args?.id;
84
- if (!agentType || !sessionId) return { success: false, error: 'agentType and sessionId required' };
85
- const ok = await h.agentStream.switchAgentSession(h.getCdp()!, agentType, sessionId);
86
- return { success: ok };
87
- }
88
-
89
- export async function handleAgentStreamFocus(h: CommandHelpers, args: any): Promise<CommandResult> {
90
- if (!h.agentStream || !h.getCdp()) return { success: false, error: 'AgentStream or CDP not available' };
91
- const agentType = args?.agentType || args?.agent || h.agentStream.activeAgentType;
92
- if (!agentType) return { success: false, error: 'agentType required' };
93
- await h.agentStream.ensureAgentPanelOpen(agentType, h.currentIdeType);
94
- const ok = await h.agentStream.focusAgentEditor(h.getCdp()!, 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);
95
16
  return { success: ok };
96
17
  }
97
18
 
98
19
  // ─── PTY Raw I/O ──────────────────────────────────
99
20
 
100
21
  export function handlePtyInput(h: CommandHelpers, args: any): CommandResult {
101
- const { cliType, data } = args || {};
22
+ const { cliType, data, targetSessionId } = args || {};
102
23
  if (!data) return { success: false, error: 'data required' };
103
-
104
- if (h.ctx.adapters) {
105
- const targetCli = cliType || '';
106
- if (!targetCli && h.ctx.adapters.size > 0) {
107
- const first = h.ctx.adapters.values().next().value;
108
- if (first && typeof first.writeRaw === 'function') {
109
- first.writeRaw(data);
110
- return { success: true };
111
- }
112
- }
113
- const directAdapter = h.ctx.adapters.get(targetCli);
114
- if (directAdapter && typeof directAdapter.writeRaw === 'function') {
115
- directAdapter.writeRaw(data);
116
- return { success: true };
117
- }
118
- for (const [, adapter] of h.ctx.adapters) {
119
- if (adapter.cliType === targetCli && typeof adapter.writeRaw === 'function') {
120
- adapter.writeRaw(data);
121
- return { success: true };
122
- }
123
- }
124
- for (const [key, adapter] of h.ctx.adapters) {
125
- if ((key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.writeRaw === 'function') {
126
- adapter.writeRaw(data);
127
- return { success: true };
128
- }
129
- }
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'}` };
130
27
  }
131
- return { success: false, error: `CLI adapter not found: ${cliType}` };
28
+ adapter.writeRaw(data);
29
+ return { success: true };
132
30
  }
133
31
 
134
32
  export function handlePtyResize(h: CommandHelpers, args: any): CommandResult {
135
- const { cliType, cols, rows, force } = args || {};
33
+ const { cliType, cols, rows, force, targetSessionId } = args || {};
136
34
  if (!cols || !rows) return { success: false, error: 'cols and rows required' };
137
-
138
- if (h.ctx.adapters) {
139
- const targetCli = cliType || '';
140
- if (!targetCli && h.ctx.adapters.size > 0) {
141
- const first = h.ctx.adapters.values().next().value;
142
- if (first && typeof first.resize === 'function') {
143
- if (force) { first.resize(cols - 1, rows); setTimeout(() => first.resize(cols, rows), 50); }
144
- else { first.resize(cols, rows); }
145
- return { success: true };
146
- }
147
- }
148
- const directAdapter = h.ctx.adapters.get(targetCli);
149
- if (directAdapter && typeof directAdapter.resize === 'function') {
150
- if (force) {
151
- directAdapter.resize(cols - 1, rows);
152
- setTimeout(() => directAdapter.resize(cols, rows), 50);
153
- } else {
154
- directAdapter.resize(cols, rows);
155
- }
156
- return { success: true };
157
- }
158
- for (const [key, adapter] of h.ctx.adapters) {
159
- if ((adapter.cliType === targetCli || key.startsWith(targetCli) || targetCli.startsWith(adapter.cliType)) && typeof adapter.resize === 'function') {
160
- if (force) {
161
- adapter.resize(cols - 1, rows);
162
- setTimeout(() => adapter.resize(cols, rows), 50);
163
- } else {
164
- adapter.resize(cols, rows);
165
- }
166
- return { success: true };
167
- }
168
- }
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);
169
44
  }
170
- return { success: false, error: `CLI adapter not found: ${cliType}` };
45
+ return { success: true };
171
46
  }
172
47
 
173
48
  // ─── Provider Settings ────────────────────────
@@ -210,7 +85,7 @@ export function handleSetProviderSetting(h: CommandHelpers, args: any): CommandR
210
85
 
211
86
  export async function handleExtensionScript(h: CommandHelpers, args: any, scriptName: string): Promise<CommandResult> {
212
87
  const { agentType, ideType } = args || {};
213
- 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 || ''}`);
214
89
  if (!agentType) return { success: false, error: 'agentType is required' };
215
90
 
216
91
  const loader = h.ctx.providerLoader;
@@ -240,7 +115,9 @@ export async function handleExtensionScript(h: CommandHelpers, args: any, script
240
115
  const scriptCode = scriptFn(normalizedArgs);
241
116
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
242
117
 
243
- 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);
244
121
  LOG.info('Command', `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
245
122
  const cdp = h.getCdp(cdpKey);
246
123
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || 'any'}` };
@@ -249,14 +126,15 @@ export async function handleExtensionScript(h: CommandHelpers, args: any, script
249
126
  let result: unknown;
250
127
 
251
128
  if (provider.category === 'extension') {
252
- const sessions = cdp.getAgentSessions();
253
- let targetSessionId: string | null = null;
254
- for (const [sessionId, target] of sessions) {
255
- if (target.agentType === agentType) {
256
- targetSessionId = sessionId;
257
- break;
258
- }
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);
259
135
  }
136
+ const managed = runtimeSessionId ? h.agentStream?.getManagedSession(runtimeSessionId) : null;
137
+ const targetSessionId = managed?.cdpSessionId || null;
260
138
 
261
139
  // IDE-level scripts (model/mode) — try session frame first, fallback to main page
262
140
  const IDE_LEVEL_SCRIPTS = ['listModes', 'setMode', 'listModels', 'setModel'];
@@ -338,7 +216,7 @@ export function handleGetIdeExtensions(h: CommandHelpers, args: any): CommandRes
338
216
  enabled: config.ideSettings?.[ide]?.extensions?.[p.type]?.enabled === true,
339
217
  }));
340
218
  }
341
- return { success: true, ides: result };
219
+ return { success: true, ideExtensions: result };
342
220
  }
343
221
 
344
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
  }
package/src/index.ts CHANGED
@@ -20,10 +20,11 @@ export type {
20
20
 
21
21
  // ── Shared Types (cross-package) ──
22
22
  export type {
23
- ManagedIdeEntry,
24
- ManagedCliEntry,
25
- ManagedAcpEntry,
26
- ManagedAgentStream,
23
+ SessionEntry,
24
+ SessionTransport,
25
+ SessionKind,
26
+ SessionCapability,
27
+ AgentSessionStream,
27
28
  AvailableProviderInfo,
28
29
  AcpConfigOption,
29
30
  AcpMode,
@@ -74,8 +75,10 @@ export type { CommandRouterDeps, CommandRouterResult } from './commands/router.j
74
75
 
75
76
  // ── Status ──
76
77
  export { DaemonStatusReporter } from './status/reporter.js';
77
- export { buildManagedIdes, buildManagedClis, buildManagedAcps, buildAllManagedEntries, findCdpManager, hasCdpManager, isCdpConnected } from './status/builders.js';
78
+ export { buildSessionEntries, findCdpManager, hasCdpManager, isCdpConnected } from './status/builders.js';
78
79
  export { buildStatusSnapshot } from './status/snapshot.js';
80
+ export { normalizeManagedStatus, isManagedStatusWorking, isManagedStatusWaiting, normalizeActiveChatData } from './status/normalize.js';
81
+ export type { ManagedStatus } from './status/normalize.js';
79
82
  export type { StatusSnapshotOptions, StatusSnapshot } from './status/snapshot.js';
80
83
 
81
84
  // ── Logger ──
@@ -50,7 +50,7 @@ function maskArgs(args: any): Record<string, unknown> | undefined {
50
50
  ? `[${value.length} chars]`
51
51
  : '[masked]';
52
52
  } else if (key.startsWith('_')) {
53
- // internal fields: keep as-is (e.g. _targetType, _targetInstance)
53
+ // internal fields: keep as-is (e.g. targetSessionId)
54
54
  masked[key] = value;
55
55
  } else if (typeof value === 'object' && value !== null) {
56
56
  // Don't recurse deeply — just note the type