chatlab-cli 0.27.0 → 0.27.1

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 (65) hide show
  1. package/dist/{chat-command-SKGPS3PG.mjs → chat-command-B7POB3C3.mjs} +5 -4
  2. package/dist/{chat-command-SKGPS3PG.mjs.map → chat-command-B7POB3C3.mjs.map} +1 -1
  3. package/dist/{chunk-MJITS5CE.mjs → chunk-3FUF3PEY.mjs} +15 -13
  4. package/dist/{chunk-MJITS5CE.mjs.map → chunk-3FUF3PEY.mjs.map} +1 -1
  5. package/dist/{chunk-FHLPXRDL.mjs → chunk-3JNVGW62.mjs} +2 -2
  6. package/dist/{chunk-KCFVHVO4.mjs → chunk-I7BHOZPX.mjs} +11202 -17966
  7. package/dist/chunk-I7BHOZPX.mjs.map +1 -0
  8. package/dist/chunk-N3G43SCQ.mjs +7337 -0
  9. package/dist/chunk-N3G43SCQ.mjs.map +1 -0
  10. package/dist/{chunk-7LPVQR77.mjs → chunk-RJ4D4QWK.mjs} +4 -4
  11. package/dist/chunk-RJ4D4QWK.mjs.map +1 -0
  12. package/dist/{chunk-6FXGC6HE.mjs → chunk-SAE2H2LN.mjs} +7 -5
  13. package/dist/{chunk-6FXGC6HE.mjs.map → chunk-SAE2H2LN.mjs.map} +1 -1
  14. package/dist/{chunk-3HOTLLFC.mjs → chunk-SIVERPF5.mjs} +215 -130
  15. package/dist/chunk-SIVERPF5.mjs.map +1 -0
  16. package/dist/{chunk-M6ABWESL.mjs → chunk-WQMKKUSD.mjs} +5 -5
  17. package/dist/chunk-WQMKKUSD.mjs.map +1 -0
  18. package/dist/{chunk-UBJHT3BU.mjs → chunk-YUFYTDRH.mjs} +6 -4
  19. package/dist/{chunk-UBJHT3BU.mjs.map → chunk-YUFYTDRH.mjs.map} +1 -1
  20. package/dist/cli.mjs +4 -3
  21. package/dist/{http-CPFUTIUB.mjs → http-A54ZBBAY.mjs} +9 -8
  22. package/dist/{import-UJXKIKYN.mjs → import-EA3TB3S2.mjs} +5 -4
  23. package/dist/index.mjs +9 -8
  24. package/dist/{mcp-SW4DTCWJ.mjs → mcp-DJDOWHH3.mjs} +8 -6
  25. package/dist/{mcp-SW4DTCWJ.mjs.map → mcp-DJDOWHH3.mjs.map} +1 -1
  26. package/dist/semantic-index-worker.mjs +34 -0
  27. package/dist/semantic-index-worker.mjs.map +1 -0
  28. package/dist/{src-M3MO3LEL.mjs → src-EKPMJRG6.mjs} +57 -41
  29. package/dist/{stream-import-4VDJZHMY.mjs → stream-import-FQFNRQ2B.mjs} +4 -3
  30. package/dist/worker-runtime-UH2OBO3T.mjs +71 -0
  31. package/dist/worker-runtime-UH2OBO3T.mjs.map +1 -0
  32. package/dist-web/assets/{DebugToolsPanel-DzZmSzIU.css → DebugToolsPanel-DFw6_G5V.css} +1 -1
  33. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-NL1sTF6i.js +110 -0
  34. package/dist-web/assets/{MessageExportModal-DCB-mzf2.js → MessageExportModal-B60rvfCh.js} +1 -1
  35. package/dist-web/assets/{electron-DDUv0ohD.js → electron-BRUg9LnY.js} +1 -1
  36. package/dist-web/assets/{fetch-C1aC5Fd1.js → fetch-BIpjJHxj.js} +1 -1
  37. package/dist-web/assets/{fetch-D8Ng59an.js → fetch-BRsMdFcn.js} +1 -1
  38. package/dist-web/assets/{fetch-Ckang-F8.js → fetch-C-m3IysH.js} +1 -1
  39. package/dist-web/assets/{fetch-Ck4rGxkO.js → fetch-Cma-mZjd.js} +1 -1
  40. package/dist-web/assets/{fetch-Bry8iJpq.js → fetch-DE8gj5Ke.js} +1 -1
  41. package/dist-web/assets/{fetch-C69hj3wS.js → fetch-DLAp7h55.js} +1 -1
  42. package/dist-web/assets/{fetch-B9Oy0bcD.js → fetch-DNSaxRnL.js} +1 -1
  43. package/dist-web/assets/{fetch-B3gsbY_U.js → fetch-DOEbyLSD.js} +1 -1
  44. package/dist-web/assets/{fetch-CwHncc6x.js → fetch-DSN-QYZE.js} +1 -1
  45. package/dist-web/assets/{fetch-DOtBAxUS.js → fetch-woAa3H2L.js} +1 -1
  46. package/dist-web/assets/{index-C16V6EoP.js → index-B3mDjury.js} +9 -9
  47. package/dist-web/assets/{index-D-J8dY7w.js → index-B9I-Y3fR.js} +1 -1
  48. package/dist-web/assets/index-GAU7LI1r.js +173 -0
  49. package/dist-web/assets/{index-zAi37ZQ4.js → index-iwqJDVMJ.js} +2 -2
  50. package/dist-web/assets/{index-BfFj8Evo.js → index-o6veAyTB.js} +3 -3
  51. package/dist-web/assets/{index-Bd9j73nw.js → index-r_zHR2t9.js} +1 -1
  52. package/dist-web/assets/{web-DKFeBb_F.js → web-C2UtGskd.js} +1 -1
  53. package/dist-web/index.html +1 -1
  54. package/package.json +1 -1
  55. package/dist/chunk-3HOTLLFC.mjs.map +0 -1
  56. package/dist/chunk-7LPVQR77.mjs.map +0 -1
  57. package/dist/chunk-KCFVHVO4.mjs.map +0 -1
  58. package/dist/chunk-M6ABWESL.mjs.map +0 -1
  59. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-C8mwzxDa.js +0 -110
  60. package/dist-web/assets/index-B40SKtW8.js +0 -173
  61. /package/dist/{chunk-FHLPXRDL.mjs.map → chunk-3JNVGW62.mjs.map} +0 -0
  62. /package/dist/{http-CPFUTIUB.mjs.map → http-A54ZBBAY.mjs.map} +0 -0
  63. /package/dist/{import-UJXKIKYN.mjs.map → import-EA3TB3S2.mjs.map} +0 -0
  64. /package/dist/{src-M3MO3LEL.mjs.map → src-EKPMJRG6.mjs.map} +0 -0
  65. /package/dist/{stream-import-4VDJZHMY.mjs.map → stream-import-FQFNRQ2B.mjs.map} +0 -0
