@adhdev/daemon-core 0.9.82-rc.9 → 0.9.82-rc.90

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 (67) hide show
  1. package/dist/cli-adapters/provider-cli-adapter.d.ts +2 -0
  2. package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
  3. package/dist/cli-adapters/provider-cli-shared.d.ts +2 -0
  4. package/dist/commands/router.d.ts +22 -0
  5. package/dist/config/mesh-config.d.ts +66 -1
  6. package/dist/index.d.ts +13 -6
  7. package/dist/index.js +5395 -1197
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +5359 -1183
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/installer.d.ts +1 -4
  12. package/dist/launch.d.ts +1 -1
  13. package/dist/logging/async-batch-writer.d.ts +10 -0
  14. package/dist/mesh/beads-db.d.ts +18 -0
  15. package/dist/mesh/mesh-active-work.d.ts +60 -0
  16. package/dist/mesh/mesh-events.d.ts +29 -5
  17. package/dist/mesh/mesh-fast-forward.d.ts +39 -0
  18. package/dist/mesh/mesh-host-ownership.d.ts +9 -0
  19. package/dist/mesh/mesh-ledger.d.ts +38 -1
  20. package/dist/mesh/mesh-work-queue.d.ts +27 -5
  21. package/dist/mesh/refine-config.d.ts +176 -0
  22. package/dist/providers/chat-message-normalization.d.ts +1 -0
  23. package/dist/providers/cli-provider-instance.d.ts +2 -1
  24. package/dist/repo-mesh-types.d.ts +46 -0
  25. package/dist/status/reporter.d.ts +2 -0
  26. package/package.json +3 -1
  27. package/src/boot/daemon-lifecycle.ts +1 -0
  28. package/src/cli-adapters/provider-cli-adapter.ts +91 -3
  29. package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
  30. package/src/cli-adapters/provider-cli-parse.ts +4 -0
  31. package/src/cli-adapters/provider-cli-runtime.ts +3 -1
  32. package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
  33. package/src/cli-adapters/provider-cli-shared.ts +20 -10
  34. package/src/commands/chat-commands.ts +454 -15
  35. package/src/commands/cli-manager.ts +126 -0
  36. package/src/commands/handler.ts +8 -1
  37. package/src/commands/mesh-coordinator.ts +13 -143
  38. package/src/commands/router.ts +2687 -435
  39. package/src/config/chat-history.ts +9 -7
  40. package/src/config/mesh-config.ts +245 -1
  41. package/src/daemon/dev-cli-debug.ts +10 -1
  42. package/src/detection/ide-detector.ts +26 -16
  43. package/src/index.ts +31 -5
  44. package/src/installer.d.ts +1 -1
  45. package/src/installer.ts +8 -6
  46. package/src/launch.d.ts +1 -1
  47. package/src/launch.ts +37 -28
  48. package/src/logging/async-batch-writer.ts +55 -0
  49. package/src/logging/logger.ts +2 -1
  50. package/src/mesh/beads-db.ts +176 -0
  51. package/src/mesh/coordinator-prompt.ts +30 -7
  52. package/src/mesh/mesh-active-work.ts +243 -0
  53. package/src/mesh/mesh-events.ts +400 -47
  54. package/src/mesh/mesh-fast-forward.ts +430 -0
  55. package/src/mesh/mesh-host-ownership.ts +73 -0
  56. package/src/mesh/mesh-ledger.ts +138 -1
  57. package/src/mesh/mesh-work-queue.ts +199 -137
  58. package/src/mesh/refine-config.ts +356 -0
  59. package/src/providers/chat-message-normalization.ts +3 -1
  60. package/src/providers/cli-provider-instance.ts +91 -13
  61. package/src/providers/ide-provider-instance.ts +17 -3
  62. package/src/providers/provider-loader.ts +10 -4
  63. package/src/providers/read-chat-contract.ts +1 -1
  64. package/src/providers/version-archive.ts +38 -20
  65. package/src/repo-mesh-types.ts +51 -0
  66. package/src/status/reporter.ts +15 -0
  67. package/src/system/host-memory.ts +29 -12
