@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.mjs CHANGED
@@ -7522,6 +7522,125 @@ function listSavedHistorySessions(agentType, options = {}) {
7522
7522
  return { sessions: [], hasMore: false };
7523
7523
  }
7524
7524
  }
7525
+ function normalizeCanonicalHermesMessageContent(content) {
7526
+ if (typeof content === "string") return content.trim();
7527
+ if (content == null) return "";
7528
+ try {
7529
+ return JSON.stringify(content).trim();
7530
+ } catch {
7531
+ return String(content).trim();
7532
+ }
7533
+ }
7534
+ function extractCanonicalHermesMessageTimestamp(message, fallbackTs) {
7535
+ const numericTimestamp = Number(message.receivedAt || message.timestamp || message.ts || 0);
7536
+ if (Number.isFinite(numericTimestamp) && numericTimestamp > 0) return numericTimestamp;
7537
+ const stringTimestamp = typeof message.ts === "string" ? Date.parse(message.ts) : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
7538
+ if (Number.isFinite(stringTimestamp) && stringTimestamp > 0) return stringTimestamp;
7539
+ return fallbackTs;
7540
+ }
7541
+ function readExistingHermesSessionStartRecord(historySessionId) {
7542
+ try {
7543
+ const dir = path7.join(HISTORY_DIR, "hermes-cli");
7544
+ if (!fs3.existsSync(dir)) return null;
7545
+ const files = listHistoryFiles(dir, historySessionId).sort();
7546
+ for (const file of files) {
7547
+ const lines = fs3.readFileSync(path7.join(dir, file), "utf-8").split("\n").filter(Boolean);
7548
+ for (const line of lines) {
7549
+ try {
7550
+ const parsed = JSON.parse(line);
7551
+ if (parsed.historySessionId !== historySessionId) continue;
7552
+ if (parsed.kind === "session_start" && parsed.role === "system") {
7553
+ return parsed;
7554
+ }
7555
+ } catch {
7556
+ }
7557
+ }
7558
+ }
7559
+ return null;
7560
+ } catch {
7561
+ return null;
7562
+ }
7563
+ }
7564
+ function rebuildHermesSavedHistoryFromCanonicalSession(historySessionId) {
7565
+ const normalizedSessionId = normalizeSavedHistorySessionId("hermes-cli", historySessionId);
7566
+ if (!normalizedSessionId) return false;
7567
+ try {
7568
+ const sessionFilePath = path7.join(os5.homedir(), ".hermes", "sessions", `session_${normalizedSessionId}.json`);
7569
+ if (!fs3.existsSync(sessionFilePath)) return false;
7570
+ const raw = JSON.parse(fs3.readFileSync(sessionFilePath, "utf-8"));
7571
+ const canonicalMessages = Array.isArray(raw.messages) ? raw.messages : [];
7572
+ const dir = path7.join(HISTORY_DIR, "hermes-cli");
7573
+ fs3.mkdirSync(dir, { recursive: true });
7574
+ const existingSessionStart = readExistingHermesSessionStartRecord(normalizedSessionId);
7575
+ const records = [];
7576
+ if (existingSessionStart) {
7577
+ records.push({
7578
+ ...existingSessionStart,
7579
+ historySessionId: normalizedSessionId
7580
+ });
7581
+ }
7582
+ let fallbackTs = Date.parse(raw.session_start || raw.last_updated || "") || Date.now();
7583
+ for (const message of canonicalMessages) {
7584
+ const role = String(message.role || "").trim();
7585
+ const content = normalizeCanonicalHermesMessageContent(message.content);
7586
+ if (!content) continue;
7587
+ const receivedAt = extractCanonicalHermesMessageTimestamp(message, fallbackTs);
7588
+ fallbackTs = receivedAt + 1;
7589
+ if (role === "user") {
7590
+ records.push({
7591
+ ts: new Date(receivedAt).toISOString(),
7592
+ receivedAt,
7593
+ role: "user",
7594
+ content,
7595
+ kind: "standard",
7596
+ agent: "hermes-cli",
7597
+ historySessionId: normalizedSessionId
7598
+ });
7599
+ continue;
7600
+ }
7601
+ if (role === "assistant") {
7602
+ records.push({
7603
+ ts: new Date(receivedAt).toISOString(),
7604
+ receivedAt,
7605
+ role: "assistant",
7606
+ content,
7607
+ kind: "standard",
7608
+ agent: "hermes-cli",
7609
+ historySessionId: normalizedSessionId
7610
+ });
7611
+ continue;
7612
+ }
7613
+ if (role === "tool") {
7614
+ records.push({
7615
+ ts: new Date(receivedAt).toISOString(),
7616
+ receivedAt,
7617
+ role: "assistant",
7618
+ content,
7619
+ kind: "tool",
7620
+ senderName: "Tool",
7621
+ agent: "hermes-cli",
7622
+ historySessionId: normalizedSessionId
7623
+ });
7624
+ }
7625
+ }
7626
+ if (records.length === 0) return false;
7627
+ const prefix = `${normalizedSessionId.replace(/[^a-zA-Z0-9_-]/g, "_")}_`;
7628
+ for (const file of fs3.readdirSync(dir)) {
7629
+ if (file.startsWith(prefix) && file.endsWith(".jsonl")) {
7630
+ fs3.unlinkSync(path7.join(dir, file));
7631
+ }
7632
+ }
7633
+ const targetDate = new Date(records[records.length - 1].receivedAt || Date.now()).toISOString().slice(0, 10);
7634
+ const filePath = path7.join(dir, `${prefix}${targetDate}.jsonl`);
7635
+ fs3.writeFileSync(filePath, `${records.map((record) => JSON.stringify(record)).join("\n")}
7636
+ `, "utf-8");
7637
+ invalidatePersistedSavedHistoryIndex("hermes-cli", dir);
7638
+ savedHistorySessionCache.delete("hermes-cli");
7639
+ return true;
7640
+ } catch {
7641
+ return false;
7642
+ }
7643
+ }
7525
7644
 