@@ -8,30 +8,52 @@ import {
8
8
  closeServerAiLogger,
9
9
  createCliRunAgentStream,
10
10
  initServerAiLogger
11
- } from "./chunk-7LPVQR77.mjs";
12
- import {
13
- importData,
14
- parseFile
15
- } from "./chunk-UBJHT3BU.mjs";
16
- import {
17
- analyzeIncrementalImport,
18
- analyzeNewImport,
19
- incrementalImport,
20
- streamImport
21
- } from "./chunk-6FXGC6HE.mjs";
11
+ } from "./chunk-RJ4D4QWK.mjs";
22
12
  import {
23
13
  AGENT_TOOL_REGISTRY,
24
14
  CoreDataProvider
25
- } from "./chunk-M6ABWESL.mjs";
15
+ } from "./chunk-WQMKKUSD.mjs";
26
16
  import {
27
17
  assertCliDataDirCompatible
28
- } from "./chunk-FHLPXRDL.mjs";
18
+ } from "./chunk-3JNVGW62.mjs";
29
19
  import {
30
20
  getVersion
31
21
  } from "./chunk-5ADKYYNA.mjs";
32
22
  import {
33
23
  resolveCliPath
34
24
  } from "./chunk-FJ35V3TE.mjs";
25
+ import {
26
+ importData,
27
+ parseFile
28
+ } from "./chunk-YUFYTDRH.mjs";
29
+ import {
30
+ analyzeIncrementalImport,
31
+ analyzeNewImport,
32
+ incrementalImport,
33
+ streamImport
34
+ } from "./chunk-SAE2H2LN.mjs";
35
+ import {
36
+ NodePathProvider,
37
+ PreferencesManager,
38
+ applyPendingNodeDataDirMigrationIfNeeded,
39
+ buildMergedOutput,
40
+ checkConflictsFromSources,
41
+ createJiebaNlpProvider,
42
+ createNodeDataDirSwitch,
43
+ createSemanticIndexWorkerRuntimeClient,
44
+ detectAllFormats,
45
+ detectFormat,
46
+ findEntryFileInDirectory,
47
+ getDbFileVersion,
48
+ getDefaultNodeUserDataDir,
49
+ getOrComputeAnalysisCache,
50
+ getPendingNodeDataDirMigration,
51
+ getSupportedFormats,
52
+ hasPendingElectronDataWarning,
53
+ scanMultiChatFile,
54
+ serializeChatLabToJsonl,
55
+ streamParseFileInfo
56
+ } from "./chunk-N3G43SCQ.mjs";
35
57
  import {
36
58
  AIChatManager,
37
59
  ALL_MIGRATIONS,
@@ -46,24 +68,16 @@ import {
46
68
  LLMConfigStore,
47
69
  MergeSessionCache,
48
70
  MigrationRunner,
49
- NodePathProvider,
50
71
  POS_TAG_DEFINITIONS,
51
- PreferencesManager,
72
+ SEMANTIC_INDEX_CONFIG_FILE,
73
+ SemanticIndexConfigStore,
52
74
  SkillManagerCore,
53
75
  TempDbWriter,
54
- applyPendingNodeDataDirMigrationIfNeeded,
55
- buildMergedOutput,
56
76
  buildPiModel,
57
- checkConflictsFromSources,
58
77
  computeWordFrequency,
59
78
  countMessagesTokens,
60
79
  createDatabaseManagerAdapter,
61
- createJiebaNlpProvider,
62
- createNodeDataDirSwitch,
63
- createSemanticIndexService,
64
80
  deleteDict,
65
- detectAllFormats,
66
- detectFormat,
67
81
  downloadDict,
68
82
  ensureDefaultDict,
69
83
  executeReadonlySql,
@@ -71,15 +85,12 @@ import {
71
85
  exportSessionToJson,
72
86
  export_service_exports,
73
87
  fetchRemoteModels,
74
- findEntryFileInDirectory,
75
88
  getAvailableYears,
76
89
  getBuiltinModelsByProvider,
77
90
  getCatchphraseAnalysis,
78
91
  getCheckInAnalysis,
79
92
  getClusterGraph,
80
93
  getDailyActivity,
81
- getDbFileVersion,
82
- getDefaultNodeUserDataDir,
83
94
  getDefaultRulesForLocale,
84
95
  getDictList,
85
96
  getDivingAnalysis,
@@ -99,44 +110,40 @@ import {
99
110
  getMessageTypeStats,
100
111
  getMonthlyActivity,
101
112
  getNightOwlAnalysis,
102
- getOrComputeAnalysisCache,
103
- getPendingNodeDataDirMigration,
104
113
  getRelationshipStats,
105
114
  getRepeatAnalysis,
106
115
  getSchemaDetailed,
107
116
  getSessionInfo,
108
117
  getSessionMeta,
109
118
  getSessionOverview,
110
- getSupportedFormats,
111
119
  getTextLengthPercentiles,
112
120
  getTextStats,
113
121
  getTimeRange,
114
122
  getWeekdayActivity,
115
123
  getYearlyActivity,
116
- hasPendingElectronDataWarning,
117
124
  initNlpDir,
118
125
  isDictDownloaded,
119
126
  isNewerStableVersion,
127
+ isSemanticIndexConfigured,
120
128
  loadConfig,
121
129
  member_service_exports,
122
130
  mergeRulesForLocale,
123
131
  owner_profile_service_exports,
132
+ persistSemanticIndexConfig,
124
133
  queryMessages,
125
134
  resolveApiKey,
135
+ resolveSemanticIndexApiKeySet,
126
136
  runSimpleLlmStream,
127
- scanMultiChatFile,
128
137
  segmentText,
129
- serializeChatLabToJsonl,
130
138
  session_index_service_exports,
131
139
  session_service_exports,
132
- streamParseFileInfo,
133
140
  stripAvatarFields,
134
141
  summary_service_exports,
135
142
  validateApiKey,
136
143
  verifyCliDataPath,
137
144
  writeAuthProfile,
138
145
  writeConfigField
139
- } from "./chunk-KCFVHVO4.mjs";
146
+ } from "./chunk-I7BHOZPX.mjs";
140
147
 
141
148
  // src/http/index.ts
142
149
  import * as fs11 from "fs";
@@ -1618,34 +1625,63 @@ function registerAiSummaryRoutes(server2, ctx) {
1618
1625
  }
1619
1626
 
1620
1627
  // ../../packages/http-routes/src/routes/web/ai-semantic-index.ts
1628
+ import path4 from "path";
1621
1629
  function registerSemanticIndexRoutes(server2, ctx) {
1622
1630
  const service = ctx.semanticIndexService;
1623
- if (!service) return;
1631
+ if (!service) {
1632
+ if (ctx.aiDataDir) {
1633
+ const configStore = new SemanticIndexConfigStore(path4.join(ctx.aiDataDir, SEMANTIC_INDEX_CONFIG_FILE));
1634
+ const configResponse = (config) => ({
1635
+ config,
1636
+ apiKeySet: resolveSemanticIndexApiKeySet(config, ctx.resolveApiKey),
1637
+ configured: isSemanticIndexConfigured(config)
1638
+ });
1639
+ server2.get("/_web/ai/semantic-index/config", async () => configResponse(configStore.get()));
1640
+ server2.put(
1641
+ "/_web/ai/semantic-index/config",
1642
+ async (request) => {
1643
+ const config = persistSemanticIndexConfig(configStore, request.body.config, {
1644
+ apiKey: request.body.apiKey,
1645
+ writeAuthProfile: ctx.writeAuthProfile
1646
+ });
1647
+ return configResponse(config);
1648
+ }
1649
+ );
1650
+ }
1651
+ server2.get("/_web/ai/semantic-index/enabled", async () => ({ sessions: [] }));
1652
+ server2.get("/_web/ai/semantic-index/status", async () => ({ status: null }));
1653
+ server2.post("/_web/ai/semantic-index/status", async () => ({ sessions: [] }));
1654
+ return;
1655
+ }
1624
1656
  server2.get("/_web/ai/semantic-index/config", async () => {
1625
- return { config: service.getConfig(), apiKeySet: service.hasApiKey(), configured: service.isConfigured() };
1657
+ return {
1658
+ config: await service.getConfig(),
1659
+ apiKeySet: await service.hasApiKey(),
1660
+ configured: await service.isConfigured()
1661
+ };
1626
1662
  });
1627
1663
  server2.put(
1628
1664
  "/_web/ai/semantic-index/config",
1629
1665
  async (request) => {
1630
- const config = service.setConfig(request.body.config, { apiKey: request.body.apiKey });
1631
- return { config, apiKeySet: service.hasApiKey(), configured: service.isConfigured() };
1666
+ const config = await service.setConfig(request.body.config, { apiKey: request.body.apiKey });
1667
+ return { config, apiKeySet: await service.hasApiKey(), configured: await service.isConfigured() };
1632
1668
  }
1633
1669
  );
1634
1670
  server2.get("/_web/ai/semantic-index/enabled", async () => {
1635
- return { sessions: service.listEnabledStatuses() };
1671
+ return { sessions: await service.listEnabledStatuses() };
1636
1672
  });
1637
1673
  server2.get("/_web/ai/semantic-index/status", async (request) => {
1638
- return { status: service.status(request.query.sessionId) };
1674
+ return { status: await service.status(request.query.sessionId) };
1639
1675
  });
1640
1676
  server2.post("/_web/ai/semantic-index/status", async (request) => {
1641
- return { sessions: service.statusForSessions(request.body.sessionIds ?? []) };
1677
+ return { sessions: await service.statusForSessions(request.body.sessionIds ?? []) };
1642
1678
  });
1643
- const sessionAction = (path12, action) => {
1644
- server2.post(path12, async (request, reply) => {
1679
+ const sessionAction = (path13, action) => {
1680
+ server2.post(path13, async (request, reply) => {
1645
1681
  const { sessionId } = request.body;
1646
1682
  if (!sessionId) return reply.code(400).send({ error: "sessionId is required" });
1647
1683
  await action(sessionId);
1648
- return { status: service.status(sessionId) };
1684
+ return { status: await service.status(sessionId) };
1649
1685
  });
1650
1686
  };
1651
1687
  sessionAction("/_web/ai/semantic-index/enable", (id) => service.enable(id));
@@ -1655,11 +1691,11 @@ function registerSemanticIndexRoutes(server2, ctx) {
1655
1691
  sessionAction("/_web/ai/semantic-index/cancel", (id) => service.cancel(id));
1656
1692
  sessionAction("/_web/ai/semantic-index/rebuild", (id) => service.rebuild(id));
1657
1693
  server2.post("/_web/ai/semantic-index/build-pending", async () => {
1658
- service.buildAllPending();
1659
- return { sessions: service.listEnabledStatuses() };
1694
+ await service.buildAllPending();
1695
+ return { sessions: await service.listEnabledStatuses() };
1660
1696
  });
1661
1697
  server2.post("/_web/ai/semantic-index/cleanup", async () => {
1662
- return service.cleanupUnused();
1698
+ return await service.cleanupUnused();
1663
1699
  });
1664
1700
  server2.post(
1665
1701
  "/_web/ai/semantic-index/search",
@@ -1674,7 +1710,7 @@ function registerSemanticIndexRoutes(server2, ctx) {
1674
1710
  // ../../packages/http-routes/src/routes/web/merge.ts
1675
1711
  import * as fs3 from "fs";
1676
1712
  import * as os from "os";
1677
- import * as path4 from "path";
1713
+ import * as path5 from "path";
1678
1714
  function ensureDb2(ctx, sessionId) {
1679
1715
  const db = ctx.dbManager.open(sessionId);
1680
1716
  if (!db) throw sessionNotFound(sessionId);
@@ -1691,8 +1727,8 @@ function registerMergeRoutes(server2, ctx) {
1691
1727
  if (contentType.includes("multipart/form-data")) {
1692
1728
  const data = await request.file();
1693
1729
  if (!data) return reply.code(400).send({ error: "No file uploaded" });
1694
- const tmpDir = fs3.mkdtempSync(path4.join(os.tmpdir(), "chatlab-merge-"));
1695
- const tmpPath = path4.join(tmpDir, data.filename || "upload");
1730
+ const tmpDir = fs3.mkdtempSync(path5.join(os.tmpdir(), "chatlab-merge-"));
1731
+ const tmpPath = path5.join(tmpDir, data.filename || "upload");
1696
1732
  const chunks = [];
1697
1733
  for await (const chunk of data.file) {
1698
1734
  chunks.push(chunk);
@@ -1710,12 +1746,12 @@ function registerMergeRoutes(server2, ctx) {
1710
1746
  try {
1711
1747
  const result = await streamParseFileInfo(filePath, {
1712
1748
  createTempDatabase(sourceFilePath) {
1713
- return mergeCache.createTempDatabase(path4.basename(sourceFilePath));
1749
+ return mergeCache.createTempDatabase(path5.basename(sourceFilePath));
1714
1750
  },
1715
1751
  onProgress() {
1716
1752
  }
1717
1753
  });
1718
- const filename = path4.basename(filePath);
1754
+ const filename = path5.basename(filePath);
1719
1755
  const handle = mergeCache.store(filename, result.tempDbPath);
1720
1756
  return {
1721
1757
  handle,
@@ -1784,9 +1820,9 @@ function registerMergeRoutes(server2, ctx) {
1784
1820
  return reply.code(501).send({ error: "Import not available on this server" });
1785
1821
  }
1786
1822
  const jsonData = JSON.stringify(merged.chatLabData);
1787
- const tmpDir = fs3.mkdtempSync(path4.join(os.tmpdir(), "chatlab-merged-"));
1788
- const safeName = path4.basename(outputName).replace(/[/\\?%*:|"<>]/g, "_") || "merged";
1789
- const tmpPath = path4.join(tmpDir, `${safeName}.json`);
1823
+ const tmpDir = fs3.mkdtempSync(path5.join(os.tmpdir(), "chatlab-merged-"));
1824
+ const safeName = path5.basename(outputName).replace(/[/\\?%*:|"<>]/g, "_") || "merged";
1825
+ const tmpPath = path5.join(tmpDir, `${safeName}.json`);
1790
1826
  fs3.writeFileSync(tmpPath, jsonData, "utf-8");
1791
1827
  try {
1792
1828
  const importResult = await streamImport2(dbManager2, tmpPath);
@@ -1873,14 +1909,14 @@ function registerMergeRoutes(server2, ctx) {
1873
1909
  // ../../packages/http-routes/src/routes/web/cache.ts
1874
1910
  import * as fs4 from "fs";
1875
1911
  import * as fsp from "fs/promises";
1876
- import * as path5 from "path";
1912
+ import * as path6 from "path";
1877
1913
  async function getDirSize(dirPath) {
1878
1914
  let totalSize = 0;
1879
1915
  try {
1880
1916
  if (!fs4.existsSync(dirPath)) return 0;
1881
1917
  const files = await fsp.readdir(dirPath, { withFileTypes: true });
1882
1918
  for (const file of files) {
1883
- const filePath = path5.join(dirPath, file.name);
1919
+ const filePath = path6.join(dirPath, file.name);
1884
1920
  if (file.isDirectory()) {
1885
1921
  totalSize += await getDirSize(filePath);
1886
1922
  } else {
@@ -1898,7 +1934,7 @@ async function getFileCount(dirPath) {
1898
1934
  if (!fs4.existsSync(dirPath)) return 0;
1899
1935
  const files = await fsp.readdir(dirPath, { withFileTypes: true });
1900
1936
  for (const file of files) {
1901
- const filePath = path5.join(dirPath, file.name);
1937
+ const filePath = path6.join(dirPath, file.name);
1902
1938
  if (file.isDirectory()) {
1903
1939
  count += await getFileCount(filePath);
1904
1940
  } else {
@@ -1911,8 +1947,8 @@ async function getFileCount(dirPath) {
1911
1947
  }
1912
1948
  function hasChatLabDatabases(dirPath) {
1913
1949
  try {
1914
- const markerPath = path5.join(dirPath, ".chatlab");
1915
- const dbDir = path5.join(dirPath, "databases");
1950
+ const markerPath = path6.join(dirPath, ".chatlab");
1951
+ const dbDir = path6.join(dirPath, "databases");
1916
1952
  if (!fs4.existsSync(markerPath) || !fs4.existsSync(dbDir)) return false;
1917
1953
  return fs4.readdirSync(dbDir).some((file) => file.endsWith(".db"));
1918
1954
  } catch {
@@ -1980,7 +2016,7 @@ function registerCacheRoutes(server2, ctx) {
1980
2016
  if (!fs4.existsSync(dirPath)) return { success: true };
1981
2017
  const files = await fsp.readdir(dirPath);
1982
2018
  for (const file of files) {
1983
- const filePath = path5.join(dirPath, file);
2019
+ const filePath = path6.join(dirPath, file);
1984
2020
  const stat2 = await fsp.stat(filePath);
1985
2021
  if (stat2.isDirectory()) {
1986
2022
  await fsp.rm(filePath, { recursive: true });
@@ -1997,7 +2033,7 @@ function registerCacheRoutes(server2, ctx) {
1997
2033
  defaultPath: ctx.defaultUserDataDir,
1998
2034
  isCustom: ctx.isCustomDataDir ?? false,
1999
2035
  canSetDataDir: ctx.canSetDataDir ?? Boolean(ctx.setDataDir),
2000
- hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) && path5.resolve(pp.getUserDataDir()) !== path5.resolve(ctx.defaultUserDataDir ?? "") && hasChatLabDatabases(ctx.defaultUserDataDir ?? ""),
2036
+ hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) && path6.resolve(pp.getUserDataDir()) !== path6.resolve(ctx.defaultUserDataDir ?? "") && hasChatLabDatabases(ctx.defaultUserDataDir ?? ""),
2001
2037
  pendingMigration: pending ? {
2002
2038
  from: pending.from,
2003
2039
  to: pending.to,
@@ -2014,7 +2050,7 @@ function registerCacheRoutes(server2, ctx) {
2014
2050
  return ctx.setDataDir(targetPath, migrate);
2015
2051
  });
2016
2052
  server2.get("/_web/cache/latest-import-log", async () => {
2017
- const importLogDir = path5.join(pp.getLogsDir(), "import");
2053
+ const importLogDir = path6.join(pp.getLogsDir(), "import");
2018
2054
  if (!fs4.existsSync(importLogDir)) {
2019
2055
  return { success: false, error: "Log directory not found" };
2020
2056
  }
@@ -2025,7 +2061,7 @@ function registerCacheRoutes(server2, ctx) {
2025
2061
  }
2026
2062
  const fileStats = await Promise.all(
2027
2063
  logFiles.map(async (f) => {
2028
- const filePath = path5.join(importLogDir, f);
2064
+ const filePath = path6.join(importLogDir, f);
2029
2065
  const stat2 = await fsp.stat(filePath);
2030
2066
  return { name: f, path: filePath, mtime: stat2.mtime.getTime() };
2031
2067
  })
@@ -2052,7 +2088,7 @@ function registerCacheRoutes(server2, ctx) {
2052
2088
  if (!fs4.existsSync(downloadsDir)) {
2053
2089
  fs4.mkdirSync(downloadsDir, { recursive: true });
2054
2090
  }
2055
- const filePath = path5.join(downloadsDir, filename);
2091
+ const filePath = path6.join(downloadsDir, filename);
2056
2092
  fs4.writeFileSync(filePath, buffer);
2057
2093
  return { success: true, filePath };
2058
2094
  });
@@ -2207,12 +2243,12 @@ function createServer() {
2207
2243
 
2208
2244
  // src/http/routes/web/index.ts
2209
2245
  import * as os3 from "os";
2210
- import * as path8 from "path";
2246
+ import * as path9 from "path";
2211
2247
 
2212
2248
  // src/http/routes/web/import.ts
2213
2249
  import * as fs6 from "fs";
2214
2250
  import * as os2 from "os";
2215
- import * as path6 from "path";
2251
+ import * as path7 from "path";
2216
2252
 
2217
2253
  // src/http/routes/web/helpers.ts
2218
2254
  import * as fs5 from "fs";
@@ -2245,8 +2281,8 @@ function registerImportRoutes(server2, dbManager2) {
2245
2281
  server2.post("/_web/detect-format", async (request, reply) => {
2246
2282
  const data = await request.file();
2247
2283
  if (!data) return reply.code(400).send({ error: "No file uploaded" });
2248
- const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-detect-"));
2249
- const tmpPath = path6.join(tmpDir, data.filename || "upload");
2284
+ const tmpDir = fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-detect-"));
2285
+ const tmpPath = path7.join(tmpDir, data.filename || "upload");
2250
2286
  try {
2251
2287
  const chunks = [];
2252
2288
  for await (const chunk of data.file) {
@@ -2263,8 +2299,8 @@ function registerImportRoutes(server2, dbManager2) {
2263
2299
  server2.post("/_web/scan-multi-chat", async (request, reply) => {
2264
2300
  const data = await request.file();
2265
2301
  if (!data) return reply.code(400).send({ error: "No file uploaded" });
2266
- const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-scan-"));
2267
- const tmpPath = path6.join(tmpDir, data.filename || "upload");
2302
+ const tmpDir = fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-scan-"));
2303
+ const tmpPath = path7.join(tmpDir, data.filename || "upload");
2268
2304
  try {
2269
2305
  const chunks = [];
2270
2306
  for await (const chunk of data.file) {
@@ -2280,8 +2316,8 @@ function registerImportRoutes(server2, dbManager2) {
2280
2316
  server2.post("/_web/import", async (request, reply) => {
2281
2317
  const data = await request.file();
2282
2318
  if (!data) return reply.code(400).send({ error: "No file uploaded" });
2283
- const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-import-"));
2284
- const tmpPath = path6.join(tmpDir, data.filename || "upload");
2319
+ const tmpDir = fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-import-"));
2320
+ const tmpPath = path7.join(tmpDir, data.filename || "upload");
2285
2321
  const chunks = [];
2286
2322
  for await (const chunk of data.file) {
2287
2323
  chunks.push(chunk);
@@ -2329,7 +2365,7 @@ data: ${JSON.stringify(eventData)}
2329
2365
  server2.post("/_web/import-directory", async (request, reply) => {
2330
2366
  const parts = request.parts();
2331
2367
  if (!parts) return reply.code(400).send({ error: "No files uploaded" });
2332
- const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-dir-import-"));
2368
+ const tmpDir = fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-dir-import-"));
2333
2369
  const relativePaths = [];
2334
2370
  const fileBuffers = [];
2335
2371
  try {
@@ -2357,9 +2393,9 @@ data: ${JSON.stringify(eventData)}
2357
2393
  if (segments.length > 1) {
2358
2394
  relPath = segments.slice(1).join("/");
2359
2395
  }
2360
- const targetPath = path6.resolve(tmpDir, relPath);
2361
- if (!targetPath.startsWith(tmpDir + path6.sep)) continue;
2362
- fs6.mkdirSync(path6.dirname(targetPath), { recursive: true });
2396
+ const targetPath = path7.resolve(tmpDir, relPath);
2397
+ if (!targetPath.startsWith(tmpDir + path7.sep)) continue;
2398
+ fs6.mkdirSync(path7.dirname(targetPath), { recursive: true });
2363
2399
  fs6.writeFileSync(targetPath, fileBuffers[i].data);
2364
2400
  }
2365
2401
  const entryPath = findEntryFileInDirectory(tmpDir);
@@ -2405,8 +2441,8 @@ data: ${JSON.stringify({ success: false, error: err instanceof Error ? err.messa
2405
2441
  const sessionId = request.params.id;
2406
2442
  const data = await request.file();
2407
2443
  if (!data) return reply.code(400).send({ error: "No file uploaded" });
2408
- const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-inc-"));
2409
- const tmpPath = path6.join(tmpDir, data.filename || "upload");
2444
+ const tmpDir = fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-inc-"));
2445
+ const tmpPath = path7.join(tmpDir, data.filename || "upload");
2410
2446
  const chunks = [];
2411
2447
  for await (const chunk of data.file) {
2412
2448
  chunks.push(chunk);
@@ -2443,8 +2479,8 @@ data: ${JSON.stringify(eventData)}
2443
2479
  const sessionId = request.params.id;
2444
2480
  const data = await request.file();
2445
2481
  if (!data) return reply.code(400).send({ error: "No file uploaded" });
2446
- const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-analyze-"));
2447
- const tmpPath = path6.join(tmpDir, data.filename || "upload");
2482
+ const tmpDir = fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-analyze-"));
2483
+ const tmpPath = path7.join(tmpDir, data.filename || "upload");
2448
2484
  const chunks = [];
2449
2485
  for await (const chunk of data.file) {
2450
2486
  chunks.push(chunk);
@@ -2461,8 +2497,8 @@ data: ${JSON.stringify(eventData)}
2461
2497
  server2.post("/_web/import/analyze", async (request, reply) => {
2462
2498
  const data = await request.file();
2463
2499
  if (!data) return reply.code(400).send({ error: "No file uploaded" });
2464
- const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-analyze-"));
2465
- const tmpPath = path6.join(tmpDir, data.filename || "upload");
2500
+ const tmpDir = fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-analyze-"));
2501
+ const tmpPath = path7.join(tmpDir, data.filename || "upload");
2466
2502
  const chunks = [];
2467
2503
  for await (const chunk of data.file) {
2468
2504
  chunks.push(chunk);
@@ -2497,12 +2533,12 @@ data: ${JSON.stringify(eventData)}
2497
2533
 
2498
2534
  `);
2499
2535
  }
2500
- const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-demo-"));
2536
+ const tmpDir = fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-demo-"));
2501
2537
  try {
2502
2538
  const localPaths = [];
2503
2539
  for (let i = 0; i < total; i++) {
2504
2540
  sendEvent("progress", { stage: "downloading", current: i + 1, total });
2505
- const localPath = path6.join(tmpDir, DEMO_FILES[i]);
2541
+ const localPath = path7.join(tmpDir, DEMO_FILES[i]);
2506
2542
  const resp = await fetch(`${DEMO_BASE_URL}/${locale}/${DEMO_FILES[i]}`, {
2507
2543
  signal: AbortSignal.timeout(6e4)
2508
2544
  });
@@ -2538,7 +2574,7 @@ data: ${JSON.stringify(eventData)}
2538
2574
  }
2539
2575
 
2540
2576
  // src/http/routes/web/cache.ts
2541
- import * as path7 from "path";
2577
+ import * as path8 from "path";
2542
2578
  import { execFile } from "child_process";
2543
2579
  function runExecFile(file, args, runner = execFile) {
2544
2580
  return new Promise((resolve4, reject) => {
@@ -2566,7 +2602,7 @@ async function showPathInFolder(filePath, platform = process.platform, runner) {
2566
2602
  } else if (platform === "win32") {
2567
2603
  await runExecFile("explorer.exe", [`/select,${filePath}`], runner);
2568
2604
  } else {
2569
- await runExecFile("xdg-open", [path7.dirname(filePath)], runner);
2605
+ await runExecFile("xdg-open", [path8.dirname(filePath)], runner);
2570
2606
  }
2571
2607
  }
2572
2608
 
@@ -2589,42 +2625,28 @@ function registerWebRoutes(server2, dbManager2, options) {
2589
2625
  const mergeCache = options?.pathProvider ? new MergeSessionCache(options.pathProvider, { nativeBinding: options.nativeBinding }) : null;
2590
2626
  mergeCache?.cleanupOrphans();
2591
2627
  const fallbackPathProvider = {
2592
- getSystemDir: () => path8.join(os3.homedir(), ".chatlab"),
2593
- getUserDataDir: () => path8.join(os3.homedir(), ".chatlab", "data"),
2594
- getDatabaseDir: () => path8.join(os3.homedir(), ".chatlab", "data", "databases"),
2595
- getVectorDir: () => path8.join(os3.homedir(), ".chatlab", "data", "vector"),
2596
- getAiDataDir: () => path8.join(os3.homedir(), ".chatlab", "ai"),
2597
- getSettingsDir: () => path8.join(os3.homedir(), ".chatlab", "settings"),
2598
- getCacheDir: () => path8.join(os3.homedir(), ".chatlab", "cache"),
2599
- getTempDir: () => path8.join(os3.homedir(), ".chatlab", "temp"),
2600
- getLogsDir: () => path8.join(os3.homedir(), ".chatlab", "logs"),
2601
- getDownloadsDir: () => path8.join(os3.homedir(), "Downloads")
2628
+ getSystemDir: () => path9.join(os3.homedir(), ".chatlab"),
2629
+ getUserDataDir: () => path9.join(os3.homedir(), ".chatlab", "data"),
2630
+ getDatabaseDir: () => path9.join(os3.homedir(), ".chatlab", "data", "databases"),
2631
+ getVectorDir: () => path9.join(os3.homedir(), ".chatlab", "data", "vector"),
2632
+ getAiDataDir: () => path9.join(os3.homedir(), ".chatlab", "ai"),
2633
+ getSettingsDir: () => path9.join(os3.homedir(), ".chatlab", "settings"),
2634
+ getCacheDir: () => path9.join(os3.homedir(), ".chatlab", "cache"),
2635
+ getTempDir: () => path9.join(os3.homedir(), ".chatlab", "temp"),
2636
+ getLogsDir: () => path9.join(os3.homedir(), ".chatlab", "logs"),
2637
+ getDownloadsDir: () => path9.join(os3.homedir(), "Downloads")
2602
2638
  };
2603
2639
  const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider;
2604
2640
  const ai = options?.aiContext;
2605
2641
  const cliStreamImport = async (dm, filePath) => {
2606
- const { streamImport: streamImport2 } = await import("./stream-import-4VDJZHMY.mjs");
2642
+ const { streamImport: streamImport2 } = await import("./stream-import-FQFNRQ2B.mjs");
2607
2643
  const result = await streamImport2(dm, filePath);
2608
2644
  if (!result.sessionId) throw new Error("Import succeeded but no sessionId returned");
2609
2645
  return { sessionId: result.sessionId };
2610
2646
  };
2611
2647
  const defaultUserDataDir = getDefaultNodeUserDataDir();
2612
- const isCustom = path8.resolve(resolvedPathProvider.getUserDataDir()) !== path8.resolve(defaultUserDataDir);
2613
- let semanticIndexService = options?.semanticIndexService;
2614
- if (!semanticIndexService) {
2615
- try {
2616
- semanticIndexService = createSemanticIndexService({
2617
- pathProvider: resolvedPathProvider,
2618
- sessionAdapter: adapter,
2619
- nativeBinding: options?.nativeBinding
2620
- });
2621
- semanticIndexService.recover();
2622
- server2.addHook("onClose", async () => semanticIndexService?.close());
2623
- } catch (err) {
2624
- console.warn("[semantic-index] service unavailable:", err instanceof Error ? err.message : String(err));
2625
- semanticIndexService = void 0;
2626
- }
2627
- }
2648
+ const isCustom = path9.resolve(resolvedPathProvider.getUserDataDir()) !== path9.resolve(defaultUserDataDir);
2649
+ const semanticIndexService = options?.semanticIndexService;
2628
2650
  registerSharedRoutes(
2629
2651
  server2,
2630
2652
  {
@@ -2717,7 +2739,7 @@ function registerProxyRoutes(server2) {
2717
2739
 
2718
2740
  // src/ai/manager-factory.ts
2719
2741
  import * as fs7 from "fs";
2720
- import * as path9 from "path";
2742
+ import * as path10 from "path";
2721
2743
  import { createHash, randomUUID } from "crypto";
2722
2744
  var nodeFs = {
2723
2745
  ensureDir(dir) {
@@ -2740,7 +2762,7 @@ var nodeFs = {
2740
2762
  return fs7.existsSync(filePath);
2741
2763
  },
2742
2764
  joinPath(...parts) {
2743
- return path9.join(...parts);
2765
+ return path10.join(...parts);
2744
2766
  }
2745
2767
  };
2746
2768
  var managerCache = /* @__PURE__ */ new Map();
@@ -2750,13 +2772,13 @@ function getAssistantManager(aiDataDir) {
2750
2772
  cached = {
2751
2773
  assistant: new AssistantManager({
2752
2774
  fs: nodeFs,
2753
- assistantsDir: path9.join(aiDataDir, "assistants"),
2775
+ assistantsDir: path10.join(aiDataDir, "assistants"),
2754
2776
  builtinRawConfigs: [],
2755
2777
  generateId: () => `custom_${randomUUID().replace(/-/g, "").slice(0, 12)}`
2756
2778
  }),
2757
2779
  skill: new SkillManagerCore({
2758
2780
  fs: nodeFs,
2759
- skillsDir: path9.join(aiDataDir, "skills"),
2781
+ skillsDir: path10.join(aiDataDir, "skills"),
2760
2782
  builtinRawSkills: [],
2761
2783
  contentHash: (content) => createHash("md5").update(content).digest("hex")
2762
2784
  })
@@ -2781,7 +2803,7 @@ var NOOP_LOGGER = { info: noop, warn: noop, error: noop };
2781
2803
 
2782
2804
  // ../../packages/sync/src/data-source-manager.ts
2783
2805
  import * as fs8 from "fs";
2784
- import * as path10 from "path";
2806
+ import * as path11 from "path";
2785
2807
  import * as crypto from "crypto";
2786
2808
  var CONFIG_FILE = "data-sources.json";
2787
2809
  function generateId(prefix = "ds") {
@@ -2809,7 +2831,7 @@ var DataSourceManager = class {
2809
2831
  this.logger = logger ?? NOOP_LOGGER;
2810
2832
  }
2811
2833
  getConfigPath() {
2812
- return path10.join(this.configDir, CONFIG_FILE);
2834
+ return path11.join(this.configDir, CONFIG_FILE);
2813
2835
  }
2814
2836
  ensureDir() {
2815
2837
  fs8.mkdirSync(this.configDir, { recursive: true });
@@ -3036,6 +3058,41 @@ function fileContainsMessages(filePath) {
3036
3058
  return false;
3037
3059
  }
3038
3060
  }
3061
+ function getMaxMessageTimestampFromFile(filePath) {
3062
+ try {
3063
+ let maxTs = null;
3064
+ const visitTimestamp = (value) => {
3065
+ const ts = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
3066
+ if (typeof ts === "number" && Number.isFinite(ts)) {
3067
+ maxTs = maxTs === null ? ts : Math.max(maxTs, ts);
3068
+ }
3069
+ };
3070
+ if (filePath.endsWith(".jsonl")) {
3071
+ const content = fs9.readFileSync(filePath, "utf-8");
3072
+ for (const line of content.split("\n")) {
3073
+ const trimmed = line.trim();
3074
+ if (!trimmed) continue;
3075
+ try {
3076
+ const obj = JSON.parse(trimmed);
3077
+ if (obj._type === "message") visitTimestamp(obj.timestamp);
3078
+ } catch {
3079
+ continue;
3080
+ }
3081
+ }
3082
+ return maxTs;
3083
+ }
3084
+ const raw = fs9.readFileSync(filePath, "utf-8");
3085
+ const parsed = JSON.parse(raw);
3086
+ if (Array.isArray(parsed.messages)) {
3087
+ for (const message of parsed.messages) {
3088
+ visitTimestamp(message?.timestamp);
3089
+ }
3090
+ }
3091
+ return maxTs;
3092
+ } catch {
3093
+ return null;
3094
+ }
3095
+ }
3039
3096
  function cleanupTempFile(filePath) {
3040
3097
  try {
3041
3098
  if (fs9.existsSync(filePath)) fs9.unlinkSync(filePath);
@@ -3089,6 +3146,8 @@ var PullEngine = class {
3089
3146
  this.logger.info(`[Pull] Pulling "${sess.name}" from ${ds.baseUrl}`);
3090
3147
  let totalNewMessages = 0;
3091
3148
  let since = sess.lastPullAt;
3149
+ let offset;
3150
+ let nextPullSince = sess.lastPullAt;
3092
3151
  let pageCount = 0;
3093
3152
  let resyncAttempted = false;
3094
3153
  this.progressMap.set(sess.id, { sessionId: sess.id, sessionName: sess.name, current: 0, pages: 0, done: false });
@@ -3097,6 +3156,7 @@ var PullEngine = class {
3097
3156
  pageCount++;
3098
3157
  const tempFile = await this.fetcher.fetchToTempFile(ds.baseUrl, sess.remoteSessionId, ds.token, {
3099
3158
  since,
3159
+ offset,
3100
3160
  limit: ds.pullLimit
3101
3161
  });
3102
3162
  try {
@@ -3106,6 +3166,7 @@ var PullEngine = class {
3106
3166
  if (stat2.size < 1024) {
3107
3167
  if (!fileContainsMessages(tempFile) && sync0?.hasMore === false) {
3108
3168
  cleanupTempFile(tempFile);
3169
+ if (sync0?.nextSince !== void 0) nextPullSince = Math.max(nextPullSince, sync0.nextSince);
3109
3170
  const retryDelays = [2e3, 3e3, 5e3];
3110
3171
  let retrySuccess = false;
3111
3172
  let retryHasMore = false;
@@ -3116,21 +3177,26 @@ var PullEngine = class {
3116
3177
  await new Promise((r) => setTimeout(r, retryDelays[ri]));
3117
3178
  const retryFile = await this.fetcher.fetchToTempFile(ds.baseUrl, sess.remoteSessionId, ds.token, {
3118
3179
  since,
3180
+ offset,
3119
3181
  limit: ds.pullLimit
3120
3182
  });
3121
3183
  const retryStat = fs9.statSync(retryFile);
3122
3184
  this.logger.info(`[Pull] "${sess.name}" retry ${ri + 1}: fetched ${retryStat.size} bytes`);
3123
3185
  const retrySync = parseSyncFromFile(retryFile);
3124
3186
  if (retryStat.size < 1024 && !fileContainsMessages(retryFile) && retrySync?.hasMore === false) {
3187
+ if (retrySync.nextSince !== void 0) nextPullSince = Math.max(nextPullSince, retrySync.nextSince);
3125
3188
  cleanupTempFile(retryFile);
3126
3189
  continue;
3127
3190
  }
3191
+ const retryMaxTs = getMaxMessageTimestampFromFile(retryFile);
3128
3192
  const retryResult = await this.importTempFile(ds.baseUrl, sess, retryFile);
3129
3193
  cleanupTempFile(retryFile);
3130
3194
  if (retryResult.needFullResync && !resyncAttempted) {
3131
3195
  resyncAttempted = true;
3132
3196
  this.logger.info(`[Pull] Resetting since=0 for "${sess.name}" full resync`);
3133
3197
  since = 0;
3198
+ offset = void 0;
3199
+ nextPullSince = 0;
3134
3200
  pageCount = 0;
3135
3201
  sess.targetSessionId = "";
3136
3202
  sess.lastPullAt = 0;
@@ -3167,6 +3233,8 @@ var PullEngine = class {
3167
3233
  this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: retryResult.sessionId });
3168
3234
  }
3169
3235
  totalNewMessages += retryResult.newMessageCount;
3236
+ if (retryMaxTs !== null) nextPullSince = Math.max(nextPullSince, retryMaxTs);
3237
+ if (retrySync?.nextSince !== void 0) nextPullSince = Math.max(nextPullSince, retrySync.nextSince);
3170
3238
  this.progressMap.set(sess.id, {
3171
3239
  sessionId: sess.id,
3172
3240
  sessionName: sess.name,
@@ -3176,6 +3244,9 @@ var PullEngine = class {
3176
3244
  });
3177
3245
  if (retrySync?.hasMore && retrySync.nextSince !== void 0) {
3178
3246
  since = retrySync.nextSince;
3247
+ offset = void 0;
3248
+ } else if (retrySync?.hasMore && retrySync.nextOffset !== void 0) {
3249
+ offset = retrySync.nextOffset;
3179
3250
  }
3180
3251
  retryHasMore = !!retrySync?.hasMore;
3181
3252
  retrySuccess = true;
@@ -3191,12 +3262,15 @@ var PullEngine = class {
3191
3262
  break;
3192
3263
  }
3193
3264
  const sync = sync0;
3265
+ const maxMessageTs = getMaxMessageTimestampFromFile(tempFile);
3194
3266
  const result = await this.importTempFile(ds.baseUrl, sess, tempFile);
3195
3267
  cleanupTempFile(tempFile);
3196
3268
  if (result.needFullResync && !resyncAttempted) {
3197
3269
  resyncAttempted = true;
3198
3270
  this.logger.info(`[Pull] Resetting since=0 for "${sess.name}" full resync`);
3199
3271
  since = 0;
3272
+ offset = void 0;
3273
+ nextPullSince = 0;
3200
3274
  pageCount = 0;
3201
3275
  sess.targetSessionId = "";
3202
3276
  sess.lastPullAt = 0;
@@ -3231,6 +3305,7 @@ var PullEngine = class {
3231
3305
  this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: result.sessionId });
3232
3306
  }
3233
3307
  totalNewMessages += result.newMessageCount;
3308
+ if (maxMessageTs !== null) nextPullSince = Math.max(nextPullSince, maxMessageTs);
3234
3309
  this.progressMap.set(sess.id, {
3235
3310
  sessionId: sess.id,
3236
3311
  sessionName: sess.name,
@@ -3238,8 +3313,17 @@ var PullEngine = class {
3238
3313
  pages: pageCount,
3239
3314
  done: false
3240
3315
  });
3316
+ if (sync?.nextSince !== void 0) nextPullSince = Math.max(nextPullSince, sync.nextSince);
3241
3317
  if (!sync || !sync.hasMore) break;
3242
- if (sync.nextSince !== void 0) since = sync.nextSince;
3318
+ if (sync.nextSince !== void 0) {
3319
+ since = sync.nextSince;
3320
+ offset = void 0;
3321
+ } else if (sync.nextOffset !== void 0) {
3322
+ offset = sync.nextOffset;
3323
+ } else {
3324
+ this.logger.warn(`[Pull] "${sess.name}" returned hasMore=true without nextSince or nextOffset, stopping`);
3325
+ break;
3326
+ }
3243
3327
  } catch (importErr) {
3244
3328
  cleanupTempFile(tempFile);
3245
3329
  throw importErr;
@@ -3248,8 +3332,9 @@ var PullEngine = class {
3248
3332
  if (pageCount >= MAX_PAGES_PER_PULL) {
3249
3333
  this.logger.warn(`[Pull] "${sess.name}" reached page limit (${MAX_PAGES_PER_PULL}), data may be incomplete`);
3250
3334
  }
3335
+ const savedLastPullAt = Math.max(sess.lastPullAt, Math.max(0, nextPullSince - PULL_OVERLAP_SECONDS));
3251
3336
  this.dsManager.updateSession(sourceId, sess.id, {
3252
- lastPullAt: Math.floor(Date.now() / 1e3) - PULL_OVERLAP_SECONDS,
3337
+ lastPullAt: savedLastPullAt,
3253
3338
  lastStatus: "success",
3254
3339
  lastNewMessages: totalNewMessages,
3255
3340
  lastError: ""
@@ -3401,11 +3486,11 @@ function reloadTimer(dsId, skipInitialPull = false) {
3401
3486
  // src/sync/adapters.ts
3402
3487
  import * as fs10 from "fs";
3403
3488
  import * as os4 from "os";
3404
- import * as path11 from "path";
3489
+ import * as path12 from "path";
3405
3490
  import * as crypto3 from "crypto";
3406
3491
  function getTempFilePath(ext) {
3407
3492
  const id = crypto3.randomBytes(8).toString("hex");
3408
- return path11.join(os4.tmpdir(), `chatlab-pull-${id}${ext}`);
3493
+ return path12.join(os4.tmpdir(), `chatlab-pull-${id}${ext}`);
3409
3494
  }
3410
3495
  var NodeFetcher = class {
3411
3496
  async fetchToTempFile(baseUrl, remoteSessionId, token, params) {
@@ -3745,15 +3830,15 @@ async function startHttpServer(options) {
3745
3830
  });
3746
3831
  let semanticIndexService;
3747
3832
  try {
3748
- semanticIndexService = createSemanticIndexService({
3833
+ semanticIndexService = createSemanticIndexWorkerRuntimeClient({
3749
3834
  pathProvider,
3750
- sessionAdapter: createDatabaseManagerAdapter(dbManager),
3751
- nativeBinding
3835
+ runtime,
3836
+ nativeBinding,
3837
+ workerEntryUrl: import.meta.url.endsWith(".ts") ? void 0 : new URL("./semantic-index-worker.mjs", import.meta.url)
3752
3838
  });
3753
- semanticIndexService.recover();
3754
3839
  server.addHook("onClose", async () => semanticIndexService?.close());
3755
3840
  } catch (err) {
3756
- console.warn("[semantic-index] service unavailable:", err instanceof Error ? err.message : String(err));
3841
+ console.warn("[semantic-index] worker client unavailable:", err instanceof Error ? err.message : String(err));
3757
3842
  semanticIndexService = void 0;
3758
3843
  }
3759
3844
  registerWebRoutes(server, dbManager, {
@@ -3813,4 +3898,4 @@ export {
3813
3898
  startHttpServer,
3814
3899
  stopHttpServer
3815
3900
  };
3816
- //# sourceMappingURL=chunk-3HOTLLFC.mjs.map
3901
+ //# sourceMappingURL=chunk-SIVERPF5.mjs.map