@adhdev/daemon-core 0.9.82-rc.11 → 0.9.82-rc.111
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 +21 -0
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +14 -0
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/chat-history.d.ts +4 -0
- package/dist/config/mesh-config.d.ts +66 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +6001 -1225
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5967 -1212
- 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 +45 -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 +453 -17
- 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 +32 -10
- package/src/commands/chat-commands.ts +626 -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 +37 -9
- 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 +143 -18
- 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 +50 -0
- package/src/status/reporter.ts +15 -0
- package/src/system/host-memory.ts +29 -12
|
@@ -12,8 +12,9 @@ import type { CliAdapter } from '../cli-adapter-types.js';
|
|
|
12
12
|
import { flattenContent, normalizeInputEnvelope, type InputEnvelope, type ProviderModule, type ProviderScripts } from '../providers/contracts.js';
|
|
13
13
|
import { assertProviderSupportsDeclaredInput, assertTextOnlyInput } from '../providers/provider-input-support.js';
|
|
14
14
|
import { validateReadChatResultPayload } from '../providers/read-chat-contract.js';
|
|
15
|
+
import { pickApprovalButton } from '../providers/approval-utils.js';
|
|
15
16
|
import type { ProviderInstance } from '../providers/provider-instance.js';
|
|
16
|
-
import { readProviderChatHistory } from '../config/chat-history.js';
|
|
17
|
+
import { isNativeSourceCanonicalHistory, readProviderChatHistory } from '../config/chat-history.js';
|
|
17
18
|
import { LOG, getRecentLogs } from '../logging/logger.js';
|
|
18
19
|
import { getRecentDebugTrace, recordDebugTrace } from '../logging/debug-trace.js';
|
|
19
20
|
import { buildChatMessageSignature, hashSignatureParts } from '../chat/chat-signatures.js';
|
|
@@ -24,6 +25,8 @@ import { filterUserFacingChatMessages, normalizeChatMessages } from '../provider
|
|
|
24
25
|
const RECENT_SEND_WINDOW_MS = 1200;
|
|
25
26
|
export const READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25_000;
|
|
26
27
|
const HERMES_CLI_STARTING_SEND_SETTLE_MS = 2_000;
|
|
28
|
+
const CLI_NATIVE_HISTORY_FRESH_MS = 5 * 60_000;
|
|
29
|
+
const CLI_NATIVE_TRANSCRIPT_PROVIDERS = new Set(['codex-cli', 'claude-cli', 'hermes-cli', 'antigravity-cli']);
|
|
27
30
|
const recentSendByTarget = new Map<string, number>();
|
|
28
31
|
|
|
29
32
|
interface ApprovalSelectableInstance extends ProviderInstance {
|
|
@@ -48,6 +51,16 @@ function getTargetedCliAdapter(h: CommandHelpers, args: any, providerType?: stri
|
|
|
48
51
|
return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
function getExplicitHistorySessionId(args: any): string | undefined {
|
|
55
|
+
const explicit = typeof args?.historySessionId === 'string' ? args.historySessionId.trim() : '';
|
|
56
|
+
if (explicit) return explicit;
|
|
57
|
+
|
|
58
|
+
const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
|
|
59
|
+
if (explicitProviderSessionId) return explicitProviderSessionId;
|
|
60
|
+
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
function getTargetInstance(h: CommandHelpers, args: any): ApprovalSelectableInstance | null {
|
|
52
65
|
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
53
66
|
const sessionId = targetSessionId || h.currentSession?.sessionId || '';
|
|
@@ -139,19 +152,47 @@ async function waitOnceForFreshHermesCliStart(adapter: CliAdapter, log: (msg: st
|
|
|
139
152
|
}
|
|
140
153
|
|
|
141
154
|
function getHistorySessionId(h: CommandHelpers, args: any): string | undefined {
|
|
142
|
-
const explicit =
|
|
155
|
+
const explicit = getExplicitHistorySessionId(args);
|
|
143
156
|
if (explicit) return explicit;
|
|
144
157
|
|
|
145
|
-
const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
|
|
146
|
-
if (explicitProviderSessionId) return explicitProviderSessionId;
|
|
147
|
-
|
|
148
158
|
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
149
159
|
if (!targetSessionId) return undefined;
|
|
150
160
|
|
|
151
|
-
const
|
|
161
|
+
const session = h.ctx.sessionRegistry?.get(targetSessionId) as any;
|
|
162
|
+
const registeredProviderSessionId = typeof session?.providerSessionId === 'string' ? session.providerSessionId.trim() : '';
|
|
163
|
+
if (registeredProviderSessionId) return registeredProviderSessionId;
|
|
164
|
+
|
|
165
|
+
const instance = getTargetInstance(h, args);
|
|
152
166
|
const state = instance?.getState?.();
|
|
153
167
|
const providerSessionId = typeof state?.providerSessionId === 'string' ? state.providerSessionId.trim() : '';
|
|
154
|
-
|
|
168
|
+
if (providerSessionId) return providerSessionId;
|
|
169
|
+
|
|
170
|
+
const currentSession = h.currentSession as any;
|
|
171
|
+
if (currentSession?.sessionId === targetSessionId) {
|
|
172
|
+
const currentProviderSessionId = typeof currentSession.providerSessionId === 'string'
|
|
173
|
+
? currentSession.providerSessionId.trim()
|
|
174
|
+
: '';
|
|
175
|
+
if (currentProviderSessionId) return currentProviderSessionId;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return targetSessionId;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function resolveCliNativeHistorySessionId(args: any, currentHistorySessionId: string | undefined, parsedProviderSessionId: string | undefined): string | undefined {
|
|
182
|
+
const explicit = getExplicitHistorySessionId(args);
|
|
183
|
+
if (explicit) return explicit;
|
|
184
|
+
|
|
185
|
+
const parsed = typeof parsedProviderSessionId === 'string' ? parsedProviderSessionId.trim() : '';
|
|
186
|
+
const current = typeof currentHistorySessionId === 'string' ? currentHistorySessionId.trim() : '';
|
|
187
|
+
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
188
|
+
|
|
189
|
+
// getHistorySessionId falls back to the runtime session id when no native
|
|
190
|
+
// handle has been registered yet. For live CLI adapters the parser may
|
|
191
|
+
// already know the provider-native handle; prefer it over the runtime id so
|
|
192
|
+
// exact native reads do not miss the worker transcript and fall back to PTY
|
|
193
|
+
// or same-workspace history.
|
|
194
|
+
if (parsed && (!current || current === targetSessionId)) return parsed;
|
|
195
|
+
return current || parsed || undefined;
|
|
155
196
|
}
|
|
156
197
|
|
|
157
198
|
function getInteractionId(args: any): string | undefined {
|
|
@@ -221,6 +262,272 @@ function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[]
|
|
|
221
262
|
return normalizeChatMessages(messages);
|
|
222
263
|
}
|
|
223
264
|
|
|
265
|
+
function getMessageNewestReceivedAt(messages: Array<{ receivedAt?: unknown; timestamp?: unknown }>): number {
|
|
266
|
+
let newest = 0;
|
|
267
|
+
for (const message of messages) {
|
|
268
|
+
const receivedAt = Number(message?.receivedAt ?? message?.timestamp ?? 0);
|
|
269
|
+
if (Number.isFinite(receivedAt) && receivedAt > newest) newest = receivedAt;
|
|
270
|
+
}
|
|
271
|
+
return newest;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function readHistorySessionIdFromMessages(messages: ChatMessage[]): string | undefined {
|
|
275
|
+
for (const message of messages as Array<ChatMessage & { historySessionId?: unknown }>) {
|
|
276
|
+
const historySessionId = typeof message?.historySessionId === 'string' ? message.historySessionId.trim() : '';
|
|
277
|
+
if (historySessionId) return historySessionId;
|
|
278
|
+
}
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function normalizeNativeHistoryMessages(providerType: string, messages: ChatMessage[]): ChatMessage[] {
|
|
283
|
+
let turnIndex = 0;
|
|
284
|
+
return normalizeChatMessages(messages).map((message, index) => {
|
|
285
|
+
const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
|
|
286
|
+
const kind = typeof message.kind === 'string' && message.kind.trim() ? message.kind.trim() : (role === 'system' ? 'system' : 'standard');
|
|
287
|
+
if ((role === 'user' || role === 'human') && index > 0) turnIndex += 1;
|
|
288
|
+
const historySessionId = typeof (message as any).historySessionId === 'string'
|
|
289
|
+
? (message as any).historySessionId.trim()
|
|
290
|
+
: '';
|
|
291
|
+
const contentHash = hashSignatureParts([
|
|
292
|
+
providerType,
|
|
293
|
+
historySessionId,
|
|
294
|
+
String(message.receivedAt || message.timestamp || index),
|
|
295
|
+
role,
|
|
296
|
+
kind,
|
|
297
|
+
flattenContent(message.content),
|
|
298
|
+
]).slice(0, 12);
|
|
299
|
+
const providerUnitKey = typeof message.providerUnitKey === 'string' && message.providerUnitKey.trim()
|
|
300
|
+
? message.providerUnitKey.trim()
|
|
301
|
+
: `${providerType}:native:${historySessionId || 'workspace'}:${index}:${role || 'message'}:${kind}:${contentHash}`;
|
|
302
|
+
const meta = message.meta && typeof message.meta === 'object' ? message.meta as Record<string, unknown> : undefined;
|
|
303
|
+
const isSystemSessionStart = role === 'system' || kind === 'system' || kind === 'session_start';
|
|
304
|
+
const isActivity = role === 'assistant' && (kind === 'tool' || kind === 'terminal' || kind === 'thought');
|
|
305
|
+
return {
|
|
306
|
+
...message,
|
|
307
|
+
role: role === 'human' ? 'user' : (role || 'assistant'),
|
|
308
|
+
kind: isSystemSessionStart ? 'system' : kind,
|
|
309
|
+
providerUnitKey,
|
|
310
|
+
bubbleId: typeof message.bubbleId === 'string' && message.bubbleId.trim()
|
|
311
|
+
? message.bubbleId.trim()
|
|
312
|
+
: `bubble:${providerUnitKey}`,
|
|
313
|
+
_turnKey: typeof message._turnKey === 'string' && message._turnKey.trim()
|
|
314
|
+
? message._turnKey.trim()
|
|
315
|
+
: `${providerType}:native-turn:${historySessionId || 'workspace'}:${turnIndex}`,
|
|
316
|
+
bubbleState: message.bubbleState || 'final',
|
|
317
|
+
...(isSystemSessionStart ? {
|
|
318
|
+
visibility: message.visibility || 'hidden',
|
|
319
|
+
transcriptVisibility: message.transcriptVisibility || 'hidden',
|
|
320
|
+
audience: message.audience || 'internal',
|
|
321
|
+
source: message.source || 'runtime_status',
|
|
322
|
+
} : isActivity ? {
|
|
323
|
+
source: message.source || (kind === 'terminal' ? 'terminal_command' : 'tool_call'),
|
|
324
|
+
meta: { ...meta, label: message.senderName || meta?.label || (kind === 'terminal' ? 'Terminal' : 'Tool') },
|
|
325
|
+
} : {
|
|
326
|
+
source: message.source || (role === 'assistant' ? 'assistant_text' : undefined),
|
|
327
|
+
}),
|
|
328
|
+
} as ChatMessage;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function buildCliMessageSourceProvenance(args: {
|
|
333
|
+
selected: 'native-history' | 'pty-parser';
|
|
334
|
+
provider: string;
|
|
335
|
+
nativeHandle?: string;
|
|
336
|
+
fallbackReason?: string;
|
|
337
|
+
nativeSource?: string;
|
|
338
|
+
sourcePath?: string;
|
|
339
|
+
sourceMtimeMs?: number;
|
|
340
|
+
nativeHistoryCoverage?: string;
|
|
341
|
+
partialReason?: string;
|
|
342
|
+
unavailableReason?: string;
|
|
343
|
+
nativeMessages?: ChatMessage[];
|
|
344
|
+
ptyMessages?: ChatMessage[];
|
|
345
|
+
returnedMessages?: ChatMessage[];
|
|
346
|
+
safeMapping?: boolean;
|
|
347
|
+
freshEnough?: boolean;
|
|
348
|
+
ptyStatusApprovalOnly?: boolean;
|
|
349
|
+
}): Record<string, unknown> {
|
|
350
|
+
const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
|
|
351
|
+
const sourceMtimeAgeMs = sourceMtimeMs > 0 ? Math.max(0, Date.now() - sourceMtimeMs) : undefined;
|
|
352
|
+
const nativeMessages = args.nativeMessages || [];
|
|
353
|
+
const ptyMessages = args.ptyMessages || [];
|
|
354
|
+
const returnedMessages = args.returnedMessages || [];
|
|
355
|
+
return {
|
|
356
|
+
selected: args.selected,
|
|
357
|
+
provider: args.provider,
|
|
358
|
+
providerType: args.provider,
|
|
359
|
+
...(args.nativeHandle ? { nativeHandle: args.nativeHandle } : {}),
|
|
360
|
+
...(args.nativeHandle ? { nativeSessionId: args.nativeHandle } : {}),
|
|
361
|
+
...(args.fallbackReason ? { fallbackReason: args.fallbackReason } : {}),
|
|
362
|
+
...(args.nativeSource ? { nativeSource: args.nativeSource } : {}),
|
|
363
|
+
...(args.sourcePath ? { sourcePath: args.sourcePath } : {}),
|
|
364
|
+
...(args.nativeHistoryCoverage ? { nativeHistoryCoverage: args.nativeHistoryCoverage } : {}),
|
|
365
|
+
...(args.partialReason ? { partialReason: args.partialReason } : {}),
|
|
366
|
+
...(args.unavailableReason ? { unavailableReason: args.unavailableReason } : {}),
|
|
367
|
+
ptyStatusApprovalOnly: args.ptyStatusApprovalOnly === true,
|
|
368
|
+
staleness: {
|
|
369
|
+
sourceMtimeMs: sourceMtimeMs || undefined,
|
|
370
|
+
sourceMtimeAgeMs,
|
|
371
|
+
nativeNewestMessageAt: getMessageNewestReceivedAt(nativeMessages),
|
|
372
|
+
ptyNewestMessageAt: getMessageNewestReceivedAt(ptyMessages),
|
|
373
|
+
freshEnough: args.freshEnough === true,
|
|
374
|
+
},
|
|
375
|
+
coverage: {
|
|
376
|
+
nativeMessageCount: nativeMessages.length,
|
|
377
|
+
ptyMessageCount: ptyMessages.length,
|
|
378
|
+
returnedMessageCount: returnedMessages.length,
|
|
379
|
+
safeMapping: args.safeMapping === true,
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function buildNativeHistoryFallbackReason(args: {
|
|
385
|
+
providerType: string;
|
|
386
|
+
provider?: ProviderModule;
|
|
387
|
+
nativeSource?: string;
|
|
388
|
+
nativeHistoryCoverage?: string;
|
|
389
|
+
nativeMessageCount: number;
|
|
390
|
+
safeMapping: boolean;
|
|
391
|
+
freshEnough: boolean;
|
|
392
|
+
}): string {
|
|
393
|
+
if (!supportsCliNativeTranscript(args.providerType, args.provider)) return 'provider_native_transcript_not_supported';
|
|
394
|
+
if (args.nativeSource === 'native-unavailable') return 'native_history_unavailable';
|
|
395
|
+
if (args.nativeHistoryCoverage === 'partial') return 'native_history_partial';
|
|
396
|
+
if (args.nativeHistoryCoverage === 'unavailable') return 'native_history_unavailable';
|
|
397
|
+
if (args.nativeSource && args.nativeSource !== 'provider-native') return `native_history_source_${args.nativeSource}`;
|
|
398
|
+
if (args.nativeMessageCount <= 0) return 'native_history_empty';
|
|
399
|
+
if (!args.safeMapping) return 'native_history_not_safely_mapped';
|
|
400
|
+
if (!args.freshEnough) return 'native_history_stale';
|
|
401
|
+
return 'native_history_not_selected';
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function supportsCliNativeTranscript(providerType: string, provider?: ProviderModule): boolean {
|
|
405
|
+
if (CLI_NATIVE_TRANSCRIPT_PROVIDERS.has(providerType)) return true;
|
|
406
|
+
return provider?.category === 'cli' && isNativeSourceCanonicalHistory(provider?.canonicalHistory);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function getComparableVisibleText(message: ChatMessage | undefined): string {
|
|
410
|
+
if (!message) return '';
|
|
411
|
+
const role = String((message as any).role || '').trim().toLowerCase();
|
|
412
|
+
if (role !== 'user' && role !== 'assistant') return '';
|
|
413
|
+
const kind = String((message as any).kind || 'standard').trim().toLowerCase();
|
|
414
|
+
if (kind && kind !== 'standard') return '';
|
|
415
|
+
const content = flattenContent((message as any).content).replace(/\s+/g, ' ').trim();
|
|
416
|
+
return content;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function hasOverlappingVisibleConversationText(nativeMessages: ChatMessage[], ptyMessages: ChatMessage[]): boolean {
|
|
420
|
+
const nativeTexts = nativeMessages.map(getComparableVisibleText).filter(Boolean);
|
|
421
|
+
const ptyTexts = ptyMessages.map(getComparableVisibleText).filter(Boolean);
|
|
422
|
+
if (nativeTexts.length === 0 || ptyTexts.length === 0) return false;
|
|
423
|
+
for (const nativeText of nativeTexts) {
|
|
424
|
+
for (const ptyText of ptyTexts) {
|
|
425
|
+
if (nativeText === ptyText) return true;
|
|
426
|
+
const shorter = nativeText.length <= ptyText.length ? nativeText : ptyText;
|
|
427
|
+
const longer = nativeText.length <= ptyText.length ? ptyText : nativeText;
|
|
428
|
+
if (shorter.length >= 32 && longer.includes(shorter)) return true;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function hasSafeNativeHistoryMapping(args: {
|
|
435
|
+
historySessionId?: string;
|
|
436
|
+
providerSessionId?: string;
|
|
437
|
+
workspace?: string;
|
|
438
|
+
nativeMessages: ChatMessage[];
|
|
439
|
+
ptyMessages?: ChatMessage[];
|
|
440
|
+
requireWorkspaceContentOverlap?: boolean;
|
|
441
|
+
}): boolean {
|
|
442
|
+
const explicitSessionId = String(args.historySessionId || args.providerSessionId || '').trim();
|
|
443
|
+
if (explicitSessionId) {
|
|
444
|
+
const messageSessionIds = args.nativeMessages
|
|
445
|
+
.map((message: any) => typeof message?.historySessionId === 'string' ? message.historySessionId.trim() : '')
|
|
446
|
+
.filter(Boolean);
|
|
447
|
+
if (messageSessionIds.length === 0) return true;
|
|
448
|
+
return messageSessionIds.some((id) => id === explicitSessionId);
|
|
449
|
+
}
|
|
450
|
+
const workspace = String(args.workspace || '').trim();
|
|
451
|
+
if (!workspace) return false;
|
|
452
|
+
const workspaceMatches = args.nativeMessages.some((message: any) => String(message?.workspace || '').trim() === workspace);
|
|
453
|
+
if (!workspaceMatches) return false;
|
|
454
|
+
if (!args.requireWorkspaceContentOverlap) return true;
|
|
455
|
+
return hasOverlappingVisibleConversationText(args.nativeMessages, args.ptyMessages || []);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function readCliProviderNativeHistory(agentStr: string, args: {
|
|
459
|
+
canonicalHistory?: ProviderModule['canonicalHistory'];
|
|
460
|
+
historySessionId?: string;
|
|
461
|
+
workspace?: string;
|
|
462
|
+
offset: number;
|
|
463
|
+
limit: number;
|
|
464
|
+
excludeRecentCount: number;
|
|
465
|
+
historyBehavior?: ProviderModule['historyBehavior'];
|
|
466
|
+
scripts?: ProviderScripts;
|
|
467
|
+
exactSessionScoped?: boolean;
|
|
468
|
+
}): ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' } {
|
|
469
|
+
const sessionHistory = readProviderChatHistory(agentStr, {
|
|
470
|
+
canonicalHistory: args.canonicalHistory,
|
|
471
|
+
historySessionId: args.historySessionId,
|
|
472
|
+
workspace: args.workspace,
|
|
473
|
+
offset: args.offset,
|
|
474
|
+
limit: args.limit,
|
|
475
|
+
excludeRecentCount: args.excludeRecentCount,
|
|
476
|
+
historyBehavior: args.historyBehavior,
|
|
477
|
+
scripts: args.scripts as any,
|
|
478
|
+
});
|
|
479
|
+
// Exact runtime/provider transcript reads must not silently fall back to the
|
|
480
|
+
// workspace's active or most recent native transcript: multiple Hermes/Gemini/
|
|
481
|
+
// Codex sessions can run in the same workspace, and workspace fallback can make
|
|
482
|
+
// read_chat/completion evidence point at a different runtime's prompt.
|
|
483
|
+
if ((sessionHistory as any).source !== 'native-unavailable' || args.exactSessionScoped || !args.historySessionId || !args.workspace) {
|
|
484
|
+
return { ...(sessionHistory as any), lookup: args.historySessionId ? 'session' : 'workspace' };
|
|
485
|
+
}
|
|
486
|
+
const workspaceHistory = readProviderChatHistory(agentStr, {
|
|
487
|
+
canonicalHistory: args.canonicalHistory,
|
|
488
|
+
historySessionId: undefined,
|
|
489
|
+
workspace: args.workspace,
|
|
490
|
+
offset: args.offset,
|
|
491
|
+
limit: args.limit,
|
|
492
|
+
excludeRecentCount: args.excludeRecentCount,
|
|
493
|
+
historyBehavior: args.historyBehavior,
|
|
494
|
+
scripts: args.scripts as any,
|
|
495
|
+
});
|
|
496
|
+
return { ...(workspaceHistory as any), lookup: 'workspace' };
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function isNativeHistoryFreshEnough(args: {
|
|
500
|
+
sourceMtimeMs?: number;
|
|
501
|
+
nativeMessages: ChatMessage[];
|
|
502
|
+
ptyMessages: ChatMessage[];
|
|
503
|
+
}): boolean {
|
|
504
|
+
const nativeNewest = getMessageNewestReceivedAt(args.nativeMessages);
|
|
505
|
+
const ptyNewest = getMessageNewestReceivedAt(args.ptyMessages);
|
|
506
|
+
if (nativeNewest > 0 && nativeNewest >= ptyNewest) return true;
|
|
507
|
+
const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
|
|
508
|
+
if (sourceMtimeMs > 0 && Date.now() - sourceMtimeMs <= CLI_NATIVE_HISTORY_FRESH_MS) return true;
|
|
509
|
+
return ptyNewest === 0 && nativeNewest > 0;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function shouldPreserveReadChatPayloadField(key: string): boolean {
|
|
513
|
+
return key === 'messageSource' || key === 'transcriptProvenance';
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function updateMessageSourceReturnedCount(value: unknown, returnedMessageCount: number): unknown {
|
|
517
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return value;
|
|
518
|
+
const record = value as Record<string, unknown>;
|
|
519
|
+
const coverage = record.coverage && typeof record.coverage === 'object' && !Array.isArray(record.coverage)
|
|
520
|
+
? record.coverage as Record<string, unknown>
|
|
521
|
+
: undefined;
|
|
522
|
+
if (!coverage) return value;
|
|
523
|
+
return {
|
|
524
|
+
...record,
|
|
525
|
+
coverage: {
|
|
526
|
+
...coverage,
|
|
527
|
+
returnedMessageCount,
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
}
|
|
224
531
|
|
|
225
532
|
function deriveHistoryDedupKey(message: ChatMessage & { _unitKey?: string; _turnKey?: string }): string | undefined {
|
|
226
533
|
const unitKey = typeof message._unitKey === 'string' ? message._unitKey.trim() : '';
|
|
@@ -281,7 +588,7 @@ function normalizeReadChatCommandStatus(status: unknown, activeModal: unknown):
|
|
|
281
588
|
}
|
|
282
589
|
switch (raw) {
|
|
283
590
|
case 'starting':
|
|
284
|
-
return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : '
|
|
591
|
+
return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : 'starting';
|
|
285
592
|
case 'stopped':
|
|
286
593
|
case 'disconnected':
|
|
287
594
|
case 'not_monitored':
|
|
@@ -295,6 +602,16 @@ function isGeneratingLikeStatus(status: unknown): boolean {
|
|
|
295
602
|
return status === 'generating' || status === 'streaming' || status === 'long_generating' || status === 'starting';
|
|
296
603
|
}
|
|
297
604
|
|
|
605
|
+
function hasVisibleAssistantMessage(messages: unknown[] | undefined): boolean {
|
|
606
|
+
if (!Array.isArray(messages)) return false;
|
|
607
|
+
return messages.some((message: any) => {
|
|
608
|
+
if (!message || message.role !== 'assistant') return false;
|
|
609
|
+
const kind = typeof message.kind === 'string' ? message.kind : 'standard';
|
|
610
|
+
if (kind !== 'standard') return false;
|
|
611
|
+
return String(message.content || '').trim().length > 0;
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
298
615
|
function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): boolean {
|
|
299
616
|
if (!isGeneratingLikeStatus(parsedStatus)) return false;
|
|
300
617
|
if (hasNonEmptyModalButtons(activeModal)) return false;
|
|
@@ -304,7 +621,26 @@ function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal:
|
|
|
304
621
|
return true;
|
|
305
622
|
}
|
|
306
623
|
|
|
307
|
-
function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): string {
|
|
624
|
+
function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any, parsedMessages?: unknown[]): string {
|
|
625
|
+
const adapterRawStatus = typeof adapterStatus?.status === 'string' ? adapterStatus.status.trim() : '';
|
|
626
|
+
if (adapterRawStatus === 'starting'
|
|
627
|
+
&& isGeneratingLikeStatus(parsedStatus)
|
|
628
|
+
&& !hasNonEmptyModalButtons(activeModal)
|
|
629
|
+
&& Array.isArray(parsedMessages)
|
|
630
|
+
&& parsedMessages.length === 0
|
|
631
|
+
&& Array.isArray(adapterStatus?.messages)
|
|
632
|
+
&& adapterStatus.messages.length === 0
|
|
633
|
+
&& !(typeof adapter.isProcessing === 'function' && adapter.isProcessing())) {
|
|
634
|
+
return 'starting';
|
|
635
|
+
}
|
|
636
|
+
if (
|
|
637
|
+
isGeneratingLikeStatus(adapterRawStatus)
|
|
638
|
+
&& parsedStatus === 'idle'
|
|
639
|
+
&& !hasNonEmptyModalButtons(activeModal)
|
|
640
|
+
&& !hasVisibleAssistantMessage(parsedMessages)
|
|
641
|
+
) {
|
|
642
|
+
return adapterRawStatus;
|
|
643
|
+
}
|
|
308
644
|
if (shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus)) return 'idle';
|
|
309
645
|
return typeof parsedStatus === 'string' && parsedStatus.trim() ? parsedStatus : 'idle';
|
|
310
646
|
}
|
|
@@ -342,6 +678,13 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
|
|
|
342
678
|
const visibleMessages = filterUserFacingChatMessages(messages);
|
|
343
679
|
const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
|
|
344
680
|
const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
|
|
681
|
+
const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key)));
|
|
682
|
+
if (preservedPayloadFields.messageSource) {
|
|
683
|
+
preservedPayloadFields.messageSource = updateMessageSourceReturnedCount(preservedPayloadFields.messageSource, sync.messages.length);
|
|
684
|
+
}
|
|
685
|
+
if (preservedPayloadFields.transcriptProvenance) {
|
|
686
|
+
preservedPayloadFields.transcriptProvenance = updateMessageSourceReturnedCount(preservedPayloadFields.transcriptProvenance, sync.messages.length);
|
|
687
|
+
}
|
|
345
688
|
const returnedDebugReadChat = debugReadChat
|
|
346
689
|
? {
|
|
347
690
|
...debugReadChat,
|
|
@@ -356,6 +699,7 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
|
|
|
356
699
|
return {
|
|
357
700
|
success: true,
|
|
358
701
|
...validatedPayload,
|
|
702
|
+
...preservedPayloadFields,
|
|
359
703
|
messages: sync.messages,
|
|
360
704
|
totalMessages: sync.totalMessages,
|
|
361
705
|
...(returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}),
|
|
@@ -584,6 +928,8 @@ function buildChatDebugBundleSummary(bundle: Record<string, unknown>): Record<st
|
|
|
584
928
|
adapterStatus: debugReadChat.adapterStatus,
|
|
585
929
|
parsedStatus: debugReadChat.parsedStatus,
|
|
586
930
|
returnedStatus: debugReadChat.returnedStatus,
|
|
931
|
+
selectedMessageSource: debugReadChat.selectedMessageSource,
|
|
932
|
+
messageSource: debugReadChat.messageSource,
|
|
587
933
|
parsedMsgCount: debugReadChat.parsedMsgCount,
|
|
588
934
|
returnedMsgCount: debugReadChat.returnedMsgCount,
|
|
589
935
|
shouldPreferAdapterMessages: debugReadChat.shouldPreferAdapterMessages,
|
|
@@ -646,6 +992,8 @@ export async function handleGetChatDebugBundle(h: CommandHelpers, args: any): Pr
|
|
|
646
992
|
providerSessionId: readResult.providerSessionId,
|
|
647
993
|
transcriptAuthority: readResult.transcriptAuthority,
|
|
648
994
|
coverage: readResult.coverage,
|
|
995
|
+
messageSource: readResult.messageSource,
|
|
996
|
+
transcriptProvenance: readResult.transcriptProvenance,
|
|
649
997
|
activeModal: readResult.activeModal,
|
|
650
998
|
messagesTail: Array.isArray(readResult.messages) ? readResult.messages.slice(-20) : [],
|
|
651
999
|
debugReadChat: readResult.debugReadChat,
|
|
@@ -874,7 +1222,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
874
1222
|
? parsedRecord.coverage
|
|
875
1223
|
: undefined;
|
|
876
1224
|
const activeModal = parsedRecord.activeModal ?? parsedRecord.modal ?? null;
|
|
877
|
-
const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus);
|
|
1225
|
+
const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus, parsedRecord.messages);
|
|
878
1226
|
const runtimeMessageMerger = getTargetInstance(h, args) as RuntimeChatMessageMerger | null;
|
|
879
1227
|
const parsedMessages = finalizeStreamingMessagesWhenIdle(parsedRecord.messages as ChatMessage[], returnedStatus);
|
|
880
1228
|
const returnedMessages = runtimeMessageMerger?.category === 'cli'
|
|
@@ -882,25 +1230,194 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
882
1230
|
&& typeof runtimeMessageMerger.mergeRuntimeChatMessages === 'function'
|
|
883
1231
|
? runtimeMessageMerger.mergeRuntimeChatMessages(parsedMessages)
|
|
884
1232
|
: parsedMessages;
|
|
1233
|
+
const providerType = provider?.type || adapter.cliType;
|
|
1234
|
+
let selectedMessages = returnedMessages;
|
|
1235
|
+
let selectedTitle = title;
|
|
1236
|
+
let selectedProviderSessionId = providerSessionId;
|
|
1237
|
+
let selectedTranscriptAuthority = transcriptAuthority;
|
|
1238
|
+
let selectedCoverage = coverage;
|
|
1239
|
+
let messageSource = buildCliMessageSourceProvenance({
|
|
1240
|
+
selected: 'pty-parser',
|
|
1241
|
+
provider: adapter.cliType,
|
|
1242
|
+
fallbackReason: supportsCliNativeTranscript(providerType, provider) ? 'native_history_not_checked' : 'provider_native_transcript_not_supported',
|
|
1243
|
+
ptyMessages: returnedMessages,
|
|
1244
|
+
returnedMessages,
|
|
1245
|
+
ptyStatusApprovalOnly: false,
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
if (supportsCliNativeTranscript(providerType, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)) {
|
|
1249
|
+
const agentStr = provider?.type || args?.agentType || getCurrentProviderType(h, adapter.cliType);
|
|
1250
|
+
const workspace = typeof args?.workspace === 'string'
|
|
1251
|
+
? args.workspace
|
|
1252
|
+
: typeof (h.currentSession as any)?.workspace === 'string'
|
|
1253
|
+
? (h.currentSession as any).workspace
|
|
1254
|
+
: typeof adapter.workingDir === 'string'
|
|
1255
|
+
? adapter.workingDir
|
|
1256
|
+
: undefined;
|
|
1257
|
+
const nativeHistoryLimit = Math.max(
|
|
1258
|
+
normalizeReadChatTailLimit(args) || 0,
|
|
1259
|
+
returnedMessages.length,
|
|
1260
|
+
200,
|
|
1261
|
+
);
|
|
1262
|
+
const nativeHistorySessionId = resolveCliNativeHistorySessionId(args, historySessionId, providerSessionId);
|
|
1263
|
+
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
1264
|
+
const exactNativeHistoryScope = Boolean(
|
|
1265
|
+
(typeof args?.historySessionId === 'string' && args.historySessionId.trim())
|
|
1266
|
+
|| (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
|
|
1267
|
+
|| providerSessionId
|
|
1268
|
+
|| (nativeHistorySessionId && nativeHistorySessionId !== targetSessionId)
|
|
1269
|
+
|| ((h.currentSession as any)?.sessionId === args?.targetSessionId && typeof (h.currentSession as any)?.providerSessionId === 'string' && (h.currentSession as any).providerSessionId.trim())
|
|
1270
|
+
);
|
|
1271
|
+
let nativeHistory: (ReturnType<typeof readProviderChatHistory> & { lookup?: 'session' | 'workspace' }) | null = null;
|
|
1272
|
+
try {
|
|
1273
|
+
nativeHistory = readCliProviderNativeHistory(agentStr, {
|
|
1274
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
1275
|
+
historySessionId: nativeHistorySessionId,
|
|
1276
|
+
workspace,
|
|
1277
|
+
offset: 0,
|
|
1278
|
+
limit: nativeHistoryLimit,
|
|
1279
|
+
excludeRecentCount: 0,
|
|
1280
|
+
historyBehavior: provider?.historyBehavior,
|
|
1281
|
+
scripts: provider?.scripts as any,
|
|
1282
|
+
exactSessionScoped: exactNativeHistoryScope,
|
|
1283
|
+
});
|
|
1284
|
+
} catch (error: any) {
|
|
1285
|
+
const fallbackReason = `native_history_error:${error?.message || String(error)}`;
|
|
1286
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1287
|
+
selected: 'pty-parser',
|
|
1288
|
+
provider: adapter.cliType,
|
|
1289
|
+
fallbackReason,
|
|
1290
|
+
ptyMessages: returnedMessages,
|
|
1291
|
+
returnedMessages,
|
|
1292
|
+
ptyStatusApprovalOnly: false,
|
|
1293
|
+
});
|
|
1294
|
+
nativeHistory = null;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
if (nativeHistory) {
|
|
1298
|
+
const nativeMessages = Array.isArray((nativeHistory as any).messages)
|
|
1299
|
+
? normalizeNativeHistoryMessages(agentStr, (nativeHistory as any).messages as ChatMessage[])
|
|
1300
|
+
: [];
|
|
1301
|
+
const historyProviderSessionId = typeof (nativeHistory as any)?.providerSessionId === 'string'
|
|
1302
|
+
? (nativeHistory as any).providerSessionId
|
|
1303
|
+
: readHistorySessionIdFromMessages(nativeMessages) || nativeHistorySessionId || historySessionId;
|
|
1304
|
+
const nativeHistoryCoverage = typeof (nativeHistory as any)?.nativeHistoryCoverage === 'string'
|
|
1305
|
+
? (nativeHistory as any).nativeHistoryCoverage
|
|
1306
|
+
: undefined;
|
|
1307
|
+
const partialReason = typeof (nativeHistory as any)?.partialReason === 'string'
|
|
1308
|
+
? (nativeHistory as any).partialReason
|
|
1309
|
+
: undefined;
|
|
1310
|
+
const unavailableReason = typeof (nativeHistory as any)?.unavailableReason === 'string'
|
|
1311
|
+
? (nativeHistory as any).unavailableReason
|
|
1312
|
+
: undefined;
|
|
1313
|
+
const lookup = (nativeHistory as any).lookup === 'workspace' ? 'workspace' : 'session';
|
|
1314
|
+
const nativeHistorySessionForMapping = adapter.cliType === 'antigravity-cli'
|
|
1315
|
+
&& historyProviderSessionId
|
|
1316
|
+
&& nativeHistorySessionId
|
|
1317
|
+
&& historyProviderSessionId !== nativeHistorySessionId
|
|
1318
|
+
? undefined
|
|
1319
|
+
: nativeHistorySessionId;
|
|
1320
|
+
const safeMapping = hasSafeNativeHistoryMapping({
|
|
1321
|
+
historySessionId: lookup === 'workspace' ? undefined : nativeHistorySessionForMapping,
|
|
1322
|
+
providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId || providerSessionId,
|
|
1323
|
+
workspace,
|
|
1324
|
+
nativeMessages,
|
|
1325
|
+
ptyMessages: returnedMessages,
|
|
1326
|
+
requireWorkspaceContentOverlap: lookup === 'workspace' && !exactNativeHistoryScope,
|
|
1327
|
+
});
|
|
1328
|
+
const freshEnough = isNativeHistoryFreshEnough({
|
|
1329
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1330
|
+
nativeMessages,
|
|
1331
|
+
ptyMessages: returnedMessages,
|
|
1332
|
+
});
|
|
1333
|
+
const nativeUsableForChatMessages = (nativeHistory as any).source === 'provider-native'
|
|
1334
|
+
&& nativeMessages.length > 0
|
|
1335
|
+
&& nativeHistoryCoverage !== 'partial'
|
|
1336
|
+
&& nativeHistoryCoverage !== 'unavailable'
|
|
1337
|
+
&& safeMapping;
|
|
1338
|
+
const allowStaleNativeChatMessages = adapter.cliType === 'antigravity-cli' && nativeUsableForChatMessages;
|
|
1339
|
+
if (nativeUsableForChatMessages && (freshEnough || allowStaleNativeChatMessages)) {
|
|
1340
|
+
selectedMessages = finalizeStreamingMessagesWhenIdle(nativeMessages, returnedStatus);
|
|
1341
|
+
selectedProviderSessionId = historyProviderSessionId || providerSessionId;
|
|
1342
|
+
selectedTranscriptAuthority = 'provider';
|
|
1343
|
+
selectedCoverage = (nativeHistory as any).hasMore ? 'tail' : 'full';
|
|
1344
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1345
|
+
selected: 'native-history',
|
|
1346
|
+
provider: adapter.cliType,
|
|
1347
|
+
nativeHandle: selectedProviderSessionId || nativeHistorySessionId || historySessionId,
|
|
1348
|
+
nativeSource: (nativeHistory as any).source,
|
|
1349
|
+
sourcePath: (nativeHistory as any).sourcePath,
|
|
1350
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1351
|
+
nativeHistoryCoverage,
|
|
1352
|
+
partialReason,
|
|
1353
|
+
unavailableReason,
|
|
1354
|
+
nativeMessages,
|
|
1355
|
+
ptyMessages: returnedMessages,
|
|
1356
|
+
returnedMessages: selectedMessages,
|
|
1357
|
+
safeMapping,
|
|
1358
|
+
freshEnough,
|
|
1359
|
+
ptyStatusApprovalOnly: true,
|
|
1360
|
+
});
|
|
1361
|
+
} else {
|
|
1362
|
+
const fallbackReason = buildNativeHistoryFallbackReason({
|
|
1363
|
+
providerType,
|
|
1364
|
+
provider,
|
|
1365
|
+
nativeSource: (nativeHistory as any).source,
|
|
1366
|
+
nativeHistoryCoverage,
|
|
1367
|
+
nativeMessageCount: nativeMessages.length,
|
|
1368
|
+
safeMapping,
|
|
1369
|
+
freshEnough,
|
|
1370
|
+
});
|
|
1371
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1372
|
+
selected: 'pty-parser',
|
|
1373
|
+
provider: adapter.cliType,
|
|
1374
|
+
nativeHandle: historyProviderSessionId || nativeHistorySessionId || historySessionId,
|
|
1375
|
+
fallbackReason,
|
|
1376
|
+
nativeSource: (nativeHistory as any).source,
|
|
1377
|
+
sourcePath: (nativeHistory as any).sourcePath,
|
|
1378
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1379
|
+
nativeHistoryCoverage,
|
|
1380
|
+
partialReason,
|
|
1381
|
+
unavailableReason,
|
|
1382
|
+
nativeMessages,
|
|
1383
|
+
ptyMessages: returnedMessages,
|
|
1384
|
+
returnedMessages,
|
|
1385
|
+
safeMapping,
|
|
1386
|
+
freshEnough,
|
|
1387
|
+
ptyStatusApprovalOnly: false,
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
885
1392
|
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
1393
|
return buildReadChatCommandResult({
|
|
887
|
-
messages:
|
|
1394
|
+
messages: selectedMessages,
|
|
888
1395
|
status: returnedStatus,
|
|
889
1396
|
activeModal,
|
|
1397
|
+
messageSource,
|
|
1398
|
+
transcriptProvenance: messageSource,
|
|
890
1399
|
debugReadChat: {
|
|
891
1400
|
provider: adapter.cliType,
|
|
892
1401
|
targetSessionId: String(args?.targetSessionId || ''),
|
|
893
1402
|
adapterStatus: String(adapterStatus.status || ''),
|
|
894
1403
|
parsedStatus: String(parsedRecord.status || ''),
|
|
895
1404
|
returnedStatus: String(returnedStatus || ''),
|
|
896
|
-
|
|
1405
|
+
selectedMessageSource: (messageSource as any).selected,
|
|
1406
|
+
messageSource,
|
|
1407
|
+
shouldPreferAdapterMessages: supportsCliNativeTranscript(providerType, provider)
|
|
1408
|
+
&& isNativeSourceCanonicalHistory(provider?.canonicalHistory)
|
|
1409
|
+
&& (messageSource as any).selected !== 'native-history'
|
|
1410
|
+
&& typeof (messageSource as any).fallbackReason === 'string'
|
|
1411
|
+
&& (messageSource as any).fallbackReason.startsWith('native_history_')
|
|
1412
|
+
&& (messageSource as any).fallbackReason !== 'native_history_not_checked'
|
|
1413
|
+
&& !(selectedTranscriptAuthority === 'provider' && selectedCoverage === 'full'),
|
|
897
1414
|
parsedMsgCount: parsedRecord.messages.length,
|
|
898
|
-
returnedMsgCount:
|
|
1415
|
+
returnedMsgCount: selectedMessages.length,
|
|
899
1416
|
},
|
|
900
|
-
...(
|
|
901
|
-
...(
|
|
902
|
-
...(
|
|
903
|
-
...(
|
|
1417
|
+
...(selectedTitle ? { title: selectedTitle } : {}),
|
|
1418
|
+
...(selectedProviderSessionId ? { providerSessionId: selectedProviderSessionId } : {}),
|
|
1419
|
+
...(selectedTranscriptAuthority ? { transcriptAuthority: selectedTranscriptAuthority } : {}),
|
|
1420
|
+
...(selectedCoverage ? { coverage: selectedCoverage } : {}),
|
|
904
1421
|
}, args);
|
|
905
1422
|
}
|
|
906
1423
|
const historyLimit = normalizeReadChatTailLimit(args);
|
|
@@ -911,7 +1428,25 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
911
1428
|
: typeof (h.currentSession as any)?.workspace === 'string'
|
|
912
1429
|
? (h.currentSession as any).workspace
|
|
913
1430
|
: undefined;
|
|
914
|
-
const
|
|
1431
|
+
const exactNativeHistoryScope = Boolean(
|
|
1432
|
+
(typeof args?.targetSessionId === 'string' && args.targetSessionId.trim())
|
|
1433
|
+
|| (typeof args?.historySessionId === 'string' && args.historySessionId.trim())
|
|
1434
|
+
|| (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
|
|
1435
|
+
|| ((h.currentSession as any)?.sessionId === args?.targetSessionId && typeof (h.currentSession as any)?.providerSessionId === 'string' && (h.currentSession as any).providerSessionId.trim())
|
|
1436
|
+
);
|
|
1437
|
+
const history = supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)
|
|
1438
|
+
? readCliProviderNativeHistory(agentStr, {
|
|
1439
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
1440
|
+
historySessionId,
|
|
1441
|
+
workspace,
|
|
1442
|
+
offset: 0,
|
|
1443
|
+
limit: historyLimit,
|
|
1444
|
+
excludeRecentCount: 0,
|
|
1445
|
+
historyBehavior: provider?.historyBehavior,
|
|
1446
|
+
scripts: provider?.scripts as any,
|
|
1447
|
+
exactSessionScoped: exactNativeHistoryScope,
|
|
1448
|
+
})
|
|
1449
|
+
: readProviderChatHistory(agentStr, {
|
|
915
1450
|
canonicalHistory: provider?.canonicalHistory,
|
|
916
1451
|
historySessionId,
|
|
917
1452
|
workspace,
|
|
@@ -921,12 +1456,80 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
921
1456
|
historyBehavior: provider?.historyBehavior,
|
|
922
1457
|
scripts: provider?.scripts as any,
|
|
923
1458
|
});
|
|
1459
|
+
const lookup = (history as any).lookup === 'workspace' ? 'workspace' : 'session';
|
|
1460
|
+
const historyMessages = Array.isArray((history as any)?.messages)
|
|
1461
|
+
? normalizeNativeHistoryMessages(agentStr, (history as any).messages as ChatMessage[])
|
|
1462
|
+
: [];
|
|
924
1463
|
const historyProviderSessionId = typeof (history as any)?.providerSessionId === 'string'
|
|
925
1464
|
? (history as any).providerSessionId
|
|
926
|
-
: historySessionId;
|
|
1465
|
+
: readHistorySessionIdFromMessages(historyMessages) || historySessionId;
|
|
1466
|
+
const nativeHistoryCoverage = typeof (history as any)?.nativeHistoryCoverage === 'string'
|
|
1467
|
+
? (history as any).nativeHistoryCoverage
|
|
1468
|
+
: undefined;
|
|
1469
|
+
const partialReason = typeof (history as any)?.partialReason === 'string'
|
|
1470
|
+
? (history as any).partialReason
|
|
1471
|
+
: undefined;
|
|
1472
|
+
const unavailableReason = typeof (history as any)?.unavailableReason === 'string'
|
|
1473
|
+
? (history as any).unavailableReason
|
|
1474
|
+
: undefined;
|
|
1475
|
+
const safeMapping = supportsCliNativeTranscript(agentStr, provider)
|
|
1476
|
+
? hasSafeNativeHistoryMapping({
|
|
1477
|
+
historySessionId: lookup === 'workspace' ? undefined : historySessionId,
|
|
1478
|
+
providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId,
|
|
1479
|
+
workspace,
|
|
1480
|
+
nativeMessages: historyMessages,
|
|
1481
|
+
})
|
|
1482
|
+
: false;
|
|
1483
|
+
const nativeSelected = supportsCliNativeTranscript(agentStr, provider)
|
|
1484
|
+
&& (history as any).source === 'provider-native'
|
|
1485
|
+
&& historyMessages.length > 0
|
|
1486
|
+
&& nativeHistoryCoverage !== 'partial'
|
|
1487
|
+
&& nativeHistoryCoverage !== 'unavailable'
|
|
1488
|
+
&& safeMapping;
|
|
1489
|
+
const messageSource = buildCliMessageSourceProvenance({
|
|
1490
|
+
selected: nativeSelected ? 'native-history' : 'pty-parser',
|
|
1491
|
+
provider: agentStr,
|
|
1492
|
+
nativeHandle: historyProviderSessionId || historySessionId,
|
|
1493
|
+
fallbackReason: nativeSelected
|
|
1494
|
+
? undefined
|
|
1495
|
+
: buildNativeHistoryFallbackReason({
|
|
1496
|
+
providerType: agentStr,
|
|
1497
|
+
provider,
|
|
1498
|
+
nativeSource: (history as any).source,
|
|
1499
|
+
nativeHistoryCoverage,
|
|
1500
|
+
nativeMessageCount: historyMessages.length,
|
|
1501
|
+
safeMapping,
|
|
1502
|
+
freshEnough: true,
|
|
1503
|
+
}),
|
|
1504
|
+
nativeSource: (history as any).source,
|
|
1505
|
+
sourcePath: (history as any).sourcePath,
|
|
1506
|
+
sourceMtimeMs: (history as any).sourceMtimeMs,
|
|
1507
|
+
nativeHistoryCoverage,
|
|
1508
|
+
partialReason,
|
|
1509
|
+
unavailableReason,
|
|
1510
|
+
nativeMessages: historyMessages,
|
|
1511
|
+
returnedMessages: historyMessages,
|
|
1512
|
+
safeMapping,
|
|
1513
|
+
freshEnough: true,
|
|
1514
|
+
ptyStatusApprovalOnly: false,
|
|
1515
|
+
});
|
|
1516
|
+
const requiresNativeSource = supportsCliNativeTranscript(agentStr, provider)
|
|
1517
|
+
&& isNativeSourceCanonicalHistory(provider?.canonicalHistory);
|
|
1518
|
+
if (requiresNativeSource && !nativeSelected) {
|
|
1519
|
+
return {
|
|
1520
|
+
success: false,
|
|
1521
|
+
code: 'native_history_not_safely_available',
|
|
1522
|
+
error: 'Provider-native history was not safely available for the requested CLI session.',
|
|
1523
|
+
providerSessionId: historyProviderSessionId,
|
|
1524
|
+
messageSource,
|
|
1525
|
+
transcriptProvenance: messageSource,
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
927
1528
|
return buildReadChatCommandResult({
|
|
928
|
-
messages:
|
|
1529
|
+
messages: historyMessages,
|
|
929
1530
|
status: 'idle',
|
|
1531
|
+
messageSource,
|
|
1532
|
+
transcriptProvenance: messageSource,
|
|
930
1533
|
...(typeof (history as any)?.title === 'string' ? { title: (history as any).title } : {}),
|
|
931
1534
|
...(historyProviderSessionId ? { providerSessionId: historyProviderSessionId } : {}),
|
|
932
1535
|
...(((provider?.historyBehavior as any)?.transcriptAuthority === 'provider' || (provider?.historyBehavior as any)?.transcriptAuthority === 'daemon')
|
|
@@ -1742,6 +2345,9 @@ export async function handleResolveAction(h: CommandHelpers, args: any): Promise
|
|
|
1742
2345
|
if (buttonIndex < 0 && (action === 'always' || /always/i.test(button))) {
|
|
1743
2346
|
buttonIndex = buttons.findIndex(b => /always/i.test(b));
|
|
1744
2347
|
}
|
|
2348
|
+
if (buttonIndex < 0 && (action === 'approve' || action === 'accept')) {
|
|
2349
|
+
buttonIndex = pickApprovalButton(buttons, provider).index;
|
|
2350
|
+
}
|
|
1745
2351
|
if (buttonIndex < 0) {
|
|
1746
2352
|
return { success: false, error: 'Approval action did not match any visible button' };
|
|
1747
2353
|
}
|