@adhdev/daemon-core 0.6.76 → 0.6.79
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 +2345 -0
- package/dist/index.d.ts +24 -867
- package/dist/index.js +280 -155
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14607 -0
- package/dist/index.mjs.map +1 -0
- package/dist/normalize-igHDb198.d.mts +864 -0
- package/dist/normalize-igHDb198.d.ts +864 -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 +102 -62
- package/src/agent-stream/poller.ts +27 -38
- package/src/boot/daemon-lifecycle.ts +2 -3
- package/src/cdp/manager.ts +26 -3
- package/src/commands/chat-commands.ts +12 -12
- package/src/commands/handler.ts +43 -12
- package/src/commands/stream-commands.ts +22 -16
- package/src/daemon/dev-server.ts +10 -8
- package/src/index.ts +2 -0
- package/src/status/builders.ts +7 -6
- package/src/status/normalize.ts +64 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { x as ManagedStatus, N as isManagedStatusWaiting, O as isManagedStatusWorking, V as normalizeActiveChatData, X as normalizeManagedStatus } from '../normalize-igHDb198.mjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { x as ManagedStatus, N as isManagedStatusWaiting, O as isManagedStatusWorking, V as normalizeActiveChatData, X as normalizeManagedStatus } from '../normalize-igHDb198.js';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/status/normalize.ts
|
|
21
|
+
var normalize_exports = {};
|
|
22
|
+
__export(normalize_exports, {
|
|
23
|
+
isManagedStatusWaiting: () => isManagedStatusWaiting,
|
|
24
|
+
isManagedStatusWorking: () => isManagedStatusWorking,
|
|
25
|
+
normalizeActiveChatData: () => normalizeActiveChatData,
|
|
26
|
+
normalizeManagedStatus: () => normalizeManagedStatus
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(normalize_exports);
|
|
29
|
+
var WORKING_STATUSES = /* @__PURE__ */ new Set([
|
|
30
|
+
"generating",
|
|
31
|
+
"streaming",
|
|
32
|
+
"loading",
|
|
33
|
+
"loading_reference",
|
|
34
|
+
"thinking",
|
|
35
|
+
"active"
|
|
36
|
+
]);
|
|
37
|
+
function hasApprovalButtons(activeModal) {
|
|
38
|
+
return (activeModal?.buttons?.length ?? 0) > 0;
|
|
39
|
+
}
|
|
40
|
+
function normalizeManagedStatus(status, opts) {
|
|
41
|
+
if (hasApprovalButtons(opts?.activeModal)) return "waiting_approval";
|
|
42
|
+
const normalized = String(status || "idle").trim().toLowerCase();
|
|
43
|
+
if (normalized === "waiting_approval") return "waiting_approval";
|
|
44
|
+
if (WORKING_STATUSES.has(normalized)) return "generating";
|
|
45
|
+
if (normalized === "error") return "error";
|
|
46
|
+
if (normalized === "stopped") return "stopped";
|
|
47
|
+
if (normalized === "starting") return "starting";
|
|
48
|
+
if (normalized === "panel_hidden") return "panel_hidden";
|
|
49
|
+
if (normalized === "not_monitored") return "not_monitored";
|
|
50
|
+
if (normalized === "disconnected") return "disconnected";
|
|
51
|
+
return "idle";
|
|
52
|
+
}
|
|
53
|
+
function isManagedStatusWorking(status) {
|
|
54
|
+
return normalizeManagedStatus(status) === "generating";
|
|
55
|
+
}
|
|
56
|
+
function isManagedStatusWaiting(status, opts) {
|
|
57
|
+
return normalizeManagedStatus(status, opts) === "waiting_approval";
|
|
58
|
+
}
|
|
59
|
+
function normalizeActiveChatData(activeChat) {
|
|
60
|
+
if (!activeChat) return activeChat;
|
|
61
|
+
return {
|
|
62
|
+
...activeChat,
|
|
63
|
+
status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal })
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
67
|
+
0 && (module.exports = {
|
|
68
|
+
isManagedStatusWaiting,
|
|
69
|
+
isManagedStatusWorking,
|
|
70
|
+
normalizeActiveChatData,
|
|
71
|
+
normalizeManagedStatus
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/status/normalize.ts"],"sourcesContent":["import type { ActiveChatData } from '../providers/provider-instance.js';\n\nexport type ManagedStatus =\n | 'idle'\n | 'generating'\n | 'waiting_approval'\n | 'error'\n | 'stopped'\n | 'starting'\n | 'panel_hidden'\n | 'not_monitored'\n | 'disconnected';\n\nconst WORKING_STATUSES = new Set([\n 'generating',\n 'streaming',\n 'loading',\n 'loading_reference',\n 'thinking',\n 'active',\n]);\n\nfunction hasApprovalButtons(activeModal?: { buttons?: unknown[] | null } | null): boolean {\n return (activeModal?.buttons?.length ?? 0) > 0;\n}\n\nexport function normalizeManagedStatus(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): ManagedStatus {\n if (hasApprovalButtons(opts?.activeModal)) return 'waiting_approval';\n\n const normalized = String(status || 'idle').trim().toLowerCase();\n if (normalized === 'waiting_approval') return 'waiting_approval';\n if (WORKING_STATUSES.has(normalized)) return 'generating';\n if (normalized === 'error') return 'error';\n if (normalized === 'stopped') return 'stopped';\n if (normalized === 'starting') return 'starting';\n if (normalized === 'panel_hidden') return 'panel_hidden';\n if (normalized === 'not_monitored') return 'not_monitored';\n if (normalized === 'disconnected') return 'disconnected';\n return 'idle';\n}\n\nexport function isManagedStatusWorking(status?: string | null): boolean {\n return normalizeManagedStatus(status) === 'generating';\n}\n\nexport function isManagedStatusWaiting(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): boolean {\n return normalizeManagedStatus(status, opts) === 'waiting_approval';\n}\n\nexport function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(\n activeChat: T,\n): T {\n if (!activeChat) return activeChat;\n return {\n ...activeChat,\n status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),\n } as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAED,SAAS,mBAAmB,aAA8D;AACtF,UAAQ,aAAa,SAAS,UAAU,KAAK;AACjD;AAEO,SAAS,uBACZ,QACA,MACa;AACb,MAAI,mBAAmB,MAAM,WAAW,EAAG,QAAO;AAElD,QAAM,aAAa,OAAO,UAAU,MAAM,EAAE,KAAK,EAAE,YAAY;AAC/D,MAAI,eAAe,mBAAoB,QAAO;AAC9C,MAAI,iBAAiB,IAAI,UAAU,EAAG,QAAO;AAC7C,MAAI,eAAe,QAAS,QAAO;AACnC,MAAI,eAAe,UAAW,QAAO;AACrC,MAAI,eAAe,WAAY,QAAO;AACtC,MAAI,eAAe,eAAgB,QAAO;AAC1C,MAAI,eAAe,gBAAiB,QAAO;AAC3C,MAAI,eAAe,eAAgB,QAAO;AAC1C,SAAO;AACX;AAEO,SAAS,uBAAuB,QAAiC;AACpE,SAAO,uBAAuB,MAAM,MAAM;AAC9C;AAEO,SAAS,uBACZ,QACA,MACO;AACP,SAAO,uBAAuB,QAAQ,IAAI,MAAM;AACpD;AAEO,SAAS,wBACZ,YACC;AACD,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO;AAAA,IACH,GAAG;AAAA,IACH,QAAQ,uBAAuB,WAAW,QAAQ,EAAE,aAAa,WAAW,YAAY,CAAC;AAAA,EAC7F;AACJ;","names":[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/status/normalize.ts
|
|
2
|
+
var WORKING_STATUSES = /* @__PURE__ */ new Set([
|
|
3
|
+
"generating",
|
|
4
|
+
"streaming",
|
|
5
|
+
"loading",
|
|
6
|
+
"loading_reference",
|
|
7
|
+
"thinking",
|
|
8
|
+
"active"
|
|
9
|
+
]);
|
|
10
|
+
function hasApprovalButtons(activeModal) {
|
|
11
|
+
return (activeModal?.buttons?.length ?? 0) > 0;
|
|
12
|
+
}
|
|
13
|
+
function normalizeManagedStatus(status, opts) {
|
|
14
|
+
if (hasApprovalButtons(opts?.activeModal)) return "waiting_approval";
|
|
15
|
+
const normalized = String(status || "idle").trim().toLowerCase();
|
|
16
|
+
if (normalized === "waiting_approval") return "waiting_approval";
|
|
17
|
+
if (WORKING_STATUSES.has(normalized)) return "generating";
|
|
18
|
+
if (normalized === "error") return "error";
|
|
19
|
+
if (normalized === "stopped") return "stopped";
|
|
20
|
+
if (normalized === "starting") return "starting";
|
|
21
|
+
if (normalized === "panel_hidden") return "panel_hidden";
|
|
22
|
+
if (normalized === "not_monitored") return "not_monitored";
|
|
23
|
+
if (normalized === "disconnected") return "disconnected";
|
|
24
|
+
return "idle";
|
|
25
|
+
}
|
|
26
|
+
function isManagedStatusWorking(status) {
|
|
27
|
+
return normalizeManagedStatus(status) === "generating";
|
|
28
|
+
}
|
|
29
|
+
function isManagedStatusWaiting(status, opts) {
|
|
30
|
+
return normalizeManagedStatus(status, opts) === "waiting_approval";
|
|
31
|
+
}
|
|
32
|
+
function normalizeActiveChatData(activeChat) {
|
|
33
|
+
if (!activeChat) return activeChat;
|
|
34
|
+
return {
|
|
35
|
+
...activeChat,
|
|
36
|
+
status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal })
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
isManagedStatusWaiting,
|
|
41
|
+
isManagedStatusWorking,
|
|
42
|
+
normalizeActiveChatData,
|
|
43
|
+
normalizeManagedStatus
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=normalize.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/status/normalize.ts"],"sourcesContent":["import type { ActiveChatData } from '../providers/provider-instance.js';\n\nexport type ManagedStatus =\n | 'idle'\n | 'generating'\n | 'waiting_approval'\n | 'error'\n | 'stopped'\n | 'starting'\n | 'panel_hidden'\n | 'not_monitored'\n | 'disconnected';\n\nconst WORKING_STATUSES = new Set([\n 'generating',\n 'streaming',\n 'loading',\n 'loading_reference',\n 'thinking',\n 'active',\n]);\n\nfunction hasApprovalButtons(activeModal?: { buttons?: unknown[] | null } | null): boolean {\n return (activeModal?.buttons?.length ?? 0) > 0;\n}\n\nexport function normalizeManagedStatus(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): ManagedStatus {\n if (hasApprovalButtons(opts?.activeModal)) return 'waiting_approval';\n\n const normalized = String(status || 'idle').trim().toLowerCase();\n if (normalized === 'waiting_approval') return 'waiting_approval';\n if (WORKING_STATUSES.has(normalized)) return 'generating';\n if (normalized === 'error') return 'error';\n if (normalized === 'stopped') return 'stopped';\n if (normalized === 'starting') return 'starting';\n if (normalized === 'panel_hidden') return 'panel_hidden';\n if (normalized === 'not_monitored') return 'not_monitored';\n if (normalized === 'disconnected') return 'disconnected';\n return 'idle';\n}\n\nexport function isManagedStatusWorking(status?: string | null): boolean {\n return normalizeManagedStatus(status) === 'generating';\n}\n\nexport function isManagedStatusWaiting(\n status?: string | null,\n opts?: { activeModal?: { buttons?: unknown[] | null } | null },\n): boolean {\n return normalizeManagedStatus(status, opts) === 'waiting_approval';\n}\n\nexport function normalizeActiveChatData<T extends ActiveChatData | null | undefined>(\n activeChat: T,\n): T {\n if (!activeChat) return activeChat;\n return {\n ...activeChat,\n status: normalizeManagedStatus(activeChat.status, { activeModal: activeChat.activeModal }),\n } as T;\n}\n"],"mappings":";AAaA,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAED,SAAS,mBAAmB,aAA8D;AACtF,UAAQ,aAAa,SAAS,UAAU,KAAK;AACjD;AAEO,SAAS,uBACZ,QACA,MACa;AACb,MAAI,mBAAmB,MAAM,WAAW,EAAG,QAAO;AAElD,QAAM,aAAa,OAAO,UAAU,MAAM,EAAE,KAAK,EAAE,YAAY;AAC/D,MAAI,eAAe,mBAAoB,QAAO;AAC9C,MAAI,iBAAiB,IAAI,UAAU,EAAG,QAAO;AAC7C,MAAI,eAAe,QAAS,QAAO;AACnC,MAAI,eAAe,UAAW,QAAO;AACrC,MAAI,eAAe,WAAY,QAAO;AACtC,MAAI,eAAe,eAAgB,QAAO;AAC1C,MAAI,eAAe,gBAAiB,QAAO;AAC3C,MAAI,eAAe,eAAgB,QAAO;AAC1C,SAAO;AACX;AAEO,SAAS,uBAAuB,QAAiC;AACpE,SAAO,uBAAuB,MAAM,MAAM;AAC9C;AAEO,SAAS,uBACZ,QACA,MACO;AACP,SAAO,uBAAuB,QAAQ,IAAI,MAAM;AACpD;AAEO,SAAS,wBACZ,YACC;AACD,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO;AAAA,IACH,GAAG;AAAA,IACH,QAAQ,uBAAuB,WAAW,QAAQ,EAAE,aAAa,WAAW,YAAY,CAAC;AAAA,EAC7F;AACJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.79",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
10
11
|
"require": "./dist/index.js",
|
|
11
12
|
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./status/normalize": {
|
|
15
|
+
"types": "./dist/status/normalize.d.ts",
|
|
16
|
+
"import": "./dist/status/normalize.mjs",
|
|
17
|
+
"require": "./dist/status/normalize.js",
|
|
18
|
+
"default": "./dist/status/normalize.js"
|
|
12
19
|
}
|
|
13
20
|
},
|
|
14
21
|
"scripts": {
|
|
@@ -30,14 +30,12 @@ export interface ManagedAgent {
|
|
|
30
30
|
|
|
31
31
|
export class DaemonAgentStreamManager {
|
|
32
32
|
private allAdapters: IAgentStreamAdapter[] = [];
|
|
33
|
-
private
|
|
33
|
+
private managedByScope = new Map<string, Map<string, ManagedAgent>>();
|
|
34
34
|
private enabled = true;
|
|
35
35
|
private logFn: (msg: string) => void;
|
|
36
|
-
private
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
private _activeAgentType: string | null = null;
|
|
36
|
+
private lastDiscoveryTimeByScope = new Map<string, number>();
|
|
37
|
+
private discoveryIntervalMsByScope = new Map<string, number>();
|
|
38
|
+
private activeAgentTypeByScope = new Map<string, string | null>();
|
|
41
39
|
|
|
42
40
|
constructor(logFn?: (msg: string) => void, providerLoader?: ProviderLoader) {
|
|
43
41
|
this.logFn = logFn || LOG.forComponent('AgentStream').asLogFn();
|
|
@@ -58,7 +56,25 @@ export class DaemonAgentStreamManager {
|
|
|
58
56
|
|
|
59
57
|
setEnabled(enabled: boolean) { this.enabled = enabled; }
|
|
60
58
|
get isEnabled() { return this.enabled; }
|
|
61
|
-
|
|
59
|
+
getActiveAgentType(scopeKey: string): string | null {
|
|
60
|
+
return this.activeAgentTypeByScope.get(scopeKey) || null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private getManagedScope(scopeKey: string): Map<string, ManagedAgent> {
|
|
64
|
+
let managed = this.managedByScope.get(scopeKey);
|
|
65
|
+
if (!managed) {
|
|
66
|
+
managed = new Map<string, ManagedAgent>();
|
|
67
|
+
this.managedByScope.set(scopeKey, managed);
|
|
68
|
+
}
|
|
69
|
+
return managed;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
resetScope(scopeKey: string): void {
|
|
73
|
+
this.managedByScope.delete(scopeKey);
|
|
74
|
+
this.activeAgentTypeByScope.delete(scopeKey);
|
|
75
|
+
this.lastDiscoveryTimeByScope.delete(scopeKey);
|
|
76
|
+
this.discoveryIntervalMsByScope.delete(scopeKey);
|
|
77
|
+
}
|
|
62
78
|
|
|
63
79
|
/** Panel focus based on provider.js focusPanel or extensionId (currently no-op) */
|
|
64
80
|
async ensureAgentPanelOpen(agentType: string, targetIdeType?: string): Promise<void> {
|
|
@@ -66,43 +82,52 @@ export class DaemonAgentStreamManager {
|
|
|
66
82
|
// Can be replaced with CDP-based focus (future implementation)
|
|
67
83
|
}
|
|
68
84
|
|
|
69
|
-
async switchActiveAgent(cdp: DaemonCdpManager, agentType: string | null): Promise<void> {
|
|
70
|
-
|
|
85
|
+
async switchActiveAgent(cdp: DaemonCdpManager, scopeKey: string, agentType: string | null): Promise<void> {
|
|
86
|
+
const managed = this.getManagedScope(scopeKey);
|
|
87
|
+
const previousAgentType = this.getActiveAgentType(scopeKey);
|
|
88
|
+
if (previousAgentType === agentType) return;
|
|
71
89
|
|
|
72
|
-
if (
|
|
73
|
-
const prev =
|
|
90
|
+
if (previousAgentType) {
|
|
91
|
+
const prev = managed.get(previousAgentType);
|
|
74
92
|
if (prev) {
|
|
75
93
|
try { await cdp.detachAgent(prev.sessionId); } catch { }
|
|
76
|
-
|
|
77
|
-
this.logFn(`[AgentStream] Deactivated: ${prev.adapter.agentName}`);
|
|
94
|
+
managed.delete(previousAgentType);
|
|
95
|
+
this.logFn(`[AgentStream] Deactivated: ${prev.adapter.agentName} (${scopeKey})`);
|
|
78
96
|
}
|
|
79
97
|
}
|
|
80
98
|
|
|
81
|
-
this.
|
|
82
|
-
this.
|
|
83
|
-
|
|
99
|
+
this.activeAgentTypeByScope.set(scopeKey, agentType);
|
|
100
|
+
this.lastDiscoveryTimeByScope.set(scopeKey, 0);
|
|
101
|
+
if (!agentType && managed.size === 0) {
|
|
102
|
+
this.managedByScope.delete(scopeKey);
|
|
103
|
+
}
|
|
104
|
+
this.logFn(`[AgentStream] Active agent (${scopeKey}): ${agentType || 'none'}`);
|
|
84
105
|
}
|
|
85
106
|
|
|
86
107
|
/** Agent webview discovery + session connection */
|
|
87
|
-
async syncAgentSessions(cdp: DaemonCdpManager): Promise<void> {
|
|
88
|
-
|
|
108
|
+
async syncAgentSessions(cdp: DaemonCdpManager, scopeKey: string): Promise<void> {
|
|
109
|
+
const activeAgentType = this.getActiveAgentType(scopeKey);
|
|
110
|
+
if (!this.enabled || !activeAgentType) return;
|
|
89
111
|
|
|
90
112
|
const now = Date.now();
|
|
91
|
-
|
|
113
|
+
const managed = this.getManagedScope(scopeKey);
|
|
114
|
+
const lastDiscoveryTime = this.lastDiscoveryTimeByScope.get(scopeKey) || 0;
|
|
115
|
+
const discoveryIntervalMs = this.discoveryIntervalMsByScope.get(scopeKey) || 10_000;
|
|
116
|
+
if (managed.has(activeAgentType) && (now - lastDiscoveryTime) < discoveryIntervalMs) {
|
|
92
117
|
return;
|
|
93
118
|
}
|
|
94
|
-
this.
|
|
119
|
+
this.lastDiscoveryTimeByScope.set(scopeKey, now);
|
|
95
120
|
|
|
96
121
|
try {
|
|
97
122
|
const targets = await cdp.discoverAgentWebviews();
|
|
98
|
-
const activeTarget = targets.find(t => t.agentType ===
|
|
123
|
+
const activeTarget = targets.find(t => t.agentType === activeAgentType);
|
|
99
124
|
|
|
100
|
-
if (activeTarget && !
|
|
101
|
-
const adapter = this.allAdapters.find(a => a.agentType ===
|
|
125
|
+
if (activeTarget && !managed.has(activeAgentType)) {
|
|
126
|
+
const adapter = this.allAdapters.find(a => a.agentType === activeAgentType);
|
|
102
127
|
if (adapter) {
|
|
103
128
|
const sessionId = await cdp.attachToAgent(activeTarget);
|
|
104
129
|
if (sessionId) {
|
|
105
|
-
|
|
130
|
+
managed.set(activeAgentType, {
|
|
106
131
|
adapter,
|
|
107
132
|
sessionId,
|
|
108
133
|
target: activeTarget,
|
|
@@ -110,34 +135,36 @@ export class DaemonAgentStreamManager {
|
|
|
110
135
|
lastError: null,
|
|
111
136
|
lastHiddenCheckTime: 0,
|
|
112
137
|
});
|
|
113
|
-
this.logFn(`[AgentStream] Connected: ${adapter.agentName}`);
|
|
138
|
+
this.logFn(`[AgentStream] Connected: ${adapter.agentName} (${scopeKey})`);
|
|
114
139
|
}
|
|
115
140
|
}
|
|
116
141
|
}
|
|
117
142
|
|
|
118
143
|
// Cleanup inactive agents
|
|
119
|
-
for (const [type, agent] of
|
|
120
|
-
if (type !==
|
|
144
|
+
for (const [type, agent] of managed) {
|
|
145
|
+
if (type !== activeAgentType) {
|
|
121
146
|
await cdp.detachAgent(agent.sessionId);
|
|
122
|
-
|
|
147
|
+
managed.delete(type);
|
|
123
148
|
}
|
|
124
149
|
}
|
|
125
150
|
|
|
126
|
-
this.
|
|
151
|
+
this.discoveryIntervalMsByScope.set(scopeKey, managed.has(activeAgentType) ? 30_000 : 10_000);
|
|
127
152
|
} catch (e) {
|
|
128
|
-
this.logFn(`[AgentStream] sync error: ${(e as Error).message}`);
|
|
153
|
+
this.logFn(`[AgentStream] sync error (${scopeKey}): ${(e as Error).message}`);
|
|
129
154
|
}
|
|
130
155
|
}
|
|
131
156
|
|
|
132
157
|
/** Collect active agent status */
|
|
133
|
-
async collectAgentStreams(cdp: DaemonCdpManager): Promise<AgentStreamState[]> {
|
|
158
|
+
async collectAgentStreams(cdp: DaemonCdpManager, scopeKey: string): Promise<AgentStreamState[]> {
|
|
134
159
|
if (!this.enabled) return [];
|
|
135
160
|
|
|
136
161
|
const results: AgentStreamState[] = [];
|
|
162
|
+
const activeAgentType = this.getActiveAgentType(scopeKey);
|
|
163
|
+
const managed = this.managedByScope.get(scopeKey);
|
|
137
164
|
|
|
138
|
-
if (
|
|
139
|
-
const agent =
|
|
140
|
-
const type =
|
|
165
|
+
if (activeAgentType && managed?.has(activeAgentType)) {
|
|
166
|
+
const agent = managed.get(activeAgentType)!;
|
|
167
|
+
const type = activeAgentType;
|
|
141
168
|
|
|
142
169
|
const isHidden = agent.lastState?.status === 'panel_hidden';
|
|
143
170
|
const hiddenCacheFresh = isHidden && (Date.now() - agent.lastHiddenCheckTime < 30000);
|
|
@@ -170,8 +197,8 @@ export class DaemonAgentStreamManager {
|
|
|
170
197
|
});
|
|
171
198
|
if (errorMsg.includes('timeout') || errorMsg.includes('not connected') || errorMsg.includes('Session')) {
|
|
172
199
|
try { await cdp.detachAgent(agent.sessionId); } catch { }
|
|
173
|
-
|
|
174
|
-
this.
|
|
200
|
+
managed.delete(type);
|
|
201
|
+
this.lastDiscoveryTimeByScope.set(scopeKey, 0);
|
|
175
202
|
}
|
|
176
203
|
}
|
|
177
204
|
}
|
|
@@ -180,9 +207,9 @@ export class DaemonAgentStreamManager {
|
|
|
180
207
|
return results;
|
|
181
208
|
}
|
|
182
209
|
|
|
183
|
-
async sendToAgent(cdp: DaemonCdpManager, agentType: string, text: string, targetIdeType?: string): Promise<boolean> {
|
|
210
|
+
async sendToAgent(cdp: DaemonCdpManager, scopeKey: string, agentType: string, text: string, targetIdeType?: string): Promise<boolean> {
|
|
184
211
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
185
|
-
const agent = this.
|
|
212
|
+
const agent = this.getManagedAgent(agentType, scopeKey);
|
|
186
213
|
if (!agent) return false;
|
|
187
214
|
try {
|
|
188
215
|
const evaluate: AgentEvaluateFn = (expr, timeout) =>
|
|
@@ -195,9 +222,9 @@ export class DaemonAgentStreamManager {
|
|
|
195
222
|
}
|
|
196
223
|
}
|
|
197
224
|
|
|
198
|
-
async resolveAgentAction(cdp: DaemonCdpManager, agentType: string, action: 'approve' | 'reject', targetIdeType?: string): Promise<boolean> {
|
|
225
|
+
async resolveAgentAction(cdp: DaemonCdpManager, scopeKey: string, agentType: string, action: 'approve' | 'reject', targetIdeType?: string): Promise<boolean> {
|
|
199
226
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
200
|
-
const agent = this.
|
|
227
|
+
const agent = this.getManagedAgent(agentType, scopeKey);
|
|
201
228
|
if (!agent) return false;
|
|
202
229
|
try {
|
|
203
230
|
const evaluate: AgentEvaluateFn = (expr, timeout) =>
|
|
@@ -209,9 +236,9 @@ export class DaemonAgentStreamManager {
|
|
|
209
236
|
}
|
|
210
237
|
}
|
|
211
238
|
|
|
212
|
-
async newAgentSession(cdp: DaemonCdpManager, agentType: string, targetIdeType?: string): Promise<boolean> {
|
|
239
|
+
async newAgentSession(cdp: DaemonCdpManager, scopeKey: string, agentType: string, targetIdeType?: string): Promise<boolean> {
|
|
213
240
|
await this.ensureAgentPanelOpen(agentType, targetIdeType);
|
|
214
|
-
const agent = this.
|
|
241
|
+
const agent = this.getManagedAgent(agentType, scopeKey);
|
|
215
242
|
if (!agent) return false;
|
|
216
243
|
try {
|
|
217
244
|
const evaluate: AgentEvaluateFn = (expr, timeout) =>
|
|
@@ -224,14 +251,14 @@ export class DaemonAgentStreamManager {
|
|
|
224
251
|
}
|
|
225
252
|
}
|
|
226
253
|
|
|
227
|
-
async listAgentChats(cdp: DaemonCdpManager, agentType: string): Promise<AgentChatListItem[]> {
|
|
228
|
-
let agent = this.
|
|
254
|
+
async listAgentChats(cdp: DaemonCdpManager, scopeKey: string, agentType: string): Promise<AgentChatListItem[]> {
|
|
255
|
+
let agent = this.getManagedAgent(agentType, scopeKey);
|
|
229
256
|
// on-demand: try activate+sync if not in managed list
|
|
230
257
|
if (!agent) {
|
|
231
|
-
this.logFn(`[AgentStream] listChats: ${agentType} not managed, trying on-demand activation`);
|
|
232
|
-
await this.switchActiveAgent(cdp, agentType);
|
|
233
|
-
await this.syncAgentSessions(cdp);
|
|
234
|
-
agent = this.
|
|
258
|
+
this.logFn(`[AgentStream] listChats: ${agentType} not managed in ${scopeKey}, trying on-demand activation`);
|
|
259
|
+
await this.switchActiveAgent(cdp, scopeKey, agentType);
|
|
260
|
+
await this.syncAgentSessions(cdp, scopeKey);
|
|
261
|
+
agent = this.getManagedAgent(agentType, scopeKey);
|
|
235
262
|
}
|
|
236
263
|
if (!agent || typeof agent.adapter.listChats !== 'function') return [];
|
|
237
264
|
try {
|
|
@@ -244,13 +271,13 @@ export class DaemonAgentStreamManager {
|
|
|
244
271
|
}
|
|
245
272
|
}
|
|
246
273
|
|
|
247
|
-
async switchAgentSession(cdp: DaemonCdpManager, agentType: string, sessionId: string): Promise<boolean> {
|
|
248
|
-
let agent = this.
|
|
274
|
+
async switchAgentSession(cdp: DaemonCdpManager, scopeKey: string, agentType: string, sessionId: string): Promise<boolean> {
|
|
275
|
+
let agent = this.getManagedAgent(agentType, scopeKey);
|
|
249
276
|
if (!agent) {
|
|
250
|
-
this.logFn(`[AgentStream] switchSession: ${agentType} not managed, trying on-demand activation`);
|
|
251
|
-
await this.switchActiveAgent(cdp, agentType);
|
|
252
|
-
await this.syncAgentSessions(cdp);
|
|
253
|
-
agent = this.
|
|
277
|
+
this.logFn(`[AgentStream] switchSession: ${agentType} not managed in ${scopeKey}, trying on-demand activation`);
|
|
278
|
+
await this.switchActiveAgent(cdp, scopeKey, agentType);
|
|
279
|
+
await this.syncAgentSessions(cdp, scopeKey);
|
|
280
|
+
agent = this.getManagedAgent(agentType, scopeKey);
|
|
254
281
|
}
|
|
255
282
|
if (!agent || typeof agent.adapter.switchSession !== 'function') return false;
|
|
256
283
|
try {
|
|
@@ -263,8 +290,8 @@ export class DaemonAgentStreamManager {
|
|
|
263
290
|
}
|
|
264
291
|
}
|
|
265
292
|
|
|
266
|
-
async focusAgentEditor(cdp: DaemonCdpManager, agentType: string): Promise<boolean> {
|
|
267
|
-
const agent = this.
|
|
293
|
+
async focusAgentEditor(cdp: DaemonCdpManager, scopeKey: string, agentType: string): Promise<boolean> {
|
|
294
|
+
const agent = this.getManagedAgent(agentType, scopeKey);
|
|
268
295
|
if (!agent || typeof agent.adapter.focusEditor !== 'function') return false;
|
|
269
296
|
try {
|
|
270
297
|
const evaluate: AgentEvaluateFn = (expr, timeout) =>
|
|
@@ -277,13 +304,26 @@ export class DaemonAgentStreamManager {
|
|
|
277
304
|
}
|
|
278
305
|
}
|
|
279
306
|
|
|
280
|
-
getConnectedAgents(): string[] {
|
|
281
|
-
|
|
307
|
+
getConnectedAgents(scopeKey?: string): string[] {
|
|
308
|
+
if (scopeKey) return Array.from((this.managedByScope.get(scopeKey) || new Map()).keys());
|
|
309
|
+
return Array.from(this.managedByScope.values()).flatMap(scope => Array.from(scope.keys()));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
getManagedAgent(agentType: string, scopeKey: string): ManagedAgent | undefined {
|
|
313
|
+
return this.managedByScope.get(scopeKey)?.get(agentType);
|
|
314
|
+
}
|
|
282
315
|
|
|
283
|
-
async dispose(
|
|
284
|
-
for (const [,
|
|
285
|
-
|
|
316
|
+
async dispose(cdpManagers: Map<string, DaemonCdpManager>): Promise<void> {
|
|
317
|
+
for (const [scopeKey, managed] of this.managedByScope) {
|
|
318
|
+
const cdp = cdpManagers.get(scopeKey);
|
|
319
|
+
if (!cdp) continue;
|
|
320
|
+
for (const [, agent] of managed) {
|
|
321
|
+
try { await cdp.detachAgent(agent.sessionId); } catch { }
|
|
322
|
+
}
|
|
286
323
|
}
|
|
287
|
-
this.
|
|
324
|
+
this.managedByScope.clear();
|
|
325
|
+
this.activeAgentTypeByScope.clear();
|
|
326
|
+
this.lastDiscoveryTimeByScope.clear();
|
|
327
|
+
this.discoveryIntervalMsByScope.clear();
|
|
288
328
|
}
|
|
289
329
|
}
|
|
@@ -31,7 +31,6 @@ export interface AgentStreamPollerDeps {
|
|
|
31
31
|
|
|
32
32
|
export class AgentStreamPoller {
|
|
33
33
|
private deps: AgentStreamPollerDeps;
|
|
34
|
-
private _activeIdeType: string | null = null;
|
|
35
34
|
private timer: NodeJS.Timeout | null = null;
|
|
36
35
|
|
|
37
36
|
constructor(deps: AgentStreamPollerDeps) {
|
|
@@ -40,14 +39,12 @@ export class AgentStreamPoller {
|
|
|
40
39
|
|
|
41
40
|
/** Currently active IDE type for agent streaming */
|
|
42
41
|
get activeIde(): string | null {
|
|
43
|
-
return
|
|
42
|
+
return null;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
/** Reset active IDE tracking (e.g., when IDE is stopped) */
|
|
47
46
|
resetActiveIde(ideType: string): void {
|
|
48
|
-
|
|
49
|
-
this._activeIdeType = null;
|
|
50
|
-
}
|
|
47
|
+
this.deps.agentStreamManager.resetScope(ideType);
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
/** Start polling (idempotent — ignored if already started) */
|
|
@@ -113,56 +110,48 @@ export class AgentStreamPoller {
|
|
|
113
110
|
}
|
|
114
111
|
|
|
115
112
|
// 1c. If the active agent stream belongs to a now-disabled extension, detach it
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
const activeType = agentStreamManager.getActiveAgentType(ideType);
|
|
114
|
+
if (activeType) {
|
|
118
115
|
const enabledExtTypes = new Set(
|
|
119
116
|
providerLoader.getEnabledExtensionProviders(ideType).map((p: any) => p.type)
|
|
120
117
|
);
|
|
121
118
|
if (!enabledExtTypes.has(activeType)) {
|
|
122
119
|
LOG.info('AgentStream', `Active agent ${activeType} was disabled for ${ideType} — detaching`);
|
|
123
|
-
await agentStreamManager.switchActiveAgent(cdp, null);
|
|
124
|
-
this._activeIdeType = null;
|
|
120
|
+
await agentStreamManager.switchActiveAgent(cdp, ideType, null);
|
|
125
121
|
// Report empty streams so dashboard removes the tab
|
|
126
122
|
this.deps.onStreamsUpdated?.(ideType, []);
|
|
127
123
|
}
|
|
128
124
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const cdp = cdpManagers.get(this._activeIdeType);
|
|
136
|
-
if (cdp?.isConnected) {
|
|
137
|
-
try {
|
|
138
|
-
await agentStreamManager.syncAgentSessions(cdp);
|
|
139
|
-
const streams = await agentStreamManager.collectAgentStreams(cdp);
|
|
140
|
-
this.deps.onStreamsUpdated?.(this._activeIdeType, streams);
|
|
141
|
-
} catch { }
|
|
142
|
-
return;
|
|
125
|
+
if (!cdp.isConnected) {
|
|
126
|
+
if (activeType) {
|
|
127
|
+
agentStreamManager.resetScope(ideType);
|
|
128
|
+
this.deps.onStreamsUpdated?.(ideType, []);
|
|
129
|
+
}
|
|
130
|
+
continue;
|
|
143
131
|
}
|
|
144
|
-
// CDP lost — reset
|
|
145
|
-
this._activeIdeType = null;
|
|
146
|
-
}
|
|
147
132
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
133
|
+
// ─── Phase 2: Agent session sync + collect ───
|
|
134
|
+
let resolvedActiveType = activeType;
|
|
135
|
+
|
|
136
|
+
// ─── Phase 3: Auto-discover agents ───
|
|
137
|
+
if (!resolvedActiveType) {
|
|
152
138
|
try {
|
|
153
139
|
const discovered = await cdp.discoverAgentWebviews();
|
|
154
140
|
if (discovered.length > 0) {
|
|
155
|
-
|
|
156
|
-
await agentStreamManager.switchActiveAgent(cdp,
|
|
157
|
-
LOG.info('AgentStream', `Auto-activated: ${
|
|
158
|
-
// sync+collect immediately
|
|
159
|
-
await agentStreamManager.syncAgentSessions(cdp);
|
|
160
|
-
const streams = await agentStreamManager.collectAgentStreams(cdp);
|
|
161
|
-
this.deps.onStreamsUpdated?.(ideType, streams);
|
|
162
|
-
return;
|
|
141
|
+
resolvedActiveType = discovered[0].agentType;
|
|
142
|
+
await agentStreamManager.switchActiveAgent(cdp, ideType, resolvedActiveType);
|
|
143
|
+
LOG.info('AgentStream', `Auto-activated: ${resolvedActiveType} (${ideType})`);
|
|
163
144
|
}
|
|
164
145
|
} catch { }
|
|
165
146
|
}
|
|
147
|
+
|
|
148
|
+
if (!resolvedActiveType) continue;
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
await agentStreamManager.syncAgentSessions(cdp, ideType);
|
|
152
|
+
const streams = await agentStreamManager.collectAgentStreams(cdp, ideType);
|
|
153
|
+
this.deps.onStreamsUpdated?.(ideType, streams);
|
|
154
|
+
} catch { }
|
|
166
155
|
}
|
|
167
156
|
}
|
|
168
157
|
}
|
|
@@ -288,9 +288,8 @@ export async function shutdownDaemonComponents(components: DaemonComponents): Pr
|
|
|
288
288
|
|
|
289
289
|
// 2. Dispose agent stream
|
|
290
290
|
try {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
await agentStreamManager.dispose(anyCdp);
|
|
291
|
+
if (agentStreamManager) {
|
|
292
|
+
await agentStreamManager.dispose(cdpManagers);
|
|
294
293
|
}
|
|
295
294
|
} catch (e: any) { LOG.warn('Shutdown', `AgentStream dispose: ${e?.message}`); }
|
|
296
295
|
|
package/src/cdp/manager.ts
CHANGED
|
@@ -660,9 +660,12 @@ export class DaemonCdpManager {
|
|
|
660
660
|
try {
|
|
661
661
|
// 1. Find webview iframe targets
|
|
662
662
|
const { targetInfos } = await sendWs('Target.getTargets');
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
663
|
+
const pageWebviewUrls = await this.getCurrentPageWebviewUrls();
|
|
664
|
+
const webviewIframes = (targetInfos || []).filter((t: any) => {
|
|
665
|
+
if (t.type !== 'iframe' || !(t.url || '').includes('vscode-webview')) return false;
|
|
666
|
+
if (pageWebviewUrls.size === 0) return true;
|
|
667
|
+
return pageWebviewUrls.has(t.url || '');
|
|
668
|
+
});
|
|
666
669
|
|
|
667
670
|
if (webviewIframes.length === 0) {
|
|
668
671
|
this.log('[CDP] evaluateInWebviewFrame: no webview iframes found');
|
|
@@ -766,6 +769,8 @@ export class DaemonCdpManager {
|
|
|
766
769
|
allTargets = result?.targetInfos || [];
|
|
767
770
|
}
|
|
768
771
|
|
|
772
|
+
const pageWebviewUrls = await this.getCurrentPageWebviewUrls();
|
|
773
|
+
|
|
769
774
|
const iframes = allTargets.filter((t: any) => t.type === 'iframe');
|
|
770
775
|
const typeMap = new Map<string, number>();
|
|
771
776
|
for (const t of allTargets) {
|
|
@@ -794,6 +799,7 @@ export class DaemonCdpManager {
|
|
|
794
799
|
const url = target.url || '';
|
|
795
800
|
const hasWebview = url.includes('vscode-webview');
|
|
796
801
|
if (!hasWebview) continue;
|
|
802
|
+
if (pageWebviewUrls.size > 0 && !pageWebviewUrls.has(url)) continue;
|
|
797
803
|
|
|
798
804
|
for (const known of this.extensionProviders) {
|
|
799
805
|
if (known.extensionIdPattern.test(url)) {
|
|
@@ -963,6 +969,23 @@ export class DaemonCdpManager {
|
|
|
963
969
|
return this.agentSessions;
|
|
964
970
|
}
|
|
965
971
|
|
|
972
|
+
private async getCurrentPageWebviewUrls(): Promise<Set<string>> {
|
|
973
|
+
if (!this.isConnected) return new Set();
|
|
974
|
+
try {
|
|
975
|
+
const raw = await this.evaluate(
|
|
976
|
+
`JSON.stringify(Array.from(document.querySelectorAll('iframe,webview'))
|
|
977
|
+
.map((el) => el.src || el.getAttribute('src') || '')
|
|
978
|
+
.filter((src) => typeof src === 'string' && src.includes('vscode-webview')))`,
|
|
979
|
+
5000,
|
|
980
|
+
);
|
|
981
|
+
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
982
|
+
if (!Array.isArray(parsed)) return new Set();
|
|
983
|
+
return new Set(parsed.filter((src: unknown): src is string => typeof src === 'string' && src.length > 0));
|
|
984
|
+
} catch {
|
|
985
|
+
return new Set();
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
966
989
|
// ─── Screenshot ──────────────────────────────────────────
|
|
967
990
|
|
|
968
991
|
async captureScreenshot(opts?: { quality?: number }): Promise<Buffer | null> {
|