@adhdev/daemon-core 0.5.3

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.
Files changed (217) hide show
  1. package/dist/index.d.ts +2662 -0
  2. package/dist/index.js +11341 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +48 -0
  5. package/providers/_builtin/.github/workflows/generate-registry.yml +57 -0
  6. package/providers/_builtin/COMPATIBILITY.md +217 -0
  7. package/providers/_builtin/CONTRIBUTING.md +200 -0
  8. package/providers/_builtin/README.md +119 -0
  9. package/providers/_builtin/_helpers/index.js +188 -0
  10. package/providers/_builtin/acp/agentpool/provider.json +54 -0
  11. package/providers/_builtin/acp/amp/provider.json +52 -0
  12. package/providers/_builtin/acp/auggie/provider.json +57 -0
  13. package/providers/_builtin/acp/autodev/provider.json +54 -0
  14. package/providers/_builtin/acp/autohand/provider.json +52 -0
  15. package/providers/_builtin/acp/blackbox-ai/provider.json +54 -0
  16. package/providers/_builtin/acp/claude-agent/provider.json +57 -0
  17. package/providers/_builtin/acp/cline-acp/provider.json +54 -0
  18. package/providers/_builtin/acp/codebuddy/provider.json +54 -0
  19. package/providers/_builtin/acp/codex-cli/provider.json +57 -0
  20. package/providers/_builtin/acp/corust-agent/provider.json +52 -0
  21. package/providers/_builtin/acp/crow-cli/provider.json +54 -0
  22. package/providers/_builtin/acp/cursor-acp/provider.json +54 -0
  23. package/providers/_builtin/acp/deepagents/provider.json +52 -0
  24. package/providers/_builtin/acp/dimcode/provider.json +54 -0
  25. package/providers/_builtin/acp/docker-cagent/provider.json +57 -0
  26. package/providers/_builtin/acp/factory-droid/provider.json +60 -0
  27. package/providers/_builtin/acp/fast-agent/provider.json +52 -0
  28. package/providers/_builtin/acp/gemini-cli/provider.json +114 -0
  29. package/providers/_builtin/acp/github-copilot/provider.json +54 -0
  30. package/providers/_builtin/acp/goose/provider.json +57 -0
  31. package/providers/_builtin/acp/junie/provider.json +52 -0
  32. package/providers/_builtin/acp/kilo/provider.json +54 -0
  33. package/providers/_builtin/acp/kimi-cli/provider.json +57 -0
  34. package/providers/_builtin/acp/minion-code/provider.json +52 -0
  35. package/providers/_builtin/acp/mistral-vibe/provider.json +57 -0
  36. package/providers/_builtin/acp/nova/provider.json +54 -0
  37. package/providers/_builtin/acp/openclaw/provider.json +54 -0
  38. package/providers/_builtin/acp/opencode/provider.json +52 -0
  39. package/providers/_builtin/acp/openhands/provider.json +54 -0
  40. package/providers/_builtin/acp/pi-acp/provider.json +52 -0
  41. package/providers/_builtin/acp/qoder/provider.json +54 -0
  42. package/providers/_builtin/acp/qwen-code/provider.json +60 -0
  43. package/providers/_builtin/acp/stakpak/provider.json +54 -0
  44. package/providers/_builtin/acp/vtcode/provider.json +54 -0
  45. package/providers/_builtin/cli/claude-cli/provider.json +100 -0
  46. package/providers/_builtin/cli/codex-cli/provider.json +89 -0
  47. package/providers/_builtin/cli/gemini-cli/provider.json +93 -0
  48. package/providers/_builtin/docs/CDP_SELECTOR_GUIDE.md +370 -0
  49. package/providers/_builtin/docs/PROVIDER_GUIDE.md +916 -0
  50. package/providers/_builtin/extension/cline/provider.json +35 -0
  51. package/providers/_builtin/extension/cline/scripts/focus_editor.js +48 -0
  52. package/providers/_builtin/extension/cline/scripts/list_chats.js +100 -0
  53. package/providers/_builtin/extension/cline/scripts/list_models.js +43 -0
  54. package/providers/_builtin/extension/cline/scripts/list_modes.js +35 -0
  55. package/providers/_builtin/extension/cline/scripts/new_session.js +85 -0
  56. package/providers/_builtin/extension/cline/scripts/open_panel.js +25 -0
  57. package/providers/_builtin/extension/cline/scripts/read_chat.js +257 -0
  58. package/providers/_builtin/extension/cline/scripts/resolve_action.js +83 -0
  59. package/providers/_builtin/extension/cline/scripts/send_message.js +95 -0
  60. package/providers/_builtin/extension/cline/scripts/set_mode.js +36 -0
  61. package/providers/_builtin/extension/cline/scripts/set_model.js +36 -0
  62. package/providers/_builtin/extension/cline/scripts/switch_session.js +206 -0
  63. package/providers/_builtin/extension/cline/scripts.js +73 -0
  64. package/providers/_builtin/extension/roo-code/provider.json +35 -0
  65. package/providers/_builtin/extension/roo-code/scripts.js +659 -0
  66. package/providers/_builtin/ide/antigravity/provider.json +68 -0
  67. package/providers/_builtin/ide/antigravity/scripts/1.106/focus_editor.js +20 -0
  68. package/providers/_builtin/ide/antigravity/scripts/1.106/list_chats.js +137 -0
  69. package/providers/_builtin/ide/antigravity/scripts/1.106/list_models.js +38 -0
  70. package/providers/_builtin/ide/antigravity/scripts/1.106/list_modes.js +48 -0
  71. package/providers/_builtin/ide/antigravity/scripts/1.106/new_session.js +75 -0
  72. package/providers/_builtin/ide/antigravity/scripts/1.106/read_chat.js +262 -0
  73. package/providers/_builtin/ide/antigravity/scripts/1.106/resolve_action.js +68 -0
  74. package/providers/_builtin/ide/antigravity/scripts/1.106/scripts.js +57 -0
  75. package/providers/_builtin/ide/antigravity/scripts/1.106/send_message.js +56 -0
  76. package/providers/_builtin/ide/antigravity/scripts/1.106/set_mode.js +34 -0
  77. package/providers/_builtin/ide/antigravity/scripts/1.106/set_model.js +47 -0
  78. package/providers/_builtin/ide/antigravity/scripts/1.106/switch_session.js +114 -0
  79. package/providers/_builtin/ide/antigravity/scripts/1.107/focus_editor.js +20 -0
  80. package/providers/_builtin/ide/antigravity/scripts/1.107/list_chats.js +137 -0
  81. package/providers/_builtin/ide/antigravity/scripts/1.107/list_models.js +61 -0
  82. package/providers/_builtin/ide/antigravity/scripts/1.107/list_modes.js +72 -0
  83. package/providers/_builtin/ide/antigravity/scripts/1.107/new_session.js +75 -0
  84. package/providers/_builtin/ide/antigravity/scripts/1.107/read_chat.js +262 -0
  85. package/providers/_builtin/ide/antigravity/scripts/1.107/resolve_action.js +68 -0
  86. package/providers/_builtin/ide/antigravity/scripts/1.107/scripts.js +67 -0
  87. package/providers/_builtin/ide/antigravity/scripts/1.107/send_message.js +56 -0
  88. package/providers/_builtin/ide/antigravity/scripts/1.107/set_mode.js +67 -0
  89. package/providers/_builtin/ide/antigravity/scripts/1.107/set_model.js +72 -0
  90. package/providers/_builtin/ide/antigravity/scripts/1.107/switch_session.js +114 -0
  91. package/providers/_builtin/ide/cursor/provider.json +70 -0
  92. package/providers/_builtin/ide/cursor/scripts/0.49/dismiss_notification.js +30 -0
  93. package/providers/_builtin/ide/cursor/scripts/0.49/focus_editor.js +13 -0
  94. package/providers/_builtin/ide/cursor/scripts/0.49/list_models.js +78 -0
  95. package/providers/_builtin/ide/cursor/scripts/0.49/list_modes.js +40 -0
  96. package/providers/_builtin/ide/cursor/scripts/0.49/list_notifications.js +23 -0
  97. package/providers/_builtin/ide/cursor/scripts/0.49/list_sessions.js +42 -0
  98. package/providers/_builtin/ide/cursor/scripts/0.49/new_session.js +20 -0
  99. package/providers/_builtin/ide/cursor/scripts/0.49/open_panel.js +23 -0
  100. package/providers/_builtin/ide/cursor/scripts/0.49/read_chat.js +75 -0
  101. package/providers/_builtin/ide/cursor/scripts/0.49/resolve_action.js +19 -0
  102. package/providers/_builtin/ide/cursor/scripts/0.49/scripts.js +78 -0
  103. package/providers/_builtin/ide/cursor/scripts/0.49/send_message.js +23 -0
  104. package/providers/_builtin/ide/cursor/scripts/0.49/set_mode.js +38 -0
  105. package/providers/_builtin/ide/cursor/scripts/0.49/set_model.js +81 -0
  106. package/providers/_builtin/ide/cursor/scripts/0.49/switch_session.js +28 -0
  107. package/providers/_builtin/ide/kiro/provider.json +67 -0
  108. package/providers/_builtin/ide/kiro/scripts/focus_editor.js +20 -0
  109. package/providers/_builtin/ide/kiro/scripts/open_panel.js +47 -0
  110. package/providers/_builtin/ide/kiro/scripts/resolve_action.js +54 -0
  111. package/providers/_builtin/ide/kiro/scripts/send_message.js +29 -0
  112. package/providers/_builtin/ide/kiro/scripts/webview_list_models.js +39 -0
  113. package/providers/_builtin/ide/kiro/scripts/webview_list_modes.js +39 -0
  114. package/providers/_builtin/ide/kiro/scripts/webview_list_sessions.js +21 -0
  115. package/providers/_builtin/ide/kiro/scripts/webview_new_session.js +34 -0
  116. package/providers/_builtin/ide/kiro/scripts/webview_read_chat.js +68 -0
  117. package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +72 -0
  118. package/providers/_builtin/ide/kiro/scripts/webview_set_mode.js +15 -0
  119. package/providers/_builtin/ide/kiro/scripts/webview_set_model.js +15 -0
  120. package/providers/_builtin/ide/kiro/scripts/webview_switch_session.js +26 -0
  121. package/providers/_builtin/ide/kiro/scripts.js +62 -0
  122. package/providers/_builtin/ide/pearai/provider.json +67 -0
  123. package/providers/_builtin/ide/pearai/scripts/focus_editor.js +20 -0
  124. package/providers/_builtin/ide/pearai/scripts/list_sessions.js +38 -0
  125. package/providers/_builtin/ide/pearai/scripts/new_session.js +55 -0
  126. package/providers/_builtin/ide/pearai/scripts/open_panel.js +46 -0
  127. package/providers/_builtin/ide/pearai/scripts/resolve_action.js +54 -0
  128. package/providers/_builtin/ide/pearai/scripts/send_message.js +29 -0
  129. package/providers/_builtin/ide/pearai/scripts/webview_list_models.js +43 -0
  130. package/providers/_builtin/ide/pearai/scripts/webview_list_modes.js +35 -0
  131. package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +62 -0
  132. package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +49 -0
  133. package/providers/_builtin/ide/pearai/scripts/webview_read_chat.js +92 -0
  134. package/providers/_builtin/ide/pearai/scripts/webview_resolve_action.js +59 -0
  135. package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +72 -0
  136. package/providers/_builtin/ide/pearai/scripts/webview_set_mode.js +36 -0
  137. package/providers/_builtin/ide/pearai/scripts/webview_set_model.js +36 -0
  138. package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +34 -0
  139. package/providers/_builtin/ide/pearai/scripts.js +74 -0
  140. package/providers/_builtin/ide/trae/provider.json +66 -0
  141. package/providers/_builtin/ide/trae/scripts/focus_editor.js +20 -0
  142. package/providers/_builtin/ide/trae/scripts/list_chats.js +24 -0
  143. package/providers/_builtin/ide/trae/scripts/list_models.js +39 -0
  144. package/providers/_builtin/ide/trae/scripts/list_modes.js +39 -0
  145. package/providers/_builtin/ide/trae/scripts/new_session.js +30 -0
  146. package/providers/_builtin/ide/trae/scripts/open_panel.js +44 -0
  147. package/providers/_builtin/ide/trae/scripts/read_chat.js +113 -0
  148. package/providers/_builtin/ide/trae/scripts/resolve_action.js +54 -0
  149. package/providers/_builtin/ide/trae/scripts/send_message.js +69 -0
  150. package/providers/_builtin/ide/trae/scripts/set_mode.js +15 -0
  151. package/providers/_builtin/ide/trae/scripts/set_model.js +15 -0
  152. package/providers/_builtin/ide/trae/scripts/switch_session.js +23 -0
  153. package/providers/_builtin/ide/trae/scripts.js +57 -0
  154. package/providers/_builtin/ide/vscode/provider.json +64 -0
  155. package/providers/_builtin/ide/vscode-insiders/provider.json +62 -0
  156. package/providers/_builtin/ide/vscodium/provider.json +63 -0
  157. package/providers/_builtin/ide/windsurf/provider.json +53 -0
  158. package/providers/_builtin/ide/windsurf/scripts/focus_editor.js +30 -0
  159. package/providers/_builtin/ide/windsurf/scripts/list_chats.js +117 -0
  160. package/providers/_builtin/ide/windsurf/scripts/list_models.js +39 -0
  161. package/providers/_builtin/ide/windsurf/scripts/list_modes.js +39 -0
  162. package/providers/_builtin/ide/windsurf/scripts/new_session.js +69 -0
  163. package/providers/_builtin/ide/windsurf/scripts/open_panel.js +58 -0
  164. package/providers/_builtin/ide/windsurf/scripts/read_chat.js +297 -0
  165. package/providers/_builtin/ide/windsurf/scripts/resolve_action.js +68 -0
  166. package/providers/_builtin/ide/windsurf/scripts/send_message.js +87 -0
  167. package/providers/_builtin/ide/windsurf/scripts/set_mode.js +15 -0
  168. package/providers/_builtin/ide/windsurf/scripts/set_model.js +15 -0
  169. package/providers/_builtin/ide/windsurf/scripts/switch_session.js +58 -0
  170. package/providers/_builtin/ide/windsurf/scripts.js +57 -0
  171. package/providers/_builtin/registry.json +266 -0
  172. package/providers/_builtin/validate.js +156 -0
  173. package/src/agent-stream/index.ts +6 -0
  174. package/src/agent-stream/manager.ts +286 -0
  175. package/src/agent-stream/poller.ts +154 -0
  176. package/src/agent-stream/provider-adapter.ts +138 -0
  177. package/src/agent-stream/types.ts +61 -0
  178. package/src/boot/daemon-lifecycle.ts +252 -0
  179. package/src/cdp/devtools.ts +335 -0
  180. package/src/cdp/initializer.ts +191 -0
  181. package/src/cdp/manager.ts +897 -0
  182. package/src/cdp/scanner.ts +185 -0
  183. package/src/cdp/setup.ts +150 -0
  184. package/src/cli-adapter-types.ts +25 -0
  185. package/src/cli-adapters/provider-cli-adapter.ts +448 -0
  186. package/src/commands/cdp-commands.ts +208 -0
  187. package/src/commands/chat-commands.ts +675 -0
  188. package/src/commands/cli-manager.ts +353 -0
  189. package/src/commands/handler.ts +328 -0
  190. package/src/commands/router.ts +258 -0
  191. package/src/commands/stream-commands.ts +325 -0
  192. package/src/config/chat-history.ts +211 -0
  193. package/src/config/config.ts +219 -0
  194. package/src/daemon/dev-server.ts +2378 -0
  195. package/src/daemon/scaffold-template.ts +394 -0
  196. package/src/daemon-core.ts +50 -0
  197. package/src/detection/cli-detector.ts +89 -0
  198. package/src/detection/ide-detector.ts +157 -0
  199. package/src/index.ts +103 -0
  200. package/src/installer.ts +263 -0
  201. package/src/ipc-protocol.ts +133 -0
  202. package/src/launch.ts +433 -0
  203. package/src/logging/command-log.ts +180 -0
  204. package/src/logging/logger.ts +316 -0
  205. package/src/providers/acp-provider-instance.ts +1140 -0
  206. package/src/providers/cli-provider-instance.ts +207 -0
  207. package/src/providers/contracts.ts +524 -0
  208. package/src/providers/extension-provider-instance.ts +156 -0
  209. package/src/providers/ide-provider-instance.ts +377 -0
  210. package/src/providers/index.ts +18 -0
  211. package/src/providers/provider-instance-manager.ts +182 -0
  212. package/src/providers/provider-instance.ts +112 -0
  213. package/src/providers/provider-loader.ts +1031 -0
  214. package/src/providers/status-monitor.ts +125 -0
  215. package/src/providers/version-archive.ts +266 -0
  216. package/src/status/reporter.ts +294 -0
  217. package/src/types.ts +206 -0
