@adhdev/daemon-core 0.8.94 → 0.8.95

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/index.js CHANGED
@@ -7673,6 +7673,125 @@ function listSavedHistorySessions(agentType, options = {}) {
7673
7673
  return { sessions: [], hasMore: false };
7674
7674
  }
7675
7675
  }
7676
+ function normalizeCanonicalHermesMessageContent(content) {
7677
+ if (typeof content === "string") return content.trim();
7678
+ if (content == null) return "";
7679
+ try {
7680
+ return JSON.stringify(content).trim();
7681
+ } catch {
7682
+ return String(content).trim();
7683
+ }
7684
+ }
7685
+ function extractCanonicalHermesMessageTimestamp(message, fallbackTs) {
7686
+ const numericTimestamp = Number(message.receivedAt || message.timestamp || message.ts || 0);
7687
+ if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
7688
+ const stringTimestamp = typeof message.ts === "string" ? Date.parse(message.ts) : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
7689
+ if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
7690
+ return fallbackTs;
7691
+ }
7692
+ function readExistingHermesSessionStartRecord(historySessionId) {
7693
+ try {
7694
+ const dir = path7.join(HISTORY_DIR, "hermes-cli");
7695
+ if (!fs3.existsSync(dir)) return null;
7696
+ const files = listHistoryFiles(dir, historySessionId).sort();
7697
+ for (const file of files) {
7698
+ const lines = fs3.readFileSync(path7.join(dir, file), "utf-8").split("\n").filter(Boolean);
7699
+ for (const line of lines) {
7700
+ try {
7701
+ const parsed = JSON.parse(line);
7702
+ if (parsed.historySessionId !== historySessionId) continue;
7703
+ if (parsed.kind === "session_start" && parsed.role === "system") {
7704
+ return parsed;
7705
+ }
7706
+ } catch {
7707
+ }
7708
+ }
7709
+ }
7710
+ return null;
7711
+ } catch {
7712
+ return null;
7713
+ }
7714
+ }
7715
+ function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
7716
+ const normalizedSessionId = normalizeSavedHistorySessionId("hermes-cli", historySessionId);
7717
+ if (!normalizedSessionId) return false;
7718
+ try {
7719
+ const sessionFilePath = path7.join(os5.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
7720
+ if (!fs3.existsSync(sessionFilePath)) return false;
7721
+ const raw = JSON.parse(fs3.readFileSync(sessionFilePath, "utf-8"));
7722
+ const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
7723
+ const dir = path7.join(HISTORY_DIR, "hermes-cli");
7724
+ fs3.mkdirSync(dir, { recursive: true });
7725
+ const existingSessionStart = readExistingHermesSessionStartRecord(normalizedSessionId);
7726
+ const records = [];
7727
+ if (existingSessionStart) {
7728
+ records.push({
7729
+ ...existingSessionStart,
7730
+ historySessionId: normalizedSessionId
7731
+ });
7732
+ }
7733
+ let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
7734
+ for (const message of canonicalMessages) {
7735
+ const role = String(message.role || "").trim();
7736
+ const content = normalizeCanonicalHermesMessageContent(message.content);
7737
+ if (!content) continue;
7738
+ const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
7739
+ fallbackTs = receivedAt + 1;
7740
+ if (role === "user") {
7741
+ records.push({
7742
+ ts: new Date(receivedAt).toISOString(),
7743
+ receivedAt,
7744
+ role: "user",
7745
+ content,
7746
+ kind: "standard",
7747
+ agent: "hermes-cli",
7748
+ historySessionId: normalizedSessionId
7749
+ });
7750
+ continue;
7751
+ }
7752
+ if (role === "assistant") {
7753
+ records.push({
7754
+ ts: new Date(receivedAt).toISOString(),
7755
+ receivedAt,
7756
+ role: "assistant",
7757
+ content,
7758
+ kind: "standard",
7759
+ agent: "hermes-cli",
7760
+ historySessionId: normalizedSessionId
7761
+ });
7762
+ continue;
7763
+ }
7764
+ if (role === "tool") {
7765
+ records.push({
7766
+ ts: new Date(receivedAt).toISOString(),
7767
+ receivedAt,
7768
+ role: "assistant",
7769
+ content,
7770
+ kind: "tool",
7771
+ senderName: "Tool",
7772
+ agent: "hermes-cli",
7773
+ historySessionId: normalizedSessionId
7774
+ });
7775
+ }
7776
+ }
7777
+ if (records.length === 0) return false;
7778
+ const prefix = `${normalizedSessionId.replace(/[^a-zA-Z0-9_-]/g, "_")}_`;
7779
+ for (const file of fs3.readdirSync(dir)) {
7780
+ if (file.startsWith(prefix) && file.endsWith(".jsonl")) {
7781
+ fs3.unlinkSync(path7.join(dir, file));
7782
+ }
7783
+ }
7784
+ const targetDate = new Date(records[records.length - 1].receivedAt || Date.now()).toISOString().slice(0, 10);
7785
+ const filePath = path7.join(dir, `${prefix}${targetDate}.jsonl`);
7786
+ fs3.writeFileSync(filePath, `${records.map((record) => JSON.stringify(record)).join("\n")}
7787
+ `, "utf-8");
7788
+ invalidatePersistedSavedHistoryIndex("hermes-cli", dir);
7789
+ savedHistorySessionCache.delete("hermes-cli");
7790
+ return true;
7791
+ } catch {
7792
+ return false;
7793
+ }
7794
+ }
7676
7795
 
