@adhdev/daemon-core 0.8.59 → 0.8.61
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/cli-adapters/provider-cli-adapter.d.ts +3 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +3 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +516 -350
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +506 -350
- package/dist/index.mjs.map +1 -1
- package/dist/providers/chat-message-normalization.d.ts +43 -0
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/dist/providers/contracts.d.ts +5 -1
- package/dist/providers/control-effects.d.ts +2 -0
- package/dist/providers/extension-provider-instance.d.ts +2 -1
- package/dist/providers/ide-provider-instance.d.ts +2 -1
- package/dist/types.d.ts +2 -1
- package/node_modules/@adhdev/session-host-core/package.json +2 -2
- package/package.json +2 -2
- package/src/agent-stream/poller.ts +3 -5
- package/src/cli-adapters/provider-cli-adapter.ts +35 -18
- package/src/cli-adapters/provider-cli-shared.ts +3 -2
- package/src/commands/chat-commands.ts +2 -1
- package/src/config/chat-history.ts +6 -5
- package/src/index.ts +13 -0
- package/src/providers/acp-provider-instance.ts +19 -24
- package/src/providers/chat-message-normalization.ts +68 -0
- package/src/providers/cli-provider-instance.ts +42 -29
- package/src/providers/contracts.ts +5 -1
- package/src/providers/control-effects.ts +72 -0
- package/src/providers/extension-provider-instance.ts +43 -31
- package/src/providers/ide-provider-instance.ts +42 -30
- package/src/types.ts +2 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ChatMessage } from '../types.js';
|
|
2
|
+
export declare const BUILTIN_CHAT_MESSAGE_KINDS: readonly ["standard", "thought", "tool", "terminal", "system"];
|
|
3
|
+
export type BuiltinChatMessageKind = typeof BUILTIN_CHAT_MESSAGE_KINDS[number];
|
|
4
|
+
export type ChatMessageKind = BuiltinChatMessageKind | (string & {});
|
|
5
|
+
export declare function isBuiltinChatMessageKind(kind: unknown): kind is BuiltinChatMessageKind;
|
|
6
|
+
export declare function normalizeChatMessageKind(kind: unknown, role: unknown): ChatMessageKind;
|
|
7
|
+
export declare function buildChatMessage<T extends Omit<ChatMessage, 'kind'> & {
|
|
8
|
+
kind?: ChatMessageKind;
|
|
9
|
+
}>(message: T): T & {
|
|
10
|
+
kind: ChatMessageKind;
|
|
11
|
+
};
|
|
12
|
+
export declare function buildSystemChatMessage<T extends Omit<ChatMessage, 'role' | 'kind'> & {
|
|
13
|
+
role?: 'system';
|
|
14
|
+
kind?: ChatMessageKind;
|
|
15
|
+
}>(message: T): (T & {
|
|
16
|
+
role: 'system';
|
|
17
|
+
kind: ChatMessageKind;
|
|
18
|
+
});
|
|
19
|
+
export declare function buildRuntimeSystemChatMessage<T extends Omit<ChatMessage, 'role' | 'kind' | 'senderName'> & {
|
|
20
|
+
role?: 'system';
|
|
21
|
+
kind?: ChatMessageKind;
|
|
22
|
+
senderName?: string;
|
|
23
|
+
}>(message: T): (T & {
|
|
24
|
+
role: 'system';
|
|
25
|
+
kind: ChatMessageKind;
|
|
26
|
+
senderName: string;
|
|
27
|
+
});
|
|
28
|
+
export declare function buildAssistantChatMessage<T extends Omit<ChatMessage, 'role' | 'kind'> & {
|
|
29
|
+
role?: 'assistant';
|
|
30
|
+
kind?: ChatMessageKind;
|
|
31
|
+
}>(message: T): (T & {
|
|
32
|
+
role: 'assistant';
|
|
33
|
+
kind: ChatMessageKind;
|
|
34
|
+
});
|
|
35
|
+
export declare function buildUserChatMessage<T extends Omit<ChatMessage, 'role' | 'kind'> & {
|
|
36
|
+
role?: 'user';
|
|
37
|
+
kind?: ChatMessageKind;
|
|
38
|
+
}>(message: T): (T & {
|
|
39
|
+
role: 'user';
|
|
40
|
+
kind: ChatMessageKind;
|
|
41
|
+
});
|
|
42
|
+
export declare function normalizeChatMessage<T extends ChatMessage>(message: T): T;
|
|
43
|
+
export declare function normalizeChatMessages<T extends ChatMessage>(messages: T[] | null | undefined): T[];
|
|
@@ -89,6 +89,7 @@ export declare class CliProviderInstance implements ProviderInstance {
|
|
|
89
89
|
private formatMarkerTimestamp;
|
|
90
90
|
private maybeAppendRuntimeRecoveryMessage;
|
|
91
91
|
private appendRuntimeSystemMessage;
|
|
92
|
+
private appendRuntimeMessage;
|
|
92
93
|
private mergeConversationMessages;
|
|
93
94
|
private formatApprovalRequestMessage;
|
|
94
95
|
private promoteProviderSessionId;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - User custom providers use the same contracts
|
|
8
8
|
*/
|
|
9
9
|
import type { ProviderSummaryMetadata } from '../shared-types.js';
|
|
10
|
+
import type { ChatMessageKind } from './chat-message-normalization.js';
|
|
10
11
|
export interface ReadChatResult {
|
|
11
12
|
messages: ChatMessage[];
|
|
12
13
|
status: AgentStatus;
|
|
@@ -43,7 +44,7 @@ export interface ModalInfo {
|
|
|
43
44
|
export interface ProviderEffectMessage {
|
|
44
45
|
role?: 'system' | 'assistant' | 'user';
|
|
45
46
|
content: string | MessagePart[];
|
|
46
|
-
kind?:
|
|
47
|
+
kind?: ChatMessageKind;
|
|
47
48
|
senderName?: string;
|
|
48
49
|
}
|
|
49
50
|
export interface ProviderEffectToast {
|
|
@@ -59,6 +60,9 @@ export interface ProviderEffectNotification {
|
|
|
59
60
|
channels?: ProviderNotificationChannel[];
|
|
60
61
|
preferenceKey?: ProviderNotificationPreferenceKey;
|
|
61
62
|
bubbleContent?: string | MessagePart[];
|
|
63
|
+
bubbleKind?: ChatMessageKind;
|
|
64
|
+
bubbleRole?: 'system' | 'assistant' | 'user';
|
|
65
|
+
bubbleSenderName?: string;
|
|
62
66
|
}
|
|
63
67
|
export interface ProviderEffect {
|
|
64
68
|
type: 'message' | 'toast' | 'notification';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ControlInvokeResult, ControlListResult, ControlSetResult, ProviderControlDef, ProviderEffect } from './contracts.js';
|
|
2
|
+
import type { ChatMessage } from '../types.js';
|
|
2
3
|
export type ProviderControlValue = string | number | boolean;
|
|
3
4
|
export declare function extractProviderControlValues(controls: ProviderControlDef[] | undefined, data: any): Record<string, ProviderControlValue> | undefined;
|
|
4
5
|
export declare function normalizeProviderEffects(data: any): ProviderEffect[];
|
|
6
|
+
export declare function buildPersistedProviderEffectMessage(effect: ProviderEffect | null | undefined): ChatMessage | null;
|
|
5
7
|
export declare function normalizeControlListResult(data: any): ControlListResult;
|
|
6
8
|
export declare function normalizeControlSetResult(data: any): ControlSetResult;
|
|
7
9
|
export declare function normalizeControlInvokeResult(data: any): ControlInvokeResult;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Manages IDE extensions (Cline, Roo Code, etc).
|
|
5
5
|
* CDP webview discovery + agent stream collection moved here.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type ProviderModule } from './contracts.js';
|
|
8
8
|
import type { ProviderInstance, ProviderState, InstanceContext } from './provider-instance.js';
|
|
9
9
|
export declare class ExtensionProviderInstance implements ProviderInstance {
|
|
10
10
|
readonly type: string;
|
|
@@ -45,6 +45,7 @@ export declare class ExtensionProviderInstance implements ProviderInstance {
|
|
|
45
45
|
private pushEvent;
|
|
46
46
|
private applyProviderResponse;
|
|
47
47
|
private appendRuntimeSystemMessage;
|
|
48
|
+
private appendRuntimeMessage;
|
|
48
49
|
/**
|
|
49
50
|
* Assign stable receivedAt to extension messages.
|
|
50
51
|
* Same pattern as IdeProviderInstance.readChat() prevByHash —
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* IDE Instance manages child Extension Instances.
|
|
9
9
|
* Daemon collects all via a single IDE Instance.getState() call.
|
|
10
10
|
*/
|
|
11
|
-
import type
|
|
11
|
+
import { type ProviderModule } from './contracts.js';
|
|
12
12
|
import type { ProviderInstance, ProviderState, InstanceContext } from './provider-instance.js';
|
|
13
13
|
import { ExtensionProviderInstance } from './extension-provider-instance.js';
|
|
14
14
|
export declare class IdeProviderInstance implements ProviderInstance {
|
|
@@ -60,6 +60,7 @@ export declare class IdeProviderInstance implements ProviderInstance {
|
|
|
60
60
|
private pushEvent;
|
|
61
61
|
private applyProviderResponse;
|
|
62
62
|
private appendRuntimeSystemMessage;
|
|
63
|
+
private appendRuntimeMessage;
|
|
63
64
|
private mergeConversationMessages;
|
|
64
65
|
private getPersistedEffectContent;
|
|
65
66
|
private getEffectDedupKey;
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* When modifying this file, also update interface contracts in AGENT_PROTOCOL.md.
|
|
6
6
|
*/
|
|
7
7
|
import type { StatusReportPayload, AvailableProviderInfo } from './shared-types.js';
|
|
8
|
+
import type { ChatMessageKind } from './providers/chat-message-normalization.js';
|
|
8
9
|
/** Full status response from /api/v1/status and WS events */
|
|
9
10
|
export interface StatusResponse extends StatusReportPayload {
|
|
10
11
|
/** For standalone API compat */
|
|
@@ -23,7 +24,7 @@ export interface ChatMessage {
|
|
|
23
24
|
role: string;
|
|
24
25
|
/** Plain text (legacy) or canonical message parts */
|
|
25
26
|
content: string | MessagePart[];
|
|
26
|
-
kind?:
|
|
27
|
+
kind?: ChatMessageKind;
|
|
27
28
|
id?: string;
|
|
28
29
|
index?: number;
|
|
29
30
|
timestamp?: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/session-host-core",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "ADHDev local session host core
|
|
3
|
+
"version": "0.8.61",
|
|
4
|
+
"description": "ADHDev local session host core \u2014 session registry, protocol, buffers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "ADHDev daemon core
|
|
3
|
+
"version": "0.8.61",
|
|
4
|
+
"description": "ADHDev daemon core \u2014 CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -21,6 +21,7 @@ import { LOG } from '../logging/logger.js';
|
|
|
21
21
|
import type { AgentStreamState } from './types.js';
|
|
22
22
|
import { formatAutoApprovalMessage, pickApprovalButton } from '../providers/approval-utils.js';
|
|
23
23
|
import type { ProviderModule } from '../providers/contracts.js';
|
|
24
|
+
import { buildRuntimeSystemChatMessage } from '../providers/chat-message-normalization.js';
|
|
24
25
|
|
|
25
26
|
interface ExtensionInstanceLike {
|
|
26
27
|
type?: string;
|
|
@@ -229,12 +230,9 @@ export class AgentStreamPoller {
|
|
|
229
230
|
type: 'message',
|
|
230
231
|
id: effectId,
|
|
231
232
|
persist: true,
|
|
232
|
-
message: {
|
|
233
|
-
role: 'system',
|
|
234
|
-
senderName: 'System',
|
|
235
|
-
kind: 'system',
|
|
233
|
+
message: buildRuntimeSystemChatMessage({
|
|
236
234
|
content: formatAutoApprovalMessage(stream.activeModal?.message, buttonLabel),
|
|
237
|
-
},
|
|
235
|
+
}),
|
|
238
236
|
},
|
|
239
237
|
],
|
|
240
238
|
};
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
type CliSessionStatus,
|
|
45
45
|
type CliTraceEntry,
|
|
46
46
|
} from './provider-cli-shared.js';
|
|
47
|
+
import { buildChatMessage } from '../providers/chat-message-normalization.js';
|
|
47
48
|
import {
|
|
48
49
|
buildCliParseInput,
|
|
49
50
|
buildCliTraceParseSnapshot,
|
|
@@ -183,6 +184,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
183
184
|
private static readonly MAX_TRACE_ENTRIES = 250;
|
|
184
185
|
private readonly providerResolutionMeta: ProviderResolutionMeta;
|
|
185
186
|
private static readonly IDLE_FINISH_CONFIRM_MS = 2000;
|
|
187
|
+
private static readonly HERMES_IDLE_FINISH_CONFIRM_MS = 5000;
|
|
186
188
|
private static readonly STATUS_ACTIVITY_HOLD_MS = 2000;
|
|
187
189
|
private static readonly FINISH_RETRY_DELAY_MS = 300;
|
|
188
190
|
private static readonly MAX_FINISH_RETRIES = 2;
|
|
@@ -192,6 +194,18 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
192
194
|
this.structuredMessages = [...this.committedMessages];
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
private getIdleFinishConfirmMs(): number {
|
|
198
|
+
return this.cliType === 'hermes-cli'
|
|
199
|
+
? ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS
|
|
200
|
+
: ProviderCliAdapter.IDLE_FINISH_CONFIRM_MS;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private getStatusActivityHoldMs(): number {
|
|
204
|
+
return this.cliType === 'hermes-cli'
|
|
205
|
+
? ProviderCliAdapter.HERMES_IDLE_FINISH_CONFIRM_MS
|
|
206
|
+
: ProviderCliAdapter.STATUS_ACTIVITY_HOLD_MS;
|
|
207
|
+
}
|
|
208
|
+
|
|
195
209
|
private setStatus(status: CliSessionStatus['status'], trigger?: string): void {
|
|
196
210
|
const prev = this.currentStatus;
|
|
197
211
|
if (prev === status) return;
|
|
@@ -216,6 +230,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
216
230
|
|
|
217
231
|
private armIdleFinishCandidate(assistantLength: number): void {
|
|
218
232
|
const now = Date.now();
|
|
233
|
+
const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
|
|
219
234
|
this.idleFinishCandidate = {
|
|
220
235
|
armedAt: now,
|
|
221
236
|
lastOutputAt: this.lastOutputAt,
|
|
@@ -224,7 +239,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
224
239
|
assistantLength,
|
|
225
240
|
};
|
|
226
241
|
this.recordTrace('idle_candidate_armed', {
|
|
227
|
-
confirmMs:
|
|
242
|
+
confirmMs: idleFinishConfirmMs,
|
|
228
243
|
candidate: this.idleFinishCandidate,
|
|
229
244
|
...buildCliTraceParseSnapshot({
|
|
230
245
|
accumulatedBuffer: this.accumulatedBuffer,
|
|
@@ -239,7 +254,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
239
254
|
this.settleTimer = null;
|
|
240
255
|
this.settledBuffer = this.recentOutputBuffer;
|
|
241
256
|
this.evaluateSettled();
|
|
242
|
-
},
|
|
257
|
+
}, idleFinishConfirmMs);
|
|
243
258
|
}
|
|
244
259
|
|
|
245
260
|
|
|
@@ -719,8 +734,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
719
734
|
private hasRecentInteractiveActivity(now: number): boolean {
|
|
720
735
|
const quietForMs = this.lastNonEmptyOutputAt ? (now - this.lastNonEmptyOutputAt) : Number.MAX_SAFE_INTEGER;
|
|
721
736
|
const screenStableMs = this.lastScreenChangeAt ? (now - this.lastScreenChangeAt) : Number.MAX_SAFE_INTEGER;
|
|
722
|
-
|
|
723
|
-
|
|
737
|
+
const holdMs = this.getStatusActivityHoldMs();
|
|
738
|
+
return quietForMs < holdMs
|
|
739
|
+
|| screenStableMs < holdMs;
|
|
724
740
|
}
|
|
725
741
|
|
|
726
742
|
private getStartupConfirmationModal(screenText: string): { message: string; buttons: string[] } | null {
|
|
@@ -901,6 +917,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
901
917
|
}
|
|
902
918
|
|
|
903
919
|
const recentInteractiveActivity = this.hasRecentInteractiveActivity(now);
|
|
920
|
+
const statusActivityHoldMs = this.getStatusActivityHoldMs();
|
|
904
921
|
const shouldHoldGenerating =
|
|
905
922
|
scriptStatus === 'idle'
|
|
906
923
|
&& this.isWaitingForResponse
|
|
@@ -921,7 +938,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
921
938
|
recentInteractiveActivity,
|
|
922
939
|
lastNonEmptyOutputAt: this.lastNonEmptyOutputAt,
|
|
923
940
|
lastScreenChangeAt: this.lastScreenChangeAt,
|
|
924
|
-
holdMs:
|
|
941
|
+
holdMs: statusActivityHoldMs,
|
|
925
942
|
...buildCliTraceParseSnapshot({
|
|
926
943
|
accumulatedBuffer: this.accumulatedBuffer,
|
|
927
944
|
accumulatedRawBuffer: this.accumulatedRawBuffer,
|
|
@@ -1013,8 +1030,9 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1013
1030
|
const screenStableMs = this.lastScreenChangeAt ? (now - this.lastScreenChangeAt) : 0;
|
|
1014
1031
|
const hasAssistantTurn = !!lastParsedAssistant;
|
|
1015
1032
|
const assistantLength = lastParsedAssistant?.content?.length || 0;
|
|
1016
|
-
const
|
|
1017
|
-
const
|
|
1033
|
+
const idleFinishConfirmMs = this.getIdleFinishConfirmMs();
|
|
1034
|
+
const idleQuietThresholdMs = Math.max(idleFinishConfirmMs, this.timeouts.outputSettle);
|
|
1035
|
+
const idleStableThresholdMs = idleFinishConfirmMs;
|
|
1018
1036
|
const idleReady = visibleIdlePrompt
|
|
1019
1037
|
&& !modal
|
|
1020
1038
|
&& hasAssistantTurn
|
|
@@ -1026,7 +1044,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1026
1044
|
&& candidate.lastOutputAt === this.lastOutputAt
|
|
1027
1045
|
&& candidate.lastScreenChangeAt === this.lastScreenChangeAt
|
|
1028
1046
|
&& assistantLength >= candidate.assistantLength
|
|
1029
|
-
&& (now - candidate.armedAt) >=
|
|
1047
|
+
&& (now - candidate.armedAt) >= idleFinishConfirmMs;
|
|
1030
1048
|
const canFinishImmediately = idleReady && candidateQuiet;
|
|
1031
1049
|
|
|
1032
1050
|
this.recordTrace('idle_decision', {
|
|
@@ -1039,7 +1057,7 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1039
1057
|
idleQuietThresholdMs,
|
|
1040
1058
|
idleStableThresholdMs,
|
|
1041
1059
|
idleReady,
|
|
1042
|
-
idleFinishConfirmMs
|
|
1060
|
+
idleFinishConfirmMs,
|
|
1043
1061
|
idleFinishCandidate: candidate,
|
|
1044
1062
|
candidateQuiet,
|
|
1045
1063
|
canFinishImmediately,
|
|
@@ -1280,11 +1298,10 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1280
1298
|
&& !this.activeModal;
|
|
1281
1299
|
if (parsed && Array.isArray(parsed.messages)) {
|
|
1282
1300
|
const hydratedMessages = shouldPreferCommittedMessages
|
|
1283
|
-
? this.committedMessages.map((message, index) => ({
|
|
1301
|
+
? this.committedMessages.map((message, index) => buildChatMessage({
|
|
1284
1302
|
...message,
|
|
1285
1303
|
id: message.id || `msg_${index}`,
|
|
1286
1304
|
index: typeof message.index === 'number' ? message.index : index,
|
|
1287
|
-
kind: message.kind || 'standard',
|
|
1288
1305
|
receivedAt: typeof message.receivedAt === 'number'
|
|
1289
1306
|
? message.receivedAt
|
|
1290
1307
|
: message.timestamp,
|
|
@@ -1309,13 +1326,13 @@ export class ProviderCliAdapter implements CliAdapter {
|
|
|
1309
1326
|
id: 'cli_session',
|
|
1310
1327
|
status: this.currentStatus,
|
|
1311
1328
|
title: this.cliName,
|
|
1312
|
-
messages: messages.slice(-50).map((message, index) => ({
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1329
|
+
messages: messages.slice(-50).map((message, index) => buildChatMessage({
|
|
1330
|
+
...message,
|
|
1331
|
+
id: message.id || `msg_${index}`,
|
|
1332
|
+
index: typeof message.index === 'number' ? message.index : index,
|
|
1333
|
+
receivedAt: typeof message.receivedAt === 'number'
|
|
1334
|
+
? message.receivedAt
|
|
1335
|
+
: message.timestamp,
|
|
1319
1336
|
})),
|
|
1320
1337
|
activeModal: this.activeModal,
|
|
1321
1338
|
};
|
|
@@ -2,6 +2,7 @@ import * as os from 'os';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { execSync } from 'child_process';
|
|
4
4
|
import type { ProviderResumeCapability } from '../providers/contracts.js';
|
|
5
|
+
import type { ChatMessageKind } from '../providers/chat-message-normalization.js';
|
|
5
6
|
import { sanitizeSpawnEnv } from './spawn-env.js';
|
|
6
7
|
|
|
7
8
|
export interface CliChatMessage {
|
|
@@ -9,10 +10,10 @@ export interface CliChatMessage {
|
|
|
9
10
|
content: string;
|
|
10
11
|
timestamp?: number;
|
|
11
12
|
receivedAt?: number;
|
|
12
|
-
kind?:
|
|
13
|
+
kind?: ChatMessageKind;
|
|
13
14
|
id?: string;
|
|
14
15
|
index?: number;
|
|
15
|
-
meta?: Record<string,
|
|
16
|
+
meta?: Record<string, unknown>;
|
|
16
17
|
senderName?: string;
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -12,6 +12,7 @@ import { LOG } from '../logging/logger.js';
|
|
|
12
12
|
import { recordDebugTrace } from '../logging/debug-trace.js';
|
|
13
13
|
import type { ChatMessage } from '../types.js';
|
|
14
14
|
import type { ReadChatCursor, ReadChatSyncMode, SessionTransport } from '../shared-types.js';
|
|
15
|
+
import { normalizeChatMessages } from '../providers/chat-message-normalization.js';
|
|
15
16
|
|
|
16
17
|
const RECENT_SEND_WINDOW_MS = 1200;
|
|
17
18
|
const recentSendByTarget = new Map<string, number>();
|
|
@@ -189,7 +190,7 @@ function normalizeReadChatCursor(args: any): Required<ReadChatCursor> {
|
|
|
189
190
|
|
|
190
191
|
function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[] {
|
|
191
192
|
const messages = Array.isArray(payload.messages) ? payload.messages as ChatMessage[] : [];
|
|
192
|
-
return messages;
|
|
193
|
+
return normalizeChatMessages(messages);
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
function deriveHistoryDedupKey(message: ChatMessage & { _unitKey?: string; _turnKey?: string }): string | undefined {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import * as fs from 'fs';
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import * as os from 'os';
|
|
15
|
+
import { buildRuntimeSystemChatMessage } from '../providers/chat-message-normalization.js';
|
|
15
16
|
|
|
16
17
|
const HISTORY_DIR = path.join(os.homedir(), '.adhdev', 'history');
|
|
17
18
|
const RETAIN_DAYS = 30;
|
|
@@ -314,11 +315,11 @@ export class ChatHistoryWriter {
|
|
|
314
315
|
this.appendNewMessages(
|
|
315
316
|
agentType,
|
|
316
317
|
[{
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
318
|
+
...buildRuntimeSystemChatMessage({
|
|
319
|
+
content,
|
|
320
|
+
receivedAt: options.receivedAt,
|
|
321
|
+
senderName: options.senderName,
|
|
322
|
+
}),
|
|
322
323
|
historyDedupKey: options.dedupKey,
|
|
323
324
|
}],
|
|
324
325
|
options.sessionTitle,
|
package/src/index.ts
CHANGED
|
@@ -200,6 +200,19 @@ export type { ProviderModule, CdpTargetFilter, ProviderResumeCapability, InputEn
|
|
|
200
200
|
export type { ProviderSourceConfigSnapshot, ProviderSourceConfigUpdate } from './config/provider-source-config.js';
|
|
201
201
|
export { parseProviderSourceConfigUpdate } from './config/provider-source-config.js';
|
|
202
202
|
export { normalizeInputEnvelope, normalizeMessageParts, flattenMessageParts } from './providers/io-contracts.js';
|
|
203
|
+
export {
|
|
204
|
+
BUILTIN_CHAT_MESSAGE_KINDS,
|
|
205
|
+
isBuiltinChatMessageKind,
|
|
206
|
+
normalizeChatMessageKind,
|
|
207
|
+
buildChatMessage,
|
|
208
|
+
buildSystemChatMessage,
|
|
209
|
+
buildRuntimeSystemChatMessage,
|
|
210
|
+
buildAssistantChatMessage,
|
|
211
|
+
buildUserChatMessage,
|
|
212
|
+
normalizeChatMessage,
|
|
213
|
+
normalizeChatMessages,
|
|
214
|
+
} from './providers/chat-message-normalization.js';
|
|
215
|
+
export type { BuiltinChatMessageKind, ChatMessageKind } from './providers/chat-message-normalization.js';
|
|
203
216
|
export { VersionArchive, detectAllVersions } from './providers/version-archive.js';
|
|
204
217
|
export type { ProviderVersionInfo, VersionHistory } from './providers/version-archive.js';
|
|
205
218
|
|
|
@@ -51,15 +51,16 @@ import { normalizeContent, flattenContent, normalizeInputEnvelope } from './cont
|
|
|
51
51
|
import type { ProviderInstance, ProviderState, AcpProviderState, ProviderErrorReason, ProviderEvent, InstanceContext } from './provider-instance.js';
|
|
52
52
|
import { StatusMonitor } from './status-monitor.js';
|
|
53
53
|
import { buildLegacyModelModeSummaryMetadata } from './summary-metadata.js';
|
|
54
|
+
import { buildAssistantChatMessage, buildChatMessage, buildRuntimeSystemChatMessage, buildUserChatMessage, normalizeChatMessages } from './chat-message-normalization.js';
|
|
54
55
|
import { LOG } from '../logging/logger.js';
|
|
56
|
+
import type { ChatMessage } from '../types.js';
|
|
55
57
|
|
|
56
58
|
// ─── Internal Display Types (for dashboard) ────────────────────────────
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
type AcpMessage = ChatMessage & {
|
|
59
61
|
role: 'user' | 'assistant' | 'system';
|
|
60
62
|
/** Rich content blocks (ACP standard) or plain text (legacy) */
|
|
61
63
|
content: string | ContentBlock[];
|
|
62
|
-
timestamp?: number;
|
|
63
64
|
/** Tool calls associated with this message */
|
|
64
65
|
toolCalls?: ToolCallInfo[];
|
|
65
66
|
}
|
|
@@ -293,26 +294,23 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
293
294
|
const dirName = this.workingDir.split('/').filter(Boolean).pop() || 'session';
|
|
294
295
|
|
|
295
296
|
// Recent 50 messages
|
|
296
|
-
const recentMessages = this.messages.slice(-50).map(m => {
|
|
297
|
+
const recentMessages = normalizeChatMessages(this.messages.slice(-50).map(m => {
|
|
297
298
|
const content = this.truncateContent(m.content);
|
|
298
|
-
return {
|
|
299
|
-
|
|
299
|
+
return buildChatMessage({
|
|
300
|
+
...m,
|
|
300
301
|
content,
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
};
|
|
304
|
-
});
|
|
302
|
+
});
|
|
303
|
+
}));
|
|
305
304
|
|
|
306
305
|
// generating during partial response add
|
|
307
306
|
if (this.currentStatus === 'generating' && (this.partialContent || this.partialBlocks.length > 0)) {
|
|
308
307
|
const blocks = this.buildPartialBlocks();
|
|
309
308
|
if (blocks.length > 0) {
|
|
310
|
-
recentMessages.push({
|
|
311
|
-
role: 'assistant',
|
|
309
|
+
recentMessages.push(buildAssistantChatMessage({
|
|
312
310
|
content: blocks,
|
|
313
311
|
timestamp: Date.now(),
|
|
314
312
|
toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : undefined,
|
|
315
|
-
});
|
|
313
|
+
}));
|
|
316
314
|
}
|
|
317
315
|
}
|
|
318
316
|
|
|
@@ -326,7 +324,7 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
326
324
|
id: this.sessionId || `${this.type}_${this.workingDir}`,
|
|
327
325
|
title: `${this.provider.name} · ${dirName}`,
|
|
328
326
|
status: this.currentStatus,
|
|
329
|
-
messages: recentMessages,
|
|
327
|
+
messages: normalizeChatMessages(recentMessages as any),
|
|
330
328
|
activeModal: this.currentStatus === 'waiting_approval' ? {
|
|
331
329
|
message: this.activeToolCalls.find(t => t.status === 'running')?.name || 'Permission requested',
|
|
332
330
|
buttons: ['Approve', 'Reject'],
|
|
@@ -975,11 +973,10 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
975
973
|
: [{ type: 'text', text }];
|
|
976
974
|
|
|
977
975
|
// Add user message locally (store as ContentBlock[])
|
|
978
|
-
this.messages.push({
|
|
979
|
-
role: 'user',
|
|
976
|
+
this.messages.push(buildUserChatMessage({
|
|
980
977
|
content: contentBlocks && contentBlocks.length > 0 ? contentBlocks : text,
|
|
981
978
|
timestamp: Date.now(),
|
|
982
|
-
});
|
|
979
|
+
}));
|
|
983
980
|
|
|
984
981
|
this.currentStatus = 'generating';
|
|
985
982
|
this.partialContent = '';
|
|
@@ -1181,11 +1178,11 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
1181
1178
|
}
|
|
1182
1179
|
|
|
1183
1180
|
if (content.trim()) {
|
|
1184
|
-
this.messages.push({
|
|
1181
|
+
this.messages.push(buildChatMessage({
|
|
1185
1182
|
role: m.role || 'assistant',
|
|
1186
1183
|
content: content.trim(),
|
|
1187
1184
|
timestamp: Date.now(),
|
|
1188
|
-
});
|
|
1185
|
+
}));
|
|
1189
1186
|
this.partialContent = '';
|
|
1190
1187
|
}
|
|
1191
1188
|
}
|
|
@@ -1271,14 +1268,13 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
1271
1268
|
}).filter(b => b.type !== 'text' || (b.type === 'text' && b.text.trim()));
|
|
1272
1269
|
|
|
1273
1270
|
if (finalBlocks.length > 0) {
|
|
1274
|
-
this.messages.push({
|
|
1275
|
-
role: 'assistant',
|
|
1271
|
+
this.messages.push(buildAssistantChatMessage({
|
|
1276
1272
|
content: finalBlocks.length === 1 && finalBlocks[0].type === 'text'
|
|
1277
1273
|
? (finalBlocks[0] as {type: 'text', text: string}).text // single text → string (backward compat)
|
|
1278
1274
|
: finalBlocks,
|
|
1279
1275
|
timestamp: Date.now(),
|
|
1280
1276
|
toolCalls: this.turnToolCalls.length > 0 ? [...this.turnToolCalls] : undefined,
|
|
1281
|
-
});
|
|
1277
|
+
}));
|
|
1282
1278
|
}
|
|
1283
1279
|
this.partialContent = '';
|
|
1284
1280
|
this.partialBlocks = [];
|
|
@@ -1347,11 +1343,10 @@ export class AcpProviderInstance implements ProviderInstance {
|
|
|
1347
1343
|
private appendSystemMessage(content: string, timestamp = Date.now()): void {
|
|
1348
1344
|
const normalizedContent = String(content || '').trim();
|
|
1349
1345
|
if (!normalizedContent) return;
|
|
1350
|
-
this.messages.push({
|
|
1351
|
-
role: 'system',
|
|
1346
|
+
this.messages.push(buildRuntimeSystemChatMessage({
|
|
1352
1347
|
content: normalizedContent,
|
|
1353
1348
|
timestamp,
|
|
1354
|
-
});
|
|
1349
|
+
}));
|
|
1355
1350
|
if (this.messages.length > 200) {
|
|
1356
1351
|
this.messages = this.messages.slice(-100);
|
|
1357
1352
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ChatMessage } from '../types.js';
|
|
2
|
+
|
|
3
|
+
export const BUILTIN_CHAT_MESSAGE_KINDS = ['standard', 'thought', 'tool', 'terminal', 'system'] as const;
|
|
4
|
+
|
|
5
|
+
export type BuiltinChatMessageKind = typeof BUILTIN_CHAT_MESSAGE_KINDS[number];
|
|
6
|
+
export type ChatMessageKind = BuiltinChatMessageKind | (string & {});
|
|
7
|
+
|
|
8
|
+
const KNOWN_CHAT_MESSAGE_KINDS = new Set<string>(BUILTIN_CHAT_MESSAGE_KINDS);
|
|
9
|
+
|
|
10
|
+
export function isBuiltinChatMessageKind(kind: unknown): kind is BuiltinChatMessageKind {
|
|
11
|
+
return typeof kind === 'string' && KNOWN_CHAT_MESSAGE_KINDS.has(kind.trim().toLowerCase());
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function normalizeChatMessageKind(kind: unknown, role: unknown): ChatMessageKind {
|
|
15
|
+
const normalizedKind = typeof kind === 'string' ? kind.trim().toLowerCase() : '';
|
|
16
|
+
if (normalizedKind && KNOWN_CHAT_MESSAGE_KINDS.has(normalizedKind)) return normalizedKind as BuiltinChatMessageKind;
|
|
17
|
+
|
|
18
|
+
const normalizedRole = typeof role === 'string' ? role.trim().toLowerCase() : '';
|
|
19
|
+
return normalizedRole === 'system' ? 'system' : 'standard';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buildChatMessage<T extends Omit<ChatMessage, 'kind'> & { kind?: ChatMessageKind }>(message: T): T & { kind: ChatMessageKind } {
|
|
23
|
+
return {
|
|
24
|
+
...message,
|
|
25
|
+
kind: normalizeChatMessageKind(message?.kind, message?.role),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function buildSystemChatMessage<T extends Omit<ChatMessage, 'role' | 'kind'> & { role?: 'system'; kind?: ChatMessageKind }>(message: T): (T & { role: 'system'; kind: ChatMessageKind }) {
|
|
30
|
+
return buildChatMessage({
|
|
31
|
+
...message,
|
|
32
|
+
role: 'system',
|
|
33
|
+
kind: message?.kind || 'system',
|
|
34
|
+
} as T & { role: 'system'; kind?: ChatMessageKind }) as T & { role: 'system'; kind: ChatMessageKind };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function buildRuntimeSystemChatMessage<T extends Omit<ChatMessage, 'role' | 'kind' | 'senderName'> & { role?: 'system'; kind?: ChatMessageKind; senderName?: string }>(message: T): (T & { role: 'system'; kind: ChatMessageKind; senderName: string }) {
|
|
38
|
+
return buildSystemChatMessage({
|
|
39
|
+
...message,
|
|
40
|
+
senderName: typeof message?.senderName === 'string' && message.senderName.trim()
|
|
41
|
+
? message.senderName
|
|
42
|
+
: 'System',
|
|
43
|
+
} as T & { role?: 'system'; kind?: ChatMessageKind; senderName?: string }) as T & { role: 'system'; kind: ChatMessageKind; senderName: string };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildAssistantChatMessage<T extends Omit<ChatMessage, 'role' | 'kind'> & { role?: 'assistant'; kind?: ChatMessageKind }>(message: T): (T & { role: 'assistant'; kind: ChatMessageKind }) {
|
|
47
|
+
return buildChatMessage({
|
|
48
|
+
...message,
|
|
49
|
+
role: 'assistant',
|
|
50
|
+
kind: message?.kind || 'standard',
|
|
51
|
+
} as T & { role: 'assistant'; kind?: ChatMessageKind }) as T & { role: 'assistant'; kind: ChatMessageKind };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function buildUserChatMessage<T extends Omit<ChatMessage, 'role' | 'kind'> & { role?: 'user'; kind?: ChatMessageKind }>(message: T): (T & { role: 'user'; kind: ChatMessageKind }) {
|
|
55
|
+
return buildChatMessage({
|
|
56
|
+
...message,
|
|
57
|
+
role: 'user',
|
|
58
|
+
kind: message?.kind || 'standard',
|
|
59
|
+
} as T & { role: 'user'; kind?: ChatMessageKind }) as T & { role: 'user'; kind: ChatMessageKind };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function normalizeChatMessage<T extends ChatMessage>(message: T): T {
|
|
63
|
+
return buildChatMessage(message) as T;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function normalizeChatMessages<T extends ChatMessage>(messages: T[] | null | undefined): T[] {
|
|
67
|
+
return (Array.isArray(messages) ? messages : []).map((message) => normalizeChatMessage(message));
|
|
68
|
+
}
|