@adhdev/daemon-core 0.9.64 → 0.9.66

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 -1712
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.mjs +646 -1712
  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 -366
  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
- };
177
+ return messages;
214
178
  }
215
179
 
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);
240
- }
241
-
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,153 +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 isReadChatConversationAnchorMessage(message: ChatMessage | null | undefined): boolean {
348
- if (!message) return false;
349
- const role = String(message.role || '').trim().toLowerCase();
350
- if (role !== 'user' && role !== 'assistant') return false;
351
- const kind = String(message.kind || 'standard').trim().toLowerCase();
352
- return !kind || kind === 'standard';
353
- }
354
-
355
- function buildVisibleReadChatTailMessages(messages: ChatMessage[], tailLimit: number): ChatMessage[] {
356
- const totalMessages = messages.length;
357
- if (tailLimit <= 0 || totalMessages <= tailLimit) return messages;
358
-
359
- const tailMessages = messages.slice(-tailLimit);
360
- if (tailMessages.some(isReadChatConversationAnchorMessage)) return tailMessages;
361
-
362
- const hiddenMessages = messages.slice(0, totalMessages - tailLimit);
363
- const anchors: ChatMessage[] = [];
364
- const seenRoles = new Set<string>();
365
- for (let index = hiddenMessages.length - 1; index >= 0 && anchors.length < 2; index -= 1) {
366
- const message = hiddenMessages[index];
367
- if (!isReadChatConversationAnchorMessage(message)) continue;
368
- const role = String(message.role || '').trim().toLowerCase();
369
- if (seenRoles.has(role)) continue;
370
- seenRoles.add(role);
371
- anchors.unshift(message);
372
- }
373
-
374
- return anchors.length > 0 ? [...anchors, ...tailMessages] : tailMessages;
375
- }
376
-
377
- function buildBoundedTailSync(messages: ChatMessage[], cursor: Required<ReadChatCursor>): {
378
- syncMode: ReadChatSyncMode;
379
- replaceFrom: number;
215
+ function buildFullTail(messages: ChatMessage[], tailLimit: number): {
380
216
  messages: ChatMessage[];
381
217
  totalMessages: number;
382
- lastMessageSignature: string;
383
218
  } {
384
219
  const totalMessages = messages.length;
385
- const tailMessages = buildVisibleReadChatTailMessages(messages, cursor.tailLimit);
220
+ const tailMessages = tailLimit > 0 ? messages.slice(-tailLimit) : messages;
386
221
  return {
387
- syncMode: 'full',
388
- replaceFrom: 0,
389
222
  messages: tailMessages,
390
223
  totalMessages,
391
- lastMessageSignature: getChatMessageSignature(messages[totalMessages - 1]),
392
- };
393
- }
394
-
395
- function computeReadChatSync(messages: ChatMessage[], cursor: Required<ReadChatCursor>): {
396
- syncMode: ReadChatSyncMode;
397
- replaceFrom: number;
398
- messages: ChatMessage[];
399
- totalMessages: number;
400
- lastMessageSignature: string;
401
- } {
402
- const totalMessages = messages.length;
403
- const lastMessageSignature = getChatMessageSignature(messages[totalMessages - 1]);
404
- const { knownMessageCount, lastMessageSignature: knownSignature } = cursor;
405
-
406
- if (!knownMessageCount || !knownSignature) {
407
- return {
408
- syncMode: 'full',
409
- replaceFrom: 0,
410
- messages,
411
- totalMessages,
412
- lastMessageSignature,
413
- };
414
- }
415
-
416
- if (knownMessageCount > totalMessages) {
417
- return {
418
- syncMode: 'full',
419
- replaceFrom: 0,
420
- messages,
421
- totalMessages,
422
- lastMessageSignature,
423
- };
424
- }
425
-
426
- if (knownMessageCount === totalMessages && knownSignature === lastMessageSignature) {
427
- return {
428
- syncMode: 'noop',
429
- replaceFrom: totalMessages,
430
- messages: [],
431
- totalMessages,
432
- lastMessageSignature,
433
- };
434
- }
435
-
436
- if (cursor.tailLimit > 0 && knownSignature === lastMessageSignature) {
437
- const requestedTailCount = Math.min(totalMessages, cursor.tailLimit);
438
- if (knownMessageCount >= requestedTailCount) {
439
- return {
440
- syncMode: 'noop',
441
- replaceFrom: totalMessages,
442
- messages: [],
443
- totalMessages,
444
- lastMessageSignature,
445
- };
446
- }
447
- return buildBoundedTailSync(messages, cursor);
448
- }
449
-
450
- if (knownMessageCount < totalMessages) {
451
- const anchorSignature = getChatMessageSignature(messages[knownMessageCount - 1]);
452
- if (anchorSignature === knownSignature) {
453
- return {
454
- syncMode: 'append',
455
- replaceFrom: knownMessageCount,
456
- messages: messages.slice(knownMessageCount),
457
- totalMessages,
458
- lastMessageSignature,
459
- };
460
- }
461
-
462
- if (cursor.tailLimit > 0) {
463
- const signatureIndex = findLastMessageIndexBySignature(messages, knownSignature);
464
- if (signatureIndex >= 0) {
465
- return {
466
- syncMode: 'append',
467
- replaceFrom: knownMessageCount,
468
- messages: messages.slice(signatureIndex + 1),
469
- totalMessages,
470
- lastMessageSignature,
471
- };
472
- }
473
- return buildBoundedTailSync(messages, cursor);
474
- }
475
- }
476
-
477
- const replaceFrom = Math.max(0, Math.min(knownMessageCount - 1, totalMessages));
478
- return {
479
- syncMode: replaceFrom === 0 ? 'full' : 'replace_tail',
480
- replaceFrom,
481
- messages: replaceFrom === 0 ? messages : messages.slice(replaceFrom),
482
- totalMessages,
483
- lastMessageSignature,
484
224
  };
485
225
  }
486
226
 
@@ -520,31 +260,13 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
520
260
  } catch (error: any) {
521
261
  return { success: false, error: error?.message || String(error) };
522
262
  }
