@adhdev/daemon-core 0.9.63 → 0.9.65

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.
Files changed (35) hide show
  1. package/dist/chat/chat-signatures.d.ts +0 -4
  2. package/dist/chat/chat-signatures.js +0 -4
  3. package/dist/chat/chat-signatures.js.map +1 -1
  4. package/dist/chat/chat-signatures.mjs +0 -4
  5. package/dist/chat/chat-signatures.mjs.map +1 -1
  6. package/dist/chat/subscription-updates.d.ts +0 -2
  7. package/dist/cli-adapters/provider-cli-adapter.d.ts +2 -30
  8. package/dist/cli-adapters/provider-cli-parse.d.ts +1 -8
  9. package/dist/cli-adapters/provider-cli-shared.d.ts +8 -3
  10. package/dist/commands/chat-commands.d.ts +0 -2
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +646 -1687
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.mjs +646 -1687
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/shared-types.d.ts +0 -7
  17. package/node_modules/@adhdev/session-host-core/package.json +1 -1
  18. package/package.json +1 -1
  19. package/src/chat/chat-signatures.ts +0 -8
  20. package/src/chat/subscription-updates.ts +7 -46
  21. package/src/cli-adapters/provider-cli-adapter.d.ts +2 -10
  22. package/src/cli-adapters/provider-cli-adapter.ts +66 -692
  23. package/src/cli-adapters/provider-cli-parse.d.ts +0 -7
  24. package/src/cli-adapters/provider-cli-parse.ts +2 -226
  25. package/src/cli-adapters/provider-cli-shared.d.ts +0 -1
  26. package/src/cli-adapters/provider-cli-shared.ts +8 -3
  27. package/src/commands/chat-commands.ts +54 -338
  28. package/src/daemon/dev-auto-implement.ts +3 -3
  29. package/src/daemon/dev-server.ts +3 -3
  30. package/src/index.d.ts +1 -1
  31. package/src/index.ts +0 -1
  32. package/src/launch.ts +10 -3
  33. package/src/providers/cli-provider-instance.ts +2 -39
  34. package/src/shared-types.d.ts +0 -7
  35. package/src/shared-types.ts +0 -8
@@ -18,8 +18,7 @@ import { LOG, getRecentLogs } from '../logging/logger.js';
18
18
  import { getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
19
19
  import { buildChatMessageSignature } from '../chat/chat-signatures.js';
20
20
  import type { ChatMessage } from '../types.js';
21
- import type { ReadChatCursor, ReadChatSyncMode, SessionTransport } from '../shared-types.js';
22
- import { normalizeChatMessages } from '../providers/chat-message-normalization.js';
21
+ import type { SessionTransport } from '../shared-types.js';
23
22
 
24
23
  const RECENT_SEND_WINDOW_MS = 1200;
25
24
  export const READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25_000;
@@ -168,137 +167,16 @@ function getChatMessageSignature(message: ChatMessage | null | undefined): strin
168
167
  return buildChatMessageSignature(message);
169
168
  }
170
169
 