7526
7645
  // src/providers/provider-patch-state.ts
7527
7646
  function isControlValue(value) {
@@ -12432,6 +12551,7 @@ var CliProviderInstance = class {
12432
12551
  historyWriter;
12433
12552
  runtimeMessages = [];
12434
12553
  lastPersistedHistoryMessages = [];
12554
+ lastCanonicalHermesSyncMtimeMs = 0;
12435
12555
  instanceId;
12436
12556
  suppressIdleHistoryReplay = false;
12437
12557
  errorMessage = void 0;
@@ -12464,34 +12584,7 @@ var CliProviderInstance = class {
12464
12584
  await this.enforceFreshSessionLaunchIfNeeded();
12465
12585
  this.maybeAppendRuntimeRecoveryMessage(this.adapter.getRuntimeMetadata());
12466
12586
  if (this.providerSessionId) {
12467
- this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
12468
- const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
12469
- this.historyWriter.seedSessionHistory(
12470
- this.type,
12471
- restoredHistory.messages,
12472
- this.providerSessionId,
12473
- this.instanceId
12474
- );
12475
- this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
12476
- role: message.role,
12477
- content: message.content,
12478
- kind: message.kind,
12479
- senderName: message.senderName,
12480
- receivedAt: message.receivedAt
12481
- }));
12482
- this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
12483
- if (restoredHistory.messages.length > 0) {
12484
- this.adapter.seedCommittedMessages(
12485
- restoredHistory.messages.map((message) => ({
12486
- role: message.role,
12487
- content: message.content,
12488
- timestamp: message.receivedAt,
12489
- receivedAt: message.receivedAt,
12490
- kind: message.kind,
12491
- senderName: message.senderName
12492
- }))
12493
- );
12494
- }
12587
+ this.restorePersistedHistoryFromCurrentSession();
12495
12588
  }
12496
12589
  if (this.providerSessionId && this.launchMode === "resume") {
12497
12590
  const resumedAt = Date.now();
@@ -12600,6 +12693,7 @@ var CliProviderInstance = class {
12600
12693
  parsedMessages = historyMessageCount > 0 ? parsedMessages.slice(-historyMessageCount) : [];
12601
12694
  }
12602
12695
  const mergedMessages = this.mergeConversationMessages(parsedMessages);
12696
+ const canonicalHermesBackedHistory = this.syncCanonicalHermesSavedHistoryIfNeeded();
12603
12697
  const dirName = this.workingDir.split("/").filter(Boolean).pop() || "session";
12604
12698
  if (parsedMessages.length > 0) {
12605
12699
  const shouldSkipReplayPersist = this.suppressIdleHistoryReplay && adapterStatus.status === "idle" && parsedStatus?.status === "idle";
@@ -12617,7 +12711,7 @@ var CliProviderInstance = class {
12617
12711
  senderName: typeof message.senderName === "string" ? message.senderName : void 0,
12618
12712
  receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
12619
12713
  }));