523
- const messages = collapseReplayDuplicatesFromReadChat(normalizeReadChatMessages(validatedPayload));
524
- const cursor = normalizeReadChatCursor(args);
525
- if (!cursor.knownMessageCount && !cursor.lastMessageSignature && cursor.tailLimit > 0 && messages.length > cursor.tailLimit) {
526
- const tailMessages = buildVisibleReadChatTailMessages(messages, cursor.tailLimit);
527
- const lastMessageSignature = getChatMessageSignature(messages[messages.length - 1]);
528
- return {
529
- success: true,
530
- ...validatedPayload,
531
- messages: tailMessages,
532
- syncMode: 'full',
533
- replaceFrom: 0,
534
- totalMessages: messages.length,
535
- lastMessageSignature,
536
- ...(debugReadChat ? { debugReadChat } : {}),
537
- };
538
- }
539
- const sync = computeReadChatSync(messages, cursor);
263
+ const messages = normalizeReadChatMessages(validatedPayload);
264
+ const sync = buildFullTail(messages, normalizeReadChatTailLimit(args));
540
265
  return {
541
266
  success: true,
542
267
  ...validatedPayload,
543
268
  messages: sync.messages,
544
- syncMode: sync.syncMode,
545
- replaceFrom: sync.replaceFrom,
546
269
  totalMessages: sync.totalMessages,
547
- lastMessageSignature: sync.lastMessageSignature,
548
270
  ...(debugReadChat ? { debugReadChat } : {}),
549
271
  };
550
272
  }
@@ -804,10 +526,6 @@ export async function handleGetChatDebugBundle(h: CommandHelpers, args: any): Pr
804
526
  status: readResult.status,
805
527
  title: readResult.title,
806
528
  totalMessages: readResult.totalMessages,
807
- returnedMessages: Array.isArray(readResult.messages) ? readResult.messages.length : undefined,
808
- syncMode: readResult.syncMode,
809
- replaceFrom: readResult.replaceFrom,
810
- lastMessageSignature: readResult.lastMessageSignature,
811
529
  providerSessionId: readResult.providerSessionId,
812
530
  transcriptAuthority: readResult.transcriptAuthority,
813
531
  coverage: readResult.coverage,
@@ -931,25 +649,13 @@ function toNonNegativeNumber(value: any): number {
931
649
  }
932
650
 
933
651
  function getCliVisibleTranscriptCount(adapter: any): number {
934
- const adapterStatus = adapter?.getStatus?.() || {};
935
- const adapterMessages = Array.isArray(adapterStatus.messages) ? adapterStatus.messages : [];
936
- let parsedRecord: Record<string, any> | null = null;
937
- if (typeof adapter?.getScriptParsedStatus === 'function') {
938
- try {
939
- const parsed = parseMaybeJson(adapter.getScriptParsedStatus());
940
- parsedRecord = parsed && typeof parsed === 'object' ? parsed as Record<string, any> : null;
941
- } catch {
942
- parsedRecord = null;
943
- }
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;
944
658
  }
945
- const parsedMessages = Array.isArray(parsedRecord?.messages) ? parsedRecord.messages : [];
946
- if (!parsedRecord) return adapterMessages.length;
947
- const parsedIsProviderAuthoritative = parsedRecord.transcriptAuthority === 'provider'
948
- || parsedRecord.coverage === 'full';
949
- const shouldPreferAdapterMessages = !parsedIsProviderAuthoritative
950
- && adapterMessages.length > 0
951
- && adapterMessages.length > parsedMessages.length;
952
- return shouldPreferAdapterMessages ? adapterMessages.length : parsedMessages.length;
953
659
  }
