@riconext/hermes-repo 1.2.1 → 1.2.3
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/CHANGELOG.md +12 -0
- package/dist/cli.js +170 -13
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 17d33f3: 增加 flush 调用 LLM 时的详细 debug 日志,便于检查请求输入、原始响应和标准化后的知识文件结果。
|
|
8
|
+
|
|
9
|
+
## 1.2.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- a8aff7f: 记录 flush 执行过程到 debug 日志,并在写入 MEMORY.md 前校验知识文件链接是否真实存在。
|
|
14
|
+
|
|
3
15
|
## 1.2.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/cli.js
CHANGED
|
@@ -62,6 +62,16 @@ function debugLog(enabled, phase, message) {
|
|
|
62
62
|
console.error(line);
|
|
63
63
|
writeToLogFile(line);
|
|
64
64
|
}
|
|
65
|
+
function debugLogBlock(enabled, phase, label, content) {
|
|
66
|
+
if (!enabled) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
debugLog(true, phase, `${label} BEGIN`);
|
|
70
|
+
for (const line of content.split(/\r?\n/)) {
|
|
71
|
+
debugLog(true, phase, `| ${line}`);
|
|
72
|
+
}
|
|
73
|
+
debugLog(true, phase, `${label} END`);
|
|
74
|
+
}
|
|
65
75
|
function debugFromContext(ctx, phase, message) {
|
|
66
76
|
debugLog(ctx?.config.debug === true, phase, message);
|
|
67
77
|
}
|
|
@@ -1423,7 +1433,7 @@ function buildLlmConsolidateInput(repoRoot, sessions) {
|
|
|
1423
1433
|
currentMemoryMd
|
|
1424
1434
|
};
|
|
1425
1435
|
}
|
|
1426
|
-
async function callLlmConsolidate(input2, llmConfig) {
|
|
1436
|
+
async function callLlmConsolidate(input2, llmConfig, debug = false) {
|
|
1427
1437
|
if (!llmConfig.enabled) {
|
|
1428
1438
|
throw new Error(
|
|
1429
1439
|
"LLM \u672A\u542F\u7528\uFF1A\u8BF7\u5728 config.json \u4E2D\u8BBE\u7F6E llm.enabled = true"
|
|
@@ -1436,6 +1446,23 @@ async function callLlmConsolidate(input2, llmConfig) {
|
|
|
1436
1446
|
}
|
|
1437
1447
|
const url = `${llmConfig.baseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
1438
1448
|
const userContent = formatUserMessage(input2);
|
|
1449
|
+
debugLog(
|
|
1450
|
+
debug,
|
|
1451
|
+
"llm",
|
|
1452
|
+
`request: provider=${llmConfig.provider}, model=${llmConfig.model}, baseUrl=${llmConfig.baseUrl}, pendingSessions=${input2.pendingSessions.length}, existingKnowledge=${input2.existingKnowledge.length}, currentMemoryChars=${input2.currentMemoryMd?.length ?? 0}`
|
|
1453
|
+
);
|
|
1454
|
+
debugLogBlock(debug, "llm", "system prompt", CONSOLIDATE_SYSTEM_PROMPT);
|
|
1455
|
+
debugLogBlock(debug, "llm", "user input", userContent);
|
|
1456
|
+
const requestBody = {
|
|
1457
|
+
model: llmConfig.model,
|
|
1458
|
+
response_format: { type: "json_object" },
|
|
1459
|
+
messages: [
|
|
1460
|
+
{ role: "system", content: CONSOLIDATE_SYSTEM_PROMPT },
|
|
1461
|
+
{ role: "user", content: userContent }
|
|
1462
|
+
],
|
|
1463
|
+
temperature: 0.2
|
|
1464
|
+
};
|
|
1465
|
+
debugLogBlock(debug, "llm", "request body", JSON.stringify(requestBody, null, 2));
|
|
1439
1466
|
const controller = new AbortController();
|
|
1440
1467
|
const timeout = setTimeout(() => controller.abort(), 12e4);
|
|
1441
1468
|
try {
|
|
@@ -1445,35 +1472,58 @@ async function callLlmConsolidate(input2, llmConfig) {
|
|
|
1445
1472
|
"Content-Type": "application/json",
|
|
1446
1473
|
Authorization: `Bearer ${llmConfig.apiKey}`
|
|
1447
1474
|
},
|
|
1448
|
-
body: JSON.stringify(
|
|
1449
|
-
model: llmConfig.model,
|
|
1450
|
-
response_format: { type: "json_object" },
|
|
1451
|
-
messages: [
|
|
1452
|
-
{ role: "system", content: CONSOLIDATE_SYSTEM_PROMPT },
|
|
1453
|
-
{ role: "user", content: userContent }
|
|
1454
|
-
],
|
|
1455
|
-
temperature: 0.2
|
|
1456
|
-
}),
|
|
1475
|
+
body: JSON.stringify(requestBody),
|
|
1457
1476
|
signal: controller.signal
|
|
1458
1477
|
});
|
|
1478
|
+
debugLog(debug, "llm", `response status: ${res.status} ${res.statusText}`);
|
|
1459
1479
|
if (!res.ok) {
|
|
1460
1480
|
const errBody = await res.text().catch(() => "");
|
|
1481
|
+
debugLogBlock(debug, "llm", "error response body", errBody);
|
|
1461
1482
|
throw new Error(
|
|
1462
1483
|
`LLM API \u9519\u8BEF (${res.status}): ${errBody.slice(0, 300)}`
|
|
1463
1484
|
);
|
|
1464
1485
|
}
|
|
1465
1486
|
const data = await res.json();
|
|
1487
|
+
debugLogBlock(debug, "llm", "response json", JSON.stringify(data, null, 2));
|
|
1466
1488
|
const rawContent = data.choices?.[0]?.message?.content;
|
|
1467
1489
|
if (!rawContent) {
|
|
1468
1490
|
throw new Error("LLM \u8FD4\u56DE\u5185\u5BB9\u4E3A\u7A7A");
|
|
1469
1491
|
}
|
|
1492
|
+
debugLogBlock(debug, "llm", "raw message content", rawContent);
|
|
1470
1493
|
let parsed;
|
|
1471
1494
|
try {
|
|
1472
1495
|
parsed = JSON.parse(rawContent);
|
|
1473
1496
|
} catch {
|
|
1474
1497
|
throw new Error("LLM \u8FD4\u56DE\u5185\u5BB9\u4E0D\u662F\u5408\u6CD5 JSON");
|
|
1475
1498
|
}
|
|
1476
|
-
|
|
1499
|
+
debugLogBlock(debug, "llm", "parsed content", JSON.stringify(parsed, null, 2));
|
|
1500
|
+
const normalized = validateAndNormalizeLlmResult(parsed);
|
|
1501
|
+
debugLog(
|
|
1502
|
+
debug,
|
|
1503
|
+
"llm",
|
|
1504
|
+
`normalized: knowledgeFiles=${normalized.knowledgeFiles.length}, memoryChars=${normalized.memoryMd.length}, skippedSessions=${normalized.skippedSessions.length}`
|
|
1505
|
+
);
|
|
1506
|
+
debugLogBlock(
|
|
1507
|
+
debug,
|
|
1508
|
+
"llm",
|
|
1509
|
+
"normalized knowledgeFiles",
|
|
1510
|
+
JSON.stringify(normalized.knowledgeFiles, null, 2)
|
|
1511
|
+
);
|
|
1512
|
+
debugLogBlock(debug, "llm", "normalized memoryMd", normalized.memoryMd);
|
|
1513
|
+
debugLogBlock(
|
|
1514
|
+
debug,
|
|
1515
|
+
"llm",
|
|
1516
|
+
"normalized skippedSessions",
|
|
1517
|
+
JSON.stringify(normalized.skippedSessions, null, 2)
|
|
1518
|
+
);
|
|
1519
|
+
return normalized;
|
|
1520
|
+
} catch (err) {
|
|
1521
|
+
debugLog(
|
|
1522
|
+
debug,
|
|
1523
|
+
"llm",
|
|
1524
|
+
`error: ${err instanceof Error ? err.message : String(err)}`
|
|
1525
|
+
);
|
|
1526
|
+
throw err;
|
|
1477
1527
|
} finally {
|
|
1478
1528
|
clearTimeout(timeout);
|
|
1479
1529
|
}
|
|
@@ -1537,6 +1587,13 @@ function isSkippedEntry(raw) {
|
|
|
1537
1587
|
// src/consolidate/writeKnowledge.ts
|
|
1538
1588
|
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
1539
1589
|
import { dirname as dirname4 } from "path";
|
|
1590
|
+
var KNOWLEDGE_LINK_PREFIXES = [
|
|
1591
|
+
"rules/",
|
|
1592
|
+
"domains/",
|
|
1593
|
+
"workflows/",
|
|
1594
|
+
"decisions/",
|
|
1595
|
+
"incidents/"
|
|
1596
|
+
];
|
|
1540
1597
|
function writeKnowledgeFiles(repoRoot, files) {
|
|
1541
1598
|
const result = {
|
|
1542
1599
|
created: [],
|
|
@@ -1580,6 +1637,38 @@ function writeMemoryMd(repoRoot, memoryMd) {
|
|
|
1580
1637
|
writeFileSync5(memoryPathAbs, `${memoryMd}
|
|
1581
1638
|
`, "utf8");
|
|
1582
1639
|
}
|
|
1640
|
+
function assertMemoryKnowledgeLinksExist(repoRoot, memoryMd) {
|
|
1641
|
+
const missing = findMissingKnowledgeLinks(repoRoot, memoryMd);
|
|
1642
|
+
if (missing.length > 0) {
|
|
1643
|
+
throw new Error(
|
|
1644
|
+
`MEMORY.md \u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684\u77E5\u8BC6\u6587\u4EF6: ${missing.join(", ")}`
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
function findMissingKnowledgeLinks(repoRoot, memoryMd) {
|
|
1649
|
+
const links = extractKnowledgeLinks(memoryMd);
|
|
1650
|
+
return links.filter((link) => !existsSync7(memoryPath(repoRoot, link)));
|
|
1651
|
+
}
|
|
1652
|
+
function extractKnowledgeLinks(memoryMd) {
|
|
1653
|
+
const links = /* @__PURE__ */ new Set();
|
|
1654
|
+
const markdownLink = /\[[^\]]+\]\(([^)]+)\)/g;
|
|
1655
|
+
let match;
|
|
1656
|
+
while ((match = markdownLink.exec(memoryMd)) !== null) {
|
|
1657
|
+
const normalized = normalizeKnowledgeLink(match[1]);
|
|
1658
|
+
if (normalized) {
|
|
1659
|
+
links.add(normalized);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
return [...links];
|
|
1663
|
+
}
|
|
1664
|
+
function normalizeKnowledgeLink(rawLink) {
|
|
1665
|
+
const withoutAnchor = rawLink.split("#")[0].trim();
|
|
1666
|
+
const withoutDotSlash = withoutAnchor.startsWith("./") ? withoutAnchor.slice(2) : withoutAnchor;
|
|
1667
|
+
if (withoutDotSlash.includes("://") || withoutDotSlash.startsWith("/") || withoutDotSlash.includes("..") || !withoutDotSlash.endsWith(".md")) {
|
|
1668
|
+
return null;
|
|
1669
|
+
}
|
|
1670
|
+
return KNOWLEDGE_LINK_PREFIXES.some((prefix) => withoutDotSlash.startsWith(prefix)) ? withoutDotSlash : null;
|
|
1671
|
+
}
|
|
1583
1672
|
function serializeKnowledgeFile(frontmatter, body) {
|
|
1584
1673
|
const lines = ["---"];
|
|
1585
1674
|
const fieldOrder = ["title", "domain", "type", "status", "confidence", "lastReviewed"];
|
|
@@ -1666,10 +1755,17 @@ function archiveDoneSessions(repoRoot, sessions, autoArchiveDays = 30) {
|
|
|
1666
1755
|
// src/consolidate/runConsolidate.ts
|
|
1667
1756
|
async function runConsolidate(opts) {
|
|
1668
1757
|
const { repoRoot, config, force, dryRun, debug } = opts;
|
|
1758
|
+
debugLog(
|
|
1759
|
+
debug === true,
|
|
1760
|
+
"consolidate",
|
|
1761
|
+
`start: force=${force === true}, dryRun=${dryRun === true}`
|
|
1762
|
+
);
|
|
1669
1763
|
writeConsolidateLock(repoRoot);
|
|
1764
|
+
debugLog(debug === true, "consolidate", "lock acquired");
|
|
1670
1765
|
try {
|
|
1671
1766
|
const llmConfig = config.llm;
|
|
1672
1767
|
if (!llmConfig.enabled) {
|
|
1768
|
+
debugLog(debug === true, "consolidate", "skip: llm not enabled");
|
|
1673
1769
|
return {
|
|
1674
1770
|
ran: false,
|
|
1675
1771
|
reason: "llm-not-enabled",
|
|
@@ -1688,6 +1784,7 @@ async function runConsolidate(opts) {
|
|
|
1688
1784
|
`\u626B\u63CF\u5230 ${allSessions.length} \u4E2A session\uFF0C\u5176\u4E2D ${pendingSessions.length} \u4E2A\u5F85\u5904\u7406`
|
|
1689
1785
|
);
|
|
1690
1786
|
if (pendingSessions.length === 0 && !force) {
|
|
1787
|
+
debugLog(debug === true, "consolidate", "skip: no pending sessions");
|
|
1691
1788
|
return {
|
|
1692
1789
|
ran: false,
|
|
1693
1790
|
reason: "no-pending-sessions",
|
|
@@ -1699,6 +1796,11 @@ async function runConsolidate(opts) {
|
|
|
1699
1796
|
};
|
|
1700
1797
|
}
|
|
1701
1798
|
if (dryRun) {
|
|
1799
|
+
debugLog(
|
|
1800
|
+
debug === true,
|
|
1801
|
+
"consolidate",
|
|
1802
|
+
`dry-run: would process ${pendingSessions.length} session(s)`
|
|
1803
|
+
);
|
|
1702
1804
|
return {
|
|
1703
1805
|
ran: true,
|
|
1704
1806
|
reason: "dry-run",
|
|
@@ -1710,10 +1812,14 @@ async function runConsolidate(opts) {
|
|
|
1710
1812
|
};
|
|
1711
1813
|
}
|
|
1712
1814
|
const llmInput = buildLlmConsolidateInput(repoRoot, pendingSessions);
|
|
1713
|
-
debugLog(
|
|
1815
|
+
debugLog(
|
|
1816
|
+
debug === true,
|
|
1817
|
+
"consolidate",
|
|
1818
|
+
`LLM \u8F93\u5165: ${llmInput.pendingSessions.length} sessions, ${llmInput.existingKnowledge.length} existing knowledge`
|
|
1819
|
+
);
|
|
1714
1820
|
let llmResult;
|
|
1715
1821
|
try {
|
|
1716
|
-
llmResult = await callLlmConsolidate(llmInput, llmConfig);
|
|
1822
|
+
llmResult = await callLlmConsolidate(llmInput, llmConfig, debug === true);
|
|
1717
1823
|
} catch (err) {
|
|
1718
1824
|
console.error(`[consolidate] LLM \u8C03\u7528\u5931\u8D25: ${err.message}`);
|
|
1719
1825
|
throw err;
|
|
@@ -1723,8 +1829,33 @@ async function runConsolidate(opts) {
|
|
|
1723
1829
|
"consolidate",
|
|
1724
1830
|
`LLM \u8FD4\u56DE: ${llmResult.knowledgeFiles.length} knowledge files, ${llmResult.skippedSessions.length} skipped`
|
|
1725
1831
|
);
|
|
1832
|
+
debugLog(debug === true, "consolidate", "writing knowledge files");
|
|
1726
1833
|
const writeResult = writeKnowledgeFiles(repoRoot, llmResult.knowledgeFiles);
|
|
1834
|
+
if (writeResult.failed.length > 0) {
|
|
1835
|
+
debugLog(
|
|
1836
|
+
debug === true,
|
|
1837
|
+
"consolidate",
|
|
1838
|
+
`write failed: ${writeResult.failed.join(", ")}`
|
|
1839
|
+
);
|
|
1840
|
+
throw new Error(
|
|
1841
|
+
`\u77E5\u8BC6\u6587\u4EF6\u5199\u5165\u5931\u8D25: ${writeResult.failed.join(", ")}`
|
|
1842
|
+
);
|
|
1843
|
+
}
|
|
1844
|
+
debugLog(
|
|
1845
|
+
debug === true,
|
|
1846
|
+
"consolidate",
|
|
1847
|
+
`knowledge files written: created=${writeResult.created.length}, updated=${writeResult.updated.length}`
|
|
1848
|
+
);
|
|
1849
|
+
debugLog(debug === true, "consolidate", "validating MEMORY.md links");
|
|
1850
|
+
assertMemoryKnowledgeLinksExist(repoRoot, llmResult.memoryMd);
|
|
1851
|
+
debugLog(debug === true, "consolidate", "MEMORY.md links ok");
|
|
1852
|
+
debugLog(debug === true, "consolidate", "writing MEMORY.md");
|
|
1727
1853
|
writeMemoryMd(repoRoot, llmResult.memoryMd);
|
|
1854
|
+
debugLog(
|
|
1855
|
+
debug === true,
|
|
1856
|
+
"consolidate",
|
|
1857
|
+
`marking sessions done: ${pendingSessions.length} processed, ${llmResult.skippedSessions.length} skipped`
|
|
1858
|
+
);
|
|
1728
1859
|
const processedSessionIds = /* @__PURE__ */ new Set();
|
|
1729
1860
|
for (const s of pendingSessions) {
|
|
1730
1861
|
markSessionConsolidated(repoRoot, s.sessionId);
|
|
@@ -1736,6 +1867,7 @@ async function runConsolidate(opts) {
|
|
|
1736
1867
|
}
|
|
1737
1868
|
const prevState = readConsolidateState(repoRoot);
|
|
1738
1869
|
const newDomains = extractDomainsFromResults(llmResult.knowledgeFiles);
|
|
1870
|
+
debugLog(debug === true, "consolidate", "updating consolidate state");
|
|
1739
1871
|
writeConsolidateState(repoRoot, {
|
|
1740
1872
|
version: 2,
|
|
1741
1873
|
lastConsolidatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1759,6 +1891,12 @@ async function runConsolidate(opts) {
|
|
|
1759
1891
|
allSessions,
|
|
1760
1892
|
config.consolidate.autoArchiveDays
|
|
1761
1893
|
);
|
|
1894
|
+
debugLog(debug === true, "consolidate", `archived sessions: ${archived}`);
|
|
1895
|
+
debugLog(
|
|
1896
|
+
debug === true,
|
|
1897
|
+
"consolidate",
|
|
1898
|
+
`done: sessions=${pendingSessions.length}, created=${writeResult.created.length}, updated=${writeResult.updated.length}, skipped=${llmResult.skippedSessions.length}, archived=${archived}`
|
|
1899
|
+
);
|
|
1762
1900
|
return {
|
|
1763
1901
|
ran: true,
|
|
1764
1902
|
sessionsProcessed: pendingSessions.length,
|
|
@@ -1769,6 +1907,7 @@ async function runConsolidate(opts) {
|
|
|
1769
1907
|
};
|
|
1770
1908
|
} finally {
|
|
1771
1909
|
releaseConsolidateLock(repoRoot);
|
|
1910
|
+
debugLog(debug === true, "consolidate", "lock released");
|
|
1772
1911
|
}
|
|
1773
1912
|
}
|
|
1774
1913
|
function extractDomainsFromResults(files) {
|
|
@@ -2421,12 +2560,25 @@ function runCaptureCommand(opts) {
|
|
|
2421
2560
|
|
|
2422
2561
|
// src/commands/flush.ts
|
|
2423
2562
|
async function runFlushCommandCli(opts) {
|
|
2563
|
+
const ctx = loadRepoContext(opts.cwd);
|
|
2564
|
+
const debug = ctx?.config.debug === true;
|
|
2565
|
+
configureDebugLogging(ctx?.repoRoot ?? null, debug);
|
|
2566
|
+
debugLog(
|
|
2567
|
+
debug,
|
|
2568
|
+
"flush",
|
|
2569
|
+
`start: force=${opts.force === true}, dryRun=${opts.dryRun === true}`
|
|
2570
|
+
);
|
|
2424
2571
|
try {
|
|
2425
2572
|
const result = await runFlushCommand({
|
|
2426
2573
|
cwd: opts.cwd,
|
|
2427
2574
|
force: opts.force,
|
|
2428
2575
|
dryRun: opts.dryRun
|
|
2429
2576
|
});
|
|
2577
|
+
debugLog(
|
|
2578
|
+
debug,
|
|
2579
|
+
"flush",
|
|
2580
|
+
`result: ran=${result.ran}, reason=${result.reason ?? "ok"}, sessions=${result.sessionsProcessed}, created=${result.knowledgeCreated}, updated=${result.knowledgeUpdated}, skipped=${result.skippedCount}, archived=${result.archived}`
|
|
2581
|
+
);
|
|
2430
2582
|
if (result.ran) {
|
|
2431
2583
|
if (result.reason === "dry-run") {
|
|
2432
2584
|
console.error(
|
|
@@ -2466,6 +2618,11 @@ async function runFlushCommandCli(opts) {
|
|
|
2466
2618
|
}
|
|
2467
2619
|
hookExit(0, opts.strict);
|
|
2468
2620
|
} catch (err) {
|
|
2621
|
+
debugLog(
|
|
2622
|
+
debug,
|
|
2623
|
+
"flush",
|
|
2624
|
+
`error: ${err instanceof Error ? err.message : String(err)}`
|
|
2625
|
+
);
|
|
2469
2626
|
console.error(
|
|
2470
2627
|
`hermes-repo flush: ${err instanceof Error ? err.message : String(err)}`
|
|
2471
2628
|
);
|