12620
- if (!shouldSkipReplayPersist && normalizedMessagesToSave.length > 0) {
12714
+ if (!canonicalHermesBackedHistory && !shouldSkipReplayPersist && normalizedMessagesToSave.length > 0) {
12621
12715
  const incrementalMessages = buildIncrementalHistoryAppendMessages(this.lastPersistedHistoryMessages, normalizedMessagesToSave);
12622
12716
  this.historyWriter.appendNewMessages(
12623
12717
  this.type,
@@ -12627,7 +12721,9 @@ var CliProviderInstance = class {
12627
12721
  this.providerSessionId
12628
12722
  );
12629
12723
  }
12630
- this.lastPersistedHistoryMessages = normalizedMessagesToSave;
12724
+ if (!canonicalHermesBackedHistory) {
12725
+ this.lastPersistedHistoryMessages = normalizedMessagesToSave;
12726
+ }
12631
12727
  }
12632
12728
  this.applyProviderResponse(parsedStatus, { phase: "immediate" });
12633
12729
  const surface = resolveProviderStateSurface({
@@ -13084,6 +13180,7 @@ ${effect.notification.body || ""}`.trim();
13084
13180
  this.providerSessionId = nextSessionId;
13085
13181
  this.historyWriter.promoteHistorySession(this.type, previousHistorySessionId, nextSessionId);
13086
13182
  this.historyWriter.writeSessionStart(this.type, nextSessionId, this.workingDir, this.instanceId);
13183
+ this.restorePersistedHistoryFromCurrentSession();
13087
13184
  this.adapter.updateRuntimeMeta({ providerSessionId: nextSessionId });
13088
13185
  this.onProviderSessionResolved?.({
13089
13186
  instanceId: this.instanceId,
@@ -13095,6 +13192,61 @@ ${effect.notification.body || ""}`.trim();
13095
13192
  });
13096
13193
  LOG.info("CLI", `[${this.type}] discovered provider session id: ${nextSessionId}`);
13097
13194
  }
13195
+ syncCanonicalHermesSavedHistoryIfNeeded() {
13196
+ if (this.type !== "hermes-cli" || !this.providerSessionId) return false;
13197
+ try {
13198
+ const canonicalPath = path11.join(os11.homedir(), ".hermes", "sessions", `session_${this.providerSessionId}.json`);
13199
+ if (!fs5.existsSync(canonicalPath)) return false;
13200
+ const stat = fs5.statSync(canonicalPath);
13201
+ if (stat.mtimeMs <= this.lastCanonicalHermesSyncMtimeMs) return true;
13202
+ const rebuilt = rebuildHermesSavedHistoryFromCanonicalSession(this.providerSessionId);
13203
+ if (!rebuilt) return false;
13204
+ this.lastCanonicalHermesSyncMtimeMs = stat.mtimeMs;
13205
+ const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
13206
+ this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
13207
+ role: message.role,
13208
+ content: message.content,
13209
+ kind: message.kind,
13210
+ senderName: message.senderName,
13211
+ receivedAt: message.receivedAt
13212
+ }));
13213
+ return true;
13214
+ } catch {
13215
+ return false;
13216
+ }
13217
+ }
13218
+ restorePersistedHistoryFromCurrentSession() {
13219
+ if (!this.providerSessionId) return;
13220
+ this.syncCanonicalHermesSavedHistoryIfNeeded();
13221
+ this.historyWriter.compactHistorySession(this.type, this.providerSessionId);
13222
+ const restoredHistory = readChatHistory(this.type, 0, 200, this.providerSessionId);
13223
+ this.historyWriter.seedSessionHistory(
13224
+ this.type,
13225
+ restoredHistory.messages,
13226
+ this.providerSessionId,
13227
+ this.instanceId
13228
+ );
13229
+ this.lastPersistedHistoryMessages = restoredHistory.messages.map((message) => ({
13230
+ role: message.role,
13231
+ content: message.content,
13232
+ kind: message.kind,
13233
+ senderName: message.senderName,
13234
+ receivedAt: message.receivedAt
13235
+ }));
13236
+ this.suppressIdleHistoryReplay = restoredHistory.messages.length > 0;
13237
+ if (restoredHistory.messages.length > 0) {
13238
+ this.adapter.seedCommittedMessages(
13239
+ restoredHistory.messages.map((message) => ({
13240
+ role: message.role,
13241
+ content: message.content,
13242
+ timestamp: message.receivedAt,
13243
+ receivedAt: message.receivedAt,
13244
+ kind: message.kind,
13245
+ senderName: message.senderName
13246
+ }))
13247
+ );
13248
+ }
13249
+ }
13098
13250
  getProbeDirectories() {
13099
13251
  const dirs = /* @__PURE__ */ new Set();
13100
13252
  const addDir = (value) => {