chatlab-cli 0.24.1 → 0.25.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 (73) hide show
  1. package/dist/chat-command-5XMRLXZM.mjs +166 -0
  2. package/dist/chat-command-5XMRLXZM.mjs.map +1 -0
  3. package/dist/{chunk-N5DGZQB4.mjs → chunk-7VG6QGQ2.mjs} +1543 -664
  4. package/dist/chunk-7VG6QGQ2.mjs.map +1 -0
  5. package/dist/{chunk-QJI6AOAT.mjs → chunk-CUCDRAY6.mjs} +17 -25
  6. package/dist/chunk-CUCDRAY6.mjs.map +1 -0
  7. package/dist/chunk-FDK6SPCG.mjs +40 -0
  8. package/dist/chunk-FDK6SPCG.mjs.map +1 -0
  9. package/dist/{chunk-ZL322NVL.mjs → chunk-GTCTDD2Z.mjs} +116 -504
  10. package/dist/chunk-GTCTDD2Z.mjs.map +1 -0
  11. package/dist/{chunk-43XWYC3M.mjs → chunk-JE3FOFF7.mjs} +219 -123
  12. package/dist/chunk-JE3FOFF7.mjs.map +1 -0
  13. package/dist/{chunk-QY6P2YS7.mjs → chunk-PDQFSQO6.mjs} +59 -33
  14. package/dist/chunk-PDQFSQO6.mjs.map +1 -0
  15. package/dist/chunk-WYHGWTA4.mjs +485 -0
  16. package/dist/chunk-WYHGWTA4.mjs.map +1 -0
  17. package/dist/{chunk-CIIQ3S2X.mjs → chunk-YZYP5ST6.mjs} +37 -8
  18. package/dist/chunk-YZYP5ST6.mjs.map +1 -0
  19. package/dist/cli.mjs +3 -2
  20. package/dist/{http-TPX74BLK.mjs → http-C3CGXUBU.mjs} +8 -6
  21. package/dist/{import-B745DRHD.mjs → import-VS7L5JDX.mjs} +4 -4
  22. package/dist/index.mjs +10 -8
  23. package/dist/{mcp-YWAVG5XW.mjs → mcp-6VXDPUQK.mjs} +11 -6
  24. package/dist/mcp-6VXDPUQK.mjs.map +1 -0
  25. package/dist/{stream-import-OPALPVWF.mjs → stream-import-TFDBNVIW.mjs} +3 -3
  26. package/dist-web/assets/{DebugToolsPanel-DaSNc1o-.css → DebugToolsPanel-vn-bnUxn.css} +1 -1
  27. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-B1xZmp9r.js +161 -0
  28. package/dist-web/assets/{EChartWordcloud-VpNJDurs.js → EChartWordcloud-CzhfrEF_.js} +1 -1
  29. package/dist-web/assets/{MessageExportModal-CSfaY25B.js → MessageExportModal-B79pPBL9.js} +1 -1
  30. package/dist-web/assets/{electron-BUeXfvxT.js → electron-DRkrjeIl.js} +1 -1
  31. package/dist-web/assets/electron-Dv8jdn_2.js +1 -0
  32. package/dist-web/assets/{fetch-RDFt3hmp.js → fetch-BmZIm6mS.js} +1 -1
  33. package/dist-web/assets/fetch-BqLy63aj.js +1 -0
  34. package/dist-web/assets/fetch-BstX-wd4.js +1 -0
  35. package/dist-web/assets/{fetch-BohUj3CQ.js → fetch-Bvcxe5PI.js} +1 -1
  36. package/dist-web/assets/{fetch-BneHn5qk.js → fetch-CDamdvtb.js} +1 -1
  37. package/dist-web/assets/{fetch-DypPduyQ.js → fetch-DWg-2kOp.js} +1 -1
  38. package/dist-web/assets/{fetch-CARsBVl0.js → fetch-DfgdagMN.js} +1 -1
  39. package/dist-web/assets/{fetch-Ck3tjoLa.js → fetch-Drk9dLaF.js} +1 -1
  40. package/dist-web/assets/{fetch-B2q6973s.js → fetch-Y-_WsCuk.js} +1 -1
  41. package/dist-web/assets/{fetch-BGMRmQx0.js → fetch-ph2_d79v.js} +1 -1
  42. package/dist-web/assets/{index-Bvlj3dMn.js → index-9urfJP1r.js} +1 -1
  43. package/dist-web/assets/index-CfKjh0Jr.js +9 -0
  44. package/dist-web/assets/index-CprJOAB1.css +1 -0
  45. package/dist-web/assets/{index-BvaWRM6_.js → index-DMWf5On7.js} +1 -1
  46. package/dist-web/assets/{index-DCWvD-4L.js → index-D_hHm7bQ.js} +2 -2
  47. package/dist-web/assets/index-MCdpiX_O.js +173 -0
  48. package/dist-web/assets/{index-C5LrQG3N.js → index-deYDOVUa.js} +4 -4
  49. package/dist-web/assets/vendor-echarts-CITQeskI.js +11 -0
  50. package/dist-web/assets/{vendor-echarts-wordcloud-CZ9gsX8l.js → vendor-echarts-wordcloud-B1kQMklB.js} +1 -1
  51. package/dist-web/assets/{vendor-nuxt-ui-BYU7dz8t.js → vendor-nuxt-ui-C_8CsH6e.js} +1 -1
  52. package/dist-web/assets/{vendor-reka-ui-CnB0mMNX.js → vendor-reka-ui-CIMXwvEp.js} +1 -1
  53. package/dist-web/assets/{web-CXPkmhis.js → web-XJV6Vj5t.js} +1 -1
  54. package/dist-web/index.html +4 -4
  55. package/package.json +1 -1
  56. package/dist/chunk-43XWYC3M.mjs.map +0 -1
  57. package/dist/chunk-CIIQ3S2X.mjs.map +0 -1
  58. package/dist/chunk-N5DGZQB4.mjs.map +0 -1
  59. package/dist/chunk-QJI6AOAT.mjs.map +0 -1
  60. package/dist/chunk-QY6P2YS7.mjs.map +0 -1
  61. package/dist/chunk-ZL322NVL.mjs.map +0 -1
  62. package/dist/mcp-YWAVG5XW.mjs.map +0 -1
  63. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BBDrsJfe.js +0 -161
  64. package/dist-web/assets/electron-BynNUuEA.js +0 -1
  65. package/dist-web/assets/fetch-BtHwWs8A.js +0 -1
  66. package/dist-web/assets/fetch-DzNuFdFM.js +0 -1
  67. package/dist-web/assets/index-BRwEubUe.js +0 -173
  68. package/dist-web/assets/index-DB-iqXzV.css +0 -1
  69. package/dist-web/assets/index-DS1rzk1I.js +0 -8
  70. package/dist-web/assets/vendor-echarts-C5u62NnM.js +0 -11
  71. /package/dist/{http-TPX74BLK.mjs.map → http-C3CGXUBU.mjs.map} +0 -0
  72. /package/dist/{import-B745DRHD.mjs.map → import-VS7L5JDX.mjs.map} +0 -0
  73. /package/dist/{stream-import-OPALPVWF.mjs.map → stream-import-TFDBNVIW.mjs.map} +0 -0