7677
7796
  // src/providers/provider-patch-state.ts
7678
7797
  function isControlValue(value) {
@@ -12583,6 +12702,7 @@ var CliProviderInstance = class {
12583
12702
  historyWriter;
12584
12703
  runtimeMessages = [];
12585
12704
  lastPersistedHistoryMessages = [];
12705
+ lastCanonicalHermesSyncMtimeMs = 0;
12586
12706
  instanceId;
12587
12707
  suppressIdleHistoryReplay = false;
12588
12708
  errorMessage = void 0;
@@ -12615,34 +12735,7 @@ var CliProviderInstance = class {
12615
12735
  await this.enforceFreshSessionLaunchIfNeeded();
12616
12736
  this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
12617
12737
  if (this.providerSessionId) {
12618
- this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
12619
- const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
12620
- this.historyWriter.seedSessionHistory(
12621
- this.type,
12622
- restoredHistory.messages,
12623
- this.providerSessionId,
12624
- this.instanceId
12625
- );
12626
- this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
12627
- role: message.role,
12628
- content: message.content,
12629
- kind: message.kind,
12630
- senderName: message.senderName,
12631
- receivedAt: message.receivedAt
12632
- }));
12633
- this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
12634
- if (restoredHistory.messages.length > 0) {
12635
- this.adapter.seedCommittedMessages(
12636
- restoredHistory.messages.map((message) => ({
12637
- role: message.role,
12638
- content: message.content,
12639
- timestamp: message.receivedAt,
12640
- receivedAt: message.receivedAt,
12641
- kind: message.kind,
12642
- senderName: message.senderName
12643
- }))
12644
- );
12645
- }
12738
+ this.restorePersistedHistoryFromCurrentSession();
12646
12739
  }
12647
12740
  if (this.providerSessionId && this.launchMode === "resume") {
12648
12741
  const resumedAt = Date.now();
@@ -12751,6 +12844,7 @@ var CliProviderInstance = class {
12751
12844
  parsedMessages = historyMessageCount > 0 ? parsedMessages.slice(-historyMessageCount) : [];
12752
12845
  }
12753
12846
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
12847
+ const canonicalHermesBackedHistory = this.syncCanonicalHermesSavedHistoryIfNeeded();
12754
12848
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
12755
12849
  if (parsedMessages.length > 0) {
12756
12850
  const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
@@ -12768,7 +12862,7 @@ var CliProviderInstance = class {
12768
12862
  senderName: typeof message.senderName === "string" ? message.senderName : void 0,
12769
12863
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
12770
12864
  }));