171
- function normalizeReadChatCursor(args: any): Required<ReadChatCursor> {
172
- const knownMessageCount = Math.max(0, Number(args?.knownMessageCount || 0));
173
- const lastMessageSignature = typeof args?.lastMessageSignature === 'string'
174
- ? args.lastMessageSignature
175
- : '';
176
- const tailLimit = Math.max(0, Number(args?.tailLimit || 0));
177
- return { knownMessageCount, lastMessageSignature, tailLimit };
170
+ function normalizeReadChatTailLimit(args: any): number {
171
+ const value = Number(args?.tailLimit || 0);
172
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
178
173
  }
179
174
 
180
175
  function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[] {
181
176
  const messages = Array.isArray(payload.messages) ? payload.messages as ChatMessage[] : [];
182
- return normalizeChatMessages(messages);
183
- }
184
-
185
- interface ReadChatReplayCollapseInfo {
186
- role: string;
187
- kind: string;
188
- senderName: string;
189
- content: string;
190
- signature: string;
191
- collapsible: boolean;
192
- }
193
-
194
- function normalizeReadChatReplayTextContent(content: ChatMessage['content'] | undefined): string {
195
- return flattenContent(content || '').replace(/\s+/g, ' ').trim();
196
- }
197
-
198
- function getReadChatReplayCollapseInfo(message: ChatMessage | null | undefined): ReadChatReplayCollapseInfo | null {
199
- if (!message) return null;
200
- const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
201
- const kind = typeof message.kind === 'string' ? message.kind.trim().toLowerCase() : 'standard';
202
- const senderName = typeof message.senderName === 'string' ? message.senderName.trim().toLowerCase() : '';
203
- const collapsible = role === 'assistant' || role === 'system';
204
- if (!collapsible) return { role, kind, senderName, content: '', signature: '', collapsible };
205
- const content = normalizeReadChatReplayTextContent(message.content);
206
- return {
207
- role,
208
- kind,
209
- senderName,
210
- content,
211
- signature: `${role}:${kind}:${senderName}:${content}`,
212
- collapsible,
213
- };
214
- }
215
-
216
- function isStableReadChatAssistantAnswerInfo(info: ReadChatReplayCollapseInfo | null): boolean {
217
- if (!info) return false;
218
- if (info.role !== 'assistant') return false;
219
- if (info.kind && info.kind !== 'standard') return false;
220
- if (info.content.length < 160) return false;
221
-
222
- // A provider may surface expanded command output as a standard assistant bubble
223
- // (for example Claude Code's "Bash command ..." block). That is live work output,
224
- // not a stable final answer. Treating it as a terminal answer would hide the
225
- // real final response and violate read_chat fidelity.
226
- if (/^(bash|shell|terminal) command\b/i.test(info.content)) return false;
227
- return true;
228
- }
229
-
230
- function isReplayedAssistantAnswerAfterStableAnswerInfo(
231
- info: ReadChatReplayCollapseInfo | null,
232
- stableContent: string,
233
- ): boolean {
234
- if (!info || !stableContent) return false;
235
- if (info.role !== 'assistant') return false;
236
- if (info.kind && info.kind !== 'standard') return false;
237
- const content = info.content;
238
- if (content.length < 80 || stableContent.length < 80) return false;
239
- return content === stableContent || content.startsWith(stableContent) || stableContent.startsWith(content);
177
+ return messages;
240
178
  }
241
179
 
242
- export function collapseReplayDuplicatesFromReadChat(messages: ChatMessage[]): ChatMessage[] {
243
- const collapsed: ChatMessage[] = [];
244
- const replaySignaturesInCurrentTurn = new Set<string>();
245
- let stableAssistantAnswerContentInCurrentTurn = '';
246
- let stableAssistantAnswerCollapsedIndex = -1;
247
- let stableAssistantAnswerHadInterveningActivity = false;
248
- let previousReplaySignature = '';
249
-
250
- for (const message of messages) {
251
- const info = getReadChatReplayCollapseInfo(message);
252
- if (info?.role === 'user') {
253
- replaySignaturesInCurrentTurn.clear();
254
- stableAssistantAnswerContentInCurrentTurn = '';
255
- stableAssistantAnswerCollapsedIndex = -1;
256
- stableAssistantAnswerHadInterveningActivity = false;
257
- previousReplaySignature = '';
258
- }
259
-
260
- if (info?.collapsible && info.signature) {
261
- if (previousReplaySignature === info.signature) continue;
262
- if (replaySignaturesInCurrentTurn.has(info.signature)) continue;
263
- if (isReplayedAssistantAnswerAfterStableAnswerInfo(info, stableAssistantAnswerContentInCurrentTurn)) {
264
- const isAdjacentFullerAssistantAnswer =
265
- info.role === 'assistant'
266
- && (!info.kind || info.kind === 'standard')
267
- && stableAssistantAnswerCollapsedIndex >= 0
268
- && stableAssistantAnswerCollapsedIndex === collapsed.length - 1
269
- && !stableAssistantAnswerHadInterveningActivity
270
- && info.content.length > stableAssistantAnswerContentInCurrentTurn.length
271
- && info.content.startsWith(stableAssistantAnswerContentInCurrentTurn);
272
- if (isAdjacentFullerAssistantAnswer) {
273
- collapsed[stableAssistantAnswerCollapsedIndex] = message;
274
- replaySignaturesInCurrentTurn.add(info.signature);
275
- stableAssistantAnswerContentInCurrentTurn = info.content;
276
- previousReplaySignature = info.signature;
277
- }
278
- continue;
279
- }
280
- }
281
-
282
- collapsed.push(message);
283
- previousReplaySignature = info?.collapsible ? info.signature : '';
284
- if (info?.collapsible && info.signature) {
285
- replaySignaturesInCurrentTurn.add(info.signature);
286
- }
287
- if (isStableReadChatAssistantAnswerInfo(info)) {
288
- stableAssistantAnswerContentInCurrentTurn = info?.content || '';
289
- stableAssistantAnswerCollapsedIndex = collapsed.length - 1;
290
- stableAssistantAnswerHadInterveningActivity = false;
291
- } else if (
292
- stableAssistantAnswerContentInCurrentTurn
293
- && info?.role === 'assistant'
294
- && (!info.kind || info.kind !== 'standard')
295
- ) {
296
- stableAssistantAnswerHadInterveningActivity = true;
297
- }
298
- }
299
-
300
- return collapsed;
301
- }
302
180
 
303
181
  function deriveHistoryDedupKey(message: ChatMessage & { _unitKey?: string; _turnKey?: string }): string | undefined {
304
182
  const unitKey = typeof message._unitKey === 'string' ? message._unitKey.trim() : '';
@@ -334,125 +212,15 @@ function toHistoryPersistedMessages(messages: ChatMessage[]): Array<{
334
212
  }));
335
213
  }
336
214
 