@@ -13,7 +13,7 @@ import { flattenContent, normalizeInputEnvelope, type InputEnvelope, type Provid
13
13
  import { assertProviderSupportsDeclaredInput, assertTextOnlyInput } from '../providers/provider-input-support.js';
14
14
  import { validateReadChatResultPayload } from '../providers/read-chat-contract.js';
15
15
  import type { ProviderInstance } from '../providers/provider-instance.js';
16
- import { readProviderChatHistory } from '../config/chat-history.js';
16
+ import { isNativeSourceCanonicalHistory, readProviderChatHistory } from '../config/chat-history.js';
17
17
  import { LOG, getRecentLogs } from '../logging/logger.js';
18
18
  import { getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
19
19
  import { buildChatMessageSignature, hashSignatureParts } from '../chat/chat-signatures.js';
@@ -24,6 +24,8 @@ import { filterUserFacingChatMessages, normalizeChatMessages } from '../provider
24
24
  const RECENT_SEND_WINDOW_MS = 1200;
25
25
  export const READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25_000;
26
26
  const HERMES_CLI_STARTING_SEND_SETTLE_MS = 2_000;
27
+ const CLI_NATIVE_HISTORY_FRESH_MS = 5 * 60_000;
28
+ const CLI_NATIVE_TRANSCRIPT_PROVIDERS = new Set(['codex-cli', 'claude-cli']);
27
29
  const recentSendByTarget = new Map<string, number>();
28
30
 
29
31
  interface ApprovalSelectableInstance extends ProviderInstance {
@@ -151,7 +153,17 @@ function getHistorySessionId(h: CommandHelpers, args: any): string | undefined {
151
153
  const instance = h.ctx.instanceManager?.getInstance(targetSessionId);
152
154
  const state = instance?.getState?.();
153
155
  const providerSessionId = typeof state?.providerSessionId === 'string' ? state.providerSessionId.trim() : '';
154
- return providerSessionId || targetSessionId;
156
+ if (providerSessionId) return providerSessionId;
157
+
158
+ const currentSession = h.currentSession as any;
159
+ if (currentSession?.sessionId === targetSessionId) {
160
+ const currentProviderSessionId = typeof currentSession.providerSessionId === 'string'
161
+ ? currentSession.providerSessionId.trim()
162
+ : '';
163
+ if (currentProviderSessionId) return currentProviderSessionId;
164
+ }
165
+
166
+ return targetSessionId;
155
167
  }
156
168
 
157
169
  function getInteractionId(args: any): string | undefined {
@@ -221,6 +233,212 @@ function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[]
221
233
  return normalizeChatMessages(messages);
222
234
  }
223
235
 
236
+ function getMessageNewestReceivedAt(messages: Array<{ receivedAt?: unknown; timestamp?: unknown }>): number {
237
+ let newest = 0;
238
+ for (const message of messages) {
239
+ const receivedAt = Number(message?.receivedAt ?? message?.timestamp ?? 0);
240
+ if (Number.isFinite(receivedAt) && receivedAt > newest) newest = receivedAt;
241
+ }
242
+ return newest;
243
+ }
244
+
245
+ function readHistorySessionIdFromMessages(messages: ChatMessage[]): string | undefined {
246
+ for (const message of messages as Array<ChatMessage & { historySessionId?: unknown }>) {
247
+ const historySessionId = typeof message?.historySessionId === 'string' ? message.historySessionId.trim() : '';
248
+ if (historySessionId) return historySessionId;
249
+ }
250
+ return undefined;
251
+ }
252
+
253
+ function normalizeNativeHistoryMessages(providerType: string, messages: ChatMessage[]): ChatMessage[] {
254
+ let turnIndex = 0;
255
+ return normalizeChatMessages(messages).map((message, index) => {
256
+ const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
257
+ const kind = typeof message.kind === 'string' && message.kind.trim() ? message.kind.trim() : (role === 'system' ? 'system' : 'standard');
258
+ if ((role === 'user' || role === 'human') && index > 0) turnIndex += 1;
259
+ const historySessionId = typeof (message as any).historySessionId === 'string'
260
+ ? (message as any).historySessionId.trim()
261
+ : '';
262
+ const contentHash = hashSignatureParts([
263
+ providerType,
264
+ historySessionId,
265
+ String(message.receivedAt || message.timestamp || index),
266
+ role,
267
+ kind,
268
+ flattenContent(message.content),
269
+ ]).slice(0, 12);
270
+ const providerUnitKey = typeof message.providerUnitKey === 'string' && message.providerUnitKey.trim()
271
+ ? message.providerUnitKey.trim()
272
+ : `${providerType}:native:${historySessionId || 'workspace'}:${index}:${role || 'message'}:${kind}:${contentHash}`;
273
+ const meta = message.meta && typeof message.meta === 'object' ? message.meta as Record<string, unknown> : undefined;
274
+ const isSystemSessionStart = role === 'system' || kind === 'system' || kind === 'session_start';
275
+ const isActivity = role === 'assistant' && (kind === 'tool' || kind === 'terminal' || kind === 'thought');
276
+ return {
277
+ ...message,
278
+ role: role === 'human' ? 'user' : (role || 'assistant'),
279
+ kind: isSystemSessionStart ? 'system' : kind,
280
+ providerUnitKey,
281
+ bubbleId: typeof message.bubbleId === 'string' && message.bubbleId.trim()
282
+ ? message.bubbleId.trim()
283
+ : `bubble:${providerUnitKey}`,
284
+ _turnKey: typeof message._turnKey === 'string' && message._turnKey.trim()
285
+ ? message._turnKey.trim()
286
+ : `${providerType}:native-turn:${historySessionId || 'workspace'}:${turnIndex}`,
287
+ bubbleState: message.bubbleState || 'final',
288
+ ...(isSystemSessionStart ? {
289
+ visibility: message.visibility || 'hidden',
290
+ transcriptVisibility: message.transcriptVisibility || 'hidden',
291
+ audience: message.audience || 'internal',
292
+ source: message.source || 'runtime_status',
293
+ } : isActivity ? {
294
+ source: message.source || (kind === 'terminal' ? 'terminal_command' : 'tool_call'),
295
+ meta: { ...meta, label: message.senderName || meta?.label || (kind === 'terminal' ? 'Terminal' : 'Tool') },
296
+ } : {
297
+ source: message.source || (role === 'assistant' ? 'assistant_text' : undefined),
298
+ }),
299
+ } as ChatMessage;
300
+ });
301
+ }
302
+
303
+ function buildCliMessageSourceProvenance(args: {
304
+ selected: 'native-history' | 'pty-parser';
305
+ provider: string;
306
+ nativeHandle?: string;
307
+ fallbackReason?: string;
308
+ nativeSource?: string;
309
+ sourcePath?: string;
310
+ sourceMtimeMs?: number;
311
+ nativeMessages?: ChatMessage[];
312
+ ptyMessages?: ChatMessage[];
313
+ returnedMessages?: ChatMessage[];
314
+ safeMapping?: boolean;
315
+ freshEnough?: boolean;
316
+ ptyStatusApprovalOnly?: boolean;
317
+ }): Record<string, unknown> {
318
+ const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
319
+ const sourceMtimeAgeMs = sourceMtimeMs > 0 ? Math.max(0, Date.now() - sourceMtimeMs) : undefined;
320
+ const nativeMessages = args.nativeMessages || [];
321
+ const ptyMessages = args.ptyMessages || [];
322
+ const returnedMessages = args.returnedMessages || [];
323
+ return {
324
+ selected: args.selected,
325
+ provider: args.provider,
326
+ providerType: args.provider,
327
+ ...(args.nativeHandle ? { nativeHandle: args.nativeHandle } : {}),
328
+ ...(args.nativeHandle ? { nativeSessionId: args.nativeHandle } : {}),
329
+ ...(args.fallbackReason ? { fallbackReason: args.fallbackReason } : {}),
330
+ ...(args.nativeSource ? { nativeSource: args.nativeSource } : {}),
331
+ ...(args.sourcePath ? { sourcePath: args.sourcePath } : {}),
332
+ ptyStatusApprovalOnly: args.ptyStatusApprovalOnly === true,
333
+ staleness: {
334
+ sourceMtimeMs: sourceMtimeMs || undefined,
335
+ sourceMtimeAgeMs,
336
+ nativeNewestMessageAt: getMessageNewestReceivedAt(nativeMessages),
337
+ ptyNewestMessageAt: getMessageNewestReceivedAt(ptyMessages),
338
+ freshEnough: args.freshEnough === true,
339
+ },
340
+ coverage: {
341
+ nativeMessageCount: nativeMessages.length,
342
+ ptyMessageCount: ptyMessages.length,
343
+ returnedMessageCount: returnedMessages.length,
344
+ safeMapping: args.safeMapping === true,
345
+ },
346
+ };
347
+ }
348
+
349
+ function buildNativeHistoryFallbackReason(args: {
350
+ providerType: string;
351
+ provider?: ProviderModule;
352
+ nativeSource?: string;
353
+ nativeMessageCount: number;
354
+ safeMapping: boolean;
355
+ freshEnough: boolean;
356
+ }): string {
357
+ if (!supportsCliNativeTranscript(args.providerType, args.provider)) return 'provider_native_transcript_not_supported';
358
+ if (args.nativeSource === 'native-unavailable') return 'native_history_unavailable';
359
+ if (args.nativeSource && args.nativeSource !== 'provider-native') return `native_history_source_${args.nativeSource}`;
360
+ if (args.nativeMessageCount <= 0) return 'native_history_empty';
361
+ if (!args.safeMapping) return 'native_history_not_safely_mapped';
362
+ if (!args.freshEnough) return 'native_history_stale';
363
+ return 'native_history_not_selected';
364
+ }
365
+
366
+ function supportsCliNativeTranscript(providerType: string, provider?: ProviderModule): boolean {
367
+ if (CLI_NATIVE_TRANSCRIPT_PROVIDERS.has(providerType)) return true;
368
+ return provider?.category === 'cli' && isNativeSourceCanonicalHistory(provider?.canonicalHistory);
369
+ }
370
+
371
+ function hasSafeNativeHistoryMapping(args: {
372
+ historySessionId?: string;
373
+ providerSessionId?: string;
374
+ workspace?: string;
375
+ nativeMessages: ChatMessage[];
376
+ }): boolean {
377
+ const explicitSessionId = String(args.historySessionId || args.providerSessionId || '').trim();
378
+ if (explicitSessionId) {
379
+ const messageSessionIds = args.nativeMessages
380
+ .map((message: any) => typeof message?.historySessionId === 'string' ? message.historySessionId.trim() : '')
381
+ .filter(Boolean);
382
+ if (messageSessionIds.length === 0) return true;
383
+ return messageSessionIds.some((id) => id === explicitSessionId);
384
+ }
385
+ const workspace = String(args.workspace || '').trim();
386
+ if (!workspace) return false;
387
+ return args.nativeMessages.some((message: any) => String(message?.workspace || '').trim() === workspace);
388
+ }
389
+
390
+ function readCliProviderNativeHistory(agentStr: string, args: {
391
+ canonicalHistory?: ProviderModule['canonicalHistory'];
392
+ historySessionId?: string;
393
+ workspace?: string;
394
+ offset: number;
395
+ limit: number;
396
+ excludeRecentCount: number;
397
+ historyBehavior?: ProviderModule['historyBehavior'];
398
+ scripts?: ProviderScripts;
399
+ }): ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' } {
400
+ const sessionHistory = readProviderChatHistory(agentStr, {
401
+ canonicalHistory: args.canonicalHistory,
402
+ historySessionId: args.historySessionId,
403
+ workspace: args.workspace,
404
+ offset: args.offset,
405
+ limit: args.limit,
406
+ excludeRecentCount: args.excludeRecentCount,
407
+ historyBehavior: args.historyBehavior,
408
+ scripts: args.scripts as any,
409
+ });
410
+ if ((sessionHistory as any).source !== 'native-unavailable' || !args.historySessionId || !args.workspace) {
411
+ return { ...(sessionHistory as any), lookup: 'session' };
412
+ }
413
+ const workspaceHistory = readProviderChatHistory(agentStr, {
414
+ canonicalHistory: args.canonicalHistory,
415
+ historySessionId: undefined,
416
+ workspace: args.workspace,
417
+ offset: args.offset,
418
+ limit: args.limit,
419
+ excludeRecentCount: args.excludeRecentCount,
420
+ historyBehavior: args.historyBehavior,
421
+ scripts: args.scripts as any,
422
+ });
423
+ return { ...(workspaceHistory as any), lookup: 'workspace' };
424
+ }
425
+
426
+ function isNativeHistoryFreshEnough(args: {
427
+ sourceMtimeMs?: number;
428
+ nativeMessages: ChatMessage[];
429
+ ptyMessages: ChatMessage[];
430
+ }): boolean {
431
+ const nativeNewest = getMessageNewestReceivedAt(args.nativeMessages);
432
+ const ptyNewest = getMessageNewestReceivedAt(args.ptyMessages);
433
+ if (nativeNewest > 0 && nativeNewest >= ptyNewest) return true;
434
+ const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
435
+ if (sourceMtimeMs > 0 && Date.now() - sourceMtimeMs <= CLI_NATIVE_HISTORY_FRESH_MS) return true;
436
+ return ptyNewest === 0 && nativeNewest > 0;
437
+ }
438
+
439
+ function shouldPreserveReadChatPayloadField(key: string): boolean {
440
+ return key === 'messageSource' || key === 'transcriptProvenance';
441
+ }
224
442
 
225
443
  function deriveHistoryDedupKey(message: ChatMessage & { _unitKey?: string; _turnKey?: string }): string | undefined {
226
444
  const unitKey = typeof message._unitKey === 'string' ? message._unitKey.trim() : '';
@@ -281,7 +499,7 @@ function normalizeReadChatCommandStatus(status: unknown, activeModal: unknown):
281
499
  }
282
500
  switch (raw) {
283
501
  case 'starting':
284
- return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : 'generating';
502
+ return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : 'starting';
285
503
  case 'stopped':
286
504
  case 'disconnected':
287
505
  case 'not_monitored':
@@ -295,6 +513,16 @@ function isGeneratingLikeStatus(status: unknown): boolean {
295
513
  return status === 'generating' || status === 'streaming' || status === 'long_generating' || status === 'starting';
296
514
  }
297
515
 
516
+ function hasVisibleAssistantMessage(messages: unknown[] | undefined): boolean {
517
+ if (!Array.isArray(messages)) return false;
518
+ return messages.some((message: any) => {
519
+ if (!message || message.role !== 'assistant') return false;
520
+ const kind = typeof message.kind === 'string' ? message.kind : 'standard';
521
+ if (kind !== 'standard') return false;
522
+ return String(message.content || '').trim().length > 0;
523
+ });
524
+ }
525
+
298
526
  function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): boolean {
299
527
  if (!isGeneratingLikeStatus(parsedStatus)) return false;
300
528
  if (hasNonEmptyModalButtons(activeModal)) return false;
@@ -304,7 +532,26 @@ function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal:
304
532
  return true;
305
533
  }
306
534
 
307
- function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): string {
535
+ function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any, parsedMessages?: unknown[]): string {
536
+ const adapterRawStatus = typeof adapterStatus?.status === 'string' ? adapterStatus.status.trim() : '';
537
+ if (adapterRawStatus === 'starting'
538
+ && isGeneratingLikeStatus(parsedStatus)
539
+ && !hasNonEmptyModalButtons(activeModal)
540
+ && Array.isArray(parsedMessages)
541
+ && parsedMessages.length === 0
542
+ && Array.isArray(adapterStatus?.messages)
543
+ && adapterStatus.messages.length === 0
544
+ && !(typeof adapter.isProcessing === 'function' && adapter.isProcessing())) {
545
+ return 'starting';
546
+ }
547
+ if (
548
+ isGeneratingLikeStatus(adapterRawStatus)
549
+ && parsedStatus === 'idle'
550
+ && !hasNonEmptyModalButtons(activeModal)
551
+ && !hasVisibleAssistantMessage(parsedMessages)
552
+ ) {
553
+ return adapterRawStatus;
554
+ }
308
555
  if (shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus)) return 'idle';
309
556
  return typeof parsedStatus === 'string' && parsedStatus.trim() ? parsedStatus : 'idle';
310
557
  }
