@adhdev/daemon-core 0.8.98 → 0.8.99
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 +1 -0
- package/dist/index.js +240 -40
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +240 -40
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/config/chat-history.ts +207 -14
- package/src/providers/cli-provider-instance.ts +46 -26
- package/src/providers/provider-session-id.ts +4 -0
package/dist/index.mjs
CHANGED
|
@@ -4431,6 +4431,7 @@ import { join as join3 } from "path";
|
|
|
4431
4431
|
|
|
4432
4432
|
// src/providers/provider-session-id.ts
|
|
4433
4433
|
var HERMES_SESSION_ID_RE = /^\d{8}_\d{6}_[a-z0-9]+$/i;
|
|
4434
|
+
var CLAUDE_SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4434
4435
|
function normalizeProviderSessionId(providerType, providerSessionId) {
|
|
4435
4436
|
const normalizedProviderType = typeof providerType === "string" ? providerType.trim() : "";
|
|
4436
4437
|
const normalizedId = typeof providerSessionId === "string" ? providerSessionId.trim() : "";
|
|
@@ -4440,6 +4441,9 @@ function normalizeProviderSessionId(providerType, providerSessionId) {
|
|
|
4440
4441
|
if (normalizedProviderType === "hermes-cli" && !HERMES_SESSION_ID_RE.test(normalizedId)) {
|
|
4441
4442
|
return "";
|
|
4442
4443
|
}
|
|
4444
|
+
if (normalizedProviderType === "claude-cli" && !CLAUDE_SESSION_ID_RE.test(normalizedId)) {
|
|
4445
|
+
return "";
|
|
4446
|
+
}
|
|
4443
4447
|
return normalizedId;
|
|
4444
4448
|
}
|
|
4445
4449
|
function isLegacyVolatileSessionReadKey(key) {
|
|
@@ -7561,9 +7565,16 @@ function extractCanonicalHermesMessageTimestamp(message, fallbackTs) {
|
|
|
7561
7565
|
if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
|
|
7562
7566
|
return fallbackTs;
|
|
7563
7567
|
}
|
|
7564
|
-
function
|
|
7568
|
+
function extractTimestampValue(value) {
|
|
7569
|
+
const numericTimestamp = Number(value || 0);
|
|
7570
|
+
if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
|
|
7571
|
+
const stringTimestamp = typeof value === "string" ? Date.parse(value) : NaN;
|
|
7572
|
+
if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
|
|
7573
|
+
return 0;
|
|
7574
|
+
}
|
|
7575
|
+
function readExistingSessionStartRecord(agentType, historySessionId) {
|
|
7565
7576
|
try {
|
|
7566
|
-
const dir = path7.join(HISTORY_DIR,
|
|
7577
|
+
const dir = path7.join(HISTORY_DIR, agentType);
|
|
7567
7578
|
if (!fs3.existsSync(dir)) return null;
|
|
7568
7579
|
const files = listHistoryFiles(dir, historySessionId).sort();
|
|
7569
7580
|
for (const file of files) {
|
|
@@ -7584,6 +7595,28 @@ function readExistingHermesSessionStartRecord(historySessionId) {
|
|
|
7584
7595
|
return null;
|
|
7585
7596
|
}
|
|
7586
7597
|
}
|
|
7598
|
+
function rewriteCanonicalSavedHistory(agentType, historySessionId, records) {
|
|
7599
|
+
if (records.length === 0) return false;
|
|
7600
|
+
try {
|
|
7601
|
+
const dir = path7.join(HISTORY_DIR, agentType);
|
|
7602
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
7603
|
+
const prefix = `${historySessionId.replace(/[^a-zA-Z0-9_-]/g, "_")}_`;
|
|
7604
|
+
for (const file of fs3.readdirSync(dir)) {
|
|
7605
|
+
if (file.startsWith(prefix) && file.endsWith(".jsonl")) {
|
|
7606
|
+
fs3.unlinkSync(path7.join(dir, file));
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
const targetDate = new Date(records[records.length - 1].receivedAt || Date.now()).toISOString().slice(0, 10);
|
|
7610
|
+
const filePath = path7.join(dir, `${prefix}${targetDate}.jsonl`);
|
|
7611
|
+
fs3.writeFileSync(filePath, `${records.map((record) => JSON.stringify(record)).join("\n")}
|
|
7612
|
+
`, "utf-8");
|
|
7613
|
+
invalidatePersistedSavedHistoryIndex(agentType, dir);
|
|
7614
|
+
savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
|
|
7615
|
+
return true;
|
|
7616
|
+
} catch {
|
|
7617
|
+
return false;
|
|
7618
|
+
}
|
|
7619
|
+
}
|
|
7587
7620
|
function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
|
|
7588
7621
|
const normalizedSessionId = normalizeSavedHistorySessionId("hermes-cli", historySessionId);
|
|
7589
7622
|
if (!normalizedSessionId) return false;
|
|
@@ -7594,7 +7627,7 @@ function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
|
|
|
7594
7627
|
const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
|
|
7595
7628
|
const dir = path7.join(HISTORY_DIR, "hermes-cli");
|
|
7596
7629
|
fs3.mkdirSync(dir, { recursive: true });
|
|
7597
|
-
const existingSessionStart =
|
|
7630
|
+
const existingSessionStart = readExistingSessionStartRecord("hermes-cli", normalizedSessionId);
|
|
7598
7631
|
const records = [];
|
|
7599
7632
|
if (existingSessionStart) {
|
|
7600
7633
|
records.push({
|
|
@@ -7646,20 +7679,167 @@ function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
|
|
|
7646
7679
|
});
|
|
7647
7680
|
}
|
|
7648
7681
|
}
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7682
|
+
return rewriteCanonicalSavedHistory("hermes-cli", normalizedSessionId, records);
|
|
7683
|
+
} catch {
|
|
7684
|
+
return false;
|
|
7685
|
+
}
|
|
7686
|
+
}
|
|
7687
|
+
function resolveClaudeProjectTranscriptPath(historySessionId, workspace) {
|
|
7688
|
+
const claudeProjectsDir = path7.join(os5.homedir(), ".claude", "projects");
|
|
7689
|
+
if (!fs3.existsSync(claudeProjectsDir)) return null;
|
|
7690
|
+
const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
|
|
7691
|
+
if (normalizedWorkspace) {
|
|
7692
|
+
const directPath = path7.join(claudeProjectsDir, normalizedWorkspace.replace(/[\\/]/g, "-"), `${historySessionId}.jsonl`);
|
|
7693
|
+
if (fs3.existsSync(directPath)) return directPath;
|
|
7694
|
+
}
|
|
7695
|
+
const stack = [claudeProjectsDir];
|
|
7696
|
+
while (stack.length > 0) {
|
|
7697
|
+
const current = stack.pop();
|
|
7698
|
+
if (!current) continue;
|
|
7699
|
+
for (const entry of fs3.readdirSync(current, { withFileTypes: true })) {
|
|
7700
|
+
const entryPath = path7.join(current, entry.name);
|
|
7701
|
+
if (entry.isDirectory()) {
|
|
7702
|
+
stack.push(entryPath);
|
|
7703
|
+
continue;
|
|
7704
|
+
}
|
|
7705
|
+
if (entry.isFile() && entry.name === `${historySessionId}.jsonl`) {
|
|
7706
|
+
return entryPath;
|
|
7654
7707
|
}
|
|
7655
7708
|
}
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
return
|
|
7709
|
+
}
|
|
7710
|
+
return null;
|
|
7711
|
+
}
|
|
7712
|
+
function extractClaudeAssistantContentParts(content) {
|
|
7713
|
+
if (typeof content === "string") {
|
|
7714
|
+
const trimmed = content.trim();
|
|
7715
|
+
return trimmed ? [{ content: trimmed, kind: "standard", role: "assistant" }] : [];
|
|
7716
|
+
}
|
|
7717
|
+
if (!Array.isArray(content)) return [];
|
|
7718
|
+
const parts = [];
|
|
7719
|
+
for (const block of content) {
|
|
7720
|
+
if (!block || typeof block !== "object") continue;
|
|
7721
|
+
const record = block;
|
|
7722
|
+
const type = String(record.type || "").trim();
|
|
7723
|
+
if (type === "text") {
|
|
7724
|
+
const text = String(record.text || "").trim();
|
|
7725
|
+
if (text) parts.push({ content: text, kind: "standard", role: "assistant" });
|
|
7726
|
+
continue;
|
|
7727
|
+
}
|
|
7728
|
+
if (type === "tool_use") {
|
|
7729
|
+
const name = String(record.name || "").trim() || "Tool";
|
|
7730
|
+
const input = record.input && typeof record.input === "object" ? record.input : null;
|
|
7731
|
+
const command = input ? String(input.command || "").trim() : "";
|
|
7732
|
+
const summary = command ? `${name}: ${command}` : name;
|
|
7733
|
+
if (summary) parts.push({ content: summary, kind: "tool", senderName: "Tool", role: "assistant" });
|
|
7734
|
+
}
|
|
7735
|
+
}
|
|
7736
|
+
return parts;
|
|
7737
|
+
}
|
|
7738
|
+
function extractClaudeUserContentParts(content) {
|
|
7739
|
+
if (typeof content === "string") {
|
|
7740
|
+
const trimmed = content.trim();
|
|
7741
|
+
return trimmed ? [{ role: "user", content: trimmed, kind: "standard" }] : [];
|
|
7742
|
+
}
|
|
7743
|
+
if (!Array.isArray(content)) return [];
|
|
7744
|
+
const parts = [];
|
|
7745
|
+
for (const block of content) {
|
|
7746
|
+
if (!block || typeof block !== "object") continue;
|
|
7747
|
+
const record = block;
|
|
7748
|
+
const type = String(record.type || "").trim();
|
|
7749
|
+
if (type === "text") {
|
|
7750
|
+
const text = String(record.text || "").trim();
|
|
7751
|
+
if (text) parts.push({ role: "user", content: text, kind: "standard" });
|
|
7752
|
+
continue;
|
|
7753
|
+
}
|
|
7754
|
+
if (type === "tool_result") {
|
|
7755
|
+
const rawContent = record.content;
|
|
7756
|
+
const text = typeof rawContent === "string" ? rawContent.trim() : Array.isArray(rawContent) ? rawContent.map((entry) => {
|
|
7757
|
+
if (typeof entry === "string") return entry.trim();
|
|
7758
|
+
if (!entry || typeof entry !== "object") return "";
|
|
7759
|
+
const nested = entry;
|
|
7760
|
+
if (typeof nested.text === "string") return nested.text.trim();
|
|
7761
|
+
if (typeof nested.content === "string") return nested.content.trim();
|
|
7762
|
+
return "";
|
|
7763
|
+
}).filter(Boolean).join("\n") : "";
|
|
7764
|
+
if (text) parts.push({ role: "assistant", content: text, kind: "tool", senderName: "Tool" });
|
|
7765
|
+
}
|
|
7766
|
+
}
|
|
7767
|
+
return parts;
|
|
7768
|
+
}
|
|
7769
|
+
function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace) {
|
|
7770
|
+
const normalizedSessionId = normalizeSavedHistorySessionId("claude-cli", historySessionId);
|
|
7771
|
+
if (!normalizedSessionId) return false;
|
|
7772
|
+
try {
|
|
7773
|
+
const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
|
|
7774
|
+
if (!transcriptPath) return false;
|
|
7775
|
+
const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
|
|
7776
|
+
const records = [];
|
|
7777
|
+
const existingSessionStart = readExistingSessionStartRecord("claude-cli", normalizedSessionId);
|
|
7778
|
+
if (existingSessionStart) {
|
|
7779
|
+
records.push({
|
|
7780
|
+
...existingSessionStart,
|
|
7781
|
+
historySessionId: normalizedSessionId
|
|
7782
|
+
});
|
|
7783
|
+
}
|
|
7784
|
+
let fallbackTs = Date.now();
|
|
7785
|
+
for (const line of lines) {
|
|
7786
|
+
let parsed = null;
|
|
7787
|
+
try {
|
|
7788
|
+
parsed = JSON.parse(line);
|
|
7789
|
+
} catch {
|
|
7790
|
+
parsed = null;
|
|
7791
|
+
}
|
|
7792
|
+
if (!parsed) continue;
|
|
7793
|
+
const parsedSessionId = String(parsed.sessionId || "").trim();
|
|
7794
|
+
if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
|
|
7795
|
+
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
7796
|
+
fallbackTs = receivedAt + 1;
|
|
7797
|
+
const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
|
|
7798
|
+
if (records.length === 0 && parsedWorkspace) {
|
|
7799
|
+
records.push({
|
|
7800
|
+
ts: new Date(receivedAt).toISOString(),
|
|
7801
|
+
receivedAt,
|
|
7802
|
+
role: "system",
|
|
7803
|
+
kind: "session_start",
|
|
7804
|
+
content: parsedWorkspace,
|
|
7805
|
+
agent: "claude-cli",
|
|
7806
|
+
historySessionId: normalizedSessionId,
|
|
7807
|
+
workspace: parsedWorkspace
|
|
7808
|
+
});
|
|
7809
|
+
}
|
|
7810
|
+
const type = String(parsed.type || "").trim();
|
|
7811
|
+
const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
|
|
7812
|
+
if (type === "user" && message) {
|
|
7813
|
+
for (const part of extractClaudeUserContentParts(message.content)) {
|
|
7814
|
+
records.push({
|
|
7815
|
+
ts: new Date(receivedAt).toISOString(),
|
|
7816
|
+
receivedAt,
|
|
7817
|
+
role: part.role,
|
|
7818
|
+
content: part.content,
|
|
7819
|
+
kind: part.kind,
|
|
7820
|
+
senderName: part.senderName,
|
|
7821
|
+
agent: "claude-cli",
|
|
7822
|
+
historySessionId: normalizedSessionId
|
|
7823
|
+
});
|
|
7824
|
+
}
|
|
7825
|
+
continue;
|
|
7826
|
+
}
|
|
7827
|
+
if (type === "assistant" && message) {
|
|
7828
|
+
for (const part of extractClaudeAssistantContentParts(message.content)) {
|
|
7829
|
+
records.push({
|
|
7830
|
+
ts: new Date(receivedAt).toISOString(),
|
|
7831
|
+
receivedAt,
|
|
7832
|
+
role: "assistant",
|
|
7833
|
+
content: part.content,
|
|
7834
|
+
kind: part.kind,
|
|
7835
|
+
senderName: part.senderName,
|
|
7836
|
+
agent: "claude-cli",
|
|
7837
|
+
historySessionId: normalizedSessionId
|
|
7838
|
+
});
|
|
7839
|
+
}
|
|
7840
|
+
}
|
|
7841
|
+
}
|
|
7842
|
+
return rewriteCanonicalSavedHistory("claude-cli", normalizedSessionId, records);
|
|
7663
7843
|
} catch {
|
|
7664
7844
|
return false;
|
|
7665
7845
|
}
|
|
@@ -12718,7 +12898,7 @@ var CliProviderInstance = class {
|
|
|
12718
12898
|
parsedMessages = historyMessageCount > 0 ? parsedMessages.slice(-historyMessageCount) : [];
|
|
12719
12899
|
}
|
|
12720
12900
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
12721
|
-
const
|
|
12901
|
+
const canonicalBackedHistory = this.syncCanonicalSavedHistoryIfNeeded();
|
|
12722
12902
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
12723
12903
|
if (parsedMessages.length > 0) {
|
|
12724
12904
|
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
@@ -12736,7 +12916,7 @@ var CliProviderInstance = class {
|
|
|
12736
12916
|
senderName: typeof message.senderName === "string" ? message.senderName : void 0,
|
|
12737
12917
|
receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
|
|
12738
12918
|
}));
|
|
12739
|
-
if (!
|
|
12919
|
+
if (!canonicalBackedHistory && !shouldSkipReplayPersist && normalizedMessagesToSave.length > 0) {
|
|
12740
12920
|
const incrementalMessages = buildIncrementalHistoryAppendMessages(this.lastPersistedHistoryMessages, normalizedMessagesToSave);
|
|
12741
12921
|
this.historyWriter.appendNewMessages(
|
|
12742
12922
|
this.type,
|
|
@@ -12746,7 +12926,7 @@ var CliProviderInstance = class {
|
|
|
12746
12926
|
this.providerSessionId
|
|
12747
12927
|
);
|
|
12748
12928
|
}
|
|
12749
|
-
if (!
|
|
12929
|
+
if (!canonicalBackedHistory) {
|
|
12750
12930
|
this.lastPersistedHistoryMessages = normalizedMessagesToSave;
|
|
12751
12931
|
}
|
|
12752
12932
|
}
|
|
@@ -13223,32 +13403,52 @@ ${effect.notification.body || ""}`.trim();
|
|
|
13223
13403
|
});
|
|
13224
13404
|
LOG.info("CLI", `[${this.type}] discovered provider session id: ${nextSessionId}`);
|
|
13225
13405
|
}
|
|
13226
|
-
|
|
13227
|
-
if (
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13240
|
-
|
|
13241
|
-
|
|
13242
|
-
|
|
13243
|
-
|
|
13244
|
-
|
|
13245
|
-
|
|
13246
|
-
|
|
13406
|
+
syncCanonicalSavedHistoryIfNeeded() {
|
|
13407
|
+
if (!this.providerSessionId) return false;
|
|
13408
|
+
if (this.type === "hermes-cli") {
|
|
13409
|
+
try {
|
|
13410
|
+
const canonicalPath = path11.join(os11.homedir(), ".hermes", "sessions", `session_${this.providerSessionId}.json`);
|
|
13411
|
+
if (!fs5.existsSync(canonicalPath)) return false;
|
|
13412
|
+
const stat = fs5.statSync(canonicalPath);
|
|
13413
|
+
if (stat.mtimeMs <= this.lastCanonicalHermesSyncMtimeMs) return true;
|
|
13414
|
+
const rebuilt = rebuildHermesSavedHistoryFromCanonicalSession(this.providerSessionId);
|
|
13415
|
+
if (!rebuilt) return false;
|
|
13416
|
+
this.lastCanonicalHermesSyncMtimeMs = stat.mtimeMs;
|
|
13417
|
+
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId);
|
|
13418
|
+
this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
|
|
13419
|
+
role: message.role,
|
|
13420
|
+
content: message.content,
|
|
13421
|
+
kind: message.kind,
|
|
13422
|
+
senderName: message.senderName,
|
|
13423
|
+
receivedAt: message.receivedAt
|
|
13424
|
+
}));
|
|
13425
|
+
return true;
|
|
13426
|
+
} catch {
|
|
13427
|
+
return false;
|
|
13428
|
+
}
|
|
13247
13429
|
}
|
|
13430
|
+
if (this.type === "claude-cli") {
|
|
13431
|
+
try {
|
|
13432
|
+
const rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
|
|
13433
|
+
if (!rebuilt) return false;
|
|
13434
|
+
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId);
|
|
13435
|
+
this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
|
|
13436
|
+
role: message.role,
|
|
13437
|
+
content: message.content,
|
|
13438
|
+
kind: message.kind,
|
|
13439
|
+
senderName: message.senderName,
|
|
13440
|
+
receivedAt: message.receivedAt
|
|
13441
|
+
}));
|
|
13442
|
+
return true;
|
|
13443
|
+
} catch {
|
|
13444
|
+
return false;
|
|
13445
|
+
}
|
|
13446
|
+
}
|
|
13447
|
+
return false;
|
|
13248
13448
|
}
|
|
13249
13449
|
restorePersistedHistoryFromCurrentSession() {
|
|
13250
13450
|
if (!this.providerSessionId) return;
|
|
13251
|
-
this.
|
|
13451
|
+
this.syncCanonicalSavedHistoryIfNeeded();
|
|
13252
13452
|
this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
|
|
13253
13453
|
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId);
|
|
13254
13454
|
this.historyWriter.seedSessionHistory(
|