chatlab-cli 0.25.0 → 0.26.0

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 (70) hide show
  1. package/dist/chat-command-ZP4WJVWC.mjs +304 -0
  2. package/dist/chat-command-ZP4WJVWC.mjs.map +1 -0
  3. package/dist/{chunk-CIBTUWAE.mjs → chunk-6G7AIF7I.mjs} +38 -8
  4. package/dist/chunk-6G7AIF7I.mjs.map +1 -0
  5. package/dist/chunk-ILGSSXOG.mjs +549 -0
  6. package/dist/chunk-ILGSSXOG.mjs.map +1 -0
  7. package/dist/{chunk-E65ABM3C.mjs → chunk-IRZRIHIT.mjs} +100 -63
  8. package/dist/chunk-IRZRIHIT.mjs.map +1 -0
  9. package/dist/{chunk-4OMV4VNH.mjs → chunk-QY67NN65.mjs} +1900 -729
  10. package/dist/chunk-QY67NN65.mjs.map +1 -0
  11. package/dist/{chunk-FSHVDWI2.mjs → chunk-SJIOPSMM.mjs} +116 -569
  12. package/dist/chunk-SJIOPSMM.mjs.map +1 -0
  13. package/dist/{chunk-GNT5G74Z.mjs → chunk-TC74HESZ.mjs} +59 -33
  14. package/dist/chunk-TC74HESZ.mjs.map +1 -0
  15. package/dist/chunk-UB4XBM4R.mjs +40 -0
  16. package/dist/chunk-UB4XBM4R.mjs.map +1 -0
  17. package/dist/{chunk-TCPXAX3F.mjs → chunk-WXC6RZCX.mjs} +17 -25
  18. package/dist/chunk-WXC6RZCX.mjs.map +1 -0
  19. package/dist/cli.mjs +3 -2
  20. package/dist/{http-PMNZ42EN.mjs → http-J3IOFFIA.mjs} +8 -6
  21. package/dist/{import-OP7R4CYY.mjs → import-POYG5LOX.mjs} +4 -4
  22. package/dist/index.mjs +10 -8
  23. package/dist/{mcp-ABXNFUJO.mjs → mcp-7HH7UMXT.mjs} +11 -6
  24. package/dist/mcp-7HH7UMXT.mjs.map +1 -0
  25. package/dist/{stream-import-L6RJHH4L.mjs → stream-import-O3H3FGV2.mjs} +3 -3
  26. package/dist-web/assets/{DebugToolsPanel-DaSNc1o-.css → DebugToolsPanel-CrqULdXp.css} +1 -1
  27. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BTJxEma5.js +182 -0
  28. package/dist-web/assets/{EChartWordcloud-CzhfrEF_.js → EChartWordcloud-B_YisOW5.js} +1 -1
  29. package/dist-web/assets/{MessageExportModal-BAJKWX4y.js → MessageExportModal-C7NgEL8T.js} +1 -1
  30. package/dist-web/assets/{electron-CZt36wW5.js → electron-B3DFm1cC.js} +1 -1
  31. package/dist-web/assets/electron-BMM4Nfty.js +1 -0
  32. package/dist-web/assets/{fetch-qoGEVjNg.js → fetch-ArMjdMcy.js} +1 -1
  33. package/dist-web/assets/fetch-BFtuJaa1.js +1 -0
  34. package/dist-web/assets/{fetch-yWwp067l.js → fetch-BGfaAqO9.js} +1 -1
  35. package/dist-web/assets/{fetch-DD1EGRKV.js → fetch-BaJDB31c.js} +1 -1
  36. package/dist-web/assets/{fetch-JzVEwamn.js → fetch-BjDJgdN0.js} +1 -1
  37. package/dist-web/assets/{fetch-GMkMBzBS.js → fetch-C5CLcpQW.js} +1 -1
  38. package/dist-web/assets/{fetch-DXfZysDf.js → fetch-DQZvdHnG.js} +1 -1
  39. package/dist-web/assets/{fetch-D4p9vj1A.js → fetch-Dhfbt1BQ.js} +3 -3
  40. package/dist-web/assets/fetch-DnEP0uyF.js +1 -0
  41. package/dist-web/assets/{fetch-CgBIWyAo.js → fetch-SYrb-sZe.js} +1 -1
  42. package/dist-web/assets/index-CWfkrx37.js +173 -0
  43. package/dist-web/assets/index-D3N4NDFx.css +1 -0
  44. package/dist-web/assets/{index-O0PG26_v.js → index-D7_ch4zf.js} +1 -1
  45. package/dist-web/assets/{index-BrS_VrIY.js → index-DK9LEEzT.js} +1 -1
  46. package/dist-web/assets/index-DqDa_on9.js +9 -0
  47. package/dist-web/assets/{index-Cvlw0RLu.js → index-DrN1G9bl.js} +2 -2
  48. package/dist-web/assets/{index-DvWI6Yqa.js → index-MdYeovYo.js} +4 -4
  49. package/dist-web/assets/{vendor-nuxt-ui-C_8CsH6e.js → vendor-nuxt-ui-Zy8txNdC.js} +1 -1
  50. package/dist-web/assets/{vendor-reka-ui-CIMXwvEp.js → vendor-reka-ui-mMvma1EP.js} +1 -1
  51. package/dist-web/assets/{web-4rtrAcLu.js → web-zEXsvoZW.js} +1 -1
  52. package/dist-web/index.html +4 -4
  53. package/package.json +1 -1
  54. package/dist/chunk-4OMV4VNH.mjs.map +0 -1
  55. package/dist/chunk-CIBTUWAE.mjs.map +0 -1
  56. package/dist/chunk-E65ABM3C.mjs.map +0 -1
  57. package/dist/chunk-FSHVDWI2.mjs.map +0 -1
  58. package/dist/chunk-GNT5G74Z.mjs.map +0 -1
  59. package/dist/chunk-TCPXAX3F.mjs.map +0 -1
  60. package/dist/mcp-ABXNFUJO.mjs.map +0 -1
  61. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BUwpIerl.js +0 -161
  62. package/dist-web/assets/electron-CwERrTWp.js +0 -1
  63. package/dist-web/assets/fetch-BxFAkm7O.js +0 -1
  64. package/dist-web/assets/fetch-DO8lty7_.js +0 -1
  65. package/dist-web/assets/index-C1t-kzfo.css +0 -1
  66. package/dist-web/assets/index-DABpUm3J.js +0 -9
  67. package/dist-web/assets/index-OsjX5hXw.js +0 -173
  68. /package/dist/{http-PMNZ42EN.mjs.map → http-J3IOFFIA.mjs.map} +0 -0
  69. /package/dist/{import-OP7R4CYY.mjs.map → import-POYG5LOX.mjs.map} +0 -0
  70. /package/dist/{stream-import-L6RJHH4L.mjs.map → stream-import-O3H3FGV2.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) {
@@ -2215,6 +2158,7 @@ function getChatOverview(db, topN = 10) {
2215
2158
  if (!meta) return null;
2216
2159
  const overview = getSessionOverview(db);
2217
2160
  const members = getMemberActivity(db);
2161
+ const summaryCount = getSummaryCount(db);
2218
2162
  const topMembers = members.slice(0, topN).map((m) => ({
2219
2163
  id: m.memberId,
2220
2164
  name: m.name,
@@ -2228,14 +2172,15 @@ function getChatOverview(db, topN = 10) {
2228
2172
  totalMembers: overview.totalMembers,
2229
2173
  firstMessageTs: overview.firstMessageTs,
2230
2174
  lastMessageTs: overview.lastMessageTs,
2231
- topMembers
2175
+ topMembers,
2176
+ summaryCount
2232
2177
  };
2233
2178
  }
2234
- function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5, ftsMatchExpression) {
2235
- if (!hasTable(db, "chat_session")) return [];
2179
+ function searchSegments(db, keywords, timeFilter, limit = 20, previewCount = 5, ftsMatchExpression) {
2180
+ if (!hasTable(db, "segment")) return [];
2236
2181
  let sessionSql = `
2237
2182
  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
2183
+ FROM segment cs WHERE 1=1
2239
2184
  `;
2240
2185
  const params = [];
2241
2186
  if (timeFilter?.startTs !== void 0) {
@@ -2250,7 +2195,7 @@ function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5,
2250
2195
  if (ftsMatchExpression) {
2251
2196
  sessionSql += `
2252
2197
  AND cs.id IN (
2253
- SELECT DISTINCT mc.session_id FROM message_context mc
2198
+ SELECT DISTINCT mc.segment_id FROM message_context mc
2254
2199
  WHERE mc.message_id IN (SELECT rowid FROM message_fts WHERE content MATCH ?)
2255
2200
  )
2256
2201
  `;
@@ -2259,7 +2204,7 @@ function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5,
2259
2204
  const keywordConditions = keywords.map(() => "m.content LIKE ?").join(" OR ");
2260
2205
  sessionSql += `
2261
2206
  AND cs.id IN (
2262
- SELECT DISTINCT mc.session_id FROM message_context mc
2207
+ SELECT DISTINCT mc.segment_id FROM message_context mc
2263
2208
  JOIN message m ON m.id = mc.message_id
2264
2209
  WHERE (${keywordConditions})
2265
2210
  )
@@ -2280,7 +2225,7 @@ function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5,
2280
2225
  FROM message_context mc
2281
2226
  JOIN message m ON m.id = mc.message_id
2282
2227
  JOIN member mb ON mb.id = m.sender_id
2283
- WHERE mc.session_id = ? ORDER BY m.ts ASC LIMIT ?
2228
+ WHERE mc.segment_id = ? ORDER BY m.ts ASC LIMIT ?
2284
2229
  `;
2285
2230
  return sessions.map((session) => {
2286
2231
  const previewMessages = db.prepare(previewSql).all(session.id, previewCount);
@@ -2294,12 +2239,12 @@ function searchSessions(db, keywords, timeFilter, limit = 20, previewCount = 5,
2294
2239
  };
2295
2240
  });
2296
2241
  }
2297
- function getSessionMessages(db, chatSessionId, limit = 500) {
2298
- if (!hasTable(db, "chat_session")) return null;
2242
+ function getSegmentMessages(db, segmentId, limit = 500) {
2243
+ if (!hasTable(db, "segment")) return null;
2299
2244
  const session = db.prepare(
2300
2245
  `SELECT id, start_ts as startTs, end_ts as endTs, message_count as messageCount
2301
- FROM chat_session WHERE id = ?`
2302
- ).get(chatSessionId);
2246
+ FROM segment WHERE id = ?`
2247
+ ).get(segmentId);
2303
2248
  if (!session) return null;
2304
2249
  const messages = db.prepare(
2305
2250
  `SELECT m.id, mb.id as senderId,
@@ -2309,14 +2254,14 @@ function getSessionMessages(db, chatSessionId, limit = 500) {
2309
2254
  FROM message_context mc
2310
2255
  JOIN message m ON m.id = mc.message_id
2311
2256
  JOIN member mb ON mb.id = m.sender_id
2312
- WHERE mc.session_id = ? ORDER BY m.ts ASC LIMIT ?`
2313
- ).all(chatSessionId, limit);
2257
+ WHERE mc.segment_id = ? ORDER BY m.ts ASC LIMIT ?`
2258
+ ).all(segmentId, limit);
2314
2259
  const participantsSet = /* @__PURE__ */ new Set();
2315
2260
  for (const msg of messages) {
2316
2261
  participantsSet.add(msg.senderName);
2317
2262
  }
2318
2263
  return {
2319
- sessionId: session.id,
2264
+ segmentId: session.id,
2320
2265
  startTs: session.startTs,
2321
2266
  endTs: session.endTs,
2322
2267
  messageCount: session.messageCount,
@@ -2325,13 +2270,13 @@ function getSessionMessages(db, chatSessionId, limit = 500) {
2325
2270
  messages
2326
2271
  };
2327
2272
  }
2328
- function getSessionSummaries(db, options) {
2329
- if (!hasTable(db, "chat_session")) return [];
2273
+ function getSegmentSummaries(db, options) {
2274
+ if (!hasTable(db, "segment")) return [];
2330
2275
  const { limit = 50, timeFilter } = options ?? {};
2331
2276
  let sql = `
2332
2277
  SELECT cs.id, cs.start_ts as startTs, cs.end_ts as endTs,
2333
2278
  cs.message_count as messageCount, cs.summary
2334
- FROM chat_session cs
2279
+ FROM segment cs
2335
2280
  WHERE cs.summary IS NOT NULL AND cs.summary != ''
2336
2281
  `;
2337
2282
  const params = [];
@@ -2351,7 +2296,7 @@ function getSessionSummaries(db, options) {
2351
2296
  FROM message_context mc
2352
2297
  JOIN message m ON m.id = mc.message_id
2353
2298
  JOIN member mb ON mb.id = m.sender_id
2354
- WHERE mc.session_id = ? LIMIT 10
2299
+ WHERE mc.segment_id = ? LIMIT 10
2355
2300
  `;
2356
2301
  return sessions.map((session) => {
2357
2302
  const participants = db.prepare(participantsSql).all(session.id);
@@ -2368,9 +2313,9 @@ function getSessionSummaries(db, options) {
2368
2313
  var DEFAULT_SESSION_GAP_THRESHOLD = 1800;
2369
2314
  function getSessionIndexStats(db) {
2370
2315
  let sessionCount = 0;
2371
- if (hasTable(db, "chat_session")) {
2316
+ if (hasTable(db, "segment")) {
2372
2317
  try {
2373
- const row = db.prepare("SELECT COUNT(*) as count FROM chat_session").get();
2318
+ const row = db.prepare("SELECT COUNT(*) as count FROM segment").get();
2374
2319
  sessionCount = row?.count ?? 0;
2375
2320
  } catch {
2376
2321
  }
@@ -2386,7 +2331,7 @@ function getSessionIndexStats(db) {
2386
2331
  return { sessionCount, hasIndex: sessionCount > 0, gapThreshold };
2387
2332
  }
2388
2333
  function getChatSessionList(db) {
2389
- if (!hasTable(db, "chat_session")) return [];
2334
+ if (!hasTable(db, "segment")) return [];
2390
2335
  try {
2391
2336
  return db.prepare(
2392
2337
  `SELECT
@@ -2396,24 +2341,24 @@ function getChatSessionList(db) {
2396
2341
  cs.message_count as messageCount,
2397
2342
  cs.summary,
2398
2343
  (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
2344
+ WHERE mc.segment_id = cs.id ORDER BY mc.message_id LIMIT 1) as firstMessageId
2345
+ FROM segment cs
2401
2346
  ORDER BY cs.start_ts ASC`
2402
2347
  ).all();
2403
2348
  } catch {
2404
2349
  return [];
2405
2350
  }
2406
2351
  }
2407
- function getChatSessionSummary(db, chatSessionId) {
2352
+ function getSegmentSummary(db, segmentId) {
2408
2353
  try {
2409
- const row = db.prepare("SELECT summary FROM chat_session WHERE id = ?").get(chatSessionId);
2354
+ const row = db.prepare("SELECT summary FROM segment WHERE id = ?").get(segmentId);
2410
2355
  return row?.summary ?? null;
2411
2356
  } catch {
2412
2357
  return null;
2413
2358
  }
2414
2359
  }
2415
- function saveChatSessionSummary(db, chatSessionId, summary) {
2416
- db.prepare("UPDATE chat_session SET summary = ? WHERE id = ?").run(summary, chatSessionId);
2360
+ function saveSegmentSummary(db, segmentId, summary) {
2361
+ db.prepare("UPDATE segment SET summary = ? WHERE id = ?").run(summary, segmentId);
2417
2362
  }
2418
2363
  function updateSessionOwnerId(db, ownerId) {
2419
2364
  db.prepare("UPDATE meta SET owner_id = ?").run(ownerId);
@@ -2423,7 +2368,7 @@ function renameSession(db, newName) {
2423
2368
  }
2424
2369
  function clearSessionIndex(db) {
2425
2370
  db.exec("DELETE FROM message_context");
2426
- db.exec("DELETE FROM chat_session");
2371
+ db.exec("DELETE FROM segment");
2427
2372
  }
2428
2373
  function generateSessionIndex(db, gapThreshold = DEFAULT_SESSION_GAP_THRESHOLD, onProgress) {
2429
2374
  const countRow = db.prepare("SELECT COUNT(*) as count FROM message").get();
@@ -2457,9 +2402,9 @@ function generateSessionIndex(db, gapThreshold = DEFAULT_SESSION_GAP_THRESHOLD,
2457
2402
  }
2458
2403
  }
2459
2404
  const insertSession = db.prepare(
2460
- "INSERT INTO chat_session (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)"
2405
+ "INSERT INTO segment (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)"
2461
2406
  );
2462
- const insertContext = db.prepare("INSERT INTO message_context (message_id, session_id, topic_id) VALUES (?, ?, NULL)");
2407
+ const insertContext = db.prepare("INSERT INTO message_context (message_id, segment_id, topic_id) VALUES (?, ?, NULL)");
2463
2408
  return db.transaction(() => {
2464
2409
  let processed = 0;
2465
2410
  const total = sessionMap.size;
@@ -2485,13 +2430,13 @@ function generateIncrementalSessionIndex(db, gapThreshold = DEFAULT_SESSION_GAP_
2485
2430
  const allMessages = db.prepare("SELECT id, ts FROM message ORDER BY ts, id").all();
2486
2431
  const newMessages = allMessages.filter((m) => !indexedIds.has(m.id));
2487
2432
  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;
2433
+ const lastSession = hasTable(db, "segment") ? db.prepare("SELECT id, end_ts FROM segment ORDER BY end_ts DESC LIMIT 1").get() : void 0;
2489
2434
  newMessages.sort((a, b) => a.ts - b.ts || a.id - b.id);
2490
2435
  const insertSession = db.prepare(
2491
- "INSERT INTO chat_session (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)"
2436
+ "INSERT INTO segment (start_ts, end_ts, message_count, is_manual, summary) VALUES (?, ?, ?, 0, NULL)"
2492
2437
  );
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 = ?");
2438
+ const insertContext = db.prepare("INSERT INTO message_context (message_id, segment_id, topic_id) VALUES (?, ?, NULL)");
2439
+ const updateSession = db.prepare("UPDATE segment SET end_ts = ?, message_count = message_count + ? WHERE id = ?");
2495
2440
  return db.transaction(() => {
2496
2441
  let newSessionCount = 0;
2497
2442
  let currentSessionId = null;
@@ -2867,22 +2812,22 @@ function getSearchMessageContext(db, messageIds, contextBefore = 2, contextAfter
2867
2812
  for (const messageId of messageIds) {
2868
2813
  contextIds.add(messageId);
2869
2814
  if (hasSessionData) {
2870
- const sessionRow = db.prepare("SELECT session_id FROM message_context WHERE message_id = ?").get(messageId);
2815
+ const sessionRow = db.prepare("SELECT segment_id FROM message_context WHERE message_id = ?").get(messageId);
2871
2816
  if (sessionRow) {
2872
2817
  if (contextBefore > 0) {
2873
2818
  const rows = db.prepare(
2874
2819
  `SELECT mc.message_id as id FROM message_context mc
2875
- WHERE mc.session_id = ? AND mc.message_id < ?
2820
+ WHERE mc.segment_id = ? AND mc.message_id < ?
2876
2821
  ORDER BY mc.message_id DESC LIMIT ?`
2877
- ).all(sessionRow.session_id, messageId, contextBefore);
2822
+ ).all(sessionRow.segment_id, messageId, contextBefore);
2878
2823
  rows.forEach((r) => contextIds.add(r.id));
2879
2824
  }
2880
2825
  if (contextAfter > 0) {
2881
2826
  const rows = db.prepare(
2882
2827
  `SELECT mc.message_id as id FROM message_context mc
2883
- WHERE mc.session_id = ? AND mc.message_id > ?
2828
+ WHERE mc.segment_id = ? AND mc.message_id > ?
2884
2829
  ORDER BY mc.message_id ASC LIMIT ?`
2885
- ).all(sessionRow.session_id, messageId, contextAfter);
2830
+ ).all(sessionRow.segment_id, messageId, contextAfter);
2886
2831
  rows.forEach((r) => contextIds.add(r.id));
2887
2832
  }
2888
2833
  continue;
@@ -3229,17 +3174,17 @@ function filterMessagesWithContext(db, options = {}) {
3229
3174
  pagination: { page, pageSize, totalBlocks, totalHits, hasMore }
3230
3175
  };
3231
3176
  }
3232
- function getMultipleSessionsMessages(db, chatSessionIds, page = 1, pageSize = 50) {
3233
- if (chatSessionIds.length === 0) {
3177
+ function getMultipleSessionsMessages(db, segmentIds, page = 1, pageSize = 50) {
3178
+ if (segmentIds.length === 0) {
3234
3179
  return emptyResult(page, pageSize);
3235
3180
  }
3236
3181
  const sessionsSql = `
3237
3182
  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(",")})
3183
+ FROM segment
3184
+ WHERE id IN (${segmentIds.map(() => "?").join(",")})
3240
3185
  ORDER BY start_ts ASC
3241
3186
  `;
3242
- const allSessions = db.prepare(sessionsSql).all(...chatSessionIds);
3187
+ const allSessions = db.prepare(sessionsSql).all(...segmentIds);
3243
3188
  const totalBlocks = allSessions.length;
3244
3189
  const startIdx = (page - 1) * pageSize;
3245
3190
  const endIdx = Math.min(startIdx + pageSize, totalBlocks);
@@ -3262,7 +3207,7 @@ function getMultipleSessionsMessages(db, chatSessionIds, page = 1, pageSize = 50
3262
3207
  JOIN member m ON msg.sender_id = m.id
3263
3208
  LEFT JOIN message reply_msg ON msg.reply_to_message_id = reply_msg.platform_message_id
3264
3209
  LEFT JOIN member reply_m ON reply_msg.sender_id = reply_m.id
3265
- WHERE mc.session_id = ?
3210
+ WHERE mc.segment_id = ?
3266
3211
  ORDER BY msg.ts ASC
3267
3212
  `;
3268
3213
  for (const session of pageSessions) {
@@ -3904,7 +3849,7 @@ function getRelationshipStats(db, filter, options) {
3904
3849
  monthlyPerseverance: [],
3905
3850
  perseveranceThreshold
3906
3851
  };
3907
- const sessionCount = db.prepare("SELECT COUNT(*) as count FROM chat_session").get();
3852
+ const sessionCount = db.prepare("SELECT COUNT(*) as count FROM segment").get();
3908
3853
  if (!sessionCount || sessionCount.count === 0) return emptyResult2;
3909
3854
  const timeConditions = [];
3910
3855
  const params = [];
@@ -3917,13 +3862,13 @@ function getRelationshipStats(db, filter, options) {
3917
3862
  params.push(filter.endTs);
3918
3863
  }
3919
3864
  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,
3865
+ const segmentRows = db.prepare(
3866
+ `SELECT cs.id AS segment_id, cs.start_ts, cs.end_ts,
3922
3867
  (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,
3868
+ WHERE mc.segment_id = cs.id ORDER BY m.ts ASC, m.id ASC LIMIT 1) AS initiator_id,
3924
3869
  (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`
3870
+ WHERE mc.segment_id = cs.id ORDER BY m.ts DESC, m.id DESC LIMIT 1) AS closer_id
3871
+ FROM segment cs ${whereClause} ORDER BY cs.start_ts ASC`
3927
3872
  ).all(...params);
3928
3873
  const memberNames = /* @__PURE__ */ new Map();
3929
3874
  const memberRows = db.prepare("SELECT id, COALESCE(group_nickname, account_name, platform_id) as name FROM member").all();
@@ -3934,7 +3879,7 @@ function getRelationshipStats(db, filter, options) {
3934
3879
  const iceBreakMap = /* @__PURE__ */ new Map();
3935
3880
  let totalIceBreaks = 0;
3936
3881
  let prevEndTs = null;
3937
- for (const row of sessionRows) {
3882
+ for (const row of segmentRows) {
3938
3883
  const month = toLocalMonth(row.start_ts);
3939
3884
  if (!monthMap.has(month)) monthMap.set(month, { initiateMap: /* @__PURE__ */ new Map(), closeMap: /* @__PURE__ */ new Map(), totalSessions: 0 });
3940
3885
  const ms = monthMap.get(month);
@@ -3983,12 +3928,12 @@ function getRelationshipStats(db, filter, options) {
3983
3928
  iceBreakers.push({ month, memberId, name: memberNames.get(memberId) ?? `Unknown(${memberId})`, count });
3984
3929
  }
3985
3930
  }
3986
- const sessionIdList = sessionRows.map((r) => r.session_id);
3987
- const msgStats = queryMessageLevelStats(db, sessionIdList, memberNames, perseveranceThreshold);
3931
+ const segmentIdList = segmentRows.map((r) => r.segment_id);
3932
+ const msgStats = queryMessageLevelStats(db, segmentIdList, memberNames, perseveranceThreshold);
3988
3933
  return {
3989
3934
  months,
3990
3935
  members,
3991
- totalSessions: sessionRows.length,
3936
+ totalSessions: segmentRows.length,
3992
3937
  hasSessionIndex: true,
3993
3938
  iceBreakers,
3994
3939
  totalIceBreaks,
@@ -3996,7 +3941,7 @@ function getRelationshipStats(db, filter, options) {
3996
3941
  perseveranceThreshold
3997
3942
  };
3998
3943
  }
3999
- function queryMessageLevelStats(db, sessionIds, memberNames, perseveranceThreshold) {
3944
+ function queryMessageLevelStats(db, segmentIds, memberNames, perseveranceThreshold) {
4000
3945
  const empty = {
4001
3946
  responseLatency: [],
4002
3947
  perseverance: [],
@@ -4004,22 +3949,22 @@ function queryMessageLevelStats(db, sessionIds, memberNames, perseveranceThresho
4004
3949
  monthlyResponseLatency: [],
4005
3950
  monthlyPerseverance: []
4006
3951
  };
4007
- if (sessionIds.length === 0) return empty;
3952
+ if (segmentIds.length === 0) return empty;
4008
3953
  const BATCH_SIZE2 = 500;
4009
3954
  const responseTotals = /* @__PURE__ */ new Map();
4010
3955
  const dtTotals = /* @__PURE__ */ new Map();
4011
3956
  const monthlyRespMap = /* @__PURE__ */ new Map();
4012
3957
  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);
3958
+ for (let i = 0; i < segmentIds.length; i += BATCH_SIZE2) {
3959
+ const batch = segmentIds.slice(i, i + BATCH_SIZE2);
4015
3960
  const placeholders = batch.map(() => "?").join(",");
4016
3961
  const latencyRows = db.prepare(
4017
3962
  `WITH msg_lag AS (
4018
3963
  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
3964
+ LAG(m.sender_id) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_sender_id,
3965
+ LAG(m.ts) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_ts
4021
3966
  FROM message_context mc JOIN message m ON m.id = mc.message_id
4022
- WHERE mc.session_id IN (${placeholders}) AND m.type = 0
3967
+ WHERE mc.segment_id IN (${placeholders}) AND m.type = 0
4023
3968
  )
4024
3969
  SELECT strftime('%Y-%m', datetime(ts, 'unixepoch', 'localtime')) AS month,
4025
3970
  sender_id AS responder_id, SUM(ts - prev_ts) AS total_time, COUNT(*) AS response_count
@@ -4043,10 +3988,10 @@ function queryMessageLevelStats(db, sessionIds, memberNames, perseveranceThresho
4043
3988
  const dtRows = db.prepare(
4044
3989
  `WITH msg_lag AS (
4045
3990
  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
3991
+ LAG(m.sender_id) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_sender_id,
3992
+ LAG(m.ts) OVER (PARTITION BY mc.segment_id ORDER BY m.ts, m.id) AS prev_ts
4048
3993
  FROM message_context mc JOIN message m ON m.id = mc.message_id
4049
- WHERE mc.session_id IN (${placeholders}) AND m.type = 0
3994
+ WHERE mc.segment_id IN (${placeholders}) AND m.type = 0
4050
3995
  )
4051
3996
  SELECT strftime('%Y-%m', datetime(ts, 'unixepoch', 'localtime')) AS month,
4052
3997
  sender_id, COUNT(*) AS double_text_count
@@ -4258,125 +4203,6 @@ function getLanguagePreferenceAnalysis(db, params) {
4258
4203
  return { members: memberProfiles, sharedWords, similarityScore };
4259
4204
  }
4260
4205
 
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
4206
  // ../../packages/core/src/version.ts
4381
4207
  function parseStableVersion(version) {
4382
4208
  const normalized = version.trim().replace(/^v/i, "");
@@ -5450,6 +5276,17 @@ function isValidWord(word, locale, minLength, enableStopwords, isStopwordFn) {
5450
5276
  }
5451
5277
 
5452
5278
  // ../../packages/core/src/ai/tool-catalog.ts
5279
+ var LEGACY_BUILTIN_TOOL_NAME_ALIASES = {
5280
+ search_sessions: "search_segments",
5281
+ get_session_messages: "get_segment_messages",
5282
+ get_session_summaries: "get_segment_summaries"
5283
+ };
5284
+ function normalizeBuiltinToolName(toolName) {
5285
+ return LEGACY_BUILTIN_TOOL_NAME_ALIASES[toolName] ?? toolName;
5286
+ }
5287
+ function normalizeBuiltinToolNames(toolNames) {
5288
+ return Array.from(new Set(toolNames.map(normalizeBuiltinToolName)));
5289
+ }
5453
5290
  var BUILTIN_TOOL_CATALOG = [
5454
5291
  // Core 工具
5455
5292
  { name: "get_chat_overview", category: "core" },
@@ -5457,8 +5294,8 @@ var BUILTIN_TOOL_CATALOG = [
5457
5294
  { name: "deep_search_messages", category: "core" },
5458
5295
  { name: "get_recent_messages", category: "core" },
5459
5296
  { name: "get_message_context", category: "core" },
5460
- { name: "search_sessions", category: "core" },
5461
- { name: "get_session_messages", category: "core" },
5297
+ { name: "search_segments", category: "core" },
5298
+ { name: "get_segment_messages", category: "core" },
5462
5299
  { name: "get_members", category: "core" },
5463
5300
  { name: "get_schema", category: "core" },
5464
5301
  // Analysis 工具
@@ -5466,7 +5303,7 @@ var BUILTIN_TOOL_CATALOG = [
5466
5303
  { name: "get_time_stats", category: "analysis" },
5467
5304
  { name: "get_member_name_history", category: "analysis" },
5468
5305
  { name: "get_conversation_between", category: "analysis" },
5469
- { name: "get_session_summaries", category: "analysis" },
5306
+ { name: "get_segment_summaries", category: "analysis" },
5470
5307
  { name: "response_time_analysis", category: "analysis" },
5471
5308
  { name: "keyword_frequency", category: "analysis" },
5472
5309
  { name: "render_chart", category: "analysis" },
@@ -7001,76 +6838,195 @@ function mergeMembers2(sources, collidingIds) {
7001
6838
  return map;
7002
6839
  }
7003
6840
 
7004
- // ../../packages/parser/src/index.ts
7005
- import * as fs24 from "fs";
7006
- import * as path20 from "path";
6841
+ // ../../packages/core/src/schema/tables.ts
6842
+ var CURRENT_SCHEMA_VERSION = 6;
6843
+ var CHAT_DB_TABLES = `
6844
+ CREATE TABLE IF NOT EXISTS meta (
6845
+ name TEXT NOT NULL,
6846
+ platform TEXT NOT NULL,
6847
+ type TEXT NOT NULL,
6848
+ imported_at INTEGER NOT NULL,
6849
+ group_id TEXT,
6850
+ group_avatar TEXT,
6851
+ owner_id TEXT,
6852
+ schema_version INTEGER DEFAULT ${CURRENT_SCHEMA_VERSION},
6853
+ session_gap_threshold INTEGER
6854
+ );
7007
6855
 
7008
- // ../../packages/parser/src/sniffer.ts
7009
- import * as fs9 from "fs";
7010
- import * as path9 from "path";
7011
- var HEAD_SIZE = 64 * 1024;
7012
- function readFileHead(filePath, size = HEAD_SIZE) {
7013
- const fd = fs9.openSync(filePath, "r");
7014
- const buffer = Buffer.alloc(size);
7015
- const bytesRead = fs9.readSync(fd, buffer, 0, size, 0);
7016
- fs9.closeSync(fd);
7017
- return buffer.slice(0, bytesRead).toString("utf-8");
7018
- }
7019
- function getExtension(filePath) {
7020
- return path9.extname(filePath).toLowerCase();
7021
- }
7022
- function matchHeadSignatures(headContent, patterns) {
7023
- return patterns.some((pattern) => pattern.test(headContent));
7024
- }
7025
- function matchFilenameSignatures(filePath, patterns) {
7026
- const filename = path9.basename(filePath);
7027
- return patterns.some((pattern) => pattern.test(filename));
7028
- }
7029
- function matchRequiredFields(headContent, fields) {
7030
- return fields.every((field) => {
7031
- const pattern = new RegExp(`"${field.replace(".", '"\\s*:\\s*.*"')}"\\s*:`);
7032
- return pattern.test(headContent) || headContent.includes(`"${field}"`);
7033
- });
7034
- }
7035
- var FormatSniffer = class {
7036
- formats = [];
7037
- /**
7038
- * 注册格式模块
7039
- */
7040
- register(module) {
7041
- this.formats.push(module);
7042
- this.formats.sort((a, b) => a.feature.priority - b.feature.priority);
7043
- }
7044
- /**
7045
- * 批量注册格式模块
7046
- */
7047
- registerAll(modules) {
7048
- for (const module of modules) {
7049
- this.register(module);
7050
- }
7051
- }
7052
- /**
7053
- * 嗅探文件格式
7054
- * @param filePath 文件路径
7055
- * @returns 匹配的格式特征,如果无法识别则返回 null
7056
- */
7057
- sniff(filePath) {
7058
- const ext = getExtension(filePath);
7059
- const headContent = readFileHead(filePath);
7060
- for (const { feature: feature14 } of this.formats) {
7061
- if (this.matchFeature(feature14, ext, headContent, filePath)) {
7062
- return feature14;
7063
- }
7064
- }
7065
- return null;
7066
- }
7067
- /**
7068
- * 获取文件对应的解析器
7069
- * @param filePath 文件路径
7070
- * @returns 匹配的解析器,如果无法识别则返回 null
7071
- */
7072
- getParser(filePath) {
7073
- const ext = getExtension(filePath);
6856
+ CREATE TABLE IF NOT EXISTS member (
6857
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6858
+ platform_id TEXT NOT NULL UNIQUE,
6859
+ account_name TEXT,
6860
+ group_nickname TEXT,
6861
+ aliases TEXT DEFAULT '[]',
6862
+ avatar TEXT,
6863
+ roles TEXT DEFAULT '[]'
6864
+ );
6865
+
6866
+ CREATE TABLE IF NOT EXISTS member_name_history (
6867
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6868
+ member_id INTEGER NOT NULL,
6869
+ name_type TEXT NOT NULL,
6870
+ name TEXT NOT NULL,
6871
+ start_ts INTEGER NOT NULL,
6872
+ end_ts INTEGER,
6873
+ FOREIGN KEY(member_id) REFERENCES member(id)
6874
+ );
6875
+
6876
+ CREATE TABLE IF NOT EXISTS message (
6877
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6878
+ sender_id INTEGER NOT NULL,
6879
+ sender_account_name TEXT,
6880
+ sender_group_nickname TEXT,
6881
+ ts INTEGER NOT NULL,
6882
+ type INTEGER NOT NULL,
6883
+ content TEXT,
6884
+ reply_to_message_id TEXT DEFAULT NULL,
6885
+ platform_message_id TEXT DEFAULT NULL,
6886
+ FOREIGN KEY(sender_id) REFERENCES member(id)
6887
+ );
6888
+
6889
+ CREATE TABLE IF NOT EXISTS segment (
6890
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6891
+ start_ts INTEGER NOT NULL,
6892
+ end_ts INTEGER NOT NULL,
6893
+ message_count INTEGER DEFAULT 0,
6894
+ is_manual INTEGER DEFAULT 0,
6895
+ summary TEXT
6896
+ );
6897
+
6898
+ CREATE TABLE IF NOT EXISTS message_context (
6899
+ message_id INTEGER PRIMARY KEY,
6900
+ segment_id INTEGER NOT NULL,
6901
+ topic_id INTEGER
6902
+ );
6903
+ `;
6904
+ var CHAT_DB_INDEXES = `
6905
+ CREATE INDEX IF NOT EXISTS idx_message_ts ON message(ts);
6906
+ CREATE INDEX IF NOT EXISTS idx_message_sender ON message(sender_id);
6907
+ CREATE INDEX IF NOT EXISTS idx_message_platform_id ON message(platform_message_id);
6908
+ CREATE INDEX IF NOT EXISTS idx_member_name_history_member_id ON member_name_history(member_id);
6909
+ CREATE INDEX IF NOT EXISTS idx_segment_time ON segment(start_ts, end_ts);
6910
+ CREATE INDEX IF NOT EXISTS idx_context_segment ON message_context(segment_id);
6911
+ `;
6912
+ var CHAT_DB_SCHEMA = CHAT_DB_TABLES + CHAT_DB_INDEXES;
6913
+ var FTS_TABLE_SCHEMA = `
6914
+ CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts5(
6915
+ content,
6916
+ content='',
6917
+ content_rowid=id
6918
+ );
6919
+ `;
6920
+
6921
+ // ../../packages/core/src/schema/migrations.ts
6922
+ function getSchemaVersion(db) {
6923
+ try {
6924
+ const tableInfo = db.pragma("table_info(meta)");
6925
+ const hasVersionColumn = tableInfo.some((col) => col.name === "schema_version");
6926
+ if (!hasVersionColumn) return 0;
6927
+ const result = db.prepare("SELECT schema_version FROM meta LIMIT 1").get();
6928
+ return result?.schema_version ?? 0;
6929
+ } catch {
6930
+ return 0;
6931
+ }
6932
+ }
6933
+ function setSchemaVersion(db, version) {
6934
+ const tableInfo = db.pragma("table_info(meta)");
6935
+ const hasVersionColumn = tableInfo.some((col) => col.name === "schema_version");
6936
+ if (!hasVersionColumn) {
6937
+ db.exec("ALTER TABLE meta ADD COLUMN schema_version INTEGER DEFAULT 0");
6938
+ }
6939
+ db.prepare("UPDATE meta SET schema_version = ?").run(version);
6940
+ }
6941
+ function needsMigration(db, targetVersion) {
6942
+ return getSchemaVersion(db) < targetVersion;
6943
+ }
6944
+ function runMigrations(db, migrationsList, forceRepair = false) {
6945
+ const currentVersion = getSchemaVersion(db);
6946
+ if (!forceRepair && currentVersion >= (migrationsList.at(-1)?.version ?? 0)) {
6947
+ return false;
6948
+ }
6949
+ const pending = forceRepair ? migrationsList : migrationsList.filter((m) => m.version > currentVersion);
6950
+ if (pending.length === 0) return false;
6951
+ db.transaction(() => {
6952
+ for (const migration of pending) {
6953
+ migration.up(db);
6954
+ setSchemaVersion(db, migration.version);
6955
+ }
6956
+ });
6957
+ return true;
6958
+ }
6959
+
6960
+ // ../../packages/parser/src/index.ts
6961
+ import * as fs24 from "fs";
6962
+ import * as path20 from "path";
6963
+
6964
+ // ../../packages/parser/src/sniffer.ts
6965
+ import * as fs9 from "fs";
6966
+ import * as path9 from "path";
6967
+ var HEAD_SIZE = 64 * 1024;
6968
+ function readFileHead(filePath, size = HEAD_SIZE) {
6969
+ const fd = fs9.openSync(filePath, "r");
6970
+ const buffer = Buffer.alloc(size);
6971
+ const bytesRead = fs9.readSync(fd, buffer, 0, size, 0);
6972
+ fs9.closeSync(fd);
6973
+ return buffer.slice(0, bytesRead).toString("utf-8");
6974
+ }
6975
+ function getExtension(filePath) {
6976
+ return path9.extname(filePath).toLowerCase();
6977
+ }
6978
+ function matchHeadSignatures(headContent, patterns) {
6979
+ return patterns.some((pattern) => pattern.test(headContent));
6980
+ }
6981
+ function matchFilenameSignatures(filePath, patterns) {
6982
+ const filename = path9.basename(filePath);
6983
+ return patterns.some((pattern) => pattern.test(filename));
6984
+ }
6985
+ function matchRequiredFields(headContent, fields) {
6986
+ return fields.every((field) => {
6987
+ const pattern = new RegExp(`"${field.replace(".", '"\\s*:\\s*.*"')}"\\s*:`);
6988
+ return pattern.test(headContent) || headContent.includes(`"${field}"`);
6989
+ });
6990
+ }
6991
+ var FormatSniffer = class {
6992
+ formats = [];
6993
+ /**
6994
+ * 注册格式模块
6995
+ */
6996
+ register(module) {
6997
+ this.formats.push(module);
6998
+ this.formats.sort((a, b) => a.feature.priority - b.feature.priority);
6999
+ }
7000
+ /**
7001
+ * 批量注册格式模块
7002
+ */
7003
+ registerAll(modules) {
7004
+ for (const module of modules) {
7005
+ this.register(module);
7006
+ }
7007
+ }
7008
+ /**
7009
+ * 嗅探文件格式
7010
+ * @param filePath 文件路径
7011
+ * @returns 匹配的格式特征,如果无法识别则返回 null
7012
+ */
7013
+ sniff(filePath) {
7014
+ const ext = getExtension(filePath);
7015
+ const headContent = readFileHead(filePath);
7016
+ for (const { feature: feature14 } of this.formats) {
7017
+ if (this.matchFeature(feature14, ext, headContent, filePath)) {
7018
+ return feature14;
7019
+ }
7020
+ }
7021
+ return null;
7022
+ }
7023
+ /**
7024
+ * 获取文件对应的解析器
7025
+ * @param filePath 文件路径
7026
+ * @returns 匹配的解析器,如果无法识别则返回 null
7027
+ */
7028
+ getParser(filePath) {
7029
+ const ext = getExtension(filePath);
7074
7030
  const headContent = readFileHead(filePath);
7075
7031
  for (const { feature: feature14, parser: parser9 } of this.formats) {
7076
7032
  if (this.matchFeature(feature14, ext, headContent, filePath)) {
@@ -11825,6 +11781,10 @@ function isDuplicate(msg, existingPlatformMsgIds, existingKeys) {
11825
11781
  existingKeys.add(key);
11826
11782
  return false;
11827
11783
  }
11784
+ function normalizeTimestamp(timestamp) {
11785
+ const value = typeof timestamp === "string" && timestamp.trim() !== "" ? Number(timestamp) : timestamp;
11786
+ return typeof value === "number" && value > 0 && Number.isFinite(value) ? value : null;
11787
+ }
11828
11788
  async function analyzeIncrementalImport(sessionId, filePath, deps) {
11829
11789
  const formatFeature = detectFormat(filePath);
11830
11790
  if (!formatFeature) {
@@ -11852,7 +11812,9 @@ async function analyzeIncrementalImport(sessionId, filePath, deps) {
11852
11812
  onMessageBatch: (batch) => {
11853
11813
  for (const msg of batch) {
11854
11814
  totalInFile++;
11855
- if (isDuplicate(msg, existingPlatformMsgIds, existingKeys)) {
11815
+ const timestamp = normalizeTimestamp(msg.timestamp);
11816
+ if (timestamp === null) continue;
11817
+ if (isDuplicate({ ...msg, timestamp }, existingPlatformMsgIds, existingKeys)) {
11856
11818
  duplicateCount++;
11857
11819
  } else {
11858
11820
  newMessageCount++;
@@ -11975,11 +11937,12 @@ async function incrementalImport(sessionId, filePath, deps, options) {
11975
11937
  trackError2(processedCount, "MISSING_TIMESTAMP", "timestamp field is missing");
11976
11938
  continue;
11977
11939
  }
11978
- if (typeof msg.timestamp !== "number" || msg.timestamp <= 0 || !isFinite(msg.timestamp)) {
11940
+ const timestamp = normalizeTimestamp(msg.timestamp);
11941
+ if (timestamp === null) {
11979
11942
  trackError2(processedCount, "INVALID_TIMESTAMP", `timestamp value: ${msg.timestamp}`);
11980
11943
  continue;
11981
11944
  }
11982
- if (isDuplicate(msg, existingPlatformMsgIds, existingKeys)) {
11945
+ if (isDuplicate({ ...msg, timestamp }, existingPlatformMsgIds, existingKeys)) {
11983
11946
  duplicateCount++;
11984
11947
  continue;
11985
11948
  }
@@ -12003,7 +11966,7 @@ async function incrementalImport(sessionId, filePath, deps, options) {
12003
11966
  memberId,
12004
11967
  msg.senderAccountName || null,
12005
11968
  msg.senderGroupNickname || null,
12006
- msg.timestamp,
11969
+ timestamp,
12007
11970
  msg.type,
12008
11971
  msg.content || null,
12009
11972
  msg.replyToMessageId || null,
@@ -12207,17 +12170,17 @@ function writeParseResultToDb(db, meta, members, messages) {
12207
12170
  // ../../packages/node-runtime/src/import/perf-logger.ts
12208
12171
  var lastLogTime = Date.now();
12209
12172
 
12210
- // ../../packages/node-runtime/src/ai/conversations.ts
12211
- import Database2 from "better-sqlite3";
12173
+ // ../../packages/node-runtime/src/ai/chats.ts
12174
+ import Database from "better-sqlite3";
12212
12175
  import * as fs28 from "fs";
12213
12176
  import * as path23 from "path";
12214
12177
  var DEFAULT_GENERAL_ID = "general_cn";
12215
12178
  var defaultLogger = {
12216
12179
  warn(_category, message, extra) {
12217
- console.warn(`[AI Conversations] ${message}`, extra ?? "");
12180
+ console.warn(`[AI Chats] ${message}`, extra ?? "");
12218
12181
  }
12219
12182
  };
12220
- var AIConversationManager = class {
12183
+ var AIChatManager = class {
12221
12184
  db = null;
12222
12185
  aiDataDir;
12223
12186
  logger;
@@ -12237,13 +12200,14 @@ var AIConversationManager = class {
12237
12200
  if (this.db) return this.db;
12238
12201
  this.ensureDir(this.aiDataDir);
12239
12202
  const dbPath = path23.join(this.aiDataDir, "conversations.db");
12240
- this.db = this.nativeBinding ? new Database2(dbPath, { nativeBinding: this.nativeBinding }) : new Database2(dbPath);
12203
+ this.db = this.nativeBinding ? new Database(dbPath, { nativeBinding: this.nativeBinding }) : new Database(dbPath);
12241
12204
  this.db.pragma("journal_mode = WAL");
12242
12205
  this.db.exec(`
12243
- CREATE TABLE IF NOT EXISTS ai_conversation (
12206
+ CREATE TABLE IF NOT EXISTS ai_chat (
12244
12207
  id TEXT PRIMARY KEY,
12245
12208
  session_id TEXT NOT NULL,
12246
12209
  title TEXT,
12210
+ assistant_id TEXT DEFAULT '${DEFAULT_GENERAL_ID}',
12247
12211
  active_message_id TEXT,
12248
12212
  created_at INTEGER NOT NULL,
12249
12213
  updated_at INTEGER NOT NULL
@@ -12251,7 +12215,7 @@ var AIConversationManager = class {
12251
12215
 
12252
12216
  CREATE TABLE IF NOT EXISTS ai_message (
12253
12217
  id TEXT PRIMARY KEY,
12254
- conversation_id TEXT NOT NULL,
12218
+ ai_chat_id TEXT NOT NULL,
12255
12219
  role TEXT NOT NULL,
12256
12220
  content TEXT NOT NULL,
12257
12221
  timestamp INTEGER NOT NULL,
@@ -12261,51 +12225,113 @@ var AIConversationManager = class {
12261
12225
  parent_id TEXT,
12262
12226
  sibling_group_id TEXT,
12263
12227
  branch_index INTEGER DEFAULT 0,
12264
- FOREIGN KEY(conversation_id) REFERENCES ai_conversation(id) ON DELETE CASCADE
12228
+ debug_context TEXT,
12229
+ token_usage TEXT,
12230
+ FOREIGN KEY(ai_chat_id) REFERENCES ai_chat(id) ON DELETE CASCADE
12265
12231
  );
12266
12232
 
12267
- CREATE INDEX IF NOT EXISTS idx_ai_conversation_session ON ai_conversation(session_id);
12268
- CREATE INDEX IF NOT EXISTS idx_ai_message_conversation ON ai_message(conversation_id);
12269
12233
  `);
12270
12234
  this.migrateDatabase(this.db);
12271
12235
  return this.db;
12272
12236
  }
12237
+ tableExists(db, tableName) {
12238
+ const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?").get(tableName);
12239
+ return !!row;
12240
+ }
12241
+ getTableColumns(db, tableName) {
12242
+ const tableInfo = db.pragma(`table_info(${tableName})`);
12243
+ return tableInfo.map((col) => col.name);
12244
+ }
12245
+ ensureMessageMigrationColumns(db, tableName) {
12246
+ const messageColumns = this.getTableColumns(db, tableName);
12247
+ if (!messageColumns.includes("content_blocks")) {
12248
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN content_blocks TEXT`);
12249
+ }
12250
+ if (!messageColumns.includes("token_usage")) {
12251
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN token_usage TEXT`);
12252
+ }
12253
+ if (!messageColumns.includes("debug_context")) {
12254
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN debug_context TEXT`);
12255
+ }
12256
+ if (!messageColumns.includes("parent_id")) {
12257
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN parent_id TEXT`);
12258
+ }
12259
+ if (!messageColumns.includes("sibling_group_id")) {
12260
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN sibling_group_id TEXT`);
12261
+ }
12262
+ if (!messageColumns.includes("branch_index")) {
12263
+ db.exec(`ALTER TABLE ${tableName} ADD COLUMN branch_index INTEGER DEFAULT 0`);
12264
+ }
12265
+ }
12273
12266
  migrateDatabase(db) {
12274
12267
  try {
12275
- const messageTableInfo = db.pragma("table_info(ai_message)");
12276
- const messageColumns = messageTableInfo.map((col) => col.name);
12277
- const needsMessageTreeBackfill = !messageColumns.includes("parent_id") || !messageColumns.includes("sibling_group_id") || !messageColumns.includes("branch_index");
12278
- if (!messageColumns.includes("content_blocks")) {
12279
- db.exec("ALTER TABLE ai_message ADD COLUMN content_blocks TEXT");
12280
- }
12281
- if (!messageColumns.includes("token_usage")) {
12282
- db.exec("ALTER TABLE ai_message ADD COLUMN token_usage TEXT");
12283
- }
12284
- if (!messageColumns.includes("debug_context")) {
12285
- db.exec("ALTER TABLE ai_message ADD COLUMN debug_context TEXT");
12286
- }
12287
- if (!messageColumns.includes("parent_id")) {
12288
- db.exec("ALTER TABLE ai_message ADD COLUMN parent_id TEXT");
12289
- }
12290
- if (!messageColumns.includes("sibling_group_id")) {
12291
- db.exec("ALTER TABLE ai_message ADD COLUMN sibling_group_id TEXT");
12292
- }
12293
- if (!messageColumns.includes("branch_index")) {
12294
- db.exec("ALTER TABLE ai_message ADD COLUMN branch_index INTEGER DEFAULT 0");
12268
+ const hasLegacyConversationTable = this.tableExists(db, "ai_conversation");
12269
+ if (hasLegacyConversationTable) {
12270
+ const convColumns = this.getTableColumns(db, "ai_conversation");
12271
+ if (!convColumns.includes("assistant_id")) {
12272
+ db.exec(`ALTER TABLE ai_conversation ADD COLUMN assistant_id TEXT DEFAULT '${DEFAULT_GENERAL_ID}'`);
12273
+ }
12274
+ if (!convColumns.includes("active_message_id")) {
12275
+ db.exec("ALTER TABLE ai_conversation ADD COLUMN active_message_id TEXT");
12276
+ }
12277
+ db.exec(`
12278
+ INSERT OR IGNORE INTO ai_chat (
12279
+ id, session_id, title, assistant_id, active_message_id, created_at, updated_at
12280
+ )
12281
+ SELECT id, session_id, title, COALESCE(assistant_id, '${DEFAULT_GENERAL_ID}'),
12282
+ active_message_id, created_at, updated_at
12283
+ FROM ai_conversation
12284
+ `);
12295
12285
  }
12296
- const convTableInfo = db.pragma("table_info(ai_conversation)");
12297
- const convColumns = convTableInfo.map((col) => col.name);
12298
- const needsConversationBackfill = !convColumns.includes("active_message_id");
12299
- if (!convColumns.includes("assistant_id")) {
12300
- db.exec(`ALTER TABLE ai_conversation ADD COLUMN assistant_id TEXT DEFAULT '${DEFAULT_GENERAL_ID}'`);
12286
+ this.ensureMessageMigrationColumns(db, "ai_message");
12287
+ const messageColumns = this.getTableColumns(db, "ai_message");
12288
+ const hadLegacyConversationId = messageColumns.includes("conversation_id");
12289
+ const needsMessageTreeBackfill = !messageColumns.includes("parent_id") || !messageColumns.includes("sibling_group_id") || !messageColumns.includes("branch_index") || hadLegacyConversationId;
12290
+ if (hadLegacyConversationId && !messageColumns.includes("ai_chat_id")) {
12291
+ db.exec(`
12292
+ DROP INDEX IF EXISTS idx_ai_message_conversation;
12293
+ ALTER TABLE ai_message RENAME TO ai_message_legacy;
12294
+
12295
+ CREATE TABLE ai_message (
12296
+ id TEXT PRIMARY KEY,
12297
+ ai_chat_id TEXT NOT NULL,
12298
+ role TEXT NOT NULL,
12299
+ content TEXT NOT NULL,
12300
+ timestamp INTEGER NOT NULL,
12301
+ data_keywords TEXT,
12302
+ data_message_count INTEGER,
12303
+ content_blocks TEXT,
12304
+ parent_id TEXT,
12305
+ sibling_group_id TEXT,
12306
+ branch_index INTEGER DEFAULT 0,
12307
+ debug_context TEXT,
12308
+ token_usage TEXT,
12309
+ FOREIGN KEY(ai_chat_id) REFERENCES ai_chat(id) ON DELETE CASCADE
12310
+ );
12311
+
12312
+ INSERT INTO ai_message (
12313
+ id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count,
12314
+ content_blocks, parent_id, sibling_group_id, branch_index, debug_context, token_usage
12315
+ )
12316
+ SELECT id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12317
+ content_blocks, parent_id, sibling_group_id, branch_index, debug_context, token_usage
12318
+ FROM ai_message_legacy;
12319
+
12320
+ DROP TABLE ai_message_legacy;
12321
+ `);
12301
12322
  }
12302
- if (!convColumns.includes("active_message_id")) {
12303
- db.exec("ALTER TABLE ai_conversation ADD COLUMN active_message_id TEXT");
12323
+ if (hasLegacyConversationTable) {
12324
+ db.exec(`
12325
+ DROP INDEX IF EXISTS idx_ai_conversation_session;
12326
+ DROP TABLE ai_conversation;
12327
+ `);
12304
12328
  }
12305
- if (needsMessageTreeBackfill || needsConversationBackfill || this.hasUnbackfilledMessageTree(db)) {
12329
+ if (needsMessageTreeBackfill || this.hasUnbackfilledMessageTree(db)) {
12306
12330
  this.backfillMessageTree(db);
12307
12331
  }
12308
12332
  db.exec(`
12333
+ CREATE INDEX IF NOT EXISTS idx_ai_chat_session ON ai_chat(session_id);
12334
+ CREATE INDEX IF NOT EXISTS idx_ai_message_ai_chat ON ai_message(ai_chat_id);
12309
12335
  CREATE INDEX IF NOT EXISTS idx_ai_message_parent ON ai_message(parent_id);
12310
12336
  CREATE INDEX IF NOT EXISTS idx_ai_message_sibling ON ai_message(sibling_group_id);
12311
12337
  `);
@@ -12316,16 +12342,16 @@ var AIConversationManager = class {
12316
12342
  hasUnbackfilledMessageTree(db) {
12317
12343
  const row = db.prepare(
12318
12344
  `SELECT 1
12319
- FROM ai_conversation c
12345
+ FROM ai_chat c
12320
12346
  WHERE EXISTS (
12321
12347
  SELECT 1 FROM ai_message m
12322
- WHERE m.conversation_id = c.id
12348
+ WHERE m.ai_chat_id = c.id
12323
12349
  )
12324
12350
  AND (
12325
12351
  c.active_message_id IS NULL
12326
12352
  OR EXISTS (
12327
12353
  SELECT 1 FROM ai_message m
12328
- WHERE m.conversation_id = c.id
12354
+ WHERE m.ai_chat_id = c.id
12329
12355
  AND (m.sibling_group_id IS NULL OR m.branch_index IS NULL)
12330
12356
  )
12331
12357
  )
@@ -12334,26 +12360,26 @@ var AIConversationManager = class {
12334
12360
  return !!row;
12335
12361
  }
12336
12362
  backfillMessageTree(db) {
12337
- const conversations = db.prepare("SELECT id FROM ai_conversation").all();
12363
+ const aiChats = db.prepare("SELECT id FROM ai_chat").all();
12338
12364
  const updateMessage = db.prepare(
12339
12365
  "UPDATE ai_message SET parent_id = ?, sibling_group_id = COALESCE(sibling_group_id, ?), branch_index = COALESCE(branch_index, 0) WHERE id = ?"
12340
12366
  );
12341
- const updateConversation = db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?");
12367
+ const updateAIChat = db.prepare("UPDATE ai_chat SET active_message_id = ? WHERE id = ?");
12342
12368
  const tx = db.transaction(() => {
12343
- for (const conversation of conversations) {
12369
+ for (const aiChat of aiChats) {
12344
12370
  const messages = db.prepare(
12345
12371
  `SELECT id, parent_id as parentId, sibling_group_id as siblingGroupId
12346
- FROM ai_message WHERE conversation_id = ? ORDER BY timestamp ASC, id ASC`
12347
- ).all(conversation.id);
12372
+ FROM ai_message WHERE ai_chat_id = ? ORDER BY timestamp ASC, id ASC`
12373
+ ).all(aiChat.id);
12348
12374
  let previousId = null;
12349
12375
  for (const message of messages) {
12350
12376
  const parentId = message.parentId === void 0 ? previousId : message.parentId ?? previousId;
12351
12377
  updateMessage.run(parentId, message.siblingGroupId ?? message.id, message.id);
12352
12378
  previousId = message.id;
12353
12379
  }
12354
- const conversationRow = db.prepare("SELECT active_message_id as activeMessageId FROM ai_conversation WHERE id = ?").get(conversation.id);
12355
- if (!conversationRow?.activeMessageId && previousId) {
12356
- updateConversation.run(previousId, conversation.id);
12380
+ const aiChatRow = db.prepare("SELECT active_message_id as activeMessageId FROM ai_chat WHERE id = ?").get(aiChat.id);
12381
+ if (!aiChatRow?.activeMessageId && previousId) {
12382
+ updateAIChat.run(previousId, aiChat.id);
12357
12383
  }
12358
12384
  }
12359
12385
  });
@@ -12365,7 +12391,7 @@ var AIConversationManager = class {
12365
12391
  parseMessageRow(row) {
12366
12392
  return {
12367
12393
  id: row.id,
12368
- conversationId: row.conversationId,
12394
+ aiChatId: row.aiChatId,
12369
12395
  role: row.role,
12370
12396
  content: row.content,
12371
12397
  timestamp: row.timestamp,
@@ -12379,7 +12405,7 @@ var AIConversationManager = class {
12379
12405
  getMessageRow(messageId) {
12380
12406
  const db = this.getDb();
12381
12407
  const row = db.prepare(
12382
- `SELECT id, conversation_id as conversationId, role, content, timestamp,
12408
+ `SELECT id, ai_chat_id as aiChatId, role, content, timestamp,
12383
12409
  parent_id as parentId, sibling_group_id as siblingGroupId, branch_index as branchIndex,
12384
12410
  data_keywords as dataKeywords, data_message_count as dataMessageCount,
12385
12411
  content_blocks as contentBlocks, token_usage as tokenUsage
@@ -12387,45 +12413,45 @@ var AIConversationManager = class {
12387
12413
  ).get(messageId);
12388
12414
  return row ?? null;
12389
12415
  }
12390
- getActiveMessageId(conversationId) {
12416
+ getActiveMessageId(aiChatId) {
12391
12417
  const db = this.getDb();
12392
- const row = db.prepare("SELECT active_message_id as activeMessageId FROM ai_conversation WHERE id = ?").get(conversationId);
12418
+ const row = db.prepare("SELECT active_message_id as activeMessageId FROM ai_chat WHERE id = ?").get(aiChatId);
12393
12419
  if (row?.activeMessageId) {
12394
12420
  const activeExists = db.prepare("SELECT 1 FROM ai_message WHERE id = ?").get(row.activeMessageId);
12395
12421
  if (activeExists) return row.activeMessageId;
12396
12422
  }
12397
- const fallback = db.prepare("SELECT id FROM ai_message WHERE conversation_id = ? ORDER BY timestamp DESC, id DESC LIMIT 1").get(conversationId);
12423
+ const fallback = db.prepare("SELECT id FROM ai_message WHERE ai_chat_id = ? ORDER BY timestamp DESC, id DESC LIMIT 1").get(aiChatId);
12398
12424
  if (fallback?.id) {
12399
- db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?").run(fallback.id, conversationId);
12425
+ db.prepare("UPDATE ai_chat SET active_message_id = ? WHERE id = ?").run(fallback.id, aiChatId);
12400
12426
  return fallback.id;
12401
12427
  }
12402
12428
  return null;
12403
12429
  }
12404
- getAllMessageRows(conversationId) {
12430
+ getAllMessageRows(aiChatId) {
12405
12431
  return this.getDb().prepare(
12406
- `SELECT id, conversation_id as conversationId, role, content, timestamp,
12432
+ `SELECT id, ai_chat_id as aiChatId, role, content, timestamp,
12407
12433
  parent_id as parentId, sibling_group_id as siblingGroupId, branch_index as branchIndex,
12408
12434
  data_keywords as dataKeywords, data_message_count as dataMessageCount,
12409
12435
  content_blocks as contentBlocks, token_usage as tokenUsage
12410
- FROM ai_message WHERE conversation_id = ? ORDER BY timestamp ASC, id ASC`
12411
- ).all(conversationId);
12436
+ FROM ai_message WHERE ai_chat_id = ? ORDER BY timestamp ASC, id ASC`
12437
+ ).all(aiChatId);
12412
12438
  }
12413
- getActivePathRows(conversationId, leafMessageId) {
12439
+ getActivePathRows(aiChatId, leafMessageId) {
12414
12440
  if (leafMessageId === null) return [];
12415
- const allRows = this.getAllMessageRows(conversationId);
12441
+ const allRows = this.getAllMessageRows(aiChatId);
12416
12442
  if (allRows.length === 0) return [];
12417
12443
  const rowMap = new Map(allRows.map((row) => [row.id, row]));
12418
- let currentId = leafMessageId ?? this.getActiveMessageId(conversationId);
12419
- const path35 = [];
12444
+ let currentId = leafMessageId ?? this.getActiveMessageId(aiChatId);
12445
+ const path36 = [];
12420
12446
  const seen = /* @__PURE__ */ new Set();
12421
12447
  while (currentId && !seen.has(currentId)) {
12422
12448
  seen.add(currentId);
12423
12449
  const row = rowMap.get(currentId);
12424
12450
  if (!row) break;
12425
- path35.push(row);
12451
+ path36.push(row);
12426
12452
  currentId = row.parentId;
12427
12453
  }
12428
- return path35.length > 0 ? path35.reverse() : allRows;
12454
+ return path36.length > 0 ? path36.reverse() : allRows;
12429
12455
  }
12430
12456
  // ==================== 生命周期 ====================
12431
12457
  close() {
@@ -12481,21 +12507,21 @@ var AIConversationManager = class {
12481
12507
  }
12482
12508
  }
12483
12509
  // ==================== 对话管理 ====================
12484
- createConversation(sessionId, title, assistantId) {
12510
+ createAIChat(sessionId, title, assistantId) {
12485
12511
  const db = this.getDb();
12486
12512
  const now = Math.floor(Date.now() / 1e3);
12487
12513
  const id = this.generateId("conv");
12488
12514
  db.prepare(
12489
- `INSERT INTO ai_conversation (id, session_id, title, assistant_id, created_at, updated_at)
12515
+ `INSERT INTO ai_chat (id, session_id, title, assistant_id, created_at, updated_at)
12490
12516
  VALUES (?, ?, ?, ?, ?, ?)`
12491
12517
  ).run(id, sessionId, title || null, assistantId, now, now);
12492
12518
  return { id, sessionId, title: title || null, assistantId, activeMessageId: null, createdAt: now, updatedAt: now };
12493
12519
  }
12494
- getConversationCountsBySession() {
12520
+ getAIChatCountsBySession() {
12495
12521
  const result = /* @__PURE__ */ new Map();
12496
12522
  try {
12497
12523
  const db = this.getDb();
12498
- const rows = db.prepare("SELECT session_id, COUNT(*) as count FROM ai_conversation GROUP BY session_id").all();
12524
+ const rows = db.prepare("SELECT session_id, COUNT(*) as count FROM ai_chat GROUP BY session_id").all();
12499
12525
  for (const row of rows) {
12500
12526
  result.set(row.session_id, row.count);
12501
12527
  }
@@ -12503,58 +12529,58 @@ var AIConversationManager = class {
12503
12529
  }
12504
12530
  return result;
12505
12531
  }
12506
- getConversations(sessionId) {
12532
+ getAIChats(sessionId) {
12507
12533
  const db = this.getDb();
12508
12534
  return db.prepare(
12509
12535
  `SELECT id, session_id as sessionId, title, assistant_id as assistantId,
12510
12536
  active_message_id as activeMessageId,
12511
12537
  created_at as createdAt, updated_at as updatedAt
12512
- FROM ai_conversation WHERE session_id = ? ORDER BY updated_at DESC`
12538
+ FROM ai_chat WHERE session_id = ? ORDER BY updated_at DESC`
12513
12539
  ).all(sessionId);
12514
12540
  }
12515
- getConversation(conversationId) {
12541
+ getAIChat(aiChatId) {
12516
12542
  const db = this.getDb();
12517
12543
  const row = db.prepare(
12518
12544
  `SELECT id, session_id as sessionId, title, assistant_id as assistantId,
12519
12545
  active_message_id as activeMessageId,
12520
12546
  created_at as createdAt, updated_at as updatedAt
12521
- FROM ai_conversation WHERE id = ?`
12522
- ).get(conversationId);
12547
+ FROM ai_chat WHERE id = ?`
12548
+ ).get(aiChatId);
12523
12549
  return row || null;
12524
12550
  }
12525
- updateConversationTitle(conversationId, title) {
12551
+ updateAIChatTitle(aiChatId, title) {
12526
12552
  const db = this.getDb();
12527
12553
  const now = Math.floor(Date.now() / 1e3);
12528
- const result = db.prepare("UPDATE ai_conversation SET title = ?, updated_at = ? WHERE id = ?").run(title, now, conversationId);
12554
+ const result = db.prepare("UPDATE ai_chat SET title = ?, updated_at = ? WHERE id = ?").run(title, now, aiChatId);
12529
12555
  return result.changes > 0;
12530
12556
  }
12531
- deleteConversation(conversationId) {
12557
+ deleteAIChat(aiChatId) {
12532
12558
  const db = this.getDb();
12533
- db.prepare("DELETE FROM ai_message WHERE conversation_id = ?").run(conversationId);
12534
- const result = db.prepare("DELETE FROM ai_conversation WHERE id = ?").run(conversationId);
12559
+ db.prepare("DELETE FROM ai_message WHERE ai_chat_id = ?").run(aiChatId);
12560
+ const result = db.prepare("DELETE FROM ai_chat WHERE id = ?").run(aiChatId);
12535
12561
  return result.changes > 0;
12536
12562
  }
12537
12563
  // ==================== 消息管理 ====================
12538
- addMessage(conversationId, role, content, dataKeywords, dataMessageCount, contentBlocks, tokenUsage) {
12564
+ addMessage(aiChatId, role, content, dataKeywords, dataMessageCount, contentBlocks, tokenUsage) {
12539
12565
  const db = this.getDb();
12540
12566
  const now = Math.floor(Date.now() / 1e3);
12541
12567
  const id = this.generateId("msg");
12542
- const parentId = this.getActiveMessageId(conversationId);
12568
+ const parentId = this.getActiveMessageId(aiChatId);
12543
12569
  const siblingGroupId = id;
12544
12570
  const branchIndex = 0;
12545
- const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(conversationId) : void 0;
12571
+ const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(aiChatId) : void 0;
12546
12572
  if (pendingDebug) {
12547
- this.pendingDebugContextMap.delete(conversationId);
12573
+ this.pendingDebugContextMap.delete(aiChatId);
12548
12574
  }
12549
12575
  db.prepare(
12550
12576
  `INSERT INTO ai_message (
12551
- id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12577
+ id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count,
12552
12578
  content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12553
12579
  )
12554
12580
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
12555
12581
  ).run(
12556
12582
  id,
12557
- conversationId,
12583
+ aiChatId,
12558
12584
  role,
12559
12585
  content,
12560
12586
  now,
@@ -12567,14 +12593,10 @@ var AIConversationManager = class {
12567
12593
  siblingGroupId,
12568
12594
  branchIndex
12569
12595
  );
12570
- db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12571
- id,
12572
- now,
12573
- conversationId
12574
- );
12596
+ db.prepare("UPDATE ai_chat SET active_message_id = ?, updated_at = ? WHERE id = ?").run(id, now, aiChatId);
12575
12597
  return {
12576
12598
  id,
12577
- conversationId,
12599
+ aiChatId,
12578
12600
  role,
12579
12601
  content,
12580
12602
  timestamp: now,
@@ -12585,21 +12607,21 @@ var AIConversationManager = class {
12585
12607
  tokenUsage
12586
12608
  };
12587
12609
  }
12588
- getMessages(conversationId) {
12589
- return this.getActivePathRows(conversationId).map((row) => this.parseMessageRow(row));
12610
+ getMessages(aiChatId) {
12611
+ return this.getActivePathRows(aiChatId).map((row) => this.parseMessageRow(row));
12590
12612
  }
12591
12613
  deleteMessage(messageId) {
12592
12614
  const db = this.getDb();
12593
12615
  const result = db.prepare("DELETE FROM ai_message WHERE id = ?").run(messageId);
12594
12616
  return result.changes > 0;
12595
12617
  }
12596
- deleteMessagesFrom(conversationId, messageId) {
12618
+ deleteMessagesFrom(aiChatId, messageId) {
12597
12619
  const db = this.getDb();
12598
12620
  const target = this.getMessageRow(messageId);
12599
- if (!target || target.conversationId !== conversationId) {
12600
- throw new Error("Message not found in conversation");
12621
+ if (!target || target.aiChatId !== aiChatId) {
12622
+ throw new Error("Message not found in AI chat");
12601
12623
  }
12602
- const activePath = this.getActivePathRows(conversationId);
12624
+ const activePath = this.getActivePathRows(aiChatId);
12603
12625
  const targetIndex = activePath.findIndex((row) => row.id === messageId);
12604
12626
  if (targetIndex < 0) {
12605
12627
  throw new Error("Message not on active path");
@@ -12609,19 +12631,15 @@ var AIConversationManager = class {
12609
12631
  db.prepare(`DELETE FROM ai_message WHERE id IN (${placeholders})`).run(...idsToDelete);
12610
12632
  const newLeafId = targetIndex > 0 ? activePath[targetIndex - 1].id : null;
12611
12633
  const now = Math.floor(Date.now() / 1e3);
12612
- db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12613
- newLeafId,
12614
- now,
12615
- conversationId
12616
- );
12634
+ db.prepare("UPDATE ai_chat SET active_message_id = ?, updated_at = ? WHERE id = ?").run(newLeafId, now, aiChatId);
12617
12635
  }
12618
- forkConversation(sourceConversationId, upToMessageId, title) {
12636
+ forkAIChat(sourceAIChatId, upToMessageId, title) {
12619
12637
  const db = this.getDb();
12620
- const source = this.getConversation(sourceConversationId);
12638
+ const source = this.getAIChat(sourceAIChatId);
12621
12639
  if (!source) {
12622
- throw new Error("Source conversation not found");
12640
+ throw new Error("Source AI chat not found");
12623
12641
  }
12624
- const activePath = this.getActivePathRows(sourceConversationId);
12642
+ const activePath = this.getActivePathRows(sourceAIChatId);
12625
12643
  const cutIndex = activePath.findIndex((row) => row.id === upToMessageId);
12626
12644
  if (cutIndex < 0) {
12627
12645
  throw new Error("Message not on active path");
@@ -12631,7 +12649,7 @@ var AIConversationManager = class {
12631
12649
  const newConvId = this.generateId("conv");
12632
12650
  const forkTitle = title || `${source.title || "Untitled"} (fork)`;
12633
12651
  db.prepare(
12634
- `INSERT INTO ai_conversation (id, session_id, title, assistant_id, active_message_id, created_at, updated_at)
12652
+ `INSERT INTO ai_chat (id, session_id, title, assistant_id, active_message_id, created_at, updated_at)
12635
12653
  VALUES (?, ?, ?, ?, NULL, ?, ?)`
12636
12654
  ).run(newConvId, source.sessionId, forkTitle, source.assistantId, now, now);
12637
12655
  const idMap = /* @__PURE__ */ new Map();
@@ -12642,7 +12660,7 @@ var AIConversationManager = class {
12642
12660
  const newParentId = row.parentId ? idMap.get(row.parentId) ?? null : null;
12643
12661
  db.prepare(
12644
12662
  `INSERT INTO ai_message (
12645
- id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12663
+ id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count,
12646
12664
  content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12647
12665
  )
12648
12666
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, 0)`
@@ -12662,7 +12680,7 @@ var AIConversationManager = class {
12662
12680
  lastNewId = newMsgId;
12663
12681
  }
12664
12682
  if (lastNewId) {
12665
- db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?").run(lastNewId, newConvId);
12683
+ db.prepare("UPDATE ai_chat SET active_message_id = ? WHERE id = ?").run(lastNewId, newConvId);
12666
12684
  }
12667
12685
  return {
12668
12686
  id: newConvId,
@@ -12679,46 +12697,46 @@ var AIConversationManager = class {
12679
12697
  const result = db.prepare("UPDATE ai_message SET content = ? WHERE id = ?").run(newContent, messageId);
12680
12698
  if (result.changes === 0) throw new Error("Message not found");
12681
12699
  }
12682
- deleteAndRelinkMessage(conversationId, messageId) {
12700
+ deleteAndRelinkMessage(aiChatId, messageId) {
12683
12701
  const db = this.getDb();
12684
12702
  const target = this.getMessageRow(messageId);
12685
- if (!target || target.conversationId !== conversationId) {
12686
- throw new Error("Message not found in conversation");
12703
+ if (!target || target.aiChatId !== aiChatId) {
12704
+ throw new Error("Message not found in AI chat");
12687
12705
  }
12688
- db.prepare("UPDATE ai_message SET parent_id = ? WHERE parent_id = ? AND conversation_id = ?").run(
12706
+ db.prepare("UPDATE ai_message SET parent_id = ? WHERE parent_id = ? AND ai_chat_id = ?").run(
12689
12707
  target.parentId,
12690
12708
  messageId,
12691
- conversationId
12709
+ aiChatId
12692
12710
  );
12693
- const conv = this.getConversation(conversationId);
12711
+ const conv = this.getAIChat(aiChatId);
12694
12712
  if (conv?.activeMessageId === messageId) {
12695
12713
  const now = Math.floor(Date.now() / 1e3);
12696
- db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12714
+ db.prepare("UPDATE ai_chat SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12697
12715
  target.parentId,
12698
12716
  now,
12699
- conversationId
12717
+ aiChatId
12700
12718
  );
12701
12719
  }
12702
12720
  db.prepare("DELETE FROM ai_message WHERE id = ?").run(messageId);
12703
12721
  }
12704
- insertMessageAfter(conversationId, afterMessageId, role, content, contentBlocks, tokenUsage) {
12722
+ insertMessageAfter(aiChatId, afterMessageId, role, content, contentBlocks, tokenUsage) {
12705
12723
  const db = this.getDb();
12706
12724
  const now = Math.floor(Date.now() / 1e3);
12707
12725
  const id = this.generateId("msg");
12708
- const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(conversationId) : void 0;
12726
+ const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(aiChatId) : void 0;
12709
12727
  if (pendingDebug) {
12710
- this.pendingDebugContextMap.delete(conversationId);
12728
+ this.pendingDebugContextMap.delete(aiChatId);
12711
12729
  }
12712
- const childRow = db.prepare("SELECT id FROM ai_message WHERE parent_id = ? AND conversation_id = ? LIMIT 1").get(afterMessageId, conversationId);
12730
+ const childRow = db.prepare("SELECT id FROM ai_message WHERE parent_id = ? AND ai_chat_id = ? LIMIT 1").get(afterMessageId, aiChatId);
12713
12731
  db.prepare(
12714
12732
  `INSERT INTO ai_message (
12715
- id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12733
+ id, ai_chat_id, role, content, timestamp, data_keywords, data_message_count,
12716
12734
  content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12717
12735
  )
12718
12736
  VALUES (?, ?, ?, ?, ?, NULL, NULL, ?, ?, ?, ?, ?, 0)`
12719
12737
  ).run(
12720
12738
  id,
12721
- conversationId,
12739
+ aiChatId,
12722
12740
  role,
12723
12741
  content,
12724
12742
  now,
@@ -12730,17 +12748,13 @@ var AIConversationManager = class {
12730
12748
  );
12731
12749
  if (childRow) {
12732
12750
  db.prepare("UPDATE ai_message SET parent_id = ? WHERE id = ?").run(id, childRow.id);
12733
- db.prepare("UPDATE ai_conversation SET updated_at = ? WHERE id = ?").run(now, conversationId);
12751
+ db.prepare("UPDATE ai_chat SET updated_at = ? WHERE id = ?").run(now, aiChatId);
12734
12752
  } else {
12735
- db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12736
- id,
12737
- now,
12738
- conversationId
12739
- );
12753
+ db.prepare("UPDATE ai_chat SET active_message_id = ?, updated_at = ? WHERE id = ?").run(id, now, aiChatId);
12740
12754
  }
12741
12755
  return {
12742
12756
  id,
12743
- conversationId,
12757
+ aiChatId,
12744
12758
  role,
12745
12759
  content,
12746
12760
  timestamp: now,
@@ -12749,8 +12763,8 @@ var AIConversationManager = class {
12749
12763
  tokenUsage
12750
12764
  };
12751
12765
  }
12752
- getConversationTokenUsage(conversationId) {
12753
- const rows = this.getActivePathRows(conversationId);
12766
+ getAIChatTokenUsage(aiChatId) {
12767
+ const rows = this.getActivePathRows(aiChatId);
12754
12768
  const result = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
12755
12769
  for (const row of rows) {
12756
12770
  if (row.tokenUsage) {
@@ -12763,8 +12777,8 @@ var AIConversationManager = class {
12763
12777
  return result;
12764
12778
  }
12765
12779
  // ==================== Debug context ====================
12766
- setPendingDebugContext(conversationId, debugContext) {
12767
- this.pendingDebugContextMap.set(conversationId, debugContext);
12780
+ setPendingDebugContext(aiChatId, debugContext) {
12781
+ this.pendingDebugContextMap.set(aiChatId, debugContext);
12768
12782
  }
12769
12783
  setDebugContext(messageId, debugContext) {
12770
12784
  const db = this.getDb();
@@ -12776,8 +12790,8 @@ var AIConversationManager = class {
12776
12790
  return result.changes;
12777
12791
  }
12778
12792
  // ==================== Agent 专用 ====================
12779
- getHistoryForAgent(conversationId, maxMessages, leafMessageId) {
12780
- const messages = this.getActivePathRows(conversationId, leafMessageId).map((row) => this.parseMessageRow(row));
12793
+ getHistoryForAgent(aiChatId, maxMessages, leafMessageId) {
12794
+ const messages = this.getActivePathRows(aiChatId, leafMessageId).map((row) => this.parseMessageRow(row));
12781
12795
  const validMessages = messages.filter(
12782
12796
  (m) => (m.role === "user" || m.role === "assistant" || m.role === "summary") && m.content?.trim()
12783
12797
  );
@@ -12795,8 +12809,8 @@ var AIConversationManager = class {
12795
12809
  );
12796
12810
  const bufferBoundary = metaBlock?.bufferBoundaryTimestamp;
12797
12811
  if (!metaBlock) {
12798
- this.logger.warn("Conversations", "summary message missing summary_meta; agent context will be summary-only", {
12799
- conversationId,
12812
+ this.logger.warn("AIChats", "summary message missing summary_meta; agent context will be summary-only", {
12813
+ aiChatId,
12800
12814
  messageId: summaryMsg.id
12801
12815
  });
12802
12816
  }
@@ -12819,7 +12833,7 @@ var AIConversationManager = class {
12819
12833
  return result;
12820
12834
  }
12821
12835
  // ==================== Summary / 压缩专用 ====================
12822
- addSummaryMessage(conversationId, content, meta) {
12836
+ addSummaryMessage(aiChatId, content, meta) {
12823
12837
  const contentBlocks = [
12824
12838
  {
12825
12839
  type: "summary_meta",
@@ -12827,28 +12841,28 @@ var AIConversationManager = class {
12827
12841
  compressedMessageCount: meta.compressedMessageCount
12828
12842
  }
12829
12843
  ];
12830
- return this.addMessage(conversationId, "summary", content, void 0, void 0, contentBlocks);
12844
+ return this.addMessage(aiChatId, "summary", content, void 0, void 0, contentBlocks);
12831
12845
  }
12832
- getLatestSummary(conversationId) {
12833
- const row = [...this.getActivePathRows(conversationId)].reverse().find((message) => message.role === "summary");
12846
+ getLatestSummary(aiChatId) {
12847
+ const row = [...this.getActivePathRows(aiChatId)].reverse().find((message) => message.role === "summary");
12834
12848
  return row ? this.parseMessageRow(row) : null;
12835
12849
  }
12836
- getMessagesAfterSummary(conversationId, summaryTimestamp) {
12837
- 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 }));
12850
+ getMessagesAfterSummary(aiChatId, summaryTimestamp) {
12851
+ 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 }));
12838
12852
  }
12839
- getAllUserAssistantMessages(conversationId) {
12840
- return this.getActivePathRows(conversationId).filter((row) => row.role === "user" || row.role === "assistant").map((row) => ({ role: row.role, content: row.content, timestamp: row.timestamp }));
12853
+ getAllUserAssistantMessages(aiChatId) {
12854
+ return this.getActivePathRows(aiChatId).filter((row) => row.role === "user" || row.role === "assistant").map((row) => ({ role: row.role, content: row.content, timestamp: row.timestamp }));
12841
12855
  }
12842
- getMessageCountAfterSummary(conversationId) {
12843
- const summary = this.getLatestSummary(conversationId);
12856
+ getMessageCountAfterSummary(aiChatId) {
12857
+ const summary = this.getLatestSummary(aiChatId);
12844
12858
  if (!summary) {
12845
- return this.getActivePathRows(conversationId).filter((row) => row.role === "user" || row.role === "assistant").length;
12859
+ return this.getActivePathRows(aiChatId).filter((row) => row.role === "user" || row.role === "assistant").length;
12846
12860
  }
12847
12861
  const metaBlock = summary.contentBlocks?.find(
12848
12862
  (b) => b.type === "summary_meta"
12849
12863
  );
12850
12864
  const boundary = metaBlock?.bufferBoundaryTimestamp ?? summary.timestamp;
12851
- return this.getActivePathRows(conversationId).filter(
12865
+ return this.getActivePathRows(aiChatId).filter(
12852
12866
  (row) => row.timestamp >= boundary && (row.role === "user" || row.role === "assistant")
12853
12867
  ).length;
12854
12868
  }
@@ -13316,7 +13330,7 @@ function parseAssistantFile(content, filePath) {
13316
13330
  name,
13317
13331
  systemPrompt: body.trim(),
13318
13332
  presetQuestions: parseStringArray(fm.presetQuestions),
13319
- allowedBuiltinTools: parseStringArray(fm.allowedBuiltinTools),
13333
+ allowedBuiltinTools: normalizeBuiltinToolNames(parseStringArray(fm.allowedBuiltinTools)),
13320
13334
  builtinId: typeof fm.builtinId === "string" ? fm.builtinId : void 0,
13321
13335
  applicableChatTypes: parseChatTypes(fm.applicableChatTypes),
13322
13336
  supportedLocales: parseStringArray(fm.supportedLocales)
@@ -13333,7 +13347,7 @@ function serializeAssistant(config) {
13333
13347
  if (config.builtinId) fm.builtinId = config.builtinId;
13334
13348
  if (config.applicableChatTypes?.length) fm.applicableChatTypes = config.applicableChatTypes;
13335
13349
  if (config.supportedLocales?.length) fm.supportedLocales = config.supportedLocales;
13336
- if (config.allowedBuiltinTools?.length) fm.allowedBuiltinTools = config.allowedBuiltinTools;
13350
+ if (config.allowedBuiltinTools?.length) fm.allowedBuiltinTools = normalizeBuiltinToolNames(config.allowedBuiltinTools);
13337
13351
  if (config.presetQuestions?.length) fm.presetQuestions = config.presetQuestions;
13338
13352
  return matter.stringify(`
13339
13353
  ${config.systemPrompt}
@@ -13387,8 +13401,8 @@ var AssistantManager = class {
13387
13401
  }
13388
13402
  // ==================== Init ====================
13389
13403
  init() {
13390
- const { fs: fs39, assistantsDir } = this.deps;
13391
- fs39.ensureDir(assistantsDir);
13404
+ const { fs: fs40, assistantsDir } = this.deps;
13405
+ fs40.ensureDir(assistantsDir);
13392
13406
  const generalCreated = this.ensureGeneralAssistants();
13393
13407
  this.loadAll();
13394
13408
  this.initialized = true;
@@ -13399,26 +13413,26 @@ var AssistantManager = class {
13399
13413
  return { total: this.cache.size, generalCreated };
13400
13414
  }
13401
13415
  ensureGeneralAssistants() {
13402
- const { fs: fs39, assistantsDir } = this.deps;
13416
+ const { fs: fs40, assistantsDir } = this.deps;
13403
13417
  let anyCreated = false;
13404
13418
  for (const id of this.generalIds) {
13405
13419
  const config = this.getBuiltinConfig(id);
13406
13420
  if (!config) continue;
13407
- const filePath = fs39.joinPath(assistantsDir, `${id}.md`);
13408
- if (fs39.fileExists(filePath)) continue;
13409
- fs39.writeFile(filePath, serializeAssistant({ ...config, builtinId: config.id }));
13421
+ const filePath = fs40.joinPath(assistantsDir, `${id}.md`);
13422
+ if (fs40.fileExists(filePath)) continue;
13423
+ fs40.writeFile(filePath, serializeAssistant({ ...config, builtinId: config.id }));
13410
13424
  anyCreated = true;
13411
13425
  }
13412
13426
  return anyCreated;
13413
13427
  }
13414
13428
  loadAll() {
13415
- const { fs: fs39, assistantsDir } = this.deps;
13429
+ const { fs: fs40, assistantsDir } = this.deps;
13416
13430
  this.cache.clear();
13417
- const files = fs39.listFiles(assistantsDir, ".md");
13431
+ const files = fs40.listFiles(assistantsDir, ".md");
13418
13432
  for (const file of files) {
13419
13433
  try {
13420
- const filePath = fs39.joinPath(assistantsDir, file);
13421
- const content = fs39.readFile(filePath);
13434
+ const filePath = fs40.joinPath(assistantsDir, file);
13435
+ const content = fs40.readFile(filePath);
13422
13436
  const config = parseAssistantFile(content, filePath);
13423
13437
  if (config) {
13424
13438
  this.cache.set(config.id, config);
@@ -13704,21 +13718,21 @@ var SkillManagerCore = class {
13704
13718
  }
13705
13719
  // ==================== Init ====================
13706
13720
  init() {
13707
- const { fs: fs39, skillsDir } = this.deps;
13708
- fs39.ensureDir(skillsDir);
13721
+ const { fs: fs40, skillsDir } = this.deps;
13722
+ fs40.ensureDir(skillsDir);
13709
13723
  this.loadAll();
13710
13724
  this.initialized = true;
13711
13725
  this.deps.logger?.info("SkillManager", "Initialized", { total: this.cache.size });
13712
13726
  return { total: this.cache.size };
13713
13727
  }
13714
13728
  loadAll() {
13715
- const { fs: fs39, skillsDir } = this.deps;
13729
+ const { fs: fs40, skillsDir } = this.deps;
13716
13730
  this.cache.clear();
13717
- const files = fs39.listFiles(skillsDir, ".md");
13731
+ const files = fs40.listFiles(skillsDir, ".md");
13718
13732
  for (const file of files) {
13719
13733
  try {
13720
- const filePath = fs39.joinPath(skillsDir, file);
13721
- const content = fs39.readFile(filePath);
13734
+ const filePath = fs40.joinPath(skillsDir, file);
13735
+ const content = fs40.readFile(filePath);
13722
13736
  const def = parseSkillFile(content, filePath);
13723
13737
  if (def) {
13724
13738
  this.cache.set(def.id, def);
@@ -14017,10 +14031,38 @@ function shouldUseChartCapabilityForMessage(message) {
14017
14031
  normalized
14018
14032
  );
14019
14033
  }
14034
+ function shouldOfferChartCapabilityForAnalyticalMessage(message) {
14035
+ const normalized = message.trim().toLowerCase();
14036
+ if (!normalized) return false;
14037
+ if (shouldUseChartCapabilityForMessage(normalized)) return true;
14038
+ return /(?:趋势|变化|走势|排名|排行|前\s*\d+|top\s*\d*|最多|最少|高频|分布|占比|比例|构成|份额|活跃度|热度|按.{0,8}统计|统计.{0,8}按|trend|ranking|rank|top\s*\d*|distribution|ratio|share|percentage|breakdown)/i.test(
14039
+ normalized
14040
+ );
14041
+ }
14042
+ function getChartPlannerCapabilityForMessage(options) {
14043
+ const availableToolSet = new Set(options.availableTools);
14044
+ if (!availableToolSet.has("get_schema") || !availableToolSet.has("render_chart")) {
14045
+ return null;
14046
+ }
14047
+ const chartAutoMode = options.chartAutoMode ?? "suggest";
14048
+ const shouldOffer = chartAutoMode === "explicit" ? shouldUseChartCapabilityForMessage(options.userMessage) : shouldOfferChartCapabilityForAnalyticalMessage(options.userMessage);
14049
+ if (!shouldOffer) {
14050
+ return null;
14051
+ }
14052
+ const isZh = (options.locale ?? "zh-CN").startsWith("zh");
14053
+ return {
14054
+ id: "chart_generation",
14055
+ label: isZh ? "\u56FE\u8868\u751F\u6210" : "Chart generation",
14056
+ tools: ["get_schema", "render_chart"],
14057
+ guidance: isZh ? "\u7528\u6237\u660E\u786E\u8981\u6C42\u56FE\u8868\uFF0C\u6216\u8D8B\u52BF\u3001\u6392\u540D\u3001\u5206\u5E03\u3001\u5360\u6BD4\u7B49\u5206\u6790\u7528\u56FE\u66F4\u6E05\u695A\u65F6\uFF0C\u53EF\u4EE5\u5728\u8BA1\u5212\u4E2D\u52A0\u5165\u6700\u591A\u4E00\u5F20\u56FE\u8868\u6B65\u9AA4\uFF1B\u5FC5\u987B\u5148\u7528 get_schema \u786E\u8BA4 schema\uFF0C\u518D\u7528 render_chart \u751F\u6210\u56FE\u8868\uFF1B\u4E0D\u8981\u8F93\u51FA\u7ED8\u56FE\u4EE3\u7801\u3002" : "When the user explicitly asks for charts, or when trends, rankings, distributions, or ratios are clearer visually, you may add at most one chart step to the plan; call get_schema before render_chart; do not output chart-rendering code."
14058
+ };
14059
+ }
14020
14060
  function resolveChartRuntimeForRequest(options) {
14021
14061
  const locale = options.locale ?? "zh-CN";
14022
14062
  const isExplicitChartSkill = options.skillId === CHART_CAPABILITY_SKILL_ID;
14023
- const isAutoChartSkill = options.enableAutoDetection === true && !options.skillId && shouldUseChartCapabilityForMessage(options.userMessage ?? "");
14063
+ const allowAnalyticalAutoDetection = options.chartAutoMode !== void 0 ? options.chartAutoMode === "aggressive" : options.enableAnalyticalAutoDetection === true;
14064
+ const shouldAutoDetectChart = shouldUseChartCapabilityForMessage(options.userMessage ?? "") || allowAnalyticalAutoDetection && shouldOfferChartCapabilityForAnalyticalMessage(options.userMessage ?? "");
14065
+ const isAutoChartSkill = options.enableAutoDetection === true && !options.skillId && shouldAutoDetectChart;
14024
14066
  if (!isExplicitChartSkill && !isAutoChartSkill) {
14025
14067
  return { isChartCapability: false };
14026
14068
  }
@@ -14066,23 +14108,28 @@ function getSkillConfigWithBuiltinChart(id, locale = "zh-CN", getSkillConfig) {
14066
14108
  }
14067
14109
  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
14068
14110
 
14069
- \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
14111
+ \u4F60\u5FC5\u987B\u901A\u8FC7 render_chart \u5DE5\u5177\u751F\u6210\u56FE\u8868\u3002\u4E0D\u8981\u8F93\u51FA HTML\u3001JavaScript\u3001SVG\u3001Canvas\u3001ECharts option\u3001Markdown \u56FE\u7247\u94FE\u63A5\uFF08\u5982 ![\u56FE\u8868](chart1.png)\uFF09\u6216\u4EFB\u4F55\u6E32\u67D3\u4EE3\u7801\u3002
14070
14112
 
14071
14113
  \u786C\u6027\u89C4\u5219\uFF1A
14072
14114
  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
14073
- 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
14115
+ 2. \u201C\u6700\u8FD1\u201D\u201C\u8FC7\u53BB N \u5929/\u5E74\u201D\u7B49\u76F8\u5BF9\u65F6\u95F4\u4EE5\u7CFB\u7EDF\u63D0\u793A\u4E2D\u7684\u771F\u5B9E\u5F53\u524D\u65E5\u671F\u4E3A\u57FA\u51C6\uFF1B\u5982\u679C\u542F\u52A8\u4E0A\u4E0B\u6587\u663E\u793A\u6570\u636E\u5E93\u8986\u76D6\u4E0D\u8DB3\uFF0C\u53EA\u5206\u6790\u7528\u6237\u65F6\u95F4\u8303\u56F4\u4E0E\u6570\u636E\u5E93\u8986\u76D6\u8303\u56F4\u7684\u4EA4\u96C6\u5E76\u8BF4\u660E\u7F3A\u53E3\u3002
14074
14116
  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
14075
14117
  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
14076
14118
  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
14077
- 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
14078
- 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
14119
+ 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\u539F\u751F\u56FE\u8868\u548C\u7B80\u77ED\u7ED3\u8BBA\uFF0C\u7981\u6B62\u7528 chart1.png\u3001chart2.png \u7B49\u6587\u4EF6\u540D\u5F15\u7528\u56FE\u8868\u3002
14120
+ 7. \u8C03\u7528 render_chart \u65F6\uFF0CChatLab \u4F1A\u7ACB\u5373\u5728\u8BE5\u4F4D\u7F6E\u63D2\u5165\u539F\u751F\u56FE\u8868\uFF1B\u9700\u8981\u56FE\u8868\u5C31\u76F4\u63A5\u8C03\u7528\u5DE5\u5177\uFF0C\u4E0D\u8981\u5199 SQL \u6CE8\u91CA\u3001Markdown \u56FE\u7247\u6216\u201C\u5DF2\u751F\u6210\u56FE\u8868/\u89C1\u4E0A\u56FE\u201D\u8FD9\u7C7B\u6587\u672C\u5360\u4F4D\u3002
14121
+ 8. \u7528\u6237\u8981\u6C42\u591A\u5F20\u56FE\u65F6\uFF0C\u56FE\u8868\u5E94\u5206\u6563\u51FA\u73B0\u5728\u76F8\u5173\u603B\u7ED3\u6216\u89E3\u91CA\u9644\u8FD1\uFF1B\u4E0D\u8981\u628A\u6240\u6709\u56FE\u96C6\u4E2D\u751F\u6210\u540E\u518D\u5728\u6700\u540E\u7528\u6587\u5B57\u5F15\u7528\u3002
14122
+ 9. \u4E3A\u56FE\u8868\u51C6\u5907\u6570\u636E\u65F6\uFF0C\u4F18\u5148\u8C03\u7528\u9AD8\u5C42\u5DE5\u5177\uFF08\u5982 get_time_stats\u3001member_stats\uFF09\u800C\u975E\u81EA\u5DF1\u5199 SQL\uFF1B\u53EA\u6709\u9AD8\u5C42\u5DE5\u5177\u65E0\u6CD5\u8986\u76D6\u6240\u9700\u7B5B\u9009\u6216\u805A\u5408\u65F6\u624D\u7528 sql\u3002render_chart \u4E0D\u8981\u7528\u4E8E\u5143\u6570\u636E\u63A2\u6D4B\u3001\u6807\u91CF\u67E5\u8BE2\u3001\u5355\u884C\u65E5\u671F\u5B57\u7B26\u4E32\u6216\u7EAF\u8BF4\u660E\u6027\u7ED3\u679C\u3002
14123
+ 10. render_chart \u7684\u5DE5\u5177\u7ED3\u679C\u4F1A\u5305\u542B\u6570\u636E\u9884\u89C8\uFF08Data preview\uFF09\uFF1B\u7528\u5B83\u5206\u6790\u5CF0\u503C\u3001\u4F4E\u8C37\u548C\u5DEE\u5F02\uFF0C\u4E0D\u8981\u4E3A\u4E86\u8BFB\u53D6\u6570\u503C\u91CD\u590D\u751F\u6210\u7B49\u4EF7\u56FE\u8868\u3002
14124
+ 11. \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
14079
14125
 
14080
14126
  \u5DE5\u4F5C\u6D41\u7A0B\uFF1A
14081
14127
  1. \u8C03\u7528 get_schema\u3002
14082
- 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
14083
- 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
14084
- 4. \u8C03\u7528 render_chart\uFF0C\u63D0\u4EA4\u53EA\u8BFB SQL \u548C ChartSpec v1\u3002
14085
- 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
14128
+ 2. \u6839\u636E schema\u3001\u7528\u6237\u8981\u6C42\u3001\u7CFB\u7EDF\u5F53\u524D\u65E5\u671F\u548C\u542F\u52A8\u4E0A\u4E0B\u6587\u786E\u5B9A\u7EDF\u8BA1\u5BF9\u8C61\u3001\u65F6\u95F4\u8303\u56F4\u3001\u7EF4\u5EA6\u3001\u6307\u6807\u548C\u56FE\u8868\u7C7B\u578B\u3002
14129
+ 3. \u5982\u9700\u8C03\u67E5\u4E8B\u5B9E\u3001\u5EFA\u7ACB\u8BDD\u9898\u5730\u56FE\u3001\u7B5B\u9009\u6210\u5458\u6216\u786E\u8BA4\u7EDF\u8BA1\u53E3\u5F84\uFF0C\u5148\u7528\u53EF\u7528\u7684\u666E\u901A\u5206\u6790\u5DE5\u5177\u5B8C\u6210\uFF1B\u4E0D\u8981\u4E3A\u4E86\u8C03\u67E5\u800C\u751F\u6210\u4E34\u65F6\u56FE\u8868\u3002
14130
+ 4. \u51C6\u5907\u56FE\u8868\u6570\u636E\u2014\u2014\u5148\u95EE\u81EA\u5DF1\uFF1A\u54EA\u4E2A\u53EF\u7528\u7684\u9AD8\u5C42\u5DE5\u5177\u80FD\u76F4\u63A5\u63D0\u4F9B\u8FD9\u5F20\u56FE\u6240\u9700\u7684\u6570\u636E\uFF1Fget_time_stats \u80FD\u7ED9\u65F6\u95F4\u5206\u5E03\u3001member_stats \u80FD\u7ED9\u6210\u5458\u6392\u540D\uFF1B\u80FD\u7528\u5C31\u8C03\u7528\uFF0C\u5C06 data \u6570\u7EC4\u4F5C\u4E3A rows \u4F20\u5165 render_chart\u3002\u53EA\u6709\u5728\u9AD8\u5C42\u5DE5\u5177\u65E0\u6CD5\u8986\u76D6\u6240\u9700\u7B5B\u9009\u6216\u805A\u5408\u65F6\uFF0C\u624D\u7F16\u5199 SQL\u3002
14131
+ 5. \u5728\u5E0C\u671B\u56FE\u8868\u51FA\u73B0\u7684\u4F4D\u7F6E\u8C03\u7528 render_chart\uFF0C\u63D0\u4EA4\u6570\u636E\uFF08rows \u6216 sql\uFF09\u548C ChartSpec v1\u3002
14132
+ 6. \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\uFF0C\u5E76\u628A\u6BCF\u5F20\u56FE\u653E\u5728\u6700\u76F8\u5173\u7684\u6BB5\u843D\u9644\u8FD1\u3002
14086
14133
 
14087
14134
  \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
14088
14135
 
@@ -14104,23 +14151,28 @@ ChartSpec \u793A\u4F8B\uFF1A
14104
14151
  \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`;
14105
14152
  var EN_PROMPT = `You are the ChatLab Chart Assistant. In this turn, the user wants one or more charts embedded naturally in the answer.
14106
14153
 
14107
- You must generate charts through the render_chart tool. Do not output HTML, JavaScript, SVG, Canvas, ECharts options, or rendering code.
14154
+ You must generate charts through the render_chart tool. Do not output HTML, JavaScript, SVG, Canvas, ECharts options, Markdown image links such as ![Chart](chart1.png), or rendering code.
14108
14155
 
14109
14156
  Hard rules:
14110
14157
  1. Always call get_schema before writing the first SQL query. Do not guess table names, field names, or timestamp fields.
14111
- 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.
14158
+ 2. For relative ranges like "recent" or "past N days/years", use the real current date from the system prompt as the baseline. If startup context shows incomplete database coverage, analyze only the intersection between the requested range and database coverage, and state the gap.
14112
14159
  3. ChatLab message.ts is a Unix timestamp in seconds. Use date(ts, 'unixepoch', 'localtime'); never write ts/1000.
14113
14160
  4. For "latest N members" or "recent N people", first define and select those N members from the data, then count only those members.
14114
14161
  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.
14115
- 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.
14116
- 7. Explain only trends, peaks, lows, differences, and synchrony directly supported by chart data. Do not make exaggerated personality or relationship claims.
14162
+ 6. Do not include failed SQL attempts, schema exploration, or retry reasoning in the final answer. Final answer should contain only native charts and a short conclusion; never reference charts with filenames like chart1.png or chart2.png.
14163
+ 7. When you call render_chart, ChatLab immediately inserts the native chart at that position. If a chart is needed, call the tool directly; do not write SQL-comment placeholders, Markdown images, or text such as "generated chart" or "see chart above".
14164
+ 8. If the user asks for multiple charts, place each chart near the relevant summary or explanation. Do not generate all charts together and then reference them later with text.
14165
+ 9. When preparing chart data, prefer calling a high-level tool (such as get_time_stats, member_stats) over writing SQL; only use sql when no high-level tool can satisfy the required filters or aggregations. Do not use render_chart for metadata inspection, scalar queries, single-row date strings, or purely explanatory results.
14166
+ 10. render_chart tool results include a Data preview; use it to identify peaks, lows, and differences. Do not generate equivalent charts again just to read values.
14167
+ 11. Explain only trends, peaks, lows, differences, and synchrony directly supported by chart data. Do not make exaggerated personality or relationship claims.
14117
14168
 
14118
14169
  Workflow:
14119
14170
  1. Call get_schema.
14120
- 2. Derive the target members, time range, dimensions, metrics, and chart type from schema and the user request.
14121
- 3. When needed, use read-only SQL inside render_chart to select members, determine time range, aggregate data, and fill zero rows.
14122
- 4. Call render_chart with read-only SQL and ChartSpec v1.
14123
- 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.
14171
+ 2. Derive the target members, time range, dimensions, metrics, and chart type from schema, the user request, system current date, and startup context.
14172
+ 3. If you need to investigate facts, build a topic map, select members, or confirm statistical definitions, use available ordinary analysis tools first; do not generate temporary charts for investigation.
14173
+ 4. Prepare chart data \u2014 ask yourself first: which available high-level tool can directly provide the data this chart needs? get_time_stats covers time distributions; member_stats covers member rankings. If one fits, call it and pass its data array as rows to render_chart. Only write SQL when no high-level tool can cover the required filters or aggregations.
14174
+ 5. Call render_chart at the position where the chart should appear, with data (rows or sql) and ChartSpec v1.
14175
+ 6. 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 and place each chart near its most relevant section.
14124
14176
 
14125
14177
  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.
14126
14178
 
@@ -14209,23 +14261,23 @@ var defaultLogger3 = {
14209
14261
  error: () => {
14210
14262
  }
14211
14263
  };
14212
- async function checkAndCompress(conversationId, config, systemPrompt, llmAdapter, convManager, logger = defaultLogger3) {
14264
+ async function checkAndCompress(aiChatId, config, systemPrompt, llmAdapter, convManager, logger = defaultLogger3) {
14213
14265
  if (!config.enabled) {
14214
14266
  return { compressed: false, reason: "skipped_disabled" };
14215
14267
  }
14216
14268
  try {
14217
14269
  const contextWindow = llmAdapter.contextWindow || DEFAULT_CONTEXT_WINDOW;
14218
14270
  const thresholdTokens = Math.floor(contextWindow * (config.tokenThresholdPercent / 100) * 0.95);
14219
- const summary = convManager.getLatestSummary(conversationId);
14271
+ const summary = convManager.getLatestSummary(aiChatId);
14220
14272
  let messages;
14221
14273
  if (summary) {
14222
14274
  const metaBlock = summary.contentBlocks?.find(
14223
14275
  (b) => b.type === "summary_meta"
14224
14276
  );
14225
14277
  const boundary = metaBlock?.bufferBoundaryTimestamp ?? summary.timestamp;
14226
- messages = convManager.getMessagesAfterSummary(conversationId, boundary - 1);
14278
+ messages = convManager.getMessagesAfterSummary(aiChatId, boundary - 1);
14227
14279
  } else {
14228
- messages = convManager.getAllUserAssistantMessages(conversationId);
14280
+ messages = convManager.getAllUserAssistantMessages(aiChatId);
14229
14281
  }
14230
14282
  const historyForTokenCount = [];
14231
14283
  if (summary) {
@@ -14236,7 +14288,7 @@ async function checkAndCompress(conversationId, config, systemPrompt, llmAdapter
14236
14288
  }
14237
14289
  const currentTokens = countMessagesTokens(historyForTokenCount, systemPrompt);
14238
14290
  logger.info("Compression", `Token check: ${currentTokens} / ${thresholdTokens} (${contextWindow} window)`, {
14239
- conversationId,
14291
+ aiChatId,
14240
14292
  messageCount: messages.length,
14241
14293
  hasSummary: !!summary
14242
14294
  });
@@ -14260,7 +14312,7 @@ async function checkAndCompress(conversationId, config, systemPrompt, llmAdapter
14260
14312
  summaryText = forceTruncate(compressInput, targetTokens);
14261
14313
  }
14262
14314
  const bufferBoundary = bufferMessages.length > 0 ? bufferMessages[0].timestamp : messagesToCompress[messagesToCompress.length - 1].timestamp + 1;
14263
- convManager.addSummaryMessage(conversationId, summaryText, {
14315
+ convManager.addSummaryMessage(aiChatId, summaryText, {
14264
14316
  bufferBoundaryTimestamp: bufferBoundary,
14265
14317
  compressedMessageCount: messagesToCompress.length
14266
14318
  });
@@ -14947,7 +14999,7 @@ import {
14947
14999
  } from "@earendil-works/pi-ai";
14948
15000
 
14949
15001
  // ../../packages/node-runtime/src/ai/agent/constants.ts
14950
- var DEFAULT_MAX_TOOL_ROUNDS = 10;
15002
+ var DEFAULT_MAX_TOOL_ROUNDS = 20;
14951
15003
 
14952
15004
  // ../../packages/node-runtime/src/ai/agent/core.ts
14953
15005
  function createEmptyPiUsage() {
@@ -15028,14 +15080,6 @@ async function runAgentCore(options) {
15028
15080
  return clampThinkingLevel(piModel, level);
15029
15081
  })();
15030
15082
  const finalThinkingLevel = resolvedThinkingLevel ?? (piModel.reasoning ? void 0 : "off");
15031
- console.debug("[agent-core] thinkingLevel resolution:", {
15032
- input: options.thinkingLevel,
15033
- modelId: piModel.id,
15034
- modelReasoning: effectiveModel.reasoning,
15035
- isDefault,
15036
- resolved: resolvedThinkingLevel,
15037
- final: finalThinkingLevel
15038
- });
15039
15083
  const coreAgent = new PiAgentCore({
15040
15084
  initialState: {
15041
15085
  systemPrompt,
@@ -15165,30 +15209,608 @@ async function runAgentCore(options) {
15165
15209
  }
15166
15210
  }
15167
15211
 
15168
- // ../../packages/node-runtime/src/ai/agent/event-handler.ts
15169
- function estimateTokensFromText(text) {
15170
- if (!text) return 0;
15171
- const normalized = text.replace(/\s+/g, " ").trim();
15172
- if (!normalized) return 0;
15173
- const cjkCount = (normalized.match(/[\u3400-\u9fff\uf900-\ufaff]/g) || []).length;
15174
- const latinCount = normalized.length - cjkCount;
15175
- return Math.max(1, Math.ceil(cjkCount * 1.15 + latinCount / 4));
15212
+ // ../../packages/node-runtime/src/ai/agent/data-snapshot.ts
15213
+ function roundShare(value) {
15214
+ if (!Number.isFinite(value)) return 0;
15215
+ return Math.round(value * 10) / 10;
15216
+ }
15217
+ function formatNullableTimestamp(timestamp) {
15218
+ return typeof timestamp === "number" && Number.isFinite(timestamp) ? String(timestamp) : "null";
15219
+ }
15220
+ function formatSharePercent(share) {
15221
+ if (!Number.isFinite(share)) return "0%";
15222
+ const rounded = Math.round(share * 10) / 10;
15223
+ return Number.isInteger(rounded) ? `${rounded}%` : `${rounded.toFixed(1)}%`;
15224
+ }
15225
+ function createDataSnapshotFromOverview(overview) {
15226
+ if (!overview) return void 0;
15227
+ const topMembers = overview.topMembers ?? [];
15228
+ return {
15229
+ version: 2,
15230
+ name: overview.name,
15231
+ platform: overview.platform,
15232
+ type: overview.type,
15233
+ totalMessages: overview.totalMessages,
15234
+ totalMembers: overview.totalMembers,
15235
+ firstMessageTs: overview.firstMessageTs,
15236
+ lastMessageTs: overview.lastMessageTs,
15237
+ activeMemberHints: topMembers.slice(0, 10).map((member) => ({
15238
+ memberId: member.id,
15239
+ displayName: member.name,
15240
+ messageCount: member.count,
15241
+ share: overview.totalMessages > 0 ? roundShare(member.count / overview.totalMessages * 100) : 0
15242
+ })),
15243
+ segmentSummaries: {
15244
+ availableCount: overview.summaryCount ?? 0
15245
+ }
15246
+ };
15176
15247
  }
15177
- function extractMessageText(message) {
15178
- if (message.role === "user") {
15179
- if (typeof message.content === "string") return message.content;
15180
- return message.content.map((item) => {
15181
- if (item.type === "text") return item.text;
15182
- if (item.type === "image") return "[image]";
15183
- return "";
15184
- }).join("\n");
15185
- }
15186
- if (message.role === "assistant") {
15187
- return message.content.map((item) => {
15188
- if (item.type === "text") return item.text;
15189
- if (item.type === "thinking") return item.thinking;
15190
- if (item.type === "toolCall") return `${item.name} ${JSON.stringify(item.arguments || {})}`;
15191
- return "";
15248
+ function formatDataSnapshotForRouter(dataSnapshot) {
15249
+ if (!dataSnapshot) return "(none)";
15250
+ return [
15251
+ `name: ${dataSnapshot.name}`,
15252
+ `platform: ${dataSnapshot.platform}`,
15253
+ `type: ${dataSnapshot.type}`,
15254
+ `total_messages: ${dataSnapshot.totalMessages}`,
15255
+ `total_members: ${dataSnapshot.totalMembers}`,
15256
+ `first_message_ts: ${formatNullableTimestamp(dataSnapshot.firstMessageTs)}`,
15257
+ `last_message_ts: ${formatNullableTimestamp(dataSnapshot.lastMessageTs)}`,
15258
+ `segment_summaries_available: ${dataSnapshot.segmentSummaries?.availableCount ?? 0}`,
15259
+ `active_member_hint_count: ${dataSnapshot.activeMemberHints?.length ?? 0}`
15260
+ ].join("\n");
15261
+ }
15262
+ function formatDataSnapshotForPlanner(dataSnapshot) {
15263
+ if (!dataSnapshot) return "(none)";
15264
+ const memberHints = dataSnapshot.activeMemberHints ?? [];
15265
+ const memberHintLines = memberHints.length > 0 ? memberHints.map(
15266
+ (member, index) => `${index + 1}. member_id=${member.memberId} | display_name=${member.displayName} | messages=${member.messageCount} | share=${formatSharePercent(member.share)}`
15267
+ ).join("\n") : "(none)";
15268
+ return `${formatDataSnapshotForRouter(dataSnapshot)}
15269
+ active_member_hints:
15270
+ ${memberHintLines}
15271
+ notes:
15272
+ - member_id is a tool lookup hint; display_name may not be unique.
15273
+ - Active member hints reflect historical total message volume, not recent activity.
15274
+ - Use real current date for relative ranges; database bounds only describe data coverage.
15275
+ - Do not plan a tool call only to rediscover min/max timestamp.
15276
+ - When using default recent-day tools, choose a range that intersects database bounds instead of probing an empty real-current-date window first.`;
15277
+ }
15278
+
15279
+ // ../../packages/node-runtime/src/ai/agent/router.ts
15280
+ import {
15281
+ completeSimple as completeSimple2
15282
+ } from "@earendil-works/pi-ai";
15283
+ var DEFAULT_LLM_FALLBACK_CONFIDENCE_THRESHOLD = 0.6;
15284
+ function clampConfidence(value) {
15285
+ if (!Number.isFinite(value)) return 0;
15286
+ return Math.max(0, Math.min(1, value));
15287
+ }
15288
+ function normalizeText(text) {
15289
+ return text.trim().replace(/\s+/g, " ");
15290
+ }
15291
+ function matchAny(text, patterns) {
15292
+ return patterns.some((pattern) => pattern.test(text));
15293
+ }
15294
+ function isRoute(value) {
15295
+ return value === "direct_response" || value === "tool_assisted" || value === "planned_execution";
15296
+ }
15297
+ function resultToText(result) {
15298
+ return typeof result === "string" ? { text: result } : result;
15299
+ }
15300
+ function extractJsonObject3(text) {
15301
+ const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
15302
+ if (fenced?.[1]) return fenced[1].trim();
15303
+ const start = text.indexOf("{");
15304
+ const end = text.lastIndexOf("}");
15305
+ if (start >= 0 && end > start) return text.slice(start, end + 1);
15306
+ return text.trim();
15307
+ }
15308
+ function parseLlmDecision(rawText, usage) {
15309
+ try {
15310
+ const parsed = JSON.parse(extractJsonObject3(rawText));
15311
+ if (!isRoute(parsed.route)) return null;
15312
+ return {
15313
+ route: parsed.route,
15314
+ confidence: clampConfidence(typeof parsed.confidence === "number" ? parsed.confidence : 0.5),
15315
+ reason: typeof parsed.reason === "string" && parsed.reason.trim() ? parsed.reason.trim() : "LLM route decision.",
15316
+ source: "llm",
15317
+ usage
15318
+ };
15319
+ } catch {
15320
+ return null;
15321
+ }
15322
+ }
15323
+ function buildLlmRouterPrompt(input, ruleDecision2) {
15324
+ const toolNames = input.availableTools?.length ? input.availableTools.join(", ") : "(none)";
15325
+ const dataSnapshot = formatDataSnapshotForRouter(input.dataSnapshot);
15326
+ return `Classify the user's ChatLab request into exactly one route.
15327
+
15328
+ Routes:
15329
+ - direct_response: no local chat data/tools needed.
15330
+ - tool_assisted: local chat data/tools needed, but the task is simple.
15331
+ - planned_execution: complex analysis, multi-step evidence gathering, retries, long-range comparison, relationship analysis, or insufficient-evidence handling.
15332
+
15333
+ Return strict JSON only:
15334
+ {"route":"direct_response|tool_assisted|planned_execution","confidence":0.0,"reason":"short reason"}
15335
+
15336
+ Context:
15337
+ - locale: ${input.locale}
15338
+ - chatType: ${input.chatType}
15339
+ - availableTools: ${toolNames}
15340
+ - dataSnapshot: ${dataSnapshot}
15341
+ - assistantSummary: ${input.assistantSummary ?? "(none)"}
15342
+ - skillSummary: ${input.skillSummary ?? "(none)"}
15343
+ - recentIntentSummary: ${input.recentIntentSummary ?? "(none)"}
15344
+ - ruleDecision: ${ruleDecision2.route}, confidence=${ruleDecision2.confidence}, reason=${ruleDecision2.reason}
15345
+
15346
+ User request:
15347
+ ${input.userMessage}`;
15348
+ }
15349
+ function directRuleDecision(text) {
15350
+ if (matchAny(text, [/不用查聊天记录|不用查询|不用查数据|不用看本地数据/i, /润色|改写|翻译|polish|rewrite|translate/i])) {
15351
+ return {
15352
+ route: "direct_response",
15353
+ confidence: 0.95,
15354
+ reason: "User explicitly asked for a non-data response.",
15355
+ source: "rule"
15356
+ };
15357
+ }
15358
+ if (matchAny(text, [/什么是|解释一下|概念|区别|explain|briefly explain|why .*function calling|react/i])) {
15359
+ return {
15360
+ route: "direct_response",
15361
+ confidence: 0.86,
15362
+ reason: "Conceptual explanation without local chat-data dependency.",
15363
+ source: "rule"
15364
+ };
15365
+ }
15366
+ if (matchAny(text, [/日志在哪里|怎么配置|设置|帮助|help|where .*log/i])) {
15367
+ return {
15368
+ route: "direct_response",
15369
+ confidence: 0.84,
15370
+ reason: "Help or configuration request does not require chat tools.",
15371
+ source: "rule"
15372
+ };
15373
+ }
15374
+ if (matchAny(text, [/你觉得|闲聊|随便聊聊|建议我|should i/i])) {
15375
+ return {
15376
+ route: "direct_response",
15377
+ confidence: 0.78,
15378
+ reason: "Conversational request without clear data dependency.",
15379
+ source: "rule"
15380
+ };
15381
+ }
15382
+ return null;
15383
+ }
15384
+ function plannedRuleDecision(text) {
15385
+ if (matchAny(text, [/如果.*找不到.*继续查|换几个相关|换几个.*继续|search.*if.*not.*found/i])) {
15386
+ return {
15387
+ route: "planned_execution",
15388
+ confidence: 0.88,
15389
+ reason: "Request asks for search failure recovery and retry strategy.",
15390
+ source: "rule"
15391
+ };
15392
+ }
15393
+ const dimensions = [
15394
+ matchAny(text, [/分析|复盘|总结.*规律|趋势|变化|演变|compare|comparison/i]),
15395
+ matchAny(text, [
15396
+ /过去一年|最近一年|近一年|过去.*年|最近半年|今年|去年|上半年|下半年|长期|按季度|按月份|first half|second half/i
15397
+ ]),
15398
+ matchAny(text, [/关系|互动|影响力|核心成员|最强|最活跃|被忽略|主动|回应|turning point|转折点/i]),
15399
+ matchAny(text, [/证据|举出|例子|关键分歧|主要矛盾|evidence/i]),
15400
+ matchAny(text, [/并且|同时|以及|分别|每个阶段|按阶段|阶段|多条件|参与|回复|话题|主题|发言规律|topic|theme/i]),
15401
+ matchAny(text, [/证据不够|不足以|不够|uncertain|insufficient/i])
15402
+ ];
15403
+ const score = dimensions.filter(Boolean).length;
15404
+ if (score >= 2) {
15405
+ return {
15406
+ route: "planned_execution",
15407
+ confidence: Math.min(0.95, 0.72 + score * 0.04),
15408
+ reason: `Rule matched ${score} complex-analysis signals.`,
15409
+ source: "rule"
15410
+ };
15411
+ }
15412
+ return null;
15413
+ }
15414
+ function toolRuleDecision(text) {
15415
+ if (matchAny(text, [
15416
+ /一共有多少|多少成员|多少条消息|谁发言最多|前\s*\d+\s*名|top\s*\d+/i,
15417
+ /异常活跃|活跃度|发言最多|message count|member count/i
15418
+ ])) {
15419
+ return {
15420
+ route: "tool_assisted",
15421
+ confidence: 0.82,
15422
+ reason: "Simple statistics request can be answered by one or a few tools.",
15423
+ source: "rule"
15424
+ };
15425
+ }
15426
+ if (matchAny(text, [
15427
+ /找一下|查一下|有没有聊到|有没有提过|最近提到|昨天.*聊了什么|最近.*发言/i,
15428
+ /search|find|mentioned|talked about/i
15429
+ ])) {
15430
+ return {
15431
+ route: "tool_assisted",
15432
+ confidence: 0.78,
15433
+ reason: "Simple search or lookup request with a single clear target.",
15434
+ source: "rule"
15435
+ };
15436
+ }
15437
+ return null;
15438
+ }
15439
+ function ruleDecision(input) {
15440
+ const text = normalizeText(input.userMessage);
15441
+ return plannedRuleDecision(text) ?? toolRuleDecision(text) ?? directRuleDecision(text) ?? {
15442
+ route: "tool_assisted",
15443
+ confidence: 0.45,
15444
+ reason: "Ambiguous request; conservatively keep the existing tool-assisted Agent path.",
15445
+ source: "rule"
15446
+ };
15447
+ }
15448
+ async function decideRequestRoute(input, options = {}) {
15449
+ const decision = ruleDecision(input);
15450
+ const threshold = options.llmFallbackConfidenceThreshold ?? DEFAULT_LLM_FALLBACK_CONFIDENCE_THRESHOLD;
15451
+ if (decision.confidence >= threshold || !options.llmRouter) {
15452
+ return { ...decision, confidence: clampConfidence(decision.confidence) };
15453
+ }
15454
+ const llmDecision = await options.llmRouter(input, decision);
15455
+ return {
15456
+ ...llmDecision,
15457
+ confidence: clampConfidence(llmDecision.confidence)
15458
+ };
15459
+ }
15460
+ function createLlmRouteDecider(options) {
15461
+ return async (input, ruleDecision2) => {
15462
+ const prompt = buildLlmRouterPrompt(input, ruleDecision2);
15463
+ try {
15464
+ const rawResult = options.complete ? await options.complete(prompt, options.abortSignal) : await completeWithPiAi(prompt, options);
15465
+ const { text, usage } = resultToText(rawResult);
15466
+ const decision = parseLlmDecision(text, usage);
15467
+ if (decision) return decision;
15468
+ return {
15469
+ ...ruleDecision2,
15470
+ reason: `${ruleDecision2.reason} LLM fallback returned invalid route JSON.`
15471
+ };
15472
+ } catch (error) {
15473
+ const message = error instanceof Error ? error.message : String(error);
15474
+ return {
15475
+ ...ruleDecision2,
15476
+ reason: `${ruleDecision2.reason} LLM fallback failed: ${message}`
15477
+ };
15478
+ }
15479
+ };
15480
+ }
15481
+ async function completeWithPiAi(prompt, options) {
15482
+ if (!options.piModel || !options.apiKey) {
15483
+ throw new Error("LLM router requires piModel and apiKey");
15484
+ }
15485
+ const result = await completeSimple2(
15486
+ options.piModel,
15487
+ {
15488
+ systemPrompt: "You are a strict JSON classifier for ChatLab AI routing. Return JSON only.",
15489
+ messages: [{ role: "user", content: [{ type: "text", text: prompt }], timestamp: Date.now() }]
15490
+ },
15491
+ {
15492
+ apiKey: options.apiKey,
15493
+ maxTokens: options.maxTokens ?? 180,
15494
+ temperature: options.temperature ?? 0,
15495
+ signal: options.abortSignal
15496
+ }
15497
+ );
15498
+ const text = result.content.filter((item) => item.type === "text").map((item) => item.text).join("");
15499
+ return {
15500
+ text,
15501
+ usage: {
15502
+ promptTokens: result.usage?.input,
15503
+ completionTokens: result.usage?.output,
15504
+ totalTokens: result.usage?.totalTokens
15505
+ }
15506
+ };
15507
+ }
15508
+
15509
+ // ../../packages/node-runtime/src/ai/agent/planner.ts
15510
+ import {
15511
+ completeSimple as completeSimple3,
15512
+ streamSimple
15513
+ } from "@earendil-works/pi-ai";
15514
+ var PLAN_INTENTS = [
15515
+ "summary",
15516
+ "trend",
15517
+ "relationship",
15518
+ "search",
15519
+ "comparison",
15520
+ "mixed"
15521
+ ];
15522
+ var STREAM_MARKER_HOLDBACK_CHARS = Math.max("</draft>".length, "</plan>".length, "<json>".length) - 1;
15523
+ function isRecord2(value) {
15524
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15525
+ }
15526
+ function isIntent(value) {
15527
+ return typeof value === "string" && PLAN_INTENTS.includes(value);
15528
+ }
15529
+ function normalizeString(value) {
15530
+ if (typeof value !== "string") return null;
15531
+ const text = value.trim();
15532
+ return text.length > 0 ? text : null;
15533
+ }
15534
+ function extractJsonObject4(text) {
15535
+ const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
15536
+ if (fenced?.[1]) return fenced[1].trim();
15537
+ const start = text.indexOf("{");
15538
+ const end = text.lastIndexOf("}");
15539
+ if (start >= 0 && end > start) return text.slice(start, end + 1);
15540
+ return text.trim();
15541
+ }
15542
+ function parsePlanJson(rawText, availableTools) {
15543
+ try {
15544
+ const parsed = JSON.parse(extractJsonObject4(rawText));
15545
+ if (!isRecord2(parsed)) return null;
15546
+ const title = normalizeString(parsed.title);
15547
+ if (!title || !isIntent(parsed.intent) || !Array.isArray(parsed.steps)) return null;
15548
+ const steps = parsed.steps.filter(isRecord2).map((step) => {
15549
+ const goal = normalizeString(step.goal);
15550
+ const evidenceNeeded = normalizeString(step.evidenceNeeded);
15551
+ if (!goal || !evidenceNeeded) return null;
15552
+ const suggestedTools = Array.isArray(step.suggestedTools) ? step.suggestedTools.filter((tool) => typeof tool === "string" && availableTools.has(tool)) : [];
15553
+ return { goal, suggestedTools: Array.from(new Set(suggestedTools)), evidenceNeeded };
15554
+ }).filter((step) => step !== null).slice(0, 5);
15555
+ if (steps.length === 0) return null;
15556
+ const successCriteria = Array.isArray(parsed.successCriteria) ? parsed.successCriteria.map(normalizeString).filter((item) => item !== null).slice(0, 5) : [];
15557
+ if (successCriteria.length === 0) return null;
15558
+ return {
15559
+ version: 1,
15560
+ title,
15561
+ route: "planned_execution",
15562
+ intent: parsed.intent,
15563
+ steps,
15564
+ successCriteria
15565
+ };
15566
+ } catch {
15567
+ return null;
15568
+ }
15569
+ }
15570
+ function resultToText2(result) {
15571
+ return typeof result === "string" ? result : result.text;
15572
+ }
15573
+ function buildPlannerPrompt(input) {
15574
+ const dataSnapshot = formatDataSnapshotForPlanner(input.dataSnapshot);
15575
+ const availableCapabilities = input.availableCapabilities && input.availableCapabilities.length > 0 ? input.availableCapabilities.map((capability) => {
15576
+ const tools = capability.tools.join(", ") || "none";
15577
+ return ` - ${capability.id} (${capability.label}; tools: ${tools}): ${capability.guidance}`;
15578
+ }).join("\n") : "(none)";
15579
+ return `Create a concise user-visible analysis plan for a ChatLab planned_execution request.
15580
+
15581
+ Return exactly three blocks:
15582
+ <draft>
15583
+ A brief user-visible planning note in the user's locale. Keep it high-level; do not reveal hidden chain-of-thought.
15584
+ </draft>
15585
+ <plan>
15586
+ A concise user-facing analysis approach in the user's locale. Do not include suggested tools, evidence fields, or success criteria; those details belong in <json>.
15587
+ </plan>
15588
+ <json>
15589
+ {
15590
+ "title": "short title",
15591
+ "intent": "summary|trend|relationship|search|comparison|mixed",
15592
+ "steps": [
15593
+ {"goal": "what to investigate", "suggestedTools": ["tool_name"], "evidenceNeeded": "what evidence is needed"}
15594
+ ],
15595
+ "successCriteria": ["criterion"]
15596
+ }
15597
+ </json>
15598
+
15599
+ Limits:
15600
+ - Max 5 steps.
15601
+ - Max 5 successCriteria.
15602
+ - suggestedTools must come only from availableTools.
15603
+ - If availableCapabilities are listed, you may plan steps that use their tools when the capability helps the user.
15604
+ - Do not reveal hidden chain-of-thought; draft and plan are user-readable summaries only.
15605
+ - The plan is soft guidance, not a mandatory execution script.
15606
+
15607
+ Planning strategy:
15608
+ - For open-ended topic, community profile, influence, interaction-pattern, or multi-dimensional retrospective analysis, first plan a lightweight reconnaissance step to build a topic/member activity map before drawing conclusions.
15609
+ - Prefer segment summaries, keyword frequency, time distribution, member activity, and representative message retrieval when available.
15610
+ - Do not create a dedicated step whose only purpose is finding max timestamp or confirming latest message time.
15611
+
15612
+ Context:
15613
+ - locale: ${input.locale}
15614
+ - chatType: ${input.chatType}
15615
+ - availableTools: ${input.availableTools.join(", ") || "(none)"}
15616
+ - availableCapabilities:
15617
+ ${availableCapabilities}
15618
+ - dataSnapshot: ${dataSnapshot}
15619
+ - assistantSummary: ${input.assistantSummary ?? "(none)"}
15620
+ - skillSummary: ${input.skillSummary ?? "(none)"}
15621
+ - recentIntentSummary: ${input.recentIntentSummary ?? "(none)"}
15622
+
15623
+ User request:
15624
+ ${input.userMessage}`;
15625
+ }
15626
+ function createAnalysisPlanner(options) {
15627
+ return async (input, signal) => {
15628
+ try {
15629
+ const prompt = buildPlannerPrompt(input);
15630
+ const callbacks = {
15631
+ onPlanDelta: options.onPlanDelta ?? (() => {
15632
+ }),
15633
+ onThinkingDelta: options.onThinkingDelta,
15634
+ onThinkingEnd: options.onThinkingEnd,
15635
+ onValidationDelta: options.onValidationDelta,
15636
+ onValidationEnd: options.onValidationEnd
15637
+ };
15638
+ const rawResult = options.stream ? await options.stream(prompt, callbacks, signal) : options.complete ? await options.complete(prompt, signal) : options.onPlanDelta ? await streamWithPiAi(prompt, options, signal) : await completeWithPiAi2(prompt, options, signal);
15639
+ return parsePlanJson(resultToText2(rawResult), new Set(input.availableTools));
15640
+ } catch {
15641
+ return null;
15642
+ }
15643
+ };
15644
+ }
15645
+ function createPlanContentBlock(plan, status = "created") {
15646
+ return {
15647
+ type: "plan",
15648
+ version: 1,
15649
+ status,
15650
+ plan
15651
+ };
15652
+ }
15653
+ function buildPlanGuidance(plan) {
15654
+ const steps = plan.steps.map((step, index) => {
15655
+ const tools = step.suggestedTools.length > 0 ? step.suggestedTools.join(", ") : "none";
15656
+ return `${index + 1}. ${step.goal}
15657
+ Evidence needed: ${step.evidenceNeeded}
15658
+ Suggested tools: ${tools}`;
15659
+ }).join("\n");
15660
+ const successCriteria = plan.successCriteria.map((item) => `- ${item}`).join("\n");
15661
+ return `A suggested analysis plan is available below. Use it as guidance when helpful, but do not follow it mechanically if the user's question can be answered more directly.
15662
+
15663
+ Plan title: ${plan.title}
15664
+ Intent: ${plan.intent}
15665
+
15666
+ Steps:
15667
+ ${steps}
15668
+
15669
+ Success criteria:
15670
+ ${successCriteria}`;
15671
+ }
15672
+ async function completeWithPiAi2(prompt, options, signal) {
15673
+ if (!options.piModel || !options.apiKey) {
15674
+ throw new Error("Planner requires piModel and apiKey");
15675
+ }
15676
+ const result = await completeSimple3(
15677
+ options.piModel,
15678
+ {
15679
+ systemPrompt: "You are a strict planner for ChatLab analysis. Return the requested blocks only.",
15680
+ messages: [{ role: "user", content: [{ type: "text", text: prompt }], timestamp: Date.now() }]
15681
+ },
15682
+ {
15683
+ apiKey: options.apiKey,
15684
+ maxTokens: options.maxTokens ?? 900,
15685
+ temperature: options.temperature ?? 0.2,
15686
+ signal
15687
+ }
15688
+ );
15689
+ return {
15690
+ text: result.content.filter((item) => item.type === "text").map((item) => item.text).join("")
15691
+ };
15692
+ }
15693
+ function extractTaggedBlockText(rawText, tag, nextTags) {
15694
+ const openTag = `<${tag}>`;
15695
+ const closeTag = `</${tag}>`;
15696
+ const blockStart = rawText.indexOf(openTag);
15697
+ if (blockStart < 0) return "";
15698
+ const bodyStart = blockStart + openTag.length;
15699
+ const closeStart = rawText.indexOf(closeTag, bodyStart);
15700
+ const nextStarts = nextTags.map((nextTag) => rawText.indexOf(nextTag, bodyStart)).filter((index) => index >= 0);
15701
+ const endCandidates = [closeStart, ...nextStarts].filter((index) => index >= 0);
15702
+ const bodyEnd = endCandidates.length > 0 ? Math.min(...endCandidates) : Math.max(bodyStart, rawText.length - STREAM_MARKER_HOLDBACK_CHARS);
15703
+ const text = rawText.slice(bodyStart, bodyEnd);
15704
+ return text.replace(/^\s+/, "");
15705
+ }
15706
+ async function streamWithPiAi(prompt, options, signal) {
15707
+ if (!options.piModel || !options.apiKey) {
15708
+ throw new Error("Planner requires piModel and apiKey");
15709
+ }
15710
+ const stream = streamSimple(
15711
+ options.piModel,
15712
+ {
15713
+ systemPrompt: "You are a strict planner for ChatLab analysis. Return the requested plan and JSON blocks only.",
15714
+ messages: [{ role: "user", content: [{ type: "text", text: prompt }], timestamp: Date.now() }]
15715
+ },
15716
+ {
15717
+ apiKey: options.apiKey,
15718
+ maxTokens: options.maxTokens ?? 900,
15719
+ temperature: options.temperature ?? 0.2,
15720
+ signal
15721
+ }
15722
+ );
15723
+ let text = "";
15724
+ let emittedDraftLength = 0;
15725
+ let emittedPlanLength = 0;
15726
+ let emittedJsonLength = 0;
15727
+ let thinkingStartedAt;
15728
+ let draftThinkingStartedAt;
15729
+ let draftThinkingEnded = false;
15730
+ let validationStartedAt;
15731
+ let validationEnded = false;
15732
+ for await (const event of stream) {
15733
+ if (signal?.aborted) break;
15734
+ if (event.type === "thinking_start") {
15735
+ thinkingStartedAt = Date.now();
15736
+ continue;
15737
+ }
15738
+ if (event.type === "thinking_delta") {
15739
+ options.onThinkingDelta?.(event.delta);
15740
+ continue;
15741
+ }
15742
+ if (event.type === "thinking_end") {
15743
+ const durationMs = thinkingStartedAt ? Date.now() - thinkingStartedAt : void 0;
15744
+ thinkingStartedAt = void 0;
15745
+ options.onThinkingEnd?.(durationMs);
15746
+ continue;
15747
+ }
15748
+ if (event.type !== "text_delta") continue;
15749
+ text += event.delta;
15750
+ const draftText = extractTaggedBlockText(text, "draft", ["<plan>", "<json>"]);
15751
+ if (draftText.length > emittedDraftLength) {
15752
+ if (!draftThinkingStartedAt) draftThinkingStartedAt = Date.now();
15753
+ const delta = draftText.slice(emittedDraftLength);
15754
+ emittedDraftLength = draftText.length;
15755
+ if (delta.trim().length > 0 || /\s/.test(delta)) {
15756
+ options.onThinkingDelta?.(delta);
15757
+ }
15758
+ }
15759
+ if (!draftThinkingEnded && emittedDraftLength > 0 && (text.includes("</draft>") || text.includes("<plan>"))) {
15760
+ draftThinkingEnded = true;
15761
+ const durationMs = draftThinkingStartedAt ? Date.now() - draftThinkingStartedAt : void 0;
15762
+ options.onThinkingEnd?.(durationMs);
15763
+ }
15764
+ const planText = extractTaggedBlockText(text, "plan", ["<json>"]);
15765
+ if (planText.length > emittedPlanLength) {
15766
+ const delta = planText.slice(emittedPlanLength);
15767
+ emittedPlanLength = planText.length;
15768
+ if (delta.trim().length > 0 || /\s/.test(delta)) {
15769
+ options.onPlanDelta?.(delta);
15770
+ }
15771
+ }
15772
+ const jsonText = extractTaggedBlockText(text, "json", []);
15773
+ if (jsonText.length > emittedJsonLength) {
15774
+ if (!validationStartedAt) validationStartedAt = Date.now();
15775
+ const delta = jsonText.slice(emittedJsonLength);
15776
+ emittedJsonLength = jsonText.length;
15777
+ if (delta.trim().length > 0 || /\s/.test(delta)) {
15778
+ options.onValidationDelta?.(delta);
15779
+ }
15780
+ }
15781
+ if (!validationEnded && emittedJsonLength > 0 && text.includes("</json>")) {
15782
+ validationEnded = true;
15783
+ const durationMs = validationStartedAt ? Date.now() - validationStartedAt : void 0;
15784
+ options.onValidationEnd?.(durationMs);
15785
+ }
15786
+ }
15787
+ return { text };
15788
+ }
15789
+
15790
+ // ../../packages/node-runtime/src/ai/agent/event-handler.ts
15791
+ function estimateTokensFromText(text) {
15792
+ if (!text) return 0;
15793
+ const normalized = text.replace(/\s+/g, " ").trim();
15794
+ if (!normalized) return 0;
15795
+ const cjkCount = (normalized.match(/[\u3400-\u9fff\uf900-\ufaff]/g) || []).length;
15796
+ const latinCount = normalized.length - cjkCount;
15797
+ return Math.max(1, Math.ceil(cjkCount * 1.15 + latinCount / 4));
15798
+ }
15799
+ function extractMessageText(message) {
15800
+ if (message.role === "user") {
15801
+ if (typeof message.content === "string") return message.content;
15802
+ return message.content.map((item) => {
15803
+ if (item.type === "text") return item.text;
15804
+ if (item.type === "image") return "[image]";
15805
+ return "";
15806
+ }).join("\n");
15807
+ }
15808
+ if (message.role === "assistant") {
15809
+ return message.content.map((item) => {
15810
+ if (item.type === "text") return item.text;
15811
+ if (item.type === "thinking") return item.thinking;
15812
+ if (item.type === "toolCall") return `${item.name} ${JSON.stringify(item.arguments || {})}`;
15813
+ return "";
15192
15814
  }).join("\n");
15193
15815
  }
15194
15816
  if (message.role === "toolResult") {
@@ -15301,6 +15923,39 @@ var AgentEventHandler = class {
15301
15923
  function agentT(t2, key, locale, options) {
15302
15924
  return t2(key, { lng: locale, ...options });
15303
15925
  }
15926
+ function formatNullableTimestamp2(timestamp) {
15927
+ return typeof timestamp === "number" && Number.isFinite(timestamp) ? String(timestamp) : "null";
15928
+ }
15929
+ function formatSharePercent2(share) {
15930
+ if (!Number.isFinite(share)) return "0%";
15931
+ const rounded = Math.round(share * 10) / 10;
15932
+ return Number.isInteger(rounded) ? `${rounded}%` : `${rounded.toFixed(1)}%`;
15933
+ }
15934
+ function normalizeInlineText(text) {
15935
+ return text.replace(/\s+/g, " ").trim();
15936
+ }
15937
+ function formatDataSnapshotContext(t2, dataSnapshot, locale) {
15938
+ const memberHints = dataSnapshot.activeMemberHints ?? [];
15939
+ const memberHintTitle = memberHints.length === 0 ? agentT(t2, "ai.agent.dataSnapshotMemberHintsUnavailable", locale) : dataSnapshot.totalMembers <= 10 ? agentT(t2, "ai.agent.dataSnapshotMemberHintsAll", locale) : agentT(t2, "ai.agent.dataSnapshotMemberHintsTop", locale);
15940
+ const memberHintLines = memberHints.length > 0 ? memberHints.map((member, index) => {
15941
+ return `${index + 1}. member_id=${member.memberId} | display_name=${normalizeInlineText(member.displayName)} | messages=${member.messageCount} | share=${formatSharePercent2(member.share)}`;
15942
+ }).join("\n") : agentT(t2, "ai.agent.dataSnapshotMemberHintsEmpty", locale);
15943
+ return agentT(t2, "ai.agent.dataSnapshotContext", locale, {
15944
+ name: dataSnapshot.name,
15945
+ platform: dataSnapshot.platform,
15946
+ type: dataSnapshot.type,
15947
+ totalMessages: dataSnapshot.totalMessages,
15948
+ totalMembers: dataSnapshot.totalMembers,
15949
+ firstMessageTs: formatNullableTimestamp2(dataSnapshot.firstMessageTs),
15950
+ firstMessageTime: formatTimestamp(dataSnapshot.firstMessageTs, locale),
15951
+ lastMessageTs: formatNullableTimestamp2(dataSnapshot.lastMessageTs),
15952
+ lastMessageTime: formatTimestamp(dataSnapshot.lastMessageTs, locale),
15953
+ segmentSummaryCount: dataSnapshot.segmentSummaries?.availableCount ?? 0,
15954
+ memberHintTitle,
15955
+ memberHintLines,
15956
+ usageRules: agentT(t2, "ai.agent.dataSnapshotUsageRules", locale)
15957
+ });
15958
+ }
15304
15959
  function getLockedPromptSection(t2, chatType, ownerInfo, locale, mentionedMembers, dataSnapshot) {
15305
15960
  const now = /* @__PURE__ */ new Date();
15306
15961
  const dateLocale = locale.startsWith("zh") ? "zh-CN" : "en-US";
@@ -15325,14 +15980,7 @@ ${mentionedMembers.map((member) => {
15325
15980
  }).join("\n")}
15326
15981
  ` : "";
15327
15982
  const year = now.getFullYear();
15328
- const dataSnapshotNote = dataSnapshot ? `${agentT(t2, "ai.agent.dataSnapshotNote", locale, {
15329
- name: dataSnapshot.name,
15330
- platform: dataSnapshot.platform,
15331
- totalMessages: dataSnapshot.totalMessages,
15332
- totalMembers: dataSnapshot.totalMembers,
15333
- firstMessageDate: formatTimestamp(dataSnapshot.firstMessageTs, locale),
15334
- lastMessageDate: formatTimestamp(dataSnapshot.lastMessageTs, locale)
15335
- })}
15983
+ const dataSnapshotNote = dataSnapshot ? `${formatDataSnapshotContext(t2, dataSnapshot, locale)}
15336
15984
  ` : "";
15337
15985
  return `${agentT(t2, "ai.agent.currentDateIs", locale)} ${currentDate}\u3002
15338
15986
  ${ownerNote}
@@ -15446,7 +16094,7 @@ var zh_CN_default = {
15446
16094
  }
15447
16095
  },
15448
16096
  get_time_stats: {
15449
- desc: '\u83B7\u53D6\u7FA4\u804A\u7684\u65F6\u95F4\u5206\u5E03\u7EDF\u8BA1\u3002\u9002\u7528\u4E8E\u56DE\u7B54"\u4EC0\u4E48\u65F6\u5019\u6700\u6D3B\u8DC3"\u3001"\u5927\u5BB6\u4E00\u822C\u51E0\u70B9\u804A\u5929"\u7B49\u95EE\u9898\u3002',
16097
+ desc: '\u83B7\u53D6\u7FA4\u804A\u7684\u65F6\u95F4\u5206\u5E03\u7EDF\u8BA1\u3002\u9002\u7528\u4E8E\u56DE\u7B54"\u4EC0\u4E48\u65F6\u5019\u6700\u6D3B\u8DC3"\u3001"\u5927\u5BB6\u4E00\u822C\u51E0\u70B9\u804A\u5929"\u7B49\u95EE\u9898\u3002\u8FD4\u56DE\u7684 `data` \u6570\u7EC4\u53EF\u76F4\u63A5\u4F5C\u4E3A `render_chart` \u7684 `rows` \u53C2\u6570\u7528\u4E8E\u7ED8\u56FE\u3002',
15450
16098
  params: {
15451
16099
  type: "\u7EDF\u8BA1\u7C7B\u578B\uFF1Ahourly\uFF08\u6309\u5C0F\u65F6\uFF09\u3001weekday\uFF08\u6309\u661F\u671F\uFF09\u3001daily\uFF08\u6309\u65E5\u671F\uFF09"
15452
16100
  }
@@ -15481,40 +16129,40 @@ var zh_CN_default = {
15481
16129
  context_size: "\u4E0A\u4E0B\u6587\u5927\u5C0F\uFF0C\u5373\u83B7\u53D6\u524D\u540E\u5404\u591A\u5C11\u6761\u6D88\u606F\uFF0C\u9ED8\u8BA4 20"
15482
16130
  }
15483
16131
  },
15484
- search_sessions: {
15485
- 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",
16132
+ search_segments: {
16133
+ 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",
15486
16134
  params: {
15487
- 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",
15488
- limit: "\u8FD4\u56DE\u4F1A\u8BDD\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 20",
15489
- year: "\u6307\u5B9A\u5E74\u4EFD\u7B5B\u9009\u4F1A\u8BDD\uFF0C\u5982 2024",
15490
- month: "\u6307\u5B9A\u6708\u4EFD\u7B5B\u9009\u4F1A\u8BDD\uFF081-12\uFF09\uFF0C\u9700\u914D\u5408 year \u4F7F\u7528",
15491
- day: "\u6307\u5B9A\u65E5\u671F\u7B5B\u9009\u4F1A\u8BDD\uFF081-31\uFF09\uFF0C\u9700\u914D\u5408 year \u548C month \u4F7F\u7528",
16135
+ 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",
16136
+ limit: "\u8FD4\u56DE\u6BB5\u843D\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 20",
16137
+ year: "\u6307\u5B9A\u5E74\u4EFD\u7B5B\u9009\u6BB5\u843D\uFF0C\u5982 2024",
16138
+ month: "\u6307\u5B9A\u6708\u4EFD\u7B5B\u9009\u6BB5\u843D\uFF081-12\uFF09\uFF0C\u9700\u914D\u5408 year \u4F7F\u7528",
16139
+ day: "\u6307\u5B9A\u65E5\u671F\u7B5B\u9009\u6BB5\u843D\uFF081-31\uFF09\uFF0C\u9700\u914D\u5408 year \u548C month \u4F7F\u7528",
15492
16140
  start_time: '\u5F00\u59CB\u65F6\u95F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"\uFF0C\u5982 "2024-03-15 14:00"',
15493
16141
  end_time: '\u7ED3\u675F\u65F6\u95F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"\uFF0C\u5982 "2024-03-15 18:30"'
15494
16142
  }
15495
16143
  },
15496
- get_session_messages: {
15497
- 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",
16144
+ get_segment_messages: {
16145
+ 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",
15498
16146
  params: {
15499
- session_id: "\u4F1A\u8BDD ID\uFF0C\u53EF\u4EE5\u4ECE search_sessions \u7684\u8FD4\u56DE\u7ED3\u679C\u4E2D\u83B7\u53D6",
15500
- 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"
16147
+ segment_id: "\u6BB5\u843D ID\uFF0C\u53EF\u4EE5\u4ECE search_segments \u7684\u8FD4\u56DE\u7ED3\u679C\u4E2D\u83B7\u53D6",
16148
+ 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"
15501
16149
  }
15502
16150
  },
15503
- get_session_summaries: {
15504
- desc: `\u83B7\u53D6\u4F1A\u8BDD\u6458\u8981\u5217\u8868\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u5386\u53F2\u8BA8\u8BBA\u7684\u4E3B\u9898\u3002
16151
+ get_segment_summaries: {
16152
+ desc: `\u83B7\u53D6\u6BB5\u843D\u6458\u8981\u5217\u8868\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u5386\u53F2\u8BA8\u8BBA\u7684\u4E3B\u9898\u3002
15505
16153
 
15506
16154
  \u9002\u7528\u573A\u666F\uFF1A
15507
16155
  1. \u4E86\u89E3\u7FA4\u91CC\u6700\u8FD1\u5728\u804A\u4EC0\u4E48\u8BDD\u9898
15508
16156
  2. \u6309\u5173\u952E\u8BCD\u641C\u7D22\u8BA8\u8BBA\u8FC7\u7684\u8BDD\u9898
15509
16157
  3. \u6982\u89C8\u6027\u95EE\u9898\u5982"\u7FA4\u91CC\u6709\u6CA1\u6709\u8BA8\u8BBA\u8FC7\u65C5\u6E38"
15510
16158
 
15511
- \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`,
16159
+ \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`,
15512
16160
  params: {
15513
16161
  keywords: "\u5728\u6458\u8981\u4E2D\u641C\u7D22\u7684\u5173\u952E\u8BCD\u5217\u8868\uFF08OR \u903B\u8F91\u5339\u914D\uFF09",
15514
- limit: "\u8FD4\u56DE\u4F1A\u8BDD\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 20",
15515
- year: "\u6307\u5B9A\u5E74\u4EFD\u7B5B\u9009\u4F1A\u8BDD",
15516
- month: "\u6307\u5B9A\u6708\u4EFD\u7B5B\u9009\u4F1A\u8BDD\uFF081-12\uFF09",
15517
- day: "\u6307\u5B9A\u65E5\u671F\u7B5B\u9009\u4F1A\u8BDD\uFF081-31\uFF09",
16162
+ limit: "\u8FD4\u56DE\u6BB5\u843D\u6570\u91CF\u9650\u5236\uFF0C\u9ED8\u8BA4 20",
16163
+ year: "\u6307\u5B9A\u5E74\u4EFD\u7B5B\u9009\u6BB5\u843D",
16164
+ month: "\u6307\u5B9A\u6708\u4EFD\u7B5B\u9009\u6BB5\u843D\uFF081-12\uFF09",
16165
+ day: "\u6307\u5B9A\u65E5\u671F\u7B5B\u9009\u6BB5\u843D\uFF081-31\uFF09",
15518
16166
  start_time: '\u5F00\u59CB\u65F6\u95F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"',
15519
16167
  end_time: '\u7ED3\u675F\u65F6\u95F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"'
15520
16168
  }
@@ -15601,14 +16249,14 @@ var zh_CN_default = {
15601
16249
  fallback: "\u8BE5\u65F6\u95F4\u8303\u56F4\u5185\u6CA1\u6709\u6D88\u606F\u8BB0\u5F55"
15602
16250
  },
15603
16251
  conversation_initiator_stats: {
15604
- 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",
16252
+ 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",
15605
16253
  params: {
15606
16254
  days: "\u7EDF\u8BA1\u6700\u8FD1\u591A\u5C11\u5929\u7684\u6570\u636E",
15607
16255
  limit: "\u8FD4\u56DE\u524D\u591A\u5C11\u540D"
15608
16256
  },
15609
16257
  rowTemplate: "{name}\uFF1A\u53D1\u8D77 {initiated_count} \u6B21\u8BDD\u9898",
15610
16258
  summaryTemplate: "\u8BDD\u9898\u53D1\u8D77\u8005 Top {rowCount}\uFF1A",
15611
- 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"
16259
+ 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"
15612
16260
  },
15613
16261
  activity_heatmap: {
15614
16262
  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",
@@ -15636,9 +16284,10 @@ var zh_CN_default = {
15636
16284
  params: {}
15637
16285
  },
15638
16286
  render_chart: {
15639
- 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",
16287
+ desc: "\u6839\u636E ChartSpec v1 \u751F\u6210 ChatLab \u539F\u751F\u56FE\u8868\u3002\u652F\u6301 bar\u3001line\u3001pie\u3001heatmap\u3002\u63D0\u4F9B `rows`\uFF08\u6765\u81EA\u9AD8\u5C42\u5DE5\u5177\u7684\u6570\u636E\u6570\u7EC4\uFF0C\u5982 get_time_stats \u7684 `data` \u5B57\u6BB5\uFF09\u6216 `sql`\uFF08\u53EA\u8BFB SELECT\uFF09\u4E8C\u9009\u4E00\uFF1B\u6709\u73B0\u6210\u6570\u636E\u65F6\u4F18\u5148\u7528 `rows`\uFF0C\u53EA\u6709\u9AD8\u5C42\u5DE5\u5177\u65E0\u6CD5\u6EE1\u8DB3\u65F6\u624D\u5199 `sql`\u3002\u7981\u6B62\u8F93\u51FA HTML\u3001JavaScript\u3001SVG\u3001ECharts option \u6216\u6E32\u67D3\u4EE3\u7801\u3002",
15640
16288
  params: {
15641
- sql: "\u53EA\u8BFB SELECT \u6216 WITH SELECT SQL\u3002\u5FC5\u987B\u8FD4\u56DE ChartSpec encoding \u4E2D\u5F15\u7528\u7684\u5B57\u6BB5\u3002",
16289
+ rows: "\u6765\u81EA\u9AD8\u5C42\u5DE5\u5177\u8FD4\u56DE\u7684\u6570\u636E\u6570\u7EC4\uFF08\u5982 get_time_stats \u7684 `data` \u5B57\u6BB5\uFF09\u3002\u6709\u73B0\u6210\u6570\u636E\u65F6\u4F18\u5148\u4F7F\u7528\uFF0C\u4E0E sql \u4E8C\u9009\u4E00\u3002",
16290
+ sql: "\u53EA\u8BFB SELECT \u6216 WITH SELECT SQL\u3002\u4EC5\u5728\u9AD8\u5C42\u5DE5\u5177\u65E0\u6CD5\u6EE1\u8DB3\u9700\u6C42\u65F6\u4F7F\u7528\uFF0C\u5FC5\u987B\u8FD4\u56DE ChartSpec encoding \u4E2D\u5F15\u7528\u7684\u5B57\u6BB5\u3002",
15642
16291
  params: "SQL \u547D\u540D\u53C2\u6570\u5BF9\u8C61\uFF1B\u4E0D\u9700\u8981\u53C2\u6570\u65F6\u4F20\u7A7A\u5BF9\u8C61\u3002",
15643
16292
  chartSpec: "ChartSpec v1\uFF0C\u5305\u542B version\u3001type\u3001title\u3001encoding\u3002",
15644
16293
  maxRows: "\u56FE\u8868\u67E5\u8BE2\u6700\u5927\u884C\u6570\uFF0C\u9ED8\u8BA4 1000\u3002"
@@ -15672,6 +16321,35 @@ var zh_CN_default = {
15672
16321
  timeParamsIntro: '\u65F6\u95F4\u53C2\u6570\uFF1A\u4F7F\u7528 start_time/end_time \u6307\u5B9A\u65F6\u95F4\u8303\u56F4\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"',
15673
16322
  defaultYearNote: "\u672A\u6307\u5B9A\u65F6\u95F4\u8303\u56F4\u65F6\u9ED8\u8BA4\u67E5\u8BE2\u5168\u90E8\u3002\u5F53\u524D\u5E74\u4EFD\u4E3A{{year}}\u5E74",
15674
16323
  dataSnapshotNote: "\u5F53\u524D\u804A\u5929\u6570\u636E\u5E93\u5FEB\u7167\uFF1A{{name}}\uFF08{{platform}}\uFF09\uFF0C\u603B\u6D88\u606F {{totalMessages}} \u6761\uFF0C\u6210\u5458 {{totalMembers}} \u4EBA\uFF0C\u65F6\u95F4\u8303\u56F4 {{firstMessageDate}} ~ {{lastMessageDate}}\u3002\u8BE5\u5FEB\u7167\u53EA\u7528\u4E8E\u5224\u65AD\u6570\u636E\u8303\u56F4\uFF1B\u56DE\u7B54\u5177\u4F53\u804A\u5929\u4E8B\u5B9E\u4ECD\u5FC5\u987B\u8C03\u7528\u5DE5\u5177\u68C0\u7D22\u5F53\u524D\u6570\u636E\u5E93\u3002",
16324
+ dataSnapshotContext: `\u5F53\u524D\u804A\u5929\u6570\u636E\u5E93\u542F\u52A8\u4E0A\u4E0B\u6587\uFF1A
16325
+ - name: {{name}}
16326
+ - platform: {{platform}}
16327
+ - type: {{type}}
16328
+ - total_messages: {{totalMessages}}
16329
+ - total_members: {{totalMembers}}
16330
+ - first_message_ts: {{firstMessageTs}}
16331
+ - first_message_time: {{firstMessageTime}}
16332
+ - last_message_ts: {{lastMessageTs}}
16333
+ - last_message_time: {{lastMessageTime}}
16334
+ - segment_summaries_available: {{segmentSummaryCount}}
16335
+
16336
+ {{memberHintTitle}}
16337
+ {{memberHintLines}}
16338
+
16339
+ \u4F7F\u7528\u89C4\u5219\uFF1A
16340
+ {{usageRules}}`,
16341
+ dataSnapshotMemberHintsAll: "\u6D3B\u8DC3\u6210\u5458\u67E5\u8BE2\u63D0\u793A\uFF08\u5168\u90E8\u6210\u5458\uFF09\uFF1A",
16342
+ dataSnapshotMemberHintsTop: "\u6D3B\u8DC3\u6210\u5458\u67E5\u8BE2\u63D0\u793A\uFF08\u6309\u5386\u53F2\u603B\u6D88\u606F\u91CF Top 10\uFF09\uFF1A",
16343
+ dataSnapshotMemberHintsUnavailable: "\u6D3B\u8DC3\u6210\u5458\u67E5\u8BE2\u63D0\u793A\uFF1A",
16344
+ dataSnapshotMemberHintsEmpty: "\u65E0\u53EF\u7528\u6210\u5458\u63D0\u793A\u3002",
16345
+ dataSnapshotUsageRules: `- member_id \u662F\u5DE5\u5177\u67E5\u8BE2\u63D0\u793A\uFF1Bdisplay_name \u4EC5\u7528\u4E8E\u4EBA\u7C7B\u8BC6\u522B\uFF0C\u53EF\u80FD\u4E0D\u552F\u4E00\u3002
16346
+ - \u4E0D\u8981\u5728\u6700\u7EC8\u56DE\u7B54\u4E2D\u4E3B\u52A8\u66B4\u9732 member_id \u6216\u542F\u52A8\u4E0A\u4E0B\u6587\u672C\u8EAB\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u6280\u672F\u7EC6\u8282\u3002
16347
+ - \u6D3B\u8DC3\u6210\u5458\u6392\u884C\u53EA\u4EE3\u8868\u5386\u53F2\u603B\u6D88\u606F\u91CF\uFF0C\u4E0D\u4EE3\u8868\u6700\u8FD1\u6D3B\u8DC3\u60C5\u51B5\uFF1B\u4E5F\u4E0D\u8DB3\u4EE5\u4F5C\u4E3A\u5F71\u54CD\u529B\u3001\u5173\u7CFB\u6216\u8FD1\u671F\u8D8B\u52BF\u7ED3\u8BBA\u7684\u8BC1\u636E\u3002
16348
+ - \u76F8\u5BF9\u65F6\u95F4\u8868\u8FBE\u4EE5\u771F\u5B9E\u5F53\u524D\u65E5\u671F\u4E3A\u57FA\u51C6\uFF0C\u800C\u4E0D\u662F\u6570\u636E\u5E93\u6700\u540E\u6D88\u606F\u65F6\u95F4\u3002
16349
+ - \u201C\u6700\u8FD1\u4E00\u5E74/\u8FC7\u53BB\u4E00\u5E74\u201D\u8868\u793A\u4ECE\u771F\u5B9E\u5F53\u524D\u65E5\u671F\u56DE\u63A8\u4E00\u5E74\u5230\u4ECA\u5929\uFF1B\u201C\u53BB\u5E74\u201D\u8868\u793A\u4E0A\u4E00\u81EA\u7136\u5E74\u3002
16350
+ - \u6570\u636E\u5E93\u65F6\u95F4\u8FB9\u754C\u53EA\u7528\u4E8E\u8BF4\u660E\u8986\u76D6\u8303\u56F4\uFF0C\u4E0D\u7528\u4E8E\u91CD\u5B9A\u4E49\u7528\u6237\u8981\u6C42\u7684\u65F6\u95F4\u8303\u56F4\u3002
16351
+ - \u4F7F\u7528\u9ED8\u8BA4\u201C\u8FD1 N \u5929\u201D\u7684\u5DE5\u5177\u65F6\uFF0C\u5148\u9009\u62E9\u4E0E\u6570\u636E\u5E93\u65F6\u95F4\u8FB9\u754C\u6709\u4EA4\u96C6\u7684\u8303\u56F4\uFF0C\u4E0D\u8981\u5148\u63A2\u6D4B\u771F\u5B9E\u5F53\u524D\u65E5\u671F\u7A97\u53E3\u5BFC\u81F4\u7A7A\u7ED3\u679C\u3002
16352
+ - \u4E0D\u8981\u53EA\u4E3A\u4E86\u91CD\u65B0\u53D1\u73B0 min/max timestamp \u8C03\u7528\u5DE5\u5177\uFF1B\u4F46\u56DE\u7B54\u5177\u4F53\u804A\u5929\u4E8B\u5B9E\u3001\u7EDF\u8BA1\u548C\u7ED3\u8BBA\u4ECD\u5FC5\u987B\u8C03\u7528\u5DE5\u5177\u83B7\u53D6\u8BC1\u636E\u3002`,
15675
16353
  evidencePolicy: `\u8BC1\u636E\u7B56\u7565\uFF1A
15676
16354
  - AI \u5BF9\u8BDD\u5386\u53F2\u3001\u5386\u53F2 AI \u56DE\u590D\u548C\u538B\u7F29\u6458\u8981\u53EA\u7528\u4E8E\u7406\u89E3\u7528\u6237\u610F\u56FE\uFF0C\u4E0D\u80FD\u4F5C\u4E3A\u804A\u5929\u8BB0\u5F55\u4E8B\u5B9E\u8BC1\u636E\u3002
15677
16355
  - \u53EA\u8981\u7528\u6237\u8BE2\u95EE\u804A\u5929\u8BB0\u5F55\u5185\u5BB9\u3001\u6700\u8FD1\u804A\u4EC0\u4E48\u3001\u67D0\u4EBA\u8BF4\u8FC7\u4EC0\u4E48\u3001\u7EDF\u8BA1\u6392\u884C\u3001\u662F\u5426\u51FA\u73B0\u8FC7\u67D0\u8BDD\u9898\u6216\u8981\u6C42\u5F15\u7528\u539F\u8BDD\uFF0C\u5FC5\u987B\u5148\u8C03\u7528\u5408\u9002\u7684\u6570\u636E\u5DE5\u5177\u68C0\u7D22\u5F53\u524D\u6570\u636E\u5E93\u3002
@@ -15712,10 +16390,10 @@ var zh_CN_default = {
15712
16390
  rawErrorLabel: "\u539F\u59CB\u9519\u8BEF"
15713
16391
  },
15714
16392
  summary: {
15715
- sessionNotFound: "\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u6570\u636E\u5E93\u6253\u5F00\u5931\u8D25",
16393
+ segmentNotFound: "\u6BB5\u843D\u4E0D\u5B58\u5728\u6216\u6570\u636E\u5E93\u6253\u5F00\u5931\u8D25",
15716
16394
  tooFewMessages: "\u6D88\u606F\u6570\u91CF\u5C11\u4E8E{{count}}\u6761\uFF0C\u65E0\u9700\u751F\u6210\u6458\u8981",
15717
16395
  tooFewValidMessages: "\u6709\u6548\u6D88\u606F\u6570\u91CF\u5C11\u4E8E{{count}}\u6761\uFF0C\u65E0\u9700\u751F\u6210\u6458\u8981",
15718
- sessionNotExist: "\u4F1A\u8BDD\u4E0D\u5B58\u5728",
16396
+ segmentNotExist: "\u6BB5\u843D\u4E0D\u5B58\u5728",
15719
16397
  messagesTooFew: "\u6D88\u606F\u592A\u5C11",
15720
16398
  validMessagesTooFew: "\u6709\u6548\u6D88\u606F\u592A\u5C11",
15721
16399
  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",
@@ -15780,7 +16458,7 @@ var en_US_default = {
15780
16458
  }
15781
16459
  },
15782
16460
  get_time_stats: {
15783
- desc: 'Get time distribution statistics of chat activity. Suitable for questions like "when is the group most active" or "what time do people usually chat".',
16461
+ desc: 'Get time distribution statistics of chat activity. Suitable for questions like "when is the group most active" or "what time do people usually chat". The returned `data` array can be passed directly as the `rows` parameter of render_chart for visualization.',
15784
16462
  params: {
15785
16463
  type: "Statistics type: hourly (by hour), weekday (by day of week), daily (by date)"
15786
16464
  }
@@ -15815,40 +16493,40 @@ var en_US_default = {
15815
16493
  context_size: "Context size, i.e. how many messages before and after to retrieve, default 20"
15816
16494
  }
15817
16495
  },
15818
- search_sessions: {
15819
- 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.",
16496
+ search_segments: {
16497
+ 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.",
15820
16498
  params: {
15821
- keywords: "Optional keyword list, only returns sessions containing these keywords (OR logic)",
16499
+ keywords: "Optional keyword list, only returns segments containing these keywords (OR logic)",
15822
16500
  limit: "Session count limit, default 20",
15823
- year: "Filter sessions by year, e.g. 2024",
15824
- month: "Filter sessions by month (1-12), use with year",
15825
- day: "Filter sessions by day (1-31), use with year and month",
16501
+ year: "Filter segments by year, e.g. 2024",
16502
+ month: "Filter segments by month (1-12), use with year",
16503
+ day: "Filter segments by day (1-31), use with year and month",
15826
16504
  start_time: 'Start time, format "YYYY-MM-DD HH:mm", e.g. "2024-03-15 14:00"',
15827
16505
  end_time: 'End time, format "YYYY-MM-DD HH:mm", e.g. "2024-03-15 18:30"'
15828
16506
  }
15829
16507
  },
15830
- get_session_messages: {
15831
- 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.",
16508
+ get_segment_messages: {
16509
+ 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.",
15832
16510
  params: {
15833
- session_id: "Session ID, can be obtained from search_sessions results",
15834
- limit: "Message count limit, default 1000. Can be limited for very long sessions to save tokens"
16511
+ segment_id: "Segment ID, can be obtained from search_segments results",
16512
+ limit: "Message count limit, default 1000. Can be limited for very long segments to save tokens"
15835
16513
  }
15836
16514
  },
15837
- get_session_summaries: {
15838
- desc: `Get session summary list to quickly understand discussion topics in chat history.
16515
+ get_segment_summaries: {
16516
+ desc: `Get segment summary list to quickly understand discussion topics in chat history.
15839
16517
 
15840
16518
  Use cases:
15841
16519
  1. Understand what topics have been discussed recently
15842
16520
  2. Search for discussed topics by keyword
15843
16521
  3. Overview questions like "has the group discussed travel"
15844
16522
 
15845
- Returned summaries are brief descriptions of each session, helping quickly locate sessions of interest. Use get_session_messages for details.`,
16523
+ Returned summaries are brief descriptions of each segment, helping quickly locate segments of interest. Use get_segment_messages for details.`,
15846
16524
  params: {
15847
16525
  keywords: "Keyword list to search within summaries (OR logic)",
15848
16526
  limit: "Session count limit, default 20",
15849
- year: "Filter sessions by year",
15850
- month: "Filter sessions by month (1-12)",
15851
- day: "Filter sessions by day (1-31)",
16527
+ year: "Filter segments by year",
16528
+ month: "Filter segments by month (1-12)",
16529
+ day: "Filter segments by day (1-31)",
15852
16530
  start_time: 'Start time, format "YYYY-MM-DD HH:mm"',
15853
16531
  end_time: 'End time, format "YYYY-MM-DD HH:mm"'
15854
16532
  }
@@ -15936,14 +16614,14 @@ Returned summaries are brief descriptions of each session, helping quickly locat
15936
16614
  fallback: "No messages in this time range"
15937
16615
  },
15938
16616
  conversation_initiator_stats: {
15939
- desc: "Count how many times each member initiated a conversation (was the first sender in a session). Requires session index to be generated.",
16617
+ desc: "Count how many times each member initiated a conversation (was the first sender in a segment). Requires segment index to be generated.",
15940
16618
  params: {
15941
16619
  days: "Number of recent days to analyze",
15942
16620
  limit: "Number of top members to return"
15943
16621
  },
15944
16622
  rowTemplate: "{name}: initiated {initiated_count} topics",
15945
16623
  summaryTemplate: "Topic initiator Top {rowCount}:",
15946
- fallback: "No session records in this time range. Session index may need to be generated first."
16624
+ fallback: "No segment records in this time range. Session index may need to be generated first."
15947
16625
  },
15948
16626
  activity_heatmap: {
15949
16627
  desc: "Return a weekday \xD7 hour message count matrix for generating activity heatmaps. weekday: 0=Sun, 1=Mon, ..., 6=Sat.",
@@ -15971,9 +16649,10 @@ Returned summaries are brief descriptions of each session, helping quickly locat
15971
16649
  params: {}
15972
16650
  },
15973
16651
  render_chart: {
15974
- 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.",
16652
+ desc: "Generate a native ChatLab chart from ChartSpec v1. Supports bar, line, pie, and heatmap. Provide either `rows` (pre-fetched data array from a tool result, e.g. the `data` field from get_time_stats) or `sql` (read-only SELECT) \u2014 prefer `rows` when data is already available; use `sql` only when high-level tools cannot satisfy the need. Do not output HTML, JavaScript, SVG, ECharts options, or rendering code.",
15975
16653
  params: {
15976
- sql: "Read-only SELECT or WITH SELECT SQL. It must return fields referenced by ChartSpec encoding.",
16654
+ rows: "Pre-fetched data array from a high-level tool result (e.g. the `data` field from get_time_stats). Prefer this over sql when data is already available. Mutually exclusive with sql.",
16655
+ sql: "Read-only SELECT or WITH SELECT SQL. Use only when high-level tools cannot satisfy the need. Must return fields referenced by ChartSpec encoding.",
15977
16656
  params: "Named SQL parameters. Use an empty object when no parameters are needed.",
15978
16657
  chartSpec: "ChartSpec v1 with version, type, title, and encoding.",
15979
16658
  maxRows: "Maximum chart query rows. Default 1000."
@@ -16008,6 +16687,35 @@ Returned summaries are brief descriptions of each session, helping quickly locat
16008
16687
  timeParamsIntro: 'Time parameters: use start_time/end_time to specify the range, format "YYYY-MM-DD HH:mm"',
16009
16688
  defaultYearNote: "When no time range is specified, queries default to all data. Current year is {{year}}.",
16010
16689
  dataSnapshotNote: "Current chat database snapshot: {{name}} ({{platform}}), {{totalMessages}} total messages, {{totalMembers}} members, time range {{firstMessageDate}} ~ {{lastMessageDate}}. This snapshot only helps judge data coverage; concrete chat facts still require tool retrieval from the current database.",
16690
+ dataSnapshotContext: `Current chat database startup context:
16691
+ - name: {{name}}
16692
+ - platform: {{platform}}
16693
+ - type: {{type}}
16694
+ - total_messages: {{totalMessages}}
16695
+ - total_members: {{totalMembers}}
16696
+ - first_message_ts: {{firstMessageTs}}
16697
+ - first_message_time: {{firstMessageTime}}
16698
+ - last_message_ts: {{lastMessageTs}}
16699
+ - last_message_time: {{lastMessageTime}}
16700
+ - segment_summaries_available: {{segmentSummaryCount}}
16701
+
16702
+ {{memberHintTitle}}
16703
+ {{memberHintLines}}
16704
+
16705
+ Usage rules:
16706
+ {{usageRules}}`,
16707
+ dataSnapshotMemberHintsAll: "Active member lookup hints (all members):",
16708
+ dataSnapshotMemberHintsTop: "Active member lookup hints (top 10 by historical total messages):",
16709
+ dataSnapshotMemberHintsUnavailable: "Active member lookup hints:",
16710
+ dataSnapshotMemberHintsEmpty: "No member hints available.",
16711
+ dataSnapshotUsageRules: `- member_id is a tool lookup hint; display_name is only for human recognition and may not be unique.
16712
+ - Do not proactively expose member_id or the startup context in the final answer unless the user explicitly asks for technical details.
16713
+ - Active member ranking only reflects historical total message volume, not recent activity; it is also not evidence for influence, relationships, or recent trends.
16714
+ - Interpret relative time expressions using the real current date, not the database's last message time.
16715
+ - "recent year" / "past year" means one year back from the real current date to today; "last year" means the previous calendar year.
16716
+ - Database time bounds are only for explaining coverage, not for redefining the user's requested time range.
16717
+ - When using default recent-day tools, first choose a range that intersects database time bounds instead of probing an empty real-current-date window.
16718
+ - Do not call tools only to rediscover min/max timestamp; concrete chat facts, statistics, and conclusions still require tool evidence.`,
16011
16719
  evidencePolicy: `Evidence policy:
16012
16720
  - AI conversation history, prior AI replies, and compressed summaries are only for understanding the user's intent; they are not evidence for chat-record facts.
16013
16721
  - Whenever the user asks about chat content, recent topics, what someone said, rankings/statistics, whether a topic appeared, or asks for exact quotes, first call an appropriate data tool to retrieve the current database.
@@ -16044,10 +16752,10 @@ Your task is to help users understand and analyze their private chat data.
16044
16752
  rawErrorLabel: "Raw error"
16045
16753
  },
16046
16754
  summary: {
16047
- sessionNotFound: "Session not found or database could not be opened",
16755
+ segmentNotFound: "Session not found or database could not be opened",
16048
16756
  tooFewMessages: "Message count less than {{count}}, no need to generate summary",
16049
16757
  tooFewValidMessages: "Valid message count less than {{count}}, no need to generate summary",
16050
- sessionNotExist: "Session not found",
16758
+ segmentNotExist: "Session not found",
16051
16759
  messagesTooFew: "Too few messages",
16052
16760
  validMessagesTooFew: "Too few valid messages",
16053
16761
  systemPromptDirect: "You are a conversation summarization expert. Summarize conversations concisely.",
@@ -16112,7 +16820,7 @@ var ja_JP_default = {
16112
16820
  }
16113
16821
  },
16114
16822
  get_time_stats: {
16115
- desc: "\u30B0\u30EB\u30FC\u30D7\u30C1\u30E3\u30C3\u30C8\u306E\u6642\u9593\u5206\u5E03\u7D71\u8A08\u3092\u53D6\u5F97\u3059\u308B\u3002\u300C\u3044\u3064\u304C\u4E00\u756A\u30A2\u30AF\u30C6\u30A3\u30D6\uFF1F\u300D\u300C\u307F\u3093\u306A\u4F55\u6642\u306B\u30C1\u30E3\u30C3\u30C8\u3057\u3066\u3044\u308B\uFF1F\u300D\u306A\u3069\u306E\u8CEA\u554F\u306B\u9069\u3057\u3066\u3044\u308B\u3002",
16823
+ desc: "\u30B0\u30EB\u30FC\u30D7\u30C1\u30E3\u30C3\u30C8\u306E\u6642\u9593\u5206\u5E03\u7D71\u8A08\u3092\u53D6\u5F97\u3059\u308B\u3002\u300C\u3044\u3064\u304C\u4E00\u756A\u30A2\u30AF\u30C6\u30A3\u30D6\uFF1F\u300D\u300C\u307F\u3093\u306A\u4F55\u6642\u306B\u30C1\u30E3\u30C3\u30C8\u3057\u3066\u3044\u308B\uFF1F\u300D\u306A\u3069\u306E\u8CEA\u554F\u306B\u9069\u3057\u3066\u3044\u308B\u3002\u8FD4\u3055\u308C\u308B `data` \u914D\u5217\u306F render_chart \u306E `rows` \u30D1\u30E9\u30E1\u30FC\u30BF\u3068\u3057\u3066\u76F4\u63A5\u4F7F\u7528\u3067\u304D\u308B\u3002",
16116
16824
  params: {
16117
16825
  type: "\u7D71\u8A08\u30BF\u30A4\u30D7\uFF1Ahourly\uFF08\u6642\u9593\u5225\uFF09\u3001weekday\uFF08\u66DC\u65E5\u5225\uFF09\u3001daily\uFF08\u65E5\u5225\uFF09"
16118
16826
  }
@@ -16147,40 +16855,40 @@ var ja_JP_default = {
16147
16855
  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"
16148
16856
  }
16149
16857
  },
16150
- search_sessions: {
16151
- 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",
16858
+ search_segments: {
16859
+ 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",
16152
16860
  params: {
16153
- 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",
16154
- limit: "\u8FD4\u5374\u30BB\u30C3\u30B7\u30E7\u30F3\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20",
16155
- year: "\u6307\u5B9A\u5E74\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF08\u4F8B\uFF1A2024\uFF09",
16156
- 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",
16157
- 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",
16861
+ 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",
16862
+ limit: "\u8FD4\u5374\u30BB\u30B0\u30E1\u30F3\u30C8\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20",
16863
+ year: "\u6307\u5B9A\u5E74\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF08\u4F8B\uFF1A2024\uFF09",
16864
+ 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",
16865
+ 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",
16158
16866
  start_time: '\u958B\u59CB\u6642\u523B\u3002\u5F62\u5F0F "YYYY-MM-DD HH:mm"\uFF08\u4F8B\uFF1A"2024-03-15 14:00"\uFF09',
16159
16867
  end_time: '\u7D42\u4E86\u6642\u523B\u3002\u5F62\u5F0F "YYYY-MM-DD HH:mm"\uFF08\u4F8B\uFF1A"2024-03-15 18:30"\uFF09'
16160
16868
  }
16161
16869
  },
16162
- get_session_messages: {
16163
- 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",
16870
+ get_segment_messages: {
16871
+ 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",
16164
16872
  params: {
16165
- session_id: "\u30BB\u30C3\u30B7\u30E7\u30F3 ID\u3002search_sessions \u306E\u8FD4\u5374\u7D50\u679C\u304B\u3089\u53D6\u5F97\u3067\u304D\u308B",
16166
- 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"
16873
+ segment_id: "\u30BB\u30B0\u30E1\u30F3\u30C8 ID\u3002search_segments \u306E\u8FD4\u5374\u7D50\u679C\u304B\u3089\u53D6\u5F97\u3067\u304D\u308B",
16874
+ 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"
16167
16875
  }
16168
16876
  },
16169
- get_session_summaries: {
16170
- 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
16877
+ get_segment_summaries: {
16878
+ 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
16171
16879
 
16172
16880
  \u9069\u7528\u30B7\u30FC\u30F3\uFF1A
16173
16881
  1. \u30B0\u30EB\u30FC\u30D7\u3067\u6700\u8FD1\u4F55\u304C\u8A71\u984C\u306B\u306A\u3063\u3066\u3044\u305F\u304B\u77E5\u308A\u305F\u3044
16174
16882
  2. \u30AD\u30FC\u30EF\u30FC\u30C9\u3067\u904E\u53BB\u306B\u8B70\u8AD6\u3055\u308C\u305F\u30C8\u30D4\u30C3\u30AF\u3092\u691C\u7D22
16175
16883
  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
16176
16884
 
16177
- \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`,
16885
+ \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`,
16178
16886
  params: {
16179
16887
  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",
16180
- limit: "\u8FD4\u5374\u30BB\u30C3\u30B7\u30E7\u30F3\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20",
16181
- year: "\u6307\u5B9A\u5E74\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0",
16182
- month: "\u6307\u5B9A\u6708\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-12\uFF09",
16183
- day: "\u6307\u5B9A\u65E5\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-31\uFF09",
16888
+ limit: "\u8FD4\u5374\u30BB\u30B0\u30E1\u30F3\u30C8\u6570\u306E\u4E0A\u9650\u3002\u30C7\u30D5\u30A9\u30EB\u30C8 20",
16889
+ year: "\u6307\u5B9A\u5E74\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0",
16890
+ month: "\u6307\u5B9A\u6708\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-12\uFF09",
16891
+ day: "\u6307\u5B9A\u65E5\u306E\u30BB\u30B0\u30E1\u30F3\u30C8\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0\uFF081-31\uFF09",
16184
16892
  start_time: '\u958B\u59CB\u6642\u523B\u3002\u5F62\u5F0F "YYYY-MM-DD HH:mm"',
16185
16893
  end_time: '\u7D42\u4E86\u6642\u523B\u3002\u5F62\u5F0F "YYYY-MM-DD HH:mm"'
16186
16894
  }
@@ -16267,14 +16975,14 @@ var ja_JP_default = {
16267
16975
  fallback: "\u3053\u306E\u671F\u9593\u306B\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093"
16268
16976
  },
16269
16977
  conversation_initiator_stats: {
16270
- 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",
16978
+ 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",
16271
16979
  params: {
16272
16980
  days: "\u76F4\u8FD1\u4F55\u65E5\u9593\u306E\u30C7\u30FC\u30BF\u3092\u96C6\u8A08\u3059\u308B\u304B",
16273
16981
  limit: "\u4E0A\u4F4D\u4F55\u540D\u3092\u8FD4\u5374\u3059\u308B\u304B"
16274
16982
  },
16275
16983
  rowTemplate: "{name}\uFF1A{initiated_count} \u56DE\u8A71\u984C\u3092\u958B\u59CB",
16276
16984
  summaryTemplate: "\u8A71\u984C\u958B\u59CB\u8005 Top {rowCount}\uFF1A",
16277
- 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"
16985
+ 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"
16278
16986
  },
16279
16987
  activity_heatmap: {
16280
16988
  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",
@@ -16302,9 +17010,10 @@ var ja_JP_default = {
16302
17010
  params: {}
16303
17011
  },
16304
17012
  render_chart: {
16305
- 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",
17013
+ desc: "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\u3002`rows`\uFF08\u9AD8\u30EC\u30D9\u30EB\u30C4\u30FC\u30EB\u306E\u30C7\u30FC\u30BF\u914D\u5217\u3001\u4F8B\uFF1Aget_time_stats \u306E `data` \u30D5\u30A3\u30FC\u30EB\u30C9\uFF09\u307E\u305F\u306F `sql`\uFF08\u8AAD\u307F\u53D6\u308A\u5C02\u7528 SELECT\uFF09\u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002\u30C7\u30FC\u30BF\u304C\u3059\u3067\u306B\u53D6\u5F97\u6E08\u307F\u306E\u5834\u5408\u306F `rows` \u3092\u512A\u5148\u3057\u3001\u9AD8\u30EC\u30D9\u30EB\u30C4\u30FC\u30EB\u3067\u5BFE\u5FDC\u3067\u304D\u306A\u3044\u5834\u5408\u306E\u307F `sql` \u3092\u4F7F\u7528\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",
16306
17014
  params: {
16307
- 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",
17015
+ rows: "\u9AD8\u30EC\u30D9\u30EB\u30C4\u30FC\u30EB\u304B\u3089\u53D6\u5F97\u3057\u305F\u30C7\u30FC\u30BF\u914D\u5217\uFF08\u4F8B\uFF1Aget_time_stats \u306E `data` \u30D5\u30A3\u30FC\u30EB\u30C9\uFF09\u3002\u30C7\u30FC\u30BF\u304C\u53D6\u5F97\u6E08\u307F\u306E\u5834\u5408\u306F sql \u3088\u308A\u512A\u5148\u3057\u3066\u4F7F\u7528\u3057\u307E\u3059\u3002sql \u3068\u306F\u6392\u4ED6\u3002",
17016
+ sql: "\u8AAD\u307F\u53D6\u308A\u5C02\u7528\u306E SELECT \u307E\u305F\u306F WITH SELECT SQL\u3002\u9AD8\u30EC\u30D9\u30EB\u30C4\u30FC\u30EB\u3067\u5BFE\u5FDC\u3067\u304D\u306A\u3044\u5834\u5408\u306E\u307F\u4F7F\u7528\u3057\u307E\u3059\u3002ChartSpec encoding \u3067\u53C2\u7167\u3059\u308B\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002",
16308
17017
  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",
16309
17018
  chartSpec: "version\u3001type\u3001title\u3001encoding \u3092\u542B\u3080 ChartSpec v1\u3002",
16310
17019
  maxRows: "\u30C1\u30E3\u30FC\u30C8\u30AF\u30A8\u30EA\u306E\u6700\u5927\u884C\u6570\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u306F 1000\u3002"
@@ -16338,6 +17047,35 @@ var ja_JP_default = {
16338
17047
  timeParamsIntro: '\u6642\u9593\u30D1\u30E9\u30E1\u30FC\u30BF\uFF1Astart_time/end_time \u3067\u7BC4\u56F2\u3092\u6307\u5B9A\u3002\u5F62\u5F0F\u306F "YYYY-MM-DD HH:mm"',
16339
17048
  defaultYearNote: "\u6642\u9593\u7BC4\u56F2\u3092\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u306F\u5168\u671F\u9593\u304C\u5BFE\u8C61\u3002\u73FE\u5728\u306E\u5E74\u306F{{year}}\u5E74",
16340
17049
  dataSnapshotNote: "\u73FE\u5728\u306E\u30C1\u30E3\u30C3\u30C8\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\uFF1A{{name}}\uFF08{{platform}}\uFF09\u3001\u7DCF\u30E1\u30C3\u30BB\u30FC\u30B8 {{totalMessages}} \u4EF6\u3001\u30E1\u30F3\u30D0\u30FC {{totalMembers}} \u4EBA\u3001\u671F\u9593 {{firstMessageDate}} ~ {{lastMessageDate}}\u3002\u3053\u306E\u30B9\u30CA\u30C3\u30D7\u30B7\u30E7\u30C3\u30C8\u306F\u30C7\u30FC\u30BF\u7BC4\u56F2\u306E\u5224\u65AD\u306B\u306E\u307F\u4F7F\u3044\u3001\u5177\u4F53\u7684\u306A\u30C1\u30E3\u30C3\u30C8\u4E8B\u5B9F\u306E\u56DE\u7B54\u306B\u306F\u5FC5\u305A\u73FE\u5728\u306E\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u30C4\u30FC\u30EB\u3067\u691C\u7D22\u3059\u308B\u3002",
17050
+ dataSnapshotContext: `\u73FE\u5728\u306E\u30C1\u30E3\u30C3\u30C8\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u8D77\u52D5\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\uFF1A
17051
+ - name: {{name}}
17052
+ - platform: {{platform}}
17053
+ - type: {{type}}
17054
+ - total_messages: {{totalMessages}}
17055
+ - total_members: {{totalMembers}}
17056
+ - first_message_ts: {{firstMessageTs}}
17057
+ - first_message_time: {{firstMessageTime}}
17058
+ - last_message_ts: {{lastMessageTs}}
17059
+ - last_message_time: {{lastMessageTime}}
17060
+ - segment_summaries_available: {{segmentSummaryCount}}
17061
+
17062
+ {{memberHintTitle}}
17063
+ {{memberHintLines}}
17064
+
17065
+ \u4F7F\u7528\u30EB\u30FC\u30EB\uFF1A
17066
+ {{usageRules}}`,
17067
+ dataSnapshotMemberHintsAll: "\u30A2\u30AF\u30C6\u30A3\u30D6\u30E1\u30F3\u30D0\u30FC\u691C\u7D22\u30D2\u30F3\u30C8\uFF08\u5168\u30E1\u30F3\u30D0\u30FC\uFF09\uFF1A",
17068
+ dataSnapshotMemberHintsTop: "\u30A2\u30AF\u30C6\u30A3\u30D6\u30E1\u30F3\u30D0\u30FC\u691C\u7D22\u30D2\u30F3\u30C8\uFF08\u904E\u53BB\u306E\u7DCF\u30E1\u30C3\u30BB\u30FC\u30B8\u6570 Top 10\uFF09\uFF1A",
17069
+ dataSnapshotMemberHintsUnavailable: "\u30A2\u30AF\u30C6\u30A3\u30D6\u30E1\u30F3\u30D0\u30FC\u691C\u7D22\u30D2\u30F3\u30C8\uFF1A",
17070
+ dataSnapshotMemberHintsEmpty: "\u5229\u7528\u53EF\u80FD\u306A\u30E1\u30F3\u30D0\u30FC\u30D2\u30F3\u30C8\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
17071
+ dataSnapshotUsageRules: `- member_id \u306F\u30C4\u30FC\u30EB\u691C\u7D22\u7528\u306E\u30D2\u30F3\u30C8\u3067\u3059\u3002display_name \u306F\u4EBA\u9593\u304C\u8B58\u5225\u3059\u308B\u305F\u3081\u3060\u3051\u306E\u3082\u306E\u3067\u3001\u4E00\u610F\u3068\u306F\u9650\u308A\u307E\u305B\u3093\u3002
17072
+ - \u30E6\u30FC\u30B6\u30FC\u304C\u6280\u8853\u7684\u8A73\u7D30\u3092\u660E\u793A\u7684\u306B\u6C42\u3081\u306A\u3044\u9650\u308A\u3001\u6700\u7D42\u56DE\u7B54\u3067 member_id \u3084\u8D77\u52D5\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u81EA\u4F53\u3092\u7A4D\u6975\u7684\u306B\u958B\u793A\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
17073
+ - \u30A2\u30AF\u30C6\u30A3\u30D6\u30E1\u30F3\u30D0\u30FC\u9806\u4F4D\u306F\u904E\u53BB\u306E\u7DCF\u30E1\u30C3\u30BB\u30FC\u30B8\u91CF\u3060\u3051\u3092\u793A\u3057\u3001\u6700\u8FD1\u306E\u6D3B\u767A\u3055\u3092\u793A\u3059\u3082\u306E\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u307E\u305F\u3001\u5F71\u97FF\u529B\u3001\u95A2\u4FC2\u6027\u3001\u6700\u8FD1\u306E\u50BE\u5411\u306E\u7D50\u8AD6\u8A3C\u62E0\u306B\u3082\u306A\u308A\u307E\u305B\u3093\u3002
17074
+ - \u76F8\u5BFE\u7684\u306A\u6642\u9593\u8868\u73FE\u306F\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u6700\u7D42\u30E1\u30C3\u30BB\u30FC\u30B8\u6642\u523B\u3067\u306F\u306A\u304F\u3001\u5B9F\u969B\u306E\u73FE\u5728\u65E5\u4ED8\u3092\u57FA\u6E96\u306B\u89E3\u91C8\u3057\u3066\u304F\u3060\u3055\u3044\u3002
17075
+ - \u300C\u6700\u8FD1\u4E00\u5E74/\u904E\u53BB\u4E00\u5E74\u300D\u306F\u5B9F\u969B\u306E\u73FE\u5728\u65E5\u4ED8\u304B\u3089\u4ECA\u65E5\u307E\u3067\u306E1\u5E74\u9593\u3092\u6307\u3057\u3001\u300C\u53BB\u5E74\u300D\u306F\u524D\u306E\u66A6\u5E74\u3092\u6307\u3057\u307E\u3059\u3002
17076
+ - \u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306E\u6642\u9593\u5883\u754C\u306F\u30AB\u30D0\u30EC\u30C3\u30B8\u8AAC\u660E\u306B\u306E\u307F\u4F7F\u3044\u3001\u30E6\u30FC\u30B6\u30FC\u304C\u6C42\u3081\u305F\u6642\u9593\u7BC4\u56F2\u3092\u518D\u5B9A\u7FA9\u3059\u308B\u305F\u3081\u306B\u4F7F\u308F\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
17077
+ - \u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u300C\u76F4\u8FD1 N \u65E5\u300D\u30C4\u30FC\u30EB\u3092\u4F7F\u3046\u5834\u5408\u306F\u3001\u7A7A\u306E\u5B9F\u969B\u73FE\u5728\u65E5\u4ED8\u30A6\u30A3\u30F3\u30C9\u30A6\u3092\u8A66\u3059\u306E\u3067\u306F\u306A\u304F\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u6642\u9593\u5883\u754C\u3068\u4EA4\u5DEE\u3059\u308B\u7BC4\u56F2\u3092\u5148\u306B\u9078\u3093\u3067\u304F\u3060\u3055\u3044\u3002
17078
+ - min/max timestamp \u3092\u518D\u767A\u898B\u3059\u308B\u305F\u3081\u3060\u3051\u306B\u30C4\u30FC\u30EB\u3092\u547C\u3070\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\u305F\u3060\u3057\u3001\u5177\u4F53\u7684\u306A\u30C1\u30E3\u30C3\u30C8\u4E8B\u5B9F\u3001\u7D71\u8A08\u3001\u7D50\u8AD6\u306B\u306F\u5F15\u304D\u7D9A\u304D\u30C4\u30FC\u30EB\u8A3C\u62E0\u304C\u5FC5\u8981\u3067\u3059\u3002`,
16341
17079
  evidencePolicy: `\u8A3C\u62E0\u30DD\u30EA\u30B7\u30FC\uFF1A
16342
17080
  - AI \u4F1A\u8A71\u5C65\u6B74\u3001\u904E\u53BB\u306E AI \u56DE\u7B54\u3001\u5727\u7E2E\u8981\u7D04\u306F\u30E6\u30FC\u30B6\u30FC\u610F\u56F3\u306E\u7406\u89E3\u306B\u306E\u307F\u4F7F\u7528\u3057\u3001\u30C1\u30E3\u30C3\u30C8\u8A18\u9332\u306E\u4E8B\u5B9F\u8A3C\u62E0\u3068\u3057\u3066\u6271\u308F\u306A\u3044\u3002
16343
17081
  - \u30E6\u30FC\u30B6\u30FC\u304C\u30C1\u30E3\u30C3\u30C8\u5185\u5BB9\u3001\u6700\u8FD1\u306E\u8A71\u984C\u3001\u8AB0\u304B\u306E\u767A\u8A00\u3001\u30E9\u30F3\u30AD\u30F3\u30B0/\u7D71\u8A08\u3001\u3042\u308B\u8A71\u984C\u304C\u51FA\u305F\u304B\u3069\u3046\u304B\u3001\u307E\u305F\u306F\u539F\u6587\u5F15\u7528\u3092\u6C42\u3081\u305F\u5834\u5408\u306F\u3001\u5FC5\u305A\u5148\u306B\u9069\u5207\u306A\u30C7\u30FC\u30BF\u30C4\u30FC\u30EB\u3067\u73FE\u5728\u306E\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u691C\u7D22\u3059\u308B\u3002
@@ -16376,10 +17114,10 @@ var ja_JP_default = {
16376
17114
  rawErrorLabel: "\u5143\u306E\u30A8\u30E9\u30FC"
16377
17115
  },
16378
17116
  summary: {
16379
- 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",
17117
+ 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",
16380
17118
  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",
16381
17119
  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",
16382
- sessionNotExist: "\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u5B58\u5728\u3057\u307E\u305B\u3093",
17120
+ segmentNotExist: "\u30BB\u30B0\u30E1\u30F3\u30C8\u304C\u5B58\u5728\u3057\u307E\u305B\u3093",
16383
17121
  messagesTooFew: "\u30E1\u30C3\u30BB\u30FC\u30B8\u304C\u5C11\u306A\u3059\u304E\u307E\u3059",
16384
17122
  validMessagesTooFew: "\u6709\u52B9\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u304C\u5C11\u306A\u3059\u304E\u307E\u3059",
16385
17123
  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",
@@ -16444,7 +17182,7 @@ var zh_TW_default = {
16444
17182
  }
16445
17183
  },
16446
17184
  get_time_stats: {
16447
- desc: "\u53D6\u5F97\u7FA4\u804A\u7684\u6642\u9593\u5206\u4F48\u7D71\u8A08\u3002\u9069\u7528\u65BC\u56DE\u7B54\u300C\u4EC0\u9EBC\u6642\u5019\u6700\u6D3B\u8E8D\u300D\u3001\u300C\u5927\u5BB6\u4E00\u822C\u5E7E\u9EDE\u804A\u5929\u300D\u7B49\u554F\u984C\u3002",
17185
+ desc: "\u53D6\u5F97\u7FA4\u804A\u7684\u6642\u9593\u5206\u4F48\u7D71\u8A08\u3002\u9069\u7528\u65BC\u56DE\u7B54\u300C\u4EC0\u9EBC\u6642\u5019\u6700\u6D3B\u8E8D\u300D\u3001\u300C\u5927\u5BB6\u4E00\u822C\u5E7E\u9EDE\u804A\u5929\u300D\u7B49\u554F\u984C\u3002\u8FD4\u56DE\u7684 `data` \u9663\u5217\u53EF\u76F4\u63A5\u4F5C\u70BA `render_chart` \u7684 `rows` \u53C3\u6578\u7528\u65BC\u7E6A\u5716\u3002",
16448
17186
  params: {
16449
17187
  type: "\u7D71\u8A08\u985E\u578B\uFF1Ahourly\uFF08\u6309\u5C0F\u6642\uFF09\u3001weekday\uFF08\u6309\u661F\u671F\uFF09\u3001daily\uFF08\u6309\u65E5\u671F\uFF09"
16450
17188
  }
@@ -16479,7 +17217,7 @@ var zh_TW_default = {
16479
17217
  context_size: "\u4E0A\u4E0B\u6587\u5927\u5C0F\uFF0C\u5373\u53D6\u5F97\u524D\u5F8C\u5404\u591A\u5C11\u689D\u8A0A\u606F\uFF0C\u9810\u8A2D 20"
16480
17218
  }
16481
17219
  },
16482
- search_sessions: {
17220
+ search_segments: {
16483
17221
  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",
16484
17222
  params: {
16485
17223
  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",
@@ -16491,22 +17229,22 @@ var zh_TW_default = {
16491
17229
  end_time: '\u7D50\u675F\u6642\u9593\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"\uFF0C\u5982 "2024-03-15 18:30"'
16492
17230
  }
16493
17231
  },
16494
- get_session_messages: {
16495
- 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",
17232
+ get_segment_messages: {
17233
+ 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",
16496
17234
  params: {
16497
- session_id: "\u6703\u8A71 ID\uFF0C\u53EF\u4EE5\u5F9E search_sessions \u7684\u56DE\u50B3\u7D50\u679C\u4E2D\u53D6\u5F97",
17235
+ segment_id: "\u6703\u8A71 ID\uFF0C\u53EF\u4EE5\u5F9E search_segments \u7684\u56DE\u50B3\u7D50\u679C\u4E2D\u53D6\u5F97",
16498
17236
  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"
16499
17237
  }
16500
17238
  },
16501
- get_session_summaries: {
16502
- desc: `\u53D6\u5F97\u6703\u8A71\u6458\u8981\u6E05\u55AE\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u6B77\u53F2\u8A0E\u8AD6\u7684\u4E3B\u984C\u3002
17239
+ get_segment_summaries: {
17240
+ desc: `\u53D6\u5F97\u6BB5\u843D\u6458\u8981\u6E05\u55AE\uFF0C\u5FEB\u901F\u4E86\u89E3\u7FA4\u804A\u6B77\u53F2\u8A0E\u8AD6\u7684\u4E3B\u984C\u3002
16503
17241
 
16504
17242
  \u9069\u7528\u5834\u666F\uFF1A
16505
17243
  1. \u4E86\u89E3\u7FA4\u88E1\u6700\u8FD1\u5728\u804A\u4EC0\u9EBC\u8A71\u984C
16506
17244
  2. \u6309\u95DC\u9375\u8A5E\u641C\u5C0B\u8A0E\u8AD6\u904E\u7684\u8A71\u984C
16507
17245
  3. \u6982\u89BD\u6027\u554F\u984C\u5982\u300C\u7FA4\u88E1\u6709\u6C92\u6709\u8A0E\u8AD6\u904E\u65C5\u904A\u300D
16508
17246
 
16509
- \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`,
17247
+ \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`,
16510
17248
  params: {
16511
17249
  keywords: "\u5728\u6458\u8981\u4E2D\u641C\u5C0B\u7684\u95DC\u9375\u8A5E\u6E05\u55AE\uFF08OR \u908F\u8F2F\u5339\u914D\uFF09",
16512
17250
  limit: "\u56DE\u50B3\u6703\u8A71\u6578\u91CF\u9650\u5236\uFF0C\u9810\u8A2D 20",
@@ -16634,9 +17372,10 @@ var zh_TW_default = {
16634
17372
  params: {}
16635
17373
  },
16636
17374
  render_chart: {
16637
- 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",
17375
+ desc: "\u6839\u64DA ChartSpec v1 \u751F\u6210 ChatLab \u539F\u751F\u5716\u8868\u3002\u652F\u63F4 bar\u3001line\u3001pie\u3001heatmap\u3002\u63D0\u4F9B `rows`\uFF08\u4F86\u81EA\u9AD8\u5C64\u5DE5\u5177\u7684\u8CC7\u6599\u9663\u5217\uFF0C\u5982 get_time_stats \u7684 `data` \u6B04\u4F4D\uFF09\u6216 `sql`\uFF08\u552F\u8B80 SELECT\uFF09\u4E8C\u64C7\u4E00\uFF1B\u6709\u73FE\u6210\u8CC7\u6599\u6642\u512A\u5148\u7528 `rows`\uFF0C\u50C5\u5728\u9AD8\u5C64\u5DE5\u5177\u7121\u6CD5\u6EFF\u8DB3\u9700\u6C42\u6642\u624D\u5BEB `sql`\u3002\u7981\u6B62\u8F38\u51FA HTML\u3001JavaScript\u3001SVG\u3001ECharts option \u6216\u6E32\u67D3\u7A0B\u5F0F\u78BC\u3002",
16638
17376
  params: {
16639
- sql: "\u552F\u8B80 SELECT \u6216 WITH SELECT SQL\u3002\u5FC5\u9808\u8FD4\u56DE ChartSpec encoding \u4E2D\u5F15\u7528\u7684\u6B04\u4F4D\u3002",
17377
+ rows: "\u4F86\u81EA\u9AD8\u5C64\u5DE5\u5177\u8FD4\u56DE\u7684\u8CC7\u6599\u9663\u5217\uFF08\u5982 get_time_stats \u7684 `data` \u6B04\u4F4D\uFF09\u3002\u6709\u73FE\u6210\u8CC7\u6599\u6642\u512A\u5148\u4F7F\u7528\uFF0C\u8207 sql \u4E8C\u64C7\u4E00\u3002",
17378
+ sql: "\u552F\u8B80 SELECT \u6216 WITH SELECT SQL\u3002\u50C5\u5728\u9AD8\u5C64\u5DE5\u5177\u7121\u6CD5\u6EFF\u8DB3\u9700\u6C42\u6642\u4F7F\u7528\uFF0C\u5FC5\u9808\u8FD4\u56DE ChartSpec encoding \u4E2D\u5F15\u7528\u7684\u6B04\u4F4D\u3002",
16640
17379
  params: "SQL \u547D\u540D\u53C3\u6578\u7269\u4EF6\uFF1B\u4E0D\u9700\u8981\u53C3\u6578\u6642\u50B3\u7A7A\u7269\u4EF6\u3002",
16641
17380
  chartSpec: "ChartSpec v1\uFF0C\u5305\u542B version\u3001type\u3001title\u3001encoding\u3002",
16642
17381
  maxRows: "\u5716\u8868\u67E5\u8A62\u6700\u5927\u884C\u6578\uFF0C\u9810\u8A2D 1000\u3002"
@@ -16670,6 +17409,35 @@ var zh_TW_default = {
16670
17409
  timeParamsIntro: '\u6642\u9593\u53C3\u6578\uFF1A\u4F7F\u7528 start_time/end_time \u6307\u5B9A\u6642\u9593\u7BC4\u570D\uFF0C\u683C\u5F0F "YYYY-MM-DD HH:mm"',
16671
17410
  defaultYearNote: "\u672A\u6307\u5B9A\u6642\u9593\u7BC4\u570D\u6642\u9810\u8A2D\u67E5\u8A62\u5168\u90E8\u3002\u7576\u524D\u5E74\u4EFD\u70BA{{year}}\u5E74",
16672
17411
  dataSnapshotNote: "\u76EE\u524D\u804A\u5929\u8CC7\u6599\u5EAB\u5FEB\u7167\uFF1A{{name}}\uFF08{{platform}}\uFF09\uFF0C\u7E3D\u8A0A\u606F {{totalMessages}} \u689D\uFF0C\u6210\u54E1 {{totalMembers}} \u4EBA\uFF0C\u6642\u9593\u7BC4\u570D {{firstMessageDate}} ~ {{lastMessageDate}}\u3002\u8A72\u5FEB\u7167\u53EA\u7528\u65BC\u5224\u65B7\u8CC7\u6599\u7BC4\u570D\uFF1B\u56DE\u7B54\u5177\u9AD4\u804A\u5929\u4E8B\u5BE6\u4ECD\u5FC5\u9808\u547C\u53EB\u5DE5\u5177\u6AA2\u7D22\u76EE\u524D\u8CC7\u6599\u5EAB\u3002",
17412
+ dataSnapshotContext: `\u76EE\u524D\u804A\u5929\u8CC7\u6599\u5EAB\u555F\u52D5\u4E0A\u4E0B\u6587\uFF1A
17413
+ - name: {{name}}
17414
+ - platform: {{platform}}
17415
+ - type: {{type}}
17416
+ - total_messages: {{totalMessages}}
17417
+ - total_members: {{totalMembers}}
17418
+ - first_message_ts: {{firstMessageTs}}
17419
+ - first_message_time: {{firstMessageTime}}
17420
+ - last_message_ts: {{lastMessageTs}}
17421
+ - last_message_time: {{lastMessageTime}}
17422
+ - segment_summaries_available: {{segmentSummaryCount}}
17423
+
17424
+ {{memberHintTitle}}
17425
+ {{memberHintLines}}
17426
+
17427
+ \u4F7F\u7528\u898F\u5247\uFF1A
17428
+ {{usageRules}}`,
17429
+ dataSnapshotMemberHintsAll: "\u6D3B\u8E8D\u6210\u54E1\u67E5\u8A62\u63D0\u793A\uFF08\u5168\u90E8\u6210\u54E1\uFF09\uFF1A",
17430
+ dataSnapshotMemberHintsTop: "\u6D3B\u8E8D\u6210\u54E1\u67E5\u8A62\u63D0\u793A\uFF08\u6309\u6B77\u53F2\u7E3D\u8A0A\u606F\u91CF Top 10\uFF09\uFF1A",
17431
+ dataSnapshotMemberHintsUnavailable: "\u6D3B\u8E8D\u6210\u54E1\u67E5\u8A62\u63D0\u793A\uFF1A",
17432
+ dataSnapshotMemberHintsEmpty: "\u7121\u53EF\u7528\u6210\u54E1\u63D0\u793A\u3002",
17433
+ dataSnapshotUsageRules: `- member_id \u662F\u5DE5\u5177\u67E5\u8A62\u63D0\u793A\uFF1Bdisplay_name \u50C5\u7528\u65BC\u4EBA\u985E\u8B58\u5225\uFF0C\u53EF\u80FD\u4E0D\u552F\u4E00\u3002
17434
+ - \u4E0D\u8981\u5728\u6700\u7D42\u56DE\u7B54\u4E2D\u4E3B\u52D5\u66B4\u9732 member_id \u6216\u555F\u52D5\u4E0A\u4E0B\u6587\u672C\u8EAB\uFF0C\u9664\u975E\u4F7F\u7528\u8005\u660E\u78BA\u8981\u6C42\u6280\u8853\u7D30\u7BC0\u3002
17435
+ - \u6D3B\u8E8D\u6210\u54E1\u6392\u884C\u53EA\u4EE3\u8868\u6B77\u53F2\u7E3D\u8A0A\u606F\u91CF\uFF0C\u4E0D\u4EE3\u8868\u8FD1\u671F\u6D3B\u8E8D\u60C5\u6CC1\uFF1B\u4E5F\u4E0D\u8DB3\u4EE5\u4F5C\u70BA\u5F71\u97FF\u529B\u3001\u95DC\u4FC2\u6216\u8FD1\u671F\u8DA8\u52E2\u7D50\u8AD6\u7684\u8B49\u64DA\u3002
17436
+ - \u76F8\u5C0D\u6642\u9593\u8868\u9054\u4EE5\u771F\u5BE6\u76EE\u524D\u65E5\u671F\u70BA\u57FA\u6E96\uFF0C\u800C\u4E0D\u662F\u8CC7\u6599\u5EAB\u6700\u5F8C\u8A0A\u606F\u6642\u9593\u3002
17437
+ -\u300C\u6700\u8FD1\u4E00\u5E74/\u904E\u53BB\u4E00\u5E74\u300D\u8868\u793A\u5F9E\u771F\u5BE6\u76EE\u524D\u65E5\u671F\u56DE\u63A8\u4E00\u5E74\u5230\u4ECA\u5929\uFF1B\u300C\u53BB\u5E74\u300D\u8868\u793A\u4E0A\u4E00\u81EA\u7136\u5E74\u3002
17438
+ - \u8CC7\u6599\u5EAB\u6642\u9593\u908A\u754C\u53EA\u7528\u65BC\u8AAA\u660E\u8986\u84CB\u7BC4\u570D\uFF0C\u4E0D\u7528\u65BC\u91CD\u5B9A\u7FA9\u4F7F\u7528\u8005\u8981\u6C42\u7684\u6642\u9593\u7BC4\u570D\u3002
17439
+ - \u4F7F\u7528\u9810\u8A2D\u300C\u8FD1 N \u5929\u300D\u7684\u5DE5\u5177\u6642\uFF0C\u5148\u9078\u64C7\u8207\u8CC7\u6599\u5EAB\u6642\u9593\u908A\u754C\u6709\u4EA4\u96C6\u7684\u7BC4\u570D\uFF0C\u4E0D\u8981\u5148\u63A2\u6E2C\u771F\u5BE6\u76EE\u524D\u65E5\u671F\u8996\u7A97\u5C0E\u81F4\u7A7A\u7D50\u679C\u3002
17440
+ - \u4E0D\u8981\u53EA\u70BA\u4E86\u91CD\u65B0\u767C\u73FE min/max timestamp \u547C\u53EB\u5DE5\u5177\uFF1B\u4F46\u56DE\u7B54\u5177\u9AD4\u804A\u5929\u4E8B\u5BE6\u3001\u7D71\u8A08\u548C\u7D50\u8AD6\u4ECD\u5FC5\u9808\u547C\u53EB\u5DE5\u5177\u53D6\u5F97\u8B49\u64DA\u3002`,
16673
17441
  evidencePolicy: `\u8B49\u64DA\u7B56\u7565\uFF1A
16674
17442
  - AI \u5C0D\u8A71\u6B77\u53F2\u3001\u6B77\u53F2 AI \u56DE\u8986\u548C\u58D3\u7E2E\u6458\u8981\u53EA\u7528\u65BC\u7406\u89E3\u4F7F\u7528\u8005\u610F\u5716\uFF0C\u4E0D\u80FD\u4F5C\u70BA\u804A\u5929\u7D00\u9304\u4E8B\u5BE6\u8B49\u64DA\u3002
16675
17443
  - \u53EA\u8981\u4F7F\u7528\u8005\u8A62\u554F\u804A\u5929\u7D00\u9304\u5167\u5BB9\u3001\u6700\u8FD1\u804A\u4EC0\u9EBC\u3001\u67D0\u4EBA\u8AAA\u904E\u4EC0\u9EBC\u3001\u7D71\u8A08\u6392\u884C\u3001\u662F\u5426\u51FA\u73FE\u904E\u67D0\u8A71\u984C\u6216\u8981\u6C42\u5F15\u7528\u539F\u8A71\uFF0C\u5FC5\u9808\u5148\u547C\u53EB\u5408\u9069\u7684\u8CC7\u6599\u5DE5\u5177\u6AA2\u7D22\u76EE\u524D\u8CC7\u6599\u5EAB\u3002
@@ -16708,10 +17476,10 @@ var zh_TW_default = {
16708
17476
  rawErrorLabel: "\u539F\u59CB\u932F\u8AA4"
16709
17477
  },
16710
17478
  summary: {
16711
- sessionNotFound: "\u6703\u8A71\u4E0D\u5B58\u5728\u6216\u8CC7\u6599\u5EAB\u958B\u555F\u5931\u6557",
17479
+ segmentNotFound: "\u6703\u8A71\u4E0D\u5B58\u5728\u6216\u8CC7\u6599\u5EAB\u958B\u555F\u5931\u6557",
16712
17480
  tooFewMessages: "\u8A0A\u606F\u6578\u91CF\u5C11\u65BC {{count}} \u689D\uFF0C\u7121\u9700\u7522\u751F\u6458\u8981",
16713
17481
  tooFewValidMessages: "\u6709\u6548\u8A0A\u606F\u6578\u91CF\u5C11\u65BC {{count}} \u689D\uFF0C\u7121\u9700\u7522\u751F\u6458\u8981",
16714
- sessionNotExist: "\u627E\u4E0D\u5230\u6703\u8A71",
17482
+ segmentNotExist: "\u627E\u4E0D\u5230\u6703\u8A71",
16715
17483
  messagesTooFew: "\u8A0A\u606F\u592A\u5C11",
16716
17484
  validMessagesTooFew: "\u6709\u6548\u8A0A\u606F\u592A\u5C11",
16717
17485
  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",
@@ -16726,9 +17494,9 @@ var localeMap = {
16726
17494
  "ja-JP": ja_JP_default,
16727
17495
  "zh-TW": zh_TW_default
16728
17496
  };
16729
- function getNestedValue(obj, path35) {
17497
+ function getNestedValue(obj, path36) {
16730
17498
  let current = obj;
16731
- for (const segment2 of path35.split(".")) {
17499
+ for (const segment2 of path36.split(".")) {
16732
17500
  if (current == null || typeof current !== "object") return void 0;
16733
17501
  current = current[segment2];
16734
17502
  }
@@ -16924,15 +17692,15 @@ function postProcessSummary(summary, lengthLimit) {
16924
17692
  }
16925
17693
  return result;
16926
17694
  }
16927
- async function generateSessionSummary(deps, chatSessionId, options = {}) {
17695
+ async function generateSessionSummary(deps, segmentId, options = {}) {
16928
17696
  const { locale = "zh-CN", forceRegenerate = false, strategy = "standard" } = options;
16929
17697
  const log = deps.logger;
16930
17698
  try {
16931
17699
  if (!forceRegenerate) {
16932
- const existing = deps.getSummary(chatSessionId);
17700
+ const existing = deps.getSummary(segmentId);
16933
17701
  if (existing) return { success: true, summary: existing };
16934
17702
  }
16935
- const rawMessages = deps.loadMessages(chatSessionId);
17703
+ const rawMessages = deps.loadMessages(segmentId);
16936
17704
  if (!rawMessages) {
16937
17705
  return { success: false, error: deps.t("summary.sessionNotFound") };
16938
17706
  }
@@ -16949,7 +17717,7 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
16949
17717
  const content = formatMessages(validMessages);
16950
17718
  log?.info(
16951
17719
  "Summary",
16952
- `Generating summary: sessionId=${chatSessionId}, strategy=${strategy}, raw=${rawMessages.length}, valid=${validMessages.length}, chars=${content.length}`
17720
+ `Generating summary: sessionId=${segmentId}, strategy=${strategy}, raw=${rawMessages.length}, valid=${validMessages.length}, chars=${content.length}`
16953
17721
  );
16954
17722
  let summary;
16955
17723
  if (content.length <= SEGMENT_THRESHOLD) {
@@ -16984,7 +17752,7 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
16984
17752
  }
16985
17753
  }
16986
17754
  summary = postProcessSummary(summary, lengthLimit);
16987
- deps.saveSummary(chatSessionId, summary);
17755
+ deps.saveSummary(segmentId, summary);
16988
17756
  log?.info("Summary", `Summary generated: "${summary.slice(0, 50)}..."`);
16989
17757
  return { success: true, summary };
16990
17758
  } catch (error) {
@@ -16992,9 +17760,9 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
16992
17760
  return { success: false, error: error instanceof Error ? error.message : String(error) };
16993
17761
  }
16994
17762
  }
16995
- function checkSessionsCanGenerateSummary(deps, chatSessionIds) {
17763
+ function checkSessionsCanGenerateSummary(deps, segmentIds) {
16996
17764
  const results = /* @__PURE__ */ new Map();
16997
- for (const id of chatSessionIds) {
17765
+ for (const id of segmentIds) {
16998
17766
  const messages = deps.loadMessages(id);
16999
17767
  if (!messages) {
17000
17768
  results.set(id, { canGenerate: false, reason: deps.t("summary.sessionNotExist") });
@@ -17110,7 +17878,7 @@ function buildPiModel(config, options) {
17110
17878
  }
17111
17879
 
17112
17880
  // ../../packages/node-runtime/src/ai/remote-api.ts
17113
- import { completeSimple as completeSimple2 } from "@earendil-works/pi-ai";
17881
+ import { completeSimple as completeSimple4 } from "@earendil-works/pi-ai";
17114
17882
  async function fetchRemoteModels(provider, apiKey, baseUrl, apiFormat, options) {
17115
17883
  const effectiveApiFormat = apiFormat || "openai-completions";
17116
17884
  if (effectiveApiFormat === "anthropic-messages") {
@@ -17194,7 +17962,7 @@ async function validateApiKey(provider, apiKey, baseUrl, model, apiFormat, optio
17194
17962
  const abortController = new AbortController();
17195
17963
  const timeout = setTimeout(() => abortController.abort(), 15e3);
17196
17964
  try {
17197
- const result = await completeSimple2(
17965
+ const result = await completeSimple4(
17198
17966
  piModel,
17199
17967
  { messages: [{ role: "user", content: "Hi", timestamp: Date.now() }] },
17200
17968
  { apiKey, maxTokens: 1, signal: abortController.signal }
@@ -17215,7 +17983,7 @@ async function validateApiKey(provider, apiKey, baseUrl, model, apiFormat, optio
17215
17983
  }
17216
17984
 
17217
17985
  // ../../packages/node-runtime/src/ai/llm-stream.ts
17218
- import { streamSimple } from "@earendil-works/pi-ai";
17986
+ import { streamSimple as streamSimple2 } from "@earendil-works/pi-ai";
17219
17987
  function toPiMessages(messages, timestamp) {
17220
17988
  return messages.map((m) => ({
17221
17989
  role: m.role,
@@ -17228,7 +17996,7 @@ async function runSimpleLlmStream(options) {
17228
17996
  const systemMsg = messages.find((m) => m.role === "system");
17229
17997
  const nonSystemMsgs = messages.filter((m) => m.role !== "system");
17230
17998
  const now = Date.now();
17231
- const eventStream = streamSimple(
17999
+ const eventStream = streamSimple2(
17232
18000
  piModel,
17233
18001
  {
17234
18002
  systemPrompt: systemMsg?.content,
@@ -17286,7 +18054,7 @@ async function runSimpleLlmStream(options) {
17286
18054
  }
17287
18055
 
17288
18056
  // ../../packages/node-runtime/src/ai/index.ts
17289
- import { Type, completeSimple as completeSimple4, streamSimple as streamSimple2 } from "@earendil-works/pi-ai";
18057
+ import { Type, completeSimple as completeSimple6, streamSimple as streamSimple3 } from "@earendil-works/pi-ai";
17290
18058
 
17291
18059
  // ../../packages/node-runtime/src/ai/rag/types.ts
17292
18060
  var noopLogger = {
@@ -17305,7 +18073,7 @@ function getNoopLogger() {
17305
18073
  var _logger = getNoopLogger();
17306
18074
 
17307
18075
  // ../../packages/node-runtime/src/ai/rag/chunking/session.ts
17308
- import Database3 from "better-sqlite3";
18076
+ import Database2 from "better-sqlite3";
17309
18077
  var _logger2 = getNoopLogger();
17310
18078
 
17311
18079
  // ../../packages/node-runtime/src/ai/rag/embedding/openai-compatible.ts
@@ -17315,7 +18083,7 @@ var _logger3 = getNoopLogger();
17315
18083
  var _logger4 = getNoopLogger();
17316
18084
 
17317
18085
  // ../../packages/node-runtime/src/ai/rag/store/sqlite.ts
17318
- import Database4 from "better-sqlite3";
18086
+ import Database3 from "better-sqlite3";
17319
18087
  var _logger5 = getNoopLogger();
17320
18088
 
17321
18089
  // ../../packages/node-runtime/src/ai/rag/store/memory.ts
@@ -17325,7 +18093,7 @@ var _logger6 = getNoopLogger();
17325
18093
  var _logger7 = getNoopLogger();
17326
18094
 
17327
18095
  // ../../packages/node-runtime/src/ai/rag/pipeline/semantic.ts
17328
- import { completeSimple as completeSimple3 } from "@earendil-works/pi-ai";
18096
+ import { completeSimple as completeSimple5 } from "@earendil-works/pi-ai";
17329
18097
  var _logger8 = getNoopLogger();
17330
18098
 
17331
18099
  // ../../packages/node-runtime/src/migrations/electron-data-migration.ts
@@ -17840,7 +18608,216 @@ function expandHome(filePath) {
17840
18608
  return filePath;
17841
18609
  }
17842
18610
 
18611
+ // ../../packages/node-runtime/src/data-dir-compat.ts
18612
+ import fs34 from "fs";
18613
+ import path31 from "path";
18614
+ var DataDirCompatibilityError = class extends Error {
18615
+ code;
18616
+ userDataDir;
18617
+ metaPath;
18618
+ currentVersion;
18619
+ minRuntimeVersion;
18620
+ statusCode = 409;
18621
+ constructor(code, message, options) {
18622
+ super(message, { cause: options.cause });
18623
+ this.name = "DataDirCompatibilityError";
18624
+ this.code = code;
18625
+ this.userDataDir = options.userDataDir;
18626
+ this.metaPath = options.metaPath;
18627
+ this.currentVersion = options.currentVersion;
18628
+ this.minRuntimeVersion = options.minRuntimeVersion;
18629
+ }
18630
+ };
18631
+ function readDataDirCompatibilityMeta(userDataDir) {
18632
+ const metaPath = getMetaPath(userDataDir);
18633
+ if (!fs34.existsSync(metaPath)) return null;
18634
+ let parsed;
18635
+ try {
18636
+ parsed = JSON.parse(fs34.readFileSync(metaPath, "utf-8"));
18637
+ } catch (error) {
18638
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid data directory compatibility meta JSON.", error);
18639
+ }
18640
+ return validateDataDirCompatibilityMeta(parsed, userDataDir, metaPath);
18641
+ }
18642
+ function assertDataDirCompatible(pathProvider, runtime, options = {}) {
18643
+ const userDataDir = pathProvider.getUserDataDir();
18644
+ const meta = readDataDirCompatibilityMeta(userDataDir);
18645
+ if (!meta) return;
18646
+ if (!isStableSemver(runtime.version)) {
18647
+ throw new DataDirCompatibilityError(
18648
+ "DATA_DIR_REQUIRES_NEWER_RUNTIME",
18649
+ `ChatLab data directory compatibility check requires a stable runtime version; current version is ${runtime.version}.`,
18650
+ {
18651
+ userDataDir,
18652
+ metaPath: getMetaPath(userDataDir),
18653
+ currentVersion: runtime.version,
18654
+ minRuntimeVersion: meta.minRuntimeVersion
18655
+ }
18656
+ );
18657
+ }
18658
+ if (compareStableSemver(runtime.version, meta.minRuntimeVersion) < 0) {
18659
+ const env = options.env ?? process.env;
18660
+ if (env.CHATLAB_ALLOW_INCOMPATIBLE_DATA_DIR === "1") {
18661
+ const warn = options.warn ?? console.warn;
18662
+ warn(
18663
+ [
18664
+ "CHATLAB_ALLOW_INCOMPATIBLE_DATA_DIR=1 is set. ChatLab will bypass the data directory runtime version gate.",
18665
+ `Current runtime ${runtime.kind}@${runtime.version} is below required version ${meta.minRuntimeVersion}.`,
18666
+ `Data directory: ${userDataDir}. Continuing may risk data corruption.`
18667
+ ].join(" ")
18668
+ );
18669
+ return;
18670
+ }
18671
+ throw new DataDirCompatibilityError(
18672
+ "DATA_DIR_REQUIRES_NEWER_RUNTIME",
18673
+ `ChatLab data directory requires runtime version ${meta.minRuntimeVersion} or newer; current version is ${runtime.version}.`,
18674
+ {
18675
+ userDataDir,
18676
+ metaPath: getMetaPath(userDataDir),
18677
+ currentVersion: runtime.version,
18678
+ minRuntimeVersion: meta.minRuntimeVersion
18679
+ }
18680
+ );
18681
+ }
18682
+ }
18683
+ function raiseDataDirMinRuntimeVersion(pathProvider, input) {
18684
+ const userDataDir = pathProvider.getUserDataDir();
18685
+ const metaPath = getMetaPath(userDataDir);
18686
+ assertStableVersion(input.minRuntimeVersion, userDataDir, metaPath, "Invalid minRuntimeVersion.");
18687
+ assertStableVersion(input.runtime.version, userDataDir, metaPath, "Invalid runtime version.");
18688
+ if (!isRuntimeKind(input.runtime.kind)) {
18689
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid runtime kind.");
18690
+ }
18691
+ if (!isNonEmptyString(input.module) || !isNonEmptyString(input.reason)) {
18692
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid compatibility raise metadata.");
18693
+ }
18694
+ if (!Number.isInteger(input.dataCompatibilityVersion) || input.dataCompatibilityVersion < 0) {
18695
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid dataCompatibilityVersion.");
18696
+ }
18697
+ const existing = readDataDirCompatibilityMeta(userDataDir);
18698
+ const minRuntimeVersion = existing && compareStableSemver(existing.minRuntimeVersion, input.minRuntimeVersion) > 0 ? existing.minRuntimeVersion : input.minRuntimeVersion;
18699
+ const dataCompatibilityVersion = Math.max(existing?.dataCompatibilityVersion ?? 0, input.dataCompatibilityVersion);
18700
+ const reasons = mergeReasons(existing?.reasons ?? [], input.reason);
18701
+ const now = input.now ?? (() => Math.floor(Date.now() / 1e3));
18702
+ const nextMeta = {
18703
+ formatVersion: 1,
18704
+ minRuntimeVersion,
18705
+ dataCompatibilityVersion,
18706
+ reasons,
18707
+ updatedBy: {
18708
+ runtime: input.runtime.kind,
18709
+ module: input.module,
18710
+ version: input.runtime.version
18711
+ },
18712
+ updatedAt: now()
18713
+ };
18714
+ writeMetaAtomic(userDataDir, metaPath, nextMeta);
18715
+ return nextMeta;
18716
+ }
18717
+ function getMetaPath(userDataDir) {
18718
+ return path31.join(userDataDir, ".chatlab-meta.json");
18719
+ }
18720
+ function validateDataDirCompatibilityMeta(value, userDataDir, metaPath) {
18721
+ if (!isRecord3(value)) {
18722
+ throw createInvalidMetaError(userDataDir, metaPath, "Data directory compatibility meta must be an object.");
18723
+ }
18724
+ if (value.formatVersion !== 1) {
18725
+ throw createInvalidMetaError(userDataDir, metaPath, "Unsupported data directory compatibility meta format.");
18726
+ }
18727
+ const minRuntimeVersion = value.minRuntimeVersion;
18728
+ const dataCompatibilityVersion = value.dataCompatibilityVersion;
18729
+ const reasons = value.reasons;
18730
+ const updatedBy = value.updatedBy;
18731
+ const updatedAt = value.updatedAt;
18732
+ if (!isStableSemver(minRuntimeVersion) || typeof dataCompatibilityVersion !== "number" || !Number.isInteger(dataCompatibilityVersion) || dataCompatibilityVersion < 0 || !Array.isArray(reasons) || !reasons.every(isNonEmptyString) || !isRecord3(updatedBy) || !isRuntimeKind(updatedBy.runtime) || !isNonEmptyString(updatedBy.module) || !isStableSemver(updatedBy.version) || typeof updatedAt !== "number" || !Number.isInteger(updatedAt) || updatedAt < 0) {
18733
+ throw createInvalidMetaError(userDataDir, metaPath, "Invalid data directory compatibility meta fields.");
18734
+ }
18735
+ return {
18736
+ formatVersion: 1,
18737
+ minRuntimeVersion,
18738
+ dataCompatibilityVersion,
18739
+ reasons: [...reasons],
18740
+ updatedBy: {
18741
+ runtime: updatedBy.runtime,
18742
+ module: updatedBy.module,
18743
+ version: updatedBy.version
18744
+ },
18745
+ updatedAt
18746
+ };
18747
+ }
18748
+ function createInvalidMetaError(userDataDir, metaPath, message, cause) {
18749
+ return new DataDirCompatibilityError("DATA_DIR_COMPATIBILITY_META_INVALID", message, {
18750
+ userDataDir,
18751
+ metaPath,
18752
+ cause
18753
+ });
18754
+ }
18755
+ function assertStableVersion(version, userDataDir, metaPath, message) {
18756
+ if (!isStableSemver(version)) {
18757
+ throw createInvalidMetaError(userDataDir, metaPath, message);
18758
+ }
18759
+ }
18760
+ function isStableSemver(version) {
18761
+ return typeof version === "string" && /^\d+\.\d+\.\d+$/.test(version);
18762
+ }
18763
+ function compareStableSemver(left, right) {
18764
+ const leftParts = left.split(".").map(Number);
18765
+ const rightParts = right.split(".").map(Number);
18766
+ for (let i = 0; i < 3; i += 1) {
18767
+ if (leftParts[i] > rightParts[i]) return 1;
18768
+ if (leftParts[i] < rightParts[i]) return -1;
18769
+ }
18770
+ return 0;
18771
+ }
18772
+ function mergeReasons(existing, next) {
18773
+ return [.../* @__PURE__ */ new Set([...existing, next])];
18774
+ }
18775
+ function writeMetaAtomic(userDataDir, metaPath, meta) {
18776
+ fs34.mkdirSync(userDataDir, { recursive: true });
18777
+ const tempPath = path31.join(userDataDir, `.chatlab-meta.json.${process.pid}.${Date.now()}.tmp`);
18778
+ try {
18779
+ fs34.writeFileSync(tempPath, `${JSON.stringify(meta, null, 2)}
18780
+ `, "utf-8");
18781
+ fs34.renameSync(tempPath, metaPath);
18782
+ } catch (error) {
18783
+ try {
18784
+ if (fs34.existsSync(tempPath)) fs34.unlinkSync(tempPath);
18785
+ } catch {
18786
+ }
18787
+ throw error;
18788
+ }
18789
+ }
18790
+ function isRecord3(value) {
18791
+ return typeof value === "object" && value !== null && !Array.isArray(value);
18792
+ }
18793
+ function isNonEmptyString(value) {
18794
+ return typeof value === "string" && value.trim().length > 0;
18795
+ }
18796
+ function isRuntimeKind(value) {
18797
+ return value === "cli" || value === "desktop" || value === "mcp" || value === "unknown";
18798
+ }
18799
+
17843
18800
  // ../../packages/node-runtime/src/migrations/chat-db-migrations.ts
18801
+ var CHAT_DB_COMPATIBILITY_RAISES = [
18802
+ {
18803
+ migrationVersion: 6,
18804
+ minRuntimeVersion: "0.25.1",
18805
+ dataCompatibilityVersion: 1,
18806
+ reason: "segment-schema",
18807
+ module: "chat-db-migration"
18808
+ }
18809
+ ];
18810
+ function raiseChatDbCompatibilityGate(pathProvider, runtime) {
18811
+ for (const compatibilityRaise of CHAT_DB_COMPATIBILITY_RAISES) {
18812
+ raiseDataDirMinRuntimeVersion(pathProvider, {
18813
+ minRuntimeVersion: compatibilityRaise.minRuntimeVersion,
18814
+ dataCompatibilityVersion: compatibilityRaise.dataCompatibilityVersion,
18815
+ reason: compatibilityRaise.reason,
18816
+ runtime,
18817
+ module: compatibilityRaise.module
18818
+ });
18819
+ }
18820
+ }
17844
18821
  function getChatDbMigrations(deps) {
17845
18822
  const hasColumn2 = (db, tableName, columnName) => {
17846
18823
  const tableInfo = db.pragma(`table_info(${tableName})`);
@@ -18015,13 +18992,120 @@ function getChatDbMigrations(deps) {
18015
18992
  db.exec("CREATE INDEX IF NOT EXISTS idx_session_time ON chat_session(start_ts, end_ts)");
18016
18993
  db.exec("CREATE INDEX IF NOT EXISTS idx_context_session ON message_context(session_id)");
18017
18994
  }
18995
+ },
18996
+ {
18997
+ version: 6,
18998
+ description: "Rename chat_session index tables to segment terminology",
18999
+ up: (db) => {
19000
+ const hasTable2 = (tableName) => {
19001
+ const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get(tableName);
19002
+ return !!row;
19003
+ };
19004
+ db.exec(`
19005
+ DROP INDEX IF EXISTS idx_session_time;
19006
+ DROP INDEX IF EXISTS idx_context_session;
19007
+ `);
19008
+ const hasLegacyChatSession = hasTable2("chat_session");
19009
+ const hasSegment = hasTable2("segment");
19010
+ if (hasLegacyChatSession && !hasSegment) {
19011
+ db.exec("ALTER TABLE chat_session RENAME TO segment");
19012
+ } else if (hasLegacyChatSession && hasSegment) {
19013
+ db.exec(`
19014
+ INSERT OR IGNORE INTO segment (id, start_ts, end_ts, message_count, is_manual, summary)
19015
+ SELECT id, start_ts, end_ts, message_count, is_manual, summary
19016
+ FROM chat_session;
19017
+ DROP TABLE chat_session;
19018
+ `);
19019
+ }
19020
+ if (hasTable2("message_context") && hasColumn2(db, "message_context", "session_id")) {
19021
+ db.exec("ALTER TABLE message_context RENAME COLUMN session_id TO segment_id");
19022
+ }
19023
+ db.exec(`
19024
+ CREATE TABLE IF NOT EXISTS segment (
19025
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19026
+ start_ts INTEGER NOT NULL,
19027
+ end_ts INTEGER NOT NULL,
19028
+ message_count INTEGER DEFAULT 0,
19029
+ is_manual INTEGER DEFAULT 0,
19030
+ summary TEXT
19031
+ );
19032
+
19033
+ CREATE TABLE IF NOT EXISTS message_context (
19034
+ message_id INTEGER PRIMARY KEY,
19035
+ segment_id INTEGER NOT NULL,
19036
+ topic_id INTEGER
19037
+ );
19038
+
19039
+ CREATE INDEX IF NOT EXISTS idx_segment_time ON segment(start_ts, end_ts);
19040
+ CREATE INDEX IF NOT EXISTS idx_context_segment ON message_context(segment_id);
19041
+ `);
19042
+ }
18018
19043
  }
18019
19044
  ];
18020
19045
  }
18021
19046
 
18022
19047
  // ../../packages/node-runtime/src/database-manager.ts
18023
- import * as fs34 from "fs";
18024
- import * as path31 from "path";
19048
+ import * as fs35 from "fs";
19049
+ import * as path32 from "path";
19050
+
19051
+ // ../../packages/node-runtime/src/better-sqlite3-adapter.ts
19052
+ import Database4 from "better-sqlite3";
19053
+ var BetterSqlitePreparedStatement = class {
19054
+ constructor(stmt) {
19055
+ this.stmt = stmt;
19056
+ this.readonly = stmt.readonly;
19057
+ }
19058
+ stmt;
19059
+ readonly;
19060
+ get(...params) {
19061
+ return this.stmt.get(...params);
19062
+ }
19063
+ all(...params) {
19064
+ return this.stmt.all(...params);
19065
+ }
19066
+ run(...params) {
19067
+ const result = this.stmt.run(...params);
19068
+ return {
19069
+ changes: result.changes,
19070
+ lastInsertRowid: result.lastInsertRowid
19071
+ };
19072
+ }
19073
+ };
19074
+ var BetterSqliteAdapter = class {
19075
+ constructor(db) {
19076
+ this.db = db;
19077
+ this.readonly = db.readonly;
19078
+ }
19079
+ db;
19080
+ readonly;
19081
+ exec(sql) {
19082
+ this.db.exec(sql);
19083
+ }
19084
+ prepare(sql) {
19085
+ return new BetterSqlitePreparedStatement(this.db.prepare(sql));
19086
+ }
19087
+ transaction(fn) {
19088
+ return this.db.transaction(fn)();
19089
+ }
19090
+ pragma(pragma) {
19091
+ return this.db.pragma(pragma);
19092
+ }
19093
+ close() {
19094
+ this.db.close();
19095
+ }
19096
+ };
19097
+ function openBetterSqliteDatabase(filePath, options) {
19098
+ const db = new Database4(filePath, {
19099
+ readonly: options?.readonly ?? false,
19100
+ nativeBinding: options?.nativeBinding
19101
+ });
19102
+ if (!options?.readonly) {
19103
+ db.pragma("journal_mode = WAL");
19104
+ }
19105
+ return new BetterSqliteAdapter(db);
19106
+ }
19107
+
19108
+ // ../../packages/node-runtime/src/database-manager.ts
18025
19109
  function createMigrationDeps(overrides) {
18026
19110
  return {
18027
19111
  tokenizeForFts,
@@ -18029,25 +19113,32 @@ function createMigrationDeps(overrides) {
18029
19113
  };
18030
19114
  }
18031
19115
  var DatabaseManager = class {
18032
- constructor(pathProvider, options) {
19116
+ constructor(pathProvider, options = {}) {
18033
19117
  this.pathProvider = pathProvider;
19118
+ if (!options.runtime && !options.allowMissingRuntimeForTests) {
19119
+ throw new Error("DatabaseManager runtime identity is required for data directory compatibility checks.");
19120
+ }
18034
19121
  this.nativeBinding = options?.nativeBinding;
18035
19122
  this.migrationDeps = createMigrationDeps(options?.migrationDeps);
19123
+ this.runtime = options?.runtime ?? null;
18036
19124
  }
18037
19125
  pathProvider;
18038
19126
  cache = /* @__PURE__ */ new Map();
18039
19127
  nativeBinding;
18040
19128
  migrationDeps;
19129
+ runtime;
18041
19130
  /**
18042
19131
  * Open a session DB (read-only by default, cached).
18043
19132
  */
18044
19133
  open(sessionId, options) {
19134
+ this.assertCompatible();
18045
19135
  if (this.cache.has(sessionId)) {
18046
19136
  return this.cache.get(sessionId);
18047
19137
  }
18048
19138
  const dbPath = this.getDbPath(sessionId);
18049
- if (!fs34.existsSync(dbPath)) return null;
19139
+ if (!fs35.existsSync(dbPath)) return null;
18050
19140
  this.migrateIfNeeded(dbPath);
19141
+ this.assertCompatible();
18051
19142
  const adapter = openBetterSqliteDatabase(dbPath, {
18052
19143
  readonly: options?.readonly ?? true,
18053
19144
  nativeBinding: this.nativeBinding
@@ -18061,7 +19152,9 @@ var DatabaseManager = class {
18061
19152
  nativeBinding: this.nativeBinding
18062
19153
  });
18063
19154
  try {
18064
- if (!isChatSessionDb(readonlyAdapter) || !needsMigration(readonlyAdapter, CURRENT_SCHEMA_VERSION)) return;
19155
+ if (!isChatSessionDb(readonlyAdapter)) return;
19156
+ this.raiseCompatibilityGateIfNeeded(getSchemaVersion(readonlyAdapter));
19157
+ if (!needsMigration(readonlyAdapter, CURRENT_SCHEMA_VERSION)) return;
18065
19158
  } finally {
18066
19159
  readonlyAdapter.close();
18067
19160
  }
@@ -18071,7 +19164,7 @@ var DatabaseManager = class {
18071
19164
  });
18072
19165
  try {
18073
19166
  const migrations = getChatDbMigrations(this.migrationDeps);
18074
- runMigrations(adapter, migrations);
19167
+ this.runMigrations(adapter, migrations);
18075
19168
  } finally {
18076
19169
  adapter.close();
18077
19170
  }
@@ -18081,6 +19174,7 @@ var DatabaseManager = class {
18081
19174
  * Falls back to `open(id, { readonly: false })` if migration is unnecessary.
18082
19175
  */
18083
19176
  openWritable(sessionId) {
19177
+ this.assertCompatible();
18084
19178
  const existing = this.cache.get(sessionId);
18085
19179
  if (existing && !existing.readonly) return existing;
18086
19180
  if (existing) {
@@ -18088,14 +19182,17 @@ var DatabaseManager = class {
18088
19182
  this.cache.delete(sessionId);
18089
19183
  }
18090
19184
  const dbPath = this.getDbPath(sessionId);
18091
- if (!fs34.existsSync(dbPath)) return null;
19185
+ if (!fs35.existsSync(dbPath)) return null;
18092
19186
  const adapter = openBetterSqliteDatabase(dbPath, {
18093
19187
  readonly: false,
18094
19188
  nativeBinding: this.nativeBinding
18095
19189
  });
18096
19190
  if (needsMigration(adapter, CURRENT_SCHEMA_VERSION)) {
18097
19191
  const migrations = getChatDbMigrations(this.migrationDeps);
18098
- runMigrations(adapter, migrations);
19192
+ this.runMigrations(adapter, migrations);
19193
+ this.assertCompatible();
19194
+ } else {
19195
+ this.raiseCompatibilityGateIfNeeded(getSchemaVersion(adapter));
18099
19196
  }
18100
19197
  this.cache.set(sessionId, adapter);
18101
19198
  return adapter;
@@ -18123,9 +19220,10 @@ var DatabaseManager = class {
18123
19220
  * 列举数据库目录下的所有聊天会话 ID
18124
19221
  */
18125
19222
  listSessionIds() {
19223
+ this.assertCompatible();
18126
19224
  const dbDir = this.pathProvider.getDatabaseDir();
18127
- if (!fs34.existsSync(dbDir)) return [];
18128
- return fs34.readdirSync(dbDir).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", "")).filter((id) => {
19225
+ if (!fs35.existsSync(dbDir)) return [];
19226
+ return fs35.readdirSync(dbDir).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", "")).filter((id) => {
18129
19227
  const db = this.open(id);
18130
19228
  if (!db) return false;
18131
19229
  return isChatSessionDb(db);
@@ -18135,9 +19233,70 @@ var DatabaseManager = class {
18135
19233
  * 获取数据库文件路径
18136
19234
  */
18137
19235
  getDbPath(sessionId) {
18138
- return path31.join(this.pathProvider.getDatabaseDir(), `${sessionId}.db`);
19236
+ return path32.join(this.pathProvider.getDatabaseDir(), `${sessionId}.db`);
19237
+ }
19238
+ openRawSessionDatabase(sessionId, options = {}) {
19239
+ this.assertCompatible();
19240
+ const dbPath = this.getDbPath(sessionId);
19241
+ if (!options.create && !fs35.existsSync(dbPath)) {
19242
+ throw new Error(`Session database not found: ${sessionId}`);
19243
+ }
19244
+ if (!options.readonly) {
19245
+ this.close(sessionId);
19246
+ fs35.mkdirSync(path32.dirname(dbPath), { recursive: true });
19247
+ }
19248
+ const adapter = openBetterSqliteDatabase(dbPath, {
19249
+ readonly: options.readonly ?? false,
19250
+ nativeBinding: this.nativeBinding
19251
+ });
19252
+ if (options.initializeChatTables) {
19253
+ adapter.exec(CHAT_DB_TABLES);
19254
+ }
19255
+ return adapter;
19256
+ }
19257
+ deleteSessionDatabaseFiles(sessionId) {
19258
+ this.close(sessionId);
19259
+ const dbPath = this.getDbPath(sessionId);
19260
+ for (const suffix of ["", "-wal", "-shm"]) {
19261
+ try {
19262
+ const filePath = dbPath + suffix;
19263
+ if (fs35.existsSync(filePath)) fs35.unlinkSync(filePath);
19264
+ } catch {
19265
+ }
19266
+ }
19267
+ }
19268
+ raiseCurrentChatDbCompatibilityGate() {
19269
+ if (!this.runtime) return;
19270
+ raiseChatDbCompatibilityGate(this.pathProvider, this.runtime);
19271
+ }
19272
+ assertCompatible() {
19273
+ if (!this.runtime) return;
19274
+ assertDataDirCompatible(this.pathProvider, this.runtime);
19275
+ }
19276
+ runMigrations(adapter, migrations) {
19277
+ const beforeVersion = getSchemaVersion(adapter);
19278
+ const migrated = runMigrations(adapter, migrations);
19279
+ if (!migrated || !this.runtime) return;
19280
+ const afterVersion = getSchemaVersion(adapter);
19281
+ if (shouldRaiseCompatibilityGate(beforeVersion, afterVersion)) {
19282
+ this.raiseCurrentChatDbCompatibilityGate();
19283
+ }
19284
+ }
19285
+ raiseCompatibilityGateIfNeeded(schemaVersion) {
19286
+ if (!this.runtime) return;
19287
+ if (shouldRepairCompatibilityGate(schemaVersion)) {
19288
+ this.raiseCurrentChatDbCompatibilityGate();
19289
+ }
18139
19290
  }
18140
19291
  };
19292
+ function shouldRaiseCompatibilityGate(beforeVersion, afterVersion) {
19293
+ return CHAT_DB_COMPATIBILITY_RAISES.some(
19294
+ (compatibilityRaise) => beforeVersion < compatibilityRaise.migrationVersion && afterVersion >= compatibilityRaise.migrationVersion
19295
+ );
19296
+ }
19297
+ function shouldRepairCompatibilityGate(schemaVersion) {
19298
+ return CHAT_DB_COMPATIBILITY_RAISES.some((compatibilityRaise) => schemaVersion >= compatibilityRaise.migrationVersion);
19299
+ }
18141
19300
 
18142
19301
  // ../../packages/node-runtime/src/jieba-nlp-provider.ts
18143
19302
  function createJiebaNlpProvider(dictType = "default") {
@@ -18183,7 +19342,7 @@ function exportFilterResultToMarkdown(params, deps, writer) {
18183
19342
  } else {
18184
19343
  writer.write(`- Mode: session filter
18185
19344
  `);
18186
- writer.write(`- Selected sessions: ${params.chatSessionIds?.length || 0}
19345
+ writer.write(`- Selected sessions: ${params.segmentIds?.length || 0}
18187
19346
  `);
18188
19347
  }
18189
19348
  writer.write("\n");
@@ -18341,7 +19500,7 @@ function exportConditionMode(db, params, writer, progress) {
18341
19500
  return { totalMessages, blockIndex };
18342
19501
  }
18343
19502
  function exportSessionMode(db, params, writer, progress) {
18344
- if (!params.chatSessionIds || params.chatSessionIds.length === 0) {
19503
+ if (!params.segmentIds || params.segmentIds.length === 0) {
18345
19504
  writer.write(`## Statistics
18346
19505
 
18347
19506
  - No sessions selected
@@ -18352,17 +19511,17 @@ function exportSessionMode(db, params, writer, progress) {
18352
19511
  progress?.({
18353
19512
  stage: "preparing",
18354
19513
  currentBlock: 0,
18355
- totalBlocks: params.chatSessionIds.length,
19514
+ totalBlocks: params.segmentIds.length,
18356
19515
  percentage: 10,
18357
- message: `Preparing to export ${params.chatSessionIds.length} sessions...`
19516
+ message: `Preparing to export ${params.segmentIds.length} sessions...`
18358
19517
  });
18359
19518
  const sessionsSql = `
18360
19519
  SELECT id, start_ts as startTs, end_ts as endTs
18361
- FROM chat_session
18362
- WHERE id IN (${params.chatSessionIds.map(() => "?").join(",")})
19520
+ FROM segment
19521
+ WHERE id IN (${params.segmentIds.map(() => "?").join(",")})
18363
19522
  ORDER BY start_ts ASC
18364
19523
  `;
18365
- const sessions = db.prepare(sessionsSql).all(...params.chatSessionIds);
19524
+ const sessions = db.prepare(sessionsSql).all(...params.segmentIds);
18366
19525
  const totalBlocks = sessions.length;
18367
19526
  writer.write(`## Statistics
18368
19527
 
@@ -18380,7 +19539,7 @@ function exportSessionMode(db, params, writer, progress) {
18380
19539
  FROM message_context mc
18381
19540
  JOIN message msg ON msg.id = mc.message_id
18382
19541
  JOIN member m ON msg.sender_id = m.id
18383
- WHERE mc.session_id = ?
19542
+ WHERE mc.segment_id = ?
18384
19543
  ORDER BY msg.ts ASC
18385
19544
  `;
18386
19545
  let totalMessages = 0;
@@ -18413,8 +19572,8 @@ function exportSessionMode(db, params, writer, progress) {
18413
19572
  }
18414
19573
 
18415
19574
  // ../../packages/node-runtime/src/preferences.ts
18416
- import * as fs35 from "fs";
18417
- import * as path32 from "path";
19575
+ import * as fs36 from "fs";
19576
+ import * as path33 from "path";
18418
19577
  var DEFAULTS = {
18419
19578
  pinnedSessionIds: [],
18420
19579
  aiPreprocessConfig: {
@@ -18434,6 +19593,7 @@ var DEFAULTS = {
18434
19593
  exportFormat: "markdown",
18435
19594
  sqlExportFormat: "csv",
18436
19595
  enableAutoSkill: true,
19596
+ chartAutoMode: "suggest",
18437
19597
  searchContextBefore: 2,
18438
19598
  searchContextAfter: 2,
18439
19599
  contextCompression: {
@@ -18450,19 +19610,20 @@ var DEFAULTS = {
18450
19610
  defaultSchemeId: null,
18451
19611
  sessionSchemeOverrides: {}
18452
19612
  },
18453
- filterHistory: []
19613
+ filterHistory: [],
19614
+ thinkingLevels: {}
18454
19615
  };
18455
19616
  var PreferencesManager = class {
18456
19617
  filePath;
18457
19618
  cache = null;
18458
19619
  constructor(systemDir) {
18459
- this.filePath = path32.join(systemDir, "preferences.json");
19620
+ this.filePath = path33.join(systemDir, "preferences.json");
18460
19621
  }
18461
19622
  load() {
18462
19623
  if (this.cache) return this.cache;
18463
19624
  try {
18464
- if (fs35.existsSync(this.filePath)) {
18465
- const raw = fs35.readFileSync(this.filePath, "utf-8");
19625
+ if (fs36.existsSync(this.filePath)) {
19626
+ const raw = fs36.readFileSync(this.filePath, "utf-8");
18466
19627
  const parsed = JSON.parse(raw);
18467
19628
  const migrated = this.migrateLegacyDesensitizeRules(parsed, raw);
18468
19629
  this.cache = this.mergeDefaults(migrated.preferences);
@@ -18518,7 +19679,8 @@ var PreferencesManager = class {
18518
19679
  customKeywordTemplates: partial.customKeywordTemplates ?? DEFAULTS.customKeywordTemplates,
18519
19680
  deletedPresetTemplateIds: partial.deletedPresetTemplateIds ?? DEFAULTS.deletedPresetTemplateIds,
18520
19681
  wordFilter: partial.wordFilter ? { ...DEFAULTS.wordFilter, ...partial.wordFilter } : { ...DEFAULTS.wordFilter },
18521
- filterHistory: partial.filterHistory ?? DEFAULTS.filterHistory
19682
+ filterHistory: partial.filterHistory ?? DEFAULTS.filterHistory,
19683
+ thinkingLevels: partial.thinkingLevels ?? DEFAULTS.thinkingLevels
18522
19684
  };
18523
19685
  }
18524
19686
  normalizeAiPreprocessConfig(partial) {
@@ -18574,21 +19736,21 @@ var PreferencesManager = class {
18574
19736
  };
18575
19737
  }
18576
19738
  backupLegacyPreferences(rawContent) {
18577
- const backupDir = path32.join(path32.dirname(this.filePath), "backups");
18578
- if (!fs35.existsSync(backupDir)) {
18579
- fs35.mkdirSync(backupDir, { recursive: true });
19739
+ const backupDir = path33.join(path33.dirname(this.filePath), "backups");
19740
+ if (!fs36.existsSync(backupDir)) {
19741
+ fs36.mkdirSync(backupDir, { recursive: true });
18580
19742
  }
18581
19743
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18582
- const backupPath = path32.join(backupDir, `preferences-pre-desensitize-groups-${timestamp}.json`);
18583
- fs35.writeFileSync(backupPath, rawContent, "utf-8");
19744
+ const backupPath = path33.join(backupDir, `preferences-pre-desensitize-groups-${timestamp}.json`);
19745
+ fs36.writeFileSync(backupPath, rawContent, "utf-8");
18584
19746
  console.info(`[Preferences] Backed up legacy desensitize rules to ${backupPath}`);
18585
19747
  }
18586
19748
  writePreferences(preferences) {
18587
- const dir = path32.dirname(this.filePath);
18588
- if (!fs35.existsSync(dir)) {
18589
- fs35.mkdirSync(dir, { recursive: true });
19749
+ const dir = path33.dirname(this.filePath);
19750
+ if (!fs36.existsSync(dir)) {
19751
+ fs36.mkdirSync(dir, { recursive: true });
18590
19752
  }
18591
- fs35.writeFileSync(this.filePath, JSON.stringify(preferences, null, 2), "utf-8");
19753
+ fs36.writeFileSync(this.filePath, JSON.stringify(preferences, null, 2), "utf-8");
18592
19754
  }
18593
19755
  /**
18594
19756
  * Deep merge: arrays are replaced (not concatenated), objects are merged.
@@ -18708,8 +19870,8 @@ function* serializeChatLabToJsonl(data) {
18708
19870
  }
18709
19871
 
18710
19872
  // ../../packages/node-runtime/src/merger/temp-db.ts
18711
- import * as fs36 from "fs";
18712
- import * as path33 from "path";
19873
+ import * as fs37 from "fs";
19874
+ import * as path34 from "path";
18713
19875
  var TEMP_DB_SCHEMA = `
18714
19876
  CREATE TABLE IF NOT EXISTS meta (
18715
19877
  name TEXT NOT NULL,
@@ -18954,21 +20116,21 @@ function exportSessionToJson(db) {
18954
20116
  }
18955
20117
  function deleteTempDatabase(dbPath) {
18956
20118
  try {
18957
- if (fs36.existsSync(dbPath)) fs36.unlinkSync(dbPath);
20119
+ if (fs37.existsSync(dbPath)) fs37.unlinkSync(dbPath);
18958
20120
  const walPath = dbPath + "-wal";
18959
20121
  const shmPath = dbPath + "-shm";
18960
- if (fs36.existsSync(walPath)) fs36.unlinkSync(walPath);
18961
- if (fs36.existsSync(shmPath)) fs36.unlinkSync(shmPath);
20122
+ if (fs37.existsSync(walPath)) fs37.unlinkSync(walPath);
20123
+ if (fs37.existsSync(shmPath)) fs37.unlinkSync(shmPath);
18962
20124
  } catch {
18963
20125
  }
18964
20126
  }
18965
20127
  function cleanupTempDatabases(tempDir) {
18966
20128
  try {
18967
- if (!fs36.existsSync(tempDir)) return;
18968
- const files = fs36.readdirSync(tempDir);
20129
+ if (!fs37.existsSync(tempDir)) return;
20130
+ const files = fs37.readdirSync(tempDir);
18969
20131
  for (const file of files) {
18970
20132
  if (file.startsWith("merge_") && file.endsWith(".db")) {
18971
- deleteTempDatabase(path33.join(tempDir, file));
20133
+ deleteTempDatabase(path34.join(tempDir, file));
18972
20134
  }
18973
20135
  }
18974
20136
  } catch {
@@ -19129,19 +20291,19 @@ __export(summary_service_exports, {
19129
20291
  function buildSummaryDeps(db, llmConfig, deps) {
19130
20292
  const piModel = deps.buildPiModel(llmConfig);
19131
20293
  return {
19132
- loadMessages(chatSessionId, limit = 500) {
19133
- const data = getSessionMessages(db, chatSessionId, limit);
20294
+ loadMessages(segmentId, limit = 500) {
20295
+ const data = getSegmentMessages(db, segmentId, limit);
19134
20296
  if (!data) return null;
19135
20297
  return data.messages.map((m) => ({ senderName: m.senderName, content: m.content }));
19136
20298
  },
19137
- saveSummary(chatSessionId, summary) {
19138
- saveChatSessionSummary(db, chatSessionId, summary);
20299
+ saveSummary(segmentId, summary) {
20300
+ saveSegmentSummary(db, segmentId, summary);
19139
20301
  },
19140
- getSummary(chatSessionId) {
19141
- return getChatSessionSummary(db, chatSessionId);
20302
+ getSummary(segmentId) {
20303
+ return getSegmentSummary(db, segmentId);
19142
20304
  },
19143
20305
  async llmComplete(systemPrompt, userPrompt, options) {
19144
- const result = await completeSimple4(
20306
+ const result = await completeSimple6(
19145
20307
  piModel,
19146
20308
  {
19147
20309
  systemPrompt,
@@ -19154,14 +20316,14 @@ function buildSummaryDeps(db, llmConfig, deps) {
19154
20316
  t: (key) => key
19155
20317
  };
19156
20318
  }
19157
- async function generateSummary(adapter, sessionId, chatSessionId, serviceDeps, options) {
20319
+ async function generateSummary(adapter, sessionId, segmentId, serviceDeps, options) {
19158
20320
  const llmConfig = serviceDeps.getLlmConfig();
19159
20321
  if (!llmConfig) {
19160
20322
  return { success: false, error: "No LLM configuration available" };
19161
20323
  }
19162
20324
  const db = adapter.ensureWritable(sessionId);
19163
20325
  const deps = buildSummaryDeps(db, llmConfig, serviceDeps);
19164
- return generateSessionSummary(deps, chatSessionId, options);
20326
+ return generateSessionSummary(deps, segmentId, options);
19165
20327
  }
19166
20328
  async function generateAllSummaries(adapter, sessionId, serviceDeps, options) {
19167
20329
  const llmConfig = serviceDeps.getLlmConfig();
@@ -19180,17 +20342,17 @@ async function generateAllSummaries(adapter, sessionId, serviceDeps, options) {
19180
20342
  }
19181
20343
  return { success, failed, total: chatSessions.length };
19182
20344
  }
19183
- function checkCanGenerate(adapter, sessionId, chatSessionIds) {
20345
+ function checkCanGenerate(adapter, sessionId, segmentIds) {
19184
20346
  const db = adapter.ensureReadonly(sessionId);
19185
20347
  const deps = {
19186
- loadMessages(chatSessionId, limit = 500) {
19187
- const data = getSessionMessages(db, chatSessionId, limit);
20348
+ loadMessages(segmentId, limit = 500) {
20349
+ const data = getSegmentMessages(db, segmentId, limit);
19188
20350
  if (!data) return null;
19189
20351
  return data.messages.map((m) => ({ senderName: m.senderName, content: m.content }));
19190
20352
  },
19191
20353
  t: (key) => key
19192
20354
  };
19193
- const resultMap = checkSessionsCanGenerateSummary(deps, chatSessionIds);
20355
+ const resultMap = checkSessionsCanGenerateSummary(deps, segmentIds);
19194
20356
  const result = {};
19195
20357
  for (const [id, info] of resultMap) {
19196
20358
  result[id] = info;
@@ -19224,7 +20386,7 @@ function exportMarkdown(adapter, params) {
19224
20386
  }
19225
20387
 
19226
20388
  // ../../packages/node-runtime/src/services/adapters/database-manager-adapter.ts
19227
- import * as fs37 from "fs";
20389
+ import * as fs38 from "fs";
19228
20390
  function createDatabaseManagerAdapter(dbManager) {
19229
20391
  return {
19230
20392
  listSessionIds: () => dbManager.listSessionIds(),
@@ -19235,8 +20397,8 @@ function createDatabaseManagerAdapter(dbManager) {
19235
20397
  deleteSessionFile(sessionId) {
19236
20398
  dbManager.close(sessionId);
19237
20399
  const dbPath = dbManager.getDbPath(sessionId);
19238
- if (!fs37.existsSync(dbPath)) return false;
19239
- fs37.unlinkSync(dbPath);
20400
+ if (!fs38.existsSync(dbPath)) return false;
20401
+ fs38.unlinkSync(dbPath);
19240
20402
  return true;
19241
20403
  },
19242
20404
  ensureReadonly(sessionId) {
@@ -19257,8 +20419,8 @@ function createDatabaseManagerAdapter(dbManager) {
19257
20419
  }
19258
20420
 
19259
20421
  // ../../packages/node-runtime/src/services/merge-cache.ts
19260
- import * as fs38 from "fs";
19261
- import * as path34 from "path";
20422
+ import * as fs39 from "fs";
20423
+ import * as path35 from "path";
19262
20424
  import * as crypto from "crypto";
19263
20425
  var MergeSessionCache = class {
19264
20426
  cache = /* @__PURE__ */ new Map();
@@ -19267,15 +20429,15 @@ var MergeSessionCache = class {
19267
20429
  constructor(pathProvider, options) {
19268
20430
  this.tempDir = pathProvider.getTempDir();
19269
20431
  this.nativeBinding = options?.nativeBinding;
19270
- if (!fs38.existsSync(this.tempDir)) {
19271
- fs38.mkdirSync(this.tempDir, { recursive: true });
20432
+ if (!fs39.existsSync(this.tempDir)) {
20433
+ fs39.mkdirSync(this.tempDir, { recursive: true });
19272
20434
  }
19273
20435
  }
19274
20436
  generateTempDbPath(filename) {
19275
20437
  const ts = Date.now();
19276
20438
  const rand = Math.random().toString(36).slice(2, 8);
19277
20439
  const safeName = filename.replace(/[/\\?%*:|"<>]/g, "_").slice(0, 40);
19278
- return path34.join(this.tempDir, `merge_${safeName}_${ts}_${rand}.db`);
20440
+ return path35.join(this.tempDir, `merge_${safeName}_${ts}_${rand}.db`);
19279
20441
  }
19280
20442
  /** Store a parsed file's temp DB and return a handle for subsequent requests. */
19281
20443
  store(filename, tempDbPath) {
@@ -19286,7 +20448,7 @@ var MergeSessionCache = class {
19286
20448
  /** Open a TempDbReader for a given handle. Caller must close the reader. */
19287
20449
  openReader(handle) {
19288
20450
  const entry = this.cache.get(handle);
19289
- if (!entry || !fs38.existsSync(entry.tempDbPath)) return null;
20451
+ if (!entry || !fs39.existsSync(entry.tempDbPath)) return null;
19290
20452
  const adapter = openBetterSqliteDatabase(entry.tempDbPath, {
19291
20453
  readonly: true,
19292
20454
  nativeBinding: this.nativeBinding
@@ -19339,7 +20501,6 @@ export {
19339
20501
  writeAuthProfile,
19340
20502
  MigrationRunner,
19341
20503
  ALL_MIGRATIONS,
19342
- openBetterSqliteDatabase,
19343
20504
  writeParseResultToDb,
19344
20505
  getTimeRange,
19345
20506
  getAvailableYears,
@@ -19356,9 +20517,9 @@ export {
19356
20517
  getSessionOverview,
19357
20518
  getDatabaseSchema,
19358
20519
  getChatOverview,
19359
- searchSessions,
19360
- getSessionMessages,
19361
- getSessionSummaries,
20520
+ searchSegments,
20521
+ getSegmentMessages,
20522
+ getSegmentSummaries,
19362
20523
  generateSessionIndex,
19363
20524
  generateIncrementalSessionIndex,
19364
20525
  queryMessages,
@@ -19387,6 +20548,7 @@ export {
19387
20548
  isNewerStableVersion,
19388
20549
  buildChartPayload,
19389
20550
  POS_TAG_DEFINITIONS,
20551
+ normalizeBuiltinToolNames,
19390
20552
  BUILTIN_TOOL_CATALOG,
19391
20553
  BUILTIN_PROVIDERS,
19392
20554
  BUILTIN_MODELS,
@@ -19394,7 +20556,6 @@ export {
19394
20556
  stripAvatarFields,
19395
20557
  generateMessageKey,
19396
20558
  buildMemberIdMap,
19397
- CHAT_DB_TABLES,
19398
20559
  CHAT_DB_SCHEMA,
19399
20560
  FTS_TABLE_SCHEMA,
19400
20561
  detectFormat,
@@ -19421,7 +20582,7 @@ export {
19421
20582
  formatAIError,
19422
20583
  parseAssistantFile,
19423
20584
  AssistantManager,
19424
- AIConversationManager,
20585
+ AIChatManager,
19425
20586
  initTokenizer,
19426
20587
  countMessagesTokens,
19427
20588
  SkillManagerCore,
@@ -19429,6 +20590,8 @@ export {
19429
20590
  createActivateSkillTool,
19430
20591
  CHART_CAPABILITY_CORE_TOOLS,
19431
20592
  getAllowedBuiltinToolsForChartAutoSkill,
20593
+ shouldUseChartCapabilityForMessage,
20594
+ getChartPlannerCapabilityForMessage,
19432
20595
  resolveChartRuntimeForRequest,
19433
20596
  getChartCapabilitySkill,
19434
20597
  buildSkillMenuWithBuiltinChart,
@@ -19442,6 +20605,12 @@ export {
19442
20605
  applyPreprocessingPipeline,
19443
20606
  DEFAULT_MAX_TOOL_ROUNDS,
19444
20607
  runAgentCore,
20608
+ createDataSnapshotFromOverview,
20609
+ decideRequestRoute,
20610
+ createLlmRouteDecider,
20611
+ createAnalysisPlanner,
20612
+ createPlanContentBlock,
20613
+ buildPlanGuidance,
19445
20614
  AgentEventHandler,
19446
20615
  buildSystemPrompt,
19447
20616
  createAiTranslate,
@@ -19459,6 +20628,8 @@ export {
19459
20628
  applyPendingNodeDataDirMigrationIfNeeded,
19460
20629
  NodePathProvider,
19461
20630
  getDefaultNodeUserDataDir,
20631
+ DataDirCompatibilityError,
20632
+ assertDataDirCompatible,
19462
20633
  DatabaseManager,
19463
20634
  createJiebaNlpProvider,
19464
20635
  PreferencesManager,
@@ -19475,4 +20646,4 @@ export {
19475
20646
  createDatabaseManagerAdapter,
19476
20647
  MergeSessionCache
19477
20648
  };
19478
- //# sourceMappingURL=chunk-4OMV4VNH.mjs.map
20649
+ //# sourceMappingURL=chunk-QY67NN65.mjs.map