@xopcai/xopc 0.0.30 → 0.0.31

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.
Files changed (105) hide show
  1. package/dist/extensions/telegram/xopc.extension.json +1 -1
  2. package/dist/gateway/static/root/assets/agents-3u63Fw2Y.js +216 -0
  3. package/dist/gateway/static/root/assets/agents-3u63Fw2Y.js.map +1 -0
  4. package/dist/gateway/static/root/assets/{apps-page-CTChHQAu.js → apps-page-CWegY6Kp.js} +2 -2
  5. package/dist/gateway/static/root/assets/{apps-page-CTChHQAu.js.map → apps-page-CWegY6Kp.js.map} +1 -1
  6. package/dist/gateway/static/root/assets/channels-settings-CiyeXcTK.js +9 -0
  7. package/dist/gateway/static/root/assets/channels-settings-CiyeXcTK.js.map +1 -0
  8. package/dist/gateway/static/root/assets/cron-api-_j_79Zf5.js +3 -0
  9. package/dist/gateway/static/root/assets/cron-api-_j_79Zf5.js.map +1 -0
  10. package/dist/gateway/static/root/assets/cron-page-S86YNTtI.js +2 -0
  11. package/dist/gateway/static/root/assets/cron-page-S86YNTtI.js.map +1 -0
  12. package/dist/gateway/static/root/assets/dist-D0jxbvuz.js +2 -0
  13. package/dist/gateway/static/root/assets/{dist-UWGUW3x8.js.map → dist-D0jxbvuz.js.map} +1 -1
  14. package/dist/gateway/static/root/assets/{extension-debug-page-BwB4a4cK.js → extension-debug-page-DB630cW8.js} +2 -2
  15. package/dist/gateway/static/root/assets/{extension-debug-page-BwB4a4cK.js.map → extension-debug-page-DB630cW8.js.map} +1 -1
  16. package/dist/gateway/static/root/assets/{extension-page-CSWu2PHZ.js → extension-page-CnoPUBul.js} +2 -2
  17. package/dist/gateway/static/root/assets/{extension-page-CSWu2PHZ.js.map → extension-page-CnoPUBul.js.map} +1 -1
  18. package/dist/gateway/static/root/assets/{extension-settings-page-B12K2a13.js → extension-settings-page-BsiOkvBe.js} +2 -2
  19. package/dist/gateway/static/root/assets/{extension-settings-page-B12K2a13.js.map → extension-settings-page-BsiOkvBe.js.map} +1 -1
  20. package/dist/gateway/static/root/assets/{index-D0pFZ0OE.js → index-DHLmAIQl.js} +81 -81
  21. package/dist/gateway/static/root/assets/{index-D0pFZ0OE.js.map → index-DHLmAIQl.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/index-DoPwy4aU.css +1 -0
  23. package/dist/gateway/static/root/assets/logs-page-Bndhenn2.js +2 -0
  24. package/dist/gateway/static/root/assets/logs-page-Bndhenn2.js.map +1 -0
  25. package/dist/gateway/static/root/assets/sessions-page-Q201-_lP.js +2 -0
  26. package/dist/gateway/static/root/assets/{sessions-page-DJkuWpOT.js.map → sessions-page-Q201-_lP.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/settings-page-Cw75fpc6.js +2 -0
  28. package/dist/gateway/static/root/assets/settings-page-Cw75fpc6.js.map +1 -0
  29. package/dist/gateway/static/root/assets/skills-page-CVwEzD_J.js +3 -0
  30. package/dist/gateway/static/root/assets/skills-page-CVwEzD_J.js.map +1 -0
  31. package/dist/gateway/static/root/index.html +2 -2
  32. package/dist/package.js +1 -1
  33. package/dist/src/agent/orchestration/agent-orchestrator.js +1 -1
  34. package/dist/src/agent/service/process-direct-streaming.js +12 -1
  35. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  36. package/dist/src/agent/service.d.ts +4 -0
  37. package/dist/src/agent/service.js +7 -1
  38. package/dist/src/agent/service.js.map +1 -1
  39. package/dist/src/agent/skills/marketplace/resolve-adapter.js +1 -1
  40. package/dist/src/agent/skills/marketplace/resolve-adapter.js.map +1 -1
  41. package/dist/src/config/schema.js +1 -0
  42. package/dist/src/config/schema.js.map +1 -1
  43. package/dist/src/cron/validation.js +1 -1
  44. package/dist/src/cron/validation.js.map +1 -1
  45. package/dist/src/gateway/hono/routes/sessions.js +124 -2
  46. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  47. package/dist/src/gateway/hono/sse.js +9 -2
  48. package/dist/src/gateway/hono/sse.js.map +1 -1
  49. package/dist/src/gateway/service/run-gateway-agent.d.ts +1 -0
  50. package/dist/src/gateway/service/run-gateway-agent.js +18 -10
  51. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  52. package/dist/src/gateway/service.d.ts +23 -1
  53. package/dist/src/gateway/service.js +47 -3
  54. package/dist/src/gateway/service.js.map +1 -1
  55. package/dist/src/session/abort-cutoff.d.ts +6 -0
  56. package/dist/src/session/abort-cutoff.js +10 -0
  57. package/dist/src/session/abort-cutoff.js.map +1 -0
  58. package/dist/src/session/compaction-checkpoints.d.ts +8 -0
  59. package/dist/src/session/compaction-checkpoints.js +21 -0
  60. package/dist/src/session/compaction-checkpoints.js.map +1 -0
  61. package/dist/src/session/index.d.ts +8 -1
  62. package/dist/src/session/index.js +7 -1
  63. package/dist/src/session/manager.d.ts +26 -1
  64. package/dist/src/session/manager.js +39 -2
  65. package/dist/src/session/manager.js.map +1 -1
  66. package/dist/src/session/patch-metadata.d.ts +12 -0
  67. package/dist/src/session/patch-metadata.js +23 -0
  68. package/dist/src/session/patch-metadata.js.map +1 -0
  69. package/dist/src/session/search-index.d.ts +2 -0
  70. package/dist/src/session/search-index.js +30 -2
  71. package/dist/src/session/search-index.js.map +1 -1
  72. package/dist/src/session/session-context-for-llm.d.ts +32 -0
  73. package/dist/src/session/session-context-for-llm.js +60 -0
  74. package/dist/src/session/session-context-for-llm.js.map +1 -0
  75. package/dist/src/session/store.d.ts +36 -2
  76. package/dist/src/session/store.js +200 -28
  77. package/dist/src/session/store.js.map +1 -1
  78. package/dist/src/session/strip-webchat-early-save.d.ts +5 -0
  79. package/dist/src/session/strip-webchat-early-save.js +17 -0
  80. package/dist/src/session/strip-webchat-early-save.js.map +1 -0
  81. package/dist/src/session/transcript-format.d.ts +46 -0
  82. package/dist/src/session/transcript-format.js +88 -0
  83. package/dist/src/session/transcript-format.js.map +1 -0
  84. package/dist/src/session/types.d.ts +37 -0
  85. package/dist/src/session/types.js.map +1 -1
  86. package/dist/src/utils/logger/log-store.js +4 -3
  87. package/dist/src/utils/logger/log-store.js.map +1 -1
  88. package/package.json +1 -1
  89. package/dist/gateway/static/root/assets/agents-BfwtJOPK.js +0 -216
  90. package/dist/gateway/static/root/assets/agents-BfwtJOPK.js.map +0 -1
  91. package/dist/gateway/static/root/assets/channels-settings-BpwVOvvf.js +0 -9
  92. package/dist/gateway/static/root/assets/channels-settings-BpwVOvvf.js.map +0 -1
  93. package/dist/gateway/static/root/assets/cron-page-C_6AbVRf.js +0 -2
  94. package/dist/gateway/static/root/assets/cron-page-C_6AbVRf.js.map +0 -1
  95. package/dist/gateway/static/root/assets/cron-utils-DZ7pabh5.js +0 -3
  96. package/dist/gateway/static/root/assets/cron-utils-DZ7pabh5.js.map +0 -1
  97. package/dist/gateway/static/root/assets/dist-UWGUW3x8.js +0 -2
  98. package/dist/gateway/static/root/assets/index-C6itMrqR.css +0 -1
  99. package/dist/gateway/static/root/assets/logs-page-BXqha2gI.js +0 -2
  100. package/dist/gateway/static/root/assets/logs-page-BXqha2gI.js.map +0 -1
  101. package/dist/gateway/static/root/assets/sessions-page-DJkuWpOT.js +0 -2
  102. package/dist/gateway/static/root/assets/settings-page-CleZrGHy.js +0 -2
  103. package/dist/gateway/static/root/assets/settings-page-CleZrGHy.js.map +0 -1
  104. package/dist/gateway/static/root/assets/skills-page-D7NiIOzA.js +0 -3
  105. package/dist/gateway/static/root/assets/skills-page-D7NiIOzA.js.map +0 -1
@@ -4,12 +4,14 @@ import { createLogger } from "../utils/logger/index.js";
4
4
  import { init_logger } from "../utils/logger.js";
5
5
  import { FILENAMES, init_paths, resolveSessionsDir } from "../config/paths.js";
6
6
  import { init_write_file_atomic, writeTextAtomic } from "../infra/write-file-atomic.js";
7
+ import { buildSessionContextForLlm, isTranscriptContextEntry, mergeLlmMessagesPreservingContextRows } from "./session-context-for-llm.js";
8
+ import { buildTranscriptEnvelope, parseStoredTranscriptJson } from "./transcript-format.js";
7
9
  import { invalidateSessionSearchIndexCache } from "./search-index-cache.js";
8
10
  import { resolveSessionShardRelativePath } from "./shard-path.js";
9
11
  import "./types.js";
10
12
  import { SessionCompactor } from "../agent/memory/compaction.js";
11
13
  import { SlidingWindow } from "../agent/memory/window.js";
12
- import { cleanTrailingErrors, hasProblematicMessages } from "../agent/memory/message-sanitizer.js";
14
+ import { normalizeCompactionCheckpointId } from "./compaction-checkpoints.js";
13
15
  import { basename, join } from "path";
14
16
  import { existsSync } from "fs";
15
17
  import { copyFile, mkdir, readFile, readdir, stat, unlink } from "fs/promises";
@@ -255,10 +257,18 @@ var SessionStore = class {
255
257
  return out;
256
258
  }
257
259
  async scanSessionFile(key) {
258
- const messages = await this.loadMessages(key);
259
- if (messages.length === 0) return null;
260
260
  const { jsonPath } = this.sessionPathsForKey(key);
261
+ let raw;
262
+ try {
263
+ raw = await readFile(jsonPath, "utf-8");
264
+ } catch {
265
+ return null;
266
+ }
267
+ const { messages, envelope } = parseStoredTranscriptJson(raw);
268
+ if (messages.length === 0) return null;
261
269
  const stats = await stat(jsonPath);
270
+ const sessionStartedAt = envelope?.createdAt ?? stats.birthtime.toISOString();
271
+ const lastInteractionAt = envelope?.updatedAt ?? stats.mtime.toISOString();
262
272
  const { channel, chatId } = this.parseSessionKey(key);
263
273
  const routing = this.extractRoutingFromKey(key, channel);
264
274
  const isCronSession = channel === "cron";
@@ -275,6 +285,9 @@ var SessionStore = class {
275
285
  compactedCount: 0,
276
286
  sourceChannel: channel,
277
287
  sourceChatId: chatId,
288
+ ...envelope?.id ? { transcriptId: envelope.id } : {},
289
+ sessionStartedAt,
290
+ lastInteractionAt,
278
291
  routing,
279
292
  ...isCronSession ? {
280
293
  sessionType: "cron",
@@ -352,15 +365,39 @@ var SessionStore = class {
352
365
  hasMore: offset + limit < total
353
366
  };
354
367
  }
355
- async get(key) {
368
+ async get(key, options) {
356
369
  const metadata = await this.getMetadata(key);
357
370
  if (!metadata) return null;
358
371
  const messages = await this.loadMessages(key);
372
+ let transcriptSummary;
373
+ if (options?.includeTranscriptSummary) {
374
+ const env = await this.loadTranscriptDocument(key);
375
+ if (env) transcriptSummary = {
376
+ id: env.id,
377
+ version: env.version,
378
+ createdAt: env.createdAt,
379
+ updatedAt: env.updatedAt,
380
+ compactionCount: env.compactions?.length ?? 0
381
+ };
382
+ }
383
+ let transcriptRows;
384
+ if (options?.includeTranscriptRows) transcriptRows = await this.loadTranscriptRows(key);
359
385
  return {
360
386
  ...metadata,
361
- messages: this.convertMessages(messages)
387
+ messages: this.convertMessages(messages),
388
+ ...transcriptSummary ? { transcriptSummary } : {},
389
+ ...transcriptRows !== void 0 ? { transcriptRows } : {}
362
390
  };
363
391
  }
392
+ /** Full on-disk transcript rows (LLM messages + optional `kind: 'context'`). */
393
+ async loadTranscriptRows(key) {
394
+ const { jsonPath } = this.sessionPathsForKey(key);
395
+ try {
396
+ return parseStoredTranscriptJson(await readFile(jsonPath, "utf-8")).rows;
397
+ } catch {
398
+ return [];
399
+ }
400
+ }
364
401
  async getMetadata(key) {
365
402
  const metadata = (await this.loadIndex()).sessions.find((s) => s.key === key);
366
403
  if (!metadata) {
@@ -458,17 +495,7 @@ var SessionStore = class {
458
495
  const primary = this.sessionPathsForKey(key);
459
496
  const readAndNormalize = async (path) => {
460
497
  try {
461
- const data = await readFile(path, "utf-8");
462
- const messages = JSON.parse(data);
463
- if (hasProblematicMessages(messages)) {
464
- const cleaned = cleanTrailingErrors(messages);
465
- if (cleaned.length !== messages.length) log.info({
466
- key,
467
- original: messages.length,
468
- cleaned: cleaned.length
469
- }, "Cleaned problematic messages on load");
470
- return cleaned;
471
- }
498
+ const { messages } = parseStoredTranscriptJson(await readFile(path, "utf-8"));
472
499
  return messages;
473
500
  } catch {
474
501
  return null;
@@ -484,6 +511,17 @@ var SessionStore = class {
484
511
  return [];
485
512
  }
486
513
  /**
514
+ * Load the versioned transcript document (stable id, compaction history), or null if missing/invalid.
515
+ */
516
+ async loadTranscriptDocument(key) {
517
+ const { jsonPath } = this.sessionPathsForKey(key);
518
+ try {
519
+ return parseStoredTranscriptJson(await readFile(jsonPath, "utf-8")).envelope;
520
+ } catch {
521
+ return null;
522
+ }
523
+ }
524
+ /**
487
525
  * Find the most recent archived session file for a given key.
488
526
  * Archived files have format: {safeKey}.{timestamp}.json
489
527
  */
@@ -505,11 +543,22 @@ var SessionStore = class {
505
543
  }
506
544
  /**
507
545
  * Persist transcript JSON + merge session row into the index. Caller must hold {@link runStoreMutation} (or be nested under it).
546
+ * Transcript is stored as a versioned document (pi-style header) with stable {@link XopcSessionTranscriptV1.id}.
508
547
  */
509
- async writeSessionTranscriptAndUpdateIndex(key, messages) {
548
+ async writeSessionTranscriptFromStoredRows(key, storedRows, options) {
510
549
  const { dir, jsonPath } = this.sessionPathsForKey(key);
511
550
  await mkdir(dir, { recursive: true });
512
- await writeTextAtomic(jsonPath, JSON.stringify(messages, null, 2));
551
+ let previous = null;
552
+ try {
553
+ previous = parseStoredTranscriptJson(await readFile(jsonPath, "utf-8")).envelope;
554
+ } catch {}
555
+ const doc = buildTranscriptEnvelope({
556
+ storedRows,
557
+ previous,
558
+ appendCompaction: options?.appendCompaction
559
+ });
560
+ await writeTextAtomic(jsonPath, JSON.stringify(doc, null, 2));
561
+ const llmMessages = buildSessionContextForLlm(storedRows);
513
562
  const index = await this.loadIndex();
514
563
  const existingIdx = index.sessions.findIndex((s) => s.key === key);
515
564
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -523,10 +572,13 @@ var SessionStore = class {
523
572
  ...prev,
524
573
  sourceChannel: channel,
525
574
  sourceChatId: chatId,
526
- messageCount: messages.length,
527
- estimatedTokens: this.estimateTokens(messages),
575
+ messageCount: llmMessages.length,
576
+ estimatedTokens: this.estimateTokens(llmMessages),
528
577
  updatedAt: now,
529
578
  lastAccessedAt: now,
579
+ transcriptId: doc.id,
580
+ sessionStartedAt: prev.sessionStartedAt ?? doc.createdAt,
581
+ lastInteractionAt: now,
530
582
  routing: routing || prev.routing,
531
583
  ...isCronSession ? {
532
584
  sessionType: "cron",
@@ -544,8 +596,8 @@ var SessionStore = class {
544
596
  } : {},
545
597
  stats: {
546
598
  ...prev.stats,
547
- messageCount: messages.length,
548
- tokenCount: this.estimateTokens(messages),
599
+ messageCount: llmMessages.length,
600
+ tokenCount: this.estimateTokens(llmMessages),
549
601
  lastTurnAt: Date.now()
550
602
  }
551
603
  };
@@ -556,8 +608,11 @@ var SessionStore = class {
556
608
  createdAt: now,
557
609
  updatedAt: now,
558
610
  lastAccessedAt: now,
559
- messageCount: messages.length,
560
- estimatedTokens: this.estimateTokens(messages),
611
+ transcriptId: doc.id,
612
+ sessionStartedAt: doc.createdAt,
613
+ lastInteractionAt: now,
614
+ messageCount: llmMessages.length,
615
+ estimatedTokens: this.estimateTokens(llmMessages),
561
616
  compactedCount: 0,
562
617
  sourceChannel: channel,
563
618
  sourceChatId: chatId,
@@ -571,8 +626,8 @@ var SessionStore = class {
571
626
  customData: { heartbeatTarget: chatId }
572
627
  } : {},
573
628
  stats: {
574
- messageCount: messages.length,
575
- tokenCount: this.estimateTokens(messages),
629
+ messageCount: llmMessages.length,
630
+ tokenCount: this.estimateTokens(llmMessages),
576
631
  lastTurnAt: Date.now()
577
632
  }
578
633
  });
@@ -580,6 +635,37 @@ var SessionStore = class {
580
635
  await this.saveIndex();
581
636
  invalidateSessionSearchIndexCache();
582
637
  }
638
+ async writeSessionTranscriptAndUpdateIndex(key, messages, options) {
639
+ const { jsonPath } = this.sessionPathsForKey(key);
640
+ let storedRows = messages;
641
+ try {
642
+ const parsed = parseStoredTranscriptJson(await readFile(jsonPath, "utf-8"));
643
+ if (parsed.rows.some((r) => isTranscriptContextEntry(r))) storedRows = mergeLlmMessagesPreservingContextRows(parsed.rows, messages);
644
+ } catch {}
645
+ await this.writeSessionTranscriptFromStoredRows(key, storedRows, options);
646
+ }
647
+ /**
648
+ * Append a persisted-only transcript row (`kind: 'context'`), visible on disk and in session search
649
+ * after stripping, but never returned from {@link loadMessages}.
650
+ */
651
+ async appendTranscriptContextEntry(key, entry) {
652
+ return this.runStoreMutation(async () => {
653
+ this.invalidateIndexCache();
654
+ const { jsonPath } = this.sessionPathsForKey(key);
655
+ let rows = [];
656
+ try {
657
+ rows = parseStoredTranscriptJson(await readFile(jsonPath, "utf-8")).rows;
658
+ } catch {}
659
+ const row = {
660
+ kind: "context",
661
+ id: typeof entry.id === "string" ? entry.id : void 0,
662
+ text: typeof entry.text === "string" ? entry.text : void 0,
663
+ data: entry.data,
664
+ createdAt: entry.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
665
+ };
666
+ await this.writeSessionTranscriptFromStoredRows(key, [...rows, row], {});
667
+ });
668
+ }
583
669
  async saveMessages(key, messages) {
584
670
  return this.runStoreMutation(async () => {
585
671
  this.invalidateIndexCache();
@@ -618,7 +704,13 @@ var SessionStore = class {
618
704
  this.invalidateIndexCache();
619
705
  const { jsonPath } = this.sessionPathsForKey(key);
620
706
  await this.captureCompactionCheckpointIfExists(key, jsonPath);
621
- await this.writeSessionTranscriptAndUpdateIndex(key, compacted);
707
+ await this.writeSessionTranscriptAndUpdateIndex(key, compacted, { appendCompaction: {
708
+ at: (/* @__PURE__ */ new Date()).toISOString(),
709
+ summary: result.summary,
710
+ firstKeptIndex: result.firstKeptIndex,
711
+ tokensBefore: result.tokensBefore,
712
+ tokensAfter: result.tokensAfter
713
+ } });
622
714
  const metadata = await this.getMetadata(key);
623
715
  if (metadata) await this.updateMetadata(key, { compactedCount: metadata.compactedCount + 1 });
624
716
  log.info({
@@ -651,6 +743,81 @@ var SessionStore = class {
651
743
  lastCompactionAt: void 0
652
744
  };
653
745
  }
746
+ /**
747
+ * List pre-compaction transcript snapshots for a session (newest first).
748
+ */
749
+ async listCompactionCheckpoints(key) {
750
+ const safeKey = this.sanitizeKey(key);
751
+ const dir = join(this.sessionsDir, resolveSessionShardRelativePath(key));
752
+ const prefix = this.checkpointBasenamePrefix(safeKey);
753
+ let names;
754
+ try {
755
+ names = await readdir(dir);
756
+ } catch {
757
+ return [];
758
+ }
759
+ const files = names.filter((n) => n.startsWith(prefix) && n.endsWith(".json"));
760
+ const valid = (await Promise.all(files.map(async (name) => {
761
+ const p = join(dir, name);
762
+ try {
763
+ const s = await stat(p);
764
+ const id = name.slice(prefix.length, -5);
765
+ if (!normalizeCompactionCheckpointId(id)) return null;
766
+ return {
767
+ id: normalizeCompactionCheckpointId(id),
768
+ sizeBytes: s.size,
769
+ modifiedAt: new Date(s.mtimeMs).toISOString()
770
+ };
771
+ } catch {
772
+ return null;
773
+ }
774
+ }))).filter((r) => r !== null);
775
+ valid.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
776
+ return valid;
777
+ }
778
+ /**
779
+ * Metadata for a single compaction checkpoint file.
780
+ */
781
+ async getCompactionCheckpointDetail(key, checkpointId) {
782
+ const id = normalizeCompactionCheckpointId(checkpointId);
783
+ if (!id) return null;
784
+ const safeKey = this.sanitizeKey(key);
785
+ const cpPath = join(join(this.sessionsDir, resolveSessionShardRelativePath(key)), `${this.checkpointBasenamePrefix(safeKey)}${id}.json`);
786
+ if (!existsSync(cpPath)) return null;
787
+ try {
788
+ const { messages } = parseStoredTranscriptJson(await readFile(cpPath, "utf-8"));
789
+ const s = await stat(cpPath);
790
+ return {
791
+ id,
792
+ sizeBytes: s.size,
793
+ modifiedAt: new Date(s.mtimeMs).toISOString(),
794
+ messageCount: messages.length
795
+ };
796
+ } catch {
797
+ return null;
798
+ }
799
+ }
800
+ /**
801
+ * Restore main transcript from a pre-compaction snapshot (then re-wrap + index sync).
802
+ * Does not delete the checkpoint file.
803
+ */
804
+ async restoreCompactionCheckpoint(key, checkpointId) {
805
+ const id = normalizeCompactionCheckpointId(checkpointId);
806
+ if (!id) throw new Error("Invalid checkpoint id");
807
+ return this.runStoreMutation(async () => {
808
+ const safeKey = this.sanitizeKey(key);
809
+ const cpPath = join(join(this.sessionsDir, resolveSessionShardRelativePath(key)), `${this.checkpointBasenamePrefix(safeKey)}${id}.json`);
810
+ if (!existsSync(cpPath)) throw new Error(`Checkpoint not found: ${id}`);
811
+ const { jsonPath } = this.sessionPathsForKey(key);
812
+ await copyFile(cpPath, jsonPath);
813
+ const messages = await this.loadMessages(key);
814
+ await this.writeSessionTranscriptAndUpdateIndex(key, messages);
815
+ log.info({
816
+ key,
817
+ checkpointId: id
818
+ }, "Session transcript restored from compaction checkpoint");
819
+ });
820
+ }
654
821
  /** Alias for delete */
655
822
  async deleteSession(key) {
656
823
  return this.delete(key);
@@ -674,15 +841,20 @@ var SessionStore = class {
674
841
  return this.extractTextContent(m.content).toLowerCase().includes(keywordLower);
675
842
  }));
676
843
  }
844
+ /**
845
+ * JSON export includes API-shaped `messages` (LLM-only) plus `transcriptRows` (full on-disk order).
846
+ */
677
847
  async exportSession(key, format) {
678
848
  const detail = await this.get(key);
679
849
  if (!detail) throw new Error(`Session not found: ${key}`);
680
850
  if (format === "json") {
851
+ const transcriptRows = await this.loadTranscriptRows(key);
681
852
  const exportData = {
682
853
  version: INDEX_VERSION,
683
854
  exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
684
855
  metadata: detail,
685
- messages: detail.messages
856
+ messages: detail.messages,
857
+ transcriptRows
686
858
  };
687
859
  return JSON.stringify(exportData, null, 2);
688
860
  } else {