12771
- if (!shouldSkipReplayPersist && normalizedMessagesToSave.length > 0) {
12865
+ if (!canonicalHermesBackedHistory && !shouldSkipReplayPersist && normalizedMessagesToSave.length > 0) {
12772
12866
  const incrementalMessages = buildIncrementalHistoryAppendMessages(this.lastPersistedHistoryMessages, normalizedMessagesToSave);
12773
12867
  this.historyWriter.appendNewMessages(
12774
12868
  this.type,
@@ -12778,7 +12872,9 @@ var CliProviderInstance = class {
12778
12872
  this.providerSessionId
12779
12873
  );
12780
12874
  }
12781
- this.lastPersistedHistoryMessages = normalizedMessagesToSave;
12875
+ if (!canonicalHermesBackedHistory) {
12876
+ this.lastPersistedHistoryMessages = normalizedMessagesToSave;
12877
+ }
12782
12878
  }
12783
12879
  this.applyProviderResponse(parsedStatus, { phase: "immediate" });
12784
12880
  const surface = resolveProviderStateSurface({
@@ -13235,6 +13331,7 @@ ${effect.notification.body || ""}`.trim();
13235
13331
  this.providerSessionId = nextSessionId;
13236
13332
  this.historyWriter.promoteHistorySession(this.type, previousHistorySessionId, nextSessionId);
13237
13333
  this.historyWriter.writeSessionStart(this.type, nextSessionId, this.workingDir, this.instanceId);
13334
+ this.restorePersistedHistoryFromCurrentSession();
13238
13335
  this.adapter.updateRuntimeMeta({ providerSessionId: nextSessionId });
13239
13336
  this.onProviderSessionResolved?.({
13240
13337
  instanceId: this.instanceId,
@@ -13246,6 +13343,61 @@ ${effect.notification.body || ""}`.trim();
13246
13343
  });
13247
13344
  LOG.info("CLI", `[${this.type}] discovered provider session id: ${nextSessionId}`);
13248
13345
  }
13346
+ syncCanonicalHermesSavedHistoryIfNeeded() {
13347
+ if (this.type !== "hermes-cli" || !this.providerSessionId) return false;
13348
+ try {
13349
+ const canonicalPath = path11.join(os11.homedir(), ".hermes", "sessions", `session_${this.providerSessionId}.json`);
13350
+ if (!fs5.existsSync(canonicalPath)) return false;
13351
+ const stat = fs5.statSync(canonicalPath);
13352
+ if (stat.mtimeMs <= this.lastCanonicalHermesSyncMtimeMs) return true;
13353
+ const rebuilt = rebuildHermesSavedHistoryFromCanonicalSession(this.providerSessionId);
13354
+ if (!rebuilt) return false;
13355
+ this.lastCanonicalHermesSyncMtimeMs = stat.mtimeMs;
13356
+ const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
13357
+ this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
13358
+ role: message.role,
13359
+ content: message.content,
13360
+ kind: message.kind,
13361
+ senderName: message.senderName,
13362
+ receivedAt: message.receivedAt
13363
+ }));
13364
+ return true;
13365
+ } catch {
13366
+ return false;
13367
+ }
13368
+ }
13369
+ restorePersistedHistoryFromCurrentSession() {
13370
+ if (!this.providerSessionId) return;
13371
+ this.syncCanonicalHermesSavedHistoryIfNeeded();
13372
+ this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
13373
+ const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
13374
+ this.historyWriter.seedSessionHistory(
13375
+ this.type,
13376
+ restoredHistory.messages,
13377
+ this.providerSessionId,
13378
+ this.instanceId
13379
+ );
13380
+ this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
13381
+ role: message.role,
13382
+ content: message.content,
13383
+ kind: message.kind,
13384
+ senderName: message.senderName,
13385
+ receivedAt: message.receivedAt
13386
+ }));
13387
+ this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
13388
+ if (restoredHistory.messages.length > 0) {
13389
+ this.adapter.seedCommittedMessages(
13390
+ restoredHistory.messages.map((message) => ({
13391
+ role: message.role,
13392
+ content: message.content,
13393
+ timestamp: message.receivedAt,
13394
+ receivedAt: message.receivedAt,
13395
+ kind: message.kind,
13396
+ senderName: message.senderName
13397
+ }))
13398
+ );
13399
+ }
13400
+ }
13249
13401
  getProbeDirectories() {
13250
13402
  const dirs = /* @__PURE__ */ new Set();
13251
13403
  const addDir = (value) => {