@adhdev/daemon-core 0.9.39 → 0.9.41
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/commands/chat-commands.d.ts +2 -0
- package/dist/index.js +147 -68
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +147 -68
- package/dist/index.mjs.map +1 -1
- package/dist/providers/acp-provider-instance.d.ts +2 -1
- package/dist/providers/cli-provider-instance.d.ts +2 -1
- package/dist/providers/extension-provider-instance.d.ts +2 -1
- package/dist/providers/ide-provider-instance.d.ts +2 -1
- package/dist/providers/provider-instance-manager.d.ts +4 -1
- package/dist/providers/provider-instance.d.ts +11 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +0 -8
- package/src/cli-adapters/provider-cli-parse.ts +25 -15
- package/src/commands/chat-commands.ts +65 -56
- package/src/providers/acp-provider-instance.ts +14 -1
- package/src/providers/cli-provider-instance.ts +14 -1
- package/src/providers/extension-provider-instance.ts +11 -1
- package/src/providers/ide-provider-instance.ts +25 -1
- package/src/providers/provider-instance-manager.ts +27 -1
- package/src/providers/provider-instance.ts +13 -0
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* 5. dispose() → kill process
|
|
16
16
|
*/
|
|
17
17
|
import type { ProviderModule, ContentBlock, InputEnvelope } from './contracts.js';
|
|
18
|
-
import type { ProviderInstance, AcpProviderState, InstanceContext } from './provider-instance.js';
|
|
18
|
+
import type { ProviderInstance, AcpProviderState, InstanceContext, SessionModalState } from './provider-instance.js';
|
|
19
19
|
export declare function buildAcpPromptParts(input: InputEnvelope, agentCapabilities?: Record<string, any>): ContentBlock[];
|
|
20
20
|
export declare class AcpProviderInstance implements ProviderInstance {
|
|
21
21
|
private cliArgs;
|
|
@@ -59,6 +59,7 @@ export declare class AcpProviderInstance implements ProviderInstance {
|
|
|
59
59
|
constructor(provider: ProviderModule, workingDir: string, cliArgs?: string[]);
|
|
60
60
|
init(context: InstanceContext): Promise<void>;
|
|
61
61
|
onTick(): Promise<void>;
|
|
62
|
+
getSessionModalState(): SessionModalState;
|
|
62
63
|
getState(): AcpProviderState;
|
|
63
64
|
onEvent(event: string, data?: any): void;
|
|
64
65
|
getInstanceId(): string;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* collectCliData() + status transition logic from daemon-status.ts moved here.
|
|
6
6
|
*/
|
|
7
7
|
import { type ProviderModule } from './contracts.js';
|
|
8
|
-
import type { ProviderInstance, ProviderState, InstanceContext, HotChatSessionState } from './provider-instance.js';
|
|
8
|
+
import type { ProviderInstance, ProviderState, InstanceContext, HotChatSessionState, SessionModalState } from './provider-instance.js';
|
|
9
9
|
import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
|
|
10
10
|
import type { PtyTransportFactory } from '../cli-adapters/pty-transport.js';
|
|
11
11
|
type PersistableCliHistoryMessage = {
|
|
@@ -91,6 +91,7 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
91
91
|
setPresentationMode(mode: 'terminal' | 'chat'): void;
|
|
92
92
|
getPresentationMode(): 'terminal' | 'chat';
|
|
93
93
|
getHotChatSessionState(): HotChatSessionState;
|
|
94
|
+
getSessionModalState(): SessionModalState;
|
|
94
95
|
updateSettings(newSettings: Record<string, any>): void;
|
|
95
96
|
onEvent(event: string, data?: any): void;
|
|
96
97
|
dispose(): void;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* CDP webview discovery + agent stream collection moved here.
|
|
6
6
|
*/
|
|
7
7
|
import { type ProviderModule } from './contracts.js';
|
|
8
|
-
import type { ProviderInstance, ProviderState, InstanceContext } from './provider-instance.js';
|
|
8
|
+
import type { ProviderInstance, ProviderState, InstanceContext, SessionModalState } from './provider-instance.js';
|
|
9
9
|
export declare class ExtensionProviderInstance implements ProviderInstance {
|
|
10
10
|
readonly type: string;
|
|
11
11
|
readonly category: "extension";
|
|
@@ -37,6 +37,7 @@ export declare class ExtensionProviderInstance implements ProviderInstance {
|
|
|
37
37
|
init(context: InstanceContext): Promise<void>;
|
|
38
38
|
onTick(): Promise<void>;
|
|
39
39
|
getState(): ProviderState;
|
|
40
|
+
getSessionModalState(sessionId?: string): SessionModalState | null;
|
|
40
41
|
onEvent(event: string, data?: any): void;
|
|
41
42
|
dispose(): void;
|
|
42
43
|
updateSettings(newSettings: Record<string, any>): void;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Daemon collects all via a single IDE Instance.getState() call.
|
|
10
10
|
*/
|
|
11
11
|
import { type ProviderModule } from './contracts.js';
|
|
12
|
-
import type { ProviderInstance, ProviderState, InstanceContext } from './provider-instance.js';
|
|
12
|
+
import type { ProviderInstance, ProviderState, InstanceContext, SessionModalState } from './provider-instance.js';
|
|
13
13
|
import { ExtensionProviderInstance } from './extension-provider-instance.js';
|
|
14
14
|
export declare class IdeProviderInstance implements ProviderInstance {
|
|
15
15
|
readonly type: string;
|
|
@@ -37,6 +37,7 @@ export declare class IdeProviderInstance implements ProviderInstance {
|
|
|
37
37
|
init(context: InstanceContext): Promise<void>;
|
|
38
38
|
onTick(): Promise<void>;
|
|
39
39
|
getState(): ProviderState;
|
|
40
|
+
getSessionModalState(sessionId?: string): SessionModalState | null;
|
|
40
41
|
onEvent(event: string, data?: any): void;
|
|
41
42
|
dispose(): void;
|
|
42
43
|
updateSettings(newSettings: Record<string, any>): void;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 3. Collect overall state
|
|
8
8
|
* 4. Event collection and propagation
|
|
9
9
|
*/
|
|
10
|
-
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, HotChatSessionState } from './provider-instance.js';
|
|
10
|
+
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, HotChatSessionState, SessionModalState } from './provider-instance.js';
|
|
11
11
|
export declare class ProviderInstanceManager {
|
|
12
12
|
private instances;
|
|
13
13
|
private tickTimer;
|
|
@@ -46,6 +46,9 @@ export declare class ProviderInstanceManager {
|
|
|
46
46
|
*/
|
|
47
47
|
collectAllStates(): ProviderState[];
|
|
48
48
|
collectHotChatSessionStates(): HotChatSessionState[];
|
|
49
|
+
getSessionModalState(sessionId: string, options?: {
|
|
50
|
+
instanceKey?: string | null;
|
|
51
|
+
}): SessionModalState | null;
|
|
49
52
|
/**
|
|
50
53
|
* Per-category status collect
|
|
51
54
|
*/
|
|
@@ -123,6 +123,12 @@ export interface HotChatSessionState {
|
|
|
123
123
|
runtimeRestoredFromStorage?: unknown;
|
|
124
124
|
runtimeRecoveryState?: unknown;
|
|
125
125
|
}
|
|
126
|
+
export interface SessionModalState {
|
|
127
|
+
id: string;
|
|
128
|
+
status?: unknown;
|
|
129
|
+
title?: unknown;
|
|
130
|
+
activeModal?: unknown;
|
|
131
|
+
}
|
|
126
132
|
export interface InstanceContext {
|
|
127
133
|
/** CDP connection (IDE/Extension) */
|
|
128
134
|
cdp?: {
|
|
@@ -159,6 +165,11 @@ export interface ProviderInstance {
|
|
|
159
165
|
* parsing here; callers use this on P2P hot flush paths.
|
|
160
166
|
*/
|
|
161
167
|
getHotChatSessionState?(): HotChatSessionState | HotChatSessionState[] | null;
|
|
168
|
+
/**
|
|
169
|
+
* Return the cheap modal metadata for a single session subscription. This is
|
|
170
|
+
* used on P2P topic flushes and must not invoke rich chat/transcript parsing.
|
|
171
|
+
*/
|
|
172
|
+
getSessionModalState?(sessionId?: string): SessionModalState | null;
|
|
162
173
|
/** Receive event (external → Instance) */
|
|
163
174
|
onEvent(event: string, data?: any): void;
|
|
164
175
|
/** Update settings at runtime (called when user changes settings from dashboard) */
|
package/package.json
CHANGED
|
@@ -273,12 +273,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
273
273
|
currentTurnScope: TurnParseScope | null;
|
|
274
274
|
recentOutputBuffer: string;
|
|
275
275
|
accumulatedBuffer: string;
|
|
276
|
-
accumulatedRawBuffer: string;
|
|
277
276
|
screenText: string;
|
|
278
277
|
currentStatus: CliSessionStatus['status'];
|
|
279
278
|
activeModal: { message: string; buttons: string[] } | null;
|
|
280
279
|
cliName: string;
|
|
281
|
-
lastOutputAt: number;
|
|
282
280
|
result: any;
|
|
283
281
|
} | null = null;
|
|
284
282
|
private lastStatusHotPathParseAt = Number.NEGATIVE_INFINITY;
|
|
@@ -352,12 +350,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
352
350
|
&& cached.currentTurnScope === this.currentTurnScope
|
|
353
351
|
&& cached.recentOutputBuffer === this.recentOutputBuffer
|
|
354
352
|
&& cached.accumulatedBuffer === this.accumulatedBuffer
|
|
355
|
-
&& cached.accumulatedRawBuffer === this.accumulatedRawBuffer
|
|
356
353
|
&& cached.screenText === this.lastScreenText
|
|
357
354
|
&& cached.currentStatus === this.currentStatus
|
|
358
355
|
&& cached.activeModal === this.activeModal
|
|
359
356
|
&& cached.cliName === this.cliName
|
|
360
|
-
&& cached.lastOutputAt === this.lastOutputAt
|
|
361
357
|
) {
|
|
362
358
|
return cached.result;
|
|
363
359
|
}
|
|
@@ -1835,12 +1831,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1835
1831
|
&& cached.currentTurnScope === this.currentTurnScope
|
|
1836
1832
|
&& cached.recentOutputBuffer === this.recentOutputBuffer
|
|
1837
1833
|
&& cached.accumulatedBuffer === this.accumulatedBuffer
|
|
1838
|
-
&& cached.accumulatedRawBuffer === this.accumulatedRawBuffer
|
|
1839
1834
|
&& cached.screenText === screenText
|
|
1840
1835
|
&& cached.currentStatus === this.currentStatus
|
|
1841
1836
|
&& cached.activeModal === this.activeModal
|
|
1842
1837
|
&& cached.cliName === this.cliName
|
|
1843
|
-
&& cached.lastOutputAt === this.lastOutputAt
|
|
1844
1838
|
) {
|
|
1845
1839
|
return cached.result;
|
|
1846
1840
|
}
|
|
@@ -2002,12 +1996,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
2002
1996
|
currentTurnScope: this.currentTurnScope,
|
|
2003
1997
|
recentOutputBuffer: this.recentOutputBuffer,
|
|
2004
1998
|
accumulatedBuffer: this.accumulatedBuffer,
|
|
2005
|
-
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
2006
1999
|
screenText,
|
|
2007
2000
|
currentStatus: this.currentStatus,
|
|
2008
2001
|
activeModal: this.activeModal,
|
|
2009
2002
|
cliName: this.cliName,
|
|
2010
|
-
lastOutputAt: this.lastOutputAt,
|
|
2011
2003
|
result,
|
|
2012
2004
|
};
|
|
2013
2005
|
return result;
|
|
@@ -173,10 +173,12 @@ export function hydrateCliParsedMessages(
|
|
|
173
173
|
});
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
function chooseMoreComparableCliMessage(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
function chooseMoreComparableCliMessage(
|
|
177
|
+
left: CliChatMessage,
|
|
178
|
+
right: CliChatMessage,
|
|
179
|
+
leftComparable = normalizeComparableMessageContent(left.content || ''),
|
|
180
|
+
rightComparable = normalizeComparableMessageContent(right.content || ''),
|
|
181
|
+
): CliChatMessage {
|
|
180
182
|
if (leftComparable && leftComparable === rightComparable) {
|
|
181
183
|
const leftNewlines = String(left.content || '').split(/\r\n|\n|\r/g).length - 1;
|
|
182
184
|
const rightNewlines = String(right.content || '').split(/\r\n|\n|\r/g).length - 1;
|
|
@@ -187,35 +189,43 @@ function chooseMoreComparableCliMessage(left: CliChatMessage, right: CliChatMess
|
|
|
187
189
|
}
|
|
188
190
|
|
|
189
191
|
function dedupeConsecutiveComparableCliMessages(messages: CliChatMessage[]): CliChatMessage[] {
|
|
190
|
-
const deduped: CliChatMessage
|
|
192
|
+
const deduped: Array<{ message: CliChatMessage; comparable: string }> = [];
|
|
191
193
|
|
|
192
194
|
for (const message of messages) {
|
|
193
195
|
const current = {
|
|
194
196
|
...message,
|
|
195
197
|
content: typeof message.content === 'string' ? message.content : String(message.content || ''),
|
|
196
198
|
} as CliChatMessage;
|
|
199
|
+
const currentComparable = normalizeComparableMessageContent(current.content || '');
|
|
197
200
|
const previous = deduped[deduped.length - 1];
|
|
198
201
|
if (!previous) {
|
|
199
|
-
deduped.push(current);
|
|
202
|
+
deduped.push({ message: current, comparable: currentComparable });
|
|
200
203
|
continue;
|
|
201
204
|
}
|
|
202
205
|
|
|
203
|
-
const
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
const sameSender = (previous.senderName || '') === (current.senderName || '');
|
|
208
|
-
const comparableMatch = previousComparable && previousComparable === currentComparable;
|
|
206
|
+
const sameRole = previous.message.role === current.role;
|
|
207
|
+
const sameKind = (previous.message.kind || 'standard') === (current.kind || 'standard');
|
|
208
|
+
const sameSender = (previous.message.senderName || '') === (current.senderName || '');
|
|
209
|
+
const comparableMatch = previous.comparable && previous.comparable === currentComparable;
|
|
209
210
|
|
|
210
211
|
if (sameRole && sameKind && sameSender && comparableMatch) {
|
|
211
|
-
|
|
212
|
+
const selected = chooseMoreComparableCliMessage(
|
|
213
|
+
previous.message,
|
|
214
|
+
current,
|
|
215
|
+
previous.comparable,
|
|
216
|
+
currentComparable,
|
|
217
|
+
);
|
|
218
|
+
deduped[deduped.length - 1] = {
|
|
219
|
+
message: selected,
|
|
220
|
+
comparable: selected === current ? currentComparable : previous.comparable,
|
|
221
|
+
};
|
|
212
222
|
continue;
|
|
213
223
|
}
|
|
214
224
|
|
|
215
|
-
deduped.push(current);
|
|
225
|
+
deduped.push({ message: current, comparable: currentComparable });
|
|
216
226
|
}
|
|
217
227
|
|
|
218
|
-
return deduped;
|
|
228
|
+
return deduped.map((entry) => entry.message);
|
|
219
229
|
}
|
|
220
230
|
|
|
221
231
|
export function normalizeCliParsedMessages(
|
|
@@ -178,85 +178,90 @@ function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[]
|
|
|
178
178
|
return normalizeChatMessages(messages);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function shouldCollapseReadChatReplayDuplicate(message: ChatMessage | null | undefined): boolean {
|
|
191
|
-
if (!message) return false;
|
|
192
|
-
const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
|
|
193
|
-
return role === 'assistant' || role === 'system';
|
|
181
|
+
interface ReadChatReplayCollapseInfo {
|
|
182
|
+
role: string;
|
|
183
|
+
kind: string;
|
|
184
|
+
senderName: string;
|
|
185
|
+
content: string;
|
|
186
|
+
signature: string;
|
|
187
|
+
collapsible: boolean;
|
|
194
188
|
}
|
|
195
189
|
|
|
196
|
-
function
|
|
197
|
-
return flattenContent(
|
|
190
|
+
function normalizeReadChatReplayTextContent(content: ChatMessage['content'] | undefined): string {
|
|
191
|
+
return flattenContent(content || '').replace(/\s+/g, ' ').trim();
|
|
198
192
|
}
|
|
199
193
|
|
|
200
|
-
function
|
|
201
|
-
if (!message) return
|
|
194
|
+
function getReadChatReplayCollapseInfo(message: ChatMessage | null | undefined): ReadChatReplayCollapseInfo | null {
|
|
195
|
+
if (!message) return null;
|
|
202
196
|
const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
|
|
203
197
|
const kind = typeof message.kind === 'string' ? message.kind.trim().toLowerCase() : 'standard';
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
198
|
+
const senderName = typeof message.senderName === 'string' ? message.senderName.trim().toLowerCase() : '';
|
|
199
|
+
const collapsible = role === 'assistant' || role === 'system';
|
|
200
|
+
if (!collapsible) return { role, kind, senderName, content: '', signature: '', collapsible };
|
|
201
|
+
const content = normalizeReadChatReplayTextContent(message.content);
|
|
202
|
+
return {
|
|
203
|
+
role,
|
|
204
|
+
kind,
|
|
205
|
+
senderName,
|
|
206
|
+
content,
|
|
207
|
+
signature: `${role}:${kind}:${senderName}:${content}`,
|
|
208
|
+
collapsible,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function isStableReadChatAssistantAnswerInfo(info: ReadChatReplayCollapseInfo | null): boolean {
|
|
213
|
+
if (!info) return false;
|
|
214
|
+
if (info.role !== 'assistant') return false;
|
|
215
|
+
if (info.kind && info.kind !== 'standard') return false;
|
|
216
|
+
if (info.content.length < 160) return false;
|
|
208
217
|
|
|
209
218
|
// A provider may surface expanded command output as a standard assistant bubble
|
|
210
219
|
// (for example Claude Code's "Bash command ..." block). That is live work output,
|
|
211
220
|
// not a stable final answer. Treating it as a terminal answer would hide the
|
|
212
221
|
// real final response and violate read_chat fidelity.
|
|
213
|
-
if (/^(bash|shell|terminal) command\b/i.test(content)) return false;
|
|
222
|
+
if (/^(bash|shell|terminal) command\b/i.test(info.content)) return false;
|
|
214
223
|
return true;
|
|
215
224
|
}
|
|
216
225
|
|
|
217
|
-
function
|
|
218
|
-
|
|
219
|
-
|
|
226
|
+
function isReplayedAssistantAnswerAfterStableAnswerInfo(
|
|
227
|
+
info: ReadChatReplayCollapseInfo | null,
|
|
228
|
+
stableContent: string,
|
|
220
229
|
): boolean {
|
|
221
|
-
if (!
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (kind && kind !== 'standard') return false;
|
|
226
|
-
const content = normalizeReadChatReplayText(message);
|
|
227
|
-
const stableContent = normalizeReadChatReplayText(stableAnswer);
|
|
230
|
+
if (!info || !stableContent) return false;
|
|
231
|
+
if (info.role !== 'assistant') return false;
|
|
232
|
+
if (info.kind && info.kind !== 'standard') return false;
|
|
233
|
+
const content = info.content;
|
|
228
234
|
if (content.length < 80 || stableContent.length < 80) return false;
|
|
229
235
|
return content === stableContent || content.startsWith(stableContent) || stableContent.startsWith(content);
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
function collapseReplayDuplicatesFromReadChat(messages: ChatMessage[]): ChatMessage[] {
|
|
238
|
+
export function collapseReplayDuplicatesFromReadChat(messages: ChatMessage[]): ChatMessage[] {
|
|
233
239
|
const collapsed: ChatMessage[] = [];
|
|
234
240
|
const replaySignaturesInCurrentTurn = new Set<string>();
|
|
235
|
-
let
|
|
241
|
+
let stableAssistantAnswerContentInCurrentTurn = '';
|
|
242
|
+
let previousReplaySignature = '';
|
|
236
243
|
|
|
237
244
|
for (const message of messages) {
|
|
238
|
-
const
|
|
239
|
-
if (role === 'user') {
|
|
245
|
+
const info = getReadChatReplayCollapseInfo(message);
|
|
246
|
+
if (info?.role === 'user') {
|
|
240
247
|
replaySignaturesInCurrentTurn.clear();
|
|
241
|
-
|
|
248
|
+
stableAssistantAnswerContentInCurrentTurn = '';
|
|
249
|
+
previousReplaySignature = '';
|
|
242
250
|
}
|
|
243
251
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (shouldCollapseReadChatReplayDuplicate(message) && signature) {
|
|
249
|
-
if (previousSignature === signature) continue;
|
|
250
|
-
if (replaySignaturesInCurrentTurn.has(signature)) continue;
|
|
251
|
-
if (isReplayedAssistantAnswerAfterStableAnswer(message, stableAssistantAnswerInCurrentTurn)) continue;
|
|
252
|
+
if (info?.collapsible && info.signature) {
|
|
253
|
+
if (previousReplaySignature === info.signature) continue;
|
|
254
|
+
if (replaySignaturesInCurrentTurn.has(info.signature)) continue;
|
|
255
|
+
if (isReplayedAssistantAnswerAfterStableAnswerInfo(info, stableAssistantAnswerContentInCurrentTurn)) continue;
|
|
252
256
|
}
|
|
253
257
|
|
|
254
258
|
collapsed.push(message);
|
|
255
|
-
|
|
256
|
-
|
|
259
|
+
previousReplaySignature = info?.collapsible ? info.signature : '';
|
|
260
|
+
if (info?.collapsible && info.signature) {
|
|
261
|
+
replaySignaturesInCurrentTurn.add(info.signature);
|
|
257
262
|
}
|
|
258
|
-
if (
|
|
259
|
-
|
|
263
|
+
if (isStableReadChatAssistantAnswerInfo(info)) {
|
|
264
|
+
stableAssistantAnswerContentInCurrentTurn = info?.content || '';
|
|
260
265
|
}
|
|
261
266
|
}
|
|
262
267
|
|
|
@@ -369,13 +374,17 @@ function computeReadChatSync(messages: ChatMessage[], cursor: Required<ReadChatC
|
|
|
369
374
|
}
|
|
370
375
|
|
|
371
376
|
if (cursor.tailLimit > 0 && knownSignature === lastMessageSignature) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
377
|
+
const requestedTailCount = Math.min(totalMessages, cursor.tailLimit);
|
|
378
|
+
if (knownMessageCount >= requestedTailCount) {
|
|
379
|
+
return {
|
|
380
|
+
syncMode: 'noop',
|
|
381
|
+
replaceFrom: totalMessages,
|
|
382
|
+
messages: [],
|
|
383
|
+
totalMessages,
|
|
384
|
+
lastMessageSignature,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return buildBoundedTailSync(messages, cursor);
|
|
379
388
|
}
|
|
380
389
|
|
|
381
390
|
if (knownMessageCount < totalMessages) {
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
import type { ProviderModule, ContentBlock, InputEnvelope, ToolCallInfo, ToolCallContent as TCC, ToolKind, ToolCallStatus as TCS } from './contracts.js';
|
|
50
50
|
import { normalizeContent, flattenContent, normalizeInputEnvelope } from './contracts.js';
|
|
51
51
|
import { assertProviderSupportsDeclaredInput } from './provider-input-support.js';
|
|
52
|
-
import type { ProviderInstance, ProviderState, AcpProviderState, ProviderErrorReason, ProviderEvent, InstanceContext } from './provider-instance.js';
|
|
52
|
+
import type { ProviderInstance, ProviderState, AcpProviderState, ProviderErrorReason, ProviderEvent, InstanceContext, SessionModalState } from './provider-instance.js';
|
|
53
53
|
import { StatusMonitor } from './status-monitor.js';
|
|
54
54
|
import { buildLegacyModelModeSummaryMetadata } from './summary-metadata.js';
|
|
55
55
|
import {
|
|
@@ -283,6 +283,19 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
getSessionModalState(): SessionModalState {
|
|
287
|
+
const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
|
|
288
|
+
return {
|
|
289
|
+
id: this.instanceId,
|
|
290
|
+
status: this.currentStatus,
|
|
291
|
+
title: `${this.provider.name} · ${dirName}`,
|
|
292
|
+
activeModal: this.currentStatus === 'waiting_approval' ? {
|
|
293
|
+
message: this.activeToolCalls.find(t => t.status === 'running')?.name || 'Permission requested',
|
|
294
|
+
buttons: ['Approve', 'Reject'],
|
|
295
|
+
} : null,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
286
299
|
getState(): AcpProviderState {
|
|
287
300
|
const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
|
|
288
301
|
|
|
@@ -12,7 +12,7 @@ import * as fs from 'fs';
|
|
|
12
12
|
import { createRequire } from 'node:module';
|
|
13
13
|
import { normalizeInputEnvelope, type ProviderModule, flattenContent } from './contracts.js';
|
|
14
14
|
import { assertTextOnlyInput } from './provider-input-support.js';
|
|
15
|
-
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, ProviderErrorReason, HotChatSessionState } from './provider-instance.js';
|
|
15
|
+
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, ProviderErrorReason, HotChatSessionState, SessionModalState } from './provider-instance.js';
|
|
16
16
|
import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
|
|
17
17
|
import type { CliProviderModule } from '../cli-adapters/provider-cli-adapter.js';
|
|
18
18
|
import type { PtyRuntimeMetadata, PtyTransportFactory } from '../cli-adapters/pty-transport.js';
|
|
@@ -533,6 +533,19 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
533
533
|
};
|
|
534
534
|
}
|
|
535
535
|
|
|
536
|
+
getSessionModalState(): SessionModalState {
|
|
537
|
+
const adapterStatus = this.adapter.getStatus({ allowParse: false });
|
|
538
|
+
const autoApproveActive = adapterStatus.status === 'waiting_approval' && this.shouldAutoApprove();
|
|
539
|
+
const visibleStatus = autoApproveActive ? 'generating' : adapterStatus.status;
|
|
540
|
+
const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
|
|
541
|
+
return {
|
|
542
|
+
id: this.instanceId,
|
|
543
|
+
status: visibleStatus,
|
|
544
|
+
title: dirName,
|
|
545
|
+
activeModal: autoApproveActive ? null : adapterStatus.activeModal,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
536
549
|
updateSettings(newSettings: Record<string, any>): void {
|
|
537
550
|
this.settings = { ...newSettings };
|
|
538
551
|
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { flattenContent, type ProviderModule } from './contracts.js';
|
|
9
|
-
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext } from './provider-instance.js';
|
|
9
|
+
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, SessionModalState } from './provider-instance.js';
|
|
10
10
|
import { StatusMonitor } from './status-monitor.js';
|
|
11
11
|
import { buildPersistedProviderEffectMessage, normalizeProviderEffects } from './control-effects.js';
|
|
12
12
|
import { ChatHistoryWriter } from '../config/chat-history.js';
|
|
@@ -110,6 +110,16 @@ export class ExtensionProviderInstance implements ProviderInstance {
|
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
getSessionModalState(sessionId?: string): SessionModalState | null {
|
|
114
|
+
if (sessionId && sessionId !== this.instanceId) return null;
|
|
115
|
+
return {
|
|
116
|
+
id: this.instanceId,
|
|
117
|
+
status: this.currentStatus,
|
|
118
|
+
title: this.chatTitle || this.agentName || this.provider.name,
|
|
119
|
+
activeModal: this.activeModal,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
113
123
|
onEvent(event: string, data?: any): void {
|
|
114
124
|
if (event === 'stream_update') {
|
|
115
125
|
// Reflect data collected from agent-stream-manager
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import * as os from 'os';
|
|
13
13
|
import * as crypto from 'crypto';
|
|
14
14
|
import { flattenContent, type ProviderModule } from './contracts.js';
|
|
15
|
-
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext } from './provider-instance.js';
|
|
15
|
+
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, SessionModalState } from './provider-instance.js';
|
|
16
16
|
import { ExtensionProviderInstance } from './extension-provider-instance.js';
|
|
17
17
|
import { StatusMonitor } from './status-monitor.js';
|
|
18
18
|
import { ChatHistoryWriter } from '../config/chat-history.js';
|
|
@@ -172,6 +172,30 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
getSessionModalState(sessionId?: string): SessionModalState | null {
|
|
176
|
+
if (sessionId && sessionId !== this.instanceId) {
|
|
177
|
+
for (const ext of this.extensions.values()) {
|
|
178
|
+
const projected = ext.getSessionModalState?.(sessionId);
|
|
179
|
+
if (projected?.id === sessionId) return projected;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const autoApproveActive = (
|
|
185
|
+
this.currentStatus === 'waiting_approval'
|
|
186
|
+
|| this.cachedChat?.status === 'waiting_approval'
|
|
187
|
+
) && this.canAutoApprove();
|
|
188
|
+
const visibleStatus = autoApproveActive ? 'generating' : this.currentStatus;
|
|
189
|
+
return {
|
|
190
|
+
id: this.instanceId,
|
|
191
|
+
status: autoApproveActive && this.cachedChat?.status === 'waiting_approval'
|
|
192
|
+
? 'generating'
|
|
193
|
+
: (this.cachedChat?.status || visibleStatus),
|
|
194
|
+
title: this.cachedChat?.title || this.type,
|
|
195
|
+
activeModal: autoApproveActive ? null : (this.cachedChat?.activeModal || null),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
175
199
|
onEvent(event: string, data?: any): void {
|
|
176
200
|
if (event === 'cdp_connected') {
|
|
177
201
|
// CDP connection done
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 4. Event collection and propagation
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, HotChatSessionState } from './provider-instance.js';
|
|
11
|
+
import type { ProviderInstance, ProviderState, ProviderEvent, InstanceContext, HotChatSessionState, SessionModalState } from './provider-instance.js';
|
|
12
12
|
import { LOG } from '../logging/logger.js';
|
|
13
13
|
|
|
14
14
|
function projectHotChatSessionStatesFromProviderState(state: ProviderState): HotChatSessionState[] {
|
|
@@ -165,6 +165,32 @@ export class ProviderInstanceManager {
|
|
|
165
165
|
return sessions;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
getSessionModalState(sessionId: string, options: { instanceKey?: string | null } = {}): SessionModalState | null {
|
|
169
|
+
if (!sessionId) return null;
|
|
170
|
+
const candidates = [sessionId];
|
|
171
|
+
if (options.instanceKey && options.instanceKey !== sessionId) {
|
|
172
|
+
candidates.push(options.instanceKey);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (const id of candidates) {
|
|
176
|
+
const instance = this.instances.get(id);
|
|
177
|
+
if (!instance?.getSessionModalState) continue;
|
|
178
|
+
try {
|
|
179
|
+
const projected = instance.getSessionModalState(sessionId);
|
|
180
|
+
if (!projected?.id) continue;
|
|
181
|
+
if (projected.id !== sessionId) {
|
|
182
|
+
LOG.warn('InstanceMgr', `[InstanceManager] Ignoring mismatched session modal projection from ${id}: requested=${sessionId} projected=${projected.id}`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
return projected;
|
|
186
|
+
} catch (e) {
|
|
187
|
+
LOG.warn('InstanceMgr', `[InstanceManager] Failed to project session modal metadata from ${id}: ${(e as Error).message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
168
194
|
/**
|
|
169
195
|
* Per-category status collect
|
|
170
196
|
*/
|
|
@@ -147,6 +147,13 @@ export interface HotChatSessionState {
|
|
|
147
147
|
runtimeRecoveryState?: unknown;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
export interface SessionModalState {
|
|
151
|
+
id: string;
|
|
152
|
+
status?: unknown;
|
|
153
|
+
title?: unknown;
|
|
154
|
+
activeModal?: unknown;
|
|
155
|
+
}
|
|
156
|
+
|
|
150
157
|
// ─── ProviderInstance interface ─────────────────
|
|
151
158
|
|
|
152
159
|
export interface InstanceContext {
|
|
@@ -191,6 +198,12 @@ export interface ProviderInstance {
|
|
|
191
198
|
*/
|
|
192
199
|
getHotChatSessionState?(): HotChatSessionState | HotChatSessionState[] | null;
|
|
193
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Return the cheap modal metadata for a single session subscription. This is
|
|
203
|
+
* used on P2P topic flushes and must not invoke rich chat/transcript parsing.
|
|
204
|
+
*/
|
|
205
|
+
getSessionModalState?(sessionId?: string): SessionModalState | null;
|
|
206
|
+
|
|
194
207
|
/** Receive event (external → Instance) */
|
|
195
208
|
onEvent(event: string, data?: any): void;
|
|
196
209
|
|