@adhdev/daemon-core 0.9.82-rc.13 → 0.9.82-rc.130
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/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +4 -1
- package/dist/cli-adapters/provider-cli-adapter.d.ts +25 -1
- 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 +68 -1
- package/dist/git/git-commands.d.ts +5 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.js +7461 -1380
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7417 -1364
- 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 +73 -0
- package/dist/mesh/mesh-events.d.ts +54 -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-refine-status.d.ts +27 -0
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +193 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +115 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +6 -1
- package/dist/repo-mesh-types.d.ts +62 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +1 -0
- package/src/chat/subscription-updates.ts +5 -1
- package/src/cli-adapter-types.ts +2 -1
- package/src/cli-adapters/provider-cli-adapter.ts +473 -18
- 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 +960 -40
- package/src/commands/cli-manager.ts +138 -2
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +3238 -423
- package/src/config/chat-history.ts +37 -9
- package/src/config/mesh-config.ts +249 -2
- package/src/config/recent-activity.ts +8 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +17 -5
- package/src/index.ts +41 -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 +295 -0
- package/src/mesh/mesh-events.ts +595 -48
- 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-refine-status.ts +145 -0
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +366 -0
- package/src/mesh/worktree-bootstrap-config.ts +234 -0
- package/src/providers/approval-utils.ts +12 -5
- package/src/providers/chat-message-normalization.ts +7 -12
- package/src/providers/cli-provider-instance.ts +289 -36
- 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 +67 -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, readChatHistory, 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,13 @@ 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
|
+
// Fallback list for supportsCliNativeTranscript() when the ProviderModule is
|
|
30
|
+
// unavailable (e.g. provider config not yet loaded). The authoritative check is
|
|
31
|
+
// provider.canonicalHistory via isNativeSourceCanonicalHistory(). New providers
|
|
32
|
+
// with native transcripts should set canonicalHistory in their provider.json
|
|
33
|
+
// rather than adding entries here.
|
|
34
|
+
const CLI_NATIVE_TRANSCRIPT_PROVIDERS = new Set(['codex-cli', 'claude-cli', 'hermes-cli', 'antigravity-cli']);
|
|
27
35
|
const recentSendByTarget = new Map<string, number>();
|
|
28
36
|
|
|
29
37
|
interface ApprovalSelectableInstance extends ProviderInstance {
|
|
@@ -32,6 +40,7 @@ interface ApprovalSelectableInstance extends ProviderInstance {
|
|
|
32
40
|
|
|
33
41
|
interface RuntimeChatMessageMerger extends ProviderInstance {
|
|
34
42
|
mergeRuntimeChatMessages?(messages: ChatMessage[]): ChatMessage[];
|
|
43
|
+
recordAcknowledgedUserInput?(input: InputEnvelope | string): void;
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
type LegacyStringScript = (params?: Record<string, unknown> | string) => string;
|
|
@@ -48,6 +57,16 @@ function getTargetedCliAdapter(h: CommandHelpers, args: any, providerType?: stri
|
|
|
48
57
|
return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
|
|
49
58
|
}
|
|
50
59
|
|
|
60
|
+
function getExplicitHistorySessionId(args: any): string | undefined {
|
|
61
|
+
const explicit = typeof args?.historySessionId === 'string' ? args.historySessionId.trim() : '';
|
|
62
|
+
if (explicit) return explicit;
|
|
63
|
+
|
|
64
|
+
const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
|
|
65
|
+
if (explicitProviderSessionId) return explicitProviderSessionId;
|
|
66
|
+
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
function getTargetInstance(h: CommandHelpers, args: any): ApprovalSelectableInstance | null {
|
|
52
71
|
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
53
72
|
const sessionId = targetSessionId || h.currentSession?.sessionId || '';
|
|
@@ -139,19 +158,47 @@ async function waitOnceForFreshHermesCliStart(adapter: CliAdapter, log: (msg: st
|
|
|
139
158
|
}
|
|
140
159
|
|
|
141
160
|
function getHistorySessionId(h: CommandHelpers, args: any): string | undefined {
|
|
142
|
-
const explicit =
|
|
161
|
+
const explicit = getExplicitHistorySessionId(args);
|
|
143
162
|
if (explicit) return explicit;
|
|
144
163
|
|
|
145
|
-
const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
|
|
146
|
-
if (explicitProviderSessionId) return explicitProviderSessionId;
|
|
147
|
-
|
|
148
164
|
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
149
165
|
if (!targetSessionId) return undefined;
|
|
150
166
|
|
|
151
|
-
const
|
|
167
|
+
const session = h.ctx.sessionRegistry?.get(targetSessionId) as any;
|
|
168
|
+
const registeredProviderSessionId = typeof session?.providerSessionId === 'string' ? session.providerSessionId.trim() : '';
|
|
169
|
+
if (registeredProviderSessionId) return registeredProviderSessionId;
|
|
170
|
+
|
|
171
|
+
const instance = getTargetInstance(h, args);
|
|
152
172
|
const state = instance?.getState?.();
|
|
153
173
|
const providerSessionId = typeof state?.providerSessionId === 'string' ? state.providerSessionId.trim() : '';
|
|
154
|
-
|
|
174
|
+
if (providerSessionId) return providerSessionId;
|
|
175
|
+
|
|
176
|
+
const currentSession = h.currentSession as any;
|
|
177
|
+
if (currentSession?.sessionId === targetSessionId) {
|
|
178
|
+
const currentProviderSessionId = typeof currentSession.providerSessionId === 'string'
|
|
179
|
+
? currentSession.providerSessionId.trim()
|
|
180
|
+
: '';
|
|
181
|
+
if (currentProviderSessionId) return currentProviderSessionId;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return targetSessionId;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function resolveCliNativeHistorySessionId(args: any, currentHistorySessionId: string | undefined, parsedProviderSessionId: string | undefined): string | undefined {
|
|
188
|
+
const explicit = getExplicitHistorySessionId(args);
|
|
189
|
+
if (explicit) return explicit;
|
|
190
|
+
|
|
191
|
+
const parsed = typeof parsedProviderSessionId === 'string' ? parsedProviderSessionId.trim() : '';
|
|
192
|
+
const current = typeof currentHistorySessionId === 'string' ? currentHistorySessionId.trim() : '';
|
|
193
|
+
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
194
|
+
|
|
195
|
+
// getHistorySessionId falls back to the runtime session id when no native
|
|
196
|
+
// handle has been registered yet. For live CLI adapters the parser may
|
|
197
|
+
// already know the provider-native handle; prefer it over the runtime id so
|
|
198
|
+
// exact native reads do not miss the worker transcript and fall back to PTY
|
|
199
|
+
// or same-workspace history.
|
|
200
|
+
if (parsed && (!current || current === targetSessionId)) return parsed;
|
|
201
|
+
return current || parsed || undefined;
|
|
155
202
|
}
|
|
156
203
|
|
|
157
204
|
function getInteractionId(args: any): string | undefined {
|
|
@@ -221,6 +268,446 @@ function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[]
|
|
|
221
268
|
return normalizeChatMessages(messages);
|
|
222
269
|
}
|
|
223
270
|
|
|
271
|
+
function getMessageNewestReceivedAt(messages: Array<{ receivedAt?: unknown; timestamp?: unknown }>): number {
|
|
272
|
+
let newest = 0;
|
|
273
|
+
for (const message of messages) {
|
|
274
|
+
const receivedAt = Number(message?.receivedAt ?? message?.timestamp ?? 0);
|
|
275
|
+
if (Number.isFinite(receivedAt) && receivedAt > newest) newest = receivedAt;
|
|
276
|
+
}
|
|
277
|
+
return newest;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function readHistorySessionIdFromMessages(messages: ChatMessage[]): string | undefined {
|
|
281
|
+
for (const message of messages as Array<ChatMessage & { historySessionId?: unknown }>) {
|
|
282
|
+
const historySessionId = typeof message?.historySessionId === 'string' ? message.historySessionId.trim() : '';
|
|
283
|
+
if (historySessionId) return historySessionId;
|
|
284
|
+
}
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function shouldPreserveNativeIdentity(providerType: string, sessionId: string, message: ChatMessage): boolean {
|
|
289
|
+
const providerUnitKey = typeof message.providerUnitKey === 'string' ? message.providerUnitKey.trim() : '';
|
|
290
|
+
const turnKey = typeof message._turnKey === 'string' ? message._turnKey.trim() : '';
|
|
291
|
+
if (!providerUnitKey || !turnKey) return false;
|
|
292
|
+
if (providerType === 'hermes-cli' && sessionId) {
|
|
293
|
+
return providerUnitKey.startsWith(`${providerType}:native:${sessionId}:`)
|
|
294
|
+
&& turnKey.startsWith(`${providerType}:native-turn:${sessionId}:`);
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function normalizeNativeHistoryMessages(providerType: string, messages: ChatMessage[], nativeSessionId?: string): ChatMessage[] {
|
|
300
|
+
let turnIndex = 0;
|
|
301
|
+
return normalizeChatMessages(messages).map((message, index) => {
|
|
302
|
+
const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
|
|
303
|
+
const kind = typeof message.kind === 'string' && message.kind.trim() ? message.kind.trim() : (role === 'system' ? 'system' : 'standard');
|
|
304
|
+
if ((role === 'user' || role === 'human') && index > 0) turnIndex += 1;
|
|
305
|
+
const historySessionId = typeof (message as any).historySessionId === 'string'
|
|
306
|
+
? (message as any).historySessionId.trim()
|
|
307
|
+
: '';
|
|
308
|
+
const contentHash = hashSignatureParts([
|
|
309
|
+
providerType,
|
|
310
|
+
historySessionId,
|
|
311
|
+
String(message.receivedAt || message.timestamp || index),
|
|
312
|
+
role,
|
|
313
|
+
kind,
|
|
314
|
+
flattenContent(message.content),
|
|
315
|
+
]).slice(0, 12);
|
|
316
|
+
const nativeIdentitySessionId = historySessionId || (typeof nativeSessionId === 'string' ? nativeSessionId.trim() : '');
|
|
317
|
+
const preserveNativeIdentity = shouldPreserveNativeIdentity(providerType, nativeIdentitySessionId, message);
|
|
318
|
+
const existingProviderUnitKey = typeof message.providerUnitKey === 'string' ? message.providerUnitKey.trim() : '';
|
|
319
|
+
const existingTurnKey = typeof message._turnKey === 'string' ? message._turnKey.trim() : '';
|
|
320
|
+
const providerUnitKey = preserveNativeIdentity
|
|
321
|
+
? existingProviderUnitKey
|
|
322
|
+
: `${providerType}:native:${nativeIdentitySessionId || 'workspace'}:${index}:${role || 'message'}:${kind}:${contentHash}`;
|
|
323
|
+
const meta = message.meta && typeof message.meta === 'object' ? message.meta as Record<string, unknown> : undefined;
|
|
324
|
+
const isSystemSessionStart = role === 'system' || kind === 'system' || kind === 'session_start';
|
|
325
|
+
const isActivity = role === 'assistant' && (kind === 'tool' || kind === 'terminal' || kind === 'thought');
|
|
326
|
+
return {
|
|
327
|
+
...message,
|
|
328
|
+
role: role === 'human' ? 'user' : (role || 'assistant'),
|
|
329
|
+
kind: isSystemSessionStart ? 'system' : kind,
|
|
330
|
+
providerUnitKey,
|
|
331
|
+
bubbleId: typeof message.bubbleId === 'string' && message.bubbleId.trim()
|
|
332
|
+
&& preserveNativeIdentity
|
|
333
|
+
? message.bubbleId.trim()
|
|
334
|
+
: `bubble:${providerUnitKey}`,
|
|
335
|
+
_turnKey: preserveNativeIdentity
|
|
336
|
+
? existingTurnKey
|
|
337
|
+
: `${providerType}:native-turn:${nativeIdentitySessionId || 'workspace'}:${turnIndex}`,
|
|
338
|
+
bubbleState: message.bubbleState || 'final',
|
|
339
|
+
...(isSystemSessionStart ? {
|
|
340
|
+
visibility: message.visibility || 'hidden',
|
|
341
|
+
transcriptVisibility: message.transcriptVisibility || 'hidden',
|
|
342
|
+
audience: message.audience || 'internal',
|
|
343
|
+
source: message.source || 'runtime_status',
|
|
344
|
+
} : isActivity ? {
|
|
345
|
+
source: message.source || (kind === 'terminal' ? 'terminal_command' : 'tool_call'),
|
|
346
|
+
meta: { ...meta, label: message.senderName || meta?.label || (kind === 'terminal' ? 'Terminal' : 'Tool') },
|
|
347
|
+
} : {
|
|
348
|
+
source: message.source || (role === 'assistant' ? 'assistant_text' : undefined),
|
|
349
|
+
}),
|
|
350
|
+
} as ChatMessage;
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function buildCliMessageSourceProvenance(args: {
|
|
355
|
+
selected: 'native-history' | 'pty-parser';
|
|
356
|
+
provider: string;
|
|
357
|
+
nativeHandle?: string;
|
|
358
|
+
sessionWorkspace?: string;
|
|
359
|
+
intendedWorkspace?: string;
|
|
360
|
+
transcriptWorkspace?: string;
|
|
361
|
+
fallbackReason?: string;
|
|
362
|
+
nativeSource?: string;
|
|
363
|
+
sourcePath?: string;
|
|
364
|
+
sourceMtimeMs?: number;
|
|
365
|
+
nativeHistoryCoverage?: string;
|
|
366
|
+
partialReason?: string;
|
|
367
|
+
unavailableReason?: string;
|
|
368
|
+
nativeMessages?: ChatMessage[];
|
|
369
|
+
ptyMessages?: ChatMessage[];
|
|
370
|
+
returnedMessages?: ChatMessage[];
|
|
371
|
+
safeMapping?: boolean;
|
|
372
|
+
freshEnough?: boolean;
|
|
373
|
+
ptyStatusApprovalOnly?: boolean;
|
|
374
|
+
}): Record<string, unknown> {
|
|
375
|
+
const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
|
|
376
|
+
const sourceMtimeAgeMs = sourceMtimeMs > 0 ? Math.max(0, Date.now() - sourceMtimeMs) : undefined;
|
|
377
|
+
const nativeMessages = args.nativeMessages || [];
|
|
378
|
+
const ptyMessages = args.ptyMessages || [];
|
|
379
|
+
const returnedMessages = args.returnedMessages || [];
|
|
380
|
+
const identityStatus = args.selected === 'native-history'
|
|
381
|
+
? 'safe'
|
|
382
|
+
: args.fallbackReason === 'native_history_not_safely_mapped'
|
|
383
|
+
? 'ambiguous_session_identity'
|
|
384
|
+
: args.fallbackReason?.startsWith('native_history_unavailable')
|
|
385
|
+
? 'transcript_unmapped'
|
|
386
|
+
: undefined;
|
|
387
|
+
return {
|
|
388
|
+
selected: args.selected,
|
|
389
|
+
provider: args.provider,
|
|
390
|
+
providerType: args.provider,
|
|
391
|
+
...(identityStatus ? { identityStatus } : {}),
|
|
392
|
+
...(args.nativeHandle ? { nativeHandle: args.nativeHandle } : {}),
|
|
393
|
+
...(args.nativeHandle ? { nativeSessionId: args.nativeHandle } : {}),
|
|
394
|
+
...(args.sessionWorkspace ? { sessionWorkspace: args.sessionWorkspace } : {}),
|
|
395
|
+
...(args.intendedWorkspace ? { intendedWorkspace: args.intendedWorkspace } : {}),
|
|
396
|
+
...(args.transcriptWorkspace ? { transcriptWorkspace: args.transcriptWorkspace } : {}),
|
|
397
|
+
...(args.fallbackReason ? { fallbackReason: args.fallbackReason } : {}),
|
|
398
|
+
...(args.nativeSource ? { nativeSource: args.nativeSource } : {}),
|
|
399
|
+
...(args.sourcePath ? { sourcePath: args.sourcePath } : {}),
|
|
400
|
+
...(args.nativeHistoryCoverage ? { nativeHistoryCoverage: args.nativeHistoryCoverage } : {}),
|
|
401
|
+
...(args.partialReason ? { partialReason: args.partialReason } : {}),
|
|
402
|
+
...(args.unavailableReason ? { unavailableReason: args.unavailableReason } : {}),
|
|
403
|
+
ptyStatusApprovalOnly: args.ptyStatusApprovalOnly === true,
|
|
404
|
+
staleness: {
|
|
405
|
+
sourceMtimeMs: sourceMtimeMs || undefined,
|
|
406
|
+
sourceMtimeAgeMs,
|
|
407
|
+
nativeNewestMessageAt: getMessageNewestReceivedAt(nativeMessages),
|
|
408
|
+
ptyNewestMessageAt: getMessageNewestReceivedAt(ptyMessages),
|
|
409
|
+
freshEnough: args.freshEnough === true,
|
|
410
|
+
},
|
|
411
|
+
coverage: {
|
|
412
|
+
nativeMessageCount: nativeMessages.length,
|
|
413
|
+
ptyMessageCount: ptyMessages.length,
|
|
414
|
+
returnedMessageCount: returnedMessages.length,
|
|
415
|
+
safeMapping: args.safeMapping === true,
|
|
416
|
+
// true when PTY message bodies are suppressed and must not be treated as
|
|
417
|
+
// chat content. PTY may still contribute status/approval/screen evidence.
|
|
418
|
+
ptyMessagesSuppressed: args.selected === 'native-history' || args.ptyStatusApprovalOnly === true,
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function buildNativeHistoryFallbackReason(args: {
|
|
424
|
+
providerType: string;
|
|
425
|
+
provider?: ProviderModule;
|
|
426
|
+
nativeSource?: string;
|
|
427
|
+
nativeHistoryCoverage?: string;
|
|
428
|
+
unavailableReason?: string;
|
|
429
|
+
nativeMessageCount: number;
|
|
430
|
+
safeMapping: boolean;
|
|
431
|
+
freshEnough: boolean;
|
|
432
|
+
}): string {
|
|
433
|
+
if (!supportsCliNativeTranscript(args.providerType, args.provider)) return 'provider_native_transcript_not_supported';
|
|
434
|
+
if (args.unavailableReason) return `native_history_unavailable:${args.unavailableReason}`;
|
|
435
|
+
if (args.nativeSource === 'native-unavailable') return 'native_history_unavailable';
|
|
436
|
+
if (args.nativeHistoryCoverage === 'partial') return 'native_history_partial';
|
|
437
|
+
if (args.nativeHistoryCoverage === 'unavailable') return 'native_history_unavailable';
|
|
438
|
+
if (args.nativeSource && args.nativeSource !== 'provider-native') return `native_history_source_${args.nativeSource}`;
|
|
439
|
+
if (args.nativeMessageCount <= 0) return 'native_history_empty';
|
|
440
|
+
if (!args.safeMapping) return 'native_history_not_safely_mapped';
|
|
441
|
+
if (!args.freshEnough) return 'native_history_stale';
|
|
442
|
+
return 'native_history_not_selected';
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function isUnsafeNativeTranscriptFallback(reason?: string): boolean {
|
|
446
|
+
const value = String(reason || '').trim();
|
|
447
|
+
return value.startsWith('native_history_unavailable')
|
|
448
|
+
|| value === 'native_history_not_safely_mapped'
|
|
449
|
+
|| value === 'native_history_stale'
|
|
450
|
+
|| value === 'native_history_partial';
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function coerceUnsafeNativeFallbackStatus(status: string, activeModal: unknown): string {
|
|
454
|
+
if (status === 'waiting_approval' && activeModal) return status;
|
|
455
|
+
return 'idle';
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function isRuntimeInputAckMessage(message: ChatMessage | undefined): boolean {
|
|
459
|
+
if (!message || typeof message !== 'object') return false;
|
|
460
|
+
const role = String((message as any).role || '').trim().toLowerCase();
|
|
461
|
+
if (role !== 'user' && role !== 'human') return false;
|
|
462
|
+
const meta = (message as any).meta;
|
|
463
|
+
return !!meta && typeof meta === 'object' && !Array.isArray(meta) && meta.runtimeInputAck === true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function selectRuntimeInputAckMessages(messages: ChatMessage[]): ChatMessage[] {
|
|
467
|
+
return messages.filter((message) => isRuntimeInputAckMessage(message));
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function readExactRuntimeMirrorMessages(args: {
|
|
471
|
+
providerType: string;
|
|
472
|
+
targetSessionId?: string;
|
|
473
|
+
currentSessionId?: string;
|
|
474
|
+
tailLimit: number;
|
|
475
|
+
historyBehavior?: ProviderModule['historyBehavior'];
|
|
476
|
+
}): ChatMessage[] {
|
|
477
|
+
const targetSessionId = String(args.targetSessionId || '').trim();
|
|
478
|
+
const currentSessionId = String(args.currentSessionId || '').trim();
|
|
479
|
+
if (!targetSessionId || targetSessionId !== currentSessionId) return [];
|
|
480
|
+
|
|
481
|
+
const history = readChatHistory(
|
|
482
|
+
args.providerType,
|
|
483
|
+
0,
|
|
484
|
+
Math.max(args.tailLimit || 0, 200),
|
|
485
|
+
targetSessionId,
|
|
486
|
+
0,
|
|
487
|
+
args.historyBehavior,
|
|
488
|
+
);
|
|
489
|
+
return normalizeChatMessages((history.messages || []) as ChatMessage[])
|
|
490
|
+
.filter((message) => {
|
|
491
|
+
const historySessionId = String((message as any).historySessionId || '').trim();
|
|
492
|
+
const instanceId = String((message as any).instanceId || '').trim();
|
|
493
|
+
return historySessionId === targetSessionId || instanceId === targetSessionId;
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function normalizeComparableWorkspace(value: unknown): string {
|
|
498
|
+
const text = typeof value === 'string' ? value.trim() : '';
|
|
499
|
+
if (!text) return '';
|
|
500
|
+
return path.resolve(text);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function isCurrentRuntimePtySafelyAttributed(args: {
|
|
504
|
+
adapter: CliAdapter;
|
|
505
|
+
helpers: CommandHelpers;
|
|
506
|
+
readChatArgs: any;
|
|
507
|
+
sessionWorkspace?: string;
|
|
508
|
+
intendedWorkspace?: string;
|
|
509
|
+
ptyMessages: ChatMessage[];
|
|
510
|
+
}): boolean {
|
|
511
|
+
if (args.adapter.cliType !== 'codex-cli') return false;
|
|
512
|
+
if (!Array.isArray(args.ptyMessages) || args.ptyMessages.length === 0) return false;
|
|
513
|
+
const targetSessionId = typeof args.readChatArgs?.targetSessionId === 'string'
|
|
514
|
+
? args.readChatArgs.targetSessionId.trim()
|
|
515
|
+
: '';
|
|
516
|
+
const currentSession = args.helpers.currentSession as any;
|
|
517
|
+
const currentSessionId = typeof currentSession?.sessionId === 'string'
|
|
518
|
+
? currentSession.sessionId.trim()
|
|
519
|
+
: '';
|
|
520
|
+
if (!targetSessionId || !currentSessionId || targetSessionId !== currentSessionId) return false;
|
|
521
|
+
|
|
522
|
+
const runtimeMeta = typeof (args.adapter as any).getRuntimeMetadata === 'function'
|
|
523
|
+
? (args.adapter as any).getRuntimeMetadata()
|
|
524
|
+
: null;
|
|
525
|
+
const runtimeId = typeof runtimeMeta?.runtimeId === 'string' ? runtimeMeta.runtimeId.trim() : '';
|
|
526
|
+
if (!runtimeId || runtimeId !== targetSessionId) return false;
|
|
527
|
+
const surfaceKind = typeof runtimeMeta?.surfaceKind === 'string' ? runtimeMeta.surfaceKind : '';
|
|
528
|
+
if (surfaceKind === 'inactive_record' || surfaceKind === 'recovery_snapshot') return false;
|
|
529
|
+
|
|
530
|
+
const sessionWorkspace = normalizeComparableWorkspace(args.sessionWorkspace);
|
|
531
|
+
const adapterWorkspace = normalizeComparableWorkspace(args.adapter.workingDir);
|
|
532
|
+
if (!sessionWorkspace || !adapterWorkspace || sessionWorkspace !== adapterWorkspace) return false;
|
|
533
|
+
const intendedWorkspace = normalizeComparableWorkspace(args.intendedWorkspace);
|
|
534
|
+
if (intendedWorkspace && intendedWorkspace !== sessionWorkspace) return false;
|
|
535
|
+
|
|
536
|
+
const registryEntry = args.helpers.ctx?.sessionRegistry?.get?.(targetSessionId) as any;
|
|
537
|
+
const registryInstanceKey = typeof registryEntry?.adapterKey === 'string' && registryEntry.adapterKey.trim()
|
|
538
|
+
? registryEntry.adapterKey.trim()
|
|
539
|
+
: typeof registryEntry?.instanceKey === 'string' && registryEntry.instanceKey.trim()
|
|
540
|
+
? registryEntry.instanceKey.trim()
|
|
541
|
+
: '';
|
|
542
|
+
if (registryInstanceKey) {
|
|
543
|
+
const targetInstance = args.helpers.ctx?.instanceManager?.getInstance?.(registryInstanceKey);
|
|
544
|
+
if (targetInstance) {
|
|
545
|
+
const instanceType = typeof (targetInstance as any).type === 'string' ? (targetInstance as any).type : '';
|
|
546
|
+
if (instanceType && instanceType !== args.adapter.cliType) return false;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function supportsCliNativeTranscript(providerType: string, provider?: ProviderModule): boolean {
|
|
554
|
+
if (CLI_NATIVE_TRANSCRIPT_PROVIDERS.has(providerType)) return true;
|
|
555
|
+
return provider?.category === 'cli' && isNativeSourceCanonicalHistory(provider?.canonicalHistory);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function getComparableVisibleText(message: ChatMessage | undefined): string {
|
|
559
|
+
if (!message) return '';
|
|
560
|
+
const role = String((message as any).role || '').trim().toLowerCase();
|
|
561
|
+
if (role !== 'user' && role !== 'assistant') return '';
|
|
562
|
+
const kind = String((message as any).kind || 'standard').trim().toLowerCase();
|
|
563
|
+
if (kind && kind !== 'standard') return '';
|
|
564
|
+
const content = flattenContent((message as any).content).replace(/\s+/g, ' ').trim();
|
|
565
|
+
return content;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function hasOverlappingVisibleConversationText(nativeMessages: ChatMessage[], ptyMessages: ChatMessage[]): boolean {
|
|
569
|
+
const nativeTexts = nativeMessages.map(getComparableVisibleText).filter(Boolean);
|
|
570
|
+
const ptyTexts = ptyMessages.map(getComparableVisibleText).filter(Boolean);
|
|
571
|
+
if (nativeTexts.length === 0 || ptyTexts.length === 0) return false;
|
|
572
|
+
for (const nativeText of nativeTexts) {
|
|
573
|
+
for (const ptyText of ptyTexts) {
|
|
574
|
+
if (nativeText === ptyText) return true;
|
|
575
|
+
const shorter = nativeText.length <= ptyText.length ? nativeText : ptyText;
|
|
576
|
+
const longer = nativeText.length <= ptyText.length ? ptyText : nativeText;
|
|
577
|
+
if (shorter.length >= 32 && longer.includes(shorter)) return true;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function hasSafeNativeHistoryMapping(args: {
|
|
584
|
+
historySessionId?: string;
|
|
585
|
+
providerSessionId?: string;
|
|
586
|
+
workspace?: string;
|
|
587
|
+
nativeMessages: ChatMessage[];
|
|
588
|
+
ptyMessages?: ChatMessage[];
|
|
589
|
+
requireWorkspaceContentOverlap?: boolean;
|
|
590
|
+
}): boolean {
|
|
591
|
+
const isCoordinatorTranscript = args.nativeMessages.some((m: any) => {
|
|
592
|
+
const text = typeof m?.content === 'string' ? m.content : JSON.stringify(m?.content || '');
|
|
593
|
+
return text.includes('mesh_send_task') || text.includes('mesh_status') || text.includes('mesh_read_chat') || text.includes('mesh_launch_session');
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
const explicitSessionId = String(args.historySessionId || args.providerSessionId || '').trim();
|
|
597
|
+
if (explicitSessionId) {
|
|
598
|
+
const messageSessionIds = args.nativeMessages
|
|
599
|
+
.map((message: any) => typeof message?.historySessionId === 'string' ? message.historySessionId.trim() : '')
|
|
600
|
+
.filter(Boolean);
|
|
601
|
+
if (messageSessionIds.length > 0) {
|
|
602
|
+
return messageSessionIds.some((id) => id === explicitSessionId);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Messages carry no historySessionId — cannot confirm they belong to the requested session.
|
|
606
|
+
// Only allow a coordinator transcript that is also confirmed by the PTY side; otherwise
|
|
607
|
+
// fail closed so a same-workspace session's history is never silently accepted.
|
|
608
|
+
if (isCoordinatorTranscript && args.ptyMessages && args.ptyMessages.length > 0) {
|
|
609
|
+
const ptyHasCoordinator = args.ptyMessages.some((m: any) => {
|
|
610
|
+
const text = typeof m?.content === 'string' ? m.content : JSON.stringify(m?.content || '');
|
|
611
|
+
return text.includes('mesh_send_task') || text.includes('mesh_status') || text.includes('mesh_read_chat');
|
|
612
|
+
});
|
|
613
|
+
return ptyHasCoordinator;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// No historySessionId in messages and no coordinator cross-check: fail closed.
|
|
617
|
+
// Workspace-only matching must not override an explicit session identity.
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
const workspace = String(args.workspace || '').trim();
|
|
621
|
+
if (!workspace) return false;
|
|
622
|
+
const workspaceMatches = args.nativeMessages.some((message: any) => String(message?.workspace || '').trim() === workspace);
|
|
623
|
+
if (!workspaceMatches) return false;
|
|
624
|
+
|
|
625
|
+
if (isCoordinatorTranscript && args.ptyMessages && args.ptyMessages.length > 0) {
|
|
626
|
+
const ptyHasCoordinator = args.ptyMessages.some((m: any) => {
|
|
627
|
+
const text = typeof m?.content === 'string' ? m.content : JSON.stringify(m?.content || '');
|
|
628
|
+
return text.includes('mesh_send_task') || text.includes('mesh_status') || text.includes('mesh_read_chat');
|
|
629
|
+
});
|
|
630
|
+
if (!ptyHasCoordinator) {
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (!args.requireWorkspaceContentOverlap) return true;
|
|
636
|
+
return hasOverlappingVisibleConversationText(args.nativeMessages, args.ptyMessages || []);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Provenance boundary: workspace-only native history lookup is never safe
|
|
640
|
+
// because multiple concurrent sessions sharing the same cwd would alias each
|
|
641
|
+
// other. historySessionId (the provider-native session key) is required to
|
|
642
|
+
// establish ownership. hasSafeNativeHistoryMapping() enforces the same
|
|
643
|
+
// invariant after the read; both guards must hold for native history to be used.
|
|
644
|
+
function readCliProviderNativeHistory(agentStr: string, args: {
|
|
645
|
+
canonicalHistory?: ProviderModule['canonicalHistory'];
|
|
646
|
+
historySessionId?: string;
|
|
647
|
+
workspace?: string;
|
|
648
|
+
offset: number;
|
|
649
|
+
limit: number;
|
|
650
|
+
excludeRecentCount: number;
|
|
651
|
+
historyBehavior?: ProviderModule['historyBehavior'];
|
|
652
|
+
scripts?: ProviderScripts;
|
|
653
|
+
}): ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' } {
|
|
654
|
+
if (!args.historySessionId) {
|
|
655
|
+
return {
|
|
656
|
+
messages: [],
|
|
657
|
+
hasMore: false,
|
|
658
|
+
source: 'native-unavailable',
|
|
659
|
+
unavailableReason: 'native_history_workspace_only_lookup_unsafe',
|
|
660
|
+
lookup: 'session',
|
|
661
|
+
} as ReturnType<typeof readProviderChatHistory> & { lookup: 'session' | 'workspace' };
|
|
662
|
+
}
|
|
663
|
+
const sessionHistory = readProviderChatHistory(agentStr, {
|
|
664
|
+
canonicalHistory: args.canonicalHistory,
|
|
665
|
+
historySessionId: args.historySessionId,
|
|
666
|
+
workspace: args.workspace,
|
|
667
|
+
offset: args.offset,
|
|
668
|
+
limit: args.limit,
|
|
669
|
+
excludeRecentCount: args.excludeRecentCount,
|
|
670
|
+
historyBehavior: args.historyBehavior,
|
|
671
|
+
scripts: args.scripts as any,
|
|
672
|
+
});
|
|
673
|
+
// Native transcripts are keyed by provider/runtime session identity. Falling
|
|
674
|
+
// back to workspace makes concurrent local Codex/Hermes sessions alias each
|
|
675
|
+
// other when they share the same cwd.
|
|
676
|
+
return { ...(sessionHistory as any), lookup: 'session' };
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function isNativeHistoryFreshEnough(args: {
|
|
680
|
+
sourceMtimeMs?: number;
|
|
681
|
+
nativeMessages: ChatMessage[];
|
|
682
|
+
ptyMessages: ChatMessage[];
|
|
683
|
+
}): boolean {
|
|
684
|
+
const nativeNewest = getMessageNewestReceivedAt(args.nativeMessages);
|
|
685
|
+
const ptyNewest = getMessageNewestReceivedAt(args.ptyMessages);
|
|
686
|
+
if (nativeNewest > 0 && nativeNewest >= ptyNewest) return true;
|
|
687
|
+
const sourceMtimeMs = Number(args.sourceMtimeMs || 0);
|
|
688
|
+
if (sourceMtimeMs > 0 && Date.now() - sourceMtimeMs <= CLI_NATIVE_HISTORY_FRESH_MS) return true;
|
|
689
|
+
return ptyNewest === 0 && nativeNewest > 0;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function shouldPreserveReadChatPayloadField(key: string): boolean {
|
|
693
|
+
return key === 'messageSource' || key === 'transcriptProvenance';
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function updateMessageSourceReturnedCount(value: unknown, returnedMessageCount: number): unknown {
|
|
697
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return value;
|
|
698
|
+
const record = value as Record<string, unknown>;
|
|
699
|
+
const coverage = record.coverage && typeof record.coverage === 'object' && !Array.isArray(record.coverage)
|
|
700
|
+
? record.coverage as Record<string, unknown>
|
|
701
|
+
: undefined;
|
|
702
|
+
if (!coverage) return value;
|
|
703
|
+
return {
|
|
704
|
+
...record,
|
|
705
|
+
coverage: {
|
|
706
|
+
...coverage,
|
|
707
|
+
returnedMessageCount,
|
|
708
|
+
},
|
|
709
|
+
};
|
|
710
|
+
}
|
|
224
711
|
|
|
225
712
|
function deriveHistoryDedupKey(message: ChatMessage & { _unitKey?: string; _turnKey?: string }): string | undefined {
|
|
226
713
|
const unitKey = typeof message._unitKey === 'string' ? message._unitKey.trim() : '';
|
|
@@ -281,7 +768,7 @@ function normalizeReadChatCommandStatus(status: unknown, activeModal: unknown):
|
|
|
281
768
|
}
|
|
282
769
|
switch (raw) {
|
|
283
770
|
case 'starting':
|
|
284
|
-
return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : '
|
|
771
|
+
return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : 'starting';
|
|
285
772
|
case 'stopped':
|
|
286
773
|
case 'disconnected':
|
|
287
774
|
case 'not_monitored':
|
|
@@ -295,6 +782,16 @@ function isGeneratingLikeStatus(status: unknown): boolean {
|
|
|
295
782
|
return status === 'generating' || status === 'streaming' || status === 'long_generating' || status === 'starting';
|
|
296
783
|
}
|
|
297
784
|
|
|
785
|
+
function hasVisibleAssistantMessage(messages: unknown[] | undefined): boolean {
|
|
786
|
+
if (!Array.isArray(messages)) return false;
|
|
787
|
+
return messages.some((message: any) => {
|
|
788
|
+
if (!message || message.role !== 'assistant') return false;
|
|
789
|
+
const kind = typeof message.kind === 'string' ? message.kind : 'standard';
|
|
790
|
+
if (kind !== 'standard') return false;
|
|
791
|
+
return String(message.content || '').trim().length > 0;
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
|
|
298
795
|
function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): boolean {
|
|
299
796
|
if (!isGeneratingLikeStatus(parsedStatus)) return false;
|
|
300
797
|
if (hasNonEmptyModalButtons(activeModal)) return false;
|
|
@@ -304,7 +801,26 @@ function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal:
|
|
|
304
801
|
return true;
|
|
305
802
|
}
|
|
306
803
|
|
|
307
|
-
function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): string {
|
|
804
|
+
function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any, parsedMessages?: unknown[]): string {
|
|
805
|
+
const adapterRawStatus = typeof adapterStatus?.status === 'string' ? adapterStatus.status.trim() : '';
|
|
806
|
+
if (adapterRawStatus === 'starting'
|
|
807
|
+
&& isGeneratingLikeStatus(parsedStatus)
|
|
808
|
+
&& !hasNonEmptyModalButtons(activeModal)
|
|
809
|
+
&& Array.isArray(parsedMessages)
|
|
810
|
+
&& parsedMessages.length === 0
|
|
811
|
+
&& Array.isArray(adapterStatus?.messages)
|
|
812
|
+
&& adapterStatus.messages.length === 0
|
|
813
|
+
&& !(typeof adapter.isProcessing === 'function' && adapter.isProcessing())) {
|
|
814
|
+
return 'starting';
|
|
815
|
+
}
|
|
816
|
+
if (
|
|
817
|
+
isGeneratingLikeStatus(adapterRawStatus)
|
|
818
|
+
&& parsedStatus === 'idle'
|
|
819
|
+
&& !hasNonEmptyModalButtons(activeModal)
|
|
820
|
+
&& !hasVisibleAssistantMessage(parsedMessages)
|
|
821
|
+
) {
|
|
822
|
+
return adapterRawStatus;
|
|
823
|
+
}
|
|
308
824
|
if (shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus)) return 'idle';
|
|
309
825
|
return typeof parsedStatus === 'string' && parsedStatus.trim() ? parsedStatus : 'idle';
|
|
310
826
|
}
|
|
@@ -342,6 +858,13 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
|
|
|
342
858
|
const visibleMessages = filterUserFacingChatMessages(messages);
|
|
343
859
|
const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
|
|
344
860
|
const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
|
|
861
|
+
const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key)));
|
|
862
|
+
if (preservedPayloadFields.messageSource) {
|
|
863
|
+
preservedPayloadFields.messageSource = updateMessageSourceReturnedCount(preservedPayloadFields.messageSource, sync.messages.length);
|
|
864
|
+
}
|
|
865
|
+
if (preservedPayloadFields.transcriptProvenance) {
|
|
866
|
+
preservedPayloadFields.transcriptProvenance = updateMessageSourceReturnedCount(preservedPayloadFields.transcriptProvenance, sync.messages.length);
|
|
867
|
+
}
|
|
345
868
|
const returnedDebugReadChat = debugReadChat
|
|
346
869
|
? {
|
|
347
870
|
...debugReadChat,
|
|
@@ -356,6 +879,7 @@ function buildReadChatCommandResult(payload: Record<string, any>, args: any): Co
|
|
|
356
879
|
return {
|
|
357
880
|
success: true,
|
|
358
881
|
...validatedPayload,
|
|
882
|
+
...preservedPayloadFields,
|
|
359
883
|
messages: sync.messages,
|
|
360
884
|
totalMessages: sync.totalMessages,
|
|
361
885
|
...(returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}),
|
|
@@ -584,6 +1108,8 @@ function buildChatDebugBundleSummary(bundle: Record<string, unknown>): Record<st
|
|
|
584
1108
|
adapterStatus: debugReadChat.adapterStatus,
|
|
585
1109
|
parsedStatus: debugReadChat.parsedStatus,
|
|
586
1110
|
returnedStatus: debugReadChat.returnedStatus,
|
|
1111
|
+
selectedMessageSource: debugReadChat.selectedMessageSource,
|
|
1112
|
+
messageSource: debugReadChat.messageSource,
|
|
587
1113
|
parsedMsgCount: debugReadChat.parsedMsgCount,
|
|
588
1114
|
returnedMsgCount: debugReadChat.returnedMsgCount,
|
|
589
1115
|
shouldPreferAdapterMessages: debugReadChat.shouldPreferAdapterMessages,
|
|
@@ -646,11 +1172,20 @@ export async function handleGetChatDebugBundle(h: CommandHelpers, args: any): Pr
|
|
|
646
1172
|
providerSessionId: readResult.providerSessionId,
|
|
647
1173
|
transcriptAuthority: readResult.transcriptAuthority,
|
|
648
1174
|
coverage: readResult.coverage,
|
|
1175
|
+
messageSource: readResult.messageSource,
|
|
1176
|
+
transcriptProvenance: readResult.transcriptProvenance,
|
|
649
1177
|
activeModal: readResult.activeModal,
|
|
650
1178
|
messagesTail: Array.isArray(readResult.messages) ? readResult.messages.slice(-20) : [],
|
|
651
1179
|
debugReadChat: readResult.debugReadChat,
|
|
652
1180
|
}
|
|
653
|
-
: {
|
|
1181
|
+
: {
|
|
1182
|
+
success: false,
|
|
1183
|
+
error: readResult.error,
|
|
1184
|
+
code: readResult.code,
|
|
1185
|
+
messageSource: readResult.messageSource,
|
|
1186
|
+
transcriptProvenance: readResult.transcriptProvenance,
|
|
1187
|
+
debugReadChat: readResult.debugReadChat,
|
|
1188
|
+
};
|
|
654
1189
|
} catch (error: any) {
|
|
655
1190
|
readChat = { success: false, error: error?.message || String(error) };
|
|
656
1191
|
}
|
|
@@ -817,16 +1352,56 @@ export async function handleChatHistory(h: CommandHelpers, args: any): Promise<C
|
|
|
817
1352
|
: typeof (h.currentSession as any)?.workspace === 'string'
|
|
818
1353
|
? (h.currentSession as any).workspace
|
|
819
1354
|
: undefined;
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
historySessionId
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
1355
|
+
const exactNativeHistoryScope = Boolean(
|
|
1356
|
+
(typeof args?.targetSessionId === 'string' && args.targetSessionId.trim())
|
|
1357
|
+
|| (typeof args?.historySessionId === 'string' && args.historySessionId.trim())
|
|
1358
|
+
|| (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
|
|
1359
|
+
);
|
|
1360
|
+
const result = supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)
|
|
1361
|
+
? readCliProviderNativeHistory(agentStr, {
|
|
1362
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
1363
|
+
historySessionId,
|
|
1364
|
+
workspace,
|
|
1365
|
+
offset: offset || 0,
|
|
1366
|
+
limit: limit || 30,
|
|
1367
|
+
excludeRecentCount,
|
|
1368
|
+
historyBehavior: provider?.historyBehavior,
|
|
1369
|
+
scripts: provider?.scripts as any,
|
|
1370
|
+
})
|
|
1371
|
+
: readProviderChatHistory(agentStr, {
|
|
1372
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
1373
|
+
historySessionId,
|
|
1374
|
+
workspace,
|
|
1375
|
+
offset: offset || 0,
|
|
1376
|
+
limit: limit || 30,
|
|
1377
|
+
excludeRecentCount,
|
|
1378
|
+
historyBehavior: provider?.historyBehavior,
|
|
1379
|
+
scripts: provider?.scripts as any,
|
|
1380
|
+
});
|
|
1381
|
+
if (supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)) {
|
|
1382
|
+
const lookup = (result as any).lookup === 'workspace' ? 'workspace' : 'session';
|
|
1383
|
+
const messages = Array.isArray((result as any).messages)
|
|
1384
|
+
? normalizeNativeHistoryMessages(agentStr, (result as any).messages as ChatMessage[], (result as any)?.providerSessionId)
|
|
1385
|
+
: [];
|
|
1386
|
+
const historyProviderSessionId = typeof (result as any)?.providerSessionId === 'string'
|
|
1387
|
+
? (result as any).providerSessionId
|
|
1388
|
+
: readHistorySessionIdFromMessages(messages) || historySessionId;
|
|
1389
|
+
const safeMapping = hasSafeNativeHistoryMapping({
|
|
1390
|
+
historySessionId: lookup === 'workspace' ? undefined : historySessionId,
|
|
1391
|
+
providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId,
|
|
1392
|
+
workspace,
|
|
1393
|
+
nativeMessages: messages,
|
|
1394
|
+
});
|
|
1395
|
+
if ((result as any).source === 'provider-native' && messages.length > 0 && !safeMapping) {
|
|
1396
|
+
return {
|
|
1397
|
+
success: true,
|
|
1398
|
+
messages: [],
|
|
1399
|
+
hasMore: false,
|
|
1400
|
+
source: 'native-unavailable',
|
|
1401
|
+
agent: agentStr,
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
830
1405
|
return { success: true, ...result, agent: agentStr };
|
|
831
1406
|
} catch (e: any) {
|
|
832
1407
|
return { success: false, error: e.message };
|
|
@@ -874,7 +1449,7 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
874
1449
|
? parsedRecord.coverage
|
|
875
1450
|
: undefined;
|
|
876
1451
|
const activeModal = parsedRecord.activeModal ?? parsedRecord.modal ?? null;
|
|
877
|
-
const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus);
|
|
1452
|
+
const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus, parsedRecord.messages);
|
|
878
1453
|
const runtimeMessageMerger = getTargetInstance(h, args) as RuntimeChatMessageMerger | null;
|
|
879
1454
|
const parsedMessages = finalizeStreamingMessagesWhenIdle(parsedRecord.messages as ChatMessage[], returnedStatus);
|
|
880
1455
|
const returnedMessages = runtimeMessageMerger?.category === 'cli'
|
|
@@ -882,36 +1457,287 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
882
1457
|
&& typeof runtimeMessageMerger.mergeRuntimeChatMessages === 'function'
|
|
883
1458
|
? runtimeMessageMerger.mergeRuntimeChatMessages(parsedMessages)
|
|
884
1459
|
: parsedMessages;
|
|
1460
|
+
const providerType = provider?.type || adapter.cliType;
|
|
1461
|
+
let selectedMessages = returnedMessages;
|
|
1462
|
+
let selectedTitle = title;
|
|
1463
|
+
let selectedProviderSessionId = providerSessionId;
|
|
1464
|
+
let selectedTranscriptAuthority = transcriptAuthority;
|
|
1465
|
+
let selectedCoverage = coverage;
|
|
1466
|
+
let selectedStatus = returnedStatus;
|
|
1467
|
+
const sessionWorkspace = typeof (h.currentSession as any)?.workspace === 'string'
|
|
1468
|
+
? (h.currentSession as any).workspace
|
|
1469
|
+
: typeof adapter.workingDir === 'string'
|
|
1470
|
+
? adapter.workingDir
|
|
1471
|
+
: undefined;
|
|
1472
|
+
const intendedWorkspace = typeof args?.workspace === 'string' ? args.workspace : undefined;
|
|
1473
|
+
let messageSource = buildCliMessageSourceProvenance({
|
|
1474
|
+
selected: 'pty-parser',
|
|
1475
|
+
provider: adapter.cliType,
|
|
1476
|
+
fallbackReason: supportsCliNativeTranscript(providerType, provider) ? 'native_history_not_checked' : 'provider_native_transcript_not_supported',
|
|
1477
|
+
sessionWorkspace,
|
|
1478
|
+
intendedWorkspace,
|
|
1479
|
+
ptyMessages: returnedMessages,
|
|
1480
|
+
returnedMessages,
|
|
1481
|
+
ptyStatusApprovalOnly: false,
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
if (supportsCliNativeTranscript(providerType, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)) {
|
|
1485
|
+
const agentStr = provider?.type || args?.agentType || getCurrentProviderType(h, adapter.cliType);
|
|
1486
|
+
const workspace = sessionWorkspace;
|
|
1487
|
+
const nativeHistoryLimit = Math.max(
|
|
1488
|
+
normalizeReadChatTailLimit(args) || 0,
|
|
1489
|
+
returnedMessages.length,
|
|
1490
|
+
200,
|
|
1491
|
+
);
|
|
1492
|
+
const nativeHistorySessionId = resolveCliNativeHistorySessionId(args, historySessionId, providerSessionId);
|
|
1493
|
+
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
1494
|
+
const exactNativeHistoryScope = Boolean(
|
|
1495
|
+
(typeof args?.historySessionId === 'string' && args.historySessionId.trim())
|
|
1496
|
+
|| (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
|
|
1497
|
+
|| providerSessionId
|
|
1498
|
+
|| (nativeHistorySessionId && nativeHistorySessionId !== targetSessionId)
|
|
1499
|
+
|| ((h.currentSession as any)?.sessionId === args?.targetSessionId && typeof (h.currentSession as any)?.providerSessionId === 'string' && (h.currentSession as any).providerSessionId.trim())
|
|
1500
|
+
);
|
|
1501
|
+
let nativeHistory: (ReturnType<typeof readProviderChatHistory> & { lookup?: 'session' | 'workspace' }) | null = null;
|
|
1502
|
+
try {
|
|
1503
|
+
nativeHistory = readCliProviderNativeHistory(agentStr, {
|
|
1504
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
1505
|
+
historySessionId: nativeHistorySessionId,
|
|
1506
|
+
workspace,
|
|
1507
|
+
offset: 0,
|
|
1508
|
+
limit: nativeHistoryLimit,
|
|
1509
|
+
excludeRecentCount: 0,
|
|
1510
|
+
historyBehavior: provider?.historyBehavior,
|
|
1511
|
+
scripts: provider?.scripts as any,
|
|
1512
|
+
});
|
|
1513
|
+
} catch (error: any) {
|
|
1514
|
+
const fallbackReason = `native_history_error:${error?.message || String(error)}`;
|
|
1515
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1516
|
+
selected: 'pty-parser',
|
|
1517
|
+
provider: adapter.cliType,
|
|
1518
|
+
fallbackReason,
|
|
1519
|
+
sessionWorkspace,
|
|
1520
|
+
intendedWorkspace,
|
|
1521
|
+
ptyMessages: returnedMessages,
|
|
1522
|
+
returnedMessages,
|
|
1523
|
+
ptyStatusApprovalOnly: false,
|
|
1524
|
+
});
|
|
1525
|
+
nativeHistory = null;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
if (nativeHistory) {
|
|
1529
|
+
const nativeMessages = Array.isArray((nativeHistory as any).messages)
|
|
1530
|
+
? normalizeNativeHistoryMessages(agentStr, (nativeHistory as any).messages as ChatMessage[], (nativeHistory as any)?.providerSessionId)
|
|
1531
|
+
: [];
|
|
1532
|
+
const historyProviderSessionId = typeof (nativeHistory as any)?.providerSessionId === 'string'
|
|
1533
|
+
? (nativeHistory as any).providerSessionId
|
|
1534
|
+
: readHistorySessionIdFromMessages(nativeMessages) || nativeHistorySessionId || historySessionId;
|
|
1535
|
+
const nativeHistoryCoverage = typeof (nativeHistory as any)?.nativeHistoryCoverage === 'string'
|
|
1536
|
+
? (nativeHistory as any).nativeHistoryCoverage
|
|
1537
|
+
: undefined;
|
|
1538
|
+
const partialReason = typeof (nativeHistory as any)?.partialReason === 'string'
|
|
1539
|
+
? (nativeHistory as any).partialReason
|
|
1540
|
+
: undefined;
|
|
1541
|
+
const unavailableReason = typeof (nativeHistory as any)?.unavailableReason === 'string'
|
|
1542
|
+
? (nativeHistory as any).unavailableReason
|
|
1543
|
+
: undefined;
|
|
1544
|
+
const lookup = (nativeHistory as any).lookup === 'workspace' ? 'workspace' : 'session';
|
|
1545
|
+
const transcriptWorkspace = typeof (nativeHistory as any)?.workspace === 'string'
|
|
1546
|
+
? (nativeHistory as any).workspace
|
|
1547
|
+
: nativeMessages.map((message: any) => typeof message?.workspace === 'string' ? message.workspace.trim() : '').find(Boolean);
|
|
1548
|
+
const nativeHistorySessionForMapping = adapter.cliType === 'antigravity-cli'
|
|
1549
|
+
&& historyProviderSessionId
|
|
1550
|
+
&& nativeHistorySessionId
|
|
1551
|
+
&& historyProviderSessionId !== nativeHistorySessionId
|
|
1552
|
+
? undefined
|
|
1553
|
+
: nativeHistorySessionId;
|
|
1554
|
+
const safeMapping = hasSafeNativeHistoryMapping({
|
|
1555
|
+
historySessionId: lookup === 'workspace' ? undefined : nativeHistorySessionForMapping,
|
|
1556
|
+
providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId || providerSessionId,
|
|
1557
|
+
workspace,
|
|
1558
|
+
nativeMessages,
|
|
1559
|
+
ptyMessages: returnedMessages,
|
|
1560
|
+
requireWorkspaceContentOverlap: lookup === 'workspace' && !exactNativeHistoryScope,
|
|
1561
|
+
});
|
|
1562
|
+
const freshEnough = isNativeHistoryFreshEnough({
|
|
1563
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1564
|
+
nativeMessages,
|
|
1565
|
+
ptyMessages: returnedMessages,
|
|
1566
|
+
});
|
|
1567
|
+
const nativeUsableForChatMessages = (nativeHistory as any).source === 'provider-native'
|
|
1568
|
+
&& nativeMessages.length > 0
|
|
1569
|
+
&& nativeHistoryCoverage !== 'partial'
|
|
1570
|
+
&& nativeHistoryCoverage !== 'unavailable'
|
|
1571
|
+
&& safeMapping;
|
|
1572
|
+
const allowStaleNativeChatMessages = adapter.cliType === 'antigravity-cli' && nativeUsableForChatMessages;
|
|
1573
|
+
if (nativeUsableForChatMessages && (freshEnough || allowStaleNativeChatMessages)) {
|
|
1574
|
+
selectedMessages = finalizeStreamingMessagesWhenIdle(nativeMessages, returnedStatus);
|
|
1575
|
+
selectedProviderSessionId = historyProviderSessionId || providerSessionId;
|
|
1576
|
+
selectedTranscriptAuthority = 'provider';
|
|
1577
|
+
selectedCoverage = (nativeHistory as any).hasMore ? 'tail' : 'full';
|
|
1578
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1579
|
+
selected: 'native-history',
|
|
1580
|
+
provider: adapter.cliType,
|
|
1581
|
+
nativeHandle: selectedProviderSessionId || nativeHistorySessionId || historySessionId,
|
|
1582
|
+
sessionWorkspace,
|
|
1583
|
+
intendedWorkspace,
|
|
1584
|
+
transcriptWorkspace,
|
|
1585
|
+
nativeSource: (nativeHistory as any).source,
|
|
1586
|
+
sourcePath: (nativeHistory as any).sourcePath,
|
|
1587
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1588
|
+
nativeHistoryCoverage,
|
|
1589
|
+
partialReason,
|
|
1590
|
+
unavailableReason,
|
|
1591
|
+
nativeMessages,
|
|
1592
|
+
ptyMessages: returnedMessages,
|
|
1593
|
+
returnedMessages: selectedMessages,
|
|
1594
|
+
safeMapping,
|
|
1595
|
+
freshEnough,
|
|
1596
|
+
ptyStatusApprovalOnly: true,
|
|
1597
|
+
});
|
|
1598
|
+
} else {
|
|
1599
|
+
const fallbackReason = buildNativeHistoryFallbackReason({
|
|
1600
|
+
providerType,
|
|
1601
|
+
provider,
|
|
1602
|
+
nativeSource: (nativeHistory as any).source,
|
|
1603
|
+
nativeHistoryCoverage,
|
|
1604
|
+
unavailableReason,
|
|
1605
|
+
nativeMessageCount: nativeMessages.length,
|
|
1606
|
+
safeMapping,
|
|
1607
|
+
freshEnough,
|
|
1608
|
+
});
|
|
1609
|
+
const unsafeNativeFallback = adapter.cliType === 'codex-cli'
|
|
1610
|
+
&& isUnsafeNativeTranscriptFallback(fallbackReason);
|
|
1611
|
+
const safeCurrentRuntimePtyMessages = unsafeNativeFallback
|
|
1612
|
+
&& isCurrentRuntimePtySafelyAttributed({
|
|
1613
|
+
adapter,
|
|
1614
|
+
helpers: h,
|
|
1615
|
+
readChatArgs: args,
|
|
1616
|
+
sessionWorkspace,
|
|
1617
|
+
intendedWorkspace,
|
|
1618
|
+
ptyMessages: returnedMessages,
|
|
1619
|
+
});
|
|
1620
|
+
const safeRuntimeAckMessages = unsafeNativeFallback
|
|
1621
|
+
&& !safeCurrentRuntimePtyMessages
|
|
1622
|
+
? selectRuntimeInputAckMessages(returnedMessages)
|
|
1623
|
+
: [];
|
|
1624
|
+
const exactRuntimeMirrorMessages = unsafeNativeFallback
|
|
1625
|
+
&& !safeCurrentRuntimePtyMessages
|
|
1626
|
+
&& safeRuntimeAckMessages.length === 0
|
|
1627
|
+
? readExactRuntimeMirrorMessages({
|
|
1628
|
+
providerType,
|
|
1629
|
+
targetSessionId: typeof args?.targetSessionId === 'string' ? args.targetSessionId : undefined,
|
|
1630
|
+
currentSessionId: typeof (h.currentSession as any)?.sessionId === 'string' ? (h.currentSession as any).sessionId : undefined,
|
|
1631
|
+
tailLimit: nativeHistoryLimit,
|
|
1632
|
+
historyBehavior: provider?.historyBehavior,
|
|
1633
|
+
})
|
|
1634
|
+
: [];
|
|
1635
|
+
const safeDaemonMessages = safeRuntimeAckMessages.length > 0
|
|
1636
|
+
? safeRuntimeAckMessages
|
|
1637
|
+
: exactRuntimeMirrorMessages;
|
|
1638
|
+
if (unsafeNativeFallback) {
|
|
1639
|
+
if (safeCurrentRuntimePtyMessages) {
|
|
1640
|
+
selectedMessages = returnedMessages;
|
|
1641
|
+
selectedTranscriptAuthority = 'daemon';
|
|
1642
|
+
selectedCoverage = coverage || 'current-turn';
|
|
1643
|
+
selectedStatus = returnedStatus;
|
|
1644
|
+
} else {
|
|
1645
|
+
selectedMessages = safeDaemonMessages;
|
|
1646
|
+
selectedTranscriptAuthority = safeDaemonMessages.length > 0 ? 'daemon' : undefined;
|
|
1647
|
+
selectedCoverage = safeDaemonMessages.length > 0 ? 'tail' : undefined;
|
|
1648
|
+
selectedStatus = coerceUnsafeNativeFallbackStatus(returnedStatus, activeModal);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
messageSource = buildCliMessageSourceProvenance({
|
|
1652
|
+
selected: 'pty-parser',
|
|
1653
|
+
provider: adapter.cliType,
|
|
1654
|
+
nativeHandle: historyProviderSessionId || nativeHistorySessionId || historySessionId,
|
|
1655
|
+
sessionWorkspace,
|
|
1656
|
+
intendedWorkspace,
|
|
1657
|
+
transcriptWorkspace,
|
|
1658
|
+
fallbackReason,
|
|
1659
|
+
nativeSource: (nativeHistory as any).source,
|
|
1660
|
+
sourcePath: (nativeHistory as any).sourcePath,
|
|
1661
|
+
sourceMtimeMs: (nativeHistory as any).sourceMtimeMs,
|
|
1662
|
+
nativeHistoryCoverage,
|
|
1663
|
+
partialReason,
|
|
1664
|
+
unavailableReason,
|
|
1665
|
+
nativeMessages,
|
|
1666
|
+
ptyMessages: returnedMessages,
|
|
1667
|
+
returnedMessages: unsafeNativeFallback && !safeCurrentRuntimePtyMessages ? safeDaemonMessages : returnedMessages,
|
|
1668
|
+
safeMapping,
|
|
1669
|
+
freshEnough,
|
|
1670
|
+
ptyStatusApprovalOnly: unsafeNativeFallback && !safeCurrentRuntimePtyMessages,
|
|
1671
|
+
});
|
|
1672
|
+
if (safeCurrentRuntimePtyMessages) {
|
|
1673
|
+
(messageSource as any).selectedDaemonSource = 'current-runtime-pty';
|
|
1674
|
+
(messageSource as any).transcriptAuthority = 'daemon';
|
|
1675
|
+
(messageSource as any).runtimeMappingSafe = true;
|
|
1676
|
+
}
|
|
1677
|
+
if (unsafeNativeFallback && exactRuntimeMirrorMessages.length > 0) {
|
|
1678
|
+
(messageSource as any).selectedDaemonSource = 'exact-runtime-mirror';
|
|
1679
|
+
(messageSource as any).transcriptAuthority = 'daemon';
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
885
1684
|
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
1685
|
return buildReadChatCommandResult({
|
|
887
|
-
messages:
|
|
888
|
-
status:
|
|
1686
|
+
messages: selectedMessages,
|
|
1687
|
+
status: selectedStatus,
|
|
889
1688
|
activeModal,
|
|
1689
|
+
messageSource,
|
|
1690
|
+
transcriptProvenance: messageSource,
|
|
890
1691
|
debugReadChat: {
|
|
891
1692
|
provider: adapter.cliType,
|
|
892
1693
|
targetSessionId: String(args?.targetSessionId || ''),
|
|
893
1694
|
adapterStatus: String(adapterStatus.status || ''),
|
|
894
1695
|
parsedStatus: String(parsedRecord.status || ''),
|
|
895
|
-
returnedStatus: String(
|
|
896
|
-
|
|
1696
|
+
returnedStatus: String(selectedStatus || ''),
|
|
1697
|
+
selectedMessageSource: (messageSource as any).selected,
|
|
1698
|
+
messageSource,
|
|
1699
|
+
shouldPreferAdapterMessages: supportsCliNativeTranscript(providerType, provider)
|
|
1700
|
+
&& isNativeSourceCanonicalHistory(provider?.canonicalHistory)
|
|
1701
|
+
&& (messageSource as any).selected !== 'native-history'
|
|
1702
|
+
&& typeof (messageSource as any).fallbackReason === 'string'
|
|
1703
|
+
&& (messageSource as any).fallbackReason.startsWith('native_history_')
|
|
1704
|
+
&& (messageSource as any).fallbackReason !== 'native_history_not_checked'
|
|
1705
|
+
&& !isUnsafeNativeTranscriptFallback((messageSource as any).fallbackReason)
|
|
1706
|
+
&& !(selectedTranscriptAuthority === 'provider' && selectedCoverage === 'full'),
|
|
897
1707
|
parsedMsgCount: parsedRecord.messages.length,
|
|
898
|
-
returnedMsgCount:
|
|
1708
|
+
returnedMsgCount: selectedMessages.length,
|
|
899
1709
|
},
|
|
900
|
-
...(
|
|
901
|
-
...(
|
|
902
|
-
...(
|
|
903
|
-
...(
|
|
1710
|
+
...(selectedTitle ? { title: selectedTitle } : {}),
|
|
1711
|
+
...(selectedProviderSessionId ? { providerSessionId: selectedProviderSessionId } : {}),
|
|
1712
|
+
...(selectedTranscriptAuthority ? { transcriptAuthority: selectedTranscriptAuthority } : {}),
|
|
1713
|
+
...(selectedCoverage ? { coverage: selectedCoverage } : {}),
|
|
904
1714
|
}, args);
|
|
905
1715
|
}
|
|
906
1716
|
const historyLimit = normalizeReadChatTailLimit(args);
|
|
907
1717
|
try {
|
|
908
1718
|
const agentStr = provider?.type || args?.agentType || getCurrentProviderType(h);
|
|
909
|
-
const workspace = typeof
|
|
910
|
-
?
|
|
911
|
-
:
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1719
|
+
const workspace = typeof (h.currentSession as any)?.workspace === 'string'
|
|
1720
|
+
? (h.currentSession as any).workspace
|
|
1721
|
+
: undefined;
|
|
1722
|
+
const intendedWorkspace = typeof args?.workspace === 'string' ? args.workspace : undefined;
|
|
1723
|
+
const exactNativeHistoryScope = Boolean(
|
|
1724
|
+
(typeof args?.targetSessionId === 'string' && args.targetSessionId.trim())
|
|
1725
|
+
|| (typeof args?.historySessionId === 'string' && args.historySessionId.trim())
|
|
1726
|
+
|| (typeof args?.providerSessionId === 'string' && args.providerSessionId.trim())
|
|
1727
|
+
|| ((h.currentSession as any)?.sessionId === args?.targetSessionId && typeof (h.currentSession as any)?.providerSessionId === 'string' && (h.currentSession as any).providerSessionId.trim())
|
|
1728
|
+
);
|
|
1729
|
+
const history = supportsCliNativeTranscript(agentStr, provider) && isNativeSourceCanonicalHistory(provider?.canonicalHistory)
|
|
1730
|
+
? readCliProviderNativeHistory(agentStr, {
|
|
1731
|
+
canonicalHistory: provider?.canonicalHistory,
|
|
1732
|
+
historySessionId,
|
|
1733
|
+
workspace,
|
|
1734
|
+
offset: 0,
|
|
1735
|
+
limit: historyLimit,
|
|
1736
|
+
excludeRecentCount: 0,
|
|
1737
|
+
historyBehavior: provider?.historyBehavior,
|
|
1738
|
+
scripts: provider?.scripts as any,
|
|
1739
|
+
})
|
|
1740
|
+
: readProviderChatHistory(agentStr, {
|
|
915
1741
|
canonicalHistory: provider?.canonicalHistory,
|
|
916
1742
|
historySessionId,
|
|
917
1743
|
workspace,
|
|
@@ -921,12 +1747,87 @@ export async function handleReadChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
921
1747
|
historyBehavior: provider?.historyBehavior,
|
|
922
1748
|
scripts: provider?.scripts as any,
|
|
923
1749
|
});
|
|
1750
|
+
const lookup = (history as any).lookup === 'workspace' ? 'workspace' : 'session';
|
|
1751
|
+
const historyMessages = Array.isArray((history as any)?.messages)
|
|
1752
|
+
? normalizeNativeHistoryMessages(agentStr, (history as any).messages as ChatMessage[], (history as any)?.providerSessionId)
|
|
1753
|
+
: [];
|
|
924
1754
|
const historyProviderSessionId = typeof (history as any)?.providerSessionId === 'string'
|
|
925
1755
|
? (history as any).providerSessionId
|
|
926
|
-
: historySessionId;
|
|
1756
|
+
: readHistorySessionIdFromMessages(historyMessages) || historySessionId;
|
|
1757
|
+
const nativeHistoryCoverage = typeof (history as any)?.nativeHistoryCoverage === 'string'
|
|
1758
|
+
? (history as any).nativeHistoryCoverage
|
|
1759
|
+
: undefined;
|
|
1760
|
+
const partialReason = typeof (history as any)?.partialReason === 'string'
|
|
1761
|
+
? (history as any).partialReason
|
|
1762
|
+
: undefined;
|
|
1763
|
+
const unavailableReason = typeof (history as any)?.unavailableReason === 'string'
|
|
1764
|
+
? (history as any).unavailableReason
|
|
1765
|
+
: undefined;
|
|
1766
|
+
const transcriptWorkspace = typeof (history as any)?.workspace === 'string'
|
|
1767
|
+
? (history as any).workspace
|
|
1768
|
+
: historyMessages.map((message: any) => typeof message?.workspace === 'string' ? message.workspace.trim() : '').find(Boolean);
|
|
1769
|
+
const safeMapping = supportsCliNativeTranscript(agentStr, provider)
|
|
1770
|
+
? hasSafeNativeHistoryMapping({
|
|
1771
|
+
historySessionId: lookup === 'workspace' ? undefined : historySessionId,
|
|
1772
|
+
providerSessionId: lookup === 'workspace' ? undefined : historyProviderSessionId,
|
|
1773
|
+
workspace,
|
|
1774
|
+
nativeMessages: historyMessages,
|
|
1775
|
+
})
|
|
1776
|
+
: false;
|
|
1777
|
+
const nativeSelected = supportsCliNativeTranscript(agentStr, provider)
|
|
1778
|
+
&& (history as any).source === 'provider-native'
|
|
1779
|
+
&& historyMessages.length > 0
|
|
1780
|
+
&& nativeHistoryCoverage !== 'partial'
|
|
1781
|
+
&& nativeHistoryCoverage !== 'unavailable'
|
|
1782
|
+
&& safeMapping;
|
|
1783
|
+
const messageSource = buildCliMessageSourceProvenance({
|
|
1784
|
+
selected: nativeSelected ? 'native-history' : 'pty-parser',
|
|
1785
|
+
provider: agentStr,
|
|
1786
|
+
nativeHandle: historyProviderSessionId || historySessionId,
|
|
1787
|
+
sessionWorkspace: workspace,
|
|
1788
|
+
intendedWorkspace,
|
|
1789
|
+
transcriptWorkspace,
|
|
1790
|
+
fallbackReason: nativeSelected
|
|
1791
|
+
? undefined
|
|
1792
|
+
: buildNativeHistoryFallbackReason({
|
|
1793
|
+
providerType: agentStr,
|
|
1794
|
+
provider,
|
|
1795
|
+
nativeSource: (history as any).source,
|
|
1796
|
+
nativeHistoryCoverage,
|
|
1797
|
+
unavailableReason,
|
|
1798
|
+
nativeMessageCount: historyMessages.length,
|
|
1799
|
+
safeMapping,
|
|
1800
|
+
freshEnough: true,
|
|
1801
|
+
}),
|
|
1802
|
+
nativeSource: (history as any).source,
|
|
1803
|
+
sourcePath: (history as any).sourcePath,
|
|
1804
|
+
sourceMtimeMs: (history as any).sourceMtimeMs,
|
|
1805
|
+
nativeHistoryCoverage,
|
|
1806
|
+
partialReason,
|
|
1807
|
+
unavailableReason,
|
|
1808
|
+
nativeMessages: historyMessages,
|
|
1809
|
+
returnedMessages: historyMessages,
|
|
1810
|
+
safeMapping,
|
|
1811
|
+
freshEnough: true,
|
|
1812
|
+
ptyStatusApprovalOnly: false,
|
|
1813
|
+
});
|
|
1814
|
+
const requiresNativeSource = supportsCliNativeTranscript(agentStr, provider)
|
|
1815
|
+
&& isNativeSourceCanonicalHistory(provider?.canonicalHistory);
|
|
1816
|
+
if (requiresNativeSource && !nativeSelected) {
|
|
1817
|
+
return {
|
|
1818
|
+
success: false,
|
|
1819
|
+
code: 'native_history_not_safely_available',
|
|
1820
|
+
error: 'Provider-native history was not safely available for the requested CLI session.',
|
|
1821
|
+
providerSessionId: historyProviderSessionId,
|
|
1822
|
+
messageSource,
|
|
1823
|
+
transcriptProvenance: messageSource,
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
927
1826
|
return buildReadChatCommandResult({
|
|
928
|
-
messages:
|
|
1827
|
+
messages: historyMessages,
|
|
929
1828
|
status: 'idle',
|
|
1829
|
+
messageSource,
|
|
1830
|
+
transcriptProvenance: messageSource,
|
|
930
1831
|
...(typeof (history as any)?.title === 'string' ? { title: (history as any).title } : {}),
|
|
931
1832
|
...(historyProviderSessionId ? { providerSessionId: historyProviderSessionId } : {}),
|
|
932
1833
|
...(((provider?.historyBehavior as any)?.transcriptAuthority === 'provider' || (provider?.historyBehavior as any)?.transcriptAuthority === 'daemon')
|
|
@@ -1184,8 +2085,24 @@ export async function handleSendChat(h: CommandHelpers, args: any): Promise<Comm
|
|
|
1184
2085
|
assertTextOnlyInput(provider, input);
|
|
1185
2086
|
if (!text) return { success: false, error: 'text required for PTY send' };
|
|
1186
2087
|
await waitOnceForFreshHermesCliStart(adapter, _log);
|
|
1187
|
-
|
|
1188
|
-
|
|
2088
|
+
const forceSend = args?.force === true || args?.forceSend === true;
|
|
2089
|
+
if (forceSend && typeof adapter.forceSendMessage === 'function') {
|
|
2090
|
+
await adapter.forceSendMessage(text);
|
|
2091
|
+
} else if (forceSend) {
|
|
2092
|
+
await adapter.sendMessage(text, { force: true });
|
|
2093
|
+
} else {
|
|
2094
|
+
await adapter.sendMessage(text);
|
|
2095
|
+
}
|
|
2096
|
+
const target = getTargetInstance(h, args) as RuntimeChatMessageMerger | null;
|
|
2097
|
+
if (target?.category === 'cli'
|
|
2098
|
+
&& target.type === adapter.cliType
|
|
2099
|
+
&& typeof target.recordAcknowledgedUserInput === 'function') {
|
|
2100
|
+
target.recordAcknowledgedUserInput(input);
|
|
2101
|
+
}
|
|
2102
|
+
return {
|
|
2103
|
+
..._logSendSuccess(`${transport}-adapter`, adapter.cliType),
|
|
2104
|
+
...(forceSend ? { forceSent: true } : {}),
|
|
2105
|
+
};
|
|
1189
2106
|
} catch (e: any) {
|
|
1190
2107
|
return { success: false, error: `${transport} send failed: ${e.message}` };
|
|
1191
2108
|
}
|
|
@@ -1742,6 +2659,9 @@ export async function handleResolveAction(h: CommandHelpers, args: any): Promise
|
|
|
1742
2659
|
if (buttonIndex < 0 && (action === 'always' || /always/i.test(button))) {
|
|
1743
2660
|
buttonIndex = buttons.findIndex(b => /always/i.test(b));
|
|
1744
2661
|
}
|
|
2662
|
+
if (buttonIndex < 0 && (action === 'approve' || action === 'accept')) {
|
|
2663
|
+
buttonIndex = pickApprovalButton(buttons, provider).index;
|
|
2664
|
+
}
|
|
1745
2665
|
if (buttonIndex < 0) {
|
|
1746
2666
|
return { success: false, error: 'Approval action did not match any visible button' };
|
|
1747
2667
|
}
|