337
- function findLastMessageIndexBySignature(messages: ChatMessage[], signature: string): number {
338
- if (!signature) return -1;
339
- for (let index = messages.length - 1; index >= 0; index -= 1) {
340
- if (getChatMessageSignature(messages[index]) === signature) {
341
- return index;
342
- }
343
- }
344
- return -1;
345
- }
346
-
347
- function buildBoundedTailSync(messages: ChatMessage[], cursor: Required<ReadChatCursor>): {
348
- syncMode: ReadChatSyncMode;
349
- replaceFrom: number;
215
+ function buildFullTail(messages: ChatMessage[], tailLimit: number): {
350
216
  messages: ChatMessage[];
351
217
  totalMessages: number;
352
- lastMessageSignature: string;
353
218
  } {
354
219
  const totalMessages = messages.length;
355
- const tailMessages = cursor.tailLimit > 0 && totalMessages > cursor.tailLimit
356
- ? messages.slice(-cursor.tailLimit)
357
- : messages;
220
+ const tailMessages = tailLimit > 0 ? messages.slice(-tailLimit) : messages;
358
221
  return {
359
- syncMode: 'full',
360
- replaceFrom: 0,
361
222
  messages: tailMessages,
362
223
  totalMessages,
363
- lastMessageSignature: getChatMessageSignature(messages[totalMessages - 1]),
364
- };
365
- }
366
-
367
- function computeReadChatSync(messages: ChatMessage[], cursor: Required<ReadChatCursor>): {
368
- syncMode: ReadChatSyncMode;
369
- replaceFrom: number;
370
- messages: ChatMessage[];
371
- totalMessages: number;
372
- lastMessageSignature: string;
373
- } {
374
- const totalMessages = messages.length;
375
- const lastMessageSignature = getChatMessageSignature(messages[totalMessages - 1]);
376
- const { knownMessageCount, lastMessageSignature: knownSignature } = cursor;
377
-
378
- if (!knownMessageCount || !knownSignature) {
379
- return {
380
- syncMode: 'full',
381
- replaceFrom: 0,
382
- messages,
383
- totalMessages,
384
- lastMessageSignature,
385
- };
386
- }
387
-
388
- if (knownMessageCount > totalMessages) {
389
- return {
390
- syncMode: 'full',
391
- replaceFrom: 0,
392
- messages,
393
- totalMessages,
394
- lastMessageSignature,
395
- };
396
- }
397
-
398
- if (knownMessageCount === totalMessages && knownSignature === lastMessageSignature) {
399
- return {
400
- syncMode: 'noop',
401
- replaceFrom: totalMessages,
402
- messages: [],
403
- totalMessages,
404
- lastMessageSignature,
405
- };
406
- }
407
-
408
- if (cursor.tailLimit > 0 && knownSignature === lastMessageSignature) {
409
- const requestedTailCount = Math.min(totalMessages, cursor.tailLimit);
410
- if (knownMessageCount >= requestedTailCount) {
411
- return {
412
- syncMode: 'noop',
413
- replaceFrom: totalMessages,
414
- messages: [],
415
- totalMessages,
416
- lastMessageSignature,
417
- };
418
- }
419
- return buildBoundedTailSync(messages, cursor);
420
- }
421
-
422
- if (knownMessageCount < totalMessages) {
423
- const anchorSignature = getChatMessageSignature(messages[knownMessageCount - 1]);
424
- if (anchorSignature === knownSignature) {
425
- return {
426
- syncMode: 'append',
427
- replaceFrom: knownMessageCount,
428
- messages: messages.slice(knownMessageCount),
429
- totalMessages,
430
- lastMessageSignature,
431
- };
432
- }
433
-
434
- if (cursor.tailLimit > 0) {
435
- const signatureIndex = findLastMessageIndexBySignature(messages, knownSignature);
436
- if (signatureIndex >= 0) {
437
- return {
438
- syncMode: 'append',
439
- replaceFrom: knownMessageCount,
440
- messages: messages.slice(signatureIndex + 1),
441
- totalMessages,
442
- lastMessageSignature,
443
- };
444
- }
445
- return buildBoundedTailSync(messages, cursor);
446
- }
447
- }
448
-
449
- const replaceFrom = Math.max(0, Math.min(knownMessageCount - 1, totalMessages));
450
- return {
451
- syncMode: replaceFrom === 0 ? 'full' : 'replace_tail',
452
- replaceFrom,
453
- messages: replaceFrom === 0 ? messages : messages.slice(replaceFrom),
454
- totalMessages,
455
- lastMessageSignature,
456
224
  };
457
225
  }
458
226
 
@@ -492,31 +260,13 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
492
260
  } catch (error: any) {
493
261
  return { success: false, error: error?.message || String(error) };
494
262
  }
