@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.
@@ -7,15 +7,49 @@ import type { CommandResult, CommandHelpers } from './handler.js';
7
7
  import { readChatHistory } from '../config/chat-history.js';
8
8
  import { LOG } from '../logging/logger.js';
9
9
 
10
+ const RECENT_SEND_WINDOW_MS = 1200;
11
+ const recentSendByTarget = new Map<string, number>();
12
+
13
+ function getCurrentProviderType(h: CommandHelpers, fallback = ''): string {
14
+ return h.currentSession?.providerType || h.currentProviderType || fallback;
15
+ }
16
+
17
+ function getCurrentManagerKey(h: CommandHelpers): string {
18
+ return h.currentSession?.cdpManagerKey || h.currentManagerKey || '';
19
+ }
20
+
10
21
  function getTargetedCliAdapter(h: CommandHelpers, args: any, providerType?: string) {
11
- return h.getCliAdapter(args?._targetInstance || h.currentIdeType || providerType);
22
+ return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
23
+ }
24
+
25
+ function buildRecentSendKey(h: CommandHelpers, args: any, provider: any, text: string): string {
26
+ const target =
27
+ args?.targetSessionId
28
+ || args?.agentType
29
+ || h.currentSession?.providerType
30
+ || h.currentProviderType
31
+ || h.currentManagerKey
32
+ || 'unknown';
33
+ return `${provider?.category || 'unknown'}:${target}:${text.trim()}`;
34
+ }
35
+
36
+ function isRecentDuplicateSend(key: string): boolean {
37
+ const now = Date.now();
38
+ for (const [candidate, ts] of recentSendByTarget.entries()) {
39
+ if (now - ts > RECENT_SEND_WINDOW_MS) recentSendByTarget.delete(candidate);
40
+ }
41
+ const previous = recentSendByTarget.get(key);
42
+ if (previous && (now - previous) <= RECENT_SEND_WINDOW_MS) return true;
43
+ recentSendByTarget.set(key, now);
44
+ return false;
12
45
  }
13
46
 
14
47
  export async function handleChatHistory(h: CommandHelpers, args: any): Promise<CommandResult> {
15
- const { agentType, offset, limit, instanceId } = args;
48
+ const { agentType, offset, limit } = args;
49
+ const instanceId = args?.targetSessionId;
16
50
  try {
17
51
  const provider = h.getProvider(agentType);
18
- const agentStr = provider?.type || agentType || h.currentIdeType || '';
52
+ const agentStr = provider?.type || agentType || getCurrentProviderType(h);
19
53
  const result = readChatHistory(agentStr, offset || 0, limit || 30, instanceId);
20
54
  return { success: true, ...result, agent: agentStr };
21
55
  } catch (e: any) {
@@ -60,7 +94,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
60
94
  provider.type || 'unknown_extension',
61
95
  parsed.messages || [],
62
96
  parsed.title,
63
- args?.instanceId
97
+ args?.targetSessionId
64
98
  );
65
99
  return { success: true, ...parsed };
66
100
  }
@@ -71,15 +105,18 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
71
105
  // Alternative: AgentStreamManager (script fail when)
