@adhdev/daemon-core 0.9.82-rc.10 → 0.9.82-rc.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-adapters/provider-cli-adapter.d.ts +13 -0
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +10 -0
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/mesh-config.d.ts +66 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +5793 -1219
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5759 -1206
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +18 -0
- package/dist/mesh/mesh-active-work.d.ts +60 -0
- package/dist/mesh/mesh-events.d.ts +26 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +38 -1
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/refine-config.d.ts +176 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -1
- package/dist/repo-mesh-types.d.ts +46 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +1 -0
- package/src/cli-adapters/provider-cli-adapter.ts +319 -14
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +4 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +28 -10
- package/src/commands/chat-commands.ts +570 -20
- package/src/commands/cli-manager.ts +129 -1
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +2820 -437
- package/src/config/chat-history.ts +9 -7
- package/src/config/mesh-config.ts +245 -1
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/index.ts +30 -4
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +176 -0
- package/src/mesh/coordinator-prompt.ts +31 -8
- package/src/mesh/mesh-active-work.ts +255 -0
- package/src/mesh/mesh-events.ts +389 -47
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +138 -1
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/refine-config.ts +356 -0
- package/src/providers/chat-message-normalization.ts +7 -12
- package/src/providers/cli-provider-instance.ts +102 -17
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/provider-loader.ts +10 -4
- package/src/providers/read-chat-contract.ts +1 -1
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +51 -0
- package/src/status/reporter.ts +15 -0
- 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', 'hermes-cli', 'antigravity-cli']);
|
|
27
29
|
const recentSendByTarget = new Map<string, number>();
|
|
28
30
|
|
|
29
31
|
interface ApprovalSelectableInstance extends ProviderInstance {
|
|
@@ -48,6 +50,16 @@ function getTargetedCliAdapter(h: CommandHelpers, args: any, providerType?: stri
|
|
|
48
50
|
return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
function getExplicitHistorySessionId(args: any): string | undefined {
|
|
54
|
+
const explicit = typeof args?.historySessionId === 'string' ? args.historySessionId.trim() : '';
|
|
55
|
+
if (explicit) return explicit;
|
|
56
|
+
|
|
57
|
+
const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
|
|
58
|
+
if (explicitProviderSessionId) return explicitProviderSessionId;
|
|
59
|
+
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
function getTargetInstance(h: CommandHelpers, args: any): ApprovalSelectableInstance | null {
|
|
52
64
|
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
53
65
|
const sessionId = targetSessionId || h.currentSession?.sessionId || '';
|
|
@@ -139,19 +151,47 @@ async function waitOnceForFreshHermesCliStart(adapter: CliAdapter, log: (msg: st
|
|
|
139
151
|
}
|
|
140
152
|
|
|
141
153
|
function getHistorySessionId(h: CommandHelpers, args: any): string | undefined {
|
|
142
|
-
const explicit =
|
|
154
|
+
const explicit = getExplicitHistorySessionId(args);
|
|
143
155
|
if (explicit) return explicit;
|
|
144
156
|
|
|
145
|
-
const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
|
|
146
|
-
if (explicitProviderSessionId) return explicitProviderSessionId;
|
|
147
|
-
|
|
148
157
|
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
149
158
|
if (!targetSessionId) return undefined;
|
|
150
159
|
|
|
151
|
-
const
|
|
160
|
+
const session = h.ctx.sessionRegistry?.get(targetSessionId) as any;
|
|
161
|
+
const registeredProviderSessionId = typeof session?.providerSessionId === 'string' ? session.providerSessionId.trim() : '';
|
|
162
|
+
if (registeredProviderSessionId) return registeredProviderSessionId;
|
|
163
|
+
|
|
164
|
+
const instance = getTargetInstance(h, args);
|
|
152
165
|
const state = instance?.getState?.();
|
|
153
166
|
const providerSessionId = typeof state?.providerSessionId === 'string' ? state.providerSessionId.trim() : '';
|
|
154
|
-
|
|
167
|
+
if (providerSessionId) return providerSessionId;
|
|
168
|
+
|
|
169
|
+
const currentSession = h.currentSession as any;
|
|
170
|
+
if (currentSession?.sessionId === targetSessionId) {
|
|
171
|
+
const currentProviderSessionId = typeof currentSession.providerSessionId === 'string'
|
|
172
|
+
? currentSession.providerSessionId.trim()
|
|
173
|
+
: '';
|
|
174
|
+
if (currentProviderSessionId) return currentProviderSessionId;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return targetSessionId;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function resolveCliNativeHistorySessionId(args: any, currentHistorySessionId: string | undefined, parsedProviderSessionId: string | undefined): string | undefined {
|
|
181
|
+
const explicit = getExplicitHistorySessionId(args);
|
|
182
|
+
if (explicit) return explicit;
|
|
183
|
+
|
|
184
|
+
const parsed = typeof parsedProviderSessionId === 'string' ? parsedProviderSessionId.trim() : '';
|
|
185
|
+
const current = typeof currentHistorySessionId === 'string' ? currentHistorySessionId.trim() : '';
|
|
186
|
+
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
187
|
+
|
|
188
|
+
// getHistorySessionId falls back to the runtime session id when no native
|
|
189
|
+
// handle has been registered yet. For live CLI adapters the parser may
|
|
190
|
+
// already know the provider-native handle; prefer it over the runtime id so
|
|
191
|
+
// exact native reads do not miss the worker transcript and fall back to PTY
|
|
192
|
+
// or same-workspace history.
|
|
193
|
+
if (parsed && (!current || current === targetSessionId)) return parsed;
|
|
194
|
+
return current || parsed || undefined;
|
|
155
195
|
}
|
|
156
196
|
|
|
157
197
|
function getInteractionId(args: any): string | undefined {
|
|
@@ -221,6 +261,263 @@ function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[]
|
|
|
221
261
|
return normalizeChatMessages(messages);
|
|
222
262
|
}
|
|
223
263
|
|
|
264
|
+
function getMessageNewestReceivedAt(messages: Array<{ receivedAt?: unknown; timestamp?: unknown }>): number {
|
|
265
|
+
let newest = 0;
|
|
266
|
+
for (const message of messages) {
|
|
267
|
+
const receivedAt = Number(message?.receivedAt ?? message?.timestamp ?? 0);
|
|
268
|
+
if (Number.isFinite(receivedAt) && receivedAt > newest) newest = receivedAt;
|
|
269
|
+
}
|
|
270
|
+
return newest;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function readHistorySessionIdFromMessages(messages: ChatMessage[]): string | undefined {
|
|
274
|
+
for (const message of messages as Array<ChatMessage & { historySessionId?: unknown }>) {
|
|
275
|
+
const historySessionId = typeof message?.historySessionId === 'string' ? message.historySessionId.trim() : '';
|
|
276
|
+
if (historySessionId) return historySessionId;
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function normalizeNativeHistoryMessages(providerType: string, messages: ChatMessage[]): ChatMessage[] {
|
|
282
|
+
let turnIndex = 0;
|
|
283
|
+
return normalizeChatMessages(messages).map((message, index) => {
|
|
284
|
+
const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
|
|
285
|
+
const kind = typeof message.kind === 'string' && message.kind.trim() ? message.kind.trim() : (role === 'system' ? 'system' : 'standard');
|
|
286
|
+
if ((role === 'user' || role === 'human') && index > 0) turnIndex += 1;
|
|
287
|
+
const historySessionId = typeof (message as any).historySessionId === 'string'
|
|
288
|
+
? (message as any).historySessionId.trim()
|
|
289
|
+
: '';
|
|
290
|
+
const contentHash = hashSignatureParts([
|
|
291
|
+
providerType,
|
|
292
|
+
historySessionId,
|
|
293
|
+
String(message.receivedAt || message.timestamp || index),
|
|
294
|
+
role,
|
|
295
|
+
kind,
|
|
296
|
+
flattenContent(message.content),
|
|
297
|
+
]).slice(0, 12);
|
|
298
|
+
const providerUnitKey = typeof message.providerUnitKey === 'string' && message.providerUnitKey.trim()
|
|
299
|
+
? message.providerUnitKey.trim()
|
|
300
|
+
: `${providerType}:native:${historySessionId || 'workspace'}:${index}:${role || 'message'}:${kind}:${contentHash}`;
|
|
301
|
+
const meta = message.meta && typeof message.meta === 'object' ? message.meta as Record<string, unknown> : undefined;
|
|
302
|
+
const isSystemSessionStart = role === 'system' || kind === 'system' || kind === 'session_start';
|
|
303
|
+
const isActivity = role === 'assistant' && (kind === 'tool' || kind === 'terminal' || kind === 'thought');
|
|
304
|
+
return {
|
|
305
|
+
...message,
|
|
306
|
+
role: role === 'human' ? 'user' : (role || 'assistant'),
|
|
307
|
+
kind: isSystemSessionStart ? 'system' : kind,
|
|
308
|
+
providerUnitKey,
|
|
309
|
+
bubbleId: typeof message.bubbleId === 'string' && message.bubbleId.trim()
|
|
310
|
+
? message.bubbleId.trim()
|
|
311
|
+
: `bubble:${providerUnitKey}`,
|
|
312
|
+
_turnKey: typeof message._turnKey === 'string' && message._turnKey.trim()
|
|
313
|
+
? message._turnKey.trim()
|
|
314
|
+
: `${providerType}:native-turn:${historySessionId || 'workspace'}:${turnIndex}`,
|
|
315
|
+
bubbleState: message.bubbleState || 'final',
|
|
316
|
+
...(isSystemSessionStart ? {
|
|
317
|
+
visibility: message.visibility || 'hidden',
|
|
318
|
+
transcriptVisibility: message.transcriptVisibility || 'hidden',
|
|
319
|
+
audience: message.audience || 'internal',
|
|
320
|
+
source: message.source || 'runtime_status',
|
|
321
|
+
} : isActivity ? {
|
|
322
|
+
source: message.source || (kind === 'terminal' ? 'terminal_command' : 'tool_call'),
|
|
323
|
+
meta: { ...meta, label: message.senderName || meta?.label || (kind === 'terminal' ? 'Terminal' : 'Tool') },
|
|
324
|
+
} : {
|
|
325
|
+
source: message.source || (role === 'assistant' ? 'assistant_text' : undefined),
|
|
326
|
+
}),
|
|
327
|
+
} as ChatMessage;
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function buildCliMessageSourceProvenance(args: {
|
|
332
|
+
selected: 'native-history' | 'pty-parser';
|
|
333
|
+
provider: string;
|
|
334
|
+
nativeHandle?: string;
|
|
335
|
+
fallbackReason?: string;
|
|
336
|
+
nativeSource?: string;
|
|
337
|
+
sourcePath?: string;
|
|
338
|
+
sourceMtimeMs?: number;
|
|
339
|
+
nativeMessages?: ChatMessage[];
|
|
340
|
+
ptyMessages?: ChatMessage[];
|
|
341
|
+
returnedMessages?: ChatMessage[];
|
|
342
|
+
safeMapping?: boolean;
|
|
343
|
+
freshEnough?: boolean;
|
|
344
|
+
ptyStatusApprovalOnly?: boolean;
|
|
345
|
+
}): Record<string, unknown> {
|
|
346
|
+
const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
|
|
347
|
+
const sourceMtimeAgeMs = sourceMtimeMs > 0 ? Math.max(0, Date.now() - sourceMtimeMs) : undefined;
|
|
348
|
+
const nativeMessages = args.nativeMessages || [];
|
|
349
|
+
const ptyMessages = args.ptyMessages || [];
|
|
350
|
+
const returnedMessages = args.returnedMessages || [];
|
|
351
|
+
return {
|
|
352
|
+
selected: args.selected,
|
|
353
|
+
provider: args.provider,
|
|
354
|
+
providerType: args.provider,
|
|
355
|
+
...(args.nativeHandle ? { nativeHandle: args.nativeHandle } : {}),
|
|
356
|
+
...(args.nativeHandle ? { nativeSessionId: args.nativeHandle } : {}),
|
|
357
|
+
...(args.fallbackReason ? { fallbackReason: args.fallbackReason } : {}),
|
|
358
|
+
...(args.nativeSource ? { nativeSource: args.nativeSource } : {}),
|
|
359
|
+
...(args.sourcePath ? { sourcePath: args.sourcePath } : {}),
|
|
360
|
+
ptyStatusApprovalOnly: args.ptyStatusApprovalOnly === true,
|
|
361
|
+
staleness: {
|
|
362
|
+
sourceMtimeMs: sourceMtimeMs || undefined,
|
|
363
|
+
sourceMtimeAgeMs,
|
|
364
|
+
nativeNewestMessageAt: getMessageNewestReceivedAt(nativeMessages),
|
|
365
|
+
ptyNewestMessageAt: getMessageNewestReceivedAt(ptyMessages),
|
|
366
|
+
freshEnough: args.freshEnough === true,
|
|
367
|
+
},
|
|
368
|
+
coverage: {
|
|
369
|
+
nativeMessageCount: nativeMessages.length,
|
|
370
|
+
ptyMessageCount: ptyMessages.length,
|
|
371
|
+
returnedMessageCount: returnedMessages.length,
|
|
372
|
+
safeMapping: args.safeMapping === true,
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function buildNativeHistoryFallbackReason(args: {
|
|
378
|
+
providerType: string;
|
|
379
|
+
provider?: ProviderModule;
|
|
380
|
+
nativeSource?: string;
|
|
381
|
+
nativeMessageCount: number;
|
|
382
|
+
safeMapping: boolean;
|
|
383
|
+
freshEnough: boolean;
|
|
384
|
+
}): string {
|
|
385
|
+
if (!supportsCliNativeTranscript(args.providerType, args.provider)) return 'provider_native_transcript_not_supported';
|
|
386
|
+
if (args.nativeSource === 'native-unavailable') return 'native_history_unavailable';
|
|
387
|
+
if (args.nativeSource && args.nativeSource !== 'provider-native') return `native_history_source_${args.nativeSource}`;
|
|
388
|
+
if (args.nativeMessageCount <= 0) return 'native_history_empty';
|
|
389
|
+
if (!args.safeMapping) return 'native_history_not_safely_mapped';
|
|
390
|
+
if (!args.freshEnough) return 'native_history_stale';
|
|
391
|
+
return 'native_history_not_selected';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function supportsCliNativeTranscript(providerType: string, provider?: ProviderModule): boolean {
|
|
395
|
+
if (CLI_NATIVE_TRANSCRIPT_PROVIDERS.has(providerType)) return true;
|
|
396
|
+
return provider?.category === 'cli' && isNativeSourceCanonicalHistory(provider?.canonicalHistory);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function getComparableVisibleText(message: ChatMessage | undefined): string {
|
|
400
|
+
if (!message) return '';
|
|
401
|
+
const role = String((message as any).role || '').trim().toLowerCase();
|
|
402
|
+
if (role !== 'user' && role !== 'assistant') return '';
|
|
403
|
+
const kind = String((message as any).kind || 'standard').trim().toLowerCase();
|
|
404
|
+
if (kind && kind !== 'standard') return '';
|
|
405
|
+
const content = flattenContent((message as any).content).replace(/\s+/g, ' ').trim();
|
|
406
|
+
return content;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function hasOverlappingVisibleConversationText(nativeMessages: ChatMessage[], ptyMessages: ChatMessage[]): boolean {
|
|
410
|
+
const nativeTexts = nativeMessages.map(getComparableVisibleText).filter(Boolean);
|
|
411
|
+
const ptyTexts = ptyMessages.map(getComparableVisibleText).filter(Boolean);
|
|
412
|
+
if (nativeTexts.length === 0 || ptyTexts.length === 0) return false;
|
|
413
|
+
for (const nativeText of nativeTexts) {
|
|
414
|
+
for (const ptyText of ptyTexts) {
|
|
415
|
+
if (nativeText === ptyText) return true;
|
|
416
|
+
const shorter = nativeText.length <= ptyText.length ? nativeText : ptyText;
|
|
417
|
+
const longer = nativeText.length <= ptyText.length ? ptyText : nativeText;
|
|
418
|
+
if (shorter.length >= 32 && longer.includes(shorter)) return true;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function hasSafeNativeHistoryMapping(args: {
|
|
425
|
+
historySessionId?: string;
|
|
426
|
+
providerSessionId?: string;
|
|
427
|
+
workspace?: string;
|
|
428
|
+
nativeMessages: ChatMessage[];
|
|
429
|
+
ptyMessages?: ChatMessage[];
|
|
430
|
+
requireWorkspaceContentOverlap?: boolean;
|
|
431
|
+
}): boolean {
|
|
432
|
+
const explicitSessionId = String(args.historySessionId || args.providerSessionId || '').trim();
|
|
433
|
+
if (explicitSessionId) {
|
|
434
|
+
const messageSessionIds = args.nativeMessages
|
|
435
|
+
.map((message: any) => typeof message?.historySessionId === 'string' ? message.historySessionId.trim() : '')
|
|
436
|
+
.filter(Boolean);
|
|
437
|
+
if (messageSessionIds.length === 0) return true;
|
|
438
|
+
return messageSessionIds.some((id) => id === explicitSessionId);
|
|
439
|
+
}
|
|
440
|
+
const workspace = String(args.workspace || '').trim();
|
|
441
|
+
if (!workspace) return false;
|
|
442
|
+
const workspaceMatches = args.nativeMessages.some((message: any) => String(message?.workspace || '').trim() === workspace);
|
|
443
|
+
if (!workspaceMatches) return false;
|
|
444
|
+
if (!args.requireWorkspaceContentOverlap) return true;
|
|
445
|
+
return hasOverlappingVisibleConversationText(args.nativeMessages, args.ptyMessages || []);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function readCliProviderNativeHistory(agentStr: string, args: {
|
|
449
|
+
canonicalHistory?: ProviderModule['canonicalHistory'];
|
|
450
|
+
historySessionId?: string;
|
|
451
|
+
workspace?: string;
|
|
452
|
+
offset: number;
|
|
453
|
+
limit: number;
|
|
454
|
+
excludeRecentCount: number;
|
|
455
|
+
historyBehavior?: ProviderModule['historyBehavior'];
|
|
456
|
+
scripts?: ProviderScripts;
|
|
457
|
+
exactSessionScoped?: boolean;
|
|
458
|
+
}): ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' } {
|
|
459
|
+
const sessionHistory = readProviderChatHistory(agentStr, {
|
|
460
|
+
canonicalHistory: args.canonicalHistory,
|
|
461
|
+
historySessionId: args.historySessionId,
|
|
462
|
+
workspace: args.workspace,
|
|
463
|
+
offset: args.offset,
|
|
464
|
+
limit: args.limit,
|
|
465
|
+
excludeRecentCount: args.excludeRecentCount,
|
|
466
|
+
historyBehavior: args.historyBehavior,
|
|
467
|
+
scripts: args.scripts as any,
|
|
468
|
+
});
|
|
469
|
+
// Exact runtime/provider transcript reads must not silently fall back to the
|
|
470
|
+
// workspace's active or most recent native transcript: multiple Hermes/Gemini/
|
|
471
|
+
// Codex sessions can run in the same workspace, and workspace fallback can make
|
|
472
|
+
// read_chat/completion evidence point at a different runtime's prompt.
|
|
473
|
+
if ((sessionHistory as any).source !== 'native-unavailable' || args.exactSessionScoped || !args.historySessionId || !args.workspace) {
|
|
474
|
+
return { ...(sessionHistory as any), lookup: args.historySessionId ? 'session' : 'workspace' };
|
|
475
|
+
}
|
|
476
|
+
const workspaceHistory = readProviderChatHistory(agentStr, {
|
|
477
|
+
canonicalHistory: args.canonicalHistory,
|
|
478
|
+
historySessionId: undefined,
|
|
479
|
+
workspace: args.workspace,
|
|
480
|
+
offset: args.offset,
|
|
481
|
+
limit: args.limit,
|
|
482
|
+
excludeRecentCount: args.excludeRecentCount,
|
|
483
|
+
historyBehavior: args.historyBehavior,
|
|
484
|
+
scripts: args.scripts as any,
|
|
485
|
+
});
|
|
486
|
+
return { ...(workspaceHistory as any), lookup: 'workspace' };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function isNativeHistoryFreshEnough(args: {
|
|
490
|
+
sourceMtimeMs?: number;
|
|
491
|
+
nativeMessages: ChatMessage[];
|
|
492
|
+
ptyMessages: ChatMessage[];
|
|
493
|
+
}): boolean {
|
|
494
|
+
const nativeNewest = getMessageNewestReceivedAt(args.nativeMessages);
|
|
495
|
+
const ptyNewest = getMessageNewestReceivedAt(args.ptyMessages);
|
|
496
|
+
if (nativeNewest > 0 && nativeNewest >= ptyNewest) return true;
|
|
497
|
+
const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
|
|
498
|
+
if (sourceMtimeMs > 0 && Date.now() - sourceMtimeMs <= CLI_NATIVE_HISTORY_FRESH_MS) return true;
|
|
499
|
+
return ptyNewest === 0 && nativeNewest > 0;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function shouldPreserveReadChatPayloadField(key: string): boolean {
|
|
503
|
+
return key === 'messageSource' || key === 'transcriptProvenance';
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function updateMessageSourceReturnedCount(value: unknown, returnedMessageCount: number): unknown {
|
|
507
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return value;
|
|
508
|
+
const record = value as Record<string, unknown>;
|
|
509
|
+
const coverage = record.coverage && typeof record.coverage === 'object' && !Array.isArray(record.coverage)
|
|
510
|
+
? record.coverage as Record<string, unknown>
|
|
511
|
+
: undefined;
|
|
512
|
+
if (!coverage) return value;
|
|
513
|
+
return {
|
|
514
|
+
...record,
|
|
515
|
+
coverage: {
|
|
516
|
+
...coverage,
|
|
517
|
+
returnedMessageCount,
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
}
|
|
224
521
|
|
|
225
522
|
function deriveHistoryDedupKey(message: ChatMessage & { _unitKey?: string; _turnKey?: string }): string | undefined {
|
|
226
523
|
const unitKey = typeof message._unitKey === 'string' ? message._unitKey.trim() : '';
|
|
@@ -281,7 +578,7 @@ function normalizeReadChatCommandStatus(status: unknown, activeModal: unknown):
|
|
|
281
578
|
}
|
|
282
579
|
switch (raw) {
|
|
283
580
|
case 'starting':
|
|
284
|
-
return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : '
|
|
581
|
+
return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : 'starting';
|
|
285
582
|
case 'stopped':
|
|
286
583
|
case 'disconnected':
|
|
287
584
|
case 'not_monitored':
|
|
@@ -295,6 +592,16 @@ function isGeneratingLikeStatus(status: unknown): boolean {
|
|
|
295
592
|
return status === 'generating' || status === 'streaming' || status === 'long_generating' || status === 'starting';
|
|
296
593
|
}
|
|
297
594
|
|
|
595
|
+
function hasVisibleAssistantMessage(messages: unknown[] | undefined): boolean {
|
|
596
|
+
if (!Array.isArray(messages)) return false;
|
|
597
|
+
return messages.some((message: any) => {
|
|
598
|
+
if (!message || message.role !== 'assistant') return false;
|
|
599
|
+
const kind = typeof message.kind === 'string' ? message.kind : 'standard';
|
|
600
|
+
if (kind !== 'standard') return false;
|
|
601
|
+
return String(message.content || '').trim().length > 0;
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
|
|
298
605
|
function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): boolean {
|
|
299
606
|
if (!isGeneratingLikeStatus(parsedStatus)) return false;
|
|
300
607
|
if (hasNonEmptyModalButtons(activeModal)) return false;
|
|
@@ -304,7 +611,26 @@ function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal:
|
|
|
304
611
|
return true;
|
|
305
612
|
}
|
|
306
613
|
|
|
307
|
-
function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): string {
|
|
614
|
+
function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any, parsedMessages?: unknown[]): string {
|
|
615
|
+
const adapterRawStatus = typeof adapterStatus?.status === 'string' ? adapterStatus.status.trim() : '';
|
|
616
|
+
if (adapterRawStatus === 'starting'
|
|
617
|
+
&& isGeneratingLikeStatus(parsedStatus)
|
|
618
|
+
&& !hasNonEmptyModalButtons(activeModal)
|
|
619
|
+
&& Array.isArray(parsedMessages)
|
|
620
|
+
&& parsedMessages.length === 0
|
|
621
|
+
&& Array.isArray(adapterStatus?.messages)
|
|
622
|
+
&& adapterStatus.messages.length === 0
|
|
623
|
+
&& !(typeof adapter.isProcessing === 'function' && adapter.isProcessing())) {
|
|
624
|
+
return 'starting';
|
|
625
|
+
}
|
|
626
|
+
if (
|
|
627
|
+
isGeneratingLikeStatus(adapterRawStatus)
|
|
628
|
+
&& parsedStatus === 'idle'
|
|
629
|
+
&& !hasNonEmptyModalButtons(activeModal)
|
|
630
|
+
&& !hasVisibleAssistantMessage(parsedMessages)
|
|
631
|
+
) {
|
|
632
|
+
return adapterRawStatus;
|
|
633
|
+
}
|
|
308
634
|
if (shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus)) return 'idle';
|
|
309
635
|
return typeof parsedStatus === 'string' && parsedStatus.trim() ? parsedStatus : 'idle';
|
|
310
636
|
}
|
|
@@ -342,6 +668,13 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
|
|
|
342
668
|
const visibleMessages = filterUserFacingChatMessages(messages);
|
|
343
669
|
const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
|
|
344
670
|
const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
|
|
671
|
+
const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key)));
|
|
672
|
+
if (preservedPayloadFields.messageSource) {
|
|
673
|
+
preservedPayloadFields.messageSource = updateMessageSourceReturnedCount(preservedPayloadFields.messageSource, sync.messages.length);
|
|
674
|
+
}
|
|
675
|
+
if (preservedPayloadFields.transcriptProvenance) {
|
|
676
|
+
preservedPayloadFields.transcriptProvenance = updateMessageSourceReturnedCount(preservedPayloadFields.transcriptProvenance, sync.messages.length);
|
|
677
|
+
}
|
|
345
678
|
const returnedDebugReadChat = debugReadChat
|
|
346
679
|
? {
|
|
347
680
|
...debugReadChat,
|
|
@@ -356,6 +689,7 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
|
|
|
356
689
|
return {
|
|
357
690
|
success: true,
|
|
358
691
|
...validatedPayload,
|
|
692
|
+
...preservedPayloadFields,
|
|
359
693
|
messages: sync.messages,
|
|
360
694
|
totalMessages: sync.totalMessages,
|
|
361
695
|
...(returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}),
|
|
@@ -584,6 +918,8 @@ function buildChatDebugBundleSummary(bundle: Record<string, unknown>): Record<st
|
|
|
584
918
|
adapterStatus: debugReadChat.adapterStatus,
|
|
585
919
|
parsedStatus: debugReadChat.parsedStatus,
|
|
586
920
|
returnedStatus: debugReadChat.returnedStatus,
|
|
921
|
+
selectedMessageSource: debugReadChat.selectedMessageSource,
|
|
922
|
+
messageSource: debugReadChat.messageSource,
|
|
587
923
|
parsedMsgCount: debugReadChat.parsedMsgCount,
|
|
588
924
|
returnedMsgCount: debugReadChat.returnedMsgCount,
|
|
589
925
|
shouldPreferAdapterMessages: debugReadChat.shouldPreferAdapterMessages,
|
|
@@ -646,6 +982,8 @@ export async function handleGetChatDebugBundle(h: CommandHelpers, args: any): Pr
|
|
|
646
982
|
providerSessionId: readResult.providerSessionId,
|
|
647
983
|
transcriptAuthority: readResult.transcriptAuthority,
|
|
648
984
|
coverage: readResult.coverage,
|
|
985
|
+
messageSource: readResult.messageSource,
|
|
986
|
+
transcriptProvenance: readResult.transcriptProvenance,
|
|
649
987
|
activeModal: readResult.activeModal,
|
|
650
988
|
messagesTail: Array.isArray(readResult.messages) ? readResult.messages.slice(-20) : [],
|
|
651
989
|
debugReadChat: readResult.debugReadChat,
|
|
@@ -874,7 +1212,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
874
1212
|
? parsedRecord.coverage
|
|
875
1213
|
: undefined;
|
|
876
1214
|
const activeModal = parsedRecord.activeModal ?? parsedRecord.modal ?? null;
|
|
877
|
-
const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus);
|
|
1215
|
+
const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus, parsedRecord.messages);
|
|
878
1216
|
const runtimeMessageMerger = getTargetInstance(h, args) as RuntimeChatMessageMerger | null;
|
|
879
1217
|
const parsedMessages = finalizeStreamingMessagesWhenIdle(parsedRecord.messages as ChatMessage[], returnedStatus);
|
|
880
1218
|
const returnedMessages = runtimeMessageMerger?.category === 'cli'
|
|
@@ -882,25 +1220,166 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
882
1220
|
&& typeof runtimeMessageMerger.mergeRuntimeChatMessages === 'function'
|
|
883
1221
|
? runtimeMessageMerger.mergeRuntimeChatMessages(parsedMessages)
|
|
884
1222
|
: parsedMessages;
|
|
1223
|
+
const providerType = provider?.type || adapter.cliType;
|
|
1224
|
+
let selectedMessages = returnedMessages;
|
|
1225
|
+
let selectedTitle = title;
|
|
1226
|
+
let selectedProviderSessionId = providerSessionId;
|
|
1227
|
+
let selectedTranscriptAuthority = transcriptAuthority;
|
|
1228
|
+
let selectedCoverage = coverage;
|
|
1229
|
+
let messageSource = buildCliMessageSourceProvenance({
|
|
1230
|
+
selected: 'pty-parser',
|
|
1231
|
+
provider: adapter.cliType,
|
|
1232
|
+
fallbackReason: supportsCliNativeTranscript(providerType, provider) ? 'native_history_not_checked' : 'provider_native_transcript_not_supported',
|
|
1233
|
+
ptyMessages: returnedMessages,
|
|
1234
|
+
returnedMessages,
|
|
1235
|
+
ptyStatusApprovalOnly: false,
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
if (supportsCliNativeTranscript(providerType, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)) {
|
|
1239
|
+
const agentStr = provider?.type || args?.agentType || getCurrentProviderType(h, adapter.cliType);
|
|
1240
|
+
const workspace = typeof args?.workspace === 'string'
|
|
1241
|
+
? args.workspace
|
|
1242
|
+
: typeof (h.currentSession as any)?.workspace === 'string'
|
|
1243
|
+
? (h.currentSession as any).workspace
|
|
1244
|
+
: typeof adapter.workingDir === 'string'
|
|
1245
|
+
? adapter.workingDir
|
|
1246
|
+
: undefined;
|
|
1247
|
+
const nativeHistoryLimit = Math.max(
|
|
1248
|
+
normalizeReadChatTailLimit(args) || 0,
|
|
1249
|
+
returnedMessages.length,
|
|
1250
|
+
200,
|
|
1251
|
+
);
|
|
1252
|
+
const nativeHistorySessionId = resolveCliNativeHistorySessionId(args, historySessionId, providerSessionId);
|
|
1253
|
+
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
1254
|
+
const exactNativeHistoryScope = Boolean(
|
|
1255
|
+
(typeof args?.historySessionId === 'string' && args.historySessionId.trim())
|
|
1256
|
+
|| (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
|
|
1257
|
+
|| providerSessionId
|
|
1258
|
+
|| (nativeHistorySessionId && nativeHistorySessionId !== targetSessionId)
|
|
1259
|
+
|| ((h.currentSession as any)?.sessionId === args?.targetSessionId && typeof (h.currentSession as any)?.providerSessionId === 'string' && (h.currentSession as any).providerSessionId.trim())
|
|
1260
|
+
);
|
|
1261
|
+
let nativeHistory: (ReturnType<typeof readProviderChatHistory> & { lookup?: 'session' | 'workspace' }) | null = null;
|
|
1262
|
+
try {
|
|
1263
|
+
nativeHistory = readCliProviderNativeHistory(agentStr, {
|
|
1264
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
1265
|
+
historySessionId: nativeHistorySessionId,
|
|
1266
|
+
workspace,
|
|
1267
|
+
offset: 0,
|
|
1268
|
+
limit: nativeHistoryLimit,
|
|
1269
|
+
excludeRecentCount: 0,
|
|
1270
|
+
historyBehavior: provider?.historyBehavior,
|
|
1271
|
+
scripts: provider?.scripts as any,
|
|
1272
|
+
exactSessionScoped: exactNativeHistoryScope,
|
|
1273
|
+
});
|
|
1274
|
+
} catch (error: any) {
|
|
1275
|
+
const fallbackReason = `native_history_error:${error?.message || String(error)}`;
|
|
1276
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1277
|
+
selected: 'pty-parser',
|
|
1278
|
+
provider: adapter.cliType,
|
|
1279
|
+
fallbackReason,
|
|
1280
|
+
ptyMessages: returnedMessages,
|
|
1281
|
+
returnedMessages,
|
|
1282
|
+
ptyStatusApprovalOnly: false,
|
|
1283
|
+
});
|
|
1284
|
+
nativeHistory = null;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
if (nativeHistory) {
|
|
1288
|
+
const nativeMessages = Array.isArray((nativeHistory as any).messages)
|
|
1289
|
+
? normalizeNativeHistoryMessages(agentStr, (nativeHistory as any).messages as ChatMessage[])
|
|
1290
|
+
: [];
|
|
1291
|
+
const historyProviderSessionId = typeof (nativeHistory as any)?.providerSessionId === 'string'
|
|
1292
|
+
? (nativeHistory as any).providerSessionId
|
|
1293
|
+
: readHistorySessionIdFromMessages(nativeMessages) || nativeHistorySessionId || historySessionId;
|
|
1294
|
+
const lookup = (nativeHistory as any).lookup === 'workspace' ? 'workspace' : 'session';
|
|
1295
|
+
const safeMapping = hasSafeNativeHistoryMapping({
|
|
1296
|
+
historySessionId: lookup === 'workspace' ? undefined : nativeHistorySessionId,
|
|
1297
|
+
providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId || providerSessionId,
|
|
1298
|
+
workspace,
|
|
1299
|
+
nativeMessages,
|
|
1300
|
+
ptyMessages: returnedMessages,
|
|
1301
|
+
requireWorkspaceContentOverlap: lookup === 'workspace' && !exactNativeHistoryScope,
|
|
1302
|
+
});
|
|
1303
|
+
const freshEnough = isNativeHistoryFreshEnough({
|
|
1304
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1305
|
+
nativeMessages,
|
|
1306
|
+
ptyMessages: returnedMessages,
|
|
1307
|
+
});
|
|
1308
|
+
if ((nativeHistory as any).source === 'provider-native' && nativeMessages.length > 0 && safeMapping && freshEnough) {
|
|
1309
|
+
selectedMessages = finalizeStreamingMessagesWhenIdle(nativeMessages, returnedStatus);
|
|
1310
|
+
selectedProviderSessionId = historyProviderSessionId || providerSessionId;
|
|
1311
|
+
selectedTranscriptAuthority = 'provider';
|
|
1312
|
+
selectedCoverage = (nativeHistory as any).hasMore ? 'tail' : 'full';
|
|
1313
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1314
|
+
selected: 'native-history',
|
|
1315
|
+
provider: adapter.cliType,
|
|
1316
|
+
nativeHandle: selectedProviderSessionId || nativeHistorySessionId || historySessionId,
|
|
1317
|
+
nativeSource: (nativeHistory as any).source,
|
|
1318
|
+
sourcePath: (nativeHistory as any).sourcePath,
|
|
1319
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1320
|
+
nativeMessages,
|
|
1321
|
+
ptyMessages: returnedMessages,
|
|
1322
|
+
returnedMessages: selectedMessages,
|
|
1323
|
+
safeMapping,
|
|
1324
|
+
freshEnough,
|
|
1325
|
+
ptyStatusApprovalOnly: true,
|
|
1326
|
+
});
|
|
1327
|
+
} else {
|
|
1328
|
+
const fallbackReason = buildNativeHistoryFallbackReason({
|
|
1329
|
+
providerType,
|
|
1330
|
+
provider,
|
|
1331
|
+
nativeSource: (nativeHistory as any).source,
|
|
1332
|
+
nativeMessageCount: nativeMessages.length,
|
|
1333
|
+
safeMapping,
|
|
1334
|
+
freshEnough,
|
|
1335
|
+
});
|
|
1336
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1337
|
+
selected: 'pty-parser',
|
|
1338
|
+
provider: adapter.cliType,
|
|
1339
|
+
nativeHandle: historyProviderSessionId || nativeHistorySessionId || historySessionId,
|
|
1340
|
+
fallbackReason,
|
|
1341
|
+
nativeSource: (nativeHistory as any).source,
|
|
1342
|
+
sourcePath: (nativeHistory as any).sourcePath,
|
|
1343
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1344
|
+
nativeMessages,
|
|
1345
|
+
ptyMessages: returnedMessages,
|
|
1346
|
+
returnedMessages,
|
|
1347
|
+
safeMapping,
|
|
1348
|
+
freshEnough,
|
|
1349
|
+
ptyStatusApprovalOnly: false,
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
885
1354
|
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
1355
|
return buildReadChatCommandResult({
|
|
887
|
-
messages:
|
|
1356
|
+
messages: selectedMessages,
|
|
888
1357
|
status: returnedStatus,
|
|
889
1358
|
activeModal,
|
|
1359
|
+
messageSource,
|
|
1360
|
+
transcriptProvenance: messageSource,
|
|
890
1361
|
debugReadChat: {
|
|
891
1362
|
provider: adapter.cliType,
|
|
892
1363
|
targetSessionId: String(args?.targetSessionId || ''),
|
|
893
1364
|
adapterStatus: String(adapterStatus.status || ''),
|
|
894
1365
|
parsedStatus: String(parsedRecord.status || ''),
|
|
895
1366
|
returnedStatus: String(returnedStatus || ''),
|
|
896
|
-
|
|
1367
|
+
selectedMessageSource: (messageSource as any).selected,
|
|
1368
|
+
messageSource,
|
|
1369
|
+
shouldPreferAdapterMessages: supportsCliNativeTranscript(providerType, provider)
|
|
1370
|
+
&& isNativeSourceCanonicalHistory(provider?.canonicalHistory)
|
|
1371
|
+
&& (messageSource as any).selected !== 'native-history'
|
|
1372
|
+
&& typeof (messageSource as any).fallbackReason === 'string'
|
|
1373
|
+
&& (messageSource as any).fallbackReason.startsWith('native_history_')
|
|
1374
|
+
&& (messageSource as any).fallbackReason !== 'native_history_not_checked'
|
|
1375
|
+
&& !(selectedTranscriptAuthority === 'provider' && selectedCoverage === 'full'),
|
|
897
1376
|
parsedMsgCount: parsedRecord.messages.length,
|
|
898
|
-
returnedMsgCount:
|
|
1377
|
+
returnedMsgCount: selectedMessages.length,
|
|
899
1378
|
},
|
|
900
|
-
...(
|
|
901
|
-
...(
|
|
902
|
-
...(
|
|
903
|
-
...(
|
|
1379
|
+
...(selectedTitle ? { title: selectedTitle } : {}),
|
|
1380
|
+
...(selectedProviderSessionId ? { providerSessionId: selectedProviderSessionId } : {}),
|
|
1381
|
+
...(selectedTranscriptAuthority ? { transcriptAuthority: selectedTranscriptAuthority } : {}),
|
|
1382
|
+
...(selectedCoverage ? { coverage: selectedCoverage } : {}),
|
|
904
1383
|
}, args);
|
|
905
1384
|
}
|
|
906
1385
|
const historyLimit = normalizeReadChatTailLimit(args);
|
|
@@ -911,7 +1390,25 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
911
1390
|
: typeof (h.currentSession as any)?.workspace === 'string'
|
|
912
1391
|
? (h.currentSession as any).workspace
|
|
913
1392
|
: undefined;
|
|
914
|
-
const
|
|
1393
|
+
const exactNativeHistoryScope = Boolean(
|
|
1394
|
+
(typeof args?.targetSessionId === 'string' && args.targetSessionId.trim())
|
|
1395
|
+
|| (typeof args?.historySessionId === 'string' && args.historySessionId.trim())
|
|
1396
|
+
|| (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
|
|
1397
|
+
|| ((h.currentSession as any)?.sessionId === args?.targetSessionId && typeof (h.currentSession as any)?.providerSessionId === 'string' && (h.currentSession as any).providerSessionId.trim())
|
|
1398
|
+
);
|
|
1399
|
+
const history = supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)
|
|
1400
|
+
? readCliProviderNativeHistory(agentStr, {
|
|
1401
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
1402
|
+
historySessionId,
|
|
1403
|
+
workspace,
|
|
1404
|
+
offset: 0,
|
|
1405
|
+
limit: historyLimit,
|
|
1406
|
+
excludeRecentCount: 0,
|
|
1407
|
+
historyBehavior: provider?.historyBehavior,
|
|
1408
|
+
scripts: provider?.scripts as any,
|
|
1409
|
+
exactSessionScoped: exactNativeHistoryScope,
|
|
1410
|
+
})
|
|
1411
|
+
: readProviderChatHistory(agentStr, {
|
|
915
1412
|
canonicalHistory: provider?.canonicalHistory,
|
|
916
1413
|
historySessionId,
|
|
917
1414
|
workspace,
|
|
@@ -921,12 +1418,65 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
921
1418
|
historyBehavior: provider?.historyBehavior,
|
|
922
1419
|
scripts: provider?.scripts as any,
|
|
923
1420
|
});
|
|
1421
|
+
const lookup = (history as any).lookup === 'workspace' ? 'workspace' : 'session';
|
|
1422
|
+
const historyMessages = Array.isArray((history as any)?.messages)
|
|
1423
|
+
? normalizeNativeHistoryMessages(agentStr, (history as any).messages as ChatMessage[])
|
|
1424
|
+
: [];
|
|
924
1425
|
const historyProviderSessionId = typeof (history as any)?.providerSessionId === 'string'
|
|
925
1426
|
? (history as any).providerSessionId
|
|
926
|
-
: historySessionId;
|
|
1427
|
+
: readHistorySessionIdFromMessages(historyMessages) || historySessionId;
|
|
1428
|
+
const safeMapping = supportsCliNativeTranscript(agentStr, provider)
|
|
1429
|
+
? hasSafeNativeHistoryMapping({
|
|
1430
|
+
historySessionId: lookup === 'workspace' ? undefined : historySessionId,
|
|
1431
|
+
providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId,
|
|
1432
|
+
workspace,
|
|
1433
|
+
nativeMessages: historyMessages,
|
|
1434
|
+
})
|
|
1435
|
+
: false;
|
|
1436
|
+
const nativeSelected = supportsCliNativeTranscript(agentStr, provider)
|
|
1437
|
+
&& (history as any).source === 'provider-native'
|
|
1438
|
+
&& historyMessages.length > 0
|
|
1439
|
+
&& safeMapping;
|
|
1440
|
+
const messageSource = buildCliMessageSourceProvenance({
|
|
1441
|
+
selected: nativeSelected ? 'native-history' : 'pty-parser',
|
|
1442
|
+
provider: agentStr,
|
|
1443
|
+
nativeHandle: historyProviderSessionId || historySessionId,
|
|
1444
|
+
fallbackReason: nativeSelected
|
|
1445
|
+
? undefined
|
|
1446
|
+
: buildNativeHistoryFallbackReason({
|
|
1447
|
+
providerType: agentStr,
|
|
1448
|
+
provider,
|
|
1449
|
+
nativeSource: (history as any).source,
|
|
1450
|
+
nativeMessageCount: historyMessages.length,
|
|
1451
|
+
safeMapping,
|
|
1452
|
+
freshEnough: true,
|
|
1453
|
+
}),
|
|
1454
|
+
nativeSource: (history as any).source,
|
|
1455
|
+
sourcePath: (history as any).sourcePath,
|
|
1456
|
+
sourceMtimeMs: (history as any).sourceMtimeMs,
|
|
1457
|
+
nativeMessages: historyMessages,
|
|
1458
|
+
returnedMessages: historyMessages,
|
|
1459
|
+
safeMapping,
|
|
1460
|
+
freshEnough: true,
|
|
1461
|
+
ptyStatusApprovalOnly: false,
|
|
1462
|
+
});
|
|
1463
|
+
const requiresNativeSource = supportsCliNativeTranscript(agentStr, provider)
|
|
1464
|
+
&& isNativeSourceCanonicalHistory(provider?.canonicalHistory);
|
|
1465
|
+
if (requiresNativeSource && !nativeSelected) {
|
|
1466
|
+
return {
|
|
1467
|
+
success: false,
|
|
1468
|
+
code: 'native_history_not_safely_available',
|
|
1469
|
+
error: 'Provider-native history was not safely available for the requested CLI session.',
|
|
1470
|
+
providerSessionId: historyProviderSessionId,
|
|
1471
|
+
messageSource,
|
|
1472
|
+
transcriptProvenance: messageSource,
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
927
1475
|
return buildReadChatCommandResult({
|
|
928
|
-
messages:
|
|
1476
|
+
messages: historyMessages,
|
|
929
1477
|
status: 'idle',
|
|
1478
|
+
messageSource,
|
|
1479
|
+
transcriptProvenance: messageSource,
|
|
930
1480
|
...(typeof (history as any)?.title === 'string' ? { title: (history as any).title } : {}),
|
|
931
1481
|
...(historyProviderSessionId ? { providerSessionId: historyProviderSessionId } : {}),
|
|
932
1482
|
...(((provider?.historyBehavior as any)?.transcriptAuthority === 'provider' || (provider?.historyBehavior as any)?.transcriptAuthority === 'daemon')
|