@adhdev/daemon-core 0.9.39 → 0.9.40
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 +72 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +72 -64
- package/dist/index.mjs.map +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-parse.ts +25 -15
- package/src/commands/chat-commands.ts +65 -56
package/package.json
CHANGED
|
@@ -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) {
|