@adhdev/daemon-core 0.5.7 → 0.5.16
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.ts +460 -330
- package/dist/index.js +543 -101
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/providers/_builtin/cli/claude-cli/provider.json +34 -24
- package/providers/_builtin/cli/gemini-cli/provider.json +24 -17
- package/providers/_builtin/ide/antigravity/provider.json +21 -0
- package/providers/_builtin/ide/antigravity/scripts/1.106/resolve_action.js +19 -24
- package/providers/_builtin/ide/antigravity/scripts/1.107/read_chat.js +67 -5
- package/providers/_builtin/ide/antigravity/scripts/1.107/resolve_action.js +19 -24
- package/providers/_builtin/ide/cursor/scripts/0.49/read_chat.js +271 -32
- package/providers/_builtin/registry.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +96 -25
- package/src/commands/router.ts +132 -33
- package/src/daemon/dev-server.ts +285 -0
- package/src/daemon-core.ts +7 -6
- package/src/detection/ide-detector.ts +5 -5
- package/src/index.ts +24 -7
- package/src/launch.ts +2 -2
- package/src/providers/acp-provider-instance.ts +54 -56
- package/src/providers/cli-provider-instance.ts +32 -4
- package/src/providers/contracts.ts +5 -22
- package/src/providers/ide-provider-instance.ts +8 -10
- package/src/providers/provider-instance.ts +70 -33
- package/src/shared-types.ts +203 -0
- package/src/status/reporter.ts +31 -22
- package/src/types.ts +26 -110
package/src/launch.ts
CHANGED
|
@@ -101,7 +101,7 @@ async function isCdpActive(port: number): Promise<boolean> {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/** Kill IDE process (graceful → force) */
|
|
104
|
-
async function killIdeProcess(ideId: string): Promise<boolean> {
|
|
104
|
+
export async function killIdeProcess(ideId: string): Promise<boolean> {
|
|
105
105
|
const plat = os.platform();
|
|
106
106
|
const appName = getMacAppIdentifiers()[ideId];
|
|
107
107
|
const winProcesses = getWinProcessNames()[ideId];
|
|
@@ -158,7 +158,7 @@ async function killIdeProcess(ideId: string): Promise<boolean> {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/** Check if IDE process is running */
|
|
161
|
-
function isIdeRunning(ideId: string): boolean {
|
|
161
|
+
export function isIdeRunning(ideId: string): boolean {
|
|
162
162
|
const plat = os.platform();
|
|
163
163
|
|
|
164
164
|
try {
|
|
@@ -43,10 +43,11 @@ import {
|
|
|
43
43
|
type KillTerminalResponse,
|
|
44
44
|
type SessionUpdate,
|
|
45
45
|
type ToolCallStatus,
|
|
46
|
+
type SessionConfigOption,
|
|
46
47
|
} from '@agentclientprotocol/sdk';
|
|
47
48
|
import type { ProviderModule, ContentBlock, ToolCallInfo, ToolCallContent as TCC, ToolKind, ToolCallStatus as TCS } from './contracts.js';
|
|
48
49
|
import { normalizeContent, flattenContent } from './contracts.js';
|
|
49
|
-
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext } from './provider-instance.js';
|
|
50
|
+
import type { ProviderInstance, ProviderState, AcpProviderState, ProviderErrorReason, ProviderEvent, InstanceContext } from './provider-instance.js';
|
|
50
51
|
import { StatusMonitor } from './status-monitor.js';
|
|
51
52
|
import { LOG } from '../logging/logger.js';
|
|
52
53
|
|
|
@@ -118,7 +119,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
118
119
|
|
|
119
120
|
// Error tracking
|
|
120
121
|
private errorMessage: string | null = null;
|
|
121
|
-
private errorReason:
|
|
122
|
+
private errorReason: ProviderErrorReason | null = null;
|
|
122
123
|
private stderrBuffer: string[] = [];
|
|
123
124
|
private spawnedAt = 0;
|
|
124
125
|
|
|
@@ -170,7 +171,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
|
|
173
|
-
getState():
|
|
174
|
+
getState(): AcpProviderState {
|
|
174
175
|
const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
|
|
175
176
|
|
|
176
177
|
// Recent 50 messages
|
|
@@ -214,7 +215,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
214
215
|
} : null,
|
|
215
216
|
inputContent: '',
|
|
216
217
|
},
|
|
217
|
-
|
|
218
|
+
workspace: this.workingDir,
|
|
218
219
|
currentModel: this.currentModel,
|
|
219
220
|
currentPlan: this.currentMode,
|
|
220
221
|
instanceId: this.instanceId,
|
|
@@ -225,9 +226,9 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
225
226
|
acpConfigOptions: this.configOptions,
|
|
226
227
|
acpModes: this.availableModes,
|
|
227
228
|
// Error details for dashboard display
|
|
228
|
-
errorMessage: this.errorMessage,
|
|
229
|
-
errorReason: this.errorReason,
|
|
230
|
-
}
|
|
229
|
+
errorMessage: this.errorMessage || undefined,
|
|
230
|
+
errorReason: this.errorReason || undefined,
|
|
231
|
+
};
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
onEvent(event: string, data?: any): void {
|
|
@@ -295,7 +296,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
295
296
|
}
|
|
296
297
|
}
|
|
297
298
|
|
|
298
|
-
this.configOptions.push({ category: category as
|
|
299
|
+
this.configOptions.push({ category: category as 'model' | 'mode' | 'thought_level' | 'other', configId, currentValue, options: flatOptions });
|
|
299
300
|
|
|
300
301
|
// Auto-set currentModel/currentMode from config
|
|
301
302
|
if (category === 'model' && currentValue) this.currentModel = currentValue;
|
|
@@ -490,7 +491,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
490
491
|
if (pattern.test(text)) {
|
|
491
492
|
if (/ENOENT|command not found|not recognized/i.test(text)) {
|
|
492
493
|
this.errorReason = 'not_installed';
|
|
493
|
-
this.errorMessage = `Command '${command}' not found. Install: ${
|
|
494
|
+
this.errorMessage = `Command '${command}' not found. Install: ${this.provider.install || 'check documentation'}`;
|
|
494
495
|
} else {
|
|
495
496
|
this.errorReason = 'auth_failed';
|
|
496
497
|
this.errorMessage = text.slice(0, 300);
|
|
@@ -511,7 +512,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
511
512
|
if (!this.errorReason) {
|
|
512
513
|
if (code === 127) {
|
|
513
514
|
this.errorReason = 'not_installed';
|
|
514
|
-
this.errorMessage = `Command '${command}' not found (exit code 127). Install: ${
|
|
515
|
+
this.errorMessage = `Command '${command}' not found (exit code 127). Install: ${this.provider.install || 'check documentation'}`;
|
|
515
516
|
} else if (elapsed < 3000) {
|
|
516
517
|
// 3-second crash → likely install/auth issue
|
|
517
518
|
this.errorReason = this.stderrBuffer.length > 0 ? 'crash' : 'spawn_error';
|
|
@@ -533,7 +534,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
533
534
|
this.log.error(`[${this.type}] Process spawn error: ${err.message}`);
|
|
534
535
|
if (err.message.includes('ENOENT')) {
|
|
535
536
|
this.errorReason = 'not_installed';
|
|
536
|
-
this.errorMessage = `Command '${command}' not found. Install: ${
|
|
537
|
+
this.errorMessage = `Command '${command}' not found. Install: ${this.provider.install || 'check documentation'}`;
|
|
537
538
|
} else {
|
|
538
539
|
this.errorReason = 'spawn_error';
|
|
539
540
|
this.errorMessage = err.message;
|
|
@@ -751,10 +752,10 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
751
752
|
if (contentBlocks && contentBlocks.length > 0) {
|
|
752
753
|
// Rich content — forward ContentBlock[] as ACP prompt parts
|
|
753
754
|
promptParts = contentBlocks.map(b => {
|
|
754
|
-
if (b.type === 'text') return { type: 'text', text:
|
|
755
|
-
if (b.type === 'image') return { type: 'image', data:
|
|
756
|
-
if (b.type === 'resource_link') return { type: 'resource_link', uri:
|
|
757
|
-
if (b.type === 'resource') return { type: 'resource', resource:
|
|
755
|
+
if (b.type === 'text') return { type: 'text', text: b.text };
|
|
756
|
+
if (b.type === 'image') return { type: 'image', data: b.data, mimeType: b.mimeType };
|
|
757
|
+
if (b.type === 'resource_link') return { type: 'resource_link', uri: b.uri, name: b.name };
|
|
758
|
+
if (b.type === 'resource') return { type: 'resource', resource: b.resource };
|
|
758
759
|
return { type: 'text', text: flattenContent([b]) };
|
|
759
760
|
});
|
|
760
761
|
} else {
|
|
@@ -828,35 +829,32 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
828
829
|
private handleSessionUpdate(params: SessionNotification): void {
|
|
829
830
|
if (!params) return;
|
|
830
831
|
|
|
831
|
-
const update = params.update
|
|
832
|
-
this.log.debug(`[${this.type}] sessionUpdate: ${update.sessionUpdate}
|
|
832
|
+
const update = params.update;
|
|
833
|
+
this.log.debug(`[${this.type}] sessionUpdate: ${update.sessionUpdate}`);
|
|
833
834
|
|
|
834
835
|
switch (update.sessionUpdate) {
|
|
835
836
|
case 'agent_message_chunk': {
|
|
836
837
|
const content = update.content;
|
|
837
|
-
if (content
|
|
838
|
-
this.partialContent +=
|
|
839
|
-
} else if (content
|
|
840
|
-
// Collect image block
|
|
838
|
+
if (content.type === 'text') {
|
|
839
|
+
this.partialContent += content.text;
|
|
840
|
+
} else if (content.type === 'image') {
|
|
841
841
|
this.partialBlocks.push({
|
|
842
842
|
type: 'image',
|
|
843
|
-
data:
|
|
844
|
-
mimeType:
|
|
845
|
-
uri: (content as any).uri,
|
|
843
|
+
data: content.data,
|
|
844
|
+
mimeType: content.mimeType,
|
|
846
845
|
});
|
|
847
|
-
} else if (content
|
|
846
|
+
} else if (content.type === 'resource_link') {
|
|
848
847
|
this.partialBlocks.push({
|
|
849
848
|
type: 'resource_link',
|
|
850
|
-
uri:
|
|
851
|
-
name:
|
|
852
|
-
title:
|
|
853
|
-
mimeType:
|
|
854
|
-
size: (content as any).size,
|
|
849
|
+
uri: content.uri,
|
|
850
|
+
name: content.name || 'resource',
|
|
851
|
+
title: content.title ?? undefined,
|
|
852
|
+
mimeType: content.mimeType ?? undefined,
|
|
855
853
|
});
|
|
856
|
-
} else if (content
|
|
854
|
+
} else if (content.type === 'resource') {
|
|
857
855
|
this.partialBlocks.push({
|
|
858
856
|
type: 'resource',
|
|
859
|
-
resource:
|
|
857
|
+
resource: content.resource,
|
|
860
858
|
});
|
|
861
859
|
}
|
|
862
860
|
this.currentStatus = 'generating';
|
|
@@ -868,57 +866,57 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
868
866
|
break;
|
|
869
867
|
}
|
|
870
868
|
case 'tool_call': {
|
|
871
|
-
// New tool call —
|
|
872
|
-
const tcId =
|
|
873
|
-
const tcTitle =
|
|
874
|
-
const tcKind =
|
|
875
|
-
const tcStatus = this.mapToolCallStatus(
|
|
869
|
+
// New tool call — ACP SDK ToolCall has all fields typed
|
|
870
|
+
const tcId = update.toolCallId || `tc_${Date.now()}`;
|
|
871
|
+
const tcTitle = update.title || 'unknown';
|
|
872
|
+
const tcKind = update.kind as ToolKind | undefined;
|
|
873
|
+
const tcStatus = this.mapToolCallStatus(update.status);
|
|
876
874
|
|
|
877
875
|
this.activeToolCalls.push({
|
|
878
876
|
id: tcId,
|
|
879
877
|
name: tcTitle,
|
|
880
878
|
status: tcStatus,
|
|
881
|
-
input:
|
|
879
|
+
input: update.rawInput ? (typeof update.rawInput === 'string' ? update.rawInput : JSON.stringify(update.rawInput)) : undefined,
|
|
882
880
|
});
|
|
883
881
|
|
|
884
882
|
// Also collect as ToolCallInfo for rich content
|
|
885
|
-
const acpStatus =
|
|
883
|
+
const acpStatus = update.status || 'in_progress';
|
|
886
884
|
this.turnToolCalls.push({
|
|
887
885
|
toolCallId: tcId,
|
|
888
886
|
title: tcTitle,
|
|
889
887
|
kind: tcKind,
|
|
890
888
|
status: acpStatus as TCS,
|
|
891
|
-
rawInput:
|
|
892
|
-
content: this.convertToolCallContent(
|
|
893
|
-
locations:
|
|
889
|
+
rawInput: update.rawInput,
|
|
890
|
+
content: this.convertToolCallContent(update.content),
|
|
891
|
+
locations: update.locations,
|
|
894
892
|
});
|
|
895
893
|
break;
|
|
896
894
|
}
|
|
897
895
|
case 'tool_call_update': {
|
|
898
|
-
// Update existing tool call
|
|
899
|
-
const toolCallId =
|
|
896
|
+
// Update existing tool call — ACP SDK ToolCallUpdate typed
|
|
897
|
+
const toolCallId = update.toolCallId;
|
|
900
898
|
const existing = this.activeToolCalls.find(t => t.id === toolCallId);
|
|
901
899
|
if (existing) {
|
|
902
|
-
if (
|
|
903
|
-
if (
|
|
900
|
+
if (update.status) existing.status = this.mapToolCallStatus(update.status);
|
|
901
|
+
if (update.rawOutput) existing.output = typeof update.rawOutput === 'string' ? update.rawOutput : JSON.stringify(update.rawOutput);
|
|
904
902
|
}
|
|
905
903
|
// Update ToolCallInfo too
|
|
906
904
|
const tcInfo = this.turnToolCalls.find(t => t.toolCallId === toolCallId);
|
|
907
905
|
if (tcInfo) {
|
|
908
|
-
if (
|
|
909
|
-
if (
|
|
910
|
-
if (
|
|
911
|
-
if (
|
|
906
|
+
if (update.status) tcInfo.status = update.status as TCS;
|
|
907
|
+
if (update.rawOutput) tcInfo.rawOutput = update.rawOutput;
|
|
908
|
+
if (update.content) tcInfo.content = this.convertToolCallContent(update.content);
|
|
909
|
+
if (update.locations) tcInfo.locations = update.locations;
|
|
912
910
|
}
|
|
913
911
|
break;
|
|
914
912
|
}
|
|
915
913
|
case 'current_mode_update': {
|
|
916
|
-
this.currentMode =
|
|
914
|
+
this.currentMode = update.currentModeId;
|
|
917
915
|
break;
|
|
918
916
|
}
|
|
919
917
|
case 'config_option_update': {
|
|
920
|
-
if (
|
|
921
|
-
this.parseConfigOptions(
|
|
918
|
+
if (update.configOptions) {
|
|
919
|
+
this.parseConfigOptions(update.configOptions);
|
|
922
920
|
}
|
|
923
921
|
break;
|
|
924
922
|
}
|
|
@@ -930,7 +928,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
930
928
|
break;
|
|
931
929
|
default:
|
|
932
930
|
// Unknown update type — try legacy parsing for backward compatibility
|
|
933
|
-
this.handleLegacyUpdate(update
|
|
931
|
+
this.handleLegacyUpdate(update);
|
|
934
932
|
break;
|
|
935
933
|
}
|
|
936
934
|
}
|
|
@@ -1051,13 +1049,13 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
1051
1049
|
return { ...b, text: b.text.slice(0, -3) };
|
|
1052
1050
|
}
|
|
1053
1051
|
return b;
|
|
1054
|
-
}).filter(b => b.type !== 'text' || (b
|
|
1052
|
+
}).filter(b => b.type !== 'text' || (b.type === 'text' && b.text.trim()));
|
|
1055
1053
|
|
|
1056
1054
|
if (finalBlocks.length > 0) {
|
|
1057
1055
|
this.messages.push({
|
|
1058
1056
|
role: 'assistant',
|
|
1059
1057
|
content: finalBlocks.length === 1 && finalBlocks[0].type === 'text'
|
|
1060
|
-
? (finalBlocks[0] as
|
|
1058
|
+
? (finalBlocks[0] as {type: 'text', text: string}).text // single text → string (backward compat)
|
|
1061
1059
|
: finalBlocks,
|
|
1062
1060
|
timestamp: Date.now(),
|
|
1063
1061
|
toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : undefined,
|
|
@@ -26,6 +26,8 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
26
26
|
private generatingStartedAt: number = 0;
|
|
27
27
|
private settings: Record<string, any> = {};
|
|
28
28
|
private monitor: StatusMonitor;
|
|
29
|
+
private generatingDebounceTimer: NodeJS.Timeout | null = null;
|
|
30
|
+
private generatingDebouncePending: { chatTitle: string; timestamp: number } | null = null;
|
|
29
31
|
private historyWriter: ChatHistoryWriter;
|
|
30
32
|
readonly instanceId: string;
|
|
31
33
|
|
|
@@ -126,7 +128,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
126
128
|
activeModal: adapterStatus.activeModal,
|
|
127
129
|
inputContent: '',
|
|
128
130
|
},
|
|
129
|
-
|
|
131
|
+
workspace: this.workingDir,
|
|
130
132
|
instanceId: this.instanceId,
|
|
131
133
|
lastUpdated: Date.now(),
|
|
132
134
|
settings: this.settings,
|
|
@@ -160,8 +162,24 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
160
162
|
LOG.info('CLI', `[${this.type}] status: ${this.lastStatus} → ${newStatus}`);
|
|
161
163
|
if (this.lastStatus === 'idle' && newStatus === 'generating') {
|
|
162
164
|
this.generatingStartedAt = now;
|
|
163
|
-
|
|
165
|
+
// Defer the generating_started event — if idle comes back within 1s,
|
|
166
|
+
// the whole started→completed pair was a false positive from PTY noise
|
|
167
|
+
if (this.generatingDebounceTimer) clearTimeout(this.generatingDebounceTimer);
|
|
168
|
+
this.generatingDebouncePending = { chatTitle, timestamp: now };
|
|
169
|
+
this.generatingDebounceTimer = setTimeout(() => {
|
|
170
|
+
if (this.generatingDebouncePending) {
|
|
171
|
+
this.pushEvent({ event: 'agent:generating_started', ...this.generatingDebouncePending });
|
|
172
|
+
this.generatingDebouncePending = null;
|
|
173
|
+
}
|
|
174
|
+
this.generatingDebounceTimer = null;
|
|
175
|
+
}, 1000);
|
|
164
176
|
} else if (newStatus === 'waiting_approval') {
|
|
177
|
+
// Flush pending generating_started if debounce still pending
|
|
178
|
+
if (this.generatingDebouncePending) {
|
|
179
|
+
if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
|
|
180
|
+
this.pushEvent({ event: 'agent:generating_started', ...this.generatingDebouncePending });
|
|
181
|
+
this.generatingDebouncePending = null;
|
|
182
|
+
}
|
|
165
183
|
if (!this.generatingStartedAt) this.generatingStartedAt = now;
|
|
166
184
|
const modal = adapterStatus.activeModal;
|
|
167
185
|
LOG.info('CLI', `[${this.type}] approval modal: "${modal?.message?.slice(0, 80) ?? 'none'}"`);
|
|
@@ -172,10 +190,20 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
172
190
|
});
|
|
173
191
|
} else if (newStatus === 'idle' && (this.lastStatus === 'generating' || this.lastStatus === 'waiting_approval')) {
|
|
174
192
|
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1000) : 0;
|
|
175
|
-
|
|
176
|
-
this.
|
|
193
|
+
// If debounce still pending (generating lasted < 1s), cancel both events
|
|
194
|
+
if (this.generatingDebouncePending) {
|
|
195
|
+
LOG.info('CLI', `[${this.type}] suppressed short generating (${now - this.generatingStartedAt}ms)`);
|
|
196
|
+
if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
|
|
197
|
+
this.generatingDebouncePending = null;
|
|
198
|
+
} else {
|
|
199
|
+
LOG.info('CLI', `[${this.type}] completed in ${duration}s`);
|
|
200
|
+
this.pushEvent({ event: 'agent:generating_completed', chatTitle, duration, timestamp: now });
|
|
201
|
+
}
|
|
177
202
|
this.generatingStartedAt = 0;
|
|
178
203
|
} else if (newStatus === 'stopped') {
|
|
204
|
+
// Cancel any pending debounce
|
|
205
|
+
if (this.generatingDebounceTimer) { clearTimeout(this.generatingDebounceTimer); this.generatingDebounceTimer = null; }
|
|
206
|
+
this.generatingDebouncePending = null;
|
|
179
207
|
this.pushEvent({ event: 'agent:stopped', chatTitle, timestamp: now });
|
|
180
208
|
}
|
|
181
209
|
this.lastStatus = newStatus;
|
|
@@ -25,28 +25,11 @@ export interface ReadChatResult {
|
|
|
25
25
|
isWelcomeScreen?: boolean;
|
|
26
26
|
inputContent?: string;
|
|
27
27
|
model?: string;
|
|
28
|
-
mode?: string;
|
|
29
28
|
autoApprove?: string;
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/** Plain text (legacy) or rich content blocks (ACP standard) */
|
|
35
|
-
content: string | ContentBlock[];
|
|
36
|
-
/** Optional: unique message ID */
|
|
37
|
-
id?: string;
|
|
38
|
-
/** Optional: order index */
|
|
39
|
-
index?: number;
|
|
40
|
-
/** Optional: timestamp */
|
|
41
|
-
timestamp?: number;
|
|
42
|
-
/** Optional: receivedAt (assigned by daemon) */
|
|
43
|
-
receivedAt?: number;
|
|
44
|
-
/** Optional: tool calls associated with this message */
|
|
45
|
-
toolCalls?: ToolCallInfo[];
|
|
46
|
-
/** Optional: fiber metadata */
|
|
47
|
-
_type?: string;
|
|
48
|
-
_sub?: string;
|
|
49
|
-
}
|
|
31
|
+
import type { ChatMessage } from '../types.js';
|
|
32
|
+
export type { ChatMessage };
|
|
50
33
|
|
|
51
34
|
export type AgentStatus =
|
|
52
35
|
| 'idle'
|
|
@@ -124,13 +107,13 @@ export interface ResourceBlock {
|
|
|
124
107
|
export interface TextResourceContents {
|
|
125
108
|
uri: string;
|
|
126
109
|
text: string;
|
|
127
|
-
mimeType?: string;
|
|
110
|
+
mimeType?: string | null;
|
|
128
111
|
}
|
|
129
112
|
|
|
130
113
|
export interface BlobResourceContents {
|
|
131
114
|
uri: string;
|
|
132
115
|
blob: string; // base64-encoded
|
|
133
|
-
mimeType?: string;
|
|
116
|
+
mimeType?: string | null;
|
|
134
117
|
}
|
|
135
118
|
|
|
136
119
|
export interface ContentAnnotations {
|
|
@@ -163,7 +146,7 @@ export type ToolCallContent =
|
|
|
163
146
|
|
|
164
147
|
export interface ToolCallLocation {
|
|
165
148
|
path: string;
|
|
166
|
-
line?: number;
|
|
149
|
+
line?: number | null;
|
|
167
150
|
}
|
|
168
151
|
|
|
169
152
|
// ─── Content Helpers ────────────────────────────────────
|
|
@@ -40,8 +40,7 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
40
40
|
// IDE meta
|
|
41
41
|
private ideVersion: string = '';
|
|
42
42
|
private instanceId: string;
|
|
43
|
-
private
|
|
44
|
-
private activeFile: string | null = null;
|
|
43
|
+
private workspace: string = '';
|
|
45
44
|
|
|
46
45
|
// ─── Child Extension Instances ────────────────────
|
|
47
46
|
private extensions = new Map<string, ExtensionProviderInstance>();
|
|
@@ -119,8 +118,7 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
119
118
|
activeModal: this.cachedChat.activeModal || null,
|
|
120
119
|
inputContent: this.cachedChat.inputContent || '',
|
|
121
120
|
} : null,
|
|
122
|
-
|
|
123
|
-
activeFile: this.activeFile,
|
|
121
|
+
workspace: this.workspace || null,
|
|
124
122
|
extensions: extensionStates,
|
|
125
123
|
cdpConnected: cdp?.isConnected || false,
|
|
126
124
|
currentModel: this.cachedChat?.model || undefined,
|
|
@@ -135,15 +133,10 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
135
133
|
|
|
136
134
|
onEvent(event: string, data?: any): void {
|
|
137
135
|
if (event === 'cdp_connected') {
|
|
138
|
-
// CDP
|
|
136
|
+
// CDP connection done
|
|
139
137
|
} else if (event === 'cdp_disconnected') {
|
|
140
138
|
this.cachedChat = null;
|
|
141
139
|
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
140
|
} else if (event === 'stream_update') {
|
|
148
141
|
// Forward to Extension
|
|
149
142
|
const extType = data?.extensionType;
|
|
@@ -211,6 +204,11 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
211
204
|
return [...this.extensions.values()];
|
|
212
205
|
}
|
|
213
206
|
|
|
207
|
+
/** Set workspace from daemon launch context */
|
|
208
|
+
setWorkspace(workspace: string): void {
|
|
209
|
+
this.workspace = workspace;
|
|
210
|
+
}
|
|
211
|
+
|
|
214
212
|
// ─── CDP readChat ───────────────────────────────
|
|
215
213
|
|
|
216
214
|
private async readChat(): Promise<void> {
|
|
@@ -9,57 +9,94 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { ProviderModule, ProviderSettingDef } from './contracts.js';
|
|
12
|
+
import type { AcpConfigOption, AcpMode } from '../shared-types.js';
|
|
13
|
+
import type { ChatMessage } from '../types.js';
|
|
12
14
|
|
|
13
|
-
// ─── ProviderState —
|
|
15
|
+
// ─── ProviderState — Discriminated union by category ─────────────
|
|
14
16
|
|
|
15
|
-
export
|
|
17
|
+
export type ProviderStatus = 'idle' | 'generating' | 'waiting_approval' | 'error' | 'stopped' | 'starting';
|
|
18
|
+
|
|
19
|
+
export interface ActiveChatData {
|
|
20
|
+
id: string;
|
|
21
|
+
title: string;
|
|
22
|
+
status: string;
|
|
23
|
+
messages: ChatMessage[];
|
|
24
|
+
activeModal: { message: string; buttons: string[] } | null;
|
|
25
|
+
inputContent?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Standardized error reasons across all provider categories */
|
|
29
|
+
export type ProviderErrorReason =
|
|
30
|
+
| 'not_installed' // CLI/ACP binary not found
|
|
31
|
+
| 'auth_failed' // Authentication/API key error
|
|
32
|
+
| 'spawn_error' // Process spawn failure
|
|
33
|
+
| 'init_failed' // Initialization/handshake failure
|
|
34
|
+
| 'crash' // Unexpected process crash
|
|
35
|
+
| 'timeout' // Operation timeout
|
|
36
|
+
| 'cdp_error' // CDP connection failure (IDE)
|
|
37
|
+
| 'disconnected'; // Connection lost
|
|
38
|
+
|
|
39
|
+
/** Common fields shared by all provider categories */
|
|
40
|
+
interface ProviderStateBase {
|
|
16
41
|
/** Provider type (e.g. 'gemini-cli', 'cursor', 'cline') */
|
|
17
42
|
type: string;
|
|
18
43
|
/** Provider Display name */
|
|
19
44
|
name: string;
|
|
20
|
-
/** category */
|
|
21
|
-
category: 'cli' | 'ide' | 'extension' | 'acp';
|
|
22
45
|
/** current status */
|
|
23
|
-
status:
|
|
24
|
-
|
|
25
|
-
/** CLI mode: terminal = PTY stream, chat = parsed conversation */
|
|
26
|
-
mode?: 'terminal' | 'chat';
|
|
27
|
-
|
|
46
|
+
status: ProviderStatus;
|
|
28
47
|
/** chat data */
|
|
29
|
-
activeChat:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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) */
|
|
48
|
+
activeChat: ActiveChatData | null;
|
|
49
|
+
/** Workspace — project path or name (all categories) */
|
|
50
|
+
workspace?: string | null;
|
|
51
|
+
/** Runtime info (real-time detection) */
|
|
50
52
|
currentModel?: string;
|
|
51
53
|
currentPlan?: string;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
/** Error details (when status === 'error') */
|
|
55
|
+
errorMessage?: string;
|
|
56
|
+
errorReason?: ProviderErrorReason;
|
|
54
57
|
/** meta */
|
|
55
58
|
instanceId: string;
|
|
56
59
|
lastUpdated: number;
|
|
57
60
|
settings: Record<string, any>;
|
|
58
|
-
|
|
59
61
|
/** Event queue (cleared after daemon collects) */
|
|
60
62
|
pendingEvents: ProviderEvent[];
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
/** IDE provider state */
|
|
66
|
+
export interface IdeProviderState extends ProviderStateBase {
|
|
67
|
+
category: 'ide';
|
|
68
|
+
cdpConnected: boolean;
|
|
69
|
+
/** IDE child Extension Instance status */
|
|
70
|
+
extensions: ProviderState[];
|
|
71
|
+
currentAutoApprove?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** CLI provider state */
|
|
75
|
+
export interface CliProviderState extends ProviderStateBase {
|
|
76
|
+
category: 'cli';
|
|
77
|
+
/** terminal = PTY stream, chat = parsed conversation */
|
|
78
|
+
mode: 'terminal' | 'chat';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** ACP provider state */
|
|
82
|
+
export interface AcpProviderState extends ProviderStateBase {
|
|
83
|
+
category: 'acp';
|
|
84
|
+
mode: 'chat';
|
|
85
|
+
/** ACP config options (model/mode selection) */
|
|
86
|
+
acpConfigOptions?: AcpConfigOption[];
|
|
87
|
+
/** ACP available modes */
|
|
88
|
+
acpModes?: AcpMode[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Extension provider state */
|
|
92
|
+
export interface ExtensionProviderState extends ProviderStateBase {
|
|
93
|
+
category: 'extension';
|
|
94
|
+
agentStreams?: any[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Discriminated union — switch on `.category` */
|
|
98
|
+
export type ProviderState = IdeProviderState | CliProviderState | AcpProviderState | ExtensionProviderState;
|
|
99
|
+
|
|
63
100
|
export interface ProviderEvent {
|
|
64
101
|
event: string;
|
|
65
102
|
timestamp: number;
|