@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
|
@@ -103,4 +103,5 @@ export declare function listSavedHistorySessions(agentType: string, options?: {
|
|
|
103
103
|
hasMore: boolean;
|
|
104
104
|
};
|
|
105
105
|
export declare function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId: string): boolean;
|
|
106
|
+
export declare function rebuildClaudeSavedHistoryFromNativeProject(historySessionId: string, workspace?: string): boolean;
|
|
106
107
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -4582,6 +4582,7 @@ init_config();
|
|
|
4582
4582
|
|
|
4583
4583
|
// src/providers/provider-session-id.ts
|
|
4584
4584
|
var HERMES_SESSION_ID_RE = /^\d{8}_\d{6}_[a-z0-9]+$/i;
|
|
4585
|
+
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;
|
|
4585
4586
|
function normalizeProviderSessionId(providerType, providerSessionId) {
|
|
4586
4587
|
const normalizedProviderType = typeof providerType === "string" ? providerType.trim() : "";
|
|
4587
4588
|
const normalizedId = typeof providerSessionId === "string" ? providerSessionId.trim() : "";
|
|
@@ -4591,6 +4592,9 @@ function normalizeProviderSessionId(providerType, providerSessionId) {
|
|
|
4591
4592
|
if (normalizedProviderType === "hermes-cli" && !HERMES_SESSION_ID_RE.test(normalizedId)) {
|
|
4592
4593
|
return "";
|
|
4593
4594
|
}
|
|
4595
|
+
if (normalizedProviderType === "claude-cli" && !CLAUDE_SESSION_ID_RE.test(normalizedId)) {
|
|
4596
|
+
return "";
|
|
4597
|
+
}
|
|
4594
4598
|
return normalizedId;
|
|
4595
4599
|
}
|
|
4596
4600
|
function isLegacyVolatileSessionReadKey(key) {
|
|
@@ -7712,9 +7716,16 @@ function extractCanonicalHermesMessageTimestamp(message, fallbackTs) {
|
|
|
7712
7716
|
if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
|
|
7713
7717
|
return fallbackTs;
|
|
7714
7718
|
}
|
|
7715
|
-
function
|
|
7719
|
+
function extractTimestampValue(value) {
|
|
7720
|
+
const numericTimestamp = Number(value || 0);
|
|
7721
|
+
if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
|
|
7722
|
+
const stringTimestamp = typeof value === "string" ? Date.parse(value) : NaN;
|
|
7723
|
+
if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
|
|
7724
|
+
return 0;
|
|
7725
|
+
}
|
|
7726
|
+
function readExistingSessionStartRecord(agentType, historySessionId) {
|
|
7716
7727
|
try {
|
|
7717
|
-
const dir = path7.join(HISTORY_DIR,
|
|
7728
|
+
const dir = path7.join(HISTORY_DIR, agentType);
|
|
7718
7729
|
if (!fs3.existsSync(dir)) return null;
|
|
7719
7730
|
const files = listHistoryFiles(dir, historySessionId).sort();
|
|
7720
7731
|
for (const file of files) {
|
|
@@ -7735,6 +7746,28 @@ function readExistingHermesSessionStartRecord(historySessionId) {
|
|
|
7735
7746
|
return null;
|
|
7736
7747
|
}
|
|
7737
7748
|
}
|
|
7749
|
+
function rewriteCanonicalSavedHistory(agentType, historySessionId, records) {
|
|
7750
|
+
if (records.length === 0) return false;
|
|
7751
|
+
try {
|
|
7752
|
+
const dir = path7.join(HISTORY_DIR, agentType);
|
|
7753
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
7754
|
+
const prefix = `${historySessionId.replace(/[^a-zA-Z0-9_-]/g, "_")}_`;
|
|
7755
|
+
for (const file of fs3.readdirSync(dir)) {
|
|
7756
|
+
if (file.startsWith(prefix) && file.endsWith(".jsonl")) {
|
|
7757
|
+
fs3.unlinkSync(path7.join(dir, file));
|
|
7758
|
+
}
|
|
7759
|
+
}
|
|
7760
|
+
const targetDate = new Date(records[records.length - 1].receivedAt || Date.now()).toISOString().slice(0, 10);
|
|
7761
|
+
const filePath = path7.join(dir, `${prefix}${targetDate}.jsonl`);
|
|
7762
|
+
fs3.writeFileSync(filePath, `${records.map((record) => JSON.stringify(record)).join("\n")}
|
|
7763
|
+
`, "utf-8");
|
|
7764
|
+
invalidatePersistedSavedHistoryIndex(agentType, dir);
|
|
7765
|
+
savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
|
|
7766
|
+
return true;
|
|
7767
|
+
} catch {
|
|
7768
|
+
return false;
|
|
7769
|
+
}
|
|
7770
|
+
}
|
|
7738
7771
|
function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
|
|
7739
7772
|
const normalizedSessionId = normalizeSavedHistorySessionId("hermes-cli", historySessionId);
|
|
7740
7773
|
if (!normalizedSessionId) return false;
|
|
@@ -7745,7 +7778,7 @@ function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
|
|
|
7745
7778
|
const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
|
|
7746
7779
|
const dir = path7.join(HISTORY_DIR, "hermes-cli");
|
|
7747
7780
|
fs3.mkdirSync(dir, { recursive: true });
|
|
7748
|
-
const existingSessionStart =
|
|
7781
|
+
const existingSessionStart = readExistingSessionStartRecord("hermes-cli", normalizedSessionId);
|
|
7749
7782
|
const records = [];
|
|
7750
7783
|
if (existingSessionStart) {
|
|
7751
7784
|
records.push({
|
|
@@ -7797,20 +7830,167 @@ function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
|
|
|
7797
7830
|
});
|
|
7798
7831
|
}
|
|
7799
7832
|
}
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7833
|
+
return rewriteCanonicalSavedHistory("hermes-cli", normalizedSessionId, records);
|
|
7834
|
+
} catch {
|
|
7835
|
+
return false;
|
|
7836
|
+
}
|
|
7837
|
+
}
|
|
7838
|
+
function resolveClaudeProjectTranscriptPath(historySessionId, workspace) {
|
|
7839
|
+
const claudeProjectsDir = path7.join(os5.homedir(), ".claude", "projects");
|
|
7840
|
+
if (!fs3.existsSync(claudeProjectsDir)) return null;
|
|
7841
|
+
const normalizedWorkspace = typeof workspace === "string" ? workspace.trim() : "";
|
|
7842
|
+
if (normalizedWorkspace) {
|
|
7843
|
+
const directPath = path7.join(claudeProjectsDir, normalizedWorkspace.replace(/[\\/]/g, "-"), `${historySessionId}.jsonl`);
|
|
7844
|
+
if (fs3.existsSync(directPath)) return directPath;
|
|
7845
|
+
}
|
|
7846
|
+
const stack = [claudeProjectsDir];
|
|
7847
|
+
while (stack.length > 0) {
|
|
7848
|
+
const current = stack.pop();
|
|
7849
|
+
if (!current) continue;
|
|
7850
|
+
for (const entry of fs3.readdirSync(current, { withFileTypes: true })) {
|
|
7851
|
+
const entryPath = path7.join(current, entry.name);
|
|
7852
|
+
if (entry.isDirectory()) {
|
|
7853
|
+
stack.push(entryPath);
|
|
7854
|
+
continue;
|
|
7855
|
+
}
|
|
7856
|
+
if (entry.isFile() && entry.name === `${historySessionId}.jsonl`) {
|
|
7857
|
+
return entryPath;
|
|
7805
7858
|
}
|
|
7806
7859
|
}
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
return
|
|
7860
|
+
}
|
|
7861
|
+
return null;
|
|
7862
|
+
}
|
|
7863
|
+
function extractClaudeAssistantContentParts(content) {
|
|
7864
|
+
if (typeof content === "string") {
|
|
7865
|
+
const trimmed = content.trim();
|
|
7866
|
+
return trimmed ? [{ content: trimmed, kind: "standard", role: "assistant" }] : [];
|
|
7867
|
+
}
|
|
7868
|
+
if (!Array.isArray(content)) return [];
|
|
7869
|
+
const parts = [];
|
|
7870
|
+
for (const block of content) {
|
|
7871
|
+
if (!block || typeof block !== "object") continue;
|
|
7872
|
+
const record = block;
|
|
7873
|
+
const type = String(record.type || "").trim();
|
|
7874
|
+
if (type === "text") {
|
|
7875
|
+
const text = String(record.text || "").trim();
|
|
7876
|
+
if (text) parts.push({ content: text, kind: "standard", role: "assistant" });
|
|
7877
|
+
continue;
|
|
7878
|
+
}
|
|
7879
|
+
if (type === "tool_use") {
|
|
7880
|
+
const name = String(record.name || "").trim() || "Tool";
|
|
7881
|
+
const input = record.input && typeof record.input === "object" ? record.input : null;
|
|
7882
|
+
const command = input ? String(input.command || "").trim() : "";
|
|
7883
|
+
const summary = command ? `${name}: ${command}` : name;
|
|
7884
|
+
if (summary) parts.push({ content: summary, kind: "tool", senderName: "Tool", role: "assistant" });
|
|
7885
|
+
}
|
|
7886
|
+
}
|
|
7887
|
+
return parts;
|
|
7888
|
+
}
|
|
7889
|
+
function extractClaudeUserContentParts(content) {
|
|
7890
|
+
if (typeof content === "string") {
|
|
7891
|
+
const trimmed = content.trim();
|
|
7892
|
+
return trimmed ? [{ role: "user", content: trimmed, kind: "standard" }] : [];
|
|
7893
|
+
}
|
|
7894
|
+
if (!Array.isArray(content)) return [];
|
|
7895
|
+
const parts = [];
|
|
7896
|
+
for (const block of content) {
|
|
7897
|
+
if (!block || typeof block !== "object") continue;
|
|
7898
|
+
const record = block;
|
|
7899
|
+
const type = String(record.type || "").trim();
|
|
7900
|
+
if (type === "text") {
|
|
7901
|
+
const text = String(record.text || "").trim();
|
|
7902
|
+
if (text) parts.push({ role: "user", content: text, kind: "standard" });
|
|
7903
|
+
continue;
|
|
7904
|
+
}
|
|
7905
|
+
if (type === "tool_result") {
|
|
7906
|
+
const rawContent = record.content;
|
|
7907
|
+
const text = typeof rawContent === "string" ? rawContent.trim() : Array.isArray(rawContent) ? rawContent.map((entry) => {
|
|
7908
|
+
if (typeof entry === "string") return entry.trim();
|
|
7909
|
+
if (!entry || typeof entry !== "object") return "";
|
|
7910
|
+
const nested = entry;
|
|
7911
|
+
if (typeof nested.text === "string") return nested.text.trim();
|
|
7912
|
+
if (typeof nested.content === "string") return nested.content.trim();
|
|
7913
|
+
return "";
|
|
7914
|
+
}).filter(Boolean).join("\n") : "";
|
|
7915
|
+
if (text) parts.push({ role: "assistant", content: text, kind: "tool", senderName: "Tool" });
|
|
7916
|
+
}
|
|
7917
|
+
}
|
|
7918
|
+
return parts;
|
|
7919
|
+
}
|
|
7920
|
+
function rebuildClaudeSavedHistoryFromNativeProject(historySessionId, workspace) {
|
|
7921
|
+
const normalizedSessionId = normalizeSavedHistorySessionId("claude-cli", historySessionId);
|
|
7922
|
+
if (!normalizedSessionId) return false;
|
|
7923
|
+
try {
|
|
7924
|
+
const transcriptPath = resolveClaudeProjectTranscriptPath(normalizedSessionId, workspace);
|
|
7925
|
+
if (!transcriptPath) return false;
|
|
7926
|
+
const lines = fs3.readFileSync(transcriptPath, "utf-8").split("\n").filter(Boolean);
|
|
7927
|
+
const records = [];
|
|
7928
|
+
const existingSessionStart = readExistingSessionStartRecord("claude-cli", normalizedSessionId);
|
|
7929
|
+
if (existingSessionStart) {
|
|
7930
|
+
records.push({
|
|
7931
|
+
...existingSessionStart,
|
|
7932
|
+
historySessionId: normalizedSessionId
|
|
7933
|
+
});
|
|
7934
|
+
}
|
|
7935
|
+
let fallbackTs = Date.now();
|
|
7936
|
+
for (const line of lines) {
|
|
7937
|
+
let parsed = null;
|
|
7938
|
+
try {
|
|
7939
|
+
parsed = JSON.parse(line);
|
|
7940
|
+
} catch {
|
|
7941
|
+
parsed = null;
|
|
7942
|
+
}
|
|
7943
|
+
if (!parsed) continue;
|
|
7944
|
+
const parsedSessionId = String(parsed.sessionId || "").trim();
|
|
7945
|
+
if (parsedSessionId && parsedSessionId !== normalizedSessionId) continue;
|
|
7946
|
+
const receivedAt = extractTimestampValue(parsed.timestamp) || fallbackTs;
|
|
7947
|
+
fallbackTs = receivedAt + 1;
|
|
7948
|
+
const parsedWorkspace = String(parsed.cwd || workspace || "").trim();
|
|
7949
|
+
if (records.length === 0 && parsedWorkspace) {
|
|
7950
|
+
records.push({
|
|
7951
|
+
ts: new Date(receivedAt).toISOString(),
|
|
7952
|
+
receivedAt,
|
|
7953
|
+
role: "system",
|
|
7954
|
+
kind: "session_start",
|
|
7955
|
+
content: parsedWorkspace,
|
|
7956
|
+
agent: "claude-cli",
|
|
7957
|
+
historySessionId: normalizedSessionId,
|
|
7958
|
+
workspace: parsedWorkspace
|
|
7959
|
+
});
|
|
7960
|
+
}
|
|
7961
|
+
const type = String(parsed.type || "").trim();
|
|
7962
|
+
const message = parsed.message && typeof parsed.message === "object" ? parsed.message : null;
|
|
7963
|
+
if (type === "user" && message) {
|
|
7964
|
+
for (const part of extractClaudeUserContentParts(message.content)) {
|
|
7965
|
+
records.push({
|
|
7966
|
+
ts: new Date(receivedAt).toISOString(),
|
|
7967
|
+
receivedAt,
|
|
7968
|
+
role: part.role,
|
|
7969
|
+
content: part.content,
|
|
7970
|
+
kind: part.kind,
|
|
7971
|
+
senderName: part.senderName,
|
|
7972
|
+
agent: "claude-cli",
|
|
7973
|
+
historySessionId: normalizedSessionId
|
|
7974
|
+
});
|
|
7975
|
+
}
|
|
7976
|
+
continue;
|
|
7977
|
+
}
|
|
7978
|
+
if (type === "assistant" && message) {
|
|
7979
|
+
for (const part of extractClaudeAssistantContentParts(message.content)) {
|
|
7980
|
+
records.push({
|
|
7981
|
+
ts: new Date(receivedAt).toISOString(),
|
|
7982
|
+
receivedAt,
|
|
7983
|
+
role: "assistant",
|
|
7984
|
+
content: part.content,
|
|
7985
|
+
kind: part.kind,
|
|
7986
|
+
senderName: part.senderName,
|
|
7987
|
+
agent: "claude-cli",
|
|
7988
|
+
historySessionId: normalizedSessionId
|
|
7989
|
+
});
|
|
7990
|
+
}
|
|
7991
|
+
}
|
|
7992
|
+
}
|
|
7993
|
+
return rewriteCanonicalSavedHistory("claude-cli", normalizedSessionId, records);
|
|
7814
7994
|
} catch {
|
|
7815
7995
|
return false;
|
|
7816
7996
|
}
|
|
@@ -12869,7 +13049,7 @@ var CliProviderInstance = class {
|
|
|
12869
13049
|
parsedMessages = historyMessageCount > 0 ? parsedMessages.slice(-historyMessageCount) : [];
|
|
12870
13050
|
}
|
|
12871
13051
|
const mergedMessages = this.mergeConversationMessages(parsedMessages);
|
|
12872
|
-
const
|
|
13052
|
+
const canonicalBackedHistory = this.syncCanonicalSavedHistoryIfNeeded();
|
|
12873
13053
|
const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
12874
13054
|
if (parsedMessages.length > 0) {
|
|
12875
13055
|
const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
|
|
@@ -12887,7 +13067,7 @@ var CliProviderInstance = class {
|
|
|
12887
13067
|
senderName: typeof message.senderName === "string" ? message.senderName : void 0,
|
|
12888
13068
|
receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
|
|
12889
13069
|
}));
|
|
12890
|
-
if (!
|
|
13070
|
+
if (!canonicalBackedHistory && !shouldSkipReplayPersist && normalizedMessagesToSave.length > 0) {
|
|
12891
13071
|
const incrementalMessages = buildIncrementalHistoryAppendMessages(this.lastPersistedHistoryMessages, normalizedMessagesToSave);
|
|
12892
13072
|
this.historyWriter.appendNewMessages(
|
|
12893
13073
|
this.type,
|
|
@@ -12897,7 +13077,7 @@ var CliProviderInstance = class {
|
|
|
12897
13077
|
this.providerSessionId
|
|
12898
13078
|
);
|
|
12899
13079
|
}
|
|
12900
|
-
if (!
|
|
13080
|
+
if (!canonicalBackedHistory) {
|
|
12901
13081
|
this.lastPersistedHistoryMessages = normalizedMessagesToSave;
|
|
12902
13082
|
}
|
|
12903
13083
|
}
|
|
@@ -13374,32 +13554,52 @@ ${effect.notification.body || ""}`.trim();
|
|
|
13374
13554
|
});
|
|
13375
13555
|
LOG.info("CLI", `[${this.type}] discovered provider session id: ${nextSessionId}`);
|
|
13376
13556
|
}
|
|
13377
|
-
|
|
13378
|
-
if (
|
|
13379
|
-
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
|
|
13557
|
+
syncCanonicalSavedHistoryIfNeeded() {
|
|
13558
|
+
if (!this.providerSessionId) return false;
|
|
13559
|
+
if (this.type === "hermes-cli") {
|
|
13560
|
+
try {
|
|
13561
|
+
const canonicalPath = path11.join(os11.homedir(), ".hermes", "sessions", `session_${this.providerSessionId}.json`);
|
|
13562
|
+
if (!fs5.existsSync(canonicalPath)) return false;
|
|
13563
|
+
const stat = fs5.statSync(canonicalPath);
|
|
13564
|
+
if (stat.mtimeMs <= this.lastCanonicalHermesSyncMtimeMs) return true;
|
|
13565
|
+
const rebuilt = rebuildHermesSavedHistoryFromCanonicalSession(this.providerSessionId);
|
|
13566
|
+
if (!rebuilt) return false;
|
|
13567
|
+
this.lastCanonicalHermesSyncMtimeMs = stat.mtimeMs;
|
|
13568
|
+
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId);
|
|
13569
|
+
this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
|
|
13570
|
+
role: message.role,
|
|
13571
|
+
content: message.content,
|
|
13572
|
+
kind: message.kind,
|
|
13573
|
+
senderName: message.senderName,
|
|
13574
|
+
receivedAt: message.receivedAt
|
|
13575
|
+
}));
|
|
13576
|
+
return true;
|
|
13577
|
+
} catch {
|
|
13578
|
+
return false;
|
|
13579
|
+
}
|
|
13398
13580
|
}
|
|
13581
|
+
if (this.type === "claude-cli") {
|
|
13582
|
+
try {
|
|
13583
|
+
const rebuilt = rebuildClaudeSavedHistoryFromNativeProject(this.providerSessionId, this.workingDir);
|
|
13584
|
+
if (!rebuilt) return false;
|
|
13585
|
+
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId);
|
|
13586
|
+
this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
|
|
13587
|
+
role: message.role,
|
|
13588
|
+
content: message.content,
|
|
13589
|
+
kind: message.kind,
|
|
13590
|
+
senderName: message.senderName,
|
|
13591
|
+
receivedAt: message.receivedAt
|
|
13592
|
+
}));
|
|
13593
|
+
return true;
|
|
13594
|
+
} catch {
|
|
13595
|
+
return false;
|
|
13596
|
+
}
|
|
13597
|
+
}
|
|
13598
|
+
return false;
|
|
13399
13599
|
}
|
|
13400
13600
|
restorePersistedHistoryFromCurrentSession() {
|
|
13401
13601
|
if (!this.providerSessionId) return;
|
|
13402
|
-
this.
|
|
13602
|
+
this.syncCanonicalSavedHistoryIfNeeded();
|
|
13403
13603
|
this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
|
|
13404
13604
|
const restoredHistory = readChatHistory(this.type, 0, Number.MAX_SAFE_INTEGER, this.providerSessionId);
|
|
13405
13605
|
this.historyWriter.seedSessionHistory(
|