72
106
  if (h.agentStream) {
73
107
  const cdp = h.getCdp();
74
- if (cdp) {
75
- const streams = await h.agentStream.collectAgentStreams(cdp);
76
- const stream = streams.find((s: any) => s.agentType === provider.type);
108
+ const parentSessionId = h.currentSession?.parentSessionId;
109
+ if (cdp && parentSessionId) {
110
+ const stream = await h.agentStream.collectActiveSession(cdp, parentSessionId);
111
+ if (stream?.agentType !== provider.type) {
112
+ return { success: true, messages: [], status: 'idle' };
113
+ }
77
114
  if (stream) {
78
115
  h.historyWriter.appendNewMessages(
79
116
  stream.agentType,
80
117
  stream.messages || [],
81
118
  undefined,
82
- args?.instanceId
119
+ args?.targetSessionId
83
120
  );
84
121
  return { success: true, messages: stream.messages || [], status: stream.status, agentType: stream.agentType };
85
122
  }
@@ -107,11 +144,11 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
107
144
  if (parsed && typeof parsed === 'object') {
108
145
  _log(`Webview OK: ${parsed.messages?.length || 0} msgs`);
109
146
  h.historyWriter.appendNewMessages(
110
- provider?.type || h.currentIdeType || 'unknown_webview',
111
- parsed.messages || [],
112
- parsed.title,
113
- args?.instanceId
114
- );
147
+ provider?.type || getCurrentProviderType(h, 'unknown_webview'),
148
+ parsed.messages || [],
149
+ parsed.title,
150
+ args?.targetSessionId
151
+ );
115
152
  return { success: true, ...parsed };
116
153
  }
117
154
  }
@@ -131,10 +168,10 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
131
168
  if (parsed && typeof parsed === 'object' && parsed.messages?.length > 0) {
132
169
  _log(`OK: ${parsed.messages?.length} msgs`);
133
170
  h.historyWriter.appendNewMessages(
134
- provider?.type || h.currentIdeType || 'unknown_ide',
171
+ provider?.type || getCurrentProviderType(h, 'unknown_ide'),
135
172
  parsed.messages || [],
136
173
  parsed.title,
137
- args?.instanceId
174
+ args?.targetSessionId
138
175
  );
139
176
  return { success: true, ...parsed };
140
177
  }
@@ -151,17 +188,23 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
151
188
  if (!text) return { success: false, error: 'text required' };
152
189
  const _log = (msg: string) => LOG.debug('Command', `[send_chat] ${msg}`);
153
190
  const provider = h.getProvider(args?.agentType);
191
+ const dedupeKey = buildRecentSendKey(h, args, provider, text);
154
192
 
155
193
  const _logSendSuccess = (method: string, targetAgent?: string) => {
156
194
  h.historyWriter.appendNewMessages(
157
- targetAgent || provider?.type || h.currentIdeType || 'unknown_agent',
195
+ targetAgent || provider?.type || getCurrentProviderType(h, 'unknown_agent'),
158
196
  [{ role: 'user', content: text, receivedAt: Date.now() }],
159
197
  undefined, // title
160
- args?.instanceId
198
+ args?.targetSessionId
161
199
  );
162
200
  return { success: true, sent: true, method, targetAgent };
163
201
  };
164
202
 
203
+ if (isRecentDuplicateSend(dedupeKey)) {
204
+ _log(`Suppressed duplicate send for ${dedupeKey}`);
205
+ return { success: true, sent: false, deduplicated: true };
206
+ }
207
+
165
208
  // CLI / ACP category: transmit via adapter
166
209
  if (provider?.category === 'cli' || provider?.category === 'acp') {
167
210
  const adapter = getTargetedCliAdapter(h, args, provider.type);
@@ -197,8 +240,9 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
197
240
  _log(`Extension script error: ${e.message}`);
198
241
  }
199
242
  // Method 2: AgentStreamManager
200
- if (h.agentStream && h.getCdp()) {
201
- const ok = await h.agentStream.sendToAgent(h.getCdp()!, provider.type, text, h.currentIdeType);
243
+ const extensionSessionId = h.currentSession?.sessionId;
244
+ if (h.agentStream && h.getCdp() && extensionSessionId) {
245
+ const ok = await h.agentStream.sendToSession(h.getCdp()!, extensionSessionId, text);
202
246
  if (ok) {
203
247
  _log(`AgentStreamManager sent OK`);
204
248
  return _logSendSuccess('agent-stream');
@@ -207,50 +251,15 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
207
251
  return { success: false, error: `Extension '${provider.type}' send failed` };
208
252
  }
209
253
 
210
- // IDE category (default): Provider typeAndSend script
254
+ // IDE category (default): provider sendMessage script is authoritative when present.
211
255
  const targetCdp = h.getCdp();
212
256
  if (!targetCdp?.isConnected) {
213
- _log(`No CDP for ${h.currentIdeType}`);
214
- return { success: false, error: `CDP for ${h.currentIdeType || 'unknown'} not connected` };
257
+ const managerKey = getCurrentManagerKey(h);
258
+ _log(`No CDP for ${managerKey}`);
259
+ return { success: false, error: `CDP for ${managerKey || 'unknown'} not connected` };
215
260
  }
216
261
 
217
- _log(`Targeting IDE: ${h.currentIdeType}`);
218
-
219
- // Method 0: webview-based IDE (try webviewSendMessage first)
220
- if (provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
221
- try {
222
- const webviewScript = (provider.scripts as any).webviewSendMessage(text);
223
- if (webviewScript && targetCdp.evaluateInWebviewFrame) {
224
- const matchText = provider.webviewMatchText;
225
- const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
226
- const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
227
- let wvParsed: any = wvResult;
228
- if (typeof wvResult === 'string') { try { wvParsed = JSON.parse(wvResult); } catch { } }
229
- if (wvParsed?.sent) {
230
- _log(`webviewSendMessage (priority) OK`);
231
- return _logSendSuccess('webview-script-priority');
232
- }
233
- _log(`webviewSendMessage (priority) did not confirm sent, falling through`);
234
- }
235
- } catch (e: any) {
236
- _log(`webviewSendMessage (priority) failed: ${e.message}, falling through`);
237
- }
238
- }
239
-
240
- // Method 1: use provider.inputMethod if available (main frame input)
241
- if (provider?.inputMethod === 'cdp-type-and-send' && provider.inputSelector) {
242
- try {
243
- const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
244
- if (sent) {
245
- _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
246
- return _logSendSuccess('typeAndSend-provider');
247
- }
248
- } catch (e: any) {
249
- _log(`typeAndSend(provider) failed: ${e.message}`);
250
- }
251
- }
252
-
253
- // Method 2: provider sendMessage script
262
+ _log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
254
263
  const sendScript = h.getProviderScript('sendMessage', { MESSAGE: text });
255
264
  if (sendScript) {
256
265
  try {
@@ -261,7 +270,6 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
261
270
  _log(`sendMessage script OK`);
262
271
  return _logSendSuccess('script');
263
272
  }
264
- // needsTypeAndSend response: typeAndSend using script-specified selector
265
273
  if (parsed?.needsTypeAndSend && parsed?.selector) {
266
274
  try {
267
275
  const sent = await targetCdp.typeAndSend(parsed.selector, text);
@@ -273,8 +281,30 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
273
281
  _log(`typeAndSend(script.selector) failed: ${e.message}`);
274
282
  }
275
283
  }
276
- // webviewSendMessage: attempt direct transmission from inside webview iframe
277
- if (parsed?.needsTypeAndSend && provider?.scripts?.webviewSendMessage) {
284
+ if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
285
+ try {
286
+ const { x, y } = parsed.clickCoords;
287
+ const sent = await targetCdp.typeAndSendAt(x, y, text);
288
+ if (sent) {
289
+ _log(`typeAndSendAt(${x},${y}) success`);
290
+ return _logSendSuccess('typeAndSendAt-script');
291
+ }
292
+ } catch (e: any) {
293
+ _log(`typeAndSendAt failed: ${e.message}`);
294
+ }
295
+ }
296
+ if (parsed?.needsTypeAndSend && provider?.inputMethod === 'cdp-type-and-send' && provider.inputSelector) {
297
+ try {
298
+ const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
299
+ if (sent) {
300
+ _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
301
+ return _logSendSuccess('typeAndSend-provider');
302
+ }
303
+ } catch (e: any) {
304
+ _log(`typeAndSend(provider) failed: ${e.message}`);
305
+ }
306
+ }
307
+ if (parsed?.needsTypeAndSend && provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
278
308
  try {
279
309
  const webviewScript = (provider.scripts as any).webviewSendMessage(text);
280
310
  if (webviewScript && targetCdp.evaluateInWebviewFrame) {
@@ -292,21 +322,41 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
292
322
  _log(`webviewSendMessage failed: ${e.message}`);
293
323
  }
294
324
  }
295
- // Coordinate-based fallback: input field inside webview iframe
296
- if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
297
- try {
298
- const { x, y } = parsed.clickCoords;
299
- const sent = await targetCdp.typeAndSendAt(x, y, text);
300
- if (sent) {
301
- _log(`typeAndSendAt(${x},${y}) success`);
302
- return _logSendSuccess('typeAndSendAt-script');
303
- }
304
- } catch (e: any) {
305
- _log(`typeAndSendAt failed: ${e.message}`);
325
+ return { success: false, error: parsed?.error || 'Provider sendMessage did not confirm send' };
326
+ } catch (e: any) {
327
+ _log(`sendMessage script failed: ${e.message}`);
328
+ return { success: false, error: `Provider sendMessage failed: ${e.message}` };
329
+ }
330
+ }
331
+
332
+ if (provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
333
+ try {
334
+ const webviewScript = (provider.scripts as any).webviewSendMessage(text);
335
+ if (webviewScript && targetCdp.evaluateInWebviewFrame) {
336
+ const matchText = provider.webviewMatchText;
337
+ const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
338
+ const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
339
+ let wvParsed: any = wvResult;
340
+ if (typeof wvResult === 'string') { try { wvParsed = JSON.parse(wvResult); } catch { } }
341
+ if (wvParsed?.sent) {
342
+ _log(`webviewSendMessage OK`);
343
+ return _logSendSuccess('webview-script');
306
344
  }
307
345
  }
308
346
  } catch (e: any) {
309
- _log(`sendMessage script failed: ${e.message}`);
347
+ _log(`webviewSendMessage failed: ${e.message}`);
348
+ }
349
+ }
350
+
351
+ if (provider?.inputMethod === 'cdp-type-and-send' && provider.inputSelector) {
352
+ try {
353
+ const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
354
+ if (sent) {
355
+ _log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
356
+ return _logSendSuccess('typeAndSend-provider');
357
+ }
358
+ } catch (e: any) {
359
+ _log(`typeAndSend(provider) failed: ${e.message}`);
310
360
  }
311
361
  }
312
362
 
@@ -318,9 +368,9 @@ export async function handleListChats(h: CommandHelpers, args: any): Promise<Com
318
368
  const provider = h.getProvider(args?.agentType);
319
369
 
320
370
  // Extension: via AgentStreamManager
321
- if (provider?.category === 'extension' && h.agentStream && h.getCdp()) {
371
+ if (provider?.category === 'extension' && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
322
372
  try {
323
- const chats = await h.agentStream.listAgentChats(h.getCdp()!, provider.type);
373
+ const chats = await h.agentStream.listSessionChats(h.getCdp()!, h.currentSession.sessionId);
324
374
  LOG.info('Command', `[list_chats] Extension: ${chats.length} chats`);
325
375
  return { success: true, chats };
326
376
  } catch (e: any) {
@@ -377,8 +427,8 @@ export async function handleNewChat(h: CommandHelpers, args: any): Promise<Comma
377
427
  return { success: false, error: 'new_chat not supported by this CLI provider' };
378
428
  }
379
429
 
380
- if (provider?.category === 'extension' && h.agentStream && h.getCdp()) {
381
- const ok = await h.agentStream.newAgentSession(h.getCdp()!, provider.type, h.currentIdeType);
430
+ if (provider?.category === 'extension' && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
431
+ const ok = await h.agentStream.newSession(h.getCdp()!, h.currentSession.sessionId);
382
432
  return { success: ok };
383
433
  }
384
434
 
@@ -407,17 +457,17 @@ export async function handleNewChat(h: CommandHelpers, args: any): Promise<Comma
407
457
 
408
458
  export async function handleSwitchChat(h: CommandHelpers, args: any): Promise<CommandResult> {
409
459
  const provider = h.getProvider(args?.agentType);
410
- const ideType = h.currentIdeType;
460
+ const managerKey = getCurrentManagerKey(h);
411
461
  const sessionId = args?.sessionId || args?.id || args?.chatId;
412
462
  if (!sessionId) return { success: false, error: 'sessionId required' };
413
- LOG.info('Command', `[switch_chat] sessionId=${sessionId}, ideType=${ideType}`);
463
+ LOG.info('Command', `[switch_chat] sessionId=${sessionId}, manager=${managerKey}`);
414
464
 
415
- if (provider?.category === 'extension' && h.agentStream && h.getCdp()) {
416
- const ok = await h.agentStream.switchAgentSession(h.getCdp()!, provider.type, sessionId);
465
+ if (provider?.category === 'extension' && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
466
+ const ok = await h.agentStream.switchConversation(h.getCdp()!, h.currentSession.sessionId, sessionId);
417
467
  return { success: ok, result: ok ? 'switched' : 'failed' };
418
468
  }
419
469
 
420
- const cdp = h.getCdp(ideType);
470
+ const cdp = h.getCdp(managerKey);
421
471
  if (!cdp?.isConnected) return { success: false, error: 'CDP not connected' };
422
472
 
423
473
  // webview IDE
@@ -549,7 +599,7 @@ export async function handleChangeModel(h: CommandHelpers, args: any): Promise<C
549
599
  const provider = h.getProvider(args?.agentType);
550
600
  const model = args?.model;
551
601
 
552
- LOG.info('Command', `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} ideType=${h.currentIdeType} providerType=${h.currentProviderType}`);
602
+ LOG.info('Command', `[change_model] model=${model} provider=${provider?.type} category=${provider?.category} manager=${getCurrentManagerKey(h)} providerType=${getCurrentProviderType(h)}`);
553
603
 
554
604
  // ACP provider
555
605
  if (provider?.category === 'acp') {
@@ -681,10 +731,8 @@ export async function handleResolveAction(h: CommandHelpers, args: any): Promise
681
731
  }
682
732
 
683
733
  // 1. Extension: via AgentStreamManager
684
- if (provider?.category === 'extension' && h.agentStream && h.getCdp()) {
685
- const ok = await h.agentStream.resolveAgentAction(
686
- h.getCdp()!, provider.type, action, h.currentIdeType
687
- );
734
+ if (provider?.category === 'extension' && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
735
+ const ok = await h.agentStream.resolveSessionAction(h.getCdp()!, h.currentSession.sessionId, action);
688
736
  return { success: ok };
689
737
  }
690
738
 
@@ -19,6 +19,7 @@ import { AcpProviderInstance } from '../providers/acp-provider-instance.js';
19
19
  import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
20
20
  import { ProviderLoader } from '../providers/provider-loader.js';
21
21
  import type { CliAdapter } from '../cli-adapter-types.js';
22
+ import type { SessionRegistry } from '../sessions/registry.js';
22
23
  import { LOG } from '../logging/logger.js';
23
24
 
24
25
  // ─── external dependency interface ──────────────────────────
@@ -33,6 +34,7 @@ export interface CliManagerDeps {
33
34
  removeAgentTracking(key: string): void;
34
35
  /** InstanceManager — register in CLI unified status */
35
36
  getInstanceManager(): ProviderInstanceManager | null;
37
+ getSessionRegistry?(): SessionRegistry | null;
36
38
  }
37
39
 
38
40
  type CommandResult = { success: boolean;[key: string]: unknown };
@@ -105,6 +107,7 @@ export class DaemonCliManager {
105
107
 
106
108
  // Create UUID-based key (allows separate instances even for same type+dir)
107
109
  const key = crypto.randomUUID();
110
+ const sessionRegistry = this.deps.getSessionRegistry?.() || null;
108
111
 
109
112
  // ─── ACP category handle ───
110
113
  if (provider && provider.category === 'acp') {
@@ -133,6 +136,16 @@ export class DaemonCliManager {
133
136
  await instanceManager.addInstance(key, acpInstance, {
134
137
  settings: this.providerLoader.getSettings(normalizedType),
135
138
  });
139
+ const sessionId = acpInstance.getInstanceId();
140
+ sessionRegistry?.register({
141
+ sessionId,
142
+ parentSessionId: null,
143
+ providerType: normalizedType,
144
+ providerCategory: 'acp',
145
+ transport: 'acp',
146
+ adapterKey: key,
147
+ instanceKey: key,
148
+ });
136
149
 
137
150
  // Register ACP entry in adapter map (getStatus queries from acpInstance in real-time)
138
151
  this.adapters.set(key, {
@@ -190,9 +203,18 @@ export class DaemonCliManager {
190
203
  serverConn: this.deps.getServerConn(),
191
204
  settings: {},
192
205
  onPtyData: (data: string) => {
193
- this.deps.getP2p()?.broadcastPtyOutput(key, data);
206
+ this.deps.getP2p()?.broadcastPtyOutput(cliInstance.instanceId, data);
194
207
  },
195
208
  });
209
+ sessionRegistry?.register({
210
+ sessionId: cliInstance.instanceId,
211
+ parentSessionId: null,
212
+ providerType: normalizedType,
213
+ providerCategory: 'cli',
214
+ transport: 'pty',
215
+ adapterKey: key,
216
+ instanceKey: key,
217
+ });
196
218
  } catch (spawnErr: any) {
197
219
  // Spawn failed — cleanup and propagate error
198
220
  LOG.error('CLI', `[${cliType}] Spawn failed: ${spawnErr?.message}`);
@@ -216,6 +238,7 @@ export class DaemonCliManager {
216
238
  if (this.adapters.has(key)) {
217
239
  this.adapters.delete(key);
218
240
  this.deps.removeAgentTracking(key);
241
+ sessionRegistry?.unregisterByInstanceKey(key);
219
242
  instanceManager.removeInstance(key);
220
243
  LOG.info('CLI', `🧹 Auto-cleaned ${status.status} CLI: ${cliType}`);
221
244
  this.deps.onStatusChange();
@@ -279,6 +302,7 @@ export class DaemonCliManager {
279
302
  // Always cleanup regardless of shutdown success
280
303
  this.adapters.delete(key);
281
304
  this.deps.removeAgentTracking(key);
305
+ this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
282
306
  this.deps.getInstanceManager()?.removeInstance(key);
283
307
  LOG.info('CLI', `🛑 Agent stopped: ${adapter.cliType} in ${adapter.workingDir}`);
284
308
  this.deps.onStatusChange();
@@ -286,6 +310,7 @@ export class DaemonCliManager {
286
310
  // Adapter not found — try InstanceManager direct removal
287
311
  const im = this.deps.getInstanceManager();
288
312
  if (im) {
313
+ this.deps.getSessionRegistry?.()?.unregisterByInstanceKey(key);
289
314
  im.removeInstance(key);
290
315
  this.deps.removeAgentTracking(key);
291
316
  LOG.warn('CLI', `🧹 Force-removed orphan entry: ${key}`);
@@ -303,7 +328,7 @@ export class DaemonCliManager {
303
328
 
304
329
  /**
305
330
  * Search for CLI adapter. Priority order:
306
- * 0. instanceKey (UUID direct match) — extracted from _targetInstance / composite ID
331
+ * 0. sessionId (UUID direct match)
307
332
  * 1. agentType + dir (iteration match)
308
333
  * 2. agentType fuzzy match (⚠ returns first match when multiple sessions exist)
309
334
  */
@@ -382,8 +407,8 @@ export class DaemonCliManager {
382
407
  const cliType = args?.cliType;
383
408
  const dir = args?.dir || '';
384
409
  if (!cliType) throw new Error('cliType required');
385
- // UUID (_targetInstance) based search priority
386
- const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
410
+ // UUID session target based search priority
411
+ const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
387
412
  if (found) {
388
413
  await this.stopSession(found.key);
389
414
  } else {
@@ -415,7 +440,7 @@ export class DaemonCliManager {
415
440
  }
416
441
  const dir = rdir.path;
417
442
  if (!cliType) throw new Error('cliType required');
418
- const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
443
+ const found = this.findAdapter(cliType, { instanceKey: args?.targetSessionId, dir });
419
444
  if (found) await this.stopSession(found.key);
420
445
  await this.startSession(cliType, dir);
421
446
  this.persistRecentDir(cliType, dir);
@@ -428,7 +453,7 @@ export class DaemonCliManager {
428
453
 
429
454
  const found = this.findAdapter(agentType, {
430
455
  dir: args?.dir,
431
- instanceKey: args?._targetInstance,
456
+ instanceKey: args?.targetSessionId,
432
457
  });
433
458
  if (!found) throw new Error(`CLI agent not running: ${agentType}`);
434
459
  const { adapter, key } = found;