@adhdev/daemon-core 0.9.45 → 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/config/chat-history.d.ts +6 -4
- package/dist/index.js +190 -712
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +190 -712
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +2 -7
- package/dist/providers/contracts.d.ts +23 -14
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/commands/chat-commands.ts +1 -0
- package/src/commands/router.ts +4 -0
- package/src/config/chat-history.ts +161 -680
- package/src/providers/cli-provider-instance.ts +32 -70
- package/src/providers/contracts.ts +24 -14
- package/src/providers/provider-schema.ts +40 -0
|
@@ -1288,34 +1288,6 @@ export function listSavedHistorySessions(
|
|
|
1288
1288
|
}
|
|
1289
1289
|
}
|
|
1290
1290
|
|
|
1291
|
-
function normalizeCanonicalHermesMessageContent(content: unknown): string {
|
|
1292
|
-
if (typeof content === 'string') return content.trim();
|
|
1293
|
-
if (content == null) return '';
|
|
1294
|
-
try {
|
|
1295
|
-
return JSON.stringify(content).trim();
|
|
1296
|
-
} catch {
|
|
1297
|
-
return String(content).trim();
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
function extractCanonicalHermesMessageTimestamp(message: Record<string, unknown>, fallbackTs: number): number {
|
|
1302
|
-
const numericTimestamp = Number(message.receivedAt || message.timestamp || message.ts || 0);
|
|
1303
|
-
if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
|
|
1304
|
-
const stringTimestamp = typeof message.ts === 'string'
|
|
1305
|
-
? Date.parse(message.ts)
|
|
1306
|
-
: (typeof message.timestamp === 'string' ? Date.parse(message.timestamp) : NaN);
|
|
1307
|
-
if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
|
|
1308
|
-
return fallbackTs;
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
function extractTimestampValue(value: unknown): number {
|
|
1312
|
-
const numericTimestamp = Number(value || 0);
|
|
1313
|
-
if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
|
|
1314
|
-
const stringTimestamp = typeof value === 'string' ? Date.parse(value) : NaN;
|
|
1315
|
-
if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
|
|
1316
|
-
return 0;
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
1291
|
function readExistingSessionStartRecord(agentType: string, historySessionId: string): HistoryMessage | null {
|
|
1320
1292
|
try {
|
|
1321
1293
|
const dir = path.join(HISTORY_DIR, agentType);
|
|
@@ -1363,610 +1335,119 @@ function rewriteCanonicalSavedHistory(agentType: string, historySessionId: strin
|
|
|
1363
1335
|
}
|
|
1364
1336
|
}
|
|
1365
1337
|
|
|
1366
|
-
|
|
1367
|
-
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1368
|
-
if (!normalizedSessionId) return null;
|
|
1369
|
-
try {
|
|
1370
|
-
const sessionFilePath = path.join(os.homedir(), '.hermes', 'sessions', `session_${normalizedSessionId}.json`);
|
|
1371
|
-
if (!fs.existsSync(sessionFilePath)) return null;
|
|
1372
|
-
const raw = JSON.parse(fs.readFileSync(sessionFilePath, 'utf-8')) as {
|
|
1373
|
-
session_start?: string;
|
|
1374
|
-
last_updated?: string;
|
|
1375
|
-
messages?: Array<Record<string, unknown>>;
|
|
1376
|
-
};
|
|
1377
|
-
const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
|
|
1378
|
-
const records: HistoryMessage[] = [];
|
|
1379
|
-
let fallbackTs = Date.parse(raw.session_start || raw.last_updated || '') || Date.now();
|
|
1380
|
-
for (const message of canonicalMessages) {
|
|
1381
|
-
const role = String(message.role || '').trim();
|
|
1382
|
-
const content = normalizeCanonicalHermesMessageContent(message.content);
|
|
1383
|
-
if (!content) continue;
|
|
1384
|
-
const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
|
|
1385
|
-
fallbackTs = receivedAt + 1;
|
|
1386
|
-
if (role === 'user' || role === 'assistant') {
|
|
1387
|
-
records.push({
|
|
1388
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1389
|
-
receivedAt,
|
|
1390
|
-
role,
|
|
1391
|
-
content,
|
|
1392
|
-
kind: 'standard',
|
|
1393
|
-
agent: 'hermes-cli',
|
|
1394
|
-
historySessionId: normalizedSessionId,
|
|
1395
|
-
});
|
|
1396
|
-
continue;
|
|
1397
|
-
}
|
|
1398
|
-
if (role === 'tool') {
|
|
1399
|
-
records.push({
|
|
1400
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1401
|
-
receivedAt,
|
|
1402
|
-
role: 'assistant',
|
|
1403
|
-
content,
|
|
1404
|
-
kind: 'tool',
|
|
1405
|
-
senderName: 'Tool',
|
|
1406
|
-
agent: 'hermes-cli',
|
|
1407
|
-
historySessionId: normalizedSessionId,
|
|
1408
|
-
});
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
return records;
|
|
1412
|
-
} catch {
|
|
1413
|
-
return null;
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1338
|
+
export type ProviderNativeHistoryScripts = Record<string, ((input: any) => any) | undefined>;
|
|
1416
1339
|
|
|
1417
|
-
|
|
1418
|
-
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1419
|
-
if (!normalizedSessionId) return false;
|
|
1340
|
+
type ProviderNativeHistoryReadResult = { records: HistoryMessage[]; sourcePath: string; sourceMtimeMs: number };
|
|
1420
1341
|
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
session_start?: string;
|
|
1426
|
-
last_updated?: string;
|
|
1427
|
-
messages?: Array<Record<string, unknown>>;
|
|
1428
|
-
};
|
|
1429
|
-
const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
|
|
1430
|
-
const dir = path.join(HISTORY_DIR, 'hermes-cli');
|
|
1431
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1432
|
-
const existingSessionStart = readExistingSessionStartRecord('hermes-cli', normalizedSessionId);
|
|
1433
|
-
const records: HistoryMessage[] = [];
|
|
1434
|
-
if (existingSessionStart) {
|
|
1435
|
-
records.push({
|
|
1436
|
-
...existingSessionStart,
|
|
1437
|
-
historySessionId: normalizedSessionId,
|
|
1438
|
-
});
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
let fallbackTs = Date.parse(raw.session_start || raw.last_updated || '') || Date.now();
|
|
1442
|
-
for (const message of canonicalMessages) {
|
|
1443
|
-
const role = String(message.role || '').trim();
|
|
1444
|
-
const content = normalizeCanonicalHermesMessageContent(message.content);
|
|
1445
|
-
if (!content) continue;
|
|
1446
|
-
const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
|
|
1447
|
-
fallbackTs = receivedAt + 1;
|
|
1448
|
-
|
|
1449
|
-
if (role === 'user') {
|
|
1450
|
-
records.push({
|
|
1451
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1452
|
-
receivedAt,
|
|
1453
|
-
role: 'user',
|
|
1454
|
-
content,
|
|
1455
|
-
kind: 'standard',
|
|
1456
|
-
agent: 'hermes-cli',
|
|
1457
|
-
historySessionId: normalizedSessionId,
|
|
1458
|
-
});
|
|
1459
|
-
continue;
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
if (role === 'assistant') {
|
|
1463
|
-
records.push({
|
|
1464
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1465
|
-
receivedAt,
|
|
1466
|
-
role: 'assistant',
|
|
1467
|
-
content,
|
|
1468
|
-
kind: 'standard',
|
|
1469
|
-
agent: 'hermes-cli',
|
|
1470
|
-
historySessionId: normalizedSessionId,
|
|
1471
|
-
});
|
|
1472
|
-
continue;
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
if (role === 'tool') {
|
|
1476
|
-
records.push({
|
|
1477
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1478
|
-
receivedAt,
|
|
1479
|
-
role: 'assistant',
|
|
1480
|
-
content,
|
|
1481
|
-
kind: 'tool',
|
|
1482
|
-
senderName: 'Tool',
|
|
1483
|
-
agent: 'hermes-cli',
|
|
1484
|
-
historySessionId: normalizedSessionId,
|
|
1485
|
-
});
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
return rewriteCanonicalSavedHistory('hermes-cli', normalizedSessionId, records);
|
|
1490
|
-
} catch {
|
|
1491
|
-
return false;
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
function resolveClaudeProjectTranscriptPath(historySessionId: string, workspace?: string): string | null {
|
|
1496
|
-
const claudeProjectsDir = path.join(os.homedir(), '.claude', 'projects');
|
|
1497
|
-
if (!fs.existsSync(claudeProjectsDir)) return null;
|
|
1498
|
-
const normalizedWorkspace = typeof workspace === 'string' ? workspace.trim() : '';
|
|
1499
|
-
if (normalizedWorkspace) {
|
|
1500
|
-
const directPath = path.join(claudeProjectsDir, normalizedWorkspace.replace(/[\\/]/g, '-'), `${historySessionId}.jsonl`);
|
|
1501
|
-
if (fs.existsSync(directPath)) return directPath;
|
|
1502
|
-
}
|
|
1503
|
-
const stack = [claudeProjectsDir];
|
|
1504
|
-
while (stack.length > 0) {
|
|
1505
|
-
const current = stack.pop();
|
|
1506
|
-
if (!current) continue;
|
|
1507
|
-
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
1508
|
-
const entryPath = path.join(current, entry.name);
|
|
1509
|
-
if (entry.isDirectory()) {
|
|
1510
|
-
stack.push(entryPath);
|
|
1511
|
-
continue;
|
|
1512
|
-
}
|
|
1513
|
-
if (entry.isFile() && entry.name === `${historySessionId}.jsonl`) {
|
|
1514
|
-
return entryPath;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
return null;
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
function extractClaudeAssistantContentParts(content: unknown): Array<{ content: string; kind: 'standard' | 'tool'; senderName?: string; role?: 'assistant' }> {
|
|
1522
|
-
if (typeof content === 'string') {
|
|
1523
|
-
const trimmed = content.trim();
|
|
1524
|
-
return trimmed ? [{ content: trimmed, kind: 'standard', role: 'assistant' }] : [];
|
|
1525
|
-
}
|
|
1526
|
-
if (!Array.isArray(content)) return [];
|
|
1527
|
-
const parts: Array<{ content: string; kind: 'standard' | 'tool'; senderName?: string; role?: 'assistant' }> = [];
|
|
1528
|
-
for (const block of content) {
|
|
1529
|
-
if (!block || typeof block !== 'object') continue;
|
|
1530
|
-
const record = block as Record<string, unknown>;
|
|
1531
|
-
const type = String(record.type || '').trim();
|
|
1532
|
-
if (type === 'text') {
|
|
1533
|
-
const text = String(record.text || '').trim();
|
|
1534
|
-
if (text) parts.push({ content: text, kind: 'standard', role: 'assistant' });
|
|
1535
|
-
continue;
|
|
1536
|
-
}
|
|
1537
|
-
if (type === 'tool_use') {
|
|
1538
|
-
const name = String(record.name || '').trim() || 'Tool';
|
|
1539
|
-
const input = record.input && typeof record.input === 'object'
|
|
1540
|
-
? record.input as Record<string, unknown>
|
|
1541
|
-
: null;
|
|
1542
|
-
const command = input ? String(input.command || '').trim() : '';
|
|
1543
|
-
const summary = command ? `${name}: ${command}` : name;
|
|
1544
|
-
if (summary) parts.push({ content: summary, kind: 'tool', senderName: 'Tool', role: 'assistant' });
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
return parts;
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1550
|
-
function extractClaudeUserContentParts(content: unknown): Array<{ role: 'user' | 'assistant'; content: string; kind: 'standard' | 'tool'; senderName?: string }> {
|
|
1551
|
-
if (typeof content === 'string') {
|
|
1552
|
-
const trimmed = content.trim();
|
|
1553
|
-
return trimmed ? [{ role: 'user', content: trimmed, kind: 'standard' }] : [];
|
|
1554
|
-
}
|
|
1555
|
-
if (!Array.isArray(content)) return [];
|
|
1556
|
-
const parts: Array<{ role: 'user' | 'assistant'; content: string; kind: 'standard' | 'tool'; senderName?: string }> = [];
|
|
1557
|
-
for (const block of content) {
|
|
1558
|
-
if (!block || typeof block !== 'object') continue;
|
|
1559
|
-
const record = block as Record<string, unknown>;
|
|
1560
|
-
const type = String(record.type || '').trim();
|
|
1561
|
-
if (type === 'text') {
|
|
1562
|
-
const text = String(record.text || '').trim();
|
|
1563
|
-
if (text) parts.push({ role: 'user', content: text, kind: 'standard' });
|
|
1564
|
-
continue;
|
|
1565
|
-
}
|
|
1566
|
-
if (type === 'tool_result') {
|
|
1567
|
-
const rawContent = record.content;
|
|
1568
|
-
const text = typeof rawContent === 'string'
|
|
1569
|
-
? rawContent.trim()
|
|
1570
|
-
: Array.isArray(rawContent)
|
|
1571
|
-
? rawContent
|
|
1572
|
-
.map((entry) => {
|
|
1573
|
-
if (typeof entry === 'string') return entry.trim();
|
|
1574
|
-
if (!entry || typeof entry !== 'object') return '';
|
|
1575
|
-
const nested = entry as Record<string, unknown>;
|
|
1576
|
-
if (typeof nested.text === 'string') return nested.text.trim();
|
|
1577
|
-
if (typeof nested.content === 'string') return nested.content.trim();
|
|
1578
|
-
return '';
|
|
1579
|
-
})
|
|
1580
|
-
.filter(Boolean)
|
|
1581
|
-
.join('\n')
|
|
1582
|
-
: '';
|
|
1583
|
-
if (text) parts.push({ role: 'assistant', content: text, kind: 'tool', senderName: 'Tool' });
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
return parts;
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
function buildClaudeNativeHistoryRecords(historySessionId: string, workspace?: string): HistoryMessage[] | null {
|
|
1590
|
-
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1591
|
-
if (!normalizedSessionId) return null;
|
|
1592
|
-
try {
|
|
1593
|
-
const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
|
|
1594
|
-
if (!transcriptPath) return null;
|
|
1595
|
-
const lines = fs.readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
|
|
1596
|
-
const records: HistoryMessage[] = [];
|
|
1597
|
-
let fallbackTs = Date.now();
|
|
1598
|
-
for (const line of lines) {
|
|
1599
|
-
let parsed: Record<string, unknown> | null = null;
|
|
1600
|
-
try { parsed = JSON.parse(line) as Record<string, unknown>; } catch { parsed = null; }
|
|
1601
|
-
if (!parsed) continue;
|
|
1602
|
-
const parsedSessionId = String(parsed.sessionId || '').trim();
|
|
1603
|
-
if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
|
|
1604
|
-
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
1605
|
-
fallbackTs = receivedAt + 1;
|
|
1606
|
-
const parsedWorkspace = String(parsed.cwd || workspace || '').trim();
|
|
1607
|
-
if (records.length === 0 && parsedWorkspace) {
|
|
1608
|
-
records.push({
|
|
1609
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1610
|
-
receivedAt,
|
|
1611
|
-
role: 'system',
|
|
1612
|
-
kind: 'session_start',
|
|
1613
|
-
content: parsedWorkspace,
|
|
1614
|
-
agent: 'claude-cli',
|
|
1615
|
-
historySessionId: normalizedSessionId,
|
|
1616
|
-
workspace: parsedWorkspace,
|
|
1617
|
-
});
|
|
1618
|
-
}
|
|
1619
|
-
const type = String(parsed.type || '').trim();
|
|
1620
|
-
const message = parsed.message && typeof parsed.message === 'object'
|
|
1621
|
-
? parsed.message as Record<string, unknown>
|
|
1622
|
-
: null;
|
|
1623
|
-
if (type === 'user' && message) {
|
|
1624
|
-
for (const part of extractClaudeUserContentParts(message.content)) {
|
|
1625
|
-
records.push({
|
|
1626
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1627
|
-
receivedAt,
|
|
1628
|
-
role: part.role,
|
|
1629
|
-
content: part.content,
|
|
1630
|
-
kind: part.kind,
|
|
1631
|
-
senderName: part.senderName,
|
|
1632
|
-
agent: 'claude-cli',
|
|
1633
|
-
historySessionId: normalizedSessionId,
|
|
1634
|
-
});
|
|
1635
|
-
}
|
|
1636
|
-
continue;
|
|
1637
|
-
}
|
|
1638
|
-
if (type === 'assistant' && message) {
|
|
1639
|
-
for (const part of extractClaudeAssistantContentParts(message.content)) {
|
|
1640
|
-
records.push({
|
|
1641
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1642
|
-
receivedAt,
|
|
1643
|
-
role: 'assistant',
|
|
1644
|
-
content: part.content,
|
|
1645
|
-
kind: part.kind,
|
|
1646
|
-
senderName: part.senderName,
|
|
1647
|
-
agent: 'claude-cli',
|
|
1648
|
-
historySessionId: normalizedSessionId,
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
return records;
|
|
1654
|
-
} catch {
|
|
1655
|
-
return null;
|
|
1656
|
-
}
|
|
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';
|
|
1657
1346
|
}
|
|
1658
1347
|
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
const records: HistoryMessage[] = [];
|
|
1668
|
-
const existingSessionStart = readExistingSessionStartRecord('claude-cli', normalizedSessionId);
|
|
1669
|
-
if (existingSessionStart) {
|
|
1670
|
-
records.push({
|
|
1671
|
-
...existingSessionStart,
|
|
1672
|
-
historySessionId: normalizedSessionId,
|
|
1673
|
-
});
|
|
1674
|
-
}
|
|
1675
|
-
let fallbackTs = Date.now();
|
|
1676
|
-
for (const line of lines) {
|
|
1677
|
-
let parsed: Record<string, unknown> | null = null;
|
|
1678
|
-
try {
|
|
1679
|
-
parsed = JSON.parse(line) as Record<string, unknown>;
|
|
1680
|
-
} catch {
|
|
1681
|
-
parsed = null;
|
|
1682
|
-
}
|
|
1683
|
-
if (!parsed) continue;
|
|
1684
|
-
const parsedSessionId = String(parsed.sessionId || '').trim();
|
|
1685
|
-
if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
|
|
1686
|
-
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
1687
|
-
fallbackTs = receivedAt + 1;
|
|
1688
|
-
const parsedWorkspace = String(parsed.cwd || workspace || '').trim();
|
|
1689
|
-
if (records.length === 0 && parsedWorkspace) {
|
|
1690
|
-
records.push({
|
|
1691
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1692
|
-
receivedAt,
|
|
1693
|
-
role: 'system',
|
|
1694
|
-
kind: 'session_start',
|
|
1695
|
-
content: parsedWorkspace,
|
|
1696
|
-
agent: 'claude-cli',
|
|
1697
|
-
historySessionId: normalizedSessionId,
|
|
1698
|
-
workspace: parsedWorkspace,
|
|
1699
|
-
});
|
|
1700
|
-
}
|
|
1701
|
-
const type = String(parsed.type || '').trim();
|
|
1702
|
-
const message = parsed.message && typeof parsed.message === 'object'
|
|
1703
|
-
? parsed.message as Record<string, unknown>
|
|
1704
|
-
: null;
|
|
1705
|
-
if (type === 'user' && message) {
|
|
1706
|
-
for (const part of extractClaudeUserContentParts(message.content)) {
|
|
1707
|
-
records.push({
|
|
1708
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1709
|
-
receivedAt,
|
|
1710
|
-
role: part.role,
|
|
1711
|
-
content: part.content,
|
|
1712
|
-
kind: part.kind,
|
|
1713
|
-
senderName: part.senderName,
|
|
1714
|
-
agent: 'claude-cli',
|
|
1715
|
-
historySessionId: normalizedSessionId,
|
|
1716
|
-
});
|
|
1717
|
-
}
|
|
1718
|
-
continue;
|
|
1719
|
-
}
|
|
1720
|
-
if (type === 'assistant' && message) {
|
|
1721
|
-
for (const part of extractClaudeAssistantContentParts(message.content)) {
|
|
1722
|
-
records.push({
|
|
1723
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1724
|
-
receivedAt,
|
|
1725
|
-
role: 'assistant',
|
|
1726
|
-
content: part.content,
|
|
1727
|
-
kind: part.kind,
|
|
1728
|
-
senderName: part.senderName,
|
|
1729
|
-
agent: 'claude-cli',
|
|
1730
|
-
historySessionId: normalizedSessionId,
|
|
1731
|
-
});
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1736
|
-
return rewriteCanonicalSavedHistory('claude-cli', normalizedSessionId, records);
|
|
1737
|
-
} catch {
|
|
1738
|
-
return false;
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
function isUuidLikeSessionId(sessionId: string): boolean {
|
|
1743
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);
|
|
1744
|
-
}
|
|
1745
|
-
|
|
1746
|
-
function readCodexSessionMeta(filePath: string): Record<string, unknown> | null {
|
|
1747
|
-
try {
|
|
1748
|
-
const firstLine = fs.readFileSync(filePath, 'utf-8').split('\n').find(Boolean);
|
|
1749
|
-
if (!firstLine) return null;
|
|
1750
|
-
const parsed = JSON.parse(firstLine) as Record<string, unknown>;
|
|
1751
|
-
if (String(parsed.type || '') !== 'session_meta') return null;
|
|
1752
|
-
const payload = parsed.payload && typeof parsed.payload === 'object'
|
|
1753
|
-
? parsed.payload as Record<string, unknown>
|
|
1754
|
-
: null;
|
|
1755
|
-
return payload;
|
|
1756
|
-
} catch {
|
|
1757
|
-
return null;
|
|
1758
|
-
}
|
|
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;
|
|
1759
1356
|
}
|
|
1760
1357
|
|
|
1761
|
-
|
|
1358
|
+
function normalizeProviderNativeHistoryRecords(agentType: string, historySessionId: string, records: unknown): HistoryMessage[] {
|
|
1359
|
+
if (!Array.isArray(records)) return [];
|
|
1762
1360
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
for (const entry of entries) {
|
|
1779
|
-
const entryPath = path.join(current, entry.name);
|
|
1780
|
-
if (entry.isDirectory()) {
|
|
1781
|
-
stack.push(entryPath);
|
|
1782
|
-
continue;
|
|
1783
|
-
}
|
|
1784
|
-
if (!entry.isFile() || !entry.name.endsWith('.jsonl') || !entry.name.includes(normalizedSessionId)) continue;
|
|
1785
|
-
const meta = readCodexSessionMeta(entryPath);
|
|
1786
|
-
const metaSessionId = String(meta?.id || '').trim();
|
|
1787
|
-
if (metaSessionId && metaSessionId !== normalizedSessionId) continue;
|
|
1788
|
-
const metaWorkspace = String(meta?.cwd || '').trim();
|
|
1789
|
-
let mtimeMs = 0;
|
|
1790
|
-
try { mtimeMs = fs.statSync(entryPath).mtimeMs; } catch { /* ignore */ }
|
|
1791
|
-
candidates.push({
|
|
1792
|
-
path: entryPath,
|
|
1793
|
-
mtimeMs,
|
|
1794
|
-
workspaceMatches: !!normalizedWorkspace && metaWorkspace === normalizedWorkspace,
|
|
1795
|
-
metaMatches: metaSessionId === normalizedSessionId,
|
|
1796
|
-
});
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
candidates.sort((a, b) => Number(b.workspaceMatches) - Number(a.workspaceMatches)
|
|
1800
|
-
|| Number(b.metaMatches) - Number(a.metaMatches)
|
|
1801
|
-
|| b.mtimeMs - a.mtimeMs);
|
|
1802
|
-
return candidates[0]?.path || null;
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1805
|
-
function flattenCodexContent(content: unknown): string {
|
|
1806
|
-
if (typeof content === 'string') return content.trim();
|
|
1807
|
-
if (content == null) return '';
|
|
1808
|
-
if (Array.isArray(content)) {
|
|
1809
|
-
return content
|
|
1810
|
-
.map((entry) => flattenCodexContent(entry))
|
|
1811
|
-
.filter(Boolean)
|
|
1812
|
-
.join('\n')
|
|
1813
|
-
.trim();
|
|
1814
|
-
}
|
|
1815
|
-
if (typeof content === 'object') {
|
|
1816
|
-
const record = content as Record<string, unknown>;
|
|
1817
|
-
if (typeof record.text === 'string') return record.text.trim();
|
|
1818
|
-
if (typeof record.content === 'string' || Array.isArray(record.content)) return flattenCodexContent(record.content);
|
|
1819
|
-
if (typeof record.output === 'string') return record.output.trim();
|
|
1820
|
-
if (typeof record.message === 'string') return record.message.trim();
|
|
1821
|
-
}
|
|
1822
|
-
return '';
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
function summarizeCodexToolCall(payload: Record<string, unknown>): string {
|
|
1826
|
-
const name = String(payload.name || payload.type || 'tool').trim() || 'tool';
|
|
1827
|
-
const rawArguments = payload.arguments ?? payload.input;
|
|
1828
|
-
let argumentValue = '';
|
|
1829
|
-
if (typeof rawArguments === 'string') {
|
|
1830
|
-
const trimmed = rawArguments.trim();
|
|
1831
|
-
try {
|
|
1832
|
-
const parsed = JSON.parse(trimmed) as unknown;
|
|
1833
|
-
argumentValue = summarizeCodexToolArguments(parsed);
|
|
1834
|
-
} catch {
|
|
1835
|
-
argumentValue = trimmed;
|
|
1836
|
-
}
|
|
1837
|
-
} else {
|
|
1838
|
-
argumentValue = summarizeCodexToolArguments(rawArguments);
|
|
1839
|
-
}
|
|
1840
|
-
return argumentValue ? `${name}: ${argumentValue}` : name;
|
|
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[];
|
|
1841
1376
|
}
|
|
1842
1377
|
|
|
1843
|
-
function
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
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
|
+
};
|
|
1856
1404
|
}
|
|
1857
1405
|
|
|
1858
|
-
function
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
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);
|
|
1866
1416
|
}
|
|
1867
1417
|
|
|
1868
|
-
function
|
|
1418
|
+
function materializeNativeHistoryToMirror(
|
|
1419
|
+
agentType: string,
|
|
1420
|
+
canonicalHistory: ProviderCanonicalHistoryConfig,
|
|
1421
|
+
historySessionId: string,
|
|
1422
|
+
workspace?: string,
|
|
1423
|
+
scripts?: ProviderNativeHistoryScripts,
|
|
1424
|
+
): boolean {
|
|
1869
1425
|
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId);
|
|
1870
|
-
if (!normalizedSessionId
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
const payload = parsed.payload && typeof parsed.payload === 'object'
|
|
1885
|
-
? parsed.payload as Record<string, unknown>
|
|
1886
|
-
: null;
|
|
1887
|
-
if (!payload) continue;
|
|
1888
|
-
if (type === 'session_meta') {
|
|
1889
|
-
const parsedSessionId = String(payload.id || '').trim();
|
|
1890
|
-
if (parsedSessionId && parsedSessionId !== normalizedSessionId) return null;
|
|
1891
|
-
const parsedWorkspace = String(payload.cwd || workspace || '').trim();
|
|
1892
|
-
if (records.length === 0 && parsedWorkspace) {
|
|
1893
|
-
records.push({
|
|
1894
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1895
|
-
receivedAt,
|
|
1896
|
-
role: 'system',
|
|
1897
|
-
kind: 'session_start',
|
|
1898
|
-
content: parsedWorkspace,
|
|
1899
|
-
agent: 'codex-cli',
|
|
1900
|
-
historySessionId: normalizedSessionId,
|
|
1901
|
-
workspace: parsedWorkspace,
|
|
1902
|
-
});
|
|
1903
|
-
}
|
|
1904
|
-
continue;
|
|
1905
|
-
}
|
|
1906
|
-
if (type !== 'response_item') continue;
|
|
1907
|
-
const payloadType = String(payload.type || '').trim();
|
|
1908
|
-
if (payloadType === 'message') {
|
|
1909
|
-
const role = String(payload.role || '').trim();
|
|
1910
|
-
if (role !== 'user' && role !== 'assistant') continue;
|
|
1911
|
-
const content = flattenCodexContent(payload.content);
|
|
1912
|
-
if (!content) continue;
|
|
1913
|
-
records.push({
|
|
1914
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1915
|
-
receivedAt,
|
|
1916
|
-
role,
|
|
1917
|
-
content,
|
|
1918
|
-
kind: 'standard',
|
|
1919
|
-
agent: 'codex-cli',
|
|
1920
|
-
historySessionId: normalizedSessionId,
|
|
1921
|
-
});
|
|
1922
|
-
continue;
|
|
1923
|
-
}
|
|
1924
|
-
if (payloadType === 'function_call' || payloadType === 'custom_tool_call') {
|
|
1925
|
-
const content = summarizeCodexToolCall(payload);
|
|
1926
|
-
if (!content) continue;
|
|
1927
|
-
records.push({
|
|
1928
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1929
|
-
receivedAt,
|
|
1930
|
-
role: 'assistant',
|
|
1931
|
-
content,
|
|
1932
|
-
kind: 'tool',
|
|
1933
|
-
senderName: 'Tool',
|
|
1934
|
-
agent: 'codex-cli',
|
|
1935
|
-
historySessionId: normalizedSessionId,
|
|
1936
|
-
});
|
|
1937
|
-
continue;
|
|
1938
|
-
}
|
|
1939
|
-
if (payloadType === 'function_call_output' || payloadType === 'custom_tool_call_output') {
|
|
1940
|
-
const content = codexToolOutputContent(payload);
|
|
1941
|
-
if (!content) continue;
|
|
1942
|
-
records.push({
|
|
1943
|
-
ts: new Date(receivedAt).toISOString(),
|
|
1944
|
-
receivedAt,
|
|
1945
|
-
role: 'assistant',
|
|
1946
|
-
content,
|
|
1947
|
-
kind: 'tool',
|
|
1948
|
-
senderName: 'Tool',
|
|
1949
|
-
agent: 'codex-cli',
|
|
1950
|
-
historySessionId: normalizedSessionId,
|
|
1951
|
-
});
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
return records;
|
|
1955
|
-
} catch {
|
|
1956
|
-
return null;
|
|
1957
|
-
}
|
|
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);
|
|
1958
1440
|
}
|
|
1959
1441
|
|
|
1960
|
-
export function
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
return rewriteCanonicalSavedHistory('codex-cli', normalizedSessionId, recordsToWrite);
|
|
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);
|
|
1970
1451
|
}
|
|
1971
1452
|
|
|
1972
1453
|
export function isNativeSourceCanonicalHistory(canonicalHistory?: ProviderCanonicalHistoryConfig): boolean {
|
|
@@ -1976,19 +1457,6 @@ export function isNativeSourceCanonicalHistory(canonicalHistory?: ProviderCanoni
|
|
|
1976
1457
|
return true;
|
|
1977
1458
|
}
|
|
1978
1459
|
|
|
1979
|
-
function buildNativeHistoryRecords(
|
|
1980
|
-
canonicalHistory: ProviderCanonicalHistoryConfig | undefined,
|
|
1981
|
-
historySessionId: string | undefined,
|
|
1982
|
-
workspace?: string,
|
|
1983
|
-
): HistoryMessage[] | null {
|
|
1984
|
-
const normalizedSessionId = normalizeSavedHistorySessionId(historySessionId || '');
|
|
1985
|
-
if (!canonicalHistory || !normalizedSessionId || !isNativeSourceCanonicalHistory(canonicalHistory)) return null;
|
|
1986
|
-
if (canonicalHistory.format === 'hermes-json') return buildHermesNativeHistoryRecords(normalizedSessionId);
|
|
1987
|
-
if (canonicalHistory.format === 'claude-jsonl') return buildClaudeNativeHistoryRecords(normalizedSessionId, workspace);
|
|
1988
|
-
if (canonicalHistory.format === 'codex-jsonl') return buildCodexNativeHistoryRecords(normalizedSessionId, workspace);
|
|
1989
|
-
return null;
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
1460
|
export function readProviderChatHistory(
|
|
1993
1461
|
agentType: string,
|
|
1994
1462
|
options: {
|
|
@@ -1999,14 +1467,17 @@ export function readProviderChatHistory(
|
|
|
1999
1467
|
limit?: number;
|
|
2000
1468
|
excludeRecentCount?: number;
|
|
2001
1469
|
historyBehavior?: ProviderHistoryBehavior;
|
|
1470
|
+
scripts?: ProviderNativeHistoryScripts;
|
|
2002
1471
|
} = {},
|
|
2003
|
-
): { messages: HistoryMessage[]; hasMore: boolean; source: 'provider-native' | 'adhdev-mirror' | 'native-unavailable' } {
|
|
1472
|
+
): { messages: HistoryMessage[]; hasMore: boolean; source: 'provider-native' | 'adhdev-mirror' | 'native-unavailable'; sourcePath?: string; sourceMtimeMs?: number } {
|
|
2004
1473
|
if (isNativeSourceCanonicalHistory(options.canonicalHistory) && options.historySessionId) {
|
|
2005
|
-
const
|
|
2006
|
-
if (!
|
|
1474
|
+
const nativeResult = buildNativeHistoryReadResult(agentType, options.canonicalHistory, options.scripts, options.historySessionId, options.workspace);
|
|
1475
|
+
if (!nativeResult) return { messages: [], hasMore: false, source: 'native-unavailable' };
|
|
2007
1476
|
return {
|
|
2008
|
-
...pageHistoryRecords(agentType, records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
|
|
1477
|
+
...pageHistoryRecords(agentType, nativeResult.records, options.offset || 0, options.limit || 30, options.excludeRecentCount || 0, options.historyBehavior),
|
|
2009
1478
|
source: 'provider-native',
|
|
1479
|
+
sourcePath: nativeResult.sourcePath,
|
|
1480
|
+
sourceMtimeMs: nativeResult.sourceMtimeMs,
|
|
2010
1481
|
};
|
|
2011
1482
|
}
|
|
2012
1483
|
return {
|
|
@@ -2043,61 +1514,70 @@ function buildNativeSessionSummary(
|
|
|
2043
1514
|
};
|
|
2044
1515
|
}
|
|
2045
1516
|
|
|
2046
|
-
function
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
const
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
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
|
+
};
|
|
2065
1537
|
}
|
|
2066
1538
|
|
|
2067
|
-
function
|
|
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 : [];
|
|
2068
1554
|
const summaries: SavedHistorySessionSummary[] = [];
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
const fileName = path.basename(filePath);
|
|
2073
|
-
const historySessionId = fileName.replace(/^session_/, '').replace(/\.json$/, '');
|
|
2074
|
-
const records = buildHermesNativeHistoryRecords(historySessionId);
|
|
2075
|
-
const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
|
|
2076
|
-
if (summary) summaries.push(summary);
|
|
2077
|
-
}
|
|
2078
|
-
} else if (canonicalHistory.format === 'claude-jsonl') {
|
|
2079
|
-
const root = path.join(os.homedir(), '.claude', 'projects');
|
|
2080
|
-
for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith('.jsonl'))) {
|
|
2081
|
-
const historySessionId = path.basename(filePath, '.jsonl');
|
|
2082
|
-
const records = buildClaudeNativeHistoryRecords(historySessionId);
|
|
2083
|
-
const summary = records ? buildNativeSessionSummary(agentType, historySessionId, records, filePath) : null;
|
|
2084
|
-
if (summary) summaries.push(summary);
|
|
2085
|
-
}
|
|
2086
|
-
} else if (canonicalHistory.format === 'codex-jsonl') {
|
|
2087
|
-
const root = path.join(os.homedir(), '.codex', 'sessions');
|
|
2088
|
-
const uuidPattern = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
|
|
2089
|
-
for (const filePath of listFilesRecursive(root, (_entryPath, entry) => entry.isFile() && entry.name.endsWith('.jsonl'))) {
|
|
2090
|
-
const meta = readCodexSessionMeta(filePath);
|
|
2091
|
-
const historySessionId = String(meta?.id || path.basename(filePath).match(uuidPattern)?.[1] || '').trim();
|
|
1555
|
+
for (const item of sessions) {
|
|
1556
|
+
if (Array.isArray(item?.messages || item?.records)) {
|
|
1557
|
+
const historySessionId = normalizeSavedHistorySessionId(item?.historySessionId || item?.sessionId || '');
|
|
2092
1558
|
if (!historySessionId) continue;
|
|
2093
|
-
const records =
|
|
2094
|
-
const summary =
|
|
2095
|
-
if (summary)
|
|
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
|
+
}
|
|
1565
|
+
continue;
|
|
2096
1566
|
}
|
|
1567
|
+
const summary = normalizeProviderNativeHistorySessionSummary(agentType, item);
|
|
1568
|
+
if (summary) summaries.push(summary);
|
|
2097
1569
|
}
|
|
2098
1570
|
return sortSavedHistorySessionSummaries(summaries);
|
|
2099
1571
|
}
|
|
2100
1572
|
|
|
1573
|
+
function collectNativeHistorySessionSummaries(
|
|
1574
|
+
agentType: string,
|
|
1575
|
+
canonicalHistory: ProviderCanonicalHistoryConfig,
|
|
1576
|
+
scripts?: ProviderNativeHistoryScripts,
|
|
1577
|
+
): SavedHistorySessionSummary[] {
|
|
1578
|
+
return collectProviderScriptNativeHistorySessionSummaries(agentType, canonicalHistory, scripts) || [];
|
|
1579
|
+
}
|
|
1580
|
+
|
|
2101
1581
|
export function listProviderHistorySessions(
|
|
2102
1582
|
agentType: string,
|
|
2103
1583
|
options: {
|
|
@@ -2105,12 +1585,13 @@ export function listProviderHistorySessions(
|
|
|
2105
1585
|
offset?: number;
|
|
2106
1586
|
limit?: number;
|
|
2107
1587
|
historyBehavior?: ProviderHistoryBehavior;
|
|
1588
|
+
scripts?: ProviderNativeHistoryScripts;
|
|
2108
1589
|
} = {},
|
|
2109
1590
|
): { sessions: SavedHistorySessionSummary[]; hasMore: boolean; source: 'provider-native' | 'adhdev-mirror' } {
|
|
2110
1591
|
if (isNativeSourceCanonicalHistory(options.canonicalHistory)) {
|
|
2111
1592
|
const offset = Math.max(0, options.offset || 0);
|
|
2112
1593
|
const limit = Math.max(1, options.limit || 30);
|
|
2113
|
-
const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory
|
|
1594
|
+
const summaries = collectNativeHistorySessionSummaries(agentType, options.canonicalHistory!, options.scripts);
|
|
2114
1595
|
return {
|
|
2115
1596
|
sessions: summaries.slice(offset, offset + limit),
|
|
2116
1597
|
hasMore: offset + limit < summaries.length,
|