@adhdev/daemon-core 0.8.30 → 0.8.31
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/agent-stream/manager.d.ts +1 -0
- package/dist/agent-stream/provider-adapter.d.ts +1 -0
- package/dist/agent-stream/types.d.ts +3 -0
- package/dist/boot/daemon-lifecycle.d.ts +2 -1
- package/dist/cdp/manager.d.ts +2 -0
- package/dist/cli-adapter-types.d.ts +34 -5
- package/dist/cli-adapters/provider-cli-adapter.d.ts +4 -158
- package/dist/cli-adapters/provider-cli-config.d.ts +30 -0
- package/dist/cli-adapters/provider-cli-parse.d.ts +42 -0
- package/dist/cli-adapters/provider-cli-runtime.d.ts +29 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +158 -0
- package/dist/commands/handler.d.ts +4 -3
- package/dist/config/config.d.ts +4 -3
- package/dist/index.js +866 -592
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +868 -595
- package/dist/index.mjs.map +1 -1
- package/dist/providers/contracts.d.ts +10 -1
- package/dist/providers/provider-loader.d.ts +3 -0
- package/dist/status/reporter.d.ts +2 -3
- package/dist/status/snapshot.d.ts +2 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +3 -1
- package/src/agent-stream/manager.ts +8 -2
- package/src/agent-stream/poller.ts +19 -5
- package/src/agent-stream/provider-adapter.ts +11 -7
- package/src/agent-stream/types.ts +3 -0
- package/src/boot/daemon-lifecycle.ts +7 -6
- package/src/cdp/initializer.ts +2 -2
- package/src/cdp/manager.ts +5 -0
- package/src/cdp/setup.ts +1 -1
- package/src/cli-adapter-types.ts +37 -5
- package/src/cli-adapters/provider-cli-adapter.ts +212 -795
- package/src/cli-adapters/provider-cli-config.ts +66 -0
- package/src/cli-adapters/provider-cli-parse.ts +202 -0
- package/src/cli-adapters/provider-cli-runtime.ts +142 -0
- package/src/cli-adapters/provider-cli-shared.ts +439 -0
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +1 -1
- package/src/commands/cdp-commands.ts +6 -1
- package/src/commands/chat-commands.ts +45 -29
- package/src/commands/cli-manager.ts +28 -9
- package/src/commands/handler.ts +14 -10
- package/src/commands/router.ts +23 -10
- package/src/commands/stream-commands.ts +11 -5
- package/src/config/config.ts +4 -10
- package/src/daemon/dev-auto-implement.ts +22 -18
- package/src/daemon/dev-cli-debug.ts +59 -16
- package/src/daemon/dev-server.ts +67 -43
- package/src/providers/acp-provider-instance.ts +1 -1
- package/src/providers/cli-provider-instance.ts +2 -2
- package/src/providers/contracts.ts +12 -1
- package/src/providers/extension-provider-instance.ts +1 -1
- package/src/providers/ide-provider-instance.ts +39 -18
- package/src/providers/provider-loader.ts +85 -54
- package/src/providers/version-archive.ts +23 -5
- package/src/status/reporter.ts +18 -14
- package/src/status/snapshot.ts +5 -4
package/src/daemon/dev-server.ts
CHANGED
|
@@ -20,7 +20,7 @@ import * as fs from 'fs';
|
|
|
20
20
|
import * as path from 'path';
|
|
21
21
|
import * as os from 'os';
|
|
22
22
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
23
|
-
import type { ProviderCategory } from '../providers/contracts.js';
|
|
23
|
+
import type { ProviderCategory, ProviderModule, ProviderScripts, ProviderSettingDef } from '../providers/contracts.js';
|
|
24
24
|
import type { ChildProcess } from 'child_process';
|
|
25
25
|
import type { DaemonCdpManager } from '../cdp/manager.js';
|
|
26
26
|
import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
|
|
@@ -34,6 +34,64 @@ import { handleAutoImplement, handleAutoImplCancel, handleAutoImplSSE } from './
|
|
|
34
34
|
|
|
35
35
|
export const DEV_SERVER_PORT = 19280;
|
|
36
36
|
|
|
37
|
+
interface ProviderListEntry {
|
|
38
|
+
type: string;
|
|
39
|
+
name: string;
|
|
40
|
+
category: ProviderCategory;
|
|
41
|
+
icon: string | null;
|
|
42
|
+
displayName: string;
|
|
43
|
+
scripts?: string[];
|
|
44
|
+
inputMethod?: ProviderModule['inputMethod'] | null;
|
|
45
|
+
inputSelector?: string | null;
|
|
46
|
+
extensionId?: string | null;
|
|
47
|
+
cdpPorts?: [number, number] | [];
|
|
48
|
+
spawn?: ProviderModule['spawn'] | null;
|
|
49
|
+
auth?: ProviderModule['auth'] | null;
|
|
50
|
+
install?: string | null;
|
|
51
|
+
hasSettings?: boolean;
|
|
52
|
+
settingsCount?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getScriptNames(scripts?: ProviderScripts): string[] {
|
|
56
|
+
if (!scripts) return [];
|
|
57
|
+
return Object.entries(scripts)
|
|
58
|
+
.filter(([, value]) => typeof value === 'function')
|
|
59
|
+
.map(([name]) => name);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function toProviderListEntry(provider: ProviderModule): ProviderListEntry {
|
|
63
|
+
const base: ProviderListEntry = {
|
|
64
|
+
type: provider.type,
|
|
65
|
+
name: provider.name,
|
|
66
|
+
category: provider.category,
|
|
67
|
+
icon: provider.icon || null,
|
|
68
|
+
displayName: provider.displayName || provider.name,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (provider.category === 'ide' || provider.category === 'extension') {
|
|
72
|
+
base.scripts = getScriptNames(provider.scripts);
|
|
73
|
+
base.inputMethod = provider.inputMethod || null;
|
|
74
|
+
base.inputSelector = provider.inputSelector || null;
|
|
75
|
+
base.extensionId = provider.extensionId || null;
|
|
76
|
+
base.cdpPorts = provider.cdpPorts || [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (provider.category === 'acp') {
|
|
80
|
+
base.spawn = provider.spawn || null;
|
|
81
|
+
base.auth = provider.auth || null;
|
|
82
|
+
base.install = provider.install || null;
|
|
83
|
+
base.hasSettings = !!provider.settings;
|
|
84
|
+
base.settingsCount = provider.settings ? Object.keys(provider.settings).length : 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (provider.category === 'cli') {
|
|
88
|
+
base.spawn = provider.spawn || null;
|
|
89
|
+
base.install = provider.install || null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return base;
|
|
93
|
+
}
|
|
94
|
+
|
|
37
95
|
export class DevServer implements DevServerContext {
|
|
38
96
|
private server: http.Server | null = null;
|
|
39
97
|
public providerLoader: ProviderLoader;
|
|
@@ -212,41 +270,7 @@ export class DevServer implements DevServerContext {
|
|
|
212
270
|
// ─── Handlers ───
|
|
213
271
|
|
|
214
272
|
private async handleListProviders(_req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
215
|
-
const providers = this.providerLoader.getAll().map(
|
|
216
|
-
const base: any = {
|
|
217
|
-
type: p.type,
|
|
218
|
-
name: p.name,
|
|
219
|
-
category: p.category,
|
|
220
|
-
icon: (p as any).icon || null,
|
|
221
|
-
displayName: (p as any).displayName || p.name,
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
// IDE/Extension specific
|
|
225
|
-
if (p.category === 'ide' || p.category === 'extension') {
|
|
226
|
-
base.scripts = p.scripts ? Object.keys(p.scripts).filter(k => typeof (p.scripts as any)[k] === 'function') : [];
|
|
227
|
-
base.inputMethod = p.inputMethod || null;
|
|
228
|
-
base.inputSelector = (p as any).inputSelector || null;
|
|
229
|
-
base.extensionId = p.extensionId || null;
|
|
230
|
-
base.cdpPorts = (p as any).cdpPorts || [];
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// ACP specific
|
|
234
|
-
if (p.category === 'acp') {
|
|
235
|
-
base.spawn = (p as any).spawn || null;
|
|
236
|
-
base.auth = (p as any).auth || null;
|
|
237
|
-
base.install = (p as any).install || null;
|
|
238
|
-
base.hasSettings = !!(p as any).settings;
|
|
239
|
-
base.settingsCount = (p as any).settings ? Object.keys((p as any).settings).length : 0;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// CLI specific
|
|
243
|
-
if (p.category === 'cli') {
|
|
244
|
-
base.spawn = (p as any).spawn || null;
|
|
245
|
-
base.install = (p as any).install || null;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return base;
|
|
249
|
-
});
|
|
273
|
+
const providers = this.providerLoader.getAll().map(toProviderListEntry);
|
|
250
274
|
this.json(res, 200, { providers, count: providers.length });
|
|
251
275
|
}
|
|
252
276
|
|
|
@@ -273,7 +297,7 @@ export class DevServer implements DevServerContext {
|
|
|
273
297
|
return;
|
|
274
298
|
}
|
|
275
299
|
|
|
276
|
-
const spawn =
|
|
300
|
+
const spawn = provider.spawn;
|
|
277
301
|
if (!spawn) {
|
|
278
302
|
this.json(res, 400, { error: `Provider ${type} has no spawn config` });
|
|
279
303
|
return;
|
|
@@ -330,7 +354,7 @@ export class DevServer implements DevServerContext {
|
|
|
330
354
|
return;
|
|
331
355
|
}
|
|
332
356
|
|
|
333
|
-
const fn =
|
|
357
|
+
const fn = provider.scripts?.[scriptName];
|
|
334
358
|
if (typeof fn !== 'function') {
|
|
335
359
|
this.json(res, 400, { error: `Script '${scriptName}' not found in provider '${type}'`, available: provider.scripts ? Object.keys(provider.scripts) : [] });
|
|
336
360
|
return;
|
|
@@ -446,7 +470,7 @@ export class DevServer implements DevServerContext {
|
|
|
446
470
|
}));
|
|
447
471
|
for (const cdp of this.cdpManagers.values()) {
|
|
448
472
|
if (!cdp.isConnected) {
|
|
449
|
-
(
|
|
473
|
+
cdp.clearTargetId();
|
|
450
474
|
}
|
|
451
475
|
}
|
|
452
476
|
this.json(res, 200, { reloaded: true, providers });
|
|
@@ -569,7 +593,7 @@ export class DevServer implements DevServerContext {
|
|
|
569
593
|
this.sendSSE({ type: 'watch_error', error: `Provider '${this.watchScriptPath}' not found` });
|
|
570
594
|
return;
|
|
571
595
|
}
|
|
572
|
-
const fn =
|
|
596
|
+
const fn = provider.scripts?.[this.watchScriptName!];
|
|
573
597
|
if (typeof fn !== 'function') {
|
|
574
598
|
this.sendSSE({ type: 'watch_error', error: `Script '${this.watchScriptName}' not found` });
|
|
575
599
|
return;
|
|
@@ -769,7 +793,7 @@ export class DevServer implements DevServerContext {
|
|
|
769
793
|
// Settings validation
|
|
770
794
|
if (config.settings) {
|
|
771
795
|
for (const [key, val] of Object.entries(config.settings)) {
|
|
772
|
-
const s = val as
|
|
796
|
+
const s = val as Partial<ProviderSettingDef>;
|
|
773
797
|
if (!s.type) errors.push(`settings.${key}: missing type`);
|
|
774
798
|
else if (!['boolean', 'number', 'string', 'select'].includes(s.type))
|
|
775
799
|
errors.push(`settings.${key}: invalid type '${s.type}'`);
|
|
@@ -784,7 +808,7 @@ export class DevServer implements DevServerContext {
|
|
|
784
808
|
if (config.cdpPorts && Array.isArray(config.cdpPorts)) {
|
|
785
809
|
const allProviders = this.providerLoader.getAll();
|
|
786
810
|
for (const port of config.cdpPorts) {
|
|
787
|
-
const conflict = allProviders.find(p => p.type !== type &&
|
|
811
|
+
const conflict = allProviders.find(p => p.type !== type && p.cdpPorts?.includes(port));
|
|
788
812
|
if (conflict) warnings.push(`CDP port ${port} conflicts with provider '${conflict.type}'`);
|
|
789
813
|
}
|
|
790
814
|
}
|
|
@@ -801,7 +825,7 @@ export class DevServer implements DevServerContext {
|
|
|
801
825
|
if (!message) { this.json(res, 400, { error: 'message required' }); return; }
|
|
802
826
|
const provider = this.providerLoader.getMeta(type);
|
|
803
827
|
if (!provider) { this.json(res, 404, { error: `Provider not found: ${type}` }); return; }
|
|
804
|
-
const spawn =
|
|
828
|
+
const spawn = provider.spawn;
|
|
805
829
|
if (!spawn) { this.json(res, 400, { error: `Provider ${type} has no spawn config` }); return; }
|
|
806
830
|
|
|
807
831
|
const { spawn: spawnFn } = await import('child_process');
|
|
@@ -232,7 +232,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
232
232
|
...(this.currentModel ? { model: this.currentModel } : {}),
|
|
233
233
|
...(this.currentMode ? { mode: this.currentMode } : {}),
|
|
234
234
|
},
|
|
235
|
-
providerControls: this.provider.controls
|
|
235
|
+
providerControls: this.provider.controls,
|
|
236
236
|
};
|
|
237
237
|
}
|
|
238
238
|
|
|
@@ -101,7 +101,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
101
101
|
this.providerSessionId = options?.providerSessionId;
|
|
102
102
|
this.launchMode = options?.launchMode || 'new';
|
|
103
103
|
this.onProviderSessionResolved = options?.onProviderSessionResolved;
|
|
104
|
-
this.adapter = new ProviderCliAdapter(provider as
|
|
104
|
+
this.adapter = new ProviderCliAdapter(provider as CliProviderModule, workingDir, cliArgs, transportFactory);
|
|
105
105
|
this.monitor = new StatusMonitor();
|
|
106
106
|
this.historyWriter = new ChatHistoryWriter();
|
|
107
107
|
}
|
|
@@ -328,7 +328,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
328
328
|
} : undefined,
|
|
329
329
|
resume: this.provider.resume,
|
|
330
330
|
controlValues: this.controlValues,
|
|
331
|
-
providerControls: this.provider.controls
|
|
331
|
+
providerControls: this.provider.controls,
|
|
332
332
|
};
|
|
333
333
|
}
|
|
334
334
|
|
|
@@ -304,6 +304,13 @@ export interface CdpTargetFilter {
|
|
|
304
304
|
titleExcludes?: string;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
export type ProviderVersionCommand = string | Partial<Record<string, string>>;
|
|
308
|
+
|
|
309
|
+
export interface ProviderCompatibilityEntry {
|
|
310
|
+
ideVersion: string;
|
|
311
|
+
scriptDir: string;
|
|
312
|
+
}
|
|
313
|
+
|
|
307
314
|
export interface ProviderModule {
|
|
308
315
|
/** Unique identifier (e.g. 'cline', 'cursor', 'gemini-cli') */
|
|
309
316
|
type: string;
|
|
@@ -328,7 +335,7 @@ export interface ProviderModule {
|
|
|
328
335
|
/** Install instructions (shown when command is missing) */
|
|
329
336
|
install?: string;
|
|
330
337
|
/** Custom version detection command (e.g. 'cursor --version', 'claude -v') */
|
|
331
|
-
versionCommand?:
|
|
338
|
+
versionCommand?: ProviderVersionCommand;
|
|
332
339
|
/** Versions tested by provider maintainer (informational) */
|
|
333
340
|
testedVersions?: string[];
|
|
334
341
|
/** Per-OS process names — used by launch.ts to detect/kill IDE processes */
|
|
@@ -371,6 +378,9 @@ export interface ProviderModule {
|
|
|
371
378
|
// ─── Extension category only ───
|
|
372
379
|
extensionId?: string;
|
|
373
380
|
extensionIdPattern?: RegExp;
|
|
381
|
+
extensionIdPattern_flags?: string;
|
|
382
|
+
compatibility?: ProviderCompatibilityEntry[];
|
|
383
|
+
defaultScriptDir?: string;
|
|
374
384
|
|
|
375
385
|
// ─── CLI category only ───
|
|
376
386
|
binary?: string;
|
|
@@ -380,6 +390,7 @@ export interface ProviderModule {
|
|
|
380
390
|
shell?: boolean;
|
|
381
391
|
env?: Record<string, string>;
|
|
382
392
|
};
|
|
393
|
+
approvalKeys?: Record<number, string>;
|
|
383
394
|
patterns?: {
|
|
384
395
|
prompt?: RegExp[];
|
|
385
396
|
generating?: RegExp[];
|
|
@@ -92,7 +92,7 @@ export class ExtensionProviderInstance implements ProviderInstance {
|
|
|
92
92
|
currentModel: this.currentModel || undefined,
|
|
93
93
|
currentPlan: this.currentMode || undefined,
|
|
94
94
|
controlValues: this.controlValues,
|
|
95
|
-
providerControls: this.provider.controls
|
|
95
|
+
providerControls: this.provider.controls,
|
|
96
96
|
agentStreams: this.agentStreams,
|
|
97
97
|
instanceId: this.instanceId,
|
|
98
98
|
lastUpdated: Date.now(),
|
|
@@ -21,6 +21,24 @@ import { extractProviderControlValues, normalizeProviderEffects } from './contro
|
|
|
21
21
|
import type { ChatMessage } from '../types.js';
|
|
22
22
|
import { formatAutoApprovalMessage, pickApprovalButton } from './approval-utils.js';
|
|
23
23
|
|
|
24
|
+
type ReadChatModal = {
|
|
25
|
+
message?: string;
|
|
26
|
+
buttons?: string[];
|
|
27
|
+
width?: number;
|
|
28
|
+
height?: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type ReadChatMessage = ChatMessage & { content: string };
|
|
32
|
+
|
|
33
|
+
type ReadChatPayload = {
|
|
34
|
+
activeModal?: ReadChatModal;
|
|
35
|
+
messages?: ReadChatMessage[];
|
|
36
|
+
controlValues?: Record<string, string | number | boolean>;
|
|
37
|
+
status?: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
};
|
|
41
|
+
|
|
24
42
|
export class IdeProviderInstance implements ProviderInstance {
|
|
25
43
|
readonly type: string;
|
|
26
44
|
readonly category = 'ide' as const;
|
|
@@ -138,7 +156,7 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
138
156
|
currentPlan: this.cachedChat?.mode || undefined,
|
|
139
157
|
currentAutoApprove: this.cachedChat?.autoApprove || undefined,
|
|
140
158
|
controlValues: this.cachedChat?.controlValues || undefined,
|
|
141
|
-
providerControls: this.provider.controls
|
|
159
|
+
providerControls: this.provider.controls,
|
|
142
160
|
instanceId: this.instanceId,
|
|
143
161
|
lastUpdated: Date.now(),
|
|
144
162
|
settings: this.settings,
|
|
@@ -261,14 +279,14 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
261
279
|
if (!cdp?.isConnected) return;
|
|
262
280
|
|
|
263
281
|
try {
|
|
264
|
-
let raw:
|
|
282
|
+
let raw: unknown = null;
|
|
265
283
|
|
|
266
284
|
// path 1: webview iframe internal (Kiro, PearAI etc)
|
|
267
|
-
const webviewFn =
|
|
285
|
+
const webviewFn = this.provider.scripts?.webviewReadChat;
|
|
268
286
|
if (typeof webviewFn === 'function' && cdp.evaluateInWebviewFrame) {
|
|
269
287
|
const webviewScript = webviewFn();
|
|
270
288
|
if (webviewScript) {
|
|
271
|
-
const matchText =
|
|
289
|
+
const matchText = this.provider.webviewMatchText;
|
|
272
290
|
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
273
291
|
const webviewRaw = await cdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
274
292
|
if (webviewRaw) {
|
|
@@ -281,16 +299,17 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
281
299
|
if (!raw) {
|
|
282
300
|
const readChatScript = this.getReadChatScript();
|
|
283
301
|
if (!readChatScript) return;
|
|
284
|
-
raw = await cdp.evaluate(readChatScript, 30000)
|
|
302
|
+
raw = await cdp.evaluate(readChatScript, 30000);
|
|
285
303
|
if (typeof raw === 'string') {
|
|
286
304
|
try { raw = JSON.parse(raw); } catch { return; }
|
|
287
305
|
}
|
|
288
306
|
}
|
|
289
307
|
|
|
290
308
|
if (!raw || typeof raw !== 'object') return;
|
|
309
|
+
const chat = raw as ReadChatPayload;
|
|
291
310
|
|
|
292
311
|
// Modal filter
|
|
293
|
-
let { activeModal } =
|
|
312
|
+
let { activeModal } = chat;
|
|
294
313
|
if (activeModal) {
|
|
295
314
|
const w = activeModal.width ?? Infinity;
|
|
296
315
|
const h = activeModal.height ?? Infinity;
|
|
@@ -312,33 +331,35 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
312
331
|
if (pm.receivedAt) prevByHash.set(h, pm.receivedAt);
|
|
313
332
|
}
|
|
314
333
|
const now = Date.now();
|
|
315
|
-
|
|
334
|
+
const messages = chat.messages || [];
|
|
335
|
+
for (const msg of messages) {
|
|
316
336
|
const h = `${msg.role}:${(msg.content || '').slice(0, 100)}`;
|
|
317
337
|
msg.receivedAt = prevByHash.get(h) || now;
|
|
318
338
|
}
|
|
319
339
|
|
|
320
340
|
// Filter messages by provider settings (showThinking, showToolCalls, showTerminal)
|
|
321
|
-
if (
|
|
341
|
+
if (messages.length > 0) {
|
|
322
342
|
const hiddenKinds = new Set<string>();
|
|
323
343
|
if (this.settings.showThinking === false) hiddenKinds.add('thought');
|
|
324
344
|
if (this.settings.showToolCalls === false) hiddenKinds.add('tool');
|
|
325
345
|
if (this.settings.showTerminal === false) hiddenKinds.add('terminal');
|
|
326
346
|
if (hiddenKinds.size > 0) {
|
|
327
|
-
|
|
347
|
+
chat.messages = messages.filter((m) => !hiddenKinds.has(m.kind || ''));
|
|
328
348
|
}
|
|
329
349
|
}
|
|
330
350
|
|
|
331
|
-
const controlValues = extractProviderControlValues(this.provider.controls,
|
|
332
|
-
if (controlValues)
|
|
351
|
+
const controlValues = extractProviderControlValues(this.provider.controls, chat);
|
|
352
|
+
if (controlValues) chat.controlValues = controlValues;
|
|
333
353
|
|
|
334
|
-
this.cachedChat = { ...
|
|
335
|
-
this.detectAgentTransitions(
|
|
354
|
+
this.cachedChat = { ...chat, activeModal };
|
|
355
|
+
this.detectAgentTransitions(chat, now);
|
|
336
356
|
|
|
337
357
|
// Save history (new messageonly append)
|
|
338
358
|
// Exclude last incomplete assistant message during generating status
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
359
|
+
const persistedMessages = chat.messages || messages;
|
|
360
|
+
if (persistedMessages.length > 0) {
|
|
361
|
+
let toSave = persistedMessages;
|
|
362
|
+
if (chat.status === 'generating' || chat.status === 'long_generating') {
|
|
342
363
|
// Find and exclude last assistant message
|
|
343
364
|
const lastIdx = toSave.length - 1;
|
|
344
365
|
if (lastIdx >= 0 && toSave[lastIdx].role === 'assistant') {
|
|
@@ -349,7 +370,7 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
349
370
|
this.historyWriter.appendNewMessages(
|
|
350
371
|
this.type,
|
|
351
372
|
toSave,
|
|
352
|
-
|
|
373
|
+
chat.title,
|
|
353
374
|
this.instanceId,
|
|
354
375
|
);
|
|
355
376
|
}
|
|
@@ -368,7 +389,7 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
368
389
|
private getReadChatScript(): string | null {
|
|
369
390
|
const scripts = this.provider.scripts;
|
|
370
391
|
if (!scripts?.readChat) return null;
|
|
371
|
-
return
|
|
392
|
+
return scripts.readChat({});
|
|
372
393
|
}
|
|
373
394
|
|
|
374
395
|
// ─── status transition detect ─────────────────────────────
|