@@ -0,0 +1,377 @@
1
+ /**
2
+ * IdeProviderInstance — Runtime instance for IDE Provider
3
+ *
4
+ * Within a single IDE:
5
+ * 1. Native chat (readChat via CDP)
6
+ * 2. Extension agents (Cline, Roo Code etc)
7
+ *
8
+ * IDE Instance manages child Extension Instances.
9
+ * Daemon collects all via a single IDE Instance.getState() call.
10
+ */
11
+
12
+ import * as os from 'os';
13
+ import * as crypto from 'crypto';
14
+ import type { ProviderModule } from './contracts.js';
15
+ import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext } from './provider-instance.js';
16
+ import { ExtensionProviderInstance } from './extension-provider-instance.js';
17
+ import { StatusMonitor } from './status-monitor.js';
18
+ import { ChatHistoryWriter } from '../config/chat-history.js';
19
+ import { LOG } from '../logging/logger.js';
20
+
21
+ export class IdeProviderInstance implements ProviderInstance {
22
+ readonly type: string;
23
+ readonly category = 'ide' as const;
24
+
25
+ private provider: ProviderModule;
26
+ private context: InstanceContext | null = null;
27
+ private settings: Record<string, any> = {};
28
+ private events: ProviderEvent[] = [];
29
+ private tickErrorCount = 0;
30
+
31
+ // Cached status
32
+ private cachedChat: any = null;
33
+ private currentStatus: string = 'idle';
34
+ private lastAgentStatuses = new Map<string, string>();
35
+ private generatingStartedAt = new Map<string, number>();
36
+ private tickBusy = false;
37
+ private monitor: StatusMonitor;
38
+ private historyWriter: ChatHistoryWriter;
39
+
40
+ // IDE meta
41
+ private ideVersion: string = '';
42
+ private instanceId: string;
43
+ private workspaceFolders: { name: string; path: string }[] = [];
44
+ private activeFile: string | null = null;
45
+
46
+ // ─── Child Extension Instances ────────────────────
47
+ private extensions = new Map<string, ExtensionProviderInstance>();
48
+
49
+ constructor(provider: ProviderModule, instanceKey?: string) {
50
+ // type always base provider type (e.g. 'antigravity') — display/script queryin use
51
+ this.type = provider.type;
52
+ this.provider = provider;
53
+ // instanceId UUID — unique identifier for all routing
54
+ this.instanceId = crypto.randomUUID();
55
+ this.monitor = new StatusMonitor();
56
+ this.historyWriter = new ChatHistoryWriter();
57
+ }
58
+
59
+ // ─── Lifecycle ─────────────────────────────────
60
+
61
+ async init(context: InstanceContext): Promise<void> {
62
+ this.context = context;
63
+ this.settings = context.settings || {};
64
+ // Sync Monitor config
65
+ this.monitor.updateConfig({
66
+ approvalAlert: this.settings.approvalAlert !== false,
67
+ longGeneratingAlert: this.settings.longGeneratingAlert !== false,
68
+ longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180,
69
+ });
70
+ }
71
+
72
+ async onTick(): Promise<void> {
73
+ if (!this.context?.cdp?.isConnected || this.tickBusy) return;
74
+ this.tickBusy = true;
75
+
76
+ try {
77
+ // 1. Native chat read
78
+ await this.readChat();
79
+
80
+ // 2. Child Extension tick
81
+ for (const [id, ext] of this.extensions) {
82
+ try {
83
+ await ext.onTick();
84
+ } catch (e: any) {
85
+ LOG.warn('IdeInstance', `[IdeInstance:${this.type}] Extension ${id} tick error: ${e?.message}`);
86
+ }
87
+ }
88
+
89
+ this.tickErrorCount = 0;
90
+ } catch (e: any) {
91
+ this.tickErrorCount++;
92
+ if (this.tickErrorCount <= 3 || this.tickErrorCount % 10 === 0) {
93
+ LOG.warn('IdeInstance', `[IdeInstance:${this.type}] onTick error (${this.tickErrorCount}): ${e?.message || e}`);
94
+ }
95
+ } finally {
96
+ this.tickBusy = false;
97
+ }
98
+ }
99
+
100
+ getState(): ProviderState {
101
+ const cdp = this.context?.cdp;
102
+
103
+ // Collect extension status
104
+ const extensionStates: ProviderState[] = [];
105
+ for (const ext of this.extensions.values()) {
106
+ extensionStates.push(ext.getState());
107
+ }
108
+
109
+ return {
110
+ type: this.type,
111
+ name: this.provider.name,
112
+ category: 'ide',
113
+ status: this.currentStatus as ProviderState['status'],
114
+ activeChat: this.cachedChat ? {
115
+ id: this.cachedChat.id || 'active_session',
116
+ title: this.cachedChat.title || this.type,
117
+ status: this.cachedChat.status || this.currentStatus,
118
+ messages: this.cachedChat.messages || [],
119
+ activeModal: this.cachedChat.activeModal || null,
120
+ inputContent: this.cachedChat.inputContent || '',
121
+ } : null,
122
+ workspaceFolders: this.workspaceFolders,
123
+ activeFile: this.activeFile,
124
+ extensions: extensionStates,
125
+ cdpConnected: cdp?.isConnected || false,
126
+ currentModel: this.cachedChat?.model || undefined,
127
+ currentPlan: this.cachedChat?.mode || undefined,
128
+ currentAutoApprove: this.cachedChat?.autoApprove || undefined,
129
+ instanceId: this.instanceId,
130
+ lastUpdated: Date.now(),
131
+ settings: this.settings,
132
+ pendingEvents: this.flushEvents(),
133
+ };
134
+ }
135
+
136
+ onEvent(event: string, data?: any): void {
137
+ if (event === 'cdp_connected') {
138
+ // CDP connectiondone
139
+ } else if (event === 'cdp_disconnected') {
140
+ this.cachedChat = null;
141
+ this.currentStatus = 'idle';
142
+ } else if (event === 'extension_data') {
143
+ if (data?.workspaceFolders) this.workspaceFolders = data.workspaceFolders;
144
+ if (data?.activeFile) this.activeFile = data.activeFile;
145
+ if (data?.ideVersion) this.ideVersion = data.ideVersion;
146
+ if (data?.instanceId) this.instanceId = data.instanceId;
147
+ } else if (event === 'stream_update') {
148
+ // Forward to Extension
149
+ const extType = data?.extensionType;
150
+ if (extType && this.extensions.has(extType)) {
151
+ this.extensions.get(extType)!.onEvent('stream_update', data);
152
+ }
153
+ }
154
+ }
155
+
156
+ dispose(): void {
157
+ this.cachedChat = null;
158
+ this.lastAgentStatuses.clear();
159
+ this.generatingStartedAt.clear();
160
+ this.monitor.reset();
161
+ // Child Extension cleanup
162
+ for (const ext of this.extensions.values()) {
163
+ ext.dispose();
164
+ }
165
+ this.extensions.clear();
166
+ }
167
+
168
+ // ─── Extension manage ─────────────────────────────
169
+
170
+ /** Extension Instance add */
171
+ async addExtension(provider: ProviderModule, settings?: Record<string, any>): Promise<void> {
172
+ if (this.extensions.has(provider.type)) return;
173
+
174
+ const ext = new ExtensionProviderInstance(provider);
175
+ await ext.init({
176
+ cdp: this.context?.cdp,
177
+ serverConn: this.context?.serverConn,
178
+ settings: settings || {},
179
+ });
180
+ ext.onEvent('extension_connected', { ideType: this.type });
181
+ this.extensions.set(provider.type, ext);
182
+ LOG.info('IdeInstance', `[IdeInstance:${this.type}] Extension added: ${provider.type}`);
183
+ }
184
+
185
+ /** Extension Instance remove */
186
+ removeExtension(type: string): void {
187
+ const ext = this.extensions.get(type);
188
+ if (ext) {
189
+ ext.dispose();
190
+ this.extensions.delete(type);
191
+ }
192
+ }
193
+
194
+ /** Extension Instance Import */
195
+ getExtension(type: string): ExtensionProviderInstance | undefined {
196
+ return this.extensions.get(type);
197
+ }
198
+
199
+ /** Child Extension list */
200
+ getExtensionTypes(): string[] {
201
+ return [...this.extensions.keys()];
202
+ }
203
+
204
+ /** Query UUID instanceId */
205
+ getInstanceId(): string {
206
+ return this.instanceId;
207
+ }
208
+
209
+ /** all Extension Instance list */
210
+ getExtensionInstances(): ExtensionProviderInstance[] {
211
+ return [...this.extensions.values()];
212
+ }
213
+
214
+ // ─── CDP readChat ───────────────────────────────
215
+
216
+ private async readChat(): Promise<void> {
217
+ const { cdp } = this.context!;
218
+ if (!cdp?.isConnected) return;
219
+
220
+ try {
221
+ let raw: any = null;
222
+
223
+ // path 1: webview iframe internal (Kiro, PearAI etc)
224
+ const webviewFn = (this.provider.scripts as any)?.webviewReadChat;
225
+ if (typeof webviewFn === 'function' && cdp.evaluateInWebviewFrame) {
226
+ const webviewScript = webviewFn();
227
+ if (webviewScript) {
228
+ const matchText = (this.provider as any).webviewMatchText;
229
+ const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
230
+ const webviewRaw = await cdp.evaluateInWebviewFrame(webviewScript, matchFn);
231
+ if (webviewRaw) {
232
+ raw = typeof webviewRaw === 'string' ? (() => { try { return JSON.parse(webviewRaw); } catch { return null; } })() : webviewRaw;
233
+ }
234
+ }
235
+ }
236
+
237
+ // path 2: Main DOM (Cursor, Windsurf, Trae, Antigravity etc)
238
+ if (!raw) {
239
+ const readChatScript = this.getReadChatScript();
240
+ if (!readChatScript) return;
241
+ raw = await cdp.evaluate(readChatScript, 30000) as any;
242
+ if (typeof raw === 'string') {
243
+ try { raw = JSON.parse(raw); } catch { return; }
244
+ }
245
+ }
246
+
247
+ if (!raw || typeof raw !== 'object') return;
248
+
249
+ // Modal filter
250
+ let { activeModal } = raw;
251
+ if (activeModal) {
252
+ const w = activeModal.width ?? Infinity;
253
+ const h = activeModal.height ?? Infinity;
254
+ if (w < 80 || h < 40) {
255
+ activeModal = undefined;
256
+ } else {
257
+ activeModal = {
258
+ message: activeModal.message?.slice(0, 300) ?? '',
259
+ buttons: (activeModal.buttons ?? []).filter((t: string) => t.length < 30),
260
+ };
261
+ }
262
+ }
263
+
264
+ // Assign receivedAt
265
+ const prevMsgs = this.cachedChat?.messages || [];
266
+ const prevByHash = new Map<string, number>();
267
+ for (const pm of prevMsgs) {
268
+ const h = `${pm.role}:${(pm.content || '').slice(0, 100)}`;
269
+ if (pm.receivedAt) prevByHash.set(h, pm.receivedAt);
270
+ }
271
+ const now = Date.now();
272
+ for (const msg of (raw.messages || [])) {
273
+ const h = `${msg.role}:${(msg.content || '').slice(0, 100)}`;
274
+ msg.receivedAt = prevByHash.get(h) || now;
275
+ }
276
+
277
+ this.cachedChat = { ...raw, activeModal };
278
+ this.detectAgentTransitions(raw, now);
279
+
280
+ // Save history (new messageonly append)
281
+ // Exclude last incomplete assistant message during generating status
282
+ if (raw.messages?.length > 0) {
283
+ let toSave = raw.messages;
284
+ if (raw.status === 'generating' || raw.status === 'long_generating') {
285
+ // Find and exclude last assistant message
286
+ const lastIdx = toSave.length - 1;
287
+ if (lastIdx >= 0 && toSave[lastIdx].role === 'assistant') {
288
+ toSave = toSave.slice(0, lastIdx);
289
+ }
290
+ }
291
+ if (toSave.length > 0) {
292
+ this.historyWriter.appendNewMessages(
293
+ this.type,
294
+ toSave,
295
+ raw.title,
296
+ this.instanceId,
297
+ );
298
+ }
299
+ }
300
+
301
+ } catch (e: any) {
302
+ const msg = e?.message || String(e);
303
+ if (msg.includes('Timeout') || msg.includes('timeout') || msg.includes('Target closed')) {
304
+ // CDP timeout — unified logging from onTick
305
+ } else {
306
+ LOG.warn('IdeInstance', `[IdeInstance:${this.type}] readChat internal error: ${msg}`);
307
+ }
308
+ }
309
+ }
310
+
311
+ private getReadChatScript(): string | null {
312
+ const scripts = this.provider.scripts;
313
+ if (!scripts?.readChat) return null;
314
+ return typeof scripts.readChat === 'function' ? scripts.readChat({}) : scripts.readChat as any;
315
+ }
316
+
317
+ // ─── status transition detect ─────────────────────────────
318
+
319
+ private detectAgentTransitions(chatData: any, now: number): void {
320
+ const chatStatus = chatData?.status;
321
+ if (!chatStatus) return;
322
+
323
+ const agentKey = `${this.type}:native`;
324
+ const agentStatus = (chatStatus === 'streaming' || chatStatus === 'generating') ? 'generating'
325
+ : chatStatus === 'waiting_approval' ? 'waiting_approval'
326
+ : 'idle';
327
+
328
+ this.currentStatus = agentStatus;
329
+ const lastStatus = this.lastAgentStatuses.get(agentKey) || 'idle';
330
+
331
+ if (agentStatus !== lastStatus) {
332
+ const chatTitle = chatData.title || this.provider.name;
333
+
334
+ if (lastStatus === 'idle' && agentStatus === 'generating') {
335
+ this.generatingStartedAt.set(agentKey, now);
336
+ this.pushEvent({ event: 'agent:generating_started', chatTitle, timestamp: now, ideType: this.type });
337
+ } else if (agentStatus === 'waiting_approval') {
338
+ if (!this.generatingStartedAt.has(agentKey)) this.generatingStartedAt.set(agentKey, now);
339
+ this.pushEvent({
340
+ event: 'agent:waiting_approval', chatTitle, timestamp: now, ideType: this.type,
341
+ modalMessage: chatData.activeModal?.message,
342
+ modalButtons: chatData.activeModal?.buttons,
343
+ });
344
+ } else if (agentStatus === 'idle' && (lastStatus === 'generating' || lastStatus === 'waiting_approval')) {
345
+ const startedAt = this.generatingStartedAt.get(agentKey);
346
+ const duration = startedAt ? Math.round((now - startedAt) / 1000) : 0;
347
+ this.pushEvent({ event: 'agent:generating_completed', chatTitle, duration, timestamp: now, ideType: this.type });
348
+ this.generatingStartedAt.delete(agentKey);
349
+ }
350
+
351
+ this.lastAgentStatuses.set(agentKey, agentStatus);
352
+ }
353
+
354
+ // Monitor check (cooldown based notification)
355
+ const monitorEvents = this.monitor.check(agentKey, agentStatus, now);
356
+ for (const me of monitorEvents) {
357
+ this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
358
+ }
359
+ }
360
+
361
+ private pushEvent(event: ProviderEvent): void {
362
+ this.events.push(event);
363
+ if (this.events.length > 50) this.events = this.events.slice(-50);
364
+ }
365
+
366
+ private flushEvents(): ProviderEvent[] {
367
+ const events = [...this.events];
368
+ this.events = [];
369
+ return events;
370
+ }
371
+
372
+ // ─── external access ─────────────────────────────────
373
+
374
+ updateCdp(cdp: InstanceContext['cdp']): void {
375
+ if (this.context) this.context.cdp = cdp;
376
+ }
377
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Provider System — Public API
3
+ */
4
+ export { ProviderLoader } from './provider-loader.js';
5
+ export type {
6
+ ProviderModule,
7
+ ProviderCategory,
8
+ ProviderScripts,
9
+ ResolvedProvider,
10
+ ReadChatResult,
11
+ ChatMessage,
12
+ AgentStatus,
13
+ ModalInfo,
14
+ SendMessageResult,
15
+ ListSessionsResult,
16
+ SessionInfo,
17
+ SwitchSessionResult,
18
+ } from './contracts.js';
@@ -0,0 +1,182 @@
1
+ /**
2
+ * ProviderInstanceManager — lifecycle management for all ProviderInstances
3
+ *
4
+ * Role:
5
+ * 1. Instance create/delete
6
+ * 2. Tick engine (periodic onTick calls)
7
+ * 3. Collect overall state
8
+ * 4. Event collection and propagation
9
+ */
10
+
11
+ import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext } from './provider-instance.js';
12
+ import { LOG } from '../logging/logger.js';
13
+
14
+ export class ProviderInstanceManager {
15
+ private instances = new Map<string, ProviderInstance>();
16
+ private tickTimer: NodeJS.Timeout | null = null;
17
+ private tickInterval = 5_000; // default 5seconds
18
+ private eventListeners: ((event: ProviderEvent & { providerType: string }) => void)[] = [];
19
+
20
+ // ─── Instance manage ──────────────────────────────
21
+
22
+ /**
23
+ * Instance add and initialize
24
+ */
25
+ async addInstance(id: string, instance: ProviderInstance, context: InstanceContext): Promise<void> {
26
+ if (this.instances.has(id)) {
27
+ LOG.warn('InstanceMgr', `[InstanceManager] Instance ${id} already exists, disposing old one`);
28
+ this.instances.get(id)!.dispose();
29
+ }
30
+ this.instances.set(id, instance);
31
+ await instance.init(context);
32
+ }
33
+
34
+ /**
35
+ * Instance remove
36
+ */
37
+ removeInstance(id: string): void {
38
+ const instance = this.instances.get(id);
39
+ if (instance) {
40
+ instance.dispose();
41
+ this.instances.delete(id);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Import by Instance ID
47
+ */
48
+ getInstance(id: string): ProviderInstance | undefined {
49
+ return this.instances.get(id);
50
+ }
51
+
52
+ /**
53
+ * Per-category Instance list
54
+ */
55
+ getByCategory(category: 'cli' | 'ide' | 'extension' | 'acp'): ProviderInstance[] {
56
+ return [...this.instances.values()].filter(i => i.category === category);
57
+ }
58
+
59
+ /**
60
+ * All Instance count
61
+ */
62
+ get size(): number {
63
+ return this.instances.size;
64
+ }
65
+
66
+ // ─── State collect ────────────────────────────────
67
+
68
+ /**
69
+ * all Instance's current status collect
70
+ * + Propagate pending events to event listeners
71
+ */
72
+ collectAllStates(): ProviderState[] {
73
+ const states: ProviderState[] = [];
74
+ for (const [id, instance] of this.instances) {
75
+ try {
76
+ const state = instance.getState();
77
+ states.push(state);
78
+
79
+ // pending events propagation
80
+ for (const event of state.pendingEvents) {
81
+ for (const listener of this.eventListeners) {
82
+ listener({ ...event, providerType: instance.type });
83
+ }
84
+ }
85
+ } catch (e) {
86
+ LOG.warn('InstanceMgr', `[InstanceManager] Failed to collect state from ${id}: ${(e as Error).message}`);
87
+ }
88
+ }
89
+ return states;
90
+ }
91
+
92
+ /**
93
+ * Per-category status collect
94
+ */
95
+ collectStatesByCategory(category: 'cli' | 'ide' | 'extension' | 'acp'): ProviderState[] {
96
+ return this.collectAllStates().filter(s => s.category === category);
97
+ }
98
+
99
+ // ─── Tick engine ─────────────────────────────────
100
+
101
+ /**
102
+ * Start tick — periodically call all Instance.onTick() call
103
+ */
104
+ startTicking(intervalMs?: number): void {
105
+ if (this.tickTimer) return;
106
+ this.tickInterval = intervalMs || this.tickInterval;
107
+
108
+ this.tickTimer = setInterval(async () => {
109
+ for (const [id, instance] of this.instances) {
110
+ try {
111
+ await instance.onTick();
112
+ } catch (e) {
113
+ LOG.warn('InstanceMgr', `[InstanceManager] Tick failed for ${id}: ${(e as Error).message}`);
114
+ }
115
+ }
116
+ }, this.tickInterval);
117
+ }
118
+
119
+ /**
120
+ * Stop tick
121
+ */
122
+ stopTicking(): void {
123
+ if (this.tickTimer) {
124
+ clearInterval(this.tickTimer);
125
+ this.tickTimer = null;
126
+ }
127
+ }
128
+
129
+ // ─── event ────────────────────────────────────
130
+
131
+ /**
132
+ * Register event listener (used for daemon status_event transmission)
133
+ */
134
+ onEvent(listener: (event: ProviderEvent & { providerType: string }) => void): void {
135
+ this.eventListeners.push(listener);
136
+ }
137
+
138
+ /**
139
+ * Forward event to specific Instance
140
+ */
141
+ sendEvent(id: string, event: string, data?: any): void {
142
+ this.instances.get(id)?.onEvent(event, data);
143
+ }
144
+
145
+ /**
146
+ * Broadcast event to all Instances
147
+ */
148
+ broadcast(event: string, data?: any): void {
149
+ for (const instance of this.instances.values()) {
150
+ instance.onEvent(event, data);
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Update settings for all instances of a given provider type.
156
+ * Called when user changes settings from dashboard.
157
+ */
158
+ updateInstanceSettings(providerType: string, settings: Record<string, any>): number {
159
+ let updated = 0;
160
+ for (const instance of this.instances.values()) {
161
+ if (instance.type === providerType && typeof instance.updateSettings === 'function') {
162
+ instance.updateSettings(settings);
163
+ updated++;
164
+ }
165
+ }
166
+ return updated;
167
+ }
168
+
169
+ // ─── cleanup ──────────────────────────────────────
170
+
171
+ /**
172
+ * All terminate
173
+ */
174
+ disposeAll(): void {
175
+ this.stopTicking();
176
+ for (const [id, instance] of this.instances) {
177
+ try { instance.dispose(); } catch { }
178
+ }
179
+ this.instances.clear();
180
+ this.eventListeners = [];
181
+ }
182
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * ProviderInstance — Provider runtime lifecycle
3
+ *
4
+ * provider.js = static config/scripts
5
+ * ProviderInstance = runtime status management + lifecycle
6
+ *
7
+ * Daemon only collects via ProviderInstance.getState(),
8
+ * Each Instance manages its own status.
9
+ */
10
+
11
+ import type { ProviderModule, ProviderSettingDef } from './contracts.js';
12
+
13
+ // ─── ProviderState — Standard status structure ─────────────
14
+
15
+ export interface ProviderState {
16
+ /** Provider type (e.g. 'gemini-cli', 'cursor', 'cline') */
17
+ type: string;
18
+ /** Provider Display name */
19
+ name: string;
20
+ /** category */
21
+ category: 'cli' | 'ide' | 'extension' | 'acp';
22
+ /** current status */
23
+ status: 'idle' | 'generating' | 'waiting_approval' | 'error' | 'stopped' | 'starting';
24
+
25
+ /** CLI mode: terminal = PTY stream, chat = parsed conversation */
26
+ mode?: 'terminal' | 'chat';
27
+
28
+ /** chat data */
29
+ activeChat: {
30
+ id: string;
31
+ title: string;
32
+ status: string;
33
+ messages: { role: string; content: string; timestamp?: number }[];
34
+ activeModal: { message: string; buttons: string[] } | null;
35
+ inputContent?: string;
36
+ } | null;
37
+
38
+ /** IDE/Extension -only */
39
+ workspaceFolders?: { name: string; path: string }[];
40
+ activeFile?: string | null;
41
+ agentStreams?: any[];
42
+ cdpConnected?: boolean;
43
+ /** IDE child Extension Instance status */
44
+ extensions?: ProviderState[];
45
+
46
+ /** CLI -only */
47
+ workingDir?: string;
48
+
49
+ /** Runtime info (real-time detection from IDE/CLI) */
50
+ currentModel?: string;
51
+ currentPlan?: string;
52
+ currentAutoApprove?: string;
53
+
54
+ /** meta */
55
+ instanceId: string;
56
+ lastUpdated: number;
57
+ settings: Record<string, any>;
58
+
59
+ /** Event queue (cleared after daemon collects) */
60
+ pendingEvents: ProviderEvent[];
61
+ }
62
+
63
+ export interface ProviderEvent {
64
+ event: string;
65
+ timestamp: number;
66
+ [key: string]: any;
67
+ }
68
+
69
+ // ─── ProviderInstance interface ─────────────────
70
+
71
+ export interface InstanceContext {
72
+ /** CDP connection (IDE/Extension) */
73
+ cdp?: {
74
+ isConnected: boolean;
75
+ evaluate(script: string, timeout?: number): Promise<unknown>;
76
+ evaluateInWebviewFrame?(expression: string, matchFn?: (bodyPreview: string) => boolean): Promise<string | null>;
77
+ discoverAgentWebviews?(): Promise<any[]>;
78
+ };
79
+ /** Server log transmit */
80
+ serverConn?: {
81
+ sendMessage(type: string, data: any): void;
82
+ };
83
+ /** P2P PTY output transmit */
84
+ onPtyData?: (data: string) => void;
85
+ /** Provider configvalue (resolved) */
86
+ settings: Record<string, any>;
87
+ }
88
+
89
+ export interface ProviderInstance {
90
+ /** Provider type */
91
+ readonly type: string;
92
+ /** Provider category */
93
+ readonly category: 'cli' | 'ide' | 'extension' | 'acp';
94
+
95
+ /** initialize */
96
+ init(context: InstanceContext): Promise<void>;
97
+
98
+ /** Tick — periodic status refresh (IDE: readChat, Extension: stream collection) */
99
+ onTick(): Promise<void>;
100
+
101
+ /** Return current status */
102
+ getState(): ProviderState;
103
+
104
+ /** Receive event (external → Instance) */
105
+ onEvent(event: string, data?: any): void;
106
+
107
+ /** Update settings at runtime (called when user changes settings from dashboard) */
108
+ updateSettings?(newSettings: Record<string, any>): void;
109
+
110
+ /** cleanup */
111
+ dispose(): void;
112
+ }