495
- const messages = collapseReplayDuplicatesFromReadChat(normalizeReadChatMessages(validatedPayload));
496
- const cursor = normalizeReadChatCursor(args);
497
- if (!cursor.knownMessageCount && !cursor.lastMessageSignature && cursor.tailLimit > 0 && messages.length > cursor.tailLimit) {
498
- const tailMessages = messages.slice(-cursor.tailLimit);
499
- const lastMessageSignature = getChatMessageSignature(tailMessages[tailMessages.length - 1]);
500
- return {
501
- success: true,
502
- ...validatedPayload,
503
- messages: tailMessages,
504
- syncMode: 'full',
505
- replaceFrom: 0,
506
- totalMessages: messages.length,
507
- lastMessageSignature,
508
- ...(debugReadChat ? { debugReadChat } : {}),
509
- };
510
- }
511
- const sync = computeReadChatSync(messages, cursor);
263
+ const messages = normalizeReadChatMessages(validatedPayload);
264
+ const sync = buildFullTail(messages, normalizeReadChatTailLimit(args));
512
265
  return {
513
266
  success: true,
514
267
  ...validatedPayload,
515
268
  messages: sync.messages,
516
- syncMode: sync.syncMode,
517
- replaceFrom: sync.replaceFrom,
518
269
  totalMessages: sync.totalMessages,
519
- lastMessageSignature: sync.lastMessageSignature,
520
270
  ...(debugReadChat ? { debugReadChat } : {}),
521
271
  };
522
272
  }
@@ -776,10 +526,6 @@ export async function handleGetChatDebugBundle(h: CommandHelpers, args: any): Pr
776
526
  status: readResult.status,
777
527
  title: readResult.title,
778
528
  totalMessages: readResult.totalMessages,
779
- returnedMessages: Array.isArray(readResult.messages) ? readResult.messages.length : undefined,
780
- syncMode: readResult.syncMode,
781
- replaceFrom: readResult.replaceFrom,
782
- lastMessageSignature: readResult.lastMessageSignature,
783
529
  providerSessionId: readResult.providerSessionId,
784
530
  transcriptAuthority: readResult.transcriptAuthority,
785
531
  coverage: readResult.coverage,
@@ -903,25 +649,13 @@ function toNonNegativeNumber(value: any): number {
903
649
  }
904
650
 