@@ -619,8 +619,8 @@ var require_FilterBase = __commonJS({
619
619
  var FilterBase = class _FilterBase extends Transform {
620
620
  static stringFilter(string, separator) {
621
621
  return (stack) => {
622
- const path35 = stack.join(separator);
623
- return path35.length === string.length && path35 === string || path35.length > string.length && path35.substr(0, string.length) === string && path35.substr(string.length, separator.length) === separator;
622
+ const path36 = stack.join(separator);
623
+ return path36.length === string.length && path36 === string || path36.length > string.length && path36.substr(0, string.length) === string && path36.substr(string.length, separator.length) === separator;
624
624
  };
625
625
  }
626
626
  static regExpFilter(regExp, separator) {
@@ -1074,12 +1074,12 @@ var require_Assembler = __commonJS({
1074
1074
  return (this.stack.length >> 1) + (this.done ? 0 : 1);
1075
1075
  }
1076
1076
  get path() {
1077
- const path35 = [];
1077
+ const path36 = [];
1078
1078
  for (let i = 0; i < this.stack.length; i += 2) {
1079
1079
  const key = this.stack[i + 1];
1080
- path35.push(key === null ? this.stack[i].length : key);
1080
+ path36.push(key === null ? this.stack[i].length : key);
1081
1081
  }
1082
- return path35;
1082
+ return path36;
1083
1083
  }
1084
1084
  dropToLevel(level) {
1085
1085
  if (level < this.depth) {
@@ -1854,63 +1854,6 @@ var ALL_MIGRATIONS = [
1854
1854
  m004EncryptedKeysToAuthProfiles
1855
1855
  ];
1856
1856
 
1857
- // ../../packages/node-runtime/src/better-sqlite3-adapter.ts
1858
- import Database from "better-sqlite3";
1859
- var BetterSqlitePreparedStatement = class {
1860
- constructor(stmt) {
1861
- this.stmt = stmt;
1862
- this.readonly = stmt.readonly;
1863
- }
1864
- stmt;
1865
- readonly;
1866
- get(...params) {
1867
- return this.stmt.get(...params);
1868
- }
1869
- all(...params) {
1870
- return this.stmt.all(...params);
1871
- }
1872
- run(...params) {
1873
- const result = this.stmt.run(...params);
1874
- return {
1875
- changes: result.changes,
1876
- lastInsertRowid: result.lastInsertRowid
1877
- };
1878
- }
1879
- };
1880
- var BetterSqliteAdapter = class {
1881
- constructor(db) {
1882
- this.db = db;
1883
- this.readonly = db.readonly;
1884
- }
1885
- db;
1886
- readonly;
1887
- exec(sql) {
1888
- this.db.exec(sql);
1889
- }
1890
- prepare(sql) {
1891
- return new BetterSqlitePreparedStatement(this.db.prepare(sql));
1892
- }
1893
- transaction(fn) {
1894
- return this.db.transaction(fn)();
1895
- }
1896
- pragma(pragma) {
1897
- return this.db.pragma(pragma);
1898
- }
1899
- close() {
1900
- this.db.close();
1901
- }
1902
- };
1903
- function openBetterSqliteDatabase(filePath, options) {
1904
- const db = new Database(filePath, {
1905
- readonly: options?.readonly ?? false,
1906
- nativeBinding: options?.nativeBinding
1907
- });
1908
- if (!options?.readonly) {
1909
- db.pragma("journal_mode = WAL");
1910
- }
1911
- return new BetterSqliteAdapter(db);
1912
- }
1913
-
1914
1857
  // ../../packages/core/src/query/filters.ts
1915
1858
  function buildTimeFilter(filter, tableAlias) {
1916
1859
  const conditions = [];
@@ -2165,8 +2108,8 @@ function getSessionInfo(db) {
2165
2108
  return buildSessionInfo(meta, overview, sc);
2166
2109
  }
2167
2110
  function getSummaryCount(db) {
2168
- if (!hasTable(db, "chat_session")) return 0;
2169
- const row = db.prepare("SELECT COUNT(*) as count FROM chat_session WHERE summary IS NOT NULL AND summary != ''").get();
2111
+ if (!hasTable(db, "segment")) return 0;
2112
+ const row = db.prepare("SELECT COUNT(*) as count FROM segment WHERE summary IS NOT NULL AND summary != ''").get();
2170
2113
  return row?.count ?? 0;
2171
2114
  }
2172
2115
  function isChatSessionDb(db) {
@@ -2231,11 +2174,11 @@ function getChatOverview(db, topN = 10) {
2231
2174
  topMembers
2232
2175
  };
2233
2176
  }
2234
- function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5, ftsMatchExpression) {
2235
- if (!hasTable(db, "chat_session")) return [];
2177
+ function searchSegments(db, keywords, timeFilter, limit = 20, previewCount = 5, ftsMatchExpression) {
2178
+ if (!hasTable(db, "segment")) return [];
2236
2179
  let sessionSql = `
2237
2180
  SELECT cs.id, cs.start_ts as startTs, cs.end_ts as endTs, cs.message_count as messageCount
2238
- FROM chat_session cs WHERE 1=1
2181
+ FROM segment cs WHERE 1=1
2239
2182
  `;
2240
2183
  const params = [];
2241
2184
  if (timeFilter?.startTs !== void 0) {
@@ -2250,7 +2193,7 @@ function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5,
2250
2193
  if (ftsMatchExpression) {
2251
2194
  sessionSql += `
2252
2195
  AND cs.id IN (
2253
- SELECT DISTINCT mc.session_id FROM message_context mc
2196
+ SELECT DISTINCT mc.segment_id FROM message_context mc
2254
2197
  WHERE mc.message_id IN (SELECT rowid FROM message_fts WHERE content MATCH ?)
2255
2198
  )
2256
2199
  `;
@@ -2259,7 +2202,7 @@ function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5,
2259
2202
  const keywordConditions = keywords.map(() => "m.content LIKE ?").join(" OR ");
2260
2203
  sessionSql += `
2261
2204
  AND cs.id IN (
2262
- SELECT DISTINCT mc.session_id FROM message_context mc
2205
+ SELECT DISTINCT mc.segment_id FROM message_context mc
2263
2206
  JOIN message m ON m.id = mc.message_id
2264
2207
  WHERE (${keywordConditions})
2265
2208
  )
@@ -2280,7 +2223,7 @@ function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5,
2280
2223
  FROM message_context mc
2281
2224
  JOIN message m ON m.id = mc.message_id
2282
2225
  JOIN member mb ON mb.id = m.sender_id
2283
- WHERE mc.session_id = ? ORDER BY m.ts ASC LIMIT ?
2226
+ WHERE mc.segment_id = ? ORDER BY m.ts ASC LIMIT ?
2284
2227
  `;
2285
2228
  return sessions.map((session) => {
2286
2229
  const previewMessages = db.prepare(previewSql).all(session.id, previewCount);
@@ -2294,12 +2237,12 @@ function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5,
2294
2237
  };
2295
2238
  });
2296
2239
  }
2297
- function getSessionMessages(db, chatSessionId, limit = 500) {
2298
- if (!hasTable(db, "chat_session")) return null;
2240
+ function getSegmentMessages(db, segmentId, limit = 500) {
2241
+ if (!hasTable(db, "segment")) return null;
2299
2242
  const session = db.prepare(
2300
2243
  `SELECT id, start_ts as startTs, end_ts as endTs, message_count as messageCount
2301
- FROM chat_session WHERE id = ?`
2302
- ).get(chatSessionId);
2244
+ FROM segment WHERE id = ?`
2245
+ ).get(segmentId);
2303
2246
  if (!session) return null;
2304
2247
  const messages = db.prepare(
2305
2248
  `SELECT m.id, mb.id as senderId,
@@ -2309,14 +2252,14 @@ function getSessionMessages(db, chatSessionId, limit = 500) {
2309
2252
  FROM message_context mc
2310
2253
  JOIN message m ON m.id = mc.message_id
2311
2254
  JOIN member mb ON mb.id = m.sender_id
2312
- WHERE mc.session_id = ? ORDER BY m.ts ASC LIMIT ?`
2313
- ).all(chatSessionId, limit);
2255
+ WHERE mc.segment_id = ? ORDER BY m.ts ASC LIMIT ?`
2256
+ ).all(segmentId, limit);
2314
2257
  const participantsSet = /* @__PURE__ */ new Set();
2315
2258
  for (const msg of messages) {
2316
2259
  participantsSet.add(msg.senderName);
2317
2260
  }
2318
2261
  return {
2319
- sessionId: session.id,
2262
+ segmentId: session.id,
2320
2263
  startTs: session.startTs,
2321
2264
  endTs: session.endTs,
2322
2265
  messageCount: session.messageCount,
@@ -2325,13 +2268,13 @@ function getSessionMessages(db, chatSessionId, limit = 500) {
2325
2268
  messages
2326
2269
  };
2327
2270
  }
2328
- function getSessionSummaries(db, options) {
2329
- if (!hasTable(db, "chat_session")) return [];
2271
+ function getSegmentSummaries(db, options) {
2272
+ if (!hasTable(db, "segment")) return [];
2330
2273
  const { limit = 50, timeFilter } = options ?? {};
2331
2274
  let sql = `
2332
2275
  SELECT cs.id, cs.start_ts as startTs, cs.end_ts as endTs,
2333
2276
  cs.message_count as messageCount, cs.summary
2334
- FROM chat_session cs
2277
+ FROM segment cs
2335
2278
  WHERE cs.summary IS NOT NULL AND cs.summary != ''
2336
2279
  `;
2337
2280
  const params = [];
@@ -2351,7 +2294,7 @@ function getSessionSummaries(db, options) {
2351
2294
  FROM message_context mc
2352
2295
  JOIN message m ON m.id = mc.message_id
2353
2296
  JOIN member mb ON mb.id = m.sender_id
2354
- WHERE mc.session_id = ? LIMIT 10
2297
+ WHERE mc.segment_id = ? LIMIT 10
2355
2298
  `;
2356
2299
  return sessions.map((session) => {
2357
2300
  const participants = db.prepare(participantsSql).all(session.id);
@@ -2368,9 +2311,9 @@ function getSessionSummaries(db, options) {
2368
2311
  var DEFAULT_SESSION_GAP_THRESHOLD = 1800;
2369
2312
  function getSessionIndexStats(db) {
2370
2313
  let sessionCount = 0;
2371
- if (hasTable(db, "chat_session")) {
2314
+ if (hasTable(db, "segment")) {
2372
2315
  try {
2373
- const row = db.prepare("SELECT COUNT(*) as count FROM chat_session").get();
2316
+ const row = db.prepare("SELECT COUNT(*) as count FROM segment").get();
2374
2317
  sessionCount = row?.count ?? 0;
2375
2318
  } catch {
2376
2319
  }
@@ -2386,7 +2329,7 @@ function getSessionIndexStats(db) {
2386
2329
  return { sessionCount, hasIndex: sessionCount > 0, gapThreshold };
2387
2330
  }
2388
2331
  function getChatSessionList(db) {
2389
- if (!hasTable(db, "chat_session")) return [];
2332
+ if (!hasTable(db, "segment")) return [];
2390
2333
  try {
2391
2334
  return db.prepare(
2392
2335
  `SELECT
@@ -2396,24 +2339,24 @@ function getChatSessionList(db) {
2396
2339
  cs.message_count as messageCount,
2397
2340
  cs.summary,
2398
2341
  (SELECT mc.message_id FROM message_context mc
2399
- WHERE mc.session_id = cs.id ORDER BY mc.message_id LIMIT 1) as firstMessageId
2400
- FROM chat_session cs
2342
+ WHERE mc.segment_id = cs.id ORDER BY mc.message_id LIMIT 1) as firstMessageId
2343
+ FROM segment cs
2401
2344
  ORDER BY cs.start_ts ASC`
2402
2345
  ).all();
2403
2346
  } catch {
2404
2347
  return [];
2405
2348
  }
2406
2349
  }
2407
- function getChatSessionSummary(db, chatSessionId) {
2350
+ function getSegmentSummary(db, segmentId) {
2408
2351
  try {
2409
- const row = db.prepare("SELECT summary FROM chat_session WHERE id = ?").get(chatSessionId);
2352
+ const row = db.prepare("SELECT summary FROM segment WHERE id = ?").get(segmentId);
2410
2353
  return row?.summary ?? null;
2411
2354
  } catch {
2412
2355
  return null;
2413
2356
  }
2414
2357
  }
2415
- function saveChatSessionSummary(db, chatSessionId, summary) {
2416
- db.prepare("UPDATE chat_session SET summary = ? WHERE id = ?").run(summary, chatSessionId);
2358
+ function saveSegmentSummary(db, segmentId, summary) {
2359
+ db.prepare("UPDATE segment SET summary = ? WHERE id = ?").run(summary, segmentId);
2417
2360
  }
2418
2361
  function updateSessionOwnerId(db, ownerId) {
2419
2362
  db.prepare("UPDATE meta SET owner_id = ?").run(ownerId);
@@ -2423,7 +2366,7 @@ function renameSession(db, newName) {
2423
2366
  }
2424
2367
  function clearSessionIndex(db) {
2425
2368
  db.exec("DELETE FROM message_context");
2426
- db.exec("DELETE FROM chat_session");
2369
+ db.exec("DELETE FROM segment");
2427
2370
  }
2428
2371
  function generateSessionIndex(db, gapThreshold = DEFAULT_SESSION_GAP_THRESHOLD, onProgress) {
2429
2372
  const countRow = db.prepare("SELECT COUNT(*) as count FROM message").get();
@@ -2457,9 +2400,9 @@ function generateSessionIndex(db, gapThreshold = DEFAULT_SESSION_GAP_THRESHOLD,
2457
2400
  }
2458
2401
  }
2459
2402
  const insertSession = db.prepare(
2460
- "INSERT INTO chat_session (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)"
2403
+ "INSERT INTO segment (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)"
2461
2404
  );
2462
- const insertContext = db.prepare("INSERT INTO message_context (message_id, session_id, topic_id) VALUES (?, ?, NULL)");
2405
+ const insertContext = db.prepare("INSERT INTO message_context (message_id, segment_id, topic_id) VALUES (?, ?, NULL)");
2463
2406
  return db.transaction(() => {
2464
2407
  let processed = 0;
2465
2408
  const total = sessionMap.size;
@@ -2485,13 +2428,13 @@ function generateIncrementalSessionIndex(db, gapThreshold = DEFAULT_SESSION_GAP_
2485
2428
  const allMessages = db.prepare("SELECT id, ts FROM message ORDER BY ts, id").all();
2486
2429
  const newMessages = allMessages.filter((m) => !indexedIds.has(m.id));
2487
2430
  if (newMessages.length === 0) return 0;
2488
- const lastSession = hasTable(db, "chat_session") ? db.prepare("SELECT id, end_ts FROM chat_session ORDER BY end_ts DESC LIMIT 1").get() : void 0;
2431
+ const lastSession = hasTable(db, "segment") ? db.prepare("SELECT id, end_ts FROM segment ORDER BY end_ts DESC LIMIT 1").get() : void 0;
2489
2432
  newMessages.sort((a, b) => a.ts - b.ts || a.id - b.id);
2490
2433
  const insertSession = db.prepare(
2491
- "INSERT INTO chat_session (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)"
2434
+ "INSERT INTO segment (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)"
2492
2435
  );
2493
- const insertContext = db.prepare("INSERT INTO message_context (message_id, session_id, topic_id) VALUES (?, ?, NULL)");
2494
- const updateSession = db.prepare("UPDATE chat_session SET end_ts = ?, message_count = message_count + ? WHERE id = ?");
2436
+ const insertContext = db.prepare("INSERT INTO message_context (message_id, segment_id, topic_id) VALUES (?, ?, NULL)");
2437
+ const updateSession = db.prepare("UPDATE segment SET end_ts = ?, message_count = message_count + ? WHERE id = ?");
2495
2438
  return db.transaction(() => {
2496
2439
  let newSessionCount = 0;
2497
2440
  let currentSessionId = null;
@@ -2867,22 +2810,22 @@ function getSearchMessageContext(db, messageIds, contextBefore = 2, contextAfter
2867
2810
  for (const messageId of messageIds) {
2868
2811
  contextIds.add(messageId);
2869
2812
  if (hasSessionData) {
2870
- const sessionRow = db.prepare("SELECT session_id FROM message_context WHERE message_id = ?").get(messageId);
2813
+ const sessionRow = db.prepare("SELECT segment_id FROM message_context WHERE message_id = ?").get(messageId);
2871
2814
  if (sessionRow) {
2872
2815
  if (contextBefore > 0) {
2873
2816
  const rows = db.prepare(
2874
2817
  `SELECT mc.message_id as id FROM message_context mc
2875
- WHERE mc.session_id = ? AND mc.message_id < ?
2818
+ WHERE mc.segment_id = ? AND mc.message_id < ?
2876
2819
  ORDER BY mc.message_id DESC LIMIT ?`
2877
- ).all(sessionRow.session_id, messageId, contextBefore);
2820
+ ).all(sessionRow.segment_id, messageId, contextBefore);
2878
2821
  rows.forEach((r) => contextIds.add(r.id));
2879
2822
  }
2880
2823
  if (contextAfter > 0) {
2881
2824
  const rows = db.prepare(
2882
2825
  `SELECT mc.message_id as id FROM message_context mc
2883
- WHERE mc.session_id = ? AND mc.message_id > ?
2826
+ WHERE mc.segment_id = ? AND mc.message_id > ?
2884
2827
  ORDER BY mc.message_id ASC LIMIT ?`
2885
- ).all(sessionRow.session_id, messageId, contextAfter);
2828
+ ).all(sessionRow.segment_id, messageId, contextAfter);
2886
2829
  rows.forEach((r) => contextIds.add(r.id));
2887
2830
  }
2888
2831
  continue;
@@ -3229,17 +3172,17 @@ function filterMessagesWithContext(db, options = {}) {
3229
3172
  pagination: { page, pageSize, totalBlocks, totalHits, hasMore }
3230
3173
  };
3231
3174
  }
3232
- function getMultipleSessionsMessages(db, chatSessionIds, page = 1, pageSize = 50) {
3233
- if (chatSessionIds.length === 0) {
3175
+ function getMultipleSessionsMessages(db, segmentIds, page = 1, pageSize = 50) {
3176
+ if (segmentIds.length === 0) {
3234
3177
  return emptyResult(page, pageSize);
3235
3178
  }
3236
3179
  const sessionsSql = `
3237
3180
  SELECT id, start_ts as startTs, end_ts as endTs, message_count as messageCount
3238
- FROM chat_session
3239
- WHERE id IN (${chatSessionIds.map(() => "?").join(",")})
3181
+ FROM segment
3182
+ WHERE id IN (${segmentIds.map(() => "?").join(",")})
3240
3183
  ORDER BY start_ts ASC
3241
3184
  `;
3242
- const allSessions = db.prepare(sessionsSql).all(...chatSessionIds);
3185
+ const allSessions = db.prepare(sessionsSql).all(...segmentIds);
3243
3186
  const totalBlocks = allSessions.length;
3244
3187
  const startIdx = (page - 1) * pageSize;
3245
3188
  const endIdx = Math.min(startIdx + pageSize, totalBlocks);
@@ -3262,7 +3205,7 @@ function getMultipleSessionsMessages(db, chatSessionIds, page = 1, pageSize = 50
3262
3205
  JOIN member m ON msg.sender_id = m.id
3263
3206
  LEFT JOIN message reply_msg ON msg.reply_to_message_id = reply_msg.platform_message_id
3264
3207
  LEFT JOIN member reply_m ON reply_msg.sender_id = reply_m.id
3265
- WHERE mc.session_id = ?
3208
+ WHERE mc.segment_id = ?
3266
3209
  ORDER BY msg.ts ASC
3267
3210
  `;
3268
3211
  for (const session of pageSessions) {
@@ -3904,7 +3847,7 @@ function getRelationshipStats(db, filter, options) {
3904
3847
  monthlyPerseverance: [],
3905
3848
  perseveranceThreshold
3906
3849
  };
3907
- const sessionCount = db.prepare("SELECT COUNT(*) as count FROM chat_session").get();
3850
+ const sessionCount = db.prepare("SELECT COUNT(*) as count FROM segment").get();
3908
3851
  if (!sessionCount || sessionCount.count === 0) return emptyResult2;
3909
3852
  const timeConditions = [];
3910
3853
  const params = [];
@@ -3917,13 +3860,13 @@ function getRelationshipStats(db, filter, options) {
3917
3860
  params.push(filter.endTs);
3918
3861
  }
3919
3862
  const whereClause = timeConditions.length > 0 ? `WHERE ${timeConditions.join(" AND ")}` : "";
3920
- const sessionRows = db.prepare(
3921
- `SELECT cs.id AS session_id, cs.start_ts, cs.end_ts,
3863
+ const segmentRows = db.prepare(
3864
+ `SELECT cs.id AS segment_id, cs.start_ts, cs.end_ts,
3922
3865
  (SELECT m.sender_id FROM message_context mc JOIN message m ON m.id = mc.message_id
3923
- WHERE mc.session_id = cs.id ORDER BY m.ts ASC, m.id ASC LIMIT 1) AS initiator_id,
3866
+ WHERE mc.segment_id = cs.id ORDER BY m.ts ASC, m.id ASC LIMIT 1) AS initiator_id,
3924
3867
  (SELECT m.sender_id FROM message_context mc JOIN message m ON m.id = mc.message_id
3925
- WHERE mc.session_id = cs.id ORDER BY m.ts DESC, m.id DESC LIMIT 1) AS closer_id
3926
- FROM chat_session cs ${whereClause} ORDER BY cs.start_ts ASC`
3868
+ WHERE mc.segment_id = cs.id ORDER BY m.ts DESC, m.id DESC LIMIT 1) AS closer_id
3869
+ FROM segment cs ${whereClause} ORDER BY cs.start_ts ASC`
3927
3870
  ).all(...params);
3928
3871
  const memberNames = /* @__PURE__ */ new Map();
3929
3872
  const memberRows = db.prepare("SELECT id, COALESCE(group_nickname, account_name, platform_id) as name FROM member").all();
@@ -3934,7 +3877,7 @@ function getRelationshipStats(db, filter, options) {
3934
3877
  const iceBreakMap = /* @__PURE__ */ new Map();
3935
3878
  let totalIceBreaks = 0;
3936
3879
  let prevEndTs = null;
3937
- for (const row of sessionRows) {
3880
+ for (const row of segmentRows) {
3938
3881
  const month = toLocalMonth(row.start_ts);
3939
3882
  if (!monthMap.has(month)) monthMap.set(month, { initiateMap: /* @__PURE__ */ new Map(), closeMap: /* @__PURE__ */ new Map(), totalSessions: 0 });
3940
3883
  const ms = monthMap.get(month);
@@ -3983,12 +3926,12 @@ function getRelationshipStats(db, filter, options) {
3983
3926
  iceBreakers.push({ month, memberId, name: memberNames.get(memberId) ?? `Unknown(${memberId})`, count });
3984
3927
  }
3985
3928
  }
3986
- const sessionIdList = sessionRows.map((r) => r.session_id);
3987
- const msgStats = queryMessageLevelStats(db, sessionIdList, memberNames, perseveranceThreshold);
3929
+ const segmentIdList = segmentRows.map((r) => r.segment_id);
3930
+ const msgStats = queryMessageLevelStats(db, segmentIdList, memberNames, perseveranceThreshold);
3988
3931
  return {
3989
3932
  months,
3990
3933
  members,
3991
- totalSessions: sessionRows.length,
3934
+ totalSessions: segmentRows.length,
3992
3935
  hasSessionIndex: true,
3993
3936
  iceBreakers,
3994
3937
  totalIceBreaks,
@@ -3996,7 +3939,7 @@ function getRelationshipStats(db, filter, options) {
3996
3939
  perseveranceThreshold
3997
3940
  };
3998
3941
  }
3999
- function queryMessageLevelStats(db, sessionIds, memberNames, perseveranceThreshold) {
3942
+ function queryMessageLevelStats(db, segmentIds, memberNames, perseveranceThreshold) {
4000
3943
  const empty = {
4001
3944
  responseLatency: [],
4002
3945
  perseverance: [],
@@ -4004,22 +3947,22 @@ function queryMessageLevelStats(db, sessionIds, memberNames, perseveranceThresho
4004
3947
  monthlyResponseLatency: [],
4005
3948
  monthlyPerseverance: []
4006
3949
  };
4007
- if (sessionIds.length === 0) return empty;
3950
+ if (segmentIds.length === 0) return empty;
4008
3951
  const BATCH_SIZE2 = 500;
4009
3952
  const responseTotals = /* @__PURE__ */ new Map();
4010
3953
  const dtTotals = /* @__PURE__ */ new Map();
4011
3954
  const monthlyRespMap = /* @__PURE__ */ new Map();
4012
3955
  const monthlyDtMap = /* @__PURE__ */ new Map();
4013
- for (let i = 0; i < sessionIds.length; i += BATCH_SIZE2) {
4014
- const batch = sessionIds.slice(i, i + BATCH_SIZE2);
3956
+ for (let i = 0; i < segmentIds.length; i += BATCH_SIZE2) {
3957
+ const batch = segmentIds.slice(i, i + BATCH_SIZE2);
4015
3958
  const placeholders = batch.map(() => "?").join(",");
4016
3959
  const latencyRows = db.prepare(
4017
3960
  `WITH msg_lag AS (
4018
3961
  SELECT m.sender_id, m.ts,
4019
- LAG(m.sender_id) OVER (PARTITION BY mc.session_id ORDER BY m.ts, m.id) AS prev_sender_id,
4020
- LAG(m.ts) OVER (PARTITION BY mc.session_id ORDER BY m.ts, m.id) AS prev_ts
3962
+ LAG(m.sender_id) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_sender_id,
3963
+ LAG(m.ts) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_ts
4021
3964
  FROM message_context mc JOIN message m ON m.id = mc.message_id
4022
- WHERE mc.session_id IN (${placeholders}) AND m.type = 0
3965
+ WHERE mc.segment_id IN (${placeholders}) AND m.type = 0
4023
3966
  )
4024
3967
  SELECT strftime('%Y-%m', datetime(ts, 'unixepoch', 'localtime')) AS month,
4025
3968
  sender_id AS responder_id, SUM(ts - prev_ts) AS total_time, COUNT(*) AS response_count
@@ -4043,10 +3986,10 @@ function queryMessageLevelStats(db, sessionIds, memberNames, perseveranceThresho
4043
3986
  const dtRows = db.prepare(
4044
3987
  `WITH msg_lag AS (
4045
3988
  SELECT m.sender_id, m.ts,
4046
- LAG(m.sender_id) OVER (PARTITION BY mc.session_id ORDER BY m.ts, m.id) AS prev_sender_id,
4047
- LAG(m.ts) OVER (PARTITION BY mc.session_id ORDER BY m.ts, m.id) AS prev_ts
3989
+ LAG(m.sender_id) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_sender_id,
3990
+ LAG(m.ts) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_ts
4048
3991
  FROM message_context mc JOIN message m ON m.id = mc.message_id
4049
- WHERE mc.session_id IN (${placeholders}) AND m.type = 0
3992
+ WHERE mc.segment_id IN (${placeholders}) AND m.type = 0
4050
3993
  )
4051
3994
  SELECT strftime('%Y-%m', datetime(ts, 'unixepoch', 'localtime')) AS month,
4052
3995
  sender_id, COUNT(*) AS double_text_count
@@ -4258,125 +4201,6 @@ function getLanguagePreferenceAnalysis(db, params) {
4258
4201
  return { members: memberProfiles, sharedWords, similarityScore };
4259
4202
  }
4260
4203
 
4261
- // ../../packages/core/src/schema/tables.ts
4262
- var CURRENT_SCHEMA_VERSION = 5;
4263
- var CHAT_DB_TABLES = `
4264
- CREATE TABLE IF NOT EXISTS meta (
4265
- name TEXT NOT NULL,
4266
- platform TEXT NOT NULL,
4267
- type TEXT NOT NULL,
4268
- imported_at INTEGER NOT NULL,
4269
- group_id TEXT,
4270
- group_avatar TEXT,
4271
- owner_id TEXT,
4272
- schema_version INTEGER DEFAULT ${CURRENT_SCHEMA_VERSION},
4273
- session_gap_threshold INTEGER
4274
- );
4275
-
4276
- CREATE TABLE IF NOT EXISTS member (
4277
- id INTEGER PRIMARY KEY AUTOINCREMENT,
4278
- platform_id TEXT NOT NULL UNIQUE,
4279
- account_name TEXT,
4280
- group_nickname TEXT,
4281
- aliases TEXT DEFAULT '[]',
4282
- avatar TEXT,
4283
- roles TEXT DEFAULT '[]'
4284
- );
4285
-
4286
- CREATE TABLE IF NOT EXISTS member_name_history (
4287
- id INTEGER PRIMARY KEY AUTOINCREMENT,
4288
- member_id INTEGER NOT NULL,
4289
- name_type TEXT NOT NULL,
4290
- name TEXT NOT NULL,
4291
- start_ts INTEGER NOT NULL,
4292
- end_ts INTEGER,
4293
- FOREIGN KEY(member_id) REFERENCES member(id)
4294
- );
4295
-
4296
- CREATE TABLE IF NOT EXISTS message (
4297
- id INTEGER PRIMARY KEY AUTOINCREMENT,
4298
- sender_id INTEGER NOT NULL,
4299
- sender_account_name TEXT,
4300
- sender_group_nickname TEXT,
4301
- ts INTEGER NOT NULL,
4302
- type INTEGER NOT NULL,
4303
- content TEXT,
4304
- reply_to_message_id TEXT DEFAULT NULL,
4305
- platform_message_id TEXT DEFAULT NULL,
4306
- FOREIGN KEY(sender_id) REFERENCES member(id)
4307
- );
4308
-
4309
- CREATE TABLE IF NOT EXISTS chat_session (
4310
- id INTEGER PRIMARY KEY AUTOINCREMENT,
4311
- start_ts INTEGER NOT NULL,
4312
- end_ts INTEGER NOT NULL,
4313
- message_count INTEGER DEFAULT 0,
4314
- is_manual INTEGER DEFAULT 0,
4315
- summary TEXT
4316
- );
4317
-
4318
- CREATE TABLE IF NOT EXISTS message_context (
4319
- message_id INTEGER PRIMARY KEY,
4320
- session_id INTEGER NOT NULL,
4321
- topic_id INTEGER
4322
- );
4323
- `;
4324
- var CHAT_DB_INDEXES = `
4325
- CREATE INDEX IF NOT EXISTS idx_message_ts ON message(ts);
4326
- CREATE INDEX IF NOT EXISTS idx_message_sender ON message(sender_id);
4327
- CREATE INDEX IF NOT EXISTS idx_message_platform_id ON message(platform_message_id);
4328
- CREATE INDEX IF NOT EXISTS idx_member_name_history_member_id ON member_name_history(member_id);
4329
- CREATE INDEX IF NOT EXISTS idx_session_time ON chat_session(start_ts, end_ts);
4330
- CREATE INDEX IF NOT EXISTS idx_context_session ON message_context(session_id);
4331
- `;
4332
- var CHAT_DB_SCHEMA = CHAT_DB_TABLES + CHAT_DB_INDEXES;
4333
- var FTS_TABLE_SCHEMA = `
4334
- CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts5(
4335
- content,
4336
- content='',
4337
- content_rowid=id
4338
- );
4339
- `;
4340
-
4341
- // ../../packages/core/src/schema/migrations.ts
4342
- function getSchemaVersion(db) {
4343
- try {
4344
- const tableInfo = db.pragma("table_info(meta)");
4345
- const hasVersionColumn = tableInfo.some((col) => col.name === "schema_version");
4346
- if (!hasVersionColumn) return 0;
4347
- const result = db.prepare("SELECT schema_version FROM meta LIMIT 1").get();
4348
- return result?.schema_version ?? 0;
4349
- } catch {
4350
- return 0;
4351
- }
4352
- }
4353
- function setSchemaVersion(db, version) {
4354
- const tableInfo = db.pragma("table_info(meta)");
4355
- const hasVersionColumn = tableInfo.some((col) => col.name === "schema_version");
4356
- if (!hasVersionColumn) {
4357
- db.exec("ALTER TABLE meta ADD COLUMN schema_version INTEGER DEFAULT 0");
4358
- }
4359
- db.prepare("UPDATE meta SET schema_version = ?").run(version);
4360
- }
4361
- function needsMigration(db, targetVersion) {
4362
- return getSchemaVersion(db) < targetVersion;
4363
- }
4364
- function runMigrations(db, migrationsList, forceRepair = false) {
4365
- const currentVersion = getSchemaVersion(db);
4366
- if (!forceRepair && currentVersion >= (migrationsList.at(-1)?.version ?? 0)) {
4367
- return false;
4368
- }
4369
- const pending = forceRepair ? migrationsList : migrationsList.filter((m) => m.version > currentVersion);
4370
- if (pending.length === 0) return false;
4371
- db.transaction(() => {
4372
- for (const migration of pending) {
4373
- migration.up(db);
4374
- setSchemaVersion(db, migration.version);
4375
- }
4376
- });
4377
- return true;
4378
- }
4379
-
4380
4204
  // ../../packages/core/src/version.ts
4381
4205
  function parseStableVersion(version) {
4382
4206
  const normalized = version.trim().replace(/^v/i, "");
@@ -4410,6 +4234,218 @@ function isNewerStableVersion(latest, current) {
4410
4234
  return currentVersion.prerelease;
4411
4235
  }
4412
4236
 
4237
+ // ../../packages/core/src/chart/index.ts
4238
+ var ChartValidationError = class extends Error {
4239
+ constructor(message) {
4240
+ super(message);
4241
+ this.name = "ChartValidationError";
4242
+ }
4243
+ };
4244
+ var CHART_TYPES = /* @__PURE__ */ new Set(["bar", "line", "pie", "heatmap"]);
4245
+ function isRecord(value) {
4246
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4247
+ }
4248
+ function asFieldName(value, label) {
4249
+ if (typeof value !== "string" || value.trim().length === 0) {
4250
+ throw new ChartValidationError(`${label} must be a non-empty field name`);
4251
+ }
4252
+ return value;
4253
+ }
4254
+ function assertFieldExists(rows, field) {
4255
+ if (rows.length === 0) return;
4256
+ if (!Object.prototype.hasOwnProperty.call(rows[0], field)) {
4257
+ throw new ChartValidationError(`Field "${field}" does not exist in SQL result`);
4258
+ }
4259
+ }
4260
+ function toLabel(value) {
4261
+ if (value === null || value === void 0 || value === "") return "(empty)";
4262
+ return String(value);
4263
+ }
4264
+ function toNumber(value, field) {
4265
+ const num = typeof value === "number" ? value : Number(value);
4266
+ if (!Number.isFinite(num)) {
4267
+ throw new ChartValidationError(`Field "${field}" contains non-numeric value "${String(value)}"`);
4268
+ }
4269
+ return num;
4270
+ }
4271
+ function inferFieldType(values) {
4272
+ const present = values.filter((v) => v !== null && v !== void 0);
4273
+ if (present.length === 0) return "string";
4274
+ if (present.every((v) => typeof v === "number" && Number.isInteger(v))) return "integer";
4275
+ if (present.every(
4276
+ (v) => typeof v === "number" || typeof v === "string" && v.trim() !== "" && !Number.isNaN(Number(v))
4277
+ )) {
4278
+ return "number";
4279
+ }
4280
+ if (present.every((v) => typeof v === "boolean")) return "boolean";
4281
+ return "category";
4282
+ }
4283
+ function normalizeChartSpec(raw) {
4284
+ if (!isRecord(raw)) throw new ChartValidationError("chartSpec must be an object");
4285
+ if (raw.version !== 1) throw new ChartValidationError("chartSpec.version must be 1");
4286
+ if (typeof raw.type !== "string" || !CHART_TYPES.has(raw.type)) {
4287
+ throw new ChartValidationError("chartSpec.type must be one of: bar, line, pie, heatmap");
4288
+ }
4289
+ if (typeof raw.title !== "string" || raw.title.trim().length === 0) {
4290
+ throw new ChartValidationError("chartSpec.title must be a non-empty string");
4291
+ }
4292
+ if (!isRecord(raw.encoding)) throw new ChartValidationError("chartSpec.encoding must be an object");
4293
+ const spec = raw;
4294
+ switch (spec.type) {
4295
+ case "bar":
4296
+ case "line":
4297
+ asFieldName(spec.encoding.x, "encoding.x");
4298
+ asFieldName(spec.encoding.y, "encoding.y");
4299
+ break;
4300
+ case "pie":
4301
+ asFieldName(spec.encoding.label, "encoding.label");
4302
+ asFieldName(spec.encoding.value, "encoding.value");
4303
+ break;
4304
+ case "heatmap":
4305
+ asFieldName(spec.encoding.x, "encoding.x");
4306
+ asFieldName(spec.encoding.y, "encoding.y");
4307
+ asFieldName(spec.encoding.value, "encoding.value");
4308
+ break;
4309
+ }
4310
+ return {
4311
+ ...spec,
4312
+ title: spec.title.trim()
4313
+ };
4314
+ }
4315
+ function inferChartDataset(rows, declaredFields) {
4316
+ const declared = new Map((declaredFields ?? []).map((field) => [field.name, field]));
4317
+ const columnNames = rows.length > 0 ? Object.keys(rows[0]) : [...declared.keys()];
4318
+ const columns = columnNames.map((name) => {
4319
+ const declaredField = declared.get(name);
4320
+ if (declaredField) return declaredField;
4321
+ return {
4322
+ name,
4323
+ type: inferFieldType(rows.map((row) => row[name]))
4324
+ };
4325
+ });
4326
+ return { columns, rows };
4327
+ }
4328
+ function aggregateByLabel(rows, labelField, valueField) {
4329
+ const labels = [];
4330
+ const indexByLabel = /* @__PURE__ */ new Map();
4331
+ const values = [];
4332
+ for (const row of rows) {
4333
+ const label = toLabel(row[labelField]);
4334
+ let index = indexByLabel.get(label);
4335
+ if (index === void 0) {
4336
+ index = labels.length;
4337
+ indexByLabel.set(label, index);
4338
+ labels.push(label);
4339
+ values.push(0);
4340
+ }
4341
+ values[index] += toNumber(row[valueField], valueField);
4342
+ }
4343
+ return { labels, values };
4344
+ }
4345
+ function buildLineData(rows, spec) {
4346
+ const xField = spec.encoding.x;
4347
+ const yField = spec.encoding.y;
4348
+ const seriesField = spec.encoding.series;
4349
+ if (!seriesField) return aggregateByLabel(rows, xField, yField);
4350
+ assertFieldExists(rows, seriesField);
4351
+ const labels = [];
4352
+ const labelIndex = /* @__PURE__ */ new Map();
4353
+ const seriesNames = [];
4354
+ const seriesIndex = /* @__PURE__ */ new Map();
4355
+ const matrix = [];
4356
+ for (const row of rows) {
4357
+ const label = toLabel(row[xField]);
4358
+ const seriesName = toLabel(row[seriesField]);
4359
+ let xIndex = labelIndex.get(label);
4360
+ if (xIndex === void 0) {
4361
+ xIndex = labels.length;
4362
+ labelIndex.set(label, xIndex);
4363
+ labels.push(label);
4364
+ for (const values of matrix) values.push(0);
4365
+ }
4366
+ let sIndex = seriesIndex.get(seriesName);
4367
+ if (sIndex === void 0) {
4368
+ sIndex = seriesNames.length;
4369
+ seriesIndex.set(seriesName, sIndex);
4370
+ seriesNames.push(seriesName);
4371
+ matrix.push(Array(labels.length).fill(0));
4372
+ }
4373
+ matrix[sIndex][xIndex] += toNumber(row[yField], yField);
4374
+ }
4375
+ const series = seriesNames.map((name, index) => ({ name, values: matrix[index] }));
4376
+ return {
4377
+ labels,
4378
+ values: series[0]?.values ?? [],
4379
+ series
4380
+ };
4381
+ }
4382
+ function buildHeatmapData(rows, spec) {
4383
+ const xField = spec.encoding.x;
4384
+ const yField = spec.encoding.y;
4385
+ const valueField = spec.encoding.value;
4386
+ const xLabels = [];
4387
+ const yLabels = [];
4388
+ const xIndex = /* @__PURE__ */ new Map();
4389
+ const yIndex = /* @__PURE__ */ new Map();
4390
+ const values = /* @__PURE__ */ new Map();
4391
+ for (const row of rows) {
4392
+ const xLabel = toLabel(row[xField]);
4393
+ const yLabel = toLabel(row[yField]);
4394
+ let xi = xIndex.get(xLabel);
4395
+ if (xi === void 0) {
4396
+ xi = xLabels.length;
4397
+ xIndex.set(xLabel, xi);
4398
+ xLabels.push(xLabel);
4399
+ }
4400
+ let yi = yIndex.get(yLabel);
4401
+ if (yi === void 0) {
4402
+ yi = yLabels.length;
4403
+ yIndex.set(yLabel, yi);
4404
+ yLabels.push(yLabel);
4405
+ }
4406
+ const key = `${xi}:${yi}`;
4407
+ values.set(key, (values.get(key) ?? 0) + toNumber(row[valueField], valueField));
4408
+ }
4409
+ const data = [];
4410
+ for (const [key, value] of values.entries()) {
4411
+ const [x, y] = key.split(":").map(Number);
4412
+ data.push([x, y, value]);
4413
+ }
4414
+ return { xLabels, yLabels, data };
4415
+ }
4416
+ function buildChartPayload(rows, rawSpec, options) {
4417
+ const spec = normalizeChartSpec(rawSpec);
4418
+ const dataset = inferChartDataset(rows, spec.fields);
4419
+ const requiredFields = /* @__PURE__ */ new Set();
4420
+ for (const field of Object.values(spec.encoding)) {
4421
+ if (typeof field === "string" && field) requiredFields.add(field);
4422
+ }
4423
+ for (const field of requiredFields) assertFieldExists(rows, field);
4424
+ let data;
4425
+ switch (spec.type) {
4426
+ case "bar":
4427
+ data = aggregateByLabel(rows, spec.encoding.x, spec.encoding.y);
4428
+ break;
4429
+ case "line":
4430
+ data = buildLineData(rows, spec);
4431
+ break;
4432
+ case "pie":
4433
+ data = aggregateByLabel(rows, spec.encoding.label, spec.encoding.value);
4434
+ break;
4435
+ case "heatmap":
4436
+ data = buildHeatmapData(rows, spec);
4437
+ break;
4438
+ }
4439
+ return {
4440
+ version: 1,
4441
+ spec,
4442
+ dataset,
4443
+ data,
4444
+ rowCount: rows.length,
4445
+ truncated: options?.truncated
4446
+ };
4447
+ }
4448
+
4413
4449
  // ../../packages/core/src/nlp/pos-tags.ts
4414
4450
  var POS_TAG_DEFINITIONS = [
4415
4451
  { tag: "n", name: "\u540D\u8BCD", description: "\u666E\u901A\u540D\u8BCD", meaningful: true },
@@ -5238,6 +5274,17 @@ function isValidWord(word, locale, minLength, enableStopwords, isStopwordFn) {
5238
5274
  }
5239
5275
 
5240
5276
  // ../../packages/core/src/ai/tool-catalog.ts
5277
+ var LEGACY_BUILTIN_TOOL_NAME_ALIASES = {
5278
+ search_sessions: "search_segments",
5279
+ get_session_messages: "get_segment_messages",
5280
+ get_session_summaries: "get_segment_summaries"
5281
+ };
5282
+ function normalizeBuiltinToolName(toolName) {
5283
+ return LEGACY_BUILTIN_TOOL_NAME_ALIASES[toolName] ?? toolName;
5284
+ }
5285
+ function normalizeBuiltinToolNames(toolNames) {
5286
+ return Array.from(new Set(toolNames.map(normalizeBuiltinToolName)));
5287
+ }
5241
5288
  var BUILTIN_TOOL_CATALOG = [
5242
5289
  // Core 工具
5243
5290
  { name: "get_chat_overview", category: "core" },
@@ -5245,17 +5292,20 @@ var BUILTIN_TOOL_CATALOG = [
5245
5292
  { name: "deep_search_messages", category: "core" },
5246
5293
  { name: "get_recent_messages", category: "core" },
5247
5294
  { name: "get_message_context", category: "core" },
5248
- { name: "search_sessions", category: "core" },
5249
- { name: "get_session_messages", category: "core" },
5295
+ { name: "search_segments", category: "core" },
5296
+ { name: "get_segment_messages", category: "core" },
5250
5297
  { name: "get_members", category: "core" },
5298
+ { name: "get_schema", category: "core" },
5251
5299
  // Analysis 工具
5252
5300
  { name: "get_member_stats", category: "analysis" },
5253
5301
  { name: "get_time_stats", category: "analysis" },
5254
5302
  { name: "get_member_name_history", category: "analysis" },
5255
5303
  { name: "get_conversation_between", category: "analysis" },
5256
- { name: "get_session_summaries", category: "analysis" },
5304
+ { name: "get_segment_summaries", category: "analysis" },
5257
5305
  { name: "response_time_analysis", category: "analysis" },
5258
5306
  { name: "keyword_frequency", category: "analysis" },
5307
+ { name: "render_chart", category: "analysis" },
5308
+ { name: "execute_sql", category: "analysis" },
5259
5309
  // SQL Analysis 工具
5260
5310
  { name: "message_type_breakdown", category: "analysis" },
5261
5311
  { name: "peak_chat_hours_by_member", category: "analysis" },
@@ -5270,6 +5320,9 @@ var BUILTIN_TOOL_CATALOG = [
5270
5320
  { name: "unanswered_messages", category: "analysis" }
5271
5321
  ];
5272
5322
 
5323
+ // ../../packages/core/src/ai/chart-capability.ts
5324
+ var CHART_CAPABILITY_SKILL_ID = "chart_runtime";
5325
+
5273
5326
  // ../../packages/core/src/ai/provider-registry.ts
5274
5327
  var OPENAI = {
5275
5328
  id: "openai",
@@ -6783,6 +6836,125 @@ function mergeMembers2(sources, collidingIds) {
6783
6836
  return map;
6784
6837
  }
6785
6838
 
6839
+ // ../../packages/core/src/schema/tables.ts
6840
+ var CURRENT_SCHEMA_VERSION = 6;
6841
+ var CHAT_DB_TABLES = `
6842
+ CREATE TABLE IF NOT EXISTS meta (
6843
+ name TEXT NOT NULL,
6844
+ platform TEXT NOT NULL,
6845
+ type TEXT NOT NULL,
6846
+ imported_at INTEGER NOT NULL,
6847
+ group_id TEXT,
6848
+ group_avatar TEXT,
6849
+ owner_id TEXT,
6850
+ schema_version INTEGER DEFAULT ${CURRENT_SCHEMA_VERSION},
6851
+ session_gap_threshold INTEGER
6852
+ );
6853
+
6854
+ CREATE TABLE IF NOT EXISTS member (
6855
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6856
+ platform_id TEXT NOT NULL UNIQUE,
6857
+ account_name TEXT,
6858
+ group_nickname TEXT,
6859
+ aliases TEXT DEFAULT '[]',
6860
+ avatar TEXT,
6861
+ roles TEXT DEFAULT '[]'
6862
+ );
6863
+
6864
+ CREATE TABLE IF NOT EXISTS member_name_history (
6865
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6866
+ member_id INTEGER NOT NULL,
6867
+ name_type TEXT NOT NULL,
6868
+ name TEXT NOT NULL,
6869
+ start_ts INTEGER NOT NULL,
6870
+ end_ts INTEGER,
6871
+ FOREIGN KEY(member_id) REFERENCES member(id)
6872
+ );
6873
+
6874
+ CREATE TABLE IF NOT EXISTS message (
6875
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6876
+ sender_id INTEGER NOT NULL,
6877
+ sender_account_name TEXT,
6878
+ sender_group_nickname TEXT,
6879
+ ts INTEGER NOT NULL,
6880
+ type INTEGER NOT NULL,
6881
+ content TEXT,
6882
+ reply_to_message_id TEXT DEFAULT NULL,
6883
+ platform_message_id TEXT DEFAULT NULL,
6884
+ FOREIGN KEY(sender_id) REFERENCES member(id)
6885
+ );
6886
+
6887
+ CREATE TABLE IF NOT EXISTS segment (
6888
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6889
+ start_ts INTEGER NOT NULL,
6890
+ end_ts INTEGER NOT NULL,
6891
+ message_count INTEGER DEFAULT 0,
6892
+ is_manual INTEGER DEFAULT 0,
6893
+ summary TEXT
6894
+ );
6895
+
6896
+ CREATE TABLE IF NOT EXISTS message_context (
6897
+ message_id INTEGER PRIMARY KEY,
6898
+ segment_id INTEGER NOT NULL,
6899
+ topic_id INTEGER
6900
+ );
6901
+ `;
6902
+ var CHAT_DB_INDEXES = `
6903
+ CREATE INDEX IF NOT EXISTS idx_message_ts ON message(ts);
6904
+ CREATE INDEX IF NOT EXISTS idx_message_sender ON message(sender_id);
6905
+ CREATE INDEX IF NOT EXISTS idx_message_platform_id ON message(platform_message_id);
6906
+ CREATE INDEX IF NOT EXISTS idx_member_name_history_member_id ON member_name_history(member_id);
6907
+ CREATE INDEX IF NOT EXISTS idx_segment_time ON segment(start_ts, end_ts);
6908
+ CREATE INDEX IF NOT EXISTS idx_context_segment ON message_context(segment_id);
6909
+ `;
6910
+ var CHAT_DB_SCHEMA = CHAT_DB_TABLES + CHAT_DB_INDEXES;
6911
+ var FTS_TABLE_SCHEMA = `
6912
+ CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts5(
6913
+ content,
6914
+ content='',
6915
+ content_rowid=id
6916
+ );
6917
+ `;
6918
+
6919
+ // ../../packages/core/src/schema/migrations.ts
6920
+ function getSchemaVersion(db) {
6921
+ try {
6922
+ const tableInfo = db.pragma("table_info(meta)");
6923
+ const hasVersionColumn = tableInfo.some((col) => col.name === "schema_version");
6924
+ if (!hasVersionColumn) return 0;
6925
+ const result = db.prepare("SELECT schema_version FROM meta LIMIT 1").get();
6926
+ return result?.schema_version ?? 0;
6927
+ } catch {
6928
+ return 0;
6929
+ }
6930
+ }
6931
+ function setSchemaVersion(db, version) {
6932
+ const tableInfo = db.pragma("table_info(meta)");
6933
+ const hasVersionColumn = tableInfo.some((col) => col.name === "schema_version");
6934
+ if (!hasVersionColumn) {
6935
+ db.exec("ALTER TABLE meta ADD COLUMN schema_version INTEGER DEFAULT 0");
6936
+ }
6937
+ db.prepare("UPDATE meta SET schema_version = ?").run(version);
6938
+ }
6939
+ function needsMigration(db, targetVersion) {
6940
+ return getSchemaVersion(db) < targetVersion;
6941
+ }
6942
+ function runMigrations(db, migrationsList, forceRepair = false) {
6943
+ const currentVersion = getSchemaVersion(db);
6944
+ if (!forceRepair && currentVersion >= (migrationsList.at(-1)?.version ?? 0)) {
6945
+ return false;
6946
+ }
6947
+ const pending = forceRepair ? migrationsList : migrationsList.filter((m) => m.version > currentVersion);
6948
+ if (pending.length === 0) return false;
6949
+ db.transaction(() => {
6950
+ for (const migration of pending) {
6951
+ migration.up(db);
6952
+ setSchemaVersion(db, migration.version);
6953
+ }
6954
+ });
6955
+ return true;
6956
+ }
6957
+
6786
6958
  // ../../packages/parser/src/index.ts
6787
6959
  import * as fs24 from "fs";
6788
6960
  import * as path20 from "path";
@@ -11989,17 +12161,17 @@ function writeParseResultToDb(db, meta, members, messages) {
11989
12161
  // ../../packages/node-runtime/src/import/perf-logger.ts
11990
12162
  var lastLogTime = Date.now();
11991
12163
 
11992
- // ../../packages/node-runtime/src/ai/conversations.ts
11993
- import Database2 from "better-sqlite3";
12164
+ // ../../packages/node-runtime/src/ai/chats.ts
12165
+ import Database from "better-sqlite3";
11994
12166
  import * as fs28 from "fs";
11995
12167
  import * as path23 from "path";
11996
12168
  var DEFAULT_GENERAL_ID = "general_cn";
11997
12169
  var defaultLogger = {
11998
12170
  warn(_category, message, extra) {
11999
- console.warn(`[AI Conversations] ${message}`, extra ?? "");
12171
+ console.warn(`[AI Chats] ${message}`, extra ?? "");
12000
12172
  }
12001
12173
  };
12002
- var AIConversationManager = class {
12174
+ var AIChatManager = class {
12003
12175
  db = null;
12004
12176
  aiDataDir;
12005
12177
  logger;
@@ -12019,13 +12191,14 @@ var AIConversationManager = class {
12019
12191
  if (this.db) return this.db;
12020
12192
  this.ensureDir(this.aiDataDir);
12021
12193
  const dbPath = path23.join(this.aiDataDir, "conversations.db");
12022
- this.db = this.nativeBinding ? new Database2(dbPath, { nativeBinding: this.nativeBinding }) : new Database2(dbPath);
12194
+ this.db = this.nativeBinding ? new Database(dbPath, { nativeBinding: this.nativeBinding }) : new Database(dbPath);
12023
12195
  this.db.pragma("journal_mode = WAL");
12024
12196
  this.db.exec(`
12025
- CREATE TABLE IF NOT EXISTS ai_conversation (
12197
+ CREATE TABLE IF NOT EXISTS ai_chat (
12026
12198
  id TEXT PRIMARY KEY,
12027
12199
  session_id TEXT NOT NULL,
12028
12200
  title TEXT,
12201
+ assistant_id TEXT DEFAULT '${DEFAULT_GENERAL_ID}',
12029
12202
  active_message_id TEXT,
12030
12203
  created_at INTEGER NOT NULL,
12031
12204
  updated_at INTEGER NOT NULL
@@ -12033,7 +12206,7 @@ var AIConversationManager = class {
12033
12206
 
12034
12207
  CREATE TABLE IF NOT EXISTS ai_message (
12035
12208
  id TEXT PRIMARY KEY,
12036
- conversation_id TEXT NOT NULL,
12209
+ ai_chat_id TEXT NOT NULL,
12037
12210
  role TEXT NOT NULL,
12038
12211
  content TEXT NOT NULL,
12039
12212
  timestamp INTEGER NOT NULL,
@@ -12043,51 +12216,113 @@ var AIConversationManager = class {
12043
12216
  parent_id TEXT,
12044
12217
  sibling_group_id TEXT,
12045
12218
  branch_index INTEGER DEFAULT 0,
12046
- FOREIGN KEY(conversation_id) REFERENCES ai_conversation(id) ON DELETE CASCADE
12219
+ debug_context TEXT,
12220
+ token_usage TEXT,
12221
+ FOREIGN KEY(ai_chat_id) REFERENCES ai_chat(id) ON DELETE CASCADE
12047
12222
  );
12048
12223
 
12049
- CREATE INDEX IF NOT EXISTS idx_ai_conversation_session ON ai_conversation(session_id);
12050
- CREATE INDEX IF NOT EXISTS idx_ai_message_conversation ON ai_message(conversation_id);
12051
12224
  `);
12052
12225
  this.migrateDatabase(this.db);
12053
12226
  return this.db;
12054
12227
  }
12228
+ tableExists(db, tableName) {
12229
+ const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(tableName);
12230
+ return !!row;
12231
+ }
12232
+ getTableColumns(db, tableName) {
12233
+ const tableInfo = db.pragma(`table_info(${tableName})`);
12234
+ return tableInfo.map((col) => col.name);
12235
+ }
12236
+ ensureMessageMigrationColumns(db, tableName) {
12237
+ const messageColumns = this.getTableColumns(db, tableName);
12238
+ if (!messageColumns.includes("content_blocks")) {
12239
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN content_blocks TEXT`);
12240
+ }
12241
+ if (!messageColumns.includes("token_usage")) {
12242
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN token_usage TEXT`);
12243
+ }
12244
+ if (!messageColumns.includes("debug_context")) {
12245
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN debug_context TEXT`);
12246
+ }
12247
+ if (!messageColumns.includes("parent_id")) {
12248
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN parent_id TEXT`);
12249
+ }
12250
+ if (!messageColumns.includes("sibling_group_id")) {
12251
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN sibling_group_id TEXT`);
12252
+ }
12253
+ if (!messageColumns.includes("branch_index")) {
12254
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN branch_index INTEGER DEFAULT 0`);
12255
+ }
12256
+ }
12055
12257
  migrateDatabase(db) {
12056
12258
  try {
12057
- const messageTableInfo = db.pragma("table_info(ai_message)");
12058
- const messageColumns = messageTableInfo.map((col) => col.name);
12059
- const needsMessageTreeBackfill = !messageColumns.includes("parent_id") || !messageColumns.includes("sibling_group_id") || !messageColumns.includes("branch_index");
12060
- if (!messageColumns.includes("content_blocks")) {
12061
- db.exec("ALTER TABLE ai_message ADD COLUMN content_blocks TEXT");
12062
- }
12063
- if (!messageColumns.includes("token_usage")) {
12064
- db.exec("ALTER TABLE ai_message ADD COLUMN token_usage TEXT");
12065
- }
12066
- if (!messageColumns.includes("debug_context")) {
12067
- db.exec("ALTER TABLE ai_message ADD COLUMN debug_context TEXT");
12068
- }
12069
- if (!messageColumns.includes("parent_id")) {
12070
- db.exec("ALTER TABLE ai_message ADD COLUMN parent_id TEXT");
12071
- }
12072
- if (!messageColumns.includes("sibling_group_id")) {
12073
- db.exec("ALTER TABLE ai_message ADD COLUMN sibling_group_id TEXT");
12074
- }
12075
- if (!messageColumns.includes("branch_index")) {
12076
- db.exec("ALTER TABLE ai_message ADD COLUMN branch_index INTEGER DEFAULT 0");
12259
+ const hasLegacyConversationTable = this.tableExists(db, "ai_conversation");
12260
+ if (hasLegacyConversationTable) {
12261
+ const convColumns = this.getTableColumns(db, "ai_conversation");
12262
+ if (!convColumns.includes("assistant_id")) {
12263
+ db.exec(`ALTER TABLE ai_conversation ADD COLUMN assistant_id TEXT DEFAULT '${DEFAULT_GENERAL_ID}'`);
12264
+ }
12265
+ if (!convColumns.includes("active_message_id")) {
12266
+ db.exec("ALTER TABLE ai_conversation ADD COLUMN active_message_id TEXT");
12267
+ }
12268
+ db.exec(`
12269
+ INSERT OR IGNORE INTO ai_chat (
12270
+ id, session_id, title, assistant_id, active_message_id, created_at, updated_at
12271
+ )
12272
+ SELECT id, session_id, title, COALESCE(assistant_id, '${DEFAULT_GENERAL_ID}'),
12273
+ active_message_id, created_at, updated_at
12274
+ FROM ai_conversation
12275
+ `);
12077
12276
  }
12078
- const convTableInfo = db.pragma("table_info(ai_conversation)");
12079
- const convColumns = convTableInfo.map((col) => col.name);
12080
- const needsConversationBackfill = !convColumns.includes("active_message_id");
12081
- if (!convColumns.includes("assistant_id")) {
12082
- db.exec(`ALTER TABLE ai_conversation ADD COLUMN assistant_id TEXT DEFAULT '${DEFAULT_GENERAL_ID}'`);
12277
+ this.ensureMessageMigrationColumns(db, "ai_message");
12278
+ const messageColumns = this.getTableColumns(db, "ai_message");
12279
+ const hadLegacyConversationId = messageColumns.includes("conversation_id");
12280
+ const needsMessageTreeBackfill = !messageColumns.includes("parent_id") || !messageColumns.includes("sibling_group_id") || !messageColumns.includes("branch_index") || hadLegacyConversationId;
12281
+ if (hadLegacyConversationId && !messageColumns.includes("ai_chat_id")) {
12282
+ db.exec(`
12283
+ DROP INDEX IF EXISTS idx_ai_message_conversation;
12284
+ ALTER TABLE ai_message RENAME TO ai_message_legacy;
12285
+
12286
+ CREATE TABLE ai_message (
12287
+ id TEXT PRIMARY KEY,
12288
+ ai_chat_id TEXT NOT NULL,
12289
+ role TEXT NOT NULL,
12290
+ content TEXT NOT NULL,
12291
+ timestamp INTEGER NOT NULL,
12292
+ data_keywords TEXT,
12293
+ data_message_count INTEGER,
12294
+ content_blocks TEXT,
12295
+ parent_id TEXT,
12296
+ sibling_group_id TEXT,
12297
+ branch_index INTEGER DEFAULT 0,
12298
+ debug_context TEXT,
12299
+ token_usage TEXT,
12300
+ FOREIGN KEY(ai_chat_id) REFERENCES ai_chat(id) ON DELETE CASCADE
12301
+ );
12302
+
12303
+ INSERT INTO ai_message (
12304
+ id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count,
12305
+ content_blocks, parent_id, sibling_group_id, branch_index, debug_context, token_usage
12306
+ )
12307
+ SELECT id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12308
+ content_blocks, parent_id, sibling_group_id, branch_index, debug_context, token_usage
12309
+ FROM ai_message_legacy;
12310
+
12311
+ DROP TABLE ai_message_legacy;
12312
+ `);
12083
12313
  }
12084
- if (!convColumns.includes("active_message_id")) {
12085
- db.exec("ALTER TABLE ai_conversation ADD COLUMN active_message_id TEXT");
12314
+ if (hasLegacyConversationTable) {
12315
+ db.exec(`
12316
+ DROP INDEX IF EXISTS idx_ai_conversation_session;
12317
+ DROP TABLE ai_conversation;
12318
+ `);
12086
12319
  }
12087
- if (needsMessageTreeBackfill || needsConversationBackfill || this.hasUnbackfilledMessageTree(db)) {
12320
+ if (needsMessageTreeBackfill || this.hasUnbackfilledMessageTree(db)) {
12088
12321
  this.backfillMessageTree(db);
12089
12322
  }
12090
12323
  db.exec(`
12324
+ CREATE INDEX IF NOT EXISTS idx_ai_chat_session ON ai_chat(session_id);
12325
+ CREATE INDEX IF NOT EXISTS idx_ai_message_ai_chat ON ai_message(ai_chat_id);
12091
12326
  CREATE INDEX IF NOT EXISTS idx_ai_message_parent ON ai_message(parent_id);
12092
12327
  CREATE INDEX IF NOT EXISTS idx_ai_message_sibling ON ai_message(sibling_group_id);
12093
12328
  `);
@@ -12098,16 +12333,16 @@ var AIConversationManager = class {
12098
12333
  hasUnbackfilledMessageTree(db) {
12099
12334
  const row = db.prepare(
12100
12335
  `SELECT 1
12101
- FROM ai_conversation c
12336
+ FROM ai_chat c
12102
12337
  WHERE EXISTS (
12103
12338
  SELECT 1 FROM ai_message m
12104
- WHERE m.conversation_id = c.id
12339
+ WHERE m.ai_chat_id = c.id
12105
12340
  )
12106
12341
  AND (
12107
12342
  c.active_message_id IS NULL
12108
12343
  OR EXISTS (
12109
12344
  SELECT 1 FROM ai_message m
12110
- WHERE m.conversation_id = c.id
12345
+ WHERE m.ai_chat_id = c.id
12111
12346
  AND (m.sibling_group_id IS NULL OR m.branch_index IS NULL)
12112
12347
  )
12113
12348
  )
@@ -12116,26 +12351,26 @@ var AIConversationManager = class {
12116
12351
  return !!row;
12117
12352
  }
12118
12353
  backfillMessageTree(db) {
12119
- const conversations = db.prepare("SELECT id FROM ai_conversation").all();
12354
+ const aiChats = db.prepare("SELECT id FROM ai_chat").all();
12120
12355
  const updateMessage = db.prepare(
12121
12356
  "UPDATE ai_message SET parent_id = ?, sibling_group_id = COALESCE(sibling_group_id, ?), branch_index = COALESCE(branch_index, 0) WHERE id = ?"
12122
12357
  );
12123
- const updateConversation = db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?");
12358
+ const updateAIChat = db.prepare("UPDATE ai_chat SET active_message_id = ? WHERE id = ?");
12124
12359
  const tx = db.transaction(() => {
12125
- for (const conversation of conversations) {
12360
+ for (const aiChat of aiChats) {
12126
12361
  const messages = db.prepare(
12127
12362
  `SELECT id, parent_id as parentId, sibling_group_id as siblingGroupId
12128
- FROM ai_message WHERE conversation_id = ? ORDER BY timestamp ASC, id ASC`
12129
- ).all(conversation.id);
12363
+ FROM ai_message WHERE ai_chat_id = ? ORDER BY timestamp ASC, id ASC`
12364
+ ).all(aiChat.id);
12130
12365
  let previousId = null;
12131
12366
  for (const message of messages) {
12132
12367
  const parentId = message.parentId === void 0 ? previousId : message.parentId ?? previousId;
12133
12368
  updateMessage.run(parentId, message.siblingGroupId ?? message.id, message.id);
12134
12369
  previousId = message.id;
12135
12370
  }
12136
- const conversationRow = db.prepare("SELECT active_message_id as activeMessageId FROM ai_conversation WHERE id = ?").get(conversation.id);
12137
- if (!conversationRow?.activeMessageId && previousId) {
12138
- updateConversation.run(previousId, conversation.id);
12371
+ const aiChatRow = db.prepare("SELECT active_message_id as activeMessageId FROM ai_chat WHERE id = ?").get(aiChat.id);
12372
+ if (!aiChatRow?.activeMessageId && previousId) {
12373
+ updateAIChat.run(previousId, aiChat.id);
12139
12374
  }
12140
12375
  }
12141
12376
  });
@@ -12147,7 +12382,7 @@ var AIConversationManager = class {
12147
12382
  parseMessageRow(row) {
12148
12383
  return {
12149
12384
  id: row.id,
12150
- conversationId: row.conversationId,
12385
+ aiChatId: row.aiChatId,
12151
12386
  role: row.role,
12152
12387
  content: row.content,
12153
12388
  timestamp: row.timestamp,
@@ -12161,7 +12396,7 @@ var AIConversationManager = class {
12161
12396
  getMessageRow(messageId) {
12162
12397
  const db = this.getDb();
12163
12398
  const row = db.prepare(
12164
- `SELECT id, conversation_id as conversationId, role, content, timestamp,
12399
+ `SELECT id, ai_chat_id as aiChatId, role, content, timestamp,
12165
12400
  parent_id as parentId, sibling_group_id as siblingGroupId, branch_index as branchIndex,
12166
12401
  data_keywords as dataKeywords, data_message_count as dataMessageCount,
12167
12402
  content_blocks as contentBlocks, token_usage as tokenUsage
@@ -12169,45 +12404,45 @@ var AIConversationManager = class {
12169
12404
  ).get(messageId);
12170
12405
  return row ?? null;
12171
12406
  }
12172
- getActiveMessageId(conversationId) {
12407
+ getActiveMessageId(aiChatId) {
12173
12408
  const db = this.getDb();
12174
- const row = db.prepare("SELECT active_message_id as activeMessageId FROM ai_conversation WHERE id = ?").get(conversationId);
12409
+ const row = db.prepare("SELECT active_message_id as activeMessageId FROM ai_chat WHERE id = ?").get(aiChatId);
12175
12410
  if (row?.activeMessageId) {
12176
12411
  const activeExists = db.prepare("SELECT 1 FROM ai_message WHERE id = ?").get(row.activeMessageId);
12177
12412
  if (activeExists) return row.activeMessageId;
12178
12413
  }
12179
- const fallback = db.prepare("SELECT id FROM ai_message WHERE conversation_id = ? ORDER BY timestamp DESC, id DESC LIMIT 1").get(conversationId);
12414
+ const fallback = db.prepare("SELECT id FROM ai_message WHERE ai_chat_id = ? ORDER BY timestamp DESC, id DESC LIMIT 1").get(aiChatId);
12180
12415
  if (fallback?.id) {
12181
- db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?").run(fallback.id, conversationId);
12416
+ db.prepare("UPDATE ai_chat SET active_message_id = ? WHERE id = ?").run(fallback.id, aiChatId);
12182
12417
  return fallback.id;
12183
12418
  }
12184
12419
  return null;
12185
12420
  }
12186
- getAllMessageRows(conversationId) {
12421
+ getAllMessageRows(aiChatId) {
12187
12422
  return this.getDb().prepare(
12188
- `SELECT id, conversation_id as conversationId, role, content, timestamp,
12423
+ `SELECT id, ai_chat_id as aiChatId, role, content, timestamp,
12189
12424
  parent_id as parentId, sibling_group_id as siblingGroupId, branch_index as branchIndex,
12190
12425
  data_keywords as dataKeywords, data_message_count as dataMessageCount,
12191
12426
  content_blocks as contentBlocks, token_usage as tokenUsage
12192
- FROM ai_message WHERE conversation_id = ? ORDER BY timestamp ASC, id ASC`
12193
- ).all(conversationId);
12427
+ FROM ai_message WHERE ai_chat_id = ? ORDER BY timestamp ASC, id ASC`
12428
+ ).all(aiChatId);
12194
12429
  }
12195
- getActivePathRows(conversationId, leafMessageId) {
12430
+ getActivePathRows(aiChatId, leafMessageId) {
12196
12431
  if (leafMessageId === null) return [];
12197
- const allRows = this.getAllMessageRows(conversationId);
12432
+ const allRows = this.getAllMessageRows(aiChatId);
12198
12433
  if (allRows.length === 0) return [];
12199
12434
  const rowMap = new Map(allRows.map((row) => [row.id, row]));
12200
- let currentId = leafMessageId ?? this.getActiveMessageId(conversationId);
12201
- const path35 = [];
12435
+ let currentId = leafMessageId ?? this.getActiveMessageId(aiChatId);
12436
+ const path36 = [];
12202
12437
  const seen = /* @__PURE__ */ new Set();
12203
12438
  while (currentId && !seen.has(currentId)) {
12204
12439
  seen.add(currentId);
12205
12440
  const row = rowMap.get(currentId);
12206
12441
  if (!row) break;
12207
- path35.push(row);
12442
+ path36.push(row);
12208
12443
  currentId = row.parentId;
12209
12444
  }
12210
- return path35.length > 0 ? path35.reverse() : allRows;
12445
+ return path36.length > 0 ? path36.reverse() : allRows;
12211
12446
  }
12212
12447
  // ==================== 生命周期 ====================
12213
12448
  close() {
@@ -12263,21 +12498,21 @@ var AIConversationManager = class {
12263
12498
  }
12264
12499
  }
12265
12500
  // ==================== 对话管理 ====================
12266
- createConversation(sessionId, title, assistantId) {
12501
+ createAIChat(sessionId, title, assistantId) {
12267
12502
  const db = this.getDb();
12268
12503
  const now = Math.floor(Date.now() / 1e3);
12269
12504
  const id = this.generateId("conv");
12270
12505
  db.prepare(
12271
- `INSERT INTO ai_conversation (id, session_id, title, assistant_id, created_at, updated_at)
12506
+ `INSERT INTO ai_chat (id, session_id, title, assistant_id, created_at, updated_at)
12272
12507
  VALUES (?, ?, ?, ?, ?, ?)`
12273
12508
  ).run(id, sessionId, title || null, assistantId, now, now);
12274
12509
  return { id, sessionId, title: title || null, assistantId, activeMessageId: null, createdAt: now, updatedAt: now };
12275
12510
  }
12276
- getConversationCountsBySession() {
12511
+ getAIChatCountsBySession() {
12277
12512
  const result = /* @__PURE__ */ new Map();
12278
12513
  try {
12279
12514
  const db = this.getDb();
12280
- const rows = db.prepare("SELECT session_id, COUNT(*) as count FROM ai_conversation GROUP BY session_id").all();
12515
+ const rows = db.prepare("SELECT session_id, COUNT(*) as count FROM ai_chat GROUP BY session_id").all();
12281
12516
  for (const row of rows) {
12282
12517
  result.set(row.session_id, row.count);
12283
12518
  }
@@ -12285,58 +12520,58 @@ var AIConversationManager = class {
12285
12520
  }
12286
12521
  return result;
12287
12522
  }
12288
- getConversations(sessionId) {
12523
+ getAIChats(sessionId) {
12289
12524
  const db = this.getDb();
12290
12525
  return db.prepare(
12291
12526
  `SELECT id, session_id as sessionId, title, assistant_id as assistantId,
12292
12527
  active_message_id as activeMessageId,
12293
12528
  created_at as createdAt, updated_at as updatedAt
12294
- FROM ai_conversation WHERE session_id = ? ORDER BY updated_at DESC`
12529
+ FROM ai_chat WHERE session_id = ? ORDER BY updated_at DESC`
12295
12530
  ).all(sessionId);
12296
12531
  }
12297
- getConversation(conversationId) {
12532
+ getAIChat(aiChatId) {
12298
12533
  const db = this.getDb();
12299
12534
  const row = db.prepare(
12300
12535
  `SELECT id, session_id as sessionId, title, assistant_id as assistantId,
12301
12536
  active_message_id as activeMessageId,
12302
12537
  created_at as createdAt, updated_at as updatedAt
12303
- FROM ai_conversation WHERE id = ?`
12304
- ).get(conversationId);
12538
+ FROM ai_chat WHERE id = ?`
12539
+ ).get(aiChatId);
12305
12540
  return row || null;
12306
12541
  }
12307
- updateConversationTitle(conversationId, title) {
12542
+ updateAIChatTitle(aiChatId, title) {
12308
12543
  const db = this.getDb();
12309
12544
  const now = Math.floor(Date.now() / 1e3);
12310
- const result = db.prepare("UPDATE ai_conversation SET title = ?, updated_at = ? WHERE id = ?").run(title, now, conversationId);
12545
+ const result = db.prepare("UPDATE ai_chat SET title = ?, updated_at = ? WHERE id = ?").run(title, now, aiChatId);
12311
12546
  return result.changes > 0;
12312
12547
  }
12313
- deleteConversation(conversationId) {
12548
+ deleteAIChat(aiChatId) {
12314
12549
  const db = this.getDb();
12315
- db.prepare("DELETE FROM ai_message WHERE conversation_id = ?").run(conversationId);
12316
- const result = db.prepare("DELETE FROM ai_conversation WHERE id = ?").run(conversationId);
12550
+ db.prepare("DELETE FROM ai_message WHERE ai_chat_id = ?").run(aiChatId);
12551
+ const result = db.prepare("DELETE FROM ai_chat WHERE id = ?").run(aiChatId);
12317
12552
  return result.changes > 0;
12318
12553
  }
12319
12554
  // ==================== 消息管理 ====================
12320
- addMessage(conversationId, role, content, dataKeywords, dataMessageCount, contentBlocks, tokenUsage) {
12555
+ addMessage(aiChatId, role, content, dataKeywords, dataMessageCount, contentBlocks, tokenUsage) {
12321
12556
  const db = this.getDb();
12322
12557
  const now = Math.floor(Date.now() / 1e3);
12323
12558
  const id = this.generateId("msg");
12324
- const parentId = this.getActiveMessageId(conversationId);
12559
+ const parentId = this.getActiveMessageId(aiChatId);
12325
12560
  const siblingGroupId = id;
12326
12561
  const branchIndex = 0;
12327
- const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(conversationId) : void 0;
12562
+ const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(aiChatId) : void 0;
12328
12563
  if (pendingDebug) {
12329
- this.pendingDebugContextMap.delete(conversationId);
12564
+ this.pendingDebugContextMap.delete(aiChatId);
12330
12565
  }
12331
12566
  db.prepare(
12332
12567
  `INSERT INTO ai_message (
12333
- id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12568
+ id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count,
12334
12569
  content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12335
12570
  )
12336
12571
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
12337
12572
  ).run(
12338
12573
  id,
12339
- conversationId,
12574
+ aiChatId,
12340
12575
  role,
12341
12576
  content,
12342
12577
  now,
@@ -12349,14 +12584,14 @@ var AIConversationManager = class {
12349
12584
  siblingGroupId,
12350
12585
  branchIndex
12351
12586
  );
12352
- db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12587
+ db.prepare("UPDATE ai_chat SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12353
12588
  id,
12354
12589
  now,
12355
- conversationId
12590
+ aiChatId
12356
12591
  );
12357
12592
  return {
12358
12593
  id,
12359
- conversationId,
12594
+ aiChatId,
12360
12595
  role,
12361
12596
  content,
12362
12597
  timestamp: now,
@@ -12367,21 +12602,21 @@ var AIConversationManager = class {
12367
12602
  tokenUsage
12368
12603
  };
12369
12604
  }
12370
- getMessages(conversationId) {
12371
- return this.getActivePathRows(conversationId).map((row) => this.parseMessageRow(row));
12605
+ getMessages(aiChatId) {
12606
+ return this.getActivePathRows(aiChatId).map((row) => this.parseMessageRow(row));
12372
12607
  }
12373
12608
  deleteMessage(messageId) {
12374
12609
  const db = this.getDb();
12375
12610
  const result = db.prepare("DELETE FROM ai_message WHERE id = ?").run(messageId);
12376
12611
  return result.changes > 0;
12377
12612
  }
12378
- deleteMessagesFrom(conversationId, messageId) {
12613
+ deleteMessagesFrom(aiChatId, messageId) {
12379
12614
  const db = this.getDb();
12380
12615
  const target = this.getMessageRow(messageId);
12381
- if (!target || target.conversationId !== conversationId) {
12382
- throw new Error("Message not found in conversation");
12616
+ if (!target || target.aiChatId !== aiChatId) {
12617
+ throw new Error("Message not found in AI chat");
12383
12618
  }
12384
- const activePath = this.getActivePathRows(conversationId);
12619
+ const activePath = this.getActivePathRows(aiChatId);
12385
12620
  const targetIndex = activePath.findIndex((row) => row.id === messageId);
12386
12621
  if (targetIndex < 0) {
12387
12622
  throw new Error("Message not on active path");
@@ -12391,19 +12626,19 @@ var AIConversationManager = class {
12391
12626
  db.prepare(`DELETE FROM ai_message WHERE id IN (${placeholders})`).run(...idsToDelete);
12392
12627
  const newLeafId = targetIndex > 0 ? activePath[targetIndex - 1].id : null;
12393
12628
  const now = Math.floor(Date.now() / 1e3);
12394
- db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12629
+ db.prepare("UPDATE ai_chat SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12395
12630
  newLeafId,
12396
12631
  now,
12397
- conversationId
12632
+ aiChatId
12398
12633
  );
12399
12634
  }
12400
- forkConversation(sourceConversationId, upToMessageId, title) {
12635
+ forkAIChat(sourceAIChatId, upToMessageId, title) {
12401
12636
  const db = this.getDb();
12402
- const source = this.getConversation(sourceConversationId);
12637
+ const source = this.getAIChat(sourceAIChatId);
12403
12638
  if (!source) {
12404
- throw new Error("Source conversation not found");
12639
+ throw new Error("Source AI chat not found");
12405
12640
  }
12406
- const activePath = this.getActivePathRows(sourceConversationId);
12641
+ const activePath = this.getActivePathRows(sourceAIChatId);
12407
12642
  const cutIndex = activePath.findIndex((row) => row.id === upToMessageId);
12408
12643
  if (cutIndex < 0) {
12409
12644
  throw new Error("Message not on active path");
@@ -12413,7 +12648,7 @@ var AIConversationManager = class {
12413
12648
  const newConvId = this.generateId("conv");
12414
12649
  const forkTitle = title || `${source.title || "Untitled"} (fork)`;
12415
12650
  db.prepare(
12416
- `INSERT INTO ai_conversation (id, session_id, title, assistant_id, active_message_id, created_at, updated_at)
12651
+ `INSERT INTO ai_chat (id, session_id, title, assistant_id, active_message_id, created_at, updated_at)
12417
12652
  VALUES (?, ?, ?, ?, NULL, ?, ?)`
12418
12653
  ).run(newConvId, source.sessionId, forkTitle, source.assistantId, now, now);
12419
12654
  const idMap = /* @__PURE__ */ new Map();
@@ -12424,7 +12659,7 @@ var AIConversationManager = class {
12424
12659
  const newParentId = row.parentId ? idMap.get(row.parentId) ?? null : null;
12425
12660
  db.prepare(
12426
12661
  `INSERT INTO ai_message (
12427
- id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12662
+ id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count,
12428
12663
  content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12429
12664
  )
12430
12665
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, 0)`
@@ -12444,7 +12679,7 @@ var AIConversationManager = class {
12444
12679
  lastNewId = newMsgId;
12445
12680
  }
12446
12681
  if (lastNewId) {
12447
- db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?").run(lastNewId, newConvId);
12682
+ db.prepare("UPDATE ai_chat SET active_message_id = ? WHERE id = ?").run(lastNewId, newConvId);
12448
12683
  }
12449
12684
  return {
12450
12685
  id: newConvId,
@@ -12461,46 +12696,46 @@ var AIConversationManager = class {
12461
12696
  const result = db.prepare("UPDATE ai_message SET content = ? WHERE id = ?").run(newContent, messageId);
12462
12697
  if (result.changes === 0) throw new Error("Message not found");
12463
12698
  }
12464
- deleteAndRelinkMessage(conversationId, messageId) {
12699
+ deleteAndRelinkMessage(aiChatId, messageId) {
12465
12700
  const db = this.getDb();
12466
12701
  const target = this.getMessageRow(messageId);
12467
- if (!target || target.conversationId !== conversationId) {
12468
- throw new Error("Message not found in conversation");
12702
+ if (!target || target.aiChatId !== aiChatId) {
12703
+ throw new Error("Message not found in AI chat");
12469
12704
  }
12470
- db.prepare("UPDATE ai_message SET parent_id = ? WHERE parent_id = ? AND conversation_id = ?").run(
12705
+ db.prepare("UPDATE ai_message SET parent_id = ? WHERE parent_id = ? AND ai_chat_id = ?").run(
12471
12706
  target.parentId,
12472
12707
  messageId,
12473
- conversationId
12708
+ aiChatId
12474
12709
  );
12475
- const conv = this.getConversation(conversationId);
12710
+ const conv = this.getAIChat(aiChatId);
12476
12711
  if (conv?.activeMessageId === messageId) {
12477
12712
  const now = Math.floor(Date.now() / 1e3);
12478
- db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12713
+ db.prepare("UPDATE ai_chat SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12479
12714
  target.parentId,
12480
12715
  now,
12481
- conversationId
12716
+ aiChatId
12482
12717
  );
12483
12718
  }
12484
12719
  db.prepare("DELETE FROM ai_message WHERE id = ?").run(messageId);
12485
12720
  }
12486
- insertMessageAfter(conversationId, afterMessageId, role, content, contentBlocks, tokenUsage) {
12721
+ insertMessageAfter(aiChatId, afterMessageId, role, content, contentBlocks, tokenUsage) {
12487
12722
  const db = this.getDb();
12488
12723
  const now = Math.floor(Date.now() / 1e3);
12489
12724
  const id = this.generateId("msg");
12490
- const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(conversationId) : void 0;
12725
+ const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(aiChatId) : void 0;
12491
12726
  if (pendingDebug) {
12492
- this.pendingDebugContextMap.delete(conversationId);
12727
+ this.pendingDebugContextMap.delete(aiChatId);
12493
12728
  }
12494
- const childRow = db.prepare("SELECT id FROM ai_message WHERE parent_id = ? AND conversation_id = ? LIMIT 1").get(afterMessageId, conversationId);
12729
+ const childRow = db.prepare("SELECT id FROM ai_message WHERE parent_id = ? AND ai_chat_id = ? LIMIT 1").get(afterMessageId, aiChatId);
12495
12730
  db.prepare(
12496
12731
  `INSERT INTO ai_message (
12497
- id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12732
+ id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count,
12498
12733
  content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12499
12734
  )
12500
12735
  VALUES (?, ?, ?, ?, ?, NULL, NULL, ?, ?, ?, ?, ?, 0)`
12501
12736
  ).run(
12502
12737
  id,
12503
- conversationId,
12738
+ aiChatId,
12504
12739
  role,
12505
12740
  content,
12506
12741
  now,
@@ -12512,17 +12747,17 @@ var AIConversationManager = class {
12512
12747
  );
12513
12748
  if (childRow) {
12514
12749
  db.prepare("UPDATE ai_message SET parent_id = ? WHERE id = ?").run(id, childRow.id);
12515
- db.prepare("UPDATE ai_conversation SET updated_at = ? WHERE id = ?").run(now, conversationId);
12750
+ db.prepare("UPDATE ai_chat SET updated_at = ? WHERE id = ?").run(now, aiChatId);
12516
12751
  } else {
12517
- db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12752
+ db.prepare("UPDATE ai_chat SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12518
12753
  id,
12519
12754
  now,
12520
- conversationId
12755
+ aiChatId
12521
12756
  );
12522
12757
  }
12523
12758
  return {
12524
12759
  id,
12525
- conversationId,
12760
+ aiChatId,
12526
12761
  role,
12527
12762
  content,
12528
12763
  timestamp: now,
@@ -12531,8 +12766,8 @@ var AIConversationManager = class {
12531
12766
  tokenUsage
12532
12767
  };
12533
12768
  }
12534
- getConversationTokenUsage(conversationId) {
12535
- const rows = this.getActivePathRows(conversationId);
12769
+ getAIChatTokenUsage(aiChatId) {
12770
+ const rows = this.getActivePathRows(aiChatId);
12536
12771
  const result = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
12537
12772
  for (const row of rows) {
12538
12773
  if (row.tokenUsage) {
@@ -12545,8 +12780,8 @@ var AIConversationManager = class {
12545
12780
  return result;
12546
12781
  }
12547
12782
  // ==================== Debug context ====================
12548
- setPendingDebugContext(conversationId, debugContext) {
12549
- this.pendingDebugContextMap.set(conversationId, debugContext);
12783
+ setPendingDebugContext(aiChatId, debugContext) {
12784
+ this.pendingDebugContextMap.set(aiChatId, debugContext);
12550
12785
  }
12551
12786
  setDebugContext(messageId, debugContext) {
12552
12787
  const db = this.getDb();
@@ -12558,8 +12793,8 @@ var AIConversationManager = class {
12558
12793
  return result.changes;
12559
12794
  }
12560
12795
  // ==================== Agent 专用 ====================
12561
- getHistoryForAgent(conversationId, maxMessages, leafMessageId) {
12562
- const messages = this.getActivePathRows(conversationId, leafMessageId).map((row) => this.parseMessageRow(row));
12796
+ getHistoryForAgent(aiChatId, maxMessages, leafMessageId) {
12797
+ const messages = this.getActivePathRows(aiChatId, leafMessageId).map((row) => this.parseMessageRow(row));
12563
12798
  const validMessages = messages.filter(
12564
12799
  (m) => (m.role === "user" || m.role === "assistant" || m.role === "summary") && m.content?.trim()
12565
12800
  );
@@ -12577,8 +12812,8 @@ var AIConversationManager = class {
12577
12812
  );
12578
12813
  const bufferBoundary = metaBlock?.bufferBoundaryTimestamp;
12579
12814
  if (!metaBlock) {
12580
- this.logger.warn("Conversations", "summary message missing summary_meta; agent context will be summary-only", {
12581
- conversationId,
12815
+ this.logger.warn("AIChats", "summary message missing summary_meta; agent context will be summary-only", {
12816
+ aiChatId,
12582
12817
  messageId: summaryMsg.id
12583
12818
  });
12584
12819
  }
@@ -12601,7 +12836,7 @@ var AIConversationManager = class {
12601
12836
  return result;
12602
12837
  }
12603
12838
  // ==================== Summary / 压缩专用 ====================
12604
- addSummaryMessage(conversationId, content, meta) {
12839
+ addSummaryMessage(aiChatId, content, meta) {
12605
12840
  const contentBlocks = [
12606
12841
  {
12607
12842
  type: "summary_meta",
@@ -12609,28 +12844,28 @@ var AIConversationManager = class {
12609
12844
  compressedMessageCount: meta.compressedMessageCount
12610
12845
  }
12611
12846
  ];
12612
- return this.addMessage(conversationId, "summary", content, void 0, void 0, contentBlocks);
12847
+ return this.addMessage(aiChatId, "summary", content, void 0, void 0, contentBlocks);
12613
12848
  }
12614
- getLatestSummary(conversationId) {
12615
- const row = [...this.getActivePathRows(conversationId)].reverse().find((message) => message.role === "summary");
12849
+ getLatestSummary(aiChatId) {
12850
+ const row = [...this.getActivePathRows(aiChatId)].reverse().find((message) => message.role === "summary");
12616
12851
  return row ? this.parseMessageRow(row) : null;
12617
12852
  }
12618
- getMessagesAfterSummary(conversationId, summaryTimestamp) {
12619
- return this.getActivePathRows(conversationId).filter((row) => row.timestamp > summaryTimestamp && (row.role === "user" || row.role === "assistant")).map((row) => ({ role: row.role, content: row.content, timestamp: row.timestamp }));
12853
+ getMessagesAfterSummary(aiChatId, summaryTimestamp) {
12854
+ return this.getActivePathRows(aiChatId).filter((row) => row.timestamp > summaryTimestamp && (row.role === "user" || row.role === "assistant")).map((row) => ({ role: row.role, content: row.content, timestamp: row.timestamp }));
12620
12855
  }
12621
- getAllUserAssistantMessages(conversationId) {
12622
- return this.getActivePathRows(conversationId).filter((row) => row.role === "user" || row.role === "assistant").map((row) => ({ role: row.role, content: row.content, timestamp: row.timestamp }));
12856
+ getAllUserAssistantMessages(aiChatId) {
12857
+ return this.getActivePathRows(aiChatId).filter((row) => row.role === "user" || row.role === "assistant").map((row) => ({ role: row.role, content: row.content, timestamp: row.timestamp }));
12623
12858
  }
12624
- getMessageCountAfterSummary(conversationId) {
12625
- const summary = this.getLatestSummary(conversationId);
12859
+ getMessageCountAfterSummary(aiChatId) {
12860
+ const summary = this.getLatestSummary(aiChatId);
12626
12861
  if (!summary) {
12627
- return this.getActivePathRows(conversationId).filter((row) => row.role === "user" || row.role === "assistant").length;
12862
+ return this.getActivePathRows(aiChatId).filter((row) => row.role === "user" || row.role === "assistant").length;
12628
12863
  }
12629
12864
  const metaBlock = summary.contentBlocks?.find(
12630
12865
  (b) => b.type === "summary_meta"
12631
12866
  );
12632
12867
  const boundary = metaBlock?.bufferBoundaryTimestamp ?? summary.timestamp;
12633
- return this.getActivePathRows(conversationId).filter(
12868
+ return this.getActivePathRows(aiChatId).filter(
12634
12869
  (row) => row.timestamp >= boundary && (row.role === "user" || row.role === "assistant")
12635
12870
  ).length;
12636
12871
  }
@@ -13098,7 +13333,7 @@ function parseAssistantFile(content, filePath) {
13098
13333
  name,
13099
13334
  systemPrompt: body.trim(),
13100
13335
  presetQuestions: parseStringArray(fm.presetQuestions),
13101
- allowedBuiltinTools: parseStringArray(fm.allowedBuiltinTools),
13336
+ allowedBuiltinTools: normalizeBuiltinToolNames(parseStringArray(fm.allowedBuiltinTools)),
13102
13337
  builtinId: typeof fm.builtinId === "string" ? fm.builtinId : void 0,
13103
13338
  applicableChatTypes: parseChatTypes(fm.applicableChatTypes),
13104
13339
  supportedLocales: parseStringArray(fm.supportedLocales)
@@ -13115,7 +13350,7 @@ function serializeAssistant(config) {
13115
13350
  if (config.builtinId) fm.builtinId = config.builtinId;
13116
13351
  if (config.applicableChatTypes?.length) fm.applicableChatTypes = config.applicableChatTypes;
13117
13352
  if (config.supportedLocales?.length) fm.supportedLocales = config.supportedLocales;
13118
- if (config.allowedBuiltinTools?.length) fm.allowedBuiltinTools = config.allowedBuiltinTools;
13353
+ if (config.allowedBuiltinTools?.length) fm.allowedBuiltinTools = normalizeBuiltinToolNames(config.allowedBuiltinTools);
13119
13354
  if (config.presetQuestions?.length) fm.presetQuestions = config.presetQuestions;
13120
13355
  return matter.stringify(`
13121
13356
  ${config.systemPrompt}
@@ -13169,8 +13404,8 @@ var AssistantManager = class {
13169
13404
  }
13170
13405
  // ==================== Init ====================
13171
13406
  init() {
13172
- const { fs: fs39, assistantsDir } = this.deps;
13173
- fs39.ensureDir(assistantsDir);
13407
+ const { fs: fs40, assistantsDir } = this.deps;
13408
+ fs40.ensureDir(assistantsDir);
13174
13409
  const generalCreated = this.ensureGeneralAssistants();
13175
13410
  this.loadAll();
13176
13411
  this.initialized = true;
@@ -13181,26 +13416,26 @@ var AssistantManager = class {
13181
13416
  return { total: this.cache.size, generalCreated };
13182
13417
  }
13183
13418
  ensureGeneralAssistants() {
13184
- const { fs: fs39, assistantsDir } = this.deps;
13419
+ const { fs: fs40, assistantsDir } = this.deps;
13185
13420
  let anyCreated = false;
13186
13421
  for (const id of this.generalIds) {
13187
13422
  const config = this.getBuiltinConfig(id);
13188
13423
  if (!config) continue;
13189
- const filePath = fs39.joinPath(assistantsDir, `${id}.md`);
13190
- if (fs39.fileExists(filePath)) continue;
13191
- fs39.writeFile(filePath, serializeAssistant({ ...config, builtinId: config.id }));
13424
+ const filePath = fs40.joinPath(assistantsDir, `${id}.md`);
13425
+ if (fs40.fileExists(filePath)) continue;
13426
+ fs40.writeFile(filePath, serializeAssistant({ ...config, builtinId: config.id }));
13192
13427
  anyCreated = true;
13193
13428
  }
13194
13429
  return anyCreated;
13195
13430
  }
13196
13431
  loadAll() {
13197
- const { fs: fs39, assistantsDir } = this.deps;
13432
+ const { fs: fs40, assistantsDir } = this.deps;
13198
13433
  this.cache.clear();
13199
- const files = fs39.listFiles(assistantsDir, ".md");
13434
+ const files = fs40.listFiles(assistantsDir, ".md");
13200
13435
  for (const file of files) {
13201
13436
  try {
13202
- const filePath = fs39.joinPath(assistantsDir, file);
13203
- const content = fs39.readFile(filePath);
13437
+ const filePath = fs40.joinPath(assistantsDir, file);
13438
+ const content = fs40.readFile(filePath);
13204
13439
  const config = parseAssistantFile(content, filePath);
13205
13440
  if (config) {
13206
13441
  this.cache.set(config.id, config);
@@ -13394,93 +13629,43 @@ function countMessagesTokens(messages, systemPrompt) {
13394
13629
  // ../../packages/node-runtime/src/ai/skill-manager.ts
13395
13630
  import * as fs30 from "fs";
13396
13631
  import * as path27 from "path";
13397
- var SKILLS_DIR_NAME = "skills";
13632
+
13633
+ // ../../packages/node-runtime/src/ai/skill-menu.ts
13398
13634
  var MAX_SKILL_MENU_ITEMS = 15;
13399
- var defaultLogger2 = {
13400
- info: () => {
13401
- },
13402
- warn: () => {
13403
- }
13404
- };
13405
- var SkillManager = class {
13406
- skills = /* @__PURE__ */ new Map();
13407
- initialized = false;
13408
- skillsDir;
13409
- logger;
13410
- constructor(aiDataDir, logger) {
13411
- this.skillsDir = path27.join(aiDataDir, SKILLS_DIR_NAME);
13412
- this.logger = logger ?? defaultLogger2;
13413
- }
13414
- init() {
13415
- this.loadAll();
13416
- this.initialized = true;
13417
- this.logger.info(`SkillManager initialized: ${this.skills.size} skills`);
13418
- return { total: this.skills.size };
13419
- }
13420
- getSkillConfig(id) {
13421
- this.ensureInitialized();
13422
- return this.skills.get(id) ?? null;
13423
- }
13424
- getAllSkills() {
13425
- this.ensureInitialized();
13426
- return Array.from(this.skills.values()).map((def) => ({
13427
- id: def.id,
13428
- name: def.name,
13429
- description: def.description,
13430
- tags: def.tags,
13431
- chatScope: def.chatScope,
13432
- tools: def.tools,
13433
- builtinId: def.builtinId
13434
- }));
13435
- }
13436
- /**
13437
- * 构建 AI 自选技能菜单文本
13438
- * 只包含与当前 chatType + 助手工具权限兼容的技能
13439
- */
13440
- getSkillMenu(chatType, allowedTools) {
13441
- this.ensureInitialized();
13442
- const compatible = Array.from(this.skills.values()).filter((skill) => {
13443
- if (skill.chatScope !== "all" && skill.chatScope !== chatType) return false;
13444
- if (skill.tools.length > 0 && allowedTools && allowedTools.length > 0) {
13445
- const allCovered = skill.tools.every((t2) => allowedTools.includes(t2));
13446
- if (!allCovered) return false;
13447
- }
13448
- return true;
13449
- });
13450
- if (compatible.length === 0) return null;
13451
- const items = compatible.slice(0, MAX_SKILL_MENU_ITEMS);
13452
- const lines = items.map((s) => `- ${s.id}: ${s.name} \u2014 ${s.description}`);
13453
- return `## \u53EF\u7528\u6280\u80FD
13635
+ var SKILL_MENU_HEADER = `## \u53EF\u7528\u6280\u80FD
13454
13636
  \u4EE5\u4E0B\u662F\u4F60\u53EF\u4EE5\u4F7F\u7528\u7684\u5206\u6790\u6280\u80FD\u3002\u5F53\u4F60\u5224\u65AD\u7528\u6237\u7684\u95EE\u9898\u9002\u5408\u4F7F\u7528\u67D0\u4E2A\u6280\u80FD\u65F6\uFF0C
13455
- \u8BF7\u8C03\u7528 activate_skill \u5DE5\u5177\u6FC0\u6D3B\u5B83\uFF0C\u7136\u540E\u6309\u7167\u8FD4\u56DE\u7684\u6307\u5BFC\u5B8C\u6210\u4EFB\u52A1\u3002
13637
+ \u8BF7\u8C03\u7528 activate_skill \u5DE5\u5177\u6FC0\u6D3B\u5B83\uFF0C\u7136\u540E\u6309\u7167\u8FD4\u56DE\u7684\u6307\u5BFC\u5B8C\u6210\u4EFB\u52A1\u3002`;
13638
+ var SKILL_MENU_CLOSING = "\u5982\u679C\u7528\u6237\u7684\u95EE\u9898\u4E0D\u9700\u8981\u4F7F\u7528\u6280\u80FD\uFF0C\u76F4\u63A5\u56DE\u7B54\u5373\u53EF\u3002";
13639
+ var SKILL_MENU_CLOSING_BLOCK = `
13456
13640
 
13457
- ${lines.join("\n")}
13641
+ ${SKILL_MENU_CLOSING}`;
13642
+ function formatSkillMenuLine(item) {
13643
+ const guidanceSuffix = item.guidance ? `. ${item.guidance}` : "";
13644
+ return `- ${item.id}: ${item.name} \u2014 ${item.description}${guidanceSuffix}`;
13645
+ }
13646
+ function buildSkillMenuText(lines) {
13647
+ const visibleLines = normalizeSkillMenuLines(lines);
13648
+ if (visibleLines.length === 0) return null;
13649
+ return `${SKILL_MENU_HEADER}
13458
13650
 
13459
- \u5982\u679C\u7528\u6237\u7684\u95EE\u9898\u4E0D\u9700\u8981\u4F7F\u7528\u6280\u80FD\uFF0C\u76F4\u63A5\u56DE\u7B54\u5373\u53EF\u3002`;
13460
- }
13461
- ensureInitialized() {
13462
- if (!this.initialized) {
13463
- this.init();
13464
- }
13465
- }
13466
- loadAll() {
13467
- this.skills.clear();
13468
- if (!fs30.existsSync(this.skillsDir)) return;
13469
- const files = fs30.readdirSync(this.skillsDir).filter((f) => f.endsWith(".md"));
13470
- for (const file of files) {
13471
- try {
13472
- const filePath = path27.join(this.skillsDir, file);
13473
- const content = fs30.readFileSync(filePath, "utf-8");
13474
- const def = parseSkillFile(content, filePath);
13475
- if (def) {
13476
- this.skills.set(def.id, def);
13477
- }
13478
- } catch (error) {
13479
- this.logger.warn(`Failed to load skill: ${file}`, { error: String(error) });
13480
- }
13481
- }
13651
+ ${visibleLines.join("\n")}
13652
+
13653
+ ${SKILL_MENU_CLOSING}`;
13654
+ }
13655
+ function appendSkillMenuLines(baseMenu, lines) {
13656
+ const visibleLines = normalizeSkillMenuLines(lines);
13657
+ if (visibleLines.length === 0) return baseMenu ?? null;
13658
+ if (!baseMenu) return buildSkillMenuText(visibleLines);
13659
+ const insertion = `
13660
+ ${visibleLines.join("\n")}`;
13661
+ if (!baseMenu.includes(SKILL_MENU_CLOSING_BLOCK)) {
13662
+ return `${baseMenu}${insertion}`;
13482
13663
  }
13483
- };
13664
+ return baseMenu.replace(SKILL_MENU_CLOSING_BLOCK, `${insertion}${SKILL_MENU_CLOSING_BLOCK}`);
13665
+ }
13666
+ function normalizeSkillMenuLines(lines) {
13667
+ return lines.map((line) => line.trim()).filter(Boolean);
13668
+ }
13484
13669
 
13485
13670
  // ../../packages/node-runtime/src/ai/skill-manager-core.ts
13486
13671
  function toSummary2(def) {
@@ -13511,7 +13696,6 @@ function simpleHash(content) {
13511
13696
  }
13512
13697
  return hash.toString(36);
13513
13698
  }
13514
- var MAX_SKILL_MENU_ITEMS2 = 15;
13515
13699
  var SkillManagerCore = class {
13516
13700
  deps;
13517
13701
  builtinDefs = /* @__PURE__ */ new Map();
@@ -13537,21 +13721,21 @@ var SkillManagerCore = class {
13537
13721
  }
13538
13722
  // ==================== Init ====================
13539
13723
  init() {
13540
- const { fs: fs39, skillsDir } = this.deps;
13541
- fs39.ensureDir(skillsDir);
13724
+ const { fs: fs40, skillsDir } = this.deps;
13725
+ fs40.ensureDir(skillsDir);
13542
13726
  this.loadAll();
13543
13727
  this.initialized = true;
13544
13728
  this.deps.logger?.info("SkillManager", "Initialized", { total: this.cache.size });
13545
13729
  return { total: this.cache.size };
13546
13730
  }
13547
13731
  loadAll() {
13548
- const { fs: fs39, skillsDir } = this.deps;
13732
+ const { fs: fs40, skillsDir } = this.deps;
13549
13733
  this.cache.clear();
13550
- const files = fs39.listFiles(skillsDir, ".md");
13734
+ const files = fs40.listFiles(skillsDir, ".md");
13551
13735
  for (const file of files) {
13552
13736
  try {
13553
- const filePath = fs39.joinPath(skillsDir, file);
13554
- const content = fs39.readFile(filePath);
13737
+ const filePath = fs40.joinPath(skillsDir, file);
13738
+ const content = fs40.readFile(filePath);
13555
13739
  const def = parseSkillFile(content, filePath);
13556
13740
  if (def) {
13557
13741
  this.cache.set(def.id, def);
@@ -13665,21 +13849,14 @@ var SkillManagerCore = class {
13665
13849
  this.ensureInitialized();
13666
13850
  const compatible = Array.from(this.cache.values()).filter((skill) => {
13667
13851
  if (skill.chatScope !== "all" && skill.chatScope !== chatType) return false;
13668
- if (skill.tools.length > 0 && allowedTools && allowedTools.length > 0) {
13852
+ if (skill.tools.length > 0 && allowedTools !== void 0) {
13669
13853
  if (!skill.tools.every((t2) => allowedTools.includes(t2))) return false;
13670
13854
  }
13671
13855
  return true;
13672
13856
  });
13673
13857
  if (compatible.length === 0) return null;
13674
- const items = compatible.slice(0, MAX_SKILL_MENU_ITEMS2);
13675
- const lines = items.map((s) => `- ${s.id}: ${s.name} \u2014 ${s.description}`);
13676
- return `## \u53EF\u7528\u6280\u80FD
13677
- \u4EE5\u4E0B\u662F\u4F60\u53EF\u4EE5\u4F7F\u7528\u7684\u5206\u6790\u6280\u80FD\u3002\u5F53\u4F60\u5224\u65AD\u7528\u6237\u7684\u95EE\u9898\u9002\u5408\u4F7F\u7528\u67D0\u4E2A\u6280\u80FD\u65F6\uFF0C
13678
- \u8BF7\u8C03\u7528 activate_skill \u5DE5\u5177\u6FC0\u6D3B\u5B83\uFF0C\u7136\u540E\u6309\u7167\u8FD4\u56DE\u7684\u6307\u5BFC\u5B8C\u6210\u4EFB\u52A1\u3002
13679
-
13680
- ${lines.join("\n")}
13681
-
13682
- \u5982\u679C\u7528\u6237\u7684\u95EE\u9898\u4E0D\u9700\u8981\u4F7F\u7528\u6280\u80FD\uFF0C\u76F4\u63A5\u56DE\u7B54\u5373\u53EF\u3002`;
13858
+ const items = compatible.slice(0, MAX_SKILL_MENU_ITEMS);
13859
+ return buildSkillMenuText(items.map(formatSkillMenuLine));
13683
13860
  }
13684
13861
  // ==================== Internal ====================
13685
13862
  findByBuiltinId(builtinId) {
@@ -13709,6 +13886,80 @@ ${lines.join("\n")}
13709
13886
  }
13710
13887
  };
13711
13888
 
13889
+ // ../../packages/node-runtime/src/ai/skill-manager.ts
13890
+ var SKILLS_DIR_NAME = "skills";
13891
+ var defaultLogger2 = {
13892
+ info: () => {
13893
+ },
13894
+ warn: () => {
13895
+ }
13896
+ };
13897
+ var nodeFs = {
13898
+ ensureDir(dir) {
13899
+ fs30.mkdirSync(dir, { recursive: true });
13900
+ },
13901
+ listFiles(dir, ext) {
13902
+ if (!fs30.existsSync(dir)) return [];
13903
+ return fs30.readdirSync(dir).filter((file) => file.endsWith(ext));
13904
+ },
13905
+ readFile(filePath) {
13906
+ return fs30.readFileSync(filePath, "utf-8");
13907
+ },
13908
+ writeFile(filePath, content) {
13909
+ fs30.writeFileSync(filePath, content, "utf-8");
13910
+ },
13911
+ deleteFile(filePath) {
13912
+ fs30.unlinkSync(filePath);
13913
+ },
13914
+ fileExists(filePath) {
13915
+ return fs30.existsSync(filePath);
13916
+ },
13917
+ joinPath(...parts) {
13918
+ return path27.join(...parts);
13919
+ }
13920
+ };
13921
+ var SkillManager = class {
13922
+ core;
13923
+ logger;
13924
+ constructor(aiDataDir, logger) {
13925
+ this.logger = logger ?? defaultLogger2;
13926
+ this.core = new SkillManagerCore({
13927
+ fs: nodeFs,
13928
+ skillsDir: path27.join(aiDataDir, SKILLS_DIR_NAME),
13929
+ builtinRawSkills: [],
13930
+ logger: {
13931
+ info: () => {
13932
+ },
13933
+ warn: (_category, message, data) => this.logger.warn(message, toLoggerExtra(data)),
13934
+ error: (_category, message, data) => this.logger.warn(message, toLoggerExtra(data))
13935
+ }
13936
+ });
13937
+ }
13938
+ init() {
13939
+ const result = this.core.init();
13940
+ this.logger.info(`SkillManager initialized: ${result.total} skills`);
13941
+ return result;
13942
+ }
13943
+ getSkillConfig(id) {
13944
+ return this.core.getSkillConfig(id);
13945
+ }
13946
+ getAllSkills() {
13947
+ return this.core.getAllSkills();
13948
+ }
13949
+ /**
13950
+ * 构建 AI 自选技能菜单文本
13951
+ * 只包含与当前 chatType + 助手工具权限兼容的技能
13952
+ */
13953
+ getSkillMenu(chatType, allowedTools) {
13954
+ return this.core.getSkillMenu(chatType, allowedTools);
13955
+ }
13956
+ };
13957
+ function toLoggerExtra(data) {
13958
+ if (data === void 0) return void 0;
13959
+ if (data && typeof data === "object" && !Array.isArray(data)) return data;
13960
+ return { data };
13961
+ }
13962
+
13712
13963
  // ../../packages/node-runtime/src/ai/activate-skill-tool.ts
13713
13964
  function createActivateSkillTool(options) {
13714
13965
  const { chatType, allowedTools, coreToolNames, getSkillConfig, locale = "zh-CN" } = options;
@@ -13744,7 +13995,7 @@ function createActivateSkillTool(options) {
13744
13995
  details: { skillId, found: true, applicable: false }
13745
13996
  };
13746
13997
  }
13747
- if (skill.tools.length > 0 && allowedTools && allowedTools.length > 0) {
13998
+ if (skill.tools.length > 0 && allowedTools !== void 0) {
13748
13999
  const missing = skill.tools.filter((t2) => !(coreToolNames?.has(t2) ?? false) && !allowedTools.includes(t2));
13749
14000
  if (missing.length > 0) {
13750
14001
  const msg = isZh ? `\u5F53\u524D\u52A9\u624B\u7F3A\u5C11\u8BE5\u6280\u80FD\u6240\u9700\u7684\u5DE5\u5177\uFF1A${missing.join(", ")}` : `Current assistant lacks tools required by this skill: ${missing.join(", ")}`;
@@ -13763,6 +14014,175 @@ function createActivateSkillTool(options) {
13763
14014
  };
13764
14015
  }
13765
14016
 
14017
+ // ../../packages/node-runtime/src/ai/chart-runtime.ts
14018
+ var CHART_CAPABILITY_CORE_TOOLS = ["get_schema"];
14019
+ var CHART_CAPABILITY_ANALYSIS_TOOLS = ["render_chart"];
14020
+ var RAW_SQL_TOOL_NAMES = /* @__PURE__ */ new Set(["execute_sql"]);
14021
+ function getChartCapabilityAllowedBuiltinTools(allowedTools) {
14022
+ const baseTools = (allowedTools ?? []).filter((toolName) => !RAW_SQL_TOOL_NAMES.has(toolName));
14023
+ return Array.from(/* @__PURE__ */ new Set([...baseTools, ...CHART_CAPABILITY_ANALYSIS_TOOLS]));
14024
+ }
14025
+ function getAllowedBuiltinToolsForChartAutoSkill(allowedTools) {
14026
+ if (!allowedTools) return void 0;
14027
+ if (allowedTools.length === 0) return [];
14028
+ return Array.from(/* @__PURE__ */ new Set([...allowedTools, ...CHART_CAPABILITY_ANALYSIS_TOOLS]));
14029
+ }
14030
+ function shouldUseChartCapabilityForMessage(message) {
14031
+ const normalized = message.trim().toLowerCase();
14032
+ if (!normalized) return false;
14033
+ return /(?:画|绘制|生成|做|出|展示).{0,8}(?:图|图表)|(?:图表|可视化|饼图|柱状图|条形图|折线图|热力图|趋势图|占比图|分布图|chart|charts|plot|visuali[sz]e|visuali[sz]ation|pie chart|bar chart|line chart|heatmap)/i.test(
14034
+ normalized
14035
+ );
14036
+ }
14037
+ function resolveChartRuntimeForRequest(options) {
14038
+ const locale = options.locale ?? "zh-CN";
14039
+ const isExplicitChartSkill = options.skillId === CHART_CAPABILITY_SKILL_ID;
14040
+ const isAutoChartSkill = options.enableAutoDetection === true && !options.skillId && shouldUseChartCapabilityForMessage(options.userMessage ?? "");
14041
+ if (!isExplicitChartSkill && !isAutoChartSkill) {
14042
+ return { isChartCapability: false };
14043
+ }
14044
+ return {
14045
+ isChartCapability: true,
14046
+ skillDef: { ...getChartCapabilitySkill(locale), chatScope: "all" },
14047
+ allowedBuiltinTools: getChartCapabilityAllowedBuiltinTools(options.assistantAllowedTools)
14048
+ };
14049
+ }
14050
+ function getChartCapabilitySkill(locale = "zh-CN") {
14051
+ const isZh = locale.startsWith("zh");
14052
+ return {
14053
+ id: CHART_CAPABILITY_SKILL_ID,
14054
+ name: isZh ? "\u7ED8\u56FE\u52A9\u624B" : "Chart Assistant",
14055
+ description: isZh ? "\u6309\u672C\u8F6E\u95EE\u9898\u751F\u6210\u7075\u6D3B\u7684\u804A\u5929\u6570\u636E\u56FE\u8868" : "Generate flexible charts for this chat question",
14056
+ tags: [isZh ? "\u56FE\u8868" : "chart"],
14057
+ chatScope: "all",
14058
+ tools: ["render_chart", "get_schema"],
14059
+ prompt: isZh ? ZH_PROMPT : EN_PROMPT,
14060
+ builtinId: CHART_CAPABILITY_SKILL_ID
14061
+ };
14062
+ }
14063
+ function getChartMenuLine(locale) {
14064
+ const skill = getChartCapabilitySkill(locale);
14065
+ const isZh = locale.startsWith("zh");
14066
+ const guidance = isZh ? "\u7528\u6237\u660E\u786E\u8981\u6C42\u56FE\u8868\u3001\u753B\u56FE\u3001\u5360\u6BD4\u3001\u8D8B\u52BF\u3001\u5206\u5E03\u3001\u997C\u56FE\u3001\u67F1\u72B6\u56FE\u3001\u6298\u7EBF\u56FE\u6216\u70ED\u529B\u56FE\u65F6\u4F18\u5148\u6FC0\u6D3B\uFF1B\u4E0D\u8981\u8F93\u51FA Python/JS \u7ED8\u56FE\u4EE3\u7801" : "Activate first when the user explicitly asks for charts, visualization, ratios, trends, distributions, pie, bar, line, or heatmap charts; do not output Python/JS chart code";
14067
+ return formatSkillMenuLine({
14068
+ id: skill.id,
14069
+ name: skill.name,
14070
+ description: skill.description,
14071
+ guidance
14072
+ });
14073
+ }
14074
+ function buildSkillMenuWithBuiltinChart(baseMenu, locale = "zh-CN", allowedTools) {
14075
+ if (allowedTools && !allowedTools.includes(CHART_CAPABILITY_ANALYSIS_TOOLS[0])) return baseMenu ?? null;
14076
+ if (baseMenu?.includes(CHART_CAPABILITY_SKILL_ID)) return baseMenu;
14077
+ const chartLine = getChartMenuLine(locale);
14078
+ return appendSkillMenuLines(baseMenu, [chartLine]) ?? "";
14079
+ }
14080
+ function getSkillConfigWithBuiltinChart(id, locale = "zh-CN", getSkillConfig) {
14081
+ if (id === CHART_CAPABILITY_SKILL_ID) return getChartCapabilitySkill(locale);
14082
+ return getSkillConfig(id);
14083
+ }
14084
+ var ZH_PROMPT = `\u4F60\u662F ChatLab \u7ED8\u56FE\u52A9\u624B\u3002\u672C\u8F6E\u7528\u6237\u5E0C\u671B\u4F60\u5728\u56DE\u7B54\u91CC\u81EA\u7136\u5D4C\u5165\u4E00\u5F20\u6216\u591A\u5F20\u56FE\u8868\u3002
14085
+
14086
+ \u4F60\u5FC5\u987B\u901A\u8FC7 render_chart \u5DE5\u5177\u751F\u6210\u56FE\u8868\u3002\u4E0D\u8981\u8F93\u51FA HTML\u3001JavaScript\u3001SVG\u3001Canvas\u3001ECharts option \u6216\u4EFB\u4F55\u6E32\u67D3\u4EE3\u7801\u3002
14087
+
14088
+ \u786C\u6027\u89C4\u5219\uFF1A
14089
+ 1. \u7B2C\u4E00\u6B21\u5199 SQL \u524D\u5FC5\u987B\u5148\u8C03\u7528 get_schema\u3002\u7981\u6B62\u731C\u6D4B\u8868\u540D\u3001\u5B57\u6BB5\u540D\u6216\u65F6\u95F4\u5B57\u6BB5\u3002
14090
+ 2. \u201C\u6700\u8FD1\u201D\u201C\u5230\u73B0\u5728\u201D\u201C\u5230\u6700\u8FD1\u201D\u5FC5\u987B\u7528\u6570\u636E\u4E2D\u7684 MAX(ts) \u6216\u7B49\u4EF7\u5B57\u6BB5\u786E\u5B9A\u7ED3\u675F\u65E5\u671F\uFF1B\u4E0D\u8981\u624B\u7B97\u6216\u81C6\u6D4B\u65E5\u671F\u3002
14091
+ 3. ChatLab \u7684 message.ts \u662F\u79D2\u7EA7 Unix \u65F6\u95F4\u6233\uFF0C\u4F7F\u7528 date(ts, 'unixepoch', 'localtime')\uFF1B\u7981\u6B62\u5199 ts/1000\u3002
14092
+ 4. \u201C\u6700\u8FD1\u7684 N \u4EBA/\u6210\u5458\u201D\u5FC5\u987B\u5148\u7528\u6570\u636E\u5B9A\u4E49\u5E76\u7B5B\u9009\u51FA N \u4E2A\u6210\u5458\uFF0C\u518D\u53EA\u7EDF\u8BA1\u8FD9\u4E9B\u6210\u5458\u3002
14093
+ 5. \u6298\u7EBF\u56FE\u6709\u591A\u6761\u7EBF\u65F6\uFF0C\u6BCF\u6761\u7EBF\u5FC5\u987B\u6709\u660E\u786E series \u5B57\u6BB5\u542B\u4E49\uFF1B\u6309\u65E5\u671F\u7EDF\u8BA1\u65F6\u5E94\u8865\u9F50\u65E5\u671F \xD7 series \u7684 0 \u503C\uFF0C\u907F\u514D\u7F3A\u5931\u65E5\u671F\u5BFC\u81F4\u6298\u7EBF\u65AD\u88C2\u6216\u8BEF\u8FDE\u3002
14094
+ 6. \u5DE5\u5177\u5931\u8D25\u3001SQL \u4FEE\u6B63\u3001schema \u63A2\u7D22\u8FC7\u7A0B\u4E0D\u8981\u5199\u8FDB\u6700\u7EC8\u56DE\u7B54\uFF1B\u6700\u7EC8\u53EA\u4FDD\u7559\u56FE\u8868\u548C\u7B80\u77ED\u7ED3\u8BBA\u3002
14095
+ 7. \u56FE\u8868\u89E3\u91CA\u53EA\u80FD\u63CF\u8FF0\u6570\u636E\u76F4\u63A5\u652F\u6301\u7684\u8D8B\u52BF\u3001\u5CF0\u503C\u3001\u4F4E\u8C37\u3001\u5DEE\u5F02\u548C\u540C\u6B65\u6027\uFF0C\u7981\u6B62\u5938\u5F20\u62DF\u4EBA\u5316\u6216\u65E0\u6CD5\u4ECE\u56FE\u4E2D\u9A8C\u8BC1\u7684\u5173\u7CFB\u5224\u65AD\u3002
14096
+
14097
+ \u5DE5\u4F5C\u6D41\u7A0B\uFF1A
14098
+ 1. \u8C03\u7528 get_schema\u3002
14099
+ 2. \u6839\u636E schema \u548C\u7528\u6237\u8981\u6C42\u786E\u5B9A\u7EDF\u8BA1\u5BF9\u8C61\u3001\u65F6\u95F4\u8303\u56F4\u3001\u7EF4\u5EA6\u3001\u6307\u6807\u548C\u56FE\u8868\u7C7B\u578B\u3002
14100
+ 3. \u5FC5\u8981\u65F6\u7528\u53EA\u8BFB SQL \u5728 render_chart \u4E2D\u5B8C\u6210\u6210\u5458\u7B5B\u9009\u3001\u65F6\u95F4\u8303\u56F4\u786E\u8BA4\u3001\u805A\u5408\u548C\u8865\u96F6\u3002
14101
+ 4. \u8C03\u7528 render_chart\uFF0C\u63D0\u4EA4\u53EA\u8BFB SQL \u548C ChartSpec v1\u3002
14102
+ 5. \u56FE\u8868\u751F\u6210\u540E\uFF0C\u7528 1-3 \u53E5\u7B80\u6D01\u6587\u5B57\u89E3\u91CA\u7ED3\u8BBA\u3002\u7528\u6237\u8981\u6C42\u591A\u5F20\u56FE\u65F6\uFF0C\u53EF\u4EE5\u591A\u6B21\u8C03\u7528 render_chart\u3002
14103
+
14104
+ \u5E38\u7528\u8BED\u3001\u53E3\u5934\u7985\u3001\u9AD8\u9891\u77ED\u53E5\u7C7B\u56FE\u8868\u5FC5\u987B\u6392\u9664\u975E\u771F\u4EBA\u6587\u672C\uFF1A\u53D1\u9001\u8005\u4E3A\u7CFB\u7EDF\u6D88\u606F\u3001\u5A92\u4F53\u5360\u4F4D\uFF08\u5982 [\u8868\u60C5\u5305]\u3001[\u56FE\u7247]\u3001[\u8BED\u97F3]\u3001[\u89C6\u9891]\u3001[\u6587\u4EF6]\u3001[\u94FE\u63A5]\u3001[\u7EA2\u5305]\u3001[\u8F6C\u8D26]\uFF09\u4EE5\u53CA\u64A4\u56DE/\u5220\u9664\u63D0\u793A\u90FD\u4E0D\u80FD\u8FDB\u5165\u7EDF\u8BA1\u3002
14105
+
14106
+ ChartSpec v1 \u652F\u6301\uFF1A
14107
+ - bar: encoding.x + encoding.y
14108
+ - line: encoding.x + encoding.y\uFF0C\u53EF\u9009 encoding.series \u8868\u793A\u6BCF\u6761\u7EBF\u7684\u542B\u4E49
14109
+ - pie: encoding.label + encoding.value
14110
+ - heatmap: encoding.x + encoding.y + encoding.value
14111
+
14112
+ ChartSpec \u793A\u4F8B\uFF1A
14113
+ {
14114
+ "version": 1,
14115
+ "type": "line",
14116
+ "title": "\u6700\u8FD1 30 \u5929\u6210\u5458\u53D1\u8A00\u8D8B\u52BF",
14117
+ "encoding": { "x": "day", "y": "msg_count", "series": "member_name" },
14118
+ "unit": "\u6761"
14119
+ }
14120
+
14121
+ \u7528\u6237\u660E\u786E\u8981\u6C42\u56FE\u8868\u65F6\u5FC5\u987B\u5C1D\u8BD5\u751F\u6210\u3002\u7528\u6237\u6CA1\u6709\u660E\u786E\u8981\u6C42\u65F6\uFF0C\u53EA\u6709\u6392\u540D\u3001\u8D8B\u52BF\u3001\u5206\u5E03\u3001\u5360\u6BD4\u6216\u4E8C\u7EF4\u5BC6\u5EA6\u660E\u663E\u66F4\u6E05\u695A\u65F6\u624D\u4E3B\u52A8\u751F\u6210\uFF0C\u4E14\u9ED8\u8BA4\u6700\u591A\u4E00\u5F20\u3002`;
14122
+ var EN_PROMPT = `You are the ChatLab Chart Assistant. In this turn, the user wants one or more charts embedded naturally in the answer.
14123
+
14124
+ You must generate charts through the render_chart tool. Do not output HTML, JavaScript, SVG, Canvas, ECharts options, or rendering code.
14125
+
14126
+ Hard rules:
14127
+ 1. Always call get_schema before writing the first SQL query. Do not guess table names, field names, or timestamp fields.
14128
+ 2. For "latest", "until now", or "through the latest date", derive the end date from MAX(ts) or the equivalent timestamp field in the data. Do not hand-calculate or assume dates.
14129
+ 3. ChatLab message.ts is a Unix timestamp in seconds. Use date(ts, 'unixepoch', 'localtime'); never write ts/1000.
14130
+ 4. For "latest N members" or "recent N people", first define and select those N members from the data, then count only those members.
14131
+ 5. For multi-series line charts, every line must have an explicit series field. For daily counts, fill the date x series grid with zero values so missing days do not break or mislead the line.
14132
+ 6. Do not include failed SQL attempts, schema exploration, or retry reasoning in the final answer. Final answer should contain only the chart and a short conclusion.
14133
+ 7. Explain only trends, peaks, lows, differences, and synchrony directly supported by chart data. Do not make exaggerated personality or relationship claims.
14134
+
14135
+ Workflow:
14136
+ 1. Call get_schema.
14137
+ 2. Derive the target members, time range, dimensions, metrics, and chart type from schema and the user request.
14138
+ 3. When needed, use read-only SQL inside render_chart to select members, determine time range, aggregate data, and fill zero rows.
14139
+ 4. Call render_chart with read-only SQL and ChartSpec v1.
14140
+ 5. After the chart is generated, explain the key finding in 1-3 concise sentences. If the user asks for multiple charts, call render_chart multiple times.
14141
+
14142
+ For catchphrase, common phrase, or frequent short-text charts, exclude non-human text from the SQL: system-message senders, media placeholders such as [Sticker], [Image], [Photo], [Voice], [Video], [File], [Link], and recall/deletion notices must not be counted.
14143
+
14144
+ ChartSpec v1 supports:
14145
+ - bar: encoding.x + encoding.y
14146
+ - line: encoding.x + encoding.y, optional encoding.series for the meaning of each line
14147
+ - pie: encoding.label + encoding.value
14148
+ - heatmap: encoding.x + encoding.y + encoding.value
14149
+
14150
+ ChartSpec example:
14151
+ {
14152
+ "version": 1,
14153
+ "type": "line",
14154
+ "title": "Member message trend in the last 30 days",
14155
+ "encoding": { "x": "day", "y": "msg_count", "series": "member_name" },
14156
+ "unit": "messages"
14157
+ }
14158
+
14159
+ When the user explicitly asks for charts, you must try to generate them. If the user does not explicitly ask, add at most one chart only when ranking, trend, distribution, ratio, or two-dimensional density is clearly easier to understand visually.`;
14160
+
14161
+ // ../../packages/node-runtime/src/ai/chart-schema-gate.ts
14162
+ var CHART_SCHEMA_REQUIRED_MESSAGE = "Error: Call get_schema before render_chart. Do not guess table names, fields, or timestamp units.";
14163
+ function createChartSchemaGateState() {
14164
+ return { schemaSeen: false };
14165
+ }
14166
+ function wrapWithChartSchemaGate(tool, state) {
14167
+ const originalExecute = tool.execute;
14168
+ return {
14169
+ ...tool,
14170
+ execute: async (toolCallId, params, signal, onUpdate) => {
14171
+ if (tool.name === "render_chart" && !state.schemaSeen) {
14172
+ return {
14173
+ content: [{ type: "text", text: CHART_SCHEMA_REQUIRED_MESSAGE }],
14174
+ details: null
14175
+ };
14176
+ }
14177
+ const result = await originalExecute(toolCallId, params, signal, onUpdate);
14178
+ if (tool.name === "get_schema") {
14179
+ state.schemaSeen = true;
14180
+ }
14181
+ return result;
14182
+ }
14183
+ };
14184
+ }
14185
+
13766
14186
  // ../../packages/node-runtime/src/ai/compression/compressor.ts
13767
14187
  var DEFAULT_CONTEXT_WINDOW = 128e3;
13768
14188
  var INITIAL_COMPRESSION_PROMPT = `You are a context compression assistant. Compress the conversation below into a structured summary.
@@ -13806,23 +14226,23 @@ var defaultLogger3 = {
13806
14226
  error: () => {
13807
14227
  }
13808
14228
  };
13809
- async function checkAndCompress(conversationId, config, systemPrompt, llmAdapter, convManager, logger = defaultLogger3) {
14229
+ async function checkAndCompress(aiChatId, config, systemPrompt, llmAdapter, convManager, logger = defaultLogger3) {
13810
14230
  if (!config.enabled) {
13811
14231
  return { compressed: false, reason: "skipped_disabled" };
13812
14232
  }
13813
14233
  try {
13814
14234
  const contextWindow = llmAdapter.contextWindow || DEFAULT_CONTEXT_WINDOW;
13815
14235
  const thresholdTokens = Math.floor(contextWindow * (config.tokenThresholdPercent / 100) * 0.95);
13816
- const summary = convManager.getLatestSummary(conversationId);
14236
+ const summary = convManager.getLatestSummary(aiChatId);
13817
14237
  let messages;
13818
14238
  if (summary) {
13819
14239
  const metaBlock = summary.contentBlocks?.find(
13820
14240
  (b) => b.type === "summary_meta"
13821
14241
  );
13822
14242
  const boundary = metaBlock?.bufferBoundaryTimestamp ?? summary.timestamp;
13823
- messages = convManager.getMessagesAfterSummary(conversationId, boundary - 1);
14243
+ messages = convManager.getMessagesAfterSummary(aiChatId, boundary - 1);
13824
14244
  } else {
13825
- messages = convManager.getAllUserAssistantMessages(conversationId);
14245
+ messages = convManager.getAllUserAssistantMessages(aiChatId);
13826
14246
  }
13827
14247
  const historyForTokenCount = [];
13828
14248
  if (summary) {
@@ -13833,7 +14253,7 @@ async function checkAndCompress(conversationId, config, systemPrompt, llmAdapter
13833
14253
  }
13834
14254
  const currentTokens = countMessagesTokens(historyForTokenCount, systemPrompt);
13835
14255
  logger.info("Compression", `Token check: ${currentTokens} / ${thresholdTokens} (${contextWindow} window)`, {
13836
- conversationId,
14256
+ aiChatId,
13837
14257
  messageCount: messages.length,
13838
14258
  hasSummary: !!summary
13839
14259
  });
@@ -13857,7 +14277,7 @@ async function checkAndCompress(conversationId, config, systemPrompt, llmAdapter
13857
14277
  summaryText = forceTruncate(compressInput, targetTokens);
13858
14278
  }
13859
14279
  const bufferBoundary = bufferMessages.length > 0 ? bufferMessages[0].timestamp : messagesToCompress[messagesToCompress.length - 1].timestamp + 1;
13860
- convManager.addSummaryMessage(conversationId, summaryText, {
14280
+ convManager.addSummaryMessage(aiChatId, summaryText, {
13861
14281
  bufferBoundaryTimestamp: bufferBoundary,
13862
14282
  compressedMessageCount: messagesToCompress.length
13863
14283
  });
@@ -14542,6 +14962,11 @@ import {
14542
14962
  streamSimple as defaultStreamSimple,
14543
14963
  clampThinkingLevel
14544
14964
  } from "@earendil-works/pi-ai";
14965
+
14966
+ // ../../packages/node-runtime/src/ai/agent/constants.ts
14967
+ var DEFAULT_MAX_TOOL_ROUNDS = 10;
14968
+
14969
+ // ../../packages/node-runtime/src/ai/agent/core.ts
14545
14970
  function createEmptyPiUsage() {
14546
14971
  return {
14547
14972
  input: 0,
@@ -14584,7 +15009,7 @@ async function runAgentCore(options) {
14584
15009
  tools,
14585
15010
  history,
14586
15011
  userMessage,
14587
- maxToolRounds = 5,
15012
+ maxToolRounds = DEFAULT_MAX_TOOL_ROUNDS,
14588
15013
  abortSignal,
14589
15014
  steerMessage = "Please provide your final answer based on the information gathered.",
14590
15015
  onEvent,
@@ -15073,40 +15498,40 @@ var zh_CN_default = {
15073
15498
  context_size: "\u4E0A\u4E0B\u6587\u5927\u5C0F\uFF0C\u5373\u83B7\u53D6\u524D\u540E\u5404\u591A\u5C11\u6761\u6D88\u606F\uFF0C\u9ED8\u8BA4 20"
15074
15499
  }
15075
15500
  },
15076
- search_sessions: {
15077
- desc: "\u641C\u7D22\u804A\u5929\u4F1A\u8BDD\uFF08\u5BF9\u8BDD\u6BB5\u843D\uFF09\u3002\u4F1A\u8BDD\u662F\u6839\u636E\u6D88\u606F\u65F6\u95F4\u95F4\u9694\u81EA\u52A8\u5207\u5206\u7684\u5BF9\u8BDD\u5355\u5143\u3002\u9002\u7528\u4E8E\u67E5\u627E\u7279\u5B9A\u8BDD\u9898\u7684\u8BA8\u8BBA\u3001\u4E86\u89E3\u67D0\u4E2A\u65F6\u95F4\u6BB5\u5185\u53D1\u751F\u4E86\u51E0\u6B21\u5BF9\u8BDD\u7B49\u573A\u666F\u3002\u8FD4\u56DE\u5339\u914D\u7684\u4F1A\u8BDD\u5217\u8868\u53CA\u6BCF\u4E2A\u4F1A\u8BDD\u7684\u524D5\u6761\u6D88\u606F\u9884\u89C8\u3002",
15501
+ search_segments: {
15502
+ desc: "\u641C\u7D22\u804A\u5929\u6BB5\u843D\uFF08\u5BF9\u8BDD\u6BB5\u843D\uFF09\u3002\u6BB5\u843D\u662F\u6839\u636E\u6D88\u606F\u65F6\u95F4\u95F4\u9694\u81EA\u52A8\u5207\u5206\u7684\u5BF9\u8BDD\u5355\u5143\u3002\u9002\u7528\u4E8E\u67E5\u627E\u7279\u5B9A\u8BDD\u9898\u7684\u8BA8\u8BBA\u3001\u4E86\u89E3\u67D0\u4E2A\u65F6\u95F4\u6BB5\u5185\u53D1\u751F\u4E86\u51E0\u6B21\u5BF9\u8BDD\u7B49\u573A\u666F\u3002\u8FD4\u56DE\u5339\u914D\u7684\u6BB5\u843D\u5217\u8868\u53CA\u6BCF\u4E2A\u6BB5\u843D\u7684\u524D5\u6761\u6D88\u606F\u9884\u89C8\u3002",
15078
15503
  params: {
15079
- keywords: "\u53EF\u9009\u7684\u641C\u7D22\u5173\u952E\u8BCD\u5217\u8868\uFF0C\u53EA\u8FD4\u56DE\u5305\u542B\u8FD9\u4E9B\u5173\u952E\u8BCD\u7684\u4F1A\u8BDD\uFF08OR \u903B\u8F91\u5339\u914D\uFF09",
15080
- limit: "\u8FD4\u56DE\u4F1A\u8BDD\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 20",
15081
- year: "\u6307\u5B9A\u5E74\u4EFD\u7B5B\u9009\u4F1A\u8BDD\uFF0C\u5982 2024",
15082
- month: "\u6307\u5B9A\u6708\u4EFD\u7B5B\u9009\u4F1A\u8BDD\uFF081-12\uFF09\uFF0C\u9700\u914D\u5408 year \u4F7F\u7528",
15083
- day: "\u6307\u5B9A\u65E5\u671F\u7B5B\u9009\u4F1A\u8BDD\uFF081-31\uFF09\uFF0C\u9700\u914D\u5408 year \u548C month \u4F7F\u7528",
15504
+ keywords: "\u53EF\u9009\u7684\u641C\u7D22\u5173\u952E\u8BCD\u5217\u8868\uFF0C\u53EA\u8FD4\u56DE\u5305\u542B\u8FD9\u4E9B\u5173\u952E\u8BCD\u7684\u6BB5\u843D\uFF08OR \u903B\u8F91\u5339\u914D\uFF09",
15505
+ limit: "\u8FD4\u56DE\u6BB5\u843D\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 20",
15506
+ year: "\u6307\u5B9A\u5E74\u4EFD\u7B5B\u9009\u6BB5\u843D\uFF0C\u5982 2024",
15507
+ month: "\u6307\u5B9A\u6708\u4EFD\u7B5B\u9009\u6BB5\u843D\uFF081-12\uFF09\uFF0C\u9700\u914D\u5408 year \u4F7F\u7528",
15508
+ day: "\u6307\u5B9A\u65E5\u671F\u7B5B\u9009\u6BB5\u843D\uFF081-31\uFF09\uFF0C\u9700\u914D\u5408 year \u548C month \u4F7F\u7528",
15084
15509
  start_time: '\u5F00\u59CB\u65F6\u95F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"\uFF0C\u5982 "2024-03-15 14:00"',
15085
15510
  end_time: '\u7ED3\u675F\u65F6\u95F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"\uFF0C\u5982 "2024-03-15 18:30"'
15086
15511
  }
15087
15512
  },
15088
- get_session_messages: {
15089
- desc: "\u83B7\u53D6\u6307\u5B9A\u4F1A\u8BDD\u7684\u5B8C\u6574\u6D88\u606F\u5217\u8868\u3002\u7528\u4E8E\u5728 search_sessions \u627E\u5230\u76F8\u5173\u4F1A\u8BDD\u540E\uFF0C\u83B7\u53D6\u8BE5\u4F1A\u8BDD\u7684\u5B8C\u6574\u4E0A\u4E0B\u6587\u3002\u8FD4\u56DE\u4F1A\u8BDD\u7684\u6240\u6709\u6D88\u606F\u53CA\u53C2\u4E0E\u8005\u4FE1\u606F\u3002",
15513
+ get_segment_messages: {
15514
+ desc: "\u83B7\u53D6\u6307\u5B9A\u6BB5\u843D\u7684\u5B8C\u6574\u6D88\u606F\u5217\u8868\u3002\u7528\u4E8E\u5728 search_segments \u627E\u5230\u76F8\u5173\u6BB5\u843D\u540E\uFF0C\u83B7\u53D6\u8BE5\u6BB5\u843D\u7684\u5B8C\u6574\u4E0A\u4E0B\u6587\u3002\u8FD4\u56DE\u6BB5\u843D\u7684\u6240\u6709\u6D88\u606F\u53CA\u53C2\u4E0E\u8005\u4FE1\u606F\u3002",
15090
15515
  params: {
15091
- session_id: "\u4F1A\u8BDD ID\uFF0C\u53EF\u4EE5\u4ECE search_sessions \u7684\u8FD4\u56DE\u7ED3\u679C\u4E2D\u83B7\u53D6",
15092
- limit: "\u8FD4\u56DE\u6D88\u606F\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 1000\u3002\u5BF9\u4E8E\u8D85\u957F\u4F1A\u8BDD\u53EF\u4EE5\u9650\u5236\u8FD4\u56DE\u6570\u91CF\u4EE5\u8282\u7701 token"
15516
+ segment_id: "\u6BB5\u843D ID\uFF0C\u53EF\u4EE5\u4ECE search_segments \u7684\u8FD4\u56DE\u7ED3\u679C\u4E2D\u83B7\u53D6",
15517
+ limit: "\u8FD4\u56DE\u6D88\u606F\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 1000\u3002\u5BF9\u4E8E\u8D85\u957F\u6BB5\u843D\u53EF\u4EE5\u9650\u5236\u8FD4\u56DE\u6570\u91CF\u4EE5\u8282\u7701 token"
15093
15518
  }
15094
15519
  },
15095
- get_session_summaries: {
15096
- desc: `\u83B7\u53D6\u4F1A\u8BDD\u6458\u8981\u5217\u8868\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u5386\u53F2\u8BA8\u8BBA\u7684\u4E3B\u9898\u3002
15520
+ get_segment_summaries: {
15521
+ desc: `\u83B7\u53D6\u6BB5\u843D\u6458\u8981\u5217\u8868\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u5386\u53F2\u8BA8\u8BBA\u7684\u4E3B\u9898\u3002
15097
15522
 
15098
15523
  \u9002\u7528\u573A\u666F\uFF1A
15099
15524
  1. \u4E86\u89E3\u7FA4\u91CC\u6700\u8FD1\u5728\u804A\u4EC0\u4E48\u8BDD\u9898
15100
15525
  2. \u6309\u5173\u952E\u8BCD\u641C\u7D22\u8BA8\u8BBA\u8FC7\u7684\u8BDD\u9898
15101
15526
  3. \u6982\u89C8\u6027\u95EE\u9898\u5982"\u7FA4\u91CC\u6709\u6CA1\u6709\u8BA8\u8BBA\u8FC7\u65C5\u6E38"
15102
15527
 
15103
- \u8FD4\u56DE\u7684\u6458\u8981\u662F\u5BF9\u6BCF\u4E2A\u4F1A\u8BDD\u7684\u7B80\u77ED\u603B\u7ED3\uFF0C\u53EF\u4EE5\u5E2E\u52A9\u5FEB\u901F\u5B9A\u4F4D\u611F\u5174\u8DA3\u7684\u4F1A\u8BDD\uFF0C\u7136\u540E\u7528 get_session_messages \u83B7\u53D6\u8BE6\u60C5\u3002`,
15528
+ \u8FD4\u56DE\u7684\u6458\u8981\u662F\u5BF9\u6BCF\u4E2A\u6BB5\u843D\u7684\u7B80\u77ED\u603B\u7ED3\uFF0C\u53EF\u4EE5\u5E2E\u52A9\u5FEB\u901F\u5B9A\u4F4D\u611F\u5174\u8DA3\u7684\u6BB5\u843D\uFF0C\u7136\u540E\u7528 get_segment_messages \u83B7\u53D6\u8BE6\u60C5\u3002`,
15104
15529
  params: {
15105
15530
  keywords: "\u5728\u6458\u8981\u4E2D\u641C\u7D22\u7684\u5173\u952E\u8BCD\u5217\u8868\uFF08OR \u903B\u8F91\u5339\u914D\uFF09",
15106
- limit: "\u8FD4\u56DE\u4F1A\u8BDD\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 20",
15107
- year: "\u6307\u5B9A\u5E74\u4EFD\u7B5B\u9009\u4F1A\u8BDD",
15108
- month: "\u6307\u5B9A\u6708\u4EFD\u7B5B\u9009\u4F1A\u8BDD\uFF081-12\uFF09",
15109
- day: "\u6307\u5B9A\u65E5\u671F\u7B5B\u9009\u4F1A\u8BDD\uFF081-31\uFF09",
15531
+ limit: "\u8FD4\u56DE\u6BB5\u843D\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 20",
15532
+ year: "\u6307\u5B9A\u5E74\u4EFD\u7B5B\u9009\u6BB5\u843D",
15533
+ month: "\u6307\u5B9A\u6708\u4EFD\u7B5B\u9009\u6BB5\u843D\uFF081-12\uFF09",
15534
+ day: "\u6307\u5B9A\u65E5\u671F\u7B5B\u9009\u6BB5\u843D\uFF081-31\uFF09",
15110
15535
  start_time: '\u5F00\u59CB\u65F6\u95F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"',
15111
15536
  end_time: '\u7ED3\u675F\u65F6\u95F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"'
15112
15537
  }
@@ -15193,14 +15618,14 @@ var zh_CN_default = {
15193
15618
  fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u6D88\u606F\u8BB0\u5F55"
15194
15619
  },
15195
15620
  conversation_initiator_stats: {
15196
- desc: "\u7EDF\u8BA1\u6BCF\u4E2A\u6210\u5458\u53D1\u8D77\u4F1A\u8BDD\uFF08\u4F5C\u4E3A\u4F1A\u8BDD\u9996\u6761\u6D88\u606F\u7684\u53D1\u9001\u8005\uFF09\u7684\u6B21\u6570\uFF0C\u627E\u51FA\u8C01\u6700\u5E38\u5F00\u542F\u8BDD\u9898\u3002\u9700\u8981\u5DF2\u751F\u6210\u4F1A\u8BDD\u7D22\u5F15\u3002",
15621
+ desc: "\u7EDF\u8BA1\u6BCF\u4E2A\u6210\u5458\u53D1\u8D77\u6BB5\u843D\uFF08\u4F5C\u4E3A\u6BB5\u843D\u9996\u6761\u6D88\u606F\u7684\u53D1\u9001\u8005\uFF09\u7684\u6B21\u6570\uFF0C\u627E\u51FA\u8C01\u6700\u5E38\u5F00\u542F\u8BDD\u9898\u3002\u9700\u8981\u5DF2\u751F\u6210\u6BB5\u843D\u7D22\u5F15\u3002",
15197
15622
  params: {
15198
15623
  days: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E",
15199
15624
  limit: "\u8FD4\u56DE\u524D\u591A\u5C11\u540D"
15200
15625
  },
15201
15626
  rowTemplate: "{name}\uFF1A\u53D1\u8D77 {initiated_count} \u6B21\u8BDD\u9898",
15202
15627
  summaryTemplate: "\u8BDD\u9898\u53D1\u8D77\u8005 Top {rowCount}\uFF1A",
15203
- fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u4F1A\u8BDD\u8BB0\u5F55\uFF0C\u53EF\u80FD\u9700\u8981\u5148\u751F\u6210\u4F1A\u8BDD\u7D22\u5F15"
15628
+ fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u6BB5\u843D\u8BB0\u5F55\uFF0C\u53EF\u80FD\u9700\u8981\u5148\u751F\u6210\u6BB5\u843D\u7D22\u5F15"
15204
15629
  },
15205
15630
  activity_heatmap: {
15206
15631
  desc: "\u8FD4\u56DE \u661F\u671F\xD7\u5C0F\u65F6 \u7684\u6D88\u606F\u6570\u77E9\u9635\uFF0C\u9002\u5408\u751F\u6210\u6D3B\u8DC3\u5EA6\u70ED\u529B\u56FE\u3002weekday: 0=\u5468\u65E5, 1=\u5468\u4E00, ..., 6=\u5468\u516D\u3002",
@@ -15222,6 +15647,19 @@ var zh_CN_default = {
15222
15647
  days: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E",
15223
15648
  top_n: "\u8FD4\u56DE\u524D\u591A\u5C11\u4E2A\u5173\u952E\u8BCD"
15224
15649
  }
15650
+ },
15651
+ get_schema: {
15652
+ desc: "\u67E5\u770B\u804A\u5929\u6570\u636E\u5E93\u8868\u7ED3\u6784\u3002\u7ED8\u56FE\u6216\u81EA\u5B9A\u4E49 SQL \u524D\u5E94\u5148\u7528\u5B83\u786E\u8BA4\u8868\u540D\u548C\u5B57\u6BB5\u540D\u3002",
15653
+ params: {}
15654
+ },
15655
+ render_chart: {
15656
+ desc: "\u6839\u636E\u53EA\u8BFB SQL \u548C ChartSpec v1 \u751F\u6210 ChatLab \u539F\u751F\u56FE\u8868\u3002\u652F\u6301 bar\u3001line\u3001pie\u3001heatmap\u3002\u7981\u6B62\u8F93\u51FA HTML\u3001JavaScript\u3001SVG\u3001ECharts option \u6216\u6E32\u67D3\u4EE3\u7801\u3002",
15657
+ params: {
15658
+ sql: "\u53EA\u8BFB SELECT \u6216 WITH SELECT SQL\u3002\u5FC5\u987B\u8FD4\u56DE ChartSpec encoding \u4E2D\u5F15\u7528\u7684\u5B57\u6BB5\u3002",
15659
+ params: "SQL \u547D\u540D\u53C2\u6570\u5BF9\u8C61\uFF1B\u4E0D\u9700\u8981\u53C2\u6570\u65F6\u4F20\u7A7A\u5BF9\u8C61\u3002",
15660
+ chartSpec: "ChartSpec v1\uFF0C\u5305\u542B version\u3001type\u3001title\u3001encoding\u3002",
15661
+ maxRows: "\u56FE\u8868\u67E5\u8BE2\u6700\u5927\u884C\u6570\uFF0C\u9ED8\u8BA4 1000\u3002"
15662
+ }
15225
15663
  }
15226
15664
  },
15227
15665
  agent: {
@@ -15291,10 +15729,10 @@ var zh_CN_default = {
15291
15729
  rawErrorLabel: "\u539F\u59CB\u9519\u8BEF"
15292
15730
  },
15293
15731
  summary: {
15294
- sessionNotFound: "\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u6570\u636E\u5E93\u6253\u5F00\u5931\u8D25",
15732
+ segmentNotFound: "\u6BB5\u843D\u4E0D\u5B58\u5728\u6216\u6570\u636E\u5E93\u6253\u5F00\u5931\u8D25",
15295
15733
  tooFewMessages: "\u6D88\u606F\u6570\u91CF\u5C11\u4E8E{{count}}\u6761\uFF0C\u65E0\u9700\u751F\u6210\u6458\u8981",
15296
15734
  tooFewValidMessages: "\u6709\u6548\u6D88\u606F\u6570\u91CF\u5C11\u4E8E{{count}}\u6761\uFF0C\u65E0\u9700\u751F\u6210\u6458\u8981",
15297
- sessionNotExist: "\u4F1A\u8BDD\u4E0D\u5B58\u5728",
15735
+ segmentNotExist: "\u6BB5\u843D\u4E0D\u5B58\u5728",
15298
15736
  messagesTooFew: "\u6D88\u606F\u592A\u5C11",
15299
15737
  validMessagesTooFew: "\u6709\u6548\u6D88\u606F\u592A\u5C11",
15300
15738
  systemPromptDirect: "\u4F60\u662F\u4E00\u4E2A\u5BF9\u8BDD\u6458\u8981\u4E13\u5BB6\uFF0C\u64C5\u957F\u7528\u7B80\u6D01\u7684\u8BED\u8A00\u603B\u7ED3\u5BF9\u8BDD\u5185\u5BB9\u3002",
@@ -15394,40 +15832,40 @@ var en_US_default = {
15394
15832
  context_size: "Context size, i.e. how many messages before and after to retrieve, default 20"
15395
15833
  }
15396
15834
  },
15397
- search_sessions: {
15398
- desc: "Search chat sessions (conversation segments). Sessions are conversation units automatically split by message time intervals. Suitable for finding discussions on specific topics or understanding how many conversations occurred in a time period. Returns matching sessions with a 5-message preview each.",
15835
+ search_segments: {
15836
+ desc: "Search chat segments (conversation segments). Segments are conversation units automatically split by message time intervals. Suitable for finding discussions on specific topics or understanding how many conversations occurred in a time period. Returns matching segments with a 5-message preview each.",
15399
15837
  params: {
15400
- keywords: "Optional keyword list, only returns sessions containing these keywords (OR logic)",
15838
+ keywords: "Optional keyword list, only returns segments containing these keywords (OR logic)",
15401
15839
  limit: "Session count limit, default 20",
15402
- year: "Filter sessions by year, e.g. 2024",
15403
- month: "Filter sessions by month (1-12), use with year",
15404
- day: "Filter sessions by day (1-31), use with year and month",
15840
+ year: "Filter segments by year, e.g. 2024",
15841
+ month: "Filter segments by month (1-12), use with year",
15842
+ day: "Filter segments by day (1-31), use with year and month",
15405
15843
  start_time: 'Start time, format "YYYY-MM-DD HH:mm", e.g. "2024-03-15 14:00"',
15406
15844
  end_time: 'End time, format "YYYY-MM-DD HH:mm", e.g. "2024-03-15 18:30"'
15407
15845
  }
15408
15846
  },
15409
- get_session_messages: {
15410
- desc: "Get the complete message list for a specific session. Used to get full context after finding a relevant session via search_sessions. Returns all messages and participant information.",
15847
+ get_segment_messages: {
15848
+ desc: "Get the complete message list for a specific segment. Used to get full context after finding a relevant segment via search_segments. Returns all messages and participant information.",
15411
15849
  params: {
15412
- session_id: "Session ID, can be obtained from search_sessions results",
15413
- limit: "Message count limit, default 1000. Can be limited for very long sessions to save tokens"
15850
+ segment_id: "Segment ID, can be obtained from search_segments results",
15851
+ limit: "Message count limit, default 1000. Can be limited for very long segments to save tokens"
15414
15852
  }
15415
15853
  },
15416
- get_session_summaries: {
15417
- desc: `Get session summary list to quickly understand discussion topics in chat history.
15854
+ get_segment_summaries: {
15855
+ desc: `Get segment summary list to quickly understand discussion topics in chat history.
15418
15856
 
15419
15857
  Use cases:
15420
15858
  1. Understand what topics have been discussed recently
15421
15859
  2. Search for discussed topics by keyword
15422
15860
  3. Overview questions like "has the group discussed travel"
15423
15861
 
15424
- Returned summaries are brief descriptions of each session, helping quickly locate sessions of interest. Use get_session_messages for details.`,
15862
+ Returned summaries are brief descriptions of each segment, helping quickly locate segments of interest. Use get_segment_messages for details.`,
15425
15863
  params: {
15426
15864
  keywords: "Keyword list to search within summaries (OR logic)",
15427
15865
  limit: "Session count limit, default 20",
15428
- year: "Filter sessions by year",
15429
- month: "Filter sessions by month (1-12)",
15430
- day: "Filter sessions by day (1-31)",
15866
+ year: "Filter segments by year",
15867
+ month: "Filter segments by month (1-12)",
15868
+ day: "Filter segments by day (1-31)",
15431
15869
  start_time: 'Start time, format "YYYY-MM-DD HH:mm"',
15432
15870
  end_time: 'End time, format "YYYY-MM-DD HH:mm"'
15433
15871
  }
@@ -15515,14 +15953,14 @@ Returned summaries are brief descriptions of each session, helping quickly locat
15515
15953
  fallback: "No messages in this time range"
15516
15954
  },
15517
15955
  conversation_initiator_stats: {
15518
- desc: "Count how many times each member initiated a conversation (was the first sender in a session). Requires session index to be generated.",
15956
+ desc: "Count how many times each member initiated a conversation (was the first sender in a segment). Requires segment index to be generated.",
15519
15957
  params: {
15520
15958
  days: "Number of recent days to analyze",
15521
15959
  limit: "Number of top members to return"
15522
15960
  },
15523
15961
  rowTemplate: "{name}: initiated {initiated_count} topics",
15524
15962
  summaryTemplate: "Topic initiator Top {rowCount}:",
15525
- fallback: "No session records in this time range. Session index may need to be generated first."
15963
+ fallback: "No segment records in this time range. Session index may need to be generated first."
15526
15964
  },
15527
15965
  activity_heatmap: {
15528
15966
  desc: "Return a weekday \xD7 hour message count matrix for generating activity heatmaps. weekday: 0=Sun, 1=Mon, ..., 6=Sat.",
@@ -15544,6 +15982,19 @@ Returned summaries are brief descriptions of each session, helping quickly locat
15544
15982
  days: "Number of recent days to analyze",
15545
15983
  top_n: "Number of top keywords to return"
15546
15984
  }
15985
+ },
15986
+ get_schema: {
15987
+ desc: "Inspect the chat database schema. Use it before charting or custom SQL when table or field names are uncertain.",
15988
+ params: {}
15989
+ },
15990
+ render_chart: {
15991
+ desc: "Generate a native ChatLab chart from read-only SQL and ChartSpec v1. Supports bar, line, pie, and heatmap. Do not output HTML, JavaScript, SVG, ECharts options, or rendering code.",
15992
+ params: {
15993
+ sql: "Read-only SELECT or WITH SELECT SQL. It must return fields referenced by ChartSpec encoding.",
15994
+ params: "Named SQL parameters. Use an empty object when no parameters are needed.",
15995
+ chartSpec: "ChartSpec v1 with version, type, title, and encoding.",
15996
+ maxRows: "Maximum chart query rows. Default 1000."
15997
+ }
15547
15998
  }
15548
15999
  },
15549
16000
  // ===== AI Agent system prompts =====
@@ -15610,10 +16061,10 @@ Your task is to help users understand and analyze their private chat data.
15610
16061
  rawErrorLabel: "Raw error"
15611
16062
  },
15612
16063
  summary: {
15613
- sessionNotFound: "Session not found or database could not be opened",
16064
+ segmentNotFound: "Session not found or database could not be opened",
15614
16065
  tooFewMessages: "Message count less than {{count}}, no need to generate summary",
15615
16066
  tooFewValidMessages: "Valid message count less than {{count}}, no need to generate summary",
15616
- sessionNotExist: "Session not found",
16067
+ segmentNotExist: "Session not found",
15617
16068
  messagesTooFew: "Too few messages",
15618
16069
  validMessagesTooFew: "Too few valid messages",
15619
16070
  systemPromptDirect: "You are a conversation summarization expert. Summarize conversations concisely.",
@@ -15713,40 +16164,40 @@ var ja_JP_default = {
15713
16164
  context_size: "\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u30B5\u30A4\u30BA\u3002\u524D\u5F8C\u305D\u308C\u305E\u308C\u4F55\u4EF6\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D6\u5F97\u3059\u308B\u304B\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20"
15714
16165
  }
15715
16166
  },
15716
- search_sessions: {
15717
- desc: "\u30C1\u30E3\u30C3\u30C8\u30BB\u30C3\u30B7\u30E7\u30F3\uFF08\u4F1A\u8A71\u30BB\u30B0\u30E1\u30F3\u30C8\uFF09\u3092\u691C\u7D22\u3059\u308B\u3002\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u6642\u9593\u9593\u9694\u306B\u57FA\u3065\u3044\u3066\u81EA\u52D5\u5206\u5272\u3055\u308C\u305F\u4F1A\u8A71\u5358\u4F4D\u3067\u3042\u308B\u3002\u7279\u5B9A\u30C8\u30D4\u30C3\u30AF\u306E\u8B70\u8AD6\u3092\u898B\u3064\u3051\u305F\u308A\u3001\u3042\u308B\u671F\u9593\u5185\u306E\u4F1A\u8A71\u56DE\u6570\u3092\u628A\u63E1\u3059\u308B\u306E\u306B\u9069\u3057\u3066\u3044\u308B\u3002\u30DE\u30C3\u30C1\u3057\u305F\u30BB\u30C3\u30B7\u30E7\u30F3\u30EA\u30B9\u30C8\u3068\u5404\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u6700\u521D\u306E5\u4EF6\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u30D7\u30EC\u30D3\u30E5\u30FC\u3092\u8FD4\u5374\u3059\u308B\u3002",
16167
+ search_segments: {
16168
+ desc: "\u30C1\u30E3\u30C3\u30C8\u30BB\u30B0\u30E1\u30F3\u30C8\uFF08\u4F1A\u8A71\u30BB\u30B0\u30E1\u30F3\u30C8\uFF09\u3092\u691C\u7D22\u3059\u308B\u3002\u30BB\u30B0\u30E1\u30F3\u30C8\u306F\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u6642\u9593\u9593\u9694\u306B\u57FA\u3065\u3044\u3066\u81EA\u52D5\u5206\u5272\u3055\u308C\u305F\u4F1A\u8A71\u5358\u4F4D\u3067\u3042\u308B\u3002\u7279\u5B9A\u30C8\u30D4\u30C3\u30AF\u306E\u8B70\u8AD6\u3092\u898B\u3064\u3051\u305F\u308A\u3001\u3042\u308B\u671F\u9593\u5185\u306E\u4F1A\u8A71\u56DE\u6570\u3092\u628A\u63E1\u3059\u308B\u306E\u306B\u9069\u3057\u3066\u3044\u308B\u3002\u30DE\u30C3\u30C1\u3057\u305F\u30BB\u30B0\u30E1\u30F3\u30C8\u30EA\u30B9\u30C8\u3068\u5404\u30BB\u30B0\u30E1\u30F3\u30C8\u306E\u6700\u521D\u306E5\u4EF6\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u30D7\u30EC\u30D3\u30E5\u30FC\u3092\u8FD4\u5374\u3059\u308B\u3002",
15718
16169
  params: {
15719
- keywords: "\u4EFB\u610F\u306E\u691C\u7D22\u30AD\u30FC\u30EF\u30FC\u30C9\u30EA\u30B9\u30C8\u3002\u3053\u308C\u3089\u306E\u30AD\u30FC\u30EF\u30FC\u30C9\u3092\u542B\u3080\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u307F\u8FD4\u5374\u3059\u308B\uFF08OR \u30ED\u30B8\u30C3\u30AF\u30DE\u30C3\u30C1\uFF09",
15720
- limit: "\u8FD4\u5374\u30BB\u30C3\u30B7\u30E7\u30F3\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20",
15721
- year: "\u6307\u5B9A\u5E74\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF08\u4F8B\uFF1A2024\uFF09",
15722
- month: "\u6307\u5B9A\u6708\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-12\uFF09\u3002year \u3068\u4F75\u7528\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B",
15723
- day: "\u6307\u5B9A\u65E5\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-31\uFF09\u3002year \u3068 month \u3068\u4F75\u7528\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B",
16170
+ keywords: "\u4EFB\u610F\u306E\u691C\u7D22\u30AD\u30FC\u30EF\u30FC\u30C9\u30EA\u30B9\u30C8\u3002\u3053\u308C\u3089\u306E\u30AD\u30FC\u30EF\u30FC\u30C9\u3092\u542B\u3080\u30BB\u30B0\u30E1\u30F3\u30C8\u306E\u307F\u8FD4\u5374\u3059\u308B\uFF08OR \u30ED\u30B8\u30C3\u30AF\u30DE\u30C3\u30C1\uFF09",
16171
+ limit: "\u8FD4\u5374\u30BB\u30B0\u30E1\u30F3\u30C8\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20",
16172
+ year: "\u6307\u5B9A\u5E74\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF08\u4F8B\uFF1A2024\uFF09",
16173
+ month: "\u6307\u5B9A\u6708\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-12\uFF09\u3002year \u3068\u4F75\u7528\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B",
16174
+ day: "\u6307\u5B9A\u65E5\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-31\uFF09\u3002year \u3068 month \u3068\u4F75\u7528\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B",
15724
16175
  start_time: '\u958B\u59CB\u6642\u523B\u3002\u5F62\u5F0F "YYYY-MM-DD HH:mm"\uFF08\u4F8B\uFF1A"2024-03-15 14:00"\uFF09',
15725
16176
  end_time: '\u7D42\u4E86\u6642\u523B\u3002\u5F62\u5F0F "YYYY-MM-DD HH:mm"\uFF08\u4F8B\uFF1A"2024-03-15 18:30"\uFF09'
15726
16177
  }
15727
16178
  },
15728
- get_session_messages: {
15729
- desc: "\u6307\u5B9A\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u5B8C\u5168\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u30EA\u30B9\u30C8\u3092\u53D6\u5F97\u3059\u308B\u3002search_sessions \u3067\u95A2\u9023\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u898B\u3064\u3051\u305F\u5F8C\u3001\u305D\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u5B8C\u5168\u306A\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306B\u4F7F\u7528\u3059\u308B\u3002\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u5168\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u53C2\u52A0\u8005\u60C5\u5831\u3092\u8FD4\u5374\u3059\u308B\u3002",
16179
+ get_segment_messages: {
16180
+ desc: "\u6307\u5B9A\u30BB\u30B0\u30E1\u30F3\u30C8\u306E\u5B8C\u5168\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u30EA\u30B9\u30C8\u3092\u53D6\u5F97\u3059\u308B\u3002search_segments \u3067\u95A2\u9023\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u898B\u3064\u3051\u305F\u5F8C\u3001\u305D\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u306E\u5B8C\u5168\u306A\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306B\u4F7F\u7528\u3059\u308B\u3002\u30BB\u30B0\u30E1\u30F3\u30C8\u306E\u5168\u30E1\u30C3\u30BB\u30FC\u30B8\u3068\u53C2\u52A0\u8005\u60C5\u5831\u3092\u8FD4\u5374\u3059\u308B\u3002",
15730
16181
  params: {
15731
- session_id: "\u30BB\u30C3\u30B7\u30E7\u30F3 ID\u3002search_sessions \u306E\u8FD4\u5374\u7D50\u679C\u304B\u3089\u53D6\u5F97\u3067\u304D\u308B",
15732
- limit: "\u8FD4\u5374\u30E1\u30C3\u30BB\u30FC\u30B8\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 1000\u3002\u975E\u5E38\u306B\u9577\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u306F Token \u7BC0\u7D04\u306E\u305F\u3081\u4EF6\u6570\u3092\u5236\u9650\u3067\u304D\u308B"
16182
+ segment_id: "\u30BB\u30B0\u30E1\u30F3\u30C8 ID\u3002search_segments \u306E\u8FD4\u5374\u7D50\u679C\u304B\u3089\u53D6\u5F97\u3067\u304D\u308B",
16183
+ limit: "\u8FD4\u5374\u30E1\u30C3\u30BB\u30FC\u30B8\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 1000\u3002\u975E\u5E38\u306B\u9577\u3044\u30BB\u30B0\u30E1\u30F3\u30C8\u3067\u306F Token \u7BC0\u7D04\u306E\u305F\u3081\u4EF6\u6570\u3092\u5236\u9650\u3067\u304D\u308B"
15733
16184
  }
15734
16185
  },
15735
- get_session_summaries: {
15736
- desc: `\u30BB\u30C3\u30B7\u30E7\u30F3\u8981\u7D04\u30EA\u30B9\u30C8\u3092\u53D6\u5F97\u3057\u3001\u30B0\u30EB\u30FC\u30D7\u30C1\u30E3\u30C3\u30C8\u306E\u904E\u53BB\u306E\u8B70\u8AD6\u30C8\u30D4\u30C3\u30AF\u3092\u3059\u3070\u3084\u304F\u628A\u63E1\u3059\u308B\u3002
16186
+ get_segment_summaries: {
16187
+ desc: `\u30BB\u30B0\u30E1\u30F3\u30C8\u8981\u7D04\u30EA\u30B9\u30C8\u3092\u53D6\u5F97\u3057\u3001\u30B0\u30EB\u30FC\u30D7\u30C1\u30E3\u30C3\u30C8\u306E\u904E\u53BB\u306E\u8B70\u8AD6\u30C8\u30D4\u30C3\u30AF\u3092\u3059\u3070\u3084\u304F\u628A\u63E1\u3059\u308B\u3002
15737
16188
 
15738
16189
  \u9069\u7528\u30B7\u30FC\u30F3\uFF1A
15739
16190
  1. \u30B0\u30EB\u30FC\u30D7\u3067\u6700\u8FD1\u4F55\u304C\u8A71\u984C\u306B\u306A\u3063\u3066\u3044\u305F\u304B\u77E5\u308A\u305F\u3044
15740
16191
  2. \u30AD\u30FC\u30EF\u30FC\u30C9\u3067\u904E\u53BB\u306B\u8B70\u8AD6\u3055\u308C\u305F\u30C8\u30D4\u30C3\u30AF\u3092\u691C\u7D22
15741
16192
  3. \u300C\u30B0\u30EB\u30FC\u30D7\u3067\u65C5\u884C\u306B\u3064\u3044\u3066\u8A71\u3057\u305F\u3053\u3068\u3042\u308B\uFF1F\u300D\u306A\u3069\u306E\u6982\u8981\u7684\u306A\u8CEA\u554F
15742
16193
 
15743
- \u8FD4\u5374\u3055\u308C\u308B\u8981\u7D04\u306F\u5404\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u77ED\u3044\u6982\u8981\u3067\u3042\u308A\u3001\u8208\u5473\u306E\u3042\u308B\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u3059\u3070\u3084\u304F\u7279\u5B9A\u3057\u3001get_session_messages \u3067\u8A73\u7D30\u3092\u53D6\u5F97\u3067\u304D\u308B\u3002`,
16194
+ \u8FD4\u5374\u3055\u308C\u308B\u8981\u7D04\u306F\u5404\u30BB\u30B0\u30E1\u30F3\u30C8\u306E\u77ED\u3044\u6982\u8981\u3067\u3042\u308A\u3001\u8208\u5473\u306E\u3042\u308B\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u3059\u3070\u3084\u304F\u7279\u5B9A\u3057\u3001get_segment_messages \u3067\u8A73\u7D30\u3092\u53D6\u5F97\u3067\u304D\u308B\u3002`,
15744
16195
  params: {
15745
16196
  keywords: "\u8981\u7D04\u5185\u3067\u691C\u7D22\u3059\u308B\u30AD\u30FC\u30EF\u30FC\u30C9\u30EA\u30B9\u30C8\uFF08OR \u30ED\u30B8\u30C3\u30AF\u30DE\u30C3\u30C1\uFF09",
15746
- limit: "\u8FD4\u5374\u30BB\u30C3\u30B7\u30E7\u30F3\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20",
15747
- year: "\u6307\u5B9A\u5E74\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0",
15748
- month: "\u6307\u5B9A\u6708\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-12\uFF09",
15749
- day: "\u6307\u5B9A\u65E5\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-31\uFF09",
16197
+ limit: "\u8FD4\u5374\u30BB\u30B0\u30E1\u30F3\u30C8\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20",
16198
+ year: "\u6307\u5B9A\u5E74\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0",
16199
+ month: "\u6307\u5B9A\u6708\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-12\uFF09",
16200
+ day: "\u6307\u5B9A\u65E5\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-31\uFF09",
15750
16201
  start_time: '\u958B\u59CB\u6642\u523B\u3002\u5F62\u5F0F "YYYY-MM-DD HH:mm"',
15751
16202
  end_time: '\u7D42\u4E86\u6642\u523B\u3002\u5F62\u5F0F "YYYY-MM-DD HH:mm"'
15752
16203
  }
@@ -15833,14 +16284,14 @@ var ja_JP_default = {
15833
16284
  fallback: "\u3053\u306E\u671F\u9593\u306B\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093"
15834
16285
  },
15835
16286
  conversation_initiator_stats: {
15836
- desc: "\u5404\u30E1\u30F3\u30D0\u30FC\u304C\u4F1A\u8A71\u3092\u958B\u59CB\u3057\u305F\u56DE\u6570\uFF08\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u6700\u521D\u306E\u767A\u8A00\u8005\uFF09\u3092\u96C6\u8A08\u3057\u3001\u8AB0\u304C\u6700\u3082\u8A71\u984C\u3092\u5207\u308A\u51FA\u3059\u304B\u3092\u767A\u898B\u3059\u308B\u3002\u30BB\u30C3\u30B7\u30E7\u30F3\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u306E\u751F\u6210\u304C\u5FC5\u8981\u3002",
16287
+ desc: "\u5404\u30E1\u30F3\u30D0\u30FC\u304C\u4F1A\u8A71\u3092\u958B\u59CB\u3057\u305F\u56DE\u6570\uFF08\u30BB\u30B0\u30E1\u30F3\u30C8\u306E\u6700\u521D\u306E\u767A\u8A00\u8005\uFF09\u3092\u96C6\u8A08\u3057\u3001\u8AB0\u304C\u6700\u3082\u8A71\u984C\u3092\u5207\u308A\u51FA\u3059\u304B\u3092\u767A\u898B\u3059\u308B\u3002\u30BB\u30B0\u30E1\u30F3\u30C8\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u306E\u751F\u6210\u304C\u5FC5\u8981\u3002",
15837
16288
  params: {
15838
16289
  days: "\u76F4\u8FD1\u4F55\u65E5\u9593\u306E\u30C7\u30FC\u30BF\u3092\u96C6\u8A08\u3059\u308B\u304B",
15839
16290
  limit: "\u4E0A\u4F4D\u4F55\u540D\u3092\u8FD4\u5374\u3059\u308B\u304B"
15840
16291
  },
15841
16292
  rowTemplate: "{name}\uFF1A{initiated_count} \u56DE\u8A71\u984C\u3092\u958B\u59CB",
15842
16293
  summaryTemplate: "\u8A71\u984C\u958B\u59CB\u8005 Top {rowCount}\uFF1A",
15843
- fallback: "\u3053\u306E\u671F\u9593\u306B\u30BB\u30C3\u30B7\u30E7\u30F3\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u5148\u306B\u30BB\u30C3\u30B7\u30E7\u30F3\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3092\u751F\u6210\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B\u304B\u3082\u3057\u308C\u307E\u305B\u3093"
16294
+ fallback: "\u3053\u306E\u671F\u9593\u306B\u30BB\u30B0\u30E1\u30F3\u30C8\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u5148\u306B\u30BB\u30B0\u30E1\u30F3\u30C8\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3092\u751F\u6210\u3059\u308B\u5FC5\u8981\u304C\u3042\u308B\u304B\u3082\u3057\u308C\u307E\u305B\u3093"
15844
16295
  },
15845
16296
  activity_heatmap: {
15846
16297
  desc: "\u66DC\u65E5\xD7\u6642\u9593\u5E2F\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u6570\u30DE\u30C8\u30EA\u30C3\u30AF\u30B9\u3092\u8FD4\u5374\u3059\u308B\u3002\u6D3B\u6027\u5EA6\u30D2\u30FC\u30C8\u30DE\u30C3\u30D7\u306E\u751F\u6210\u306B\u9069\u3057\u3066\u3044\u308B\u3002weekday: 0=\u65E5\u66DC, 1=\u6708\u66DC, ..., 6=\u571F\u66DC\u3002",
@@ -15862,6 +16313,19 @@ var ja_JP_default = {
15862
16313
  days: "\u76F4\u8FD1\u4F55\u65E5\u9593\u306E\u30C7\u30FC\u30BF\u3092\u96C6\u8A08\u3059\u308B\u304B",
15863
16314
  top_n: "\u4E0A\u4F4D\u4F55\u500B\u306E\u30AD\u30FC\u30EF\u30FC\u30C9\u3092\u8FD4\u5374\u3059\u308B\u304B"
15864
16315
  }
16316
+ },
16317
+ get_schema: {
16318
+ desc: "\u30C1\u30E3\u30C3\u30C8\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u30B9\u30AD\u30FC\u30DE\u3092\u78BA\u8A8D\u3057\u307E\u3059\u3002\u30C1\u30E3\u30FC\u30C8\u4F5C\u6210\u3084\u30AB\u30B9\u30BF\u30E0 SQL \u306E\u524D\u306B\u30C6\u30FC\u30D6\u30EB\u540D\u3068\u30D5\u30A3\u30FC\u30EB\u30C9\u540D\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
16319
+ params: {}
16320
+ },
16321
+ render_chart: {
16322
+ desc: "\u8AAD\u307F\u53D6\u308A\u5C02\u7528 SQL \u3068 ChartSpec v1 \u304B\u3089 ChatLab \u30CD\u30A4\u30C6\u30A3\u30D6\u30C1\u30E3\u30FC\u30C8\u3092\u751F\u6210\u3057\u307E\u3059\u3002bar\u3001line\u3001pie\u3001heatmap \u3092\u30B5\u30DD\u30FC\u30C8\u3057\u307E\u3059\u3002HTML\u3001JavaScript\u3001SVG\u3001ECharts option\u3001\u63CF\u753B\u30B3\u30FC\u30C9\u306F\u51FA\u529B\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002",
16323
+ params: {
16324
+ sql: "\u8AAD\u307F\u53D6\u308A\u5C02\u7528\u306E SELECT \u307E\u305F\u306F WITH SELECT SQL\u3002ChartSpec encoding \u3067\u53C2\u7167\u3059\u308B\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002",
16325
+ params: "SQL \u306E\u540D\u524D\u4ED8\u304D\u30D1\u30E9\u30E1\u30FC\u30BF\u3002\u4E0D\u8981\u306A\u5834\u5408\u306F\u7A7A\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u6E21\u3057\u307E\u3059\u3002",
16326
+ chartSpec: "version\u3001type\u3001title\u3001encoding \u3092\u542B\u3080 ChartSpec v1\u3002",
16327
+ maxRows: "\u30C1\u30E3\u30FC\u30C8\u30AF\u30A8\u30EA\u306E\u6700\u5927\u884C\u6570\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F 1000\u3002"
16328
+ }
15865
16329
  }
15866
16330
  },
15867
16331
  agent: {
@@ -15929,10 +16393,10 @@ var ja_JP_default = {
15929
16393
  rawErrorLabel: "\u5143\u306E\u30A8\u30E9\u30FC"
15930
16394
  },
15931
16395
  summary: {
15932
- sessionNotFound: "\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u5B58\u5728\u3057\u306A\u3044\u304B\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u958B\u3051\u307E\u305B\u3093\u3067\u3057\u305F",
16396
+ segmentNotFound: "\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u5B58\u5728\u3057\u306A\u3044\u304B\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u958B\u3051\u307E\u305B\u3093\u3067\u3057\u305F",
15933
16397
  tooFewMessages: "\u30E1\u30C3\u30BB\u30FC\u30B8\u6570\u304C{{count}}\u4EF6\u672A\u6E80\u306E\u305F\u3081\u3001\u8981\u7D04\u306E\u751F\u6210\u306F\u4E0D\u8981\u3067\u3059",
15934
16398
  tooFewValidMessages: "\u6709\u52B9\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u6570\u304C{{count}}\u4EF6\u672A\u6E80\u306E\u305F\u3081\u3001\u8981\u7D04\u306E\u751F\u6210\u306F\u4E0D\u8981\u3067\u3059",
15935
- sessionNotExist: "\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u5B58\u5728\u3057\u307E\u305B\u3093",
16399
+ segmentNotExist: "\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u5B58\u5728\u3057\u307E\u305B\u3093",
15936
16400
  messagesTooFew: "\u30E1\u30C3\u30BB\u30FC\u30B8\u304C\u5C11\u306A\u3059\u304E\u307E\u3059",
15937
16401
  validMessagesTooFew: "\u6709\u52B9\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u304C\u5C11\u306A\u3059\u304E\u307E\u3059",
15938
16402
  systemPromptDirect: "\u3042\u306A\u305F\u306F\u4F1A\u8A71\u8981\u7D04\u306E\u5C02\u9580\u5BB6\u3067\u3042\u308A\u3001\u7C21\u6F54\u306A\u8A00\u8449\u3067\u4F1A\u8A71\u5185\u5BB9\u3092\u8981\u7D04\u3059\u308B\u3053\u3068\u306B\u9577\u3051\u3066\u3044\u308B\u3002",
@@ -16032,7 +16496,7 @@ var zh_TW_default = {
16032
16496
  context_size: "\u4E0A\u4E0B\u6587\u5927\u5C0F\uFF0C\u5373\u53D6\u5F97\u524D\u5F8C\u5404\u591A\u5C11\u689D\u8A0A\u606F\uFF0C\u9810\u8A2D 20"
16033
16497
  }
16034
16498
  },
16035
- search_sessions: {
16499
+ search_segments: {
16036
16500
  desc: "\u641C\u5C0B\u804A\u5929\u6703\u8A71\uFF08\u5C0D\u8A71\u6BB5\u843D\uFF09\u3002\u6703\u8A71\u662F\u6839\u64DA\u8A0A\u606F\u6642\u9593\u9593\u9694\u81EA\u52D5\u5207\u5206\u7684\u5C0D\u8A71\u55AE\u5143\u3002\u9069\u7528\u65BC\u67E5\u627E\u7279\u5B9A\u8A71\u984C\u7684\u8A0E\u8AD6\u3001\u4E86\u89E3\u67D0\u500B\u6642\u9593\u6BB5\u5167\u767C\u751F\u4E86\u5E7E\u6B21\u5C0D\u8A71\u7B49\u5834\u666F\u3002\u56DE\u50B3\u5339\u914D\u7684\u6703\u8A71\u6E05\u55AE\u53CA\u6BCF\u500B\u6703\u8A71\u7684\u524D5\u689D\u8A0A\u606F\u9810\u89BD\u3002",
16037
16501
  params: {
16038
16502
  keywords: "\u53EF\u9078\u7684\u641C\u5C0B\u95DC\u9375\u8A5E\u6E05\u55AE\uFF0C\u53EA\u56DE\u50B3\u5305\u542B\u9019\u4E9B\u95DC\u9375\u8A5E\u7684\u6703\u8A71\uFF08OR \u908F\u8F2F\u5339\u914D\uFF09",
@@ -16044,22 +16508,22 @@ var zh_TW_default = {
16044
16508
  end_time: '\u7D50\u675F\u6642\u9593\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"\uFF0C\u5982 "2024-03-15 18:30"'
16045
16509
  }
16046
16510
  },
16047
- get_session_messages: {
16048
- desc: "\u53D6\u5F97\u6307\u5B9A\u6703\u8A71\u7684\u5B8C\u6574\u8A0A\u606F\u6E05\u55AE\u3002\u7528\u65BC\u5728 search_sessions \u627E\u5230\u76F8\u95DC\u6703\u8A71\u5F8C\uFF0C\u53D6\u5F97\u8A72\u6703\u8A71\u7684\u5B8C\u6574\u4E0A\u4E0B\u6587\u3002\u56DE\u50B3\u6703\u8A71\u7684\u6240\u6709\u8A0A\u606F\u53CA\u53C3\u8207\u8005\u8CC7\u8A0A\u3002",
16511
+ get_segment_messages: {
16512
+ desc: "\u53D6\u5F97\u6307\u5B9A\u6703\u8A71\u7684\u5B8C\u6574\u8A0A\u606F\u6E05\u55AE\u3002\u7528\u65BC\u5728 search_segments \u627E\u5230\u76F8\u95DC\u6703\u8A71\u5F8C\uFF0C\u53D6\u5F97\u8A72\u6703\u8A71\u7684\u5B8C\u6574\u4E0A\u4E0B\u6587\u3002\u56DE\u50B3\u6703\u8A71\u7684\u6240\u6709\u8A0A\u606F\u53CA\u53C3\u8207\u8005\u8CC7\u8A0A\u3002",
16049
16513
  params: {
16050
- session_id: "\u6703\u8A71 ID\uFF0C\u53EF\u4EE5\u5F9E search_sessions \u7684\u56DE\u50B3\u7D50\u679C\u4E2D\u53D6\u5F97",
16514
+ segment_id: "\u6703\u8A71 ID\uFF0C\u53EF\u4EE5\u5F9E search_segments \u7684\u56DE\u50B3\u7D50\u679C\u4E2D\u53D6\u5F97",
16051
16515
  limit: "\u56DE\u50B3\u8A0A\u606F\u6578\u91CF\u9650\u5236\uFF0C\u9810\u8A2D 1000\u3002\u5C0D\u65BC\u8D85\u9577\u6703\u8A71\u53EF\u4EE5\u9650\u5236\u56DE\u50B3\u6578\u91CF\u4EE5\u7BC0\u7701 token"
16052
16516
  }
16053
16517
  },
16054
- get_session_summaries: {
16055
- desc: `\u53D6\u5F97\u6703\u8A71\u6458\u8981\u6E05\u55AE\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u6B77\u53F2\u8A0E\u8AD6\u7684\u4E3B\u984C\u3002
16518
+ get_segment_summaries: {
16519
+ desc: `\u53D6\u5F97\u6BB5\u843D\u6458\u8981\u6E05\u55AE\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u6B77\u53F2\u8A0E\u8AD6\u7684\u4E3B\u984C\u3002
16056
16520
 
16057
16521
  \u9069\u7528\u5834\u666F\uFF1A
16058
16522
  1. \u4E86\u89E3\u7FA4\u88E1\u6700\u8FD1\u5728\u804A\u4EC0\u9EBC\u8A71\u984C
16059
16523
  2. \u6309\u95DC\u9375\u8A5E\u641C\u5C0B\u8A0E\u8AD6\u904E\u7684\u8A71\u984C
16060
16524
  3. \u6982\u89BD\u6027\u554F\u984C\u5982\u300C\u7FA4\u88E1\u6709\u6C92\u6709\u8A0E\u8AD6\u904E\u65C5\u904A\u300D
16061
16525
 
16062
- \u56DE\u50B3\u7684\u6458\u8981\u662F\u5C0D\u6BCF\u500B\u6703\u8A71\u7684\u7C21\u77ED\u7E3D\u7D50\uFF0C\u53EF\u4EE5\u5E6B\u52A9\u5FEB\u901F\u5B9A\u4F4D\u611F\u8208\u8DA3\u7684\u6703\u8A71\uFF0C\u7136\u5F8C\u7528 get_session_messages \u53D6\u5F97\u8A73\u60C5\u3002`,
16526
+ \u56DE\u50B3\u7684\u6458\u8981\u662F\u5C0D\u6BCF\u500B\u6703\u8A71\u7684\u7C21\u77ED\u7E3D\u7D50\uFF0C\u53EF\u4EE5\u5E6B\u52A9\u5FEB\u901F\u5B9A\u4F4D\u611F\u8208\u8DA3\u7684\u6703\u8A71\uFF0C\u7136\u5F8C\u7528 get_segment_messages \u53D6\u5F97\u8A73\u60C5\u3002`,
16063
16527
  params: {
16064
16528
  keywords: "\u5728\u6458\u8981\u4E2D\u641C\u5C0B\u7684\u95DC\u9375\u8A5E\u6E05\u55AE\uFF08OR \u908F\u8F2F\u5339\u914D\uFF09",
16065
16529
  limit: "\u56DE\u50B3\u6703\u8A71\u6578\u91CF\u9650\u5236\uFF0C\u9810\u8A2D 20",
@@ -16181,6 +16645,19 @@ var zh_TW_default = {
16181
16645
  days: "\u7D71\u8A08\u6700\u8FD1\u591A\u5C11\u5929\u7684\u8CC7\u6599",
16182
16646
  top_n: "\u56DE\u50B3\u524D\u591A\u5C11\u500B\u95DC\u9375\u8A5E"
16183
16647
  }
16648
+ },
16649
+ get_schema: {
16650
+ desc: "\u67E5\u770B\u804A\u5929\u8CC7\u6599\u5EAB\u8868\u7D50\u69CB\u3002\u7E6A\u5716\u6216\u81EA\u8A02 SQL \u524D\u61C9\u5148\u7528\u5B83\u78BA\u8A8D\u8868\u540D\u548C\u6B04\u4F4D\u540D\u3002",
16651
+ params: {}
16652
+ },
16653
+ render_chart: {
16654
+ desc: "\u6839\u64DA\u552F\u8B80 SQL \u548C ChartSpec v1 \u751F\u6210 ChatLab \u539F\u751F\u5716\u8868\u3002\u652F\u63F4 bar\u3001line\u3001pie\u3001heatmap\u3002\u7981\u6B62\u8F38\u51FA HTML\u3001JavaScript\u3001SVG\u3001ECharts option \u6216\u6E32\u67D3\u7A0B\u5F0F\u78BC\u3002",
16655
+ params: {
16656
+ sql: "\u552F\u8B80 SELECT \u6216 WITH SELECT SQL\u3002\u5FC5\u9808\u8FD4\u56DE ChartSpec encoding \u4E2D\u5F15\u7528\u7684\u6B04\u4F4D\u3002",
16657
+ params: "SQL \u547D\u540D\u53C3\u6578\u7269\u4EF6\uFF1B\u4E0D\u9700\u8981\u53C3\u6578\u6642\u50B3\u7A7A\u7269\u4EF6\u3002",
16658
+ chartSpec: "ChartSpec v1\uFF0C\u5305\u542B version\u3001type\u3001title\u3001encoding\u3002",
16659
+ maxRows: "\u5716\u8868\u67E5\u8A62\u6700\u5927\u884C\u6578\uFF0C\u9810\u8A2D 1000\u3002"
16660
+ }
16184
16661
  }
16185
16662
  },
16186
16663
  agent: {
@@ -16248,10 +16725,10 @@ var zh_TW_default = {
16248
16725
  rawErrorLabel: "\u539F\u59CB\u932F\u8AA4"
16249
16726
  },
16250
16727
  summary: {
16251
- sessionNotFound: "\u6703\u8A71\u4E0D\u5B58\u5728\u6216\u8CC7\u6599\u5EAB\u958B\u555F\u5931\u6557",
16728
+ segmentNotFound: "\u6703\u8A71\u4E0D\u5B58\u5728\u6216\u8CC7\u6599\u5EAB\u958B\u555F\u5931\u6557",
16252
16729
  tooFewMessages: "\u8A0A\u606F\u6578\u91CF\u5C11\u65BC {{count}} \u689D\uFF0C\u7121\u9700\u7522\u751F\u6458\u8981",
16253
16730
  tooFewValidMessages: "\u6709\u6548\u8A0A\u606F\u6578\u91CF\u5C11\u65BC {{count}} \u689D\uFF0C\u7121\u9700\u7522\u751F\u6458\u8981",
16254
- sessionNotExist: "\u627E\u4E0D\u5230\u6703\u8A71",
16731
+ segmentNotExist: "\u627E\u4E0D\u5230\u6703\u8A71",
16255
16732
  messagesTooFew: "\u8A0A\u606F\u592A\u5C11",
16256
16733
  validMessagesTooFew: "\u6709\u6548\u8A0A\u606F\u592A\u5C11",
16257
16734
  systemPromptDirect: "\u4F60\u662F\u4E00\u500B\u5C0D\u8A71\u6458\u8981\u5C08\u5BB6\uFF0C\u64C5\u9577\u7528\u7C21\u6F54\u7684\u8A9E\u8A00\u7E3D\u7D50\u5C0D\u8A71\u5167\u5BB9\u3002",
@@ -16266,9 +16743,9 @@ var localeMap = {
16266
16743
  "ja-JP": ja_JP_default,
16267
16744
  "zh-TW": zh_TW_default
16268
16745
  };
16269
- function getNestedValue(obj, path35) {
16746
+ function getNestedValue(obj, path36) {
16270
16747
  let current = obj;
16271
- for (const segment2 of path35.split(".")) {
16748
+ for (const segment2 of path36.split(".")) {
16272
16749
  if (current == null || typeof current !== "object") return void 0;
16273
16750
  current = current[segment2];
16274
16751
  }
@@ -16464,15 +16941,15 @@ function postProcessSummary(summary, lengthLimit) {
16464
16941
  }
16465
16942
  return result;
16466
16943
  }
16467
- async function generateSessionSummary(deps, chatSessionId, options = {}) {
16944
+ async function generateSessionSummary(deps, segmentId, options = {}) {
16468
16945
  const { locale = "zh-CN", forceRegenerate = false, strategy = "standard" } = options;
16469
16946
  const log = deps.logger;
16470
16947
  try {
16471
16948
  if (!forceRegenerate) {
16472
- const existing = deps.getSummary(chatSessionId);
16949
+ const existing = deps.getSummary(segmentId);
16473
16950
  if (existing) return { success: true, summary: existing };
16474
16951
  }
16475
- const rawMessages = deps.loadMessages(chatSessionId);
16952
+ const rawMessages = deps.loadMessages(segmentId);
16476
16953
  if (!rawMessages) {
16477
16954
  return { success: false, error: deps.t("summary.sessionNotFound") };
16478
16955
  }
@@ -16489,7 +16966,7 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
16489
16966
  const content = formatMessages(validMessages);
16490
16967
  log?.info(
16491
16968
  "Summary",
16492
- `Generating summary: sessionId=${chatSessionId}, strategy=${strategy}, raw=${rawMessages.length}, valid=${validMessages.length}, chars=${content.length}`
16969
+ `Generating summary: sessionId=${segmentId}, strategy=${strategy}, raw=${rawMessages.length}, valid=${validMessages.length}, chars=${content.length}`
16493
16970
  );
16494
16971
  let summary;
16495
16972
  if (content.length <= SEGMENT_THRESHOLD) {
@@ -16524,7 +17001,7 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
16524
17001
  }
16525
17002
  }
16526
17003
  summary = postProcessSummary(summary, lengthLimit);
16527
- deps.saveSummary(chatSessionId, summary);
17004
+ deps.saveSummary(segmentId, summary);
16528
17005
  log?.info("Summary", `Summary generated: "${summary.slice(0, 50)}..."`);
16529
17006
  return { success: true, summary };
16530
17007
  } catch (error) {
@@ -16532,9 +17009,9 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
16532
17009
  return { success: false, error: error instanceof Error ? error.message : String(error) };
16533
17010
  }
16534
17011
  }
16535
- function checkSessionsCanGenerateSummary(deps, chatSessionIds) {
17012
+ function checkSessionsCanGenerateSummary(deps, segmentIds) {
16536
17013
  const results = /* @__PURE__ */ new Map();
16537
- for (const id of chatSessionIds) {
17014
+ for (const id of segmentIds) {
16538
17015
  const messages = deps.loadMessages(id);
16539
17016
  if (!messages) {
16540
17017
  results.set(id, { canGenerate: false, reason: deps.t("summary.sessionNotExist") });
@@ -16845,7 +17322,7 @@ function getNoopLogger() {
16845
17322
  var _logger = getNoopLogger();
16846
17323
 
16847
17324
  // ../../packages/node-runtime/src/ai/rag/chunking/session.ts
16848
- import Database3 from "better-sqlite3";
17325
+ import Database2 from "better-sqlite3";
16849
17326
  var _logger2 = getNoopLogger();
16850
17327
 
16851
17328
  // ../../packages/node-runtime/src/ai/rag/embedding/openai-compatible.ts
@@ -16855,7 +17332,7 @@ var _logger3 = getNoopLogger();
16855
17332
  var _logger4 = getNoopLogger();
16856
17333
 
16857
17334
  // ../../packages/node-runtime/src/ai/rag/store/sqlite.ts
16858
- import Database4 from "better-sqlite3";
17335
+ import Database3 from "better-sqlite3";
16859
17336
  var _logger5 = getNoopLogger();
16860
17337
 
16861
17338
  // ../../packages/node-runtime/src/ai/rag/store/memory.ts
@@ -17380,7 +17857,216 @@ function expandHome(filePath) {
17380
17857
  return filePath;
17381
17858
  }
17382
17859
 
17860
+ // ../../packages/node-runtime/src/data-dir-compat.ts
17861
+ import fs34 from "fs";
17862
+ import path31 from "path";
17863
+ var DataDirCompatibilityError = class extends Error {
17864
+ code;
17865
+ userDataDir;
17866
+ metaPath;
17867
+ currentVersion;
17868
+ minRuntimeVersion;
17869
+ statusCode = 409;
17870
+ constructor(code, message, options) {
17871
+ super(message, { cause: options.cause });
17872
+ this.name = "DataDirCompatibilityError";
17873
+ this.code = code;
17874
+ this.userDataDir = options.userDataDir;
17875
+ this.metaPath = options.metaPath;
17876
+ this.currentVersion = options.currentVersion;
17877
+ this.minRuntimeVersion = options.minRuntimeVersion;
17878
+ }
17879
+ };
17880
+ function readDataDirCompatibilityMeta(userDataDir) {
17881
+ const metaPath = getMetaPath(userDataDir);
17882
+ if (!fs34.existsSync(metaPath)) return null;
17883
+ let parsed;
17884
+ try {
17885
+ parsed = JSON.parse(fs34.readFileSync(metaPath, "utf-8"));
17886
+ } catch (error) {
17887
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid data directory compatibility meta JSON.", error);
17888
+ }
17889
+ return validateDataDirCompatibilityMeta(parsed, userDataDir, metaPath);
17890
+ }
17891
+ function assertDataDirCompatible(pathProvider, runtime, options = {}) {
17892
+ const userDataDir = pathProvider.getUserDataDir();
17893
+ const meta = readDataDirCompatibilityMeta(userDataDir);
17894
+ if (!meta) return;
17895
+ if (!isStableSemver(runtime.version)) {
17896
+ throw new DataDirCompatibilityError(
17897
+ "DATA_DIR_REQUIRES_NEWER_RUNTIME",
17898
+ `ChatLab data directory compatibility check requires a stable runtime version; current version is ${runtime.version}.`,
17899
+ {
17900
+ userDataDir,
17901
+ metaPath: getMetaPath(userDataDir),
17902
+ currentVersion: runtime.version,
17903
+ minRuntimeVersion: meta.minRuntimeVersion
17904
+ }
17905
+ );
17906
+ }
17907
+ if (compareStableSemver(runtime.version, meta.minRuntimeVersion) < 0) {
17908
+ const env = options.env ?? process.env;
17909
+ if (env.CHATLAB_ALLOW_INCOMPATIBLE_DATA_DIR === "1") {
17910
+ const warn = options.warn ?? console.warn;
17911
+ warn(
17912
+ [
17913
+ "CHATLAB_ALLOW_INCOMPATIBLE_DATA_DIR=1 is set. ChatLab will bypass the data directory runtime version gate.",
17914
+ `Current runtime ${runtime.kind}@${runtime.version} is below required version ${meta.minRuntimeVersion}.`,
17915
+ `Data directory: ${userDataDir}. Continuing may risk data corruption.`
17916
+ ].join(" ")
17917
+ );
17918
+ return;
17919
+ }
17920
+ throw new DataDirCompatibilityError(
17921
+ "DATA_DIR_REQUIRES_NEWER_RUNTIME",
17922
+ `ChatLab data directory requires runtime version ${meta.minRuntimeVersion} or newer; current version is ${runtime.version}.`,
17923
+ {
17924
+ userDataDir,
17925
+ metaPath: getMetaPath(userDataDir),
17926
+ currentVersion: runtime.version,
17927
+ minRuntimeVersion: meta.minRuntimeVersion
17928
+ }
17929
+ );
17930
+ }
17931
+ }
17932
+ function raiseDataDirMinRuntimeVersion(pathProvider, input) {
17933
+ const userDataDir = pathProvider.getUserDataDir();
17934
+ const metaPath = getMetaPath(userDataDir);
17935
+ assertStableVersion(input.minRuntimeVersion, userDataDir, metaPath, "Invalid minRuntimeVersion.");
17936
+ assertStableVersion(input.runtime.version, userDataDir, metaPath, "Invalid runtime version.");
17937
+ if (!isRuntimeKind(input.runtime.kind)) {
17938
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid runtime kind.");
17939
+ }
17940
+ if (!isNonEmptyString(input.module) || !isNonEmptyString(input.reason)) {
17941
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid compatibility raise metadata.");
17942
+ }
17943
+ if (!Number.isInteger(input.dataCompatibilityVersion) || input.dataCompatibilityVersion < 0) {
17944
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid dataCompatibilityVersion.");
17945
+ }
17946
+ const existing = readDataDirCompatibilityMeta(userDataDir);
17947
+ const minRuntimeVersion = existing && compareStableSemver(existing.minRuntimeVersion, input.minRuntimeVersion) > 0 ? existing.minRuntimeVersion : input.minRuntimeVersion;
17948
+ const dataCompatibilityVersion = Math.max(existing?.dataCompatibilityVersion ?? 0, input.dataCompatibilityVersion);
17949
+ const reasons = mergeReasons(existing?.reasons ?? [], input.reason);
17950
+ const now = input.now ?? (() => Math.floor(Date.now() / 1e3));
17951
+ const nextMeta = {
17952
+ formatVersion: 1,
17953
+ minRuntimeVersion,
17954
+ dataCompatibilityVersion,
17955
+ reasons,
17956
+ updatedBy: {
17957
+ runtime: input.runtime.kind,
17958
+ module: input.module,
17959
+ version: input.runtime.version
17960
+ },
17961
+ updatedAt: now()
17962
+ };
17963
+ writeMetaAtomic(userDataDir, metaPath, nextMeta);
17964
+ return nextMeta;
17965
+ }
17966
+ function getMetaPath(userDataDir) {
17967
+ return path31.join(userDataDir, ".chatlab-meta.json");
17968
+ }
17969
+ function validateDataDirCompatibilityMeta(value, userDataDir, metaPath) {
17970
+ if (!isRecord2(value)) {
17971
+ throw createInvalidMetaError(userDataDir, metaPath, "Data directory compatibility meta must be an object.");
17972
+ }
17973
+ if (value.formatVersion !== 1) {
17974
+ throw createInvalidMetaError(userDataDir, metaPath, "Unsupported data directory compatibility meta format.");
17975
+ }
17976
+ const minRuntimeVersion = value.minRuntimeVersion;
17977
+ const dataCompatibilityVersion = value.dataCompatibilityVersion;
17978
+ const reasons = value.reasons;
17979
+ const updatedBy = value.updatedBy;
17980
+ const updatedAt = value.updatedAt;
17981
+ if (!isStableSemver(minRuntimeVersion) || typeof dataCompatibilityVersion !== "number" || !Number.isInteger(dataCompatibilityVersion) || dataCompatibilityVersion < 0 || !Array.isArray(reasons) || !reasons.every(isNonEmptyString) || !isRecord2(updatedBy) || !isRuntimeKind(updatedBy.runtime) || !isNonEmptyString(updatedBy.module) || !isStableSemver(updatedBy.version) || typeof updatedAt !== "number" || !Number.isInteger(updatedAt) || updatedAt < 0) {
17982
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid data directory compatibility meta fields.");
17983
+ }
17984
+ return {
17985
+ formatVersion: 1,
17986
+ minRuntimeVersion,
17987
+ dataCompatibilityVersion,
17988
+ reasons: [...reasons],
17989
+ updatedBy: {
17990
+ runtime: updatedBy.runtime,
17991
+ module: updatedBy.module,
17992
+ version: updatedBy.version
17993
+ },
17994
+ updatedAt
17995
+ };
17996
+ }
17997
+ function createInvalidMetaError(userDataDir, metaPath, message, cause) {
17998
+ return new DataDirCompatibilityError("DATA_DIR_COMPATIBILITY_META_INVALID", message, {
17999
+ userDataDir,
18000
+ metaPath,
18001
+ cause
18002
+ });
18003
+ }
18004
+ function assertStableVersion(version, userDataDir, metaPath, message) {
18005
+ if (!isStableSemver(version)) {
18006
+ throw createInvalidMetaError(userDataDir, metaPath, message);
18007
+ }
18008
+ }
18009
+ function isStableSemver(version) {
18010
+ return typeof version === "string" && /^\d+\.\d+\.\d+$/.test(version);
18011
+ }
18012
+ function compareStableSemver(left, right) {
18013
+ const leftParts = left.split(".").map(Number);
18014
+ const rightParts = right.split(".").map(Number);
18015
+ for (let i = 0; i < 3; i += 1) {
18016
+ if (leftParts[i] > rightParts[i]) return 1;
18017
+ if (leftParts[i] < rightParts[i]) return -1;
18018
+ }
18019
+ return 0;
18020
+ }
18021
+ function mergeReasons(existing, next) {
18022
+ return [.../* @__PURE__ */ new Set([...existing, next])];
18023
+ }
18024
+ function writeMetaAtomic(userDataDir, metaPath, meta) {
18025
+ fs34.mkdirSync(userDataDir, { recursive: true });
18026
+ const tempPath = path31.join(userDataDir, `.chatlab-meta.json.${process.pid}.${Date.now()}.tmp`);
18027
+ try {
18028
+ fs34.writeFileSync(tempPath, `${JSON.stringify(meta, null, 2)}
18029
+ `, "utf-8");
18030
+ fs34.renameSync(tempPath, metaPath);
18031
+ } catch (error) {
18032
+ try {
18033
+ if (fs34.existsSync(tempPath)) fs34.unlinkSync(tempPath);
18034
+ } catch {
18035
+ }
18036
+ throw error;
18037
+ }
18038
+ }
18039
+ function isRecord2(value) {
18040
+ return typeof value === "object" && value !== null && !Array.isArray(value);
18041
+ }
18042
+ function isNonEmptyString(value) {
18043
+ return typeof value === "string" && value.trim().length > 0;
18044
+ }
18045
+ function isRuntimeKind(value) {
18046
+ return value === "cli" || value === "desktop" || value === "mcp" || value === "unknown";
18047
+ }
18048
+
17383
18049
  // ../../packages/node-runtime/src/migrations/chat-db-migrations.ts
18050
+ var CHAT_DB_COMPATIBILITY_RAISES = [
18051
+ {
18052
+ migrationVersion: 6,
18053
+ minRuntimeVersion: "0.25.1",
18054
+ dataCompatibilityVersion: 1,
18055
+ reason: "segment-schema",
18056
+ module: "chat-db-migration"
18057
+ }
18058
+ ];
18059
+ function raiseChatDbCompatibilityGate(pathProvider, runtime) {
18060
+ for (const compatibilityRaise of CHAT_DB_COMPATIBILITY_RAISES) {
18061
+ raiseDataDirMinRuntimeVersion(pathProvider, {
18062
+ minRuntimeVersion: compatibilityRaise.minRuntimeVersion,
18063
+ dataCompatibilityVersion: compatibilityRaise.dataCompatibilityVersion,
18064
+ reason: compatibilityRaise.reason,
18065
+ runtime,
18066
+ module: compatibilityRaise.module
18067
+ });
18068
+ }
18069
+ }
17384
18070
  function getChatDbMigrations(deps) {
17385
18071
  const hasColumn2 = (db, tableName, columnName) => {
17386
18072
  const tableInfo = db.pragma(`table_info(${tableName})`);
@@ -17555,13 +18241,120 @@ function getChatDbMigrations(deps) {
17555
18241
  db.exec("CREATE INDEX IF NOT EXISTS idx_session_time ON chat_session(start_ts, end_ts)");
17556
18242
  db.exec("CREATE INDEX IF NOT EXISTS idx_context_session ON message_context(session_id)");
17557
18243
  }
18244
+ },
18245
+ {
18246
+ version: 6,
18247
+ description: "Rename chat_session index tables to segment terminology",
18248
+ up: (db) => {
18249
+ const hasTable2 = (tableName) => {
18250
+ const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get(tableName);
18251
+ return !!row;
18252
+ };
18253
+ db.exec(`
18254
+ DROP INDEX IF EXISTS idx_session_time;
18255
+ DROP INDEX IF EXISTS idx_context_session;
18256
+ `);
18257
+ const hasLegacyChatSession = hasTable2("chat_session");
18258
+ const hasSegment = hasTable2("segment");
18259
+ if (hasLegacyChatSession && !hasSegment) {
18260
+ db.exec("ALTER TABLE chat_session RENAME TO segment");
18261
+ } else if (hasLegacyChatSession && hasSegment) {
18262
+ db.exec(`
18263
+ INSERT OR IGNORE INTO segment (id, start_ts, end_ts, message_count, is_manual, summary)
18264
+ SELECT id, start_ts, end_ts, message_count, is_manual, summary
18265
+ FROM chat_session;
18266
+ DROP TABLE chat_session;
18267
+ `);
18268
+ }
18269
+ if (hasTable2("message_context") && hasColumn2(db, "message_context", "session_id")) {
18270
+ db.exec("ALTER TABLE message_context RENAME COLUMN session_id TO segment_id");
18271
+ }
18272
+ db.exec(`
18273
+ CREATE TABLE IF NOT EXISTS segment (
18274
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
18275
+ start_ts INTEGER NOT NULL,
18276
+ end_ts INTEGER NOT NULL,
18277
+ message_count INTEGER DEFAULT 0,
18278
+ is_manual INTEGER DEFAULT 0,
18279
+ summary TEXT
18280
+ );
18281
+
18282
+ CREATE TABLE IF NOT EXISTS message_context (
18283
+ message_id INTEGER PRIMARY KEY,
18284
+ segment_id INTEGER NOT NULL,
18285
+ topic_id INTEGER
18286
+ );
18287
+
18288
+ CREATE INDEX IF NOT EXISTS idx_segment_time ON segment(start_ts, end_ts);
18289
+ CREATE INDEX IF NOT EXISTS idx_context_segment ON message_context(segment_id);
18290
+ `);
18291
+ }
17558
18292
  }
17559
18293
  ];
17560
18294
  }
17561
18295
 
17562
18296
  // ../../packages/node-runtime/src/database-manager.ts
17563
- import * as fs34 from "fs";
17564
- import * as path31 from "path";
18297
+ import * as fs35 from "fs";
18298
+ import * as path32 from "path";
18299
+
18300
+ // ../../packages/node-runtime/src/better-sqlite3-adapter.ts
18301
+ import Database4 from "better-sqlite3";
18302
+ var BetterSqlitePreparedStatement = class {
18303
+ constructor(stmt) {
18304
+ this.stmt = stmt;
18305
+ this.readonly = stmt.readonly;
18306
+ }
18307
+ stmt;
18308
+ readonly;
18309
+ get(...params) {
18310
+ return this.stmt.get(...params);
18311
+ }
18312
+ all(...params) {
18313
+ return this.stmt.all(...params);
18314
+ }
18315
+ run(...params) {
18316
+ const result = this.stmt.run(...params);
18317
+ return {
18318
+ changes: result.changes,
18319
+ lastInsertRowid: result.lastInsertRowid
18320
+ };
18321
+ }
18322
+ };
18323
+ var BetterSqliteAdapter = class {
18324
+ constructor(db) {
18325
+ this.db = db;
18326
+ this.readonly = db.readonly;
18327
+ }
18328
+ db;
18329
+ readonly;
18330
+ exec(sql) {
18331
+ this.db.exec(sql);
18332
+ }
18333
+ prepare(sql) {
18334
+ return new BetterSqlitePreparedStatement(this.db.prepare(sql));
18335
+ }
18336
+ transaction(fn) {
18337
+ return this.db.transaction(fn)();
18338
+ }
18339
+ pragma(pragma) {
18340
+ return this.db.pragma(pragma);
18341
+ }
18342
+ close() {
18343
+ this.db.close();
18344
+ }
18345
+ };
18346
+ function openBetterSqliteDatabase(filePath, options) {
18347
+ const db = new Database4(filePath, {
18348
+ readonly: options?.readonly ?? false,
18349
+ nativeBinding: options?.nativeBinding
18350
+ });
18351
+ if (!options?.readonly) {
18352
+ db.pragma("journal_mode = WAL");
18353
+ }
18354
+ return new BetterSqliteAdapter(db);
18355
+ }
18356
+
18357
+ // ../../packages/node-runtime/src/database-manager.ts
17565
18358
  function createMigrationDeps(overrides) {
17566
18359
  return {
17567
18360
  tokenizeForFts,
@@ -17569,25 +18362,32 @@ function createMigrationDeps(overrides) {
17569
18362
  };
17570
18363
  }
17571
18364
  var DatabaseManager = class {
17572
- constructor(pathProvider, options) {
18365
+ constructor(pathProvider, options = {}) {
17573
18366
  this.pathProvider = pathProvider;
18367
+ if (!options.runtime && !options.allowMissingRuntimeForTests) {
18368
+ throw new Error("DatabaseManager runtime identity is required for data directory compatibility checks.");
18369
+ }
17574
18370
  this.nativeBinding = options?.nativeBinding;
17575
18371
  this.migrationDeps = createMigrationDeps(options?.migrationDeps);
18372
+ this.runtime = options?.runtime ?? null;
17576
18373
  }
17577
18374
  pathProvider;
17578
18375
  cache = /* @__PURE__ */ new Map();
17579
18376
  nativeBinding;
17580
18377
  migrationDeps;
18378
+ runtime;
17581
18379
  /**
17582
18380
  * Open a session DB (read-only by default, cached).
17583
18381
  */
17584
18382
  open(sessionId, options) {
18383
+ this.assertCompatible();
17585
18384
  if (this.cache.has(sessionId)) {
17586
18385
  return this.cache.get(sessionId);
17587
18386
  }
17588
18387
  const dbPath = this.getDbPath(sessionId);
17589
- if (!fs34.existsSync(dbPath)) return null;
18388
+ if (!fs35.existsSync(dbPath)) return null;
17590
18389
  this.migrateIfNeeded(dbPath);
18390
+ this.assertCompatible();
17591
18391
  const adapter = openBetterSqliteDatabase(dbPath, {
17592
18392
  readonly: options?.readonly ?? true,
17593
18393
  nativeBinding: this.nativeBinding
@@ -17601,7 +18401,9 @@ var DatabaseManager = class {
17601
18401
  nativeBinding: this.nativeBinding
17602
18402
  });
17603
18403
  try {
17604
- if (!isChatSessionDb(readonlyAdapter) || !needsMigration(readonlyAdapter, CURRENT_SCHEMA_VERSION)) return;
18404
+ if (!isChatSessionDb(readonlyAdapter)) return;
18405
+ this.raiseCompatibilityGateIfNeeded(getSchemaVersion(readonlyAdapter));
18406
+ if (!needsMigration(readonlyAdapter, CURRENT_SCHEMA_VERSION)) return;
17605
18407
  } finally {
17606
18408
  readonlyAdapter.close();
17607
18409
  }
@@ -17611,7 +18413,7 @@ var DatabaseManager = class {
17611
18413
  });
17612
18414
  try {
17613
18415
  const migrations = getChatDbMigrations(this.migrationDeps);
17614
- runMigrations(adapter, migrations);
18416
+ this.runMigrations(adapter, migrations);
17615
18417
  } finally {
17616
18418
  adapter.close();
17617
18419
  }
@@ -17621,6 +18423,7 @@ var DatabaseManager = class {
17621
18423
  * Falls back to `open(id, { readonly: false })` if migration is unnecessary.
17622
18424
  */
17623
18425
  openWritable(sessionId) {
18426
+ this.assertCompatible();
17624
18427
  const existing = this.cache.get(sessionId);
17625
18428
  if (existing && !existing.readonly) return existing;
17626
18429
  if (existing) {
@@ -17628,14 +18431,17 @@ var DatabaseManager = class {
17628
18431
  this.cache.delete(sessionId);
17629
18432
  }
17630
18433
  const dbPath = this.getDbPath(sessionId);
17631
- if (!fs34.existsSync(dbPath)) return null;
18434
+ if (!fs35.existsSync(dbPath)) return null;
17632
18435
  const adapter = openBetterSqliteDatabase(dbPath, {
17633
18436
  readonly: false,
17634
18437
  nativeBinding: this.nativeBinding
17635
18438
  });
17636
18439
  if (needsMigration(adapter, CURRENT_SCHEMA_VERSION)) {
17637
18440
  const migrations = getChatDbMigrations(this.migrationDeps);
17638
- runMigrations(adapter, migrations);
18441
+ this.runMigrations(adapter, migrations);
18442
+ this.assertCompatible();
18443
+ } else {
18444
+ this.raiseCompatibilityGateIfNeeded(getSchemaVersion(adapter));
17639
18445
  }
17640
18446
  this.cache.set(sessionId, adapter);
17641
18447
  return adapter;
@@ -17663,9 +18469,10 @@ var DatabaseManager = class {
17663
18469
  * 列举数据库目录下的所有聊天会话 ID
17664
18470
  */
17665
18471
  listSessionIds() {
18472
+ this.assertCompatible();
17666
18473
  const dbDir = this.pathProvider.getDatabaseDir();
17667
- if (!fs34.existsSync(dbDir)) return [];
17668
- return fs34.readdirSync(dbDir).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", "")).filter((id) => {
18474
+ if (!fs35.existsSync(dbDir)) return [];
18475
+ return fs35.readdirSync(dbDir).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", "")).filter((id) => {
17669
18476
  const db = this.open(id);
17670
18477
  if (!db) return false;
17671
18478
  return isChatSessionDb(db);
@@ -17675,9 +18482,70 @@ var DatabaseManager = class {
17675
18482
  * 获取数据库文件路径
17676
18483
  */
17677
18484
  getDbPath(sessionId) {
17678
- return path31.join(this.pathProvider.getDatabaseDir(), `${sessionId}.db`);
18485
+ return path32.join(this.pathProvider.getDatabaseDir(), `${sessionId}.db`);
18486
+ }
18487
+ openRawSessionDatabase(sessionId, options = {}) {
18488
+ this.assertCompatible();
18489
+ const dbPath = this.getDbPath(sessionId);
18490
+ if (!options.create && !fs35.existsSync(dbPath)) {
18491
+ throw new Error(`Session database not found: ${sessionId}`);
18492
+ }
18493
+ if (!options.readonly) {
18494
+ this.close(sessionId);
18495
+ fs35.mkdirSync(path32.dirname(dbPath), { recursive: true });
18496
+ }
18497
+ const adapter = openBetterSqliteDatabase(dbPath, {
18498
+ readonly: options.readonly ?? false,
18499
+ nativeBinding: this.nativeBinding
18500
+ });
18501
+ if (options.initializeChatTables) {
18502
+ adapter.exec(CHAT_DB_TABLES);
18503
+ }
18504
+ return adapter;
18505
+ }
18506
+ deleteSessionDatabaseFiles(sessionId) {
18507
+ this.close(sessionId);
18508
+ const dbPath = this.getDbPath(sessionId);
18509
+ for (const suffix of ["", "-wal", "-shm"]) {
18510
+ try {
18511
+ const filePath = dbPath + suffix;
18512
+ if (fs35.existsSync(filePath)) fs35.unlinkSync(filePath);
18513
+ } catch {
18514
+ }
18515
+ }
18516
+ }
18517
+ raiseCurrentChatDbCompatibilityGate() {
18518
+ if (!this.runtime) return;
18519
+ raiseChatDbCompatibilityGate(this.pathProvider, this.runtime);
18520
+ }
18521
+ assertCompatible() {
18522
+ if (!this.runtime) return;
18523
+ assertDataDirCompatible(this.pathProvider, this.runtime);
18524
+ }
18525
+ runMigrations(adapter, migrations) {
18526
+ const beforeVersion = getSchemaVersion(adapter);
18527
+ const migrated = runMigrations(adapter, migrations);
18528
+ if (!migrated || !this.runtime) return;
18529
+ const afterVersion = getSchemaVersion(adapter);
18530
+ if (shouldRaiseCompatibilityGate(beforeVersion, afterVersion)) {
18531
+ this.raiseCurrentChatDbCompatibilityGate();
18532
+ }
18533
+ }
18534
+ raiseCompatibilityGateIfNeeded(schemaVersion) {
18535
+ if (!this.runtime) return;
18536
+ if (shouldRepairCompatibilityGate(schemaVersion)) {
18537
+ this.raiseCurrentChatDbCompatibilityGate();
18538
+ }
17679
18539
  }
17680
18540
  };
18541
+ function shouldRaiseCompatibilityGate(beforeVersion, afterVersion) {
18542
+ return CHAT_DB_COMPATIBILITY_RAISES.some(
18543
+ (compatibilityRaise) => beforeVersion < compatibilityRaise.migrationVersion && afterVersion >= compatibilityRaise.migrationVersion
18544
+ );
18545
+ }
18546
+ function shouldRepairCompatibilityGate(schemaVersion) {
18547
+ return CHAT_DB_COMPATIBILITY_RAISES.some((compatibilityRaise) => schemaVersion >= compatibilityRaise.migrationVersion);
18548
+ }
17681
18549
 
17682
18550
  // ../../packages/node-runtime/src/jieba-nlp-provider.ts
17683
18551
  function createJiebaNlpProvider(dictType = "default") {
@@ -17723,7 +18591,7 @@ function exportFilterResultToMarkdown(params, deps, writer) {
17723
18591
  } else {
17724
18592
  writer.write(`- Mode: session filter
17725
18593
  `);
17726
- writer.write(`- Selected sessions: ${params.chatSessionIds?.length || 0}
18594
+ writer.write(`- Selected sessions: ${params.segmentIds?.length || 0}
17727
18595
  `);
17728
18596
  }
17729
18597
  writer.write("\n");
@@ -17881,7 +18749,7 @@ function exportConditionMode(db, params, writer, progress) {
17881
18749
  return { totalMessages, blockIndex };
17882
18750
  }
17883
18751
  function exportSessionMode(db, params, writer, progress) {
17884
- if (!params.chatSessionIds || params.chatSessionIds.length === 0) {
18752
+ if (!params.segmentIds || params.segmentIds.length === 0) {
17885
18753
  writer.write(`## Statistics
17886
18754
 
17887
18755
  - No sessions selected
@@ -17892,17 +18760,17 @@ function exportSessionMode(db, params, writer, progress) {
17892
18760
  progress?.({
17893
18761
  stage: "preparing",
17894
18762
  currentBlock: 0,
17895
- totalBlocks: params.chatSessionIds.length,
18763
+ totalBlocks: params.segmentIds.length,
17896
18764
  percentage: 10,
17897
- message: `Preparing to export ${params.chatSessionIds.length} sessions...`
18765
+ message: `Preparing to export ${params.segmentIds.length} sessions...`
17898
18766
  });
17899
18767
  const sessionsSql = `
17900
18768
  SELECT id, start_ts as startTs, end_ts as endTs
17901
- FROM chat_session
17902
- WHERE id IN (${params.chatSessionIds.map(() => "?").join(",")})
18769
+ FROM segment
18770
+ WHERE id IN (${params.segmentIds.map(() => "?").join(",")})
17903
18771
  ORDER BY start_ts ASC
17904
18772
  `;
17905
- const sessions = db.prepare(sessionsSql).all(...params.chatSessionIds);
18773
+ const sessions = db.prepare(sessionsSql).all(...params.segmentIds);
17906
18774
  const totalBlocks = sessions.length;
17907
18775
  writer.write(`## Statistics
17908
18776
 
@@ -17920,7 +18788,7 @@ function exportSessionMode(db, params, writer, progress) {
17920
18788
  FROM message_context mc
17921
18789
  JOIN message msg ON msg.id = mc.message_id
17922
18790
  JOIN member m ON msg.sender_id = m.id
17923
- WHERE mc.session_id = ?
18791
+ WHERE mc.segment_id = ?
17924
18792
  ORDER BY msg.ts ASC
17925
18793
  `;
17926
18794
  let totalMessages = 0;
@@ -17953,8 +18821,8 @@ function exportSessionMode(db, params, writer, progress) {
17953
18821
  }
17954
18822
 
17955
18823
  // ../../packages/node-runtime/src/preferences.ts
17956
- import * as fs35 from "fs";
17957
- import * as path32 from "path";
18824
+ import * as fs36 from "fs";
18825
+ import * as path33 from "path";
17958
18826
  var DEFAULTS = {
17959
18827
  pinnedSessionIds: [],
17960
18828
  aiPreprocessConfig: {
@@ -17996,13 +18864,13 @@ var PreferencesManager = class {
17996
18864
  filePath;
17997
18865
  cache = null;
17998
18866
  constructor(systemDir) {
17999
- this.filePath = path32.join(systemDir, "preferences.json");
18867
+ this.filePath = path33.join(systemDir, "preferences.json");
18000
18868
  }
18001
18869
  load() {
18002
18870
  if (this.cache) return this.cache;
18003
18871
  try {
18004
- if (fs35.existsSync(this.filePath)) {
18005
- const raw = fs35.readFileSync(this.filePath, "utf-8");
18872
+ if (fs36.existsSync(this.filePath)) {
18873
+ const raw = fs36.readFileSync(this.filePath, "utf-8");
18006
18874
  const parsed = JSON.parse(raw);
18007
18875
  const migrated = this.migrateLegacyDesensitizeRules(parsed, raw);
18008
18876
  this.cache = this.mergeDefaults(migrated.preferences);
@@ -18114,21 +18982,21 @@ var PreferencesManager = class {
18114
18982
  };
18115
18983
  }
18116
18984
  backupLegacyPreferences(rawContent) {
18117
- const backupDir = path32.join(path32.dirname(this.filePath), "backups");
18118
- if (!fs35.existsSync(backupDir)) {
18119
- fs35.mkdirSync(backupDir, { recursive: true });
18985
+ const backupDir = path33.join(path33.dirname(this.filePath), "backups");
18986
+ if (!fs36.existsSync(backupDir)) {
18987
+ fs36.mkdirSync(backupDir, { recursive: true });
18120
18988
  }
18121
18989
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18122
- const backupPath = path32.join(backupDir, `preferences-pre-desensitize-groups-${timestamp}.json`);
18123
- fs35.writeFileSync(backupPath, rawContent, "utf-8");
18990
+ const backupPath = path33.join(backupDir, `preferences-pre-desensitize-groups-${timestamp}.json`);
18991
+ fs36.writeFileSync(backupPath, rawContent, "utf-8");
18124
18992
  console.info(`[Preferences] Backed up legacy desensitize rules to ${backupPath}`);
18125
18993
  }
18126
18994
  writePreferences(preferences) {
18127
- const dir = path32.dirname(this.filePath);
18128
- if (!fs35.existsSync(dir)) {
18129
- fs35.mkdirSync(dir, { recursive: true });
18995
+ const dir = path33.dirname(this.filePath);
18996
+ if (!fs36.existsSync(dir)) {
18997
+ fs36.mkdirSync(dir, { recursive: true });
18130
18998
  }
18131
- fs35.writeFileSync(this.filePath, JSON.stringify(preferences, null, 2), "utf-8");
18999
+ fs36.writeFileSync(this.filePath, JSON.stringify(preferences, null, 2), "utf-8");
18132
19000
  }
18133
19001
  /**
18134
19002
  * Deep merge: arrays are replaced (not concatenated), objects are merged.
@@ -18248,8 +19116,8 @@ function* serializeChatLabToJsonl(data) {
18248
19116
  }
18249
19117
 
18250
19118
  // ../../packages/node-runtime/src/merger/temp-db.ts
18251
- import * as fs36 from "fs";
18252
- import * as path33 from "path";
19119
+ import * as fs37 from "fs";
19120
+ import * as path34 from "path";
18253
19121
  var TEMP_DB_SCHEMA = `
18254
19122
  CREATE TABLE IF NOT EXISTS meta (
18255
19123
  name TEXT NOT NULL,
@@ -18494,21 +19362,21 @@ function exportSessionToJson(db) {
18494
19362
  }
18495
19363
  function deleteTempDatabase(dbPath) {
18496
19364
  try {
18497
- if (fs36.existsSync(dbPath)) fs36.unlinkSync(dbPath);
19365
+ if (fs37.existsSync(dbPath)) fs37.unlinkSync(dbPath);
18498
19366
  const walPath = dbPath + "-wal";
18499
19367
  const shmPath = dbPath + "-shm";
18500
- if (fs36.existsSync(walPath)) fs36.unlinkSync(walPath);
18501
- if (fs36.existsSync(shmPath)) fs36.unlinkSync(shmPath);
19368
+ if (fs37.existsSync(walPath)) fs37.unlinkSync(walPath);
19369
+ if (fs37.existsSync(shmPath)) fs37.unlinkSync(shmPath);
18502
19370
  } catch {
18503
19371
  }
18504
19372
  }
18505
19373
  function cleanupTempDatabases(tempDir) {
18506
19374
  try {
18507
- if (!fs36.existsSync(tempDir)) return;
18508
- const files = fs36.readdirSync(tempDir);
19375
+ if (!fs37.existsSync(tempDir)) return;
19376
+ const files = fs37.readdirSync(tempDir);
18509
19377
  for (const file of files) {
18510
19378
  if (file.startsWith("merge_") && file.endsWith(".db")) {
18511
- deleteTempDatabase(path33.join(tempDir, file));
19379
+ deleteTempDatabase(path34.join(tempDir, file));
18512
19380
  }
18513
19381
  }
18514
19382
  } catch {
@@ -18669,16 +19537,16 @@ __export(summary_service_exports, {
18669
19537
  function buildSummaryDeps(db, llmConfig, deps) {
18670
19538
  const piModel = deps.buildPiModel(llmConfig);
18671
19539
  return {
18672
- loadMessages(chatSessionId, limit = 500) {
18673
- const data = getSessionMessages(db, chatSessionId, limit);
19540
+ loadMessages(segmentId, limit = 500) {
19541
+ const data = getSegmentMessages(db, segmentId, limit);
18674
19542
  if (!data) return null;
18675
19543
  return data.messages.map((m) => ({ senderName: m.senderName, content: m.content }));
18676
19544
  },
18677
- saveSummary(chatSessionId, summary) {
18678
- saveChatSessionSummary(db, chatSessionId, summary);
19545
+ saveSummary(segmentId, summary) {
19546
+ saveSegmentSummary(db, segmentId, summary);
18679
19547
  },
18680
- getSummary(chatSessionId) {
18681
- return getChatSessionSummary(db, chatSessionId);
19548
+ getSummary(segmentId) {
19549
+ return getSegmentSummary(db, segmentId);
18682
19550
  },
18683
19551
  async llmComplete(systemPrompt, userPrompt, options) {
18684
19552
  const result = await completeSimple4(
@@ -18694,14 +19562,14 @@ function buildSummaryDeps(db, llmConfig, deps) {
18694
19562
  t: (key) => key
18695
19563
  };
18696
19564
  }
18697
- async function generateSummary(adapter, sessionId, chatSessionId, serviceDeps, options) {
19565
+ async function generateSummary(adapter, sessionId, segmentId, serviceDeps, options) {
18698
19566
  const llmConfig = serviceDeps.getLlmConfig();
18699
19567
  if (!llmConfig) {
18700
19568
  return { success: false, error: "No LLM configuration available" };
18701
19569
  }
18702
19570
  const db = adapter.ensureWritable(sessionId);
18703
19571
  const deps = buildSummaryDeps(db, llmConfig, serviceDeps);
18704
- return generateSessionSummary(deps, chatSessionId, options);
19572
+ return generateSessionSummary(deps, segmentId, options);
18705
19573
  }
18706
19574
  async function generateAllSummaries(adapter, sessionId, serviceDeps, options) {
18707
19575
  const llmConfig = serviceDeps.getLlmConfig();
@@ -18720,17 +19588,17 @@ async function generateAllSummaries(adapter, sessionId, serviceDeps, options) {
18720
19588
  }
18721
19589
  return { success, failed, total: chatSessions.length };
18722
19590
  }
18723
- function checkCanGenerate(adapter, sessionId, chatSessionIds) {
19591
+ function checkCanGenerate(adapter, sessionId, segmentIds) {
18724
19592
  const db = adapter.ensureReadonly(sessionId);
18725
19593
  const deps = {
18726
- loadMessages(chatSessionId, limit = 500) {
18727
- const data = getSessionMessages(db, chatSessionId, limit);
19594
+ loadMessages(segmentId, limit = 500) {
19595
+ const data = getSegmentMessages(db, segmentId, limit);
18728
19596
  if (!data) return null;
18729
19597
  return data.messages.map((m) => ({ senderName: m.senderName, content: m.content }));
18730
19598
  },
18731
19599
  t: (key) => key
18732
19600
  };
18733
- const resultMap = checkSessionsCanGenerateSummary(deps, chatSessionIds);
19601
+ const resultMap = checkSessionsCanGenerateSummary(deps, segmentIds);
18734
19602
  const result = {};
18735
19603
  for (const [id, info] of resultMap) {
18736
19604
  result[id] = info;
@@ -18764,7 +19632,7 @@ function exportMarkdown(adapter, params) {
18764
19632
  }
18765
19633
 
18766
19634
  // ../../packages/node-runtime/src/services/adapters/database-manager-adapter.ts
18767
- import * as fs37 from "fs";
19635
+ import * as fs38 from "fs";
18768
19636
  function createDatabaseManagerAdapter(dbManager) {
18769
19637
  return {
18770
19638
  listSessionIds: () => dbManager.listSessionIds(),
@@ -18775,8 +19643,8 @@ function createDatabaseManagerAdapter(dbManager) {
18775
19643
  deleteSessionFile(sessionId) {
18776
19644
  dbManager.close(sessionId);
18777
19645
  const dbPath = dbManager.getDbPath(sessionId);
18778
- if (!fs37.existsSync(dbPath)) return false;
18779
- fs37.unlinkSync(dbPath);
19646
+ if (!fs38.existsSync(dbPath)) return false;
19647
+ fs38.unlinkSync(dbPath);
18780
19648
  return true;
18781
19649
  },
18782
19650
  ensureReadonly(sessionId) {
@@ -18797,8 +19665,8 @@ function createDatabaseManagerAdapter(dbManager) {
18797
19665
  }
18798
19666
 
18799
19667
  // ../../packages/node-runtime/src/services/merge-cache.ts
18800
- import * as fs38 from "fs";
18801
- import * as path34 from "path";
19668
+ import * as fs39 from "fs";
19669
+ import * as path35 from "path";
18802
19670
  import * as crypto from "crypto";
18803
19671
  var MergeSessionCache = class {
18804
19672
  cache = /* @__PURE__ */ new Map();
@@ -18807,15 +19675,15 @@ var MergeSessionCache = class {
18807
19675
  constructor(pathProvider, options) {
18808
19676
  this.tempDir = pathProvider.getTempDir();
18809
19677
  this.nativeBinding = options?.nativeBinding;
18810
- if (!fs38.existsSync(this.tempDir)) {
18811
- fs38.mkdirSync(this.tempDir, { recursive: true });
19678
+ if (!fs39.existsSync(this.tempDir)) {
19679
+ fs39.mkdirSync(this.tempDir, { recursive: true });
18812
19680
  }
18813
19681
  }
18814
19682
  generateTempDbPath(filename) {
18815
19683
  const ts = Date.now();
18816
19684
  const rand = Math.random().toString(36).slice(2, 8);
18817
19685
  const safeName = filename.replace(/[/\\?%*:|"<>]/g, "_").slice(0, 40);
18818
- return path34.join(this.tempDir, `merge_${safeName}_${ts}_${rand}.db`);
19686
+ return path35.join(this.tempDir, `merge_${safeName}_${ts}_${rand}.db`);
18819
19687
  }
18820
19688
  /** Store a parsed file's temp DB and return a handle for subsequent requests. */
18821
19689
  store(filename, tempDbPath) {
@@ -18826,7 +19694,7 @@ var MergeSessionCache = class {
18826
19694
  /** Open a TempDbReader for a given handle. Caller must close the reader. */
18827
19695
  openReader(handle) {
18828
19696
  const entry = this.cache.get(handle);
18829
- if (!entry || !fs38.existsSync(entry.tempDbPath)) return null;
19697
+ if (!entry || !fs39.existsSync(entry.tempDbPath)) return null;
18830
19698
  const adapter = openBetterSqliteDatabase(entry.tempDbPath, {
18831
19699
  readonly: true,
18832
19700
  nativeBinding: this.nativeBinding
@@ -18879,7 +19747,6 @@ export {
18879
19747
  writeAuthProfile,
18880
19748
  MigrationRunner,
18881
19749
  ALL_MIGRATIONS,
18882
- openBetterSqliteDatabase,
18883
19750
  writeParseResultToDb,
18884
19751
  getTimeRange,
18885
19752
  getAvailableYears,
@@ -18896,9 +19763,9 @@ export {
18896
19763
  getSessionOverview,
18897
19764
  getDatabaseSchema,
18898
19765
  getChatOverview,
18899
- searchSessions,
18900
- getSessionMessages,
18901
- getSessionSummaries,
19766
+ searchSegments,
19767
+ getSegmentMessages,
19768
+ getSegmentSummaries,
18902
19769
  generateSessionIndex,
18903
19770
  generateIncrementalSessionIndex,
18904
19771
  queryMessages,
@@ -18925,7 +19792,9 @@ export {
18925
19792
  getRelationshipStats,
18926
19793
  getLanguagePreferenceAnalysis,
18927
19794
  isNewerStableVersion,
19795
+ buildChartPayload,
18928
19796
  POS_TAG_DEFINITIONS,
19797
+ normalizeBuiltinToolNames,
18929
19798
  BUILTIN_TOOL_CATALOG,
18930
19799
  BUILTIN_PROVIDERS,
18931
19800
  BUILTIN_MODELS,
@@ -18933,7 +19802,6 @@ export {
18933
19802
  stripAvatarFields,
18934
19803
  generateMessageKey,
18935
19804
  buildMemberIdMap,
18936
- CHAT_DB_TABLES,
18937
19805
  CHAT_DB_SCHEMA,
18938
19806
  FTS_TABLE_SCHEMA,
18939
19807
  detectFormat,
@@ -18960,17 +19828,26 @@ export {
18960
19828
  formatAIError,
18961
19829
  parseAssistantFile,
18962
19830
  AssistantManager,
18963
- AIConversationManager,
19831
+ AIChatManager,
18964
19832
  initTokenizer,
18965
19833
  countMessagesTokens,
18966
- SkillManager,
18967
19834
  SkillManagerCore,
19835
+ SkillManager,
18968
19836
  createActivateSkillTool,
19837
+ CHART_CAPABILITY_CORE_TOOLS,
19838
+ getAllowedBuiltinToolsForChartAutoSkill,
19839
+ resolveChartRuntimeForRequest,
19840
+ getChartCapabilitySkill,
19841
+ buildSkillMenuWithBuiltinChart,
19842
+ getSkillConfigWithBuiltinChart,
19843
+ createChartSchemaGateState,
19844
+ wrapWithChartSchemaGate,
18969
19845
  checkAndCompress,
18970
19846
  createCompressionLlmAdapter,
18971
19847
  getDefaultRulesForLocale,
18972
19848
  mergeRulesForLocale,
18973
19849
  applyPreprocessingPipeline,
19850
+ DEFAULT_MAX_TOOL_ROUNDS,
18974
19851
  runAgentCore,
18975
19852
  AgentEventHandler,
18976
19853
  buildSystemPrompt,
@@ -18989,6 +19866,8 @@ export {
18989
19866
  applyPendingNodeDataDirMigrationIfNeeded,
18990
19867
  NodePathProvider,
18991
19868
  getDefaultNodeUserDataDir,
19869
+ DataDirCompatibilityError,
19870
+ assertDataDirCompatible,
18992
19871
  DatabaseManager,
18993
19872
  createJiebaNlpProvider,
18994
19873
  PreferencesManager,
@@ -19005,4 +19884,4 @@ export {
19005
19884
  createDatabaseManagerAdapter,
19006
19885
  MergeSessionCache
19007
19886
  };
19008
- //# sourceMappingURL=chunk-N5DGZQB4.mjs.map
19887
+ //# sourceMappingURL=chunk-7VG6QGQ2.mjs.map