954
660
 
955
661
  async function getStableExtensionBaseline(h: CommandHelpers): Promise<any | null> {
@@ -1022,74 +728,56 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
1022
728
  const adapter = getTargetedCliAdapter(h, args, provider?.type);
1023
729
  if (adapter) {
1024
730
  _log(`${transport} adapter: ${adapter.cliType}`);
731
+ if (typeof adapter.getScriptParsedStatus !== 'function') {
732
+ return { success: false, error: `${transport} adapter parseSession unavailable` };
733
+ }
1025
734
  let parsedStatus: any = null;
1026
- if (typeof adapter.getScriptParsedStatus === 'function') {
1027
- try {
1028
- parsedStatus = parseMaybeJson(adapter.getScriptParsedStatus());
1029
- } catch (error: any) {
1030
- return { success: false, error: error?.message || String(error) };
1031
- }
735
+ try {
736
+ parsedStatus = parseMaybeJson(adapter.getScriptParsedStatus());
737
+ } catch (error: any) {
738
+ return { success: false, error: error?.message || String(error) };
1032
739
  }
1033
740
  const parsedRecord = parsedStatus && typeof parsedStatus === 'object'
1034
741
  ? parsedStatus as Record<string, any>
1035
742
  : null;
1036
- const adapterStatus = adapter.getStatus();
1037
- const parsedIsProviderAuthoritative = parsedRecord?.transcriptAuthority === 'provider'
1038
- || parsedRecord?.coverage === 'full';
1039
- const shouldPreferAdapterMessages =
1040
- !parsedIsProviderAuthoritative
1041
- && Array.isArray(adapterStatus.messages)
1042
- && adapterStatus.messages.length > 0
1043
- && Array.isArray(parsedRecord?.messages)
1044
- && adapterStatus.messages.length > parsedRecord.messages.length;
1045
- const parsedShowsApproval = hasNonEmptyModalButtons(parsedRecord?.activeModal)
1046
- && parsedRecord?.status === 'waiting_approval';
1047
- const status = parsedRecord
1048
- ? {
1049
- ...parsedRecord,
1050
- messages: shouldPreferAdapterMessages ? adapterStatus.messages : parsedRecord.messages,
1051
- status: parsedShowsApproval
1052
- ? parsedRecord.status
1053
- : (adapterStatus.status !== 'idle'
1054
- ? adapterStatus.status
1055
- : (parsedRecord.status || adapterStatus.status)),
1056
- activeModal: parsedRecord.activeModal || adapterStatus.activeModal,
1057
- }
1058
- : adapterStatus;
1059
-
1060
- const title = typeof parsedRecord?.title === 'string' ? parsedRecord.title : undefined;
1061
- 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'
1062
751
  ? parsedRecord.providerSessionId
1063
752
  : undefined;
1064
- const transcriptAuthority = parsedRecord?.transcriptAuthority === 'provider' || parsedRecord?.transcriptAuthority === 'daemon'
753
+ const transcriptAuthority = parsedRecord.transcriptAuthority === 'provider' || parsedRecord.transcriptAuthority === 'daemon'
1065
754
  ? parsedRecord.transcriptAuthority
1066
755
  : undefined;
1067
- 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'
1068
757
  ? parsedRecord.coverage
1069
758
  : undefined;
1070
- if (status) {
1071
- 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}`);
1072
- return buildReadChatCommandResult({
1073
- messages: (status as any).messages || [],
1074
- status: status.status,
1075
- activeModal: status.activeModal,
1076
- debugReadChat: {
1077
- provider: adapter.cliType,
1078
- targetSessionId: String(args?.targetSessionId || ''),
1079
- adapterStatus: String(adapterStatus.status || ''),
1080
- parsedStatus: String(parsedRecord?.status || ''),
1081
- returnedStatus: String(status.status || ''),
1082
- shouldPreferAdapterMessages,
1083
- adapterMsgCount: Array.isArray(adapterStatus.messages) ? adapterStatus.messages.length : 0,
1084
- parsedMsgCount: Array.isArray(parsedRecord?.messages) ? parsedRecord.messages.length : 0,
1085
- returnedMsgCount: Array.isArray((status as any).messages) ? (status as any).messages.length : 0,
1086
- },
1087
- ...(title ? { title } : {}),
1088
- ...(providerSessionId ? { providerSessionId } : {}),
1089
- ...(transcriptAuthority ? { transcriptAuthority } : {}),
1090
- ...(coverage ? { coverage } : {}),
1091
- }, args);
1092
- }
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);
1093
781
  }
1094
782
  return { success: false, error: `${transport} adapter not found` };
1095
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 {