@adhdev/daemon-core 0.9.44 → 0.9.46
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 +3 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +6 -0
- package/dist/config/chat-history.d.ts +34 -11
- package/dist/index.js +410 -310
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +410 -310
- package/dist/index.mjs.map +1 -1
- package/dist/launch/macos-app-process.d.ts +2 -0
- package/dist/providers/cli-provider-instance.d.ts +2 -5
- package/dist/providers/contracts.d.ts +33 -11
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +25 -3
- package/src/cli-adapters/provider-cli-shared.ts +6 -0
- package/src/commands/chat-commands.ts +28 -3
- package/src/commands/router.ts +13 -3
- package/src/config/chat-history.ts +282 -284
- package/src/launch/macos-app-process.ts +37 -0
- package/src/launch.ts +43 -5
- package/src/providers/cli-provider-instance.ts +57 -46
- package/src/providers/contracts.ts +35 -12
- package/src/providers/provider-schema.ts +40 -0
- package/src/providers/read-chat-contract.ts +2 -0
|
@@ -13,7 +13,7 @@ import * as fs from 'fs';
|
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import * as os from 'os';
|
|
15
15
|
import { buildRuntimeSystemChatMessage } from '../providers/chat-message-normalization.js';
|
|
16
|
-
import type { ProviderHistoryBehavior } from '../providers/contracts.js';
|
|
16
|
+
import type { ProviderCanonicalHistoryConfig, ProviderHistoryBehavior } from '../providers/contracts.js';
|
|
17
17
|
|
|
18
18
|
const HISTORY_DIR = path.join(os.homedir(), '.adhdev', 'history');
|
|
19
19
|
const RETAIN_DAYS = 30;
|
|
@@ -170,6 +170,9 @@ export interface SavedHistorySessionSummary {
|
|
|
170
170
|
lastMessageAt: number;
|
|
171
171
|
preview?: string;
|
|
172
172
|
workspace?: string;
|
|
173
|
+
source?: 'adhdev-mirror' | 'provider-native';
|
|
174
|
+
sourcePath?: string;
|
|
175
|
+
sourceMtimeMs?: number;
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
function sortSavedHistorySessionSummaries(summaries: SavedHistorySessionSummary[]): SavedHistorySessionSummary[] {
|
|
@@ -1138,6 +1141,37 @@ export class ChatHistoryWriter {
|
|
|
1138
1141
|
* the newest N messages are skipped so older-history pagination can avoid
|
|
1139
1142
|
* duplicating the live transcript tail already shown in the UI.
|
|
1140
1143
|
*/
|
|
1144
|
+
function pageHistoryRecords(
|
|
1145
|
+
agentType: string,
|
|
1146
|
+
records: HistoryMessage[],
|
|
1147
|
+
offset: number = 0,
|
|
1148
|
+
limit: number = 30,
|
|
1149
|
+
excludeRecentCount: number = 0,
|
|
1150
|
+
historyBehavior?: ProviderHistoryBehavior,
|
|
1151
|
+
): { messages: HistoryMessage[]; hasMore: boolean } {
|
|
1152
|
+
const allMessages = records
|
|
1153
|
+
.map((message) => sanitizeHistoryMessage(agentType, message))
|
|
1154
|
+
.filter(Boolean) as HistoryMessage[];
|
|
1155
|
+
allMessages.sort((a, b) => a.receivedAt - b.receivedAt);
|
|
1156
|
+
const chronological: HistoryMessage[] = [];
|
|
1157
|
+
let lastTurn: HistoryMessage | null = null;
|
|
1158
|
+
for (const message of allMessages) {
|
|
1159
|
+
const previous = chronological[chronological.length - 1];
|
|
1160
|
+
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
1161
|
+
if (message.role !== 'system' && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
1162
|
+
chronological.push(message);
|
|
1163
|
+
if (message.role !== 'system') lastTurn = message;
|
|
1164
|
+
}
|
|
1165
|
+
const collapsed = collapseReplayAssistantTurns(chronological, historyBehavior);
|
|
1166
|
+
const boundedLimit = Math.max(1, limit);
|
|
1167
|
+
const boundedOffset = Math.max(0, offset);
|
|
1168
|
+
const boundedExclude = Math.max(0, Math.min(excludeRecentCount, collapsed.length));
|
|
1169
|
+
const endExclusive = Math.max(0, collapsed.length - boundedExclude - boundedOffset);
|
|
1170
|
+
const startInclusive = Math.max(0, endExclusive - boundedLimit);
|
|
1171
|
+
const sliced = collapsed.slice(startInclusive, endExclusive);
|
|
1172
|
+
return { messages: sliced, hasMore: startInclusive > 0 };
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1141
1175
|
export function readChatHistory(
|
|
1142
1176
|
agentType: string,
|
|
1143
1177
|
offset: number = 0,
|
|
@@ -1175,29 +1209,7 @@ export function readChatHistory(
|
|
|
1175
1209
|
}
|
|
1176
1210
|
}
|
|
1177
1211
|
|
|
1178
|
-
|
|
1179
|
-
const chronological: HistoryMessage[] = [];
|
|
1180
|
-
let lastTurn: HistoryMessage | null = null;
|
|
1181
|
-
for (const message of allMessages) {
|
|
1182
|
-
const previous = chronological[chronological.length - 1];
|
|
1183
|
-
if (isAdjacentHistoryDuplicate(agentType, previous, message)) continue;
|
|
1184
|
-
if (message.role !== 'system' && isAdjacentHistoryDuplicate(agentType, lastTurn, message)) continue;
|
|
1185
|
-
chronological.push(message);
|
|
1186
|
-
if (message.role !== 'system') lastTurn = message;
|
|
1187
|
-
}
|
|
1188
|
-
const collapsed = collapseReplayAssistantTurns(chronological, historyBehavior);
|
|
1189
|
-
|
|
1190
|
-
// Page backwards from the newest saved messages while keeping the returned
|
|
1191
|
-
// slice in chronological order for prepend-based UI rendering.
|
|
1192
|
-
const boundedLimit = Math.max(1, limit);
|
|
1193
|
-
const boundedOffset = Math.max(0, offset);
|
|
1194
|
-
const boundedExclude = Math.max(0, Math.min(excludeRecentCount, collapsed.length));
|
|
1195
|
-
const endExclusive = Math.max(0, collapsed.length - boundedExclude - boundedOffset);
|
|
1196
|
-
const startInclusive = Math.max(0, endExclusive - boundedLimit);
|
|
1197
|
-
const sliced = collapsed.slice(startInclusive, endExclusive);
|
|
1198
|
-
const hasMore = startInclusive > 0;
|
|
1199
|
-
|
|
1200
|
-
return { messages: sliced, hasMore };
|
|
1212
|
+
return pageHistoryRecords(agentType, allMessages, offset, limit, excludeRecentCount, historyBehavior);
|
|
1201
1213
|
} catch {
|
|
1202
1214
|
return { messages: [], hasMore: false };
|
|
1203
1215
|
}
|
|
@@ -1276,34 +1288,6 @@ export function listSavedHistorySessions(
|
|
|
1276
1288
|
}
|
|
1277
1289
|
}
|
|
1278
1290
|
|
|
1279
|
-
function normalizeCanonicalHermesMessageContent(content: unknown): string {
|
|
1280
|
-
if (typeof content === 'string') return content.trim();
|
|
1281
|
-
if (content == null) return '';
|
|
1282
|
-
try {
|
|
1283
|
-
return JSON.stringify(content).trim();
|
|
1284
|
-
} catch {
|
|
1285
|
-
return String(content).trim();
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
function extractCanonicalHermesMessageTimestamp(message: Record<string, unknown>, fallbackTs: number): number {
|
|
1290
|
-
const numericTimestamp = Number(message.receivedAt || message.timestamp || message.ts || 0);
|
|
1291
|
-
if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
|
|
1292
|
-
const stringTimestamp = typeof message.ts === 'string'
|
|
1293
|
-
? Date.parse(message.ts)
|
|
1294
|
-
: (typeof message.timestamp === 'string' ? Date.parse(message.timestamp) : NaN);
|
|
1295
|
-
if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
|
|
1296
|
-
return fallbackTs;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
function extractTimestampValue(value: unknown): number {
|
|
1300
|
-
const numericTimestamp = Number(value || 0);
|
|
1301
|
-
if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
|
|
1302
|
-
const stringTimestamp = typeof value === 'string' ? Date.parse(value) : NaN;
|
|
1303
|
-
if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
|
|
1304
|
-
return 0;
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
1291
|
function readExistingSessionStartRecord(agentType: string, historySessionId: string): HistoryMessage | null {
|
|
1308
1292
|
try {
|
|
1309
1293
|
const dir = path.join(HISTORY_DIR, agentType);
|
|
@@ -1351,257 +1335,271 @@ function rewriteCanonicalSavedHistory(agentType: string, historySessionId: strin
|
|
|
1351
1335
|
}
|
|
1352
1336
|
}
|
|
1353
1337
|
|
|
1354
|
-
export
|
|
1338
|
+
export type ProviderNativeHistoryScripts = Record<string, ((input: any) => any) | undefined>;
|
|
1339
|
+
|
|
1340
|
+
type ProviderNativeHistoryReadResult = { records: HistoryMessage[]; sourcePath: string; sourceMtimeMs: number };
|
|
1341
|
+
|
|
1342
|
+
function getNativeHistoryScriptName(canonicalHistory: ProviderCanonicalHistoryConfig | undefined, key: 'readSession' | 'listSessions'): string {
|
|
1343
|
+
const configured = canonicalHistory?.scripts?.[key];
|
|
1344
|
+
if (typeof configured === 'string' && configured.trim()) return configured.trim();
|
|
1345
|
+
return key === 'readSession' ? 'readNativeHistory' : 'listNativeHistory';
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
function getProviderNativeHistoryScript(
|
|
1349
|
+
scripts: ProviderNativeHistoryScripts | undefined,
|
|
1350
|
+
canonicalHistory: ProviderCanonicalHistoryConfig | undefined,
|
|
1351
|
+
key: 'readSession' | 'listSessions',
|
|
1352
|
+
): ((input: any) => any) | null {
|
|
1353
|
+
if (!canonicalHistory?.scripts) return null;
|
|
1354
|
+
const fn = scripts?.[getNativeHistoryScriptName(canonicalHistory, key)];
|
|
1355
|
+
return typeof fn === 'function' ? fn : null;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
function normalizeProviderNativeHistoryRecords(agentType: string, historySessionId: string, records: unknown): HistoryMessage[] {
|
|
1359
|
+
if (!Array.isArray(records)) return [];
|
|
1355
1360
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1356
|
-
|
|
1361
|
+
return records
|
|
1362
|
+
.map((record: any) => sanitizeHistoryMessage(agentType, {
|
|
1363
|
+
ts: typeof record?.ts === 'string' ? record.ts : new Date(Number(record?.receivedAt) || Date.now()).toISOString(),
|
|
1364
|
+
receivedAt: Number(record?.receivedAt) || Date.parse(record?.ts || '') || Date.now(),
|
|
1365
|
+
role: record?.role,
|
|
1366
|
+
content: String(record?.content || ''),
|
|
1367
|
+
kind: record?.kind || (record?.role === 'system' ? 'session_start' : 'standard'),
|
|
1368
|
+
senderName: record?.senderName,
|
|
1369
|
+
agent: agentType,
|
|
1370
|
+
instanceId: record?.instanceId,
|
|
1371
|
+
historySessionId: normalizeSavedHistorySessionId(record?.historySessionId || normalizedSessionId),
|
|
1372
|
+
sessionTitle: record?.sessionTitle,
|
|
1373
|
+
workspace: record?.workspace,
|
|
1374
|
+
} as HistoryMessage))
|
|
1375
|
+
.filter(Boolean) as HistoryMessage[];
|
|
1376
|
+
}
|
|
1357
1377
|
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1378
|
+
function callProviderNativeHistoryRead(
|
|
1379
|
+
agentType: string,
|
|
1380
|
+
canonicalHistory: ProviderCanonicalHistoryConfig | undefined,
|
|
1381
|
+
scripts: ProviderNativeHistoryScripts | undefined,
|
|
1382
|
+
historySessionId: string,
|
|
1383
|
+
workspace?: string,
|
|
1384
|
+
): ProviderNativeHistoryReadResult | null {
|
|
1385
|
+
const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, 'readSession');
|
|
1386
|
+
if (!fn) return null;
|
|
1387
|
+
const result = fn({
|
|
1388
|
+
agentType,
|
|
1389
|
+
sessionId: historySessionId,
|
|
1390
|
+
historySessionId,
|
|
1391
|
+
workspace,
|
|
1392
|
+
format: canonicalHistory?.format,
|
|
1393
|
+
watchPath: canonicalHistory?.watchPath,
|
|
1394
|
+
args: { sessionId: historySessionId, historySessionId, workspace },
|
|
1395
|
+
});
|
|
1396
|
+
if (!result || typeof result !== 'object') return null;
|
|
1397
|
+
const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, (result as any).messages || (result as any).records);
|
|
1398
|
+
if (records.length === 0) return null;
|
|
1399
|
+
return {
|
|
1400
|
+
records,
|
|
1401
|
+
sourcePath: typeof (result as any).sourcePath === 'string' ? (result as any).sourcePath : '',
|
|
1402
|
+
sourceMtimeMs: Number((result as any).sourceMtimeMs) || 0,
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1377
1405
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1406
|
+
function buildNativeHistoryReadResult(
|
|
1407
|
+
agentType: string,
|
|
1408
|
+
canonicalHistory: ProviderCanonicalHistoryConfig | undefined,
|
|
1409
|
+
scripts: ProviderNativeHistoryScripts | undefined,
|
|
1410
|
+
historySessionId: string | undefined,
|
|
1411
|
+
workspace?: string,
|
|
1412
|
+
): ProviderNativeHistoryReadResult | null {
|
|
1413
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || '');
|
|
1414
|
+
if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
|
|
1415
|
+
return callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
|
|
1416
|
+
}
|
|
1385
1417
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1418
|
+
function materializeNativeHistoryToMirror(
|
|
1419
|
+
agentType: string,
|
|
1420
|
+
canonicalHistory: ProviderCanonicalHistoryConfig,
|
|
1421
|
+
historySessionId: string,
|
|
1422
|
+
workspace?: string,
|
|
1423
|
+
scripts?: ProviderNativeHistoryScripts,
|
|
1424
|
+
): boolean {
|
|
1425
|
+
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1426
|
+
if (!normalizedSessionId) return false;
|
|
1427
|
+
const nativeResult = callProviderNativeHistoryRead(agentType, canonicalHistory, scripts, normalizedSessionId, workspace);
|
|
1428
|
+
const nativeRecords = nativeResult?.records || [];
|
|
1429
|
+
if (nativeRecords.length === 0) return false;
|
|
1430
|
+
const normalizedRecords = nativeRecords.map((record) => ({
|
|
1431
|
+
...record,
|
|
1432
|
+
agent: agentType,
|
|
1433
|
+
historySessionId: normalizedSessionId,
|
|
1434
|
+
}));
|
|
1435
|
+
const existingSessionStart = readExistingSessionStartRecord(agentType, normalizedSessionId);
|
|
1436
|
+
const records = existingSessionStart && normalizedRecords[0]?.kind !== 'session_start'
|
|
1437
|
+
? [{ ...existingSessionStart, historySessionId: normalizedSessionId, agent: agentType }, ...normalizedRecords]
|
|
1438
|
+
: normalizedRecords;
|
|
1439
|
+
return rewriteCanonicalSavedHistory(agentType, normalizedSessionId, records);
|
|
1440
|
+
}
|
|
1398
1441
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
continue;
|
|
1410
|
-
}
|
|
1442
|
+
export function materializeProviderNativeHistory(
|
|
1443
|
+
agentType: string,
|
|
1444
|
+
canonicalHistory: ProviderCanonicalHistoryConfig | undefined,
|
|
1445
|
+
historySessionId: string,
|
|
1446
|
+
workspace?: string,
|
|
1447
|
+
scripts?: ProviderNativeHistoryScripts,
|
|
1448
|
+
): boolean {
|
|
1449
|
+
if (!canonicalHistory || canonicalHistory.mode !== 'materialized-mirror') return false;
|
|
1450
|
+
return materializeNativeHistoryToMirror(agentType, canonicalHistory, historySessionId, workspace, scripts);
|
|
1451
|
+
}
|
|
1411
1452
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
kind: 'tool',
|
|
1419
|
-
senderName: 'Tool',
|
|
1420
|
-
agent: 'hermes-cli',
|
|
1421
|
-
historySessionId: normalizedSessionId,
|
|
1422
|
-
});
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1453
|
+
export function isNativeSourceCanonicalHistory(canonicalHistory?: ProviderCanonicalHistoryConfig): boolean {
|
|
1454
|
+
if (!canonicalHistory) return false;
|
|
1455
|
+
if ((canonicalHistory as any).mode === 'disabled') return false;
|
|
1456
|
+
if ((canonicalHistory as any).mode === 'materialized-mirror') return false;
|
|
1457
|
+
return true;
|
|
1458
|
+
}
|
|
1425
1459
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1460
|
+
export function readProviderChatHistory(
|
|
1461
|
+
agentType: string,
|
|
1462
|
+
options: {
|
|
1463
|
+
canonicalHistory?: ProviderCanonicalHistoryConfig;
|
|
1464
|
+
historySessionId?: string;
|
|
1465
|
+
workspace?: string;
|
|
1466
|
+
offset?: number;
|
|
1467
|
+
limit?: number;
|
|
1468
|
+
excludeRecentCount?: number;
|
|
1469
|
+
historyBehavior?: ProviderHistoryBehavior;
|
|
1470
|
+
scripts?: ProviderNativeHistoryScripts;
|
|
1471
|
+
} = {},
|
|
1472
|
+
): { messages: HistoryMessage[]; hasMore: boolean; source: 'provider-native' | 'adhdev-mirror' | 'native-unavailable'; sourcePath?: string; sourceMtimeMs?: number } {
|
|
1473
|
+
if (isNativeSourceCanonicalHistory(options.canonicalHistory) && options.historySessionId) {
|
|
1474
|
+
const nativeResult = buildNativeHistoryReadResult(agentType, options.canonicalHistory, options.scripts, options.historySessionId, options.workspace);
|
|
1475
|
+
if (!nativeResult) return { messages: [], hasMore: false, source: 'native-unavailable' };
|
|
1476
|
+
return {
|
|
1477
|
+
...pageHistoryRecords(agentType, nativeResult.records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
|
|
1478
|
+
source: 'provider-native',
|
|
1479
|
+
sourcePath: nativeResult.sourcePath,
|
|
1480
|
+
sourceMtimeMs: nativeResult.sourceMtimeMs,
|
|
1481
|
+
};
|
|
1429
1482
|
}
|
|
1483
|
+
return {
|
|
1484
|
+
...readChatHistory(agentType, options.offset || 0, options.limit || 30, options.historySessionId, options.excludeRecentCount || 0, options.historyBehavior),
|
|
1485
|
+
source: 'adhdev-mirror',
|
|
1486
|
+
};
|
|
1430
1487
|
}
|
|
1431
1488
|
|
|
1432
|
-
function
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1489
|
+
function buildNativeSessionSummary(
|
|
1490
|
+
agentType: string,
|
|
1491
|
+
historySessionId: string,
|
|
1492
|
+
records: HistoryMessage[],
|
|
1493
|
+
sourcePath: string,
|
|
1494
|
+
): SavedHistorySessionSummary | null {
|
|
1495
|
+
const visible = pageHistoryRecords(agentType, records, 0, Number.MAX_SAFE_INTEGER).messages;
|
|
1496
|
+
if (visible.length === 0) return null;
|
|
1497
|
+
let sourceMtimeMs = 0;
|
|
1498
|
+
try { sourceMtimeMs = fs.statSync(sourcePath).mtimeMs; } catch { /* ignore */ }
|
|
1499
|
+
const firstMessageAt = visible[0]?.receivedAt || sourceMtimeMs || Date.now();
|
|
1500
|
+
const lastMessageAt = visible[visible.length - 1]?.receivedAt || firstMessageAt;
|
|
1501
|
+
const lastNonSystem = [...visible].reverse().find((message) => message.role !== 'system') || visible[visible.length - 1];
|
|
1502
|
+
const firstSystem = visible.find((message) => message.kind === 'session_start');
|
|
1503
|
+
return {
|
|
1504
|
+
historySessionId,
|
|
1505
|
+
sessionTitle: lastNonSystem?.content,
|
|
1506
|
+
messageCount: visible.length,
|
|
1507
|
+
firstMessageAt,
|
|
1508
|
+
lastMessageAt,
|
|
1509
|
+
preview: lastNonSystem?.content,
|
|
1510
|
+
workspace: firstSystem?.workspace || (firstSystem?.kind === 'session_start' ? firstSystem.content : undefined),
|
|
1511
|
+
source: 'provider-native',
|
|
1512
|
+
sourcePath,
|
|
1513
|
+
sourceMtimeMs,
|
|
1514
|
+
};
|
|
1456
1515
|
}
|
|
1457
1516
|
|
|
1458
|
-
function
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
const
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
: null;
|
|
1479
|
-
const command = input ? String(input.command || '').trim() : '';
|
|
1480
|
-
const summary = command ? `${name}: ${command}` : name;
|
|
1481
|
-
if (summary) parts.push({ content: summary, kind: 'tool', senderName: 'Tool', role: 'assistant' });
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
return parts;
|
|
1517
|
+
function normalizeProviderNativeHistorySessionSummary(agentType: string, item: any): SavedHistorySessionSummary | null {
|
|
1518
|
+
const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || '');
|
|
1519
|
+
if (!historySessionId) return null;
|
|
1520
|
+
const sourcePath = typeof item?.sourcePath === 'string' ? item.sourcePath : '';
|
|
1521
|
+
const sourceMtimeMs = Number(item?.sourceMtimeMs) || 0;
|
|
1522
|
+
const firstMessageAt = Number(item?.firstMessageAt) || sourceMtimeMs || Date.now();
|
|
1523
|
+
const lastMessageAt = Number(item?.lastMessageAt) || firstMessageAt;
|
|
1524
|
+
const messageCount = Math.max(0, Number(item?.messageCount) || 0);
|
|
1525
|
+
return {
|
|
1526
|
+
historySessionId,
|
|
1527
|
+
sessionTitle: typeof item?.sessionTitle === 'string' ? item.sessionTitle : undefined,
|
|
1528
|
+
messageCount,
|
|
1529
|
+
firstMessageAt,
|
|
1530
|
+
lastMessageAt,
|
|
1531
|
+
preview: typeof item?.preview === 'string' ? item.preview : undefined,
|
|
1532
|
+
workspace: typeof item?.workspace === 'string' ? item.workspace : undefined,
|
|
1533
|
+
source: 'provider-native',
|
|
1534
|
+
sourcePath,
|
|
1535
|
+
sourceMtimeMs,
|
|
1536
|
+
};
|
|
1485
1537
|
}
|
|
1486
1538
|
|
|
1487
|
-
function
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1539
|
+
function collectProviderScriptNativeHistorySessionSummaries(
|
|
1540
|
+
agentType: string,
|
|
1541
|
+
canonicalHistory: ProviderCanonicalHistoryConfig,
|
|
1542
|
+
scripts?: ProviderNativeHistoryScripts,
|
|
1543
|
+
): SavedHistorySessionSummary[] | null {
|
|
1544
|
+
const fn = getProviderNativeHistoryScript(scripts, canonicalHistory, 'listSessions');
|
|
1545
|
+
if (!fn) return null;
|
|
1546
|
+
const result = fn({
|
|
1547
|
+
agentType,
|
|
1548
|
+
format: canonicalHistory.format,
|
|
1549
|
+
watchPath: canonicalHistory.watchPath,
|
|
1550
|
+
args: {},
|
|
1551
|
+
});
|
|
1552
|
+
if (!result || typeof result !== 'object') return [];
|
|
1553
|
+
const sessions = Array.isArray((result as any).sessions) ? (result as any).sessions : [];
|
|
1554
|
+
const summaries: SavedHistorySessionSummary[] = [];
|
|
1555
|
+
for (const item of sessions) {
|
|
1556
|
+
if (Array.isArray(item?.messages || item?.records)) {
|
|
1557
|
+
const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || '');
|
|
1558
|
+
if (!historySessionId) continue;
|
|
1559
|
+
const records = normalizeProviderNativeHistoryRecords(agentType, historySessionId, item.messages || item.records);
|
|
1560
|
+
const summary = buildNativeSessionSummary(agentType, historySessionId, records, typeof item?.sourcePath === 'string' ? item.sourcePath : '');
|
|
1561
|
+
if (summary) {
|
|
1562
|
+
if (Number(item?.sourceMtimeMs)) summary.sourceMtimeMs = Number(item.sourceMtimeMs);
|
|
1563
|
+
summaries.push(summary);
|
|
1564
|
+
}
|
|
1501
1565
|
continue;
|
|
1502
1566
|
}
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
const text = typeof rawContent === 'string'
|
|
1506
|
-
? rawContent.trim()
|
|
1507
|
-
: Array.isArray(rawContent)
|
|
1508
|
-
? rawContent
|
|
1509
|
-
.map((entry) => {
|
|
1510
|
-
if (typeof entry === 'string') return entry.trim();
|
|
1511
|
-
if (!entry || typeof entry !== 'object') return '';
|
|
1512
|
-
const nested = entry as Record<string, unknown>;
|
|
1513
|
-
if (typeof nested.text === 'string') return nested.text.trim();
|
|
1514
|
-
if (typeof nested.content === 'string') return nested.content.trim();
|
|
1515
|
-
return '';
|
|
1516
|
-
})
|
|
1517
|
-
.filter(Boolean)
|
|
1518
|
-
.join('\n')
|
|
1519
|
-
: '';
|
|
1520
|
-
if (text) parts.push({ role: 'assistant', content: text, kind: 'tool', senderName: 'Tool' });
|
|
1521
|
-
}
|
|
1567
|
+
const summary = normalizeProviderNativeHistorySessionSummary(agentType, item);
|
|
1568
|
+
if (summary) summaries.push(summary);
|
|
1522
1569
|
}
|
|
1523
|
-
return
|
|
1570
|
+
return sortSavedHistorySessionSummaries(summaries);
|
|
1524
1571
|
}
|
|
1525
1572
|
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
const lines = fs.readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
|
|
1534
|
-
const records: HistoryMessage[] = [];
|
|
1535
|
-
const existingSessionStart = readExistingSessionStartRecord('claude-cli', normalizedSessionId);
|
|
1536
|
-
if (existingSessionStart) {
|
|
1537
|
-
records.push({
|
|
1538
|
-
...existingSessionStart,
|
|
1539
|
-
historySessionId: normalizedSessionId,
|
|
1540
|
-
});
|
|
1541
|
-
}
|
|
1542
|
-
let fallbackTs = Date.now();
|
|
1543
|
-
for (const line of lines) {
|
|
1544
|
-
let parsed: Record<string, unknown> | null = null;
|
|
1545
|
-
try {
|
|
1546
|
-
parsed = JSON.parse(line) as Record<string, unknown>;
|
|
1547
|
-
} catch {
|
|
1548
|
-
parsed = null;
|
|
1549
|
-
}
|
|
1550
|
-
if (!parsed) continue;
|
|
1551
|
-
const parsedSessionId = String(parsed.sessionId || '').trim();
|
|
1552
|
-
if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
|
|
1553
|
-
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
1554
|
-
fallbackTs = receivedAt + 1;
|
|
1555
|
-
const parsedWorkspace = String(parsed.cwd || workspace || '').trim();
|
|
1556
|
-
if (records.length === 0 && parsedWorkspace) {
|
|
1557
|
-
records.push({
|
|
1558
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1559
|
-
receivedAt,
|
|
1560
|
-
role: 'system',
|
|
1561
|
-
kind: 'session_start',
|
|
1562
|
-
content: parsedWorkspace,
|
|
1563
|
-
agent: 'claude-cli',
|
|
1564
|
-
historySessionId: normalizedSessionId,
|
|
1565
|
-
workspace: parsedWorkspace,
|
|
1566
|
-
});
|
|
1567
|
-
}
|
|
1568
|
-
const type = String(parsed.type || '').trim();
|
|
1569
|
-
const message = parsed.message && typeof parsed.message === 'object'
|
|
1570
|
-
? parsed.message as Record<string, unknown>
|
|
1571
|
-
: null;
|
|
1572
|
-
if (type === 'user' && message) {
|
|
1573
|
-
for (const part of extractClaudeUserContentParts(message.content)) {
|
|
1574
|
-
records.push({
|
|
1575
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1576
|
-
receivedAt,
|
|
1577
|
-
role: part.role,
|
|
1578
|
-
content: part.content,
|
|
1579
|
-
kind: part.kind,
|
|
1580
|
-
senderName: part.senderName,
|
|
1581
|
-
agent: 'claude-cli',
|
|
1582
|
-
historySessionId: normalizedSessionId,
|
|
1583
|
-
});
|
|
1584
|
-
}
|
|
1585
|
-
continue;
|
|
1586
|
-
}
|
|
1587
|
-
if (type === 'assistant' && message) {
|
|
1588
|
-
for (const part of extractClaudeAssistantContentParts(message.content)) {
|
|
1589
|
-
records.push({
|
|
1590
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1591
|
-
receivedAt,
|
|
1592
|
-
role: 'assistant',
|
|
1593
|
-
content: part.content,
|
|
1594
|
-
kind: part.kind,
|
|
1595
|
-
senderName: part.senderName,
|
|
1596
|
-
agent: 'claude-cli',
|
|
1597
|
-
historySessionId: normalizedSessionId,
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1573
|
+
function collectNativeHistorySessionSummaries(
|
|
1574
|
+
agentType: string,
|
|
1575
|
+
canonicalHistory: ProviderCanonicalHistoryConfig,
|
|
1576
|
+
scripts?: ProviderNativeHistoryScripts,
|
|
1577
|
+
): SavedHistorySessionSummary[] {
|
|
1578
|
+
return collectProviderScriptNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) || [];
|
|
1579
|
+
}
|
|
1602
1580
|
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1581
|
+
export function listProviderHistorySessions(
|
|
1582
|
+
agentType: string,
|
|
1583
|
+
options: {
|
|
1584
|
+
canonicalHistory?: ProviderCanonicalHistoryConfig;
|
|
1585
|
+
offset?: number;
|
|
1586
|
+
limit?: number;
|
|
1587
|
+
historyBehavior?: ProviderHistoryBehavior;
|
|
1588
|
+
scripts?: ProviderNativeHistoryScripts;
|
|
1589
|
+
} = {},
|
|
1590
|
+
): { sessions: SavedHistorySessionSummary[]; hasMore: boolean; source: 'provider-native' | 'adhdev-mirror' } {
|
|
1591
|
+
if (isNativeSourceCanonicalHistory(options.canonicalHistory)) {
|
|
1592
|
+
const offset = Math.max(0, options.offset || 0);
|
|
1593
|
+
const limit = Math.max(1, options.limit || 30);
|
|
1594
|
+
const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory!, options.scripts);
|
|
1595
|
+
return {
|
|
1596
|
+
sessions: summaries.slice(offset, offset + limit),
|
|
1597
|
+
hasMore: offset + limit < summaries.length,
|
|
1598
|
+
source: 'provider-native',
|
|
1599
|
+
};
|
|
1606
1600
|
}
|
|
1601
|
+
return {
|
|
1602
|
+
...listSavedHistorySessions(agentType, { offset: options.offset, limit: options.limit }, options.historyBehavior),
|
|
1603
|
+
source: 'adhdev-mirror',
|
|
1604
|
+
};
|
|
1607
1605
|
}
|