@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.d.mts +2342 -0
- package/dist/index.d.ts +86 -932
- package/dist/index.js +879 -664
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14702 -0
- package/dist/index.mjs.map +1 -0
- package/dist/normalize-S2PmiRgB.d.mts +843 -0
- package/dist/normalize-S2PmiRgB.d.ts +843 -0
- package/dist/status/normalize.d.mts +1 -0
- package/dist/status/normalize.d.ts +1 -0
- package/dist/status/normalize.js +73 -0
- package/dist/status/normalize.js.map +1 -0
- package/dist/status/normalize.mjs +45 -0
- package/dist/status/normalize.mjs.map +1 -0
- package/package.json +8 -1
- package/src/agent-stream/manager.ts +213 -150
- package/src/agent-stream/poller.ts +57 -45
- package/src/boot/daemon-lifecycle.ts +30 -12
- package/src/cdp/initializer.ts +47 -0
- package/src/cdp/manager.ts +45 -4
- package/src/cdp/setup.ts +26 -11
- package/src/commands/chat-commands.ts +136 -88
- package/src/commands/cli-manager.ts +31 -6
- package/src/commands/handler.ts +71 -109
- package/src/commands/router.ts +4 -20
- package/src/commands/stream-commands.ts +34 -156
- package/src/daemon-core.ts +3 -9
- package/src/index.ts +8 -5
- package/src/logging/command-log.ts +1 -1
- package/src/providers/acp-provider-instance.ts +4 -0
- package/src/providers/provider-instance-manager.ts +1 -0
- package/src/sessions/registry.ts +76 -0
- package/src/shared-types.ts +45 -54
- package/src/status/builders.ts +157 -120
- package/src/status/normalize.ts +64 -0
- package/src/status/reporter.ts +16 -15
- package/src/status/snapshot.ts +3 -11
|
@@ -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?.
|
|
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
|
|
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
|
|
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?.
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
const stream =
|
|
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?.
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
171
|
+
provider?.type || getCurrentProviderType(h, 'unknown_ide'),
|
|
135
172
|
parsed.messages || [],
|
|
136
173
|
parsed.title,
|
|
137
|
-
args?.
|
|
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
|
|
195
|
+
targetAgent || provider?.type || getCurrentProviderType(h, 'unknown_agent'),
|
|
158
196
|
[{ role: 'user', content: text, receivedAt: Date.now() }],
|
|
159
197
|
undefined, // title
|
|
160
|
-
args?.
|
|
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
|
-
|
|
201
|
-
|
|
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):
|
|
254
|
+
// IDE category (default): provider sendMessage script is authoritative when present.
|
|
211
255
|
const targetCdp = h.getCdp();
|
|
212
256
|
if (!targetCdp?.isConnected) {
|
|
213
|
-
|
|
214
|
-
|
|
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
|
|
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
|
-
|
|
277
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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(`
|
|
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.
|
|
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.
|
|
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
|
|
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},
|
|
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.
|
|
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(
|
|
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}
|
|
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.
|
|
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(
|
|
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.
|
|
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
|
|
386
|
-
const found = this.findAdapter(cliType, { instanceKey: args?.
|
|
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?.
|
|
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?.
|
|
456
|
+
instanceKey: args?.targetSessionId,
|
|
432
457
|
});
|
|
433
458
|
if (!found) throw new Error(`CLI agent not running: ${agentType}`);
|
|
434
459
|
const { adapter, key } = found;
|