@@ -356,6 +603,7 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
356
603
  return {
357
604
  success: true,
358
605
  ...validatedPayload,
606
+ ...Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key))),
359
607
  messages: sync.messages,
360
608
  totalMessages: sync.totalMessages,
361
609
  ...(returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}),
@@ -584,6 +832,8 @@ function buildChatDebugBundleSummary(bundle: Record<string, unknown>): Record<st
584
832
  adapterStatus: debugReadChat.adapterStatus,
585
833
  parsedStatus: debugReadChat.parsedStatus,
586
834
  returnedStatus: debugReadChat.returnedStatus,
835
+ selectedMessageSource: debugReadChat.selectedMessageSource,
836
+ messageSource: debugReadChat.messageSource,
587
837
  parsedMsgCount: debugReadChat.parsedMsgCount,
588
838
  returnedMsgCount: debugReadChat.returnedMsgCount,
589
839
  shouldPreferAdapterMessages: debugReadChat.shouldPreferAdapterMessages,
@@ -646,6 +896,8 @@ export async function handleGetChatDebugBundle(h: CommandHelpers, args: any): Pr
646
896
  providerSessionId: readResult.providerSessionId,
647
897
  transcriptAuthority: readResult.transcriptAuthority,
648
898
  coverage: readResult.coverage,