905
651
  function getCliVisibleTranscriptCount(adapter: any): number {
906
- const adapterStatus = adapter?.getStatus?.() || {};
907
- const adapterMessages = Array.isArray(adapterStatus.messages) ? adapterStatus.messages : [];
908
- let parsedRecord: Record<string, any> | null = null;
909
- if (typeof adapter?.getScriptParsedStatus === 'function') {
910
- try {
911
- const parsed = parseMaybeJson(adapter.getScriptParsedStatus());
912
- parsedRecord = parsed && typeof parsed === 'object' ? parsed as Record<string, any> : null;
913
- } catch {
914
- parsedRecord = null;
915
- }
652
+ if (typeof adapter?.getScriptParsedStatus !== 'function') return 0;
653
+ try {
654
+ const parsed = parseMaybeJson(adapter.getScriptParsedStatus());
655
+ return Array.isArray(parsed?.messages) ? parsed.messages.length : 0;
656
+ } catch {
657
+ return 0;
916
658
  }
917
- const parsedMessages = Array.isArray(parsedRecord?.messages) ? parsedRecord.messages : [];
918
- if (!parsedRecord) return adapterMessages.length;
919
- const parsedIsProviderAuthoritative = parsedRecord.transcriptAuthority === 'provider'
920
- || parsedRecord.coverage === 'full';
921
- const shouldPreferAdapterMessages = !parsedIsProviderAuthoritative
922
- && adapterMessages.length > 0
923
- && adapterMessages.length > parsedMessages.length;
924
- return shouldPreferAdapterMessages ? adapterMessages.length : parsedMessages.length;
925
659
  }
926
660
 
927
661
  async function getStableExtensionBaseline(h: CommandHelpers): Promise<any | null> {
@@ -994,74 +728,56 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
994
728
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
995
729
  if (adapter) {
996
730
  _log(`${transport} adapter: ${adapter.cliType}`);
731
+ if (typeof adapter.getScriptParsedStatus !== 'function') {
732
+ return { success: false, error: `${transport} adapter parseSession unavailable` };
733
+ }
997
734
  let parsedStatus: any = null;
998
- if (typeof adapter.getScriptParsedStatus === 'function') {
999
- try {
1000
- parsedStatus = parseMaybeJson(adapter.getScriptParsedStatus());
1001
- } catch (error: any) {
1002
- return { success: false, error: error?.message || String(error) };
1003
- }
735
+ try {
736
+ parsedStatus = parseMaybeJson(adapter.getScriptParsedStatus());
737
+ } catch (error: any) {
738
+ return { success: false, error: error?.message || String(error) };
1004
739
  }
1005
740
  const parsedRecord = parsedStatus && typeof parsedStatus === 'object'
1006
741
  ? parsedStatus as Record<string, any>
1007
742
  : null;
1008
- const adapterStatus = adapter.getStatus();
1009
- const parsedIsProviderAuthoritative = parsedRecord?.transcriptAuthority === 'provider'
1010
- || parsedRecord?.coverage === 'full';
1011
- const shouldPreferAdapterMessages =
1012
- !parsedIsProviderAuthoritative
1013
- && Array.isArray(adapterStatus.messages)
1014
- && adapterStatus.messages.length > 0
1015
- && Array.isArray(parsedRecord?.messages)
1016
- && adapterStatus.messages.length > parsedRecord.messages.length;
1017
- const parsedShowsApproval = hasNonEmptyModalButtons(parsedRecord?.activeModal)
1018
- && parsedRecord?.status === 'waiting_approval';
1019
- const status = parsedRecord
1020
- ? {
1021
- ...parsedRecord,
1022
- messages: shouldPreferAdapterMessages ? adapterStatus.messages : parsedRecord.messages,
1023
- status: parsedShowsApproval
1024
- ? parsedRecord.status
1025
- : (adapterStatus.status !== 'idle'
1026
- ? adapterStatus.status
1027
- : (parsedRecord.status || adapterStatus.status)),
1028
- activeModal: parsedRecord.activeModal || adapterStatus.activeModal,
1029
- }
1030
- : adapterStatus;
1031
-
1032
- const title = typeof parsedRecord?.title === 'string' ? parsedRecord.title : undefined;
1033
- const providerSessionId = typeof parsedRecord?.providerSessionId === 'string'
743
+ if (!parsedRecord || !Array.isArray(parsedRecord.messages)) {
744
+ return { success: false, error: `${transport} parser did not return messages` };
745
+ }
746
+ const adapterStatus = typeof adapter.getStatus === 'function'
747
+ ? adapter.getStatus()
748
+ : {};
749
+ const title = typeof parsedRecord.title === 'string' ? parsedRecord.title : undefined;
750
+ const providerSessionId = typeof parsedRecord.providerSessionId === 'string'
1034
751
  ? parsedRecord.providerSessionId
1035
752
  : undefined;
1036
- const transcriptAuthority = parsedRecord?.transcriptAuthority === 'provider' || parsedRecord?.transcriptAuthority === 'daemon'
753
+ const transcriptAuthority = parsedRecord.transcriptAuthority === 'provider' || parsedRecord.transcriptAuthority === 'daemon'
1037
754
  ? parsedRecord.transcriptAuthority
1038
755
  : undefined;
1039
- const coverage = parsedRecord?.coverage === 'full' || parsedRecord?.coverage === 'tail' || parsedRecord?.coverage === 'current-turn'
756
+ const coverage = parsedRecord.coverage === 'full' || parsedRecord.coverage === 'tail' || parsedRecord.coverage === 'current-turn'
1040
757
  ? parsedRecord.coverage
1041
758
  : undefined;
1042
- if (status) {
1043
- LOG.debug('Command', `[read_chat] cli-like resolved provider=${adapter.cliType} target=${String(args?.targetSessionId || '')} adapterStatus=${String(adapterStatus.status || '')} parsedStatus=${String(parsedRecord?.status || '')} shouldPreferAdapterMessages=${String(shouldPreferAdapterMessages)} adapterMsgCount=${Array.isArray(adapterStatus.messages) ? adapterStatus.messages.length : 0} parsedMsgCount=${Array.isArray(parsedRecord?.messages) ? parsedRecord.messages.length : 0} returnedMsgCount=${Array.isArray((status as any).messages) ? (status as any).messages.length : 0}`);
1044
- return buildReadChatCommandResult({
1045
- messages: (status as any).messages || [],
1046
- status: status.status,
1047
- activeModal: status.activeModal,
1048
- debugReadChat: {
1049
- provider: adapter.cliType,
1050
- targetSessionId: String(args?.targetSessionId || ''),
1051
- adapterStatus: String(adapterStatus.status || ''),
1052
- parsedStatus: String(parsedRecord?.status || ''),
1053
- returnedStatus: String(status.status || ''),
1054
- shouldPreferAdapterMessages,
1055
- adapterMsgCount: Array.isArray(adapterStatus.messages) ? adapterStatus.messages.length : 0,
1056
- parsedMsgCount: Array.isArray(parsedRecord?.messages) ? parsedRecord.messages.length : 0,
1057
- returnedMsgCount: Array.isArray((status as any).messages) ? (status as any).messages.length : 0,
1058
- },
1059
- ...(title ? { title } : {}),
1060
- ...(providerSessionId ? { providerSessionId } : {}),
1061
- ...(transcriptAuthority ? { transcriptAuthority } : {}),
1062
- ...(coverage ? { coverage } : {}),
1063
- }, args);
1064
- }
759
+ const activeModal = parsedRecord.activeModal ?? parsedRecord.modal ?? null;
760
+ const returnedStatus = parsedRecord.status || 'idle';
761
+ LOG.debug('Command', `[read_chat] cli-like parsed provider=${adapter.cliType} target=${String(args?.targetSessionId || '')} adapterStatus=${String(adapterStatus.status || '')} parsedStatus=${String(parsedRecord.status || '')} parsedMsgCount=${parsedRecord.messages.length}`);
762
+ return buildReadChatCommandResult({
763
+ messages: parsedRecord.messages,
764
+ status: returnedStatus,
765
+ activeModal,
766
+ debugReadChat: {
767
+ provider: adapter.cliType,
768
+ targetSessionId: String(args?.targetSessionId || ''),
769
+ adapterStatus: String(adapterStatus.status || ''),
770
+ parsedStatus: String(parsedRecord.status || ''),
771
+ returnedStatus: String(returnedStatus || ''),
772
+ shouldPreferAdapterMessages: false,
773
+ parsedMsgCount: parsedRecord.messages.length,
774
+ returnedMsgCount: parsedRecord.messages.length,
775
+ },
776
+ ...(title ? { title } : {}),
777
+ ...(providerSessionId ? { providerSessionId } : {}),
778
+ ...(transcriptAuthority ? { transcriptAuthority } : {}),
779
+ ...(coverage ? { coverage } : {}),
780
+ }, args);
1065
781
  }
1066
782
  return { success: false, error: `${transport} adapter not found` };
1067
783
  }
@@ -1098,7 +1098,7 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
1098
1098
  lines.push('');
1099
1099
 
1100
1100
  const funcToFile: Record<string, string> = {
1101
- parseOutput: 'parse_output.js',
1101
+ parseSession: 'parse_session.js',
1102
1102
  detectStatus: 'detect_status.js',
1103
1103
  parseApproval: 'parse_approval.js',
1104
1104
  };
@@ -1200,7 +1200,7 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
1200
1200
  lines.push('');
1201
1201
  lines.push('| Function | Input | Return |');
1202
1202
  lines.push('|---|---|---|');
1203
- lines.push('| `parseOutput` | `{ buffer, rawBuffer, recentBuffer, screenText, messages, partialResponse }` | `{ id, status, title, messages, activeModal }` |');
1203
+ lines.push('| `parseSession` | `{ buffer, rawBuffer, recentBuffer, screenText, messages, partialResponse }` | `{ id, status, title, messages, activeModal }` |');
1204
1204
  lines.push('| `detectStatus` | `{ tail, screenText, rawBuffer }` | `idle`, `generating`, `waiting_approval`, or `error` |');
1205
1205
  lines.push('| `parseApproval` | `{ buffer, rawBuffer, tail }` | `{ message, buttons }` or `null` |');
1206
1206
  lines.push('');
@@ -1434,7 +1434,7 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
1434
1434
 
1435
1435
  lines.push('## Required Validation');
1436
1436
  lines.push('1. Confirm `detectStatus` changes sensibly between startup, generating, approval, and idle.');
1437
- lines.push('2. Confirm `parseOutput` produces a stable transcript without duplicating past turns when the PTY redraws.');
1437
+ lines.push('2. Confirm `parseSession` produces a stable transcript without duplicating past turns when the PTY redraws.');
1438
1438
  lines.push('3. Confirm the latest assistant message streams through `partialResponse` while generation is in progress.');
1439
1439
  lines.push('4. Confirm approval parsing returns meaningful button labels when the CLI requests permission.');
1440
1440
  lines.push('5. Confirm the Python file was actually created and executed, not just described in chat text.');
@@ -1367,7 +1367,7 @@ export class DevServer implements DevServerContext {
1367
1367
  lines.push('');
1368
1368
 
1369
1369
  const funcToFile: Record<string, string> = {
1370
- parseOutput: 'parse_output.js',
1370
+ parseSession: 'parse_session.js',
1371
1371
  detectStatus: 'detect_status.js',
1372
1372
  parseApproval: 'parse_approval.js',
1373
1373
  };
@@ -1469,7 +1469,7 @@ export class DevServer implements DevServerContext {
1469
1469
  lines.push('');
1470
1470
  lines.push('| Function | Input | Return |');
1471
1471
  lines.push('|---|---|---|');
1472
- lines.push('| `parseOutput` | `{ buffer, rawBuffer, recentBuffer, screenText, messages, partialResponse }` | `{ id, status, title, messages, activeModal }` |');
1472
+ lines.push('| `parseSession` | `{ buffer, rawBuffer, recentBuffer, screenText, messages, partialResponse }` | `{ id, status, title, messages, activeModal }` |');
1473
1473
  lines.push('| `detectStatus` | `{ tail, screenText, rawBuffer }` | `idle`, `generating`, `waiting_approval`, or `error` |');
1474
1474
  lines.push('| `parseApproval` | `{ buffer, rawBuffer, tail }` | `{ message, buttons }` or `null` |');
1475
1475
  lines.push('');
@@ -1568,7 +1568,7 @@ export class DevServer implements DevServerContext {
1568
1568
 
1569
1569
  lines.push('## Required Validation');
1570
1570
  lines.push('1. Confirm `detectStatus` changes sensibly between startup, generating, approval, and idle.');
1571
- lines.push('2. Confirm `parseOutput` produces a stable transcript without duplicating past turns when the PTY redraws.');
1571
+ lines.push('2. Confirm `parseSession` produces a stable transcript without duplicating past turns when the PTY redraws.');
1572
1572
  lines.push('3. Confirm the latest assistant message streams through `partialResponse` while generation is in progress.');
1573
1573
  lines.push('4. Confirm approval parsing returns meaningful button labels when the CLI requests permission.');
1574
1574
  lines.push('5. Confirm the Python file was actually created and executed, not just described in chat text.');
package/src/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * Core logic for daemon: CDP, Provider, IDE detection, CLI/ACP adapters and more.
5
5
  */
6
6
  export type { ChatBubbleState, ChatMessage, ExtensionInfo, CommandResult as CoreCommandResult, ProviderConfig, DaemonEvent, StatusResponse, SystemInfo, DetectedIde, ProviderInfo, AgentEntry, } from './types.js';
7
- export type { SessionEntry, CompactSessionEntry, CompactDaemonEntry, SessionTransport, SessionKind, SessionCapability, AgentSessionStream, ReadChatCursor, ReadChatSyncMode, ReadChatSyncResult, TransportTopic, SessionChatTailSubscriptionParams, SessionRuntimeOutputSubscriptionParams, MachineRuntimeSubscriptionParams, SessionHostDiagnosticsSubscriptionParams, SessionModalSubscriptionParams, DaemonMetadataSubscriptionParams, SessionChatTailUpdate, SessionRuntimeOutputUpdate, MachineRuntimeUpdate, SessionHostDiagnosticsUpdate, SessionModalUpdate, DaemonMetadataUpdate, TopicUpdateEnvelope, SubscribeRequest, UnsubscribeRequest, StandaloneWsStatusPayload, AvailableProviderInfo, AcpConfigOption, AcpMode, ProviderControlSchema, StatusReportPayload, MachineInfo, SessionHostDiagnosticsSnapshot, SessionHostRecord, SessionHostWriteOwner, SessionHostAttachedClient, SessionHostLogEntry, SessionHostRequestTrace, SessionHostRuntimeTransition, DetectedIdeInfo, WorkspaceEntry, ProviderSummaryItem, ProviderSummaryMetadata, ProviderState, ProviderStatus, ProviderErrorReason, ActiveChatData, IdeProviderState, CliProviderState, AcpProviderState, ExtensionProviderState, } from './shared-types.js';
7
+ export type { SessionEntry, CompactSessionEntry, CompactDaemonEntry, SessionTransport, SessionKind, SessionCapability, AgentSessionStream, ReadChatCursor, ReadChatSyncResult, TransportTopic, SessionChatTailSubscriptionParams, SessionRuntimeOutputSubscriptionParams, MachineRuntimeSubscriptionParams, SessionHostDiagnosticsSubscriptionParams, SessionModalSubscriptionParams, DaemonMetadataSubscriptionParams, SessionChatTailUpdate, SessionRuntimeOutputUpdate, MachineRuntimeUpdate, SessionHostDiagnosticsUpdate, SessionModalUpdate, DaemonMetadataUpdate, TopicUpdateEnvelope, SubscribeRequest, UnsubscribeRequest, StandaloneWsStatusPayload, AvailableProviderInfo, AcpConfigOption, AcpMode, ProviderControlSchema, StatusReportPayload, MachineInfo, SessionHostDiagnosticsSnapshot, SessionHostRecord, SessionHostWriteOwner, SessionHostAttachedClient, SessionHostLogEntry, SessionHostRequestTrace, SessionHostRuntimeTransition, DetectedIdeInfo, WorkspaceEntry, ProviderSummaryItem, ProviderSummaryMetadata, ProviderState, ProviderStatus, ProviderErrorReason, ActiveChatData, IdeProviderState, CliProviderState, AcpProviderState, ExtensionProviderState, } from './shared-types.js';
8
8
  import type { RuntimeWriteOwner as _RuntimeWriteOwner } from './shared-types-extra.js';
9
9
  import type { RuntimeAttachedClient as _RuntimeAttachedClient } from './shared-types-extra.js';
10
10
  import type { RecentLaunchEntry as _RecentLaunchEntry } from './shared-types.js';
package/src/index.ts CHANGED
@@ -35,7 +35,6 @@ export type {
35
35
  SessionCapability,
36
36
  AgentSessionStream,
37
37
  ReadChatCursor,
38
- ReadChatSyncMode,
39
38
  ReadChatSyncResult,
40
39
  TransportTopic,
41
40
  SessionChatTailSubscriptionParams,
package/src/launch.ts CHANGED
@@ -433,12 +433,19 @@ export async function launchWithCdp(options: LaunchOptions = {}): Promise<Launch
433
433
  }
434
434
  }
435
435
 
436
+ if (!cdpReady) {
437
+ return {
438
+ success: false, ideId: targetIde.id, ideName: targetIde.displayName,
439
+ port, action: 'failed',
440
+ message: '',
441
+ error: `${targetIde.displayName} launched but CDP did not become available on port ${port}`,
442
+ };
443
+ }
444
+
436
445
  return {
437
446
  success: true, ideId: targetIde.id, ideName: targetIde.displayName,
438
447
  port, action: alreadyRunning ? 'restarted' : 'started',
439
- message: cdpReady
440
- ? `${targetIde.displayName} launched with CDP on port ${port}`
441
- : `${targetIde.displayName} launched (CDP may take a moment to initialize)`,
448
+ message: `${targetIde.displayName} launched with CDP on port ${port}`,
442
449
  };
443
450
  } catch (e: any) {
444
451
  return {
@@ -378,9 +378,7 @@ export class CliProviderInstance implements ProviderInstance {
378
378
  this.maybeAppendRuntimeRecoveryMessage(runtime);
379
379
  let parsedMessages = Array.isArray(parsedStatus?.messages)
380
380
  ? parsedStatus.messages
381
- : (parseErrorMessage
382
- ? normalizeChatMessages(Array.isArray(adapterStatus.messages) ? adapterStatus.messages as any : [])
383
- : []);
381
+ : [];
384
382
  const historyMessageCount = Number.isFinite(parsedStatus?.historyMessageCount)
385
383
  ? Math.max(0, Number(parsedStatus.historyMessageCount))
386
384
  : null;
@@ -389,25 +387,6 @@ export class CliProviderInstance implements ProviderInstance {
389
387
  ? parsedMessages.slice(-historyMessageCount)
390
388
  : [];
391
389
  }
392
- // committedMessages (adapterStatus.messages) is the adapter's accumulated
393
- // conversation history — use it as a floor to prevent history from disappearing.
394
- //
395
- // waiting_approval: always override — the approval dialog fills the terminal,
396
- // pushing prior conversation out of view; parsedMessages will be partial or empty
397
- // regardless of historyMessageCount.
398
- //
399
- // Other active states (generating, long_generating, error): apply floor only
400
- // when the script has not explicitly windowed via historyMessageCount, so
401
- // intentional windowing is preserved. Excludes idle — scripts may legitimately
402
- // return messages:[] when the CLI exits or a new session begins.
403
- const committedMessages = Array.isArray(adapterStatus.messages) ? adapterStatus.messages : [];
404
- const isActiveNonIdle = adapterStatus.status !== 'idle';
405
- const shouldApplyCommittedFloor = parsedMessages.length < committedMessages.length
406
- && (adapterStatus.status === 'waiting_approval'
407
- || (isActiveNonIdle && historyMessageCount === null));
408
- if (shouldApplyCommittedFloor) {
409
- parsedMessages = normalizeChatMessages(committedMessages as any);
410
- }
411
390
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
412
391
  const canonicalBackedHistory = this.syncCanonicalSavedHistoryIfNeeded();
413
392
 
@@ -516,13 +495,9 @@ export class CliProviderInstance implements ProviderInstance {
516
495
  const autoApproveActive = adapterStatus.status === 'waiting_approval' && this.shouldAutoApprove();
517
496
  const visibleStatus = autoApproveActive ? 'generating' : adapterStatus.status;
518
497
  const runtime = this.adapter.getRuntimeMetadata();
519
- const lastCommittedMessageActivityAt = typeof this.adapter.getLastCommittedMessageActivityAt === 'function'
520
- ? this.adapter.getLastCommittedMessageActivityAt()
521
- : 0;
522
498
  return {
523
499
  id: this.instanceId,
524
500
  status: visibleStatus,
525
- lastMessageAt: lastCommittedMessageActivityAt || undefined,
526
501
  runtimeLifecycle: runtime?.lifecycle ?? null,
527
502
  runtimeSurfaceKind: runtime?.surfaceKind,
528
503
  runtimeRestoredFromStorage: runtime?.restoredFromStorage === true,
@@ -643,7 +618,7 @@ export class CliProviderInstance implements ProviderInstance {
643
618
  const chatTitle = `${this.provider.name} · ${dirName}`;
644
619
  const partial = this.adapter.getPartialResponse();
645
620
  const progressFingerprint = newStatus === 'generating'
646
- ? `${partial || ''}::${adapterStatus.messages.at(-1)?.content || ''}`.slice(-2000)
621
+ ? `${partial || ''}`.slice(-2000)
647
622
  : undefined;
648
623
 
649
624
  const previousStatus = this.lastStatus;
@@ -1136,18 +1111,6 @@ export class CliProviderInstance implements ProviderInstance {
1136
1111
  receivedAt: message.receivedAt,
1137
1112
  }));
1138
1113
  this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
1139
- if (restoredHistory.messages.length > 0) {
1140
- this.adapter.seedCommittedMessages(
1141
- restoredHistory.messages.map((message) => ({
1142
- role: message.role,
1143
- content: message.content,
1144
- timestamp: message.receivedAt,
1145
- receivedAt: message.receivedAt,
1146
- kind: message.kind,
1147
- senderName: message.senderName,
1148
- })),
1149
- );
1150
- }
1151
1114
  }
1152
1115
 
1153
1116