899
+ messageSource: readResult.messageSource,
900
+ transcriptProvenance: readResult.transcriptProvenance,
649
901
  activeModal: readResult.activeModal,
650
902
  messagesTail: Array.isArray(readResult.messages) ? readResult.messages.slice(-20) : [],
651
903
  debugReadChat: readResult.debugReadChat,
@@ -874,7 +1126,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
874
1126
  ? parsedRecord.coverage
875
1127
  : undefined;
876
1128
  const activeModal = parsedRecord.activeModal ?? parsedRecord.modal ?? null;
877
- const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus);
1129
+ const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus, parsedRecord.messages);
878
1130
  const runtimeMessageMerger = getTargetInstance(h, args) as RuntimeChatMessageMerger | null;
879
1131
  const parsedMessages = finalizeStreamingMessagesWhenIdle(parsedRecord.messages as ChatMessage[], returnedStatus);
880
1132
  const returnedMessages = runtimeMessageMerger?.category === 'cli'
@@ -882,25 +1134,148 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
882
1134
  && typeof runtimeMessageMerger.mergeRuntimeChatMessages === 'function'
883
1135
  ? runtimeMessageMerger.mergeRuntimeChatMessages(parsedMessages)
884
1136
  : parsedMessages;
1137
+ const providerType = provider?.type || adapter.cliType;
1138
+ let selectedMessages = returnedMessages;
1139
+ let selectedTitle = title;
1140
+ let selectedProviderSessionId = providerSessionId;
1141
+ let selectedTranscriptAuthority = transcriptAuthority;
1142
+ let selectedCoverage = coverage;
1143
+ let messageSource = buildCliMessageSourceProvenance({
1144
+ selected: 'pty-parser',
1145
+ provider: adapter.cliType,
1146
+ fallbackReason: supportsCliNativeTranscript(providerType, provider) ? 'native_history_not_checked' : 'provider_native_transcript_not_supported',
1147
+ ptyMessages: returnedMessages,
1148
+ returnedMessages,
1149
+ ptyStatusApprovalOnly: false,
1150
+ });
1151
+
1152
+ if (supportsCliNativeTranscript(providerType, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)) {
1153
+ const agentStr = provider?.type || args?.agentType || getCurrentProviderType(h, adapter.cliType);
1154
+ const workspace = typeof args?.workspace === 'string'
1155
+ ? args.workspace
1156
+ : typeof (h.currentSession as any)?.workspace === 'string'
1157
+ ? (h.currentSession as any).workspace
1158
+ : typeof adapter.workingDir === 'string'
1159
+ ? adapter.workingDir
1160
+ : undefined;
1161
+ const nativeHistoryLimit = Math.max(
1162
+ normalizeReadChatTailLimit(args) || 0,
1163
+ returnedMessages.length,
1164
+ 200,
1165
+ );
1166
+ let nativeHistory: (ReturnType<typeof readProviderChatHistory> & { lookup?: 'session' | 'workspace' }) | null = null;
1167
+ try {
1168
+ nativeHistory = readCliProviderNativeHistory(agentStr, {
1169
+ canonicalHistory: provider?.canonicalHistory,
1170
+ historySessionId,
1171
+ workspace,
1172
+ offset: 0,
1173
+ limit: nativeHistoryLimit,
1174
+ excludeRecentCount: 0,
1175
+ historyBehavior: provider?.historyBehavior,
1176
+ scripts: provider?.scripts as any,
1177
+ });
1178
+ } catch (error: any) {
1179
+ const fallbackReason = `native_history_error:${error?.message || String(error)}`;
1180
+ messageSource = buildCliMessageSourceProvenance({
1181
+ selected: 'pty-parser',
1182
+ provider: adapter.cliType,
1183
+ fallbackReason,
1184
+ ptyMessages: returnedMessages,
1185
+ returnedMessages,
1186
+ ptyStatusApprovalOnly: false,
1187
+ });
1188
+ nativeHistory = null;
1189
+ }
1190
+
1191
+ if (nativeHistory) {
1192
+ const nativeMessages = Array.isArray((nativeHistory as any).messages)
1193
+ ? normalizeNativeHistoryMessages(agentStr, (nativeHistory as any).messages as ChatMessage[])
1194
+ : [];
1195
+ const historyProviderSessionId = typeof (nativeHistory as any)?.providerSessionId === 'string'
1196
+ ? (nativeHistory as any).providerSessionId
1197
+ : readHistorySessionIdFromMessages(nativeMessages) || historySessionId;
1198
+ const lookup = (nativeHistory as any).lookup === 'workspace' ? 'workspace' : 'session';
1199
+ const safeMapping = hasSafeNativeHistoryMapping({
1200
+ historySessionId: lookup === 'workspace' ? undefined : historySessionId,
1201
+ providerSessionId: lookup === 'workspace' ? undefined : providerSessionId,
1202
+ workspace,
1203
+ nativeMessages,
1204
+ });
1205
+ const freshEnough = isNativeHistoryFreshEnough({
1206
+ sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
1207
+ nativeMessages,
1208
+ ptyMessages: returnedMessages,
1209
+ });
1210
+ if ((nativeHistory as any).source === 'provider-native' && nativeMessages.length > 0 && safeMapping && freshEnough) {
1211
+ selectedMessages = finalizeStreamingMessagesWhenIdle(nativeMessages, returnedStatus);
1212
+ selectedProviderSessionId = historyProviderSessionId || providerSessionId;
1213
+ selectedTranscriptAuthority = 'provider';
1214
+ selectedCoverage = (nativeHistory as any).hasMore ? 'tail' : 'full';
1215
+ messageSource = buildCliMessageSourceProvenance({
1216
+ selected: 'native-history',
1217
+ provider: adapter.cliType,
1218
+ nativeHandle: selectedProviderSessionId || historySessionId,
1219
+ nativeSource: (nativeHistory as any).source,
1220
+ sourcePath: (nativeHistory as any).sourcePath,
1221
+ sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
1222
+ nativeMessages,
1223
+ ptyMessages: returnedMessages,
1224
+ returnedMessages: selectedMessages,
1225
+ safeMapping,
1226
+ freshEnough,
1227
+ ptyStatusApprovalOnly: true,
1228
+ });
1229
+ } else {
1230
+ const fallbackReason = buildNativeHistoryFallbackReason({
1231
+ providerType,
1232
+ provider,
1233
+ nativeSource: (nativeHistory as any).source,
1234
+ nativeMessageCount: nativeMessages.length,
1235
+ safeMapping,
1236
+ freshEnough,
1237
+ });
1238
+ messageSource = buildCliMessageSourceProvenance({
1239
+ selected: 'pty-parser',
1240
+ provider: adapter.cliType,
1241
+ nativeHandle: historyProviderSessionId || historySessionId,
1242
+ fallbackReason,
1243
+ nativeSource: (nativeHistory as any).source,
1244
+ sourcePath: (nativeHistory as any).sourcePath,
1245
+ sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
1246
+ nativeMessages,
1247
+ ptyMessages: returnedMessages,
1248
+ returnedMessages,
1249
+ safeMapping,
1250
+ freshEnough,
1251
+ ptyStatusApprovalOnly: false,
1252
+ });
1253
+ }
1254
+ }
1255
+ }
885
1256
  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} returnedMsgCount=${returnedMessages.length}`);
886
1257
  return buildReadChatCommandResult({
887
- messages: returnedMessages,
1258
+ messages: selectedMessages,
888
1259
  status: returnedStatus,
889
1260
  activeModal,
1261
+ messageSource,
1262
+ transcriptProvenance: messageSource,
890
1263
  debugReadChat: {
891
1264
  provider: adapter.cliType,
892
1265
  targetSessionId: String(args?.targetSessionId || ''),
893
1266
  adapterStatus: String(adapterStatus.status || ''),
894
1267
  parsedStatus: String(parsedRecord.status || ''),
895
1268
  returnedStatus: String(returnedStatus || ''),
896
- shouldPreferAdapterMessages: false,
1269
+ selectedMessageSource: (messageSource as any).selected,
1270
+ messageSource,
1271
+ shouldPreferAdapterMessages: supportsCliNativeTranscript(providerType, provider) && (messageSource as any).selected !== 'native-history',
897
1272
  parsedMsgCount: parsedRecord.messages.length,
898
- returnedMsgCount: returnedMessages.length,
1273
+ returnedMsgCount: selectedMessages.length,
899
1274
  },
900
- ...(title ? { title } : {}),
901
- ...(providerSessionId ? { providerSessionId } : {}),
902
- ...(transcriptAuthority ? { transcriptAuthority } : {}),
903
- ...(coverage ? { coverage } : {}),
1275
+ ...(selectedTitle ? { title: selectedTitle } : {}),
1276
+ ...(selectedProviderSessionId ? { providerSessionId: selectedProviderSessionId } : {}),
1277
+ ...(selectedTranscriptAuthority ? { transcriptAuthority: selectedTranscriptAuthority } : {}),
1278
+ ...(selectedCoverage ? { coverage: selectedCoverage } : {}),
904
1279
  }, args);
905
1280
  }
906
1281
  const historyLimit = normalizeReadChatTailLimit(args);
@@ -911,7 +1286,18 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
911
1286
  : typeof (h.currentSession as any)?.workspace === 'string'
912
1287
  ? (h.currentSession as any).workspace
913
1288
  : undefined;
914
- const history = readProviderChatHistory(agentStr, {
1289
+ const history = supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)
1290
+ ? readCliProviderNativeHistory(agentStr, {
1291
+ canonicalHistory: provider?.canonicalHistory,
1292
+ historySessionId,
1293
+ workspace,
1294
+ offset: 0,
1295
+ limit: historyLimit,
1296
+ excludeRecentCount: 0,
1297
+ historyBehavior: provider?.historyBehavior,
1298
+ scripts: provider?.scripts as any,
1299
+ })
1300
+ : readProviderChatHistory(agentStr, {
915
1301
  canonicalHistory: provider?.canonicalHistory,
916
1302
  historySessionId,
917
1303
  workspace,
@@ -921,12 +1307,65 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
921
1307
  historyBehavior: provider?.historyBehavior,
922
1308
  scripts: provider?.scripts as any,
923
1309
  });
1310
+ const lookup = (history as any).lookup === 'workspace' ? 'workspace' : 'session';
1311
+ const historyMessages = Array.isArray((history as any)?.messages)
1312
+ ? normalizeNativeHistoryMessages(agentStr, (history as any).messages as ChatMessage[])
1313
+ : [];
924
1314
  const historyProviderSessionId = typeof (history as any)?.providerSessionId === 'string'
925
1315
  ? (history as any).providerSessionId
926
- : historySessionId;
1316
+ : readHistorySessionIdFromMessages(historyMessages) || historySessionId;
1317
+ const safeMapping = supportsCliNativeTranscript(agentStr, provider)
1318
+ ? hasSafeNativeHistoryMapping({
1319
+ historySessionId: lookup === 'workspace' ? undefined : historySessionId,
1320
+ providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId,
1321
+ workspace,
1322
+ nativeMessages: historyMessages,
1323
+ })
1324
+ : false;
1325
+ const nativeSelected = supportsCliNativeTranscript(agentStr, provider)
1326
+ && (history as any).source === 'provider-native'
1327
+ && historyMessages.length > 0
1328
+ && safeMapping;
1329
+ const messageSource = buildCliMessageSourceProvenance({
1330
+ selected: nativeSelected ? 'native-history' : 'pty-parser',
1331
+ provider: agentStr,
1332
+ nativeHandle: historyProviderSessionId || historySessionId,
1333
+ fallbackReason: nativeSelected
1334
+ ? undefined
1335
+ : buildNativeHistoryFallbackReason({
1336
+ providerType: agentStr,
1337
+ provider,
1338
+ nativeSource: (history as any).source,
1339
+ nativeMessageCount: historyMessages.length,
1340
+ safeMapping,
1341
+ freshEnough: true,
1342
+ }),
1343
+ nativeSource: (history as any).source,
1344
+ sourcePath: (history as any).sourcePath,
1345
+ sourceMtimeMs: (history as any).sourceMtimeMs,
1346
+ nativeMessages: historyMessages,
1347
+ returnedMessages: historyMessages,
1348
+ safeMapping,
1349
+ freshEnough: true,
1350
+ ptyStatusApprovalOnly: false,
1351
+ });
1352
+ const requiresNativeSource = supportsCliNativeTranscript(agentStr, provider)
1353
+ && isNativeSourceCanonicalHistory(provider?.canonicalHistory);
1354
+ if (requiresNativeSource && !nativeSelected) {
1355
+ return {
1356
+ success: false,
1357
+ code: 'native_history_not_safely_available',
1358
+ error: 'Provider-native history was not safely available for the requested CLI session.',
1359
+ providerSessionId: historyProviderSessionId,
1360
+ messageSource,
1361
+ transcriptProvenance: messageSource,
1362
+ };
1363
+ }
927
1364
  return buildReadChatCommandResult({
928
- messages: Array.isArray((history as any)?.messages) ? (history as any).messages : [],
1365
+ messages: historyMessages,
929
1366
  status: 'idle',
1367
+ messageSource,
1368
+ transcriptProvenance: messageSource,
930
1369
  ...(typeof (history as any)?.title === 'string' ? { title: (history as any).title } : {}),
931
1370
  ...(historyProviderSessionId ? { providerSessionId: historyProviderSessionId } : {}),
932
1371
  ...(((provider?.historyBehavior as any)?.transcriptAuthority === 'provider' || (provider?.historyBehavior as any)?.transcriptAuthority === 'daemon')