chatlab-cli 0.22.0 → 0.23.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 (77) hide show
  1. package/dist/{chunk-3UBWLWXB.mjs → chunk-3MX7VF2B.mjs} +2 -2
  2. package/dist/chunk-5ADKYYNA.mjs +28 -0
  3. package/dist/chunk-5ADKYYNA.mjs.map +1 -0
  4. package/dist/{chunk-4U45E7TO.mjs → chunk-643S2N2U.mjs} +2 -2
  5. package/dist/{chunk-AMMSVJG4.mjs → chunk-D2BR3MFN.mjs} +132 -43
  6. package/dist/chunk-D2BR3MFN.mjs.map +1 -0
  7. package/dist/{chunk-6NB475AA.mjs → chunk-FJ35V3TE.mjs} +2 -17
  8. package/dist/chunk-FJ35V3TE.mjs.map +1 -0
  9. package/dist/{chunk-QJQJIUUS.mjs → chunk-G7Q7EQ3V.mjs} +618 -108
  10. package/dist/chunk-G7Q7EQ3V.mjs.map +1 -0
  11. package/dist/{chunk-6GLMVWGA.mjs → chunk-NTWDG52H.mjs} +66 -68
  12. package/dist/chunk-NTWDG52H.mjs.map +1 -0
  13. package/dist/{chunk-D2XJ4Z7G.mjs → chunk-WTQ7LAZR.mjs} +2 -2
  14. package/dist/cli.mjs +4 -3
  15. package/dist/{http-R5UECMPJ.mjs → http-2DN3XOXO.mjs} +8 -7
  16. package/dist/{import-3LOWRXWA.mjs → import-LCBSBLYF.mjs} +4 -4
  17. package/dist/index.mjs +10 -9
  18. package/dist/{mcp-WBHNLED2.mjs → mcp-AR7HBIK4.mjs} +8 -6
  19. package/dist/{mcp-WBHNLED2.mjs.map → mcp-AR7HBIK4.mjs.map} +1 -1
  20. package/dist/service-WNEVPQ4N.mjs +258 -0
  21. package/dist/service-WNEVPQ4N.mjs.map +1 -0
  22. package/dist/{src-UHM2UDRD.mjs → src-EGXULYFQ.mjs} +2 -2
  23. package/dist/{stream-import-V6Z7DXSW.mjs → stream-import-QM5R5FDG.mjs} +3 -3
  24. package/dist/{update-checker-MQMQ6A4B.mjs → update-checker-JG5K67O2.mjs} +3 -2
  25. package/dist/{update-checker-MQMQ6A4B.mjs.map → update-checker-JG5K67O2.mjs.map} +1 -1
  26. package/dist-web/assets/{DebugToolsPanel-DkbsB6lr.css → DebugToolsPanel-D9O_4J04.css} +1 -1
  27. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-hp9Mjpub.js +161 -0
  28. package/dist-web/assets/{EChartWordcloud-Ubal_aYg.js → EChartWordcloud-Cenbzcyj.js} +1 -1
  29. package/dist-web/assets/MessageExportModal-CvocO88D.js +1 -0
  30. package/dist-web/assets/electron-DpE69lO4.js +1 -0
  31. package/dist-web/assets/electron-SyPZX9OU.js +1 -0
  32. package/dist-web/assets/{fetch-BS4SlQjA.js → fetch-B7OBmANJ.js} +1 -1
  33. package/dist-web/assets/{fetch-o5NJ2uWE.js → fetch-BikSxuED.js} +1 -1
  34. package/dist-web/assets/fetch-BwMZFwbF.js +1 -0
  35. package/dist-web/assets/fetch-CfTcWxqG.js +1 -0
  36. package/dist-web/assets/fetch-DTP8Q782.js +17 -0
  37. package/dist-web/assets/{fetch-W43zfemj.js → fetch-LBHcBBkv.js} +1 -1
  38. package/dist-web/assets/http-C741fIwb.js +1 -0
  39. package/dist-web/assets/index-B6dkyIiA.js +2 -0
  40. package/dist-web/assets/index-CALU1QPF.js +163 -0
  41. package/dist-web/assets/{index-BJUGnYn5.js → index-CHkXbqMb.js} +1 -1
  42. package/dist-web/assets/index-CvuHOeoU.css +1 -0
  43. package/dist-web/assets/{index-BE64tUWq.css → index-CxMP0DNd.css} +1 -1
  44. package/dist-web/assets/index-DOS5o2_H.js +7 -0
  45. package/dist-web/assets/{index-CbQMfheg.css → index-DuepdDzz.css} +1 -1
  46. package/dist-web/assets/index-niwrSprs.js +173 -0
  47. package/dist-web/assets/model-catalog-DUuKjIVU.js +1 -0
  48. package/dist-web/assets/{vendor-nuxt-ui-7OAuiz_i.js → vendor-nuxt-ui-CuCZ1aRl.js} +2 -2
  49. package/dist-web/assets/vendor-reka-ui-BrTYuIfH.js +11 -0
  50. package/dist-web/assets/{web-CZe0Ismu.js → web-DGd63PrV.js} +1 -1
  51. package/dist-web/index.html +4 -4
  52. package/package.json +1 -1
  53. package/dist/chunk-6GLMVWGA.mjs.map +0 -1
  54. package/dist/chunk-6NB475AA.mjs.map +0 -1
  55. package/dist/chunk-AMMSVJG4.mjs.map +0 -1
  56. package/dist/chunk-QJQJIUUS.mjs.map +0 -1
  57. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-CxYyne-Q.js +0 -147
  58. package/dist-web/assets/MessageExportModal-De5_tX6a.js +0 -1
  59. package/dist-web/assets/electron-8qi0d92j.js +0 -1
  60. package/dist-web/assets/electron-H65bUnhW.js +0 -1
  61. package/dist-web/assets/fetch-CclRWOHf.js +0 -1
  62. package/dist-web/assets/fetch-Q-PqsiMb.js +0 -17
  63. package/dist-web/assets/fetch-cFjLAPG2.js +0 -1
  64. package/dist-web/assets/http-CeXMt-Fv.js +0 -1
  65. package/dist-web/assets/index-B_31fXeB.js +0 -2
  66. package/dist-web/assets/index-CbOwHYzO.css +0 -1
  67. package/dist-web/assets/index-DKMUn8F5.js +0 -173
  68. package/dist-web/assets/index-D_ksezTw.js +0 -163
  69. package/dist-web/assets/index-Dhg3SDb6.js +0 -7
  70. package/dist-web/assets/vendor-reka-ui-CW2cIgMu.js +0 -11
  71. /package/dist/{chunk-3UBWLWXB.mjs.map → chunk-3MX7VF2B.mjs.map} +0 -0
  72. /package/dist/{chunk-4U45E7TO.mjs.map → chunk-643S2N2U.mjs.map} +0 -0
  73. /package/dist/{chunk-D2XJ4Z7G.mjs.map → chunk-WTQ7LAZR.mjs.map} +0 -0
  74. /package/dist/{http-R5UECMPJ.mjs.map → http-2DN3XOXO.mjs.map} +0 -0
  75. /package/dist/{import-3LOWRXWA.mjs.map → import-LCBSBLYF.mjs.map} +0 -0
  76. /package/dist/{src-UHM2UDRD.mjs.map → src-EGXULYFQ.mjs.map} +0 -0
  77. /package/dist/{stream-import-V6Z7DXSW.mjs.map → stream-import-QM5R5FDG.mjs.map} +0 -0
@@ -2022,6 +2022,25 @@ function getSessionSummaries(db, options) {
2022
2022
  });
2023
2023
  }
2024
2024
  var DEFAULT_SESSION_GAP_THRESHOLD = 1800;
2025
+ function getSessionIndexStats(db) {
2026
+ let sessionCount = 0;
2027
+ if (hasTable(db, "chat_session")) {
2028
+ try {
2029
+ const row = db.prepare("SELECT COUNT(*) as count FROM chat_session").get();
2030
+ sessionCount = row?.count ?? 0;
2031
+ } catch {
2032
+ }
2033
+ }
2034
+ let gapThreshold = DEFAULT_SESSION_GAP_THRESHOLD;
2035
+ try {
2036
+ const meta = db.prepare("SELECT session_gap_threshold FROM meta LIMIT 1").get();
2037
+ if (meta?.session_gap_threshold) {
2038
+ gapThreshold = meta.session_gap_threshold;
2039
+ }
2040
+ } catch {
2041
+ }
2042
+ return { sessionCount, hasIndex: sessionCount > 0, gapThreshold };
2043
+ }
2025
2044
  function getChatSessionList(db) {
2026
2045
  if (!hasTable(db, "chat_session")) return [];
2027
2046
  try {
@@ -5774,6 +5793,138 @@ function needsStreamingThinkParsing(provider, _modelId) {
5774
5793
  return provider === "minimax";
5775
5794
  }
5776
5795
 
5796
+ // ../../packages/core/src/ai/thinking.ts
5797
+ var TYPE_LEVEL_MAP = {
5798
+ gpt5: {
5799
+ minimal: "minimal",
5800
+ low: "low",
5801
+ medium: "medium",
5802
+ high: "high"
5803
+ },
5804
+ gpt5_1: {
5805
+ off: "none",
5806
+ low: "low",
5807
+ medium: "medium",
5808
+ high: "high",
5809
+ xhigh: null
5810
+ },
5811
+ gpt5_2plus: {
5812
+ off: "none",
5813
+ minimal: "minimal",
5814
+ low: "low",
5815
+ medium: "medium",
5816
+ high: "high",
5817
+ xhigh: "xhigh"
5818
+ },
5819
+ grok: {
5820
+ minimal: null,
5821
+ low: "low",
5822
+ medium: null,
5823
+ high: "high",
5824
+ xhigh: null
5825
+ },
5826
+ kimi: {
5827
+ off: "none",
5828
+ auto: "auto",
5829
+ minimal: null,
5830
+ low: "low",
5831
+ medium: "medium",
5832
+ high: "high",
5833
+ xhigh: null
5834
+ },
5835
+ doubao: {
5836
+ off: null,
5837
+ auto: "auto",
5838
+ high: "high"
5839
+ },
5840
+ deepseek_hybrid: {
5841
+ off: null,
5842
+ auto: "auto",
5843
+ high: "high"
5844
+ }
5845
+ };
5846
+ var REASONING_FALLBACK_REGEX = /^(?!.*-non-reasoning\b)(o\d+(?:-[\w-]+)?|.*\b(?:reasoning|reasoner|thinking|think)\b.*|.*-[rR]\d+.*|.*\bqwq(?:-[\w-]+)?\b.*|.*\bhunyuan-t1(?:-[\w-]+)?\b.*|.*\bgrok-(?:3-mini|4|4-fast|build)(?:-[\w-]+)?\b.*)$/i;
5847
+ function classifyThinkingType(provider, modelId) {
5848
+ const id = modelId.toLowerCase();
5849
+ const prov = provider.toLowerCase();
5850
+ if (id.includes("-non-reasoning")) return "none";
5851
+ if (prov === "anthropic") return "none";
5852
+ if (prov === "gemini") {
5853
+ if (/gemini-(?:2\.5|3(?:\.\d+)?-(?:flash|pro)|flash-latest|pro-latest)/i.test(id)) return "gemini";
5854
+ return "none";
5855
+ }
5856
+ if (prov === "qwen" || /\bqwen|qwq/i.test(id)) return "qwen";
5857
+ if (/deepseek[_-]v([4-9]|\d{2,})/i.test(id) || /deepseek-ai\/deepseek-r1/i.test(id)) return "deepseek_v4";
5858
+ if (/deepseek[_-]chat|deepseek[_-]v3/i.test(id)) return "deepseek_hybrid";
5859
+ if (prov === "openai" || prov === "openai-compatible") {
5860
+ if (/gpt-5\.[2-9]|gpt-5\.[1-9]\d/.test(id)) return "gpt5_2plus";
5861
+ if (/gpt-5\.1/.test(id)) return "gpt5_1";
5862
+ if (/gpt-5/.test(id)) return "gpt5";
5863
+ if (/^o\d/.test(id)) return "o_series";
5864
+ }
5865
+ if (/^o\d/.test(id)) return "o_series";
5866
+ if (/\bgrok-(?:3-mini|4|4-fast|build)\b/i.test(id)) return "grok";
5867
+ if (/\bkimi-k(?:2-thinking|2\.[5-9]|[3-9])/i.test(id)) return "kimi";
5868
+ if (prov === "kimi" && /thinking/i.test(id)) return "kimi";
5869
+ if (/\bdoubao-.*seed/i.test(id) || prov === "doubao" && /seed/i.test(id)) return "doubao";
5870
+ if (/\bhunyuan-(?:t1|a13b)/i.test(id)) return "hunyuan";
5871
+ if (prov === "glm" || /\bglm-?(?:5|4\.[5-7]|z1|zero)/i.test(id)) return "qwen";
5872
+ if (prov === "siliconflow" && /r[1-9]/i.test(id)) return "deepseek_v4";
5873
+ if (prov === "openrouter") {
5874
+ if (/deepseek.*(v[4-9]|r\d)/i.test(id)) return "deepseek_v4";
5875
+ if (/deepseek[_-]?(chat|v3)/i.test(id)) return "deepseek_hybrid";
5876
+ if (/\bgrok-(?:3-mini|4|4-fast|build)\b/i.test(id)) return "grok";
5877
+ if (/\bkimi-k(?:2-thinking|2\.[5-9]|[3-9])/i.test(id)) return "kimi";
5878
+ if (REASONING_FALLBACK_REGEX.test(id)) return "default";
5879
+ return "none";
5880
+ }
5881
+ if (/\bmimo-v2/i.test(id)) return "deepseek_hybrid";
5882
+ if (/\bminimax-m[123]/i.test(id)) return "deepseek_hybrid";
5883
+ if (/\bseed-oss/i.test(id)) return "deepseek_hybrid";
5884
+ if (/\bbaichuan-m[23]/i.test(id)) return "default";
5885
+ if (/\bmagistral/i.test(id)) return "default";
5886
+ if (/\bmistral-small-2603/i.test(id)) return "default";
5887
+ if (/\bstep-(?:3|r1-v-mini)/i.test(id)) return "default";
5888
+ if (/\bgemma-?4/i.test(id)) return "default";
5889
+ if (/\bpangu-pro-moe/i.test(id)) return "default";
5890
+ if (/\bsonar-deep-research/i.test(id)) return "default";
5891
+ if (/\bring-(?:1t|mini|flash)/i.test(id)) return "default";
5892
+ if (/\bqwen|qwq/i.test(id)) return "qwen";
5893
+ if (/deepseek[_-]?r\d|deepseek[_-]?v[4-9]/i.test(id)) return "deepseek_v4";
5894
+ if (/^o\d/i.test(id)) return "o_series";
5895
+ if (REASONING_FALLBACK_REGEX.test(id)) return "default";
5896
+ return "none";
5897
+ }
5898
+ function isReasoningModel(provider, modelId) {
5899
+ return classifyThinkingType(provider, modelId) !== "none";
5900
+ }
5901
+ function getThinkingCompat(provider, modelId) {
5902
+ const type = classifyThinkingType(provider, modelId);
5903
+ if (type === "none") return {};
5904
+ if (type === "gemini") return {};
5905
+ if (type === "qwen" || type === "hunyuan") {
5906
+ return { thinkingFormat: "qwen" };
5907
+ }
5908
+ if (type === "deepseek_v4") {
5909
+ return {
5910
+ thinkingFormat: "deepseek",
5911
+ thinkingLevelMap: { high: "high", xhigh: "max", minimal: null, low: null, medium: null }
5912
+ };
5913
+ }
5914
+ if (type === "deepseek_hybrid") {
5915
+ return {
5916
+ thinkingFormat: "deepseek",
5917
+ thinkingLevelMap: { auto: "auto", high: "high", minimal: null, low: null, medium: null, xhigh: null }
5918
+ };
5919
+ }
5920
+ const compat = { supportsReasoningEffort: true };
5921
+ const levelMap = TYPE_LEVEL_MAP[type];
5922
+ if (levelMap) {
5923
+ compat.thinkingLevelMap = levelMap;
5924
+ }
5925
+ return compat;
5926
+ }
5927
+
5777
5928
  // ../../packages/core/src/import/dedup.ts
5778
5929
  function fnv1a64(input) {
5779
5930
  let h0 = 2166136261;
@@ -11657,6 +11808,7 @@ var AIConversationManager = class {
11657
11808
  id TEXT PRIMARY KEY,
11658
11809
  session_id TEXT NOT NULL,
11659
11810
  title TEXT,
11811
+ active_message_id TEXT,
11660
11812
  created_at INTEGER NOT NULL,
11661
11813
  updated_at INTEGER NOT NULL
11662
11814
  );
@@ -11670,6 +11822,9 @@ var AIConversationManager = class {
11670
11822
  data_keywords TEXT,
11671
11823
  data_message_count INTEGER,
11672
11824
  content_blocks TEXT,
11825
+ parent_id TEXT,
11826
+ sibling_group_id TEXT,
11827
+ branch_index INTEGER DEFAULT 0,
11673
11828
  FOREIGN KEY(conversation_id) REFERENCES ai_conversation(id) ON DELETE CASCADE
11674
11829
  );
11675
11830
 
@@ -11683,6 +11838,7 @@ var AIConversationManager = class {
11683
11838
  try {
11684
11839
  const messageTableInfo = db.pragma("table_info(ai_message)");
11685
11840
  const messageColumns = messageTableInfo.map((col) => col.name);
11841
+ const needsMessageTreeBackfill = !messageColumns.includes("parent_id") || !messageColumns.includes("sibling_group_id") || !messageColumns.includes("branch_index");
11686
11842
  if (!messageColumns.includes("content_blocks")) {
11687
11843
  db.exec("ALTER TABLE ai_message ADD COLUMN content_blocks TEXT");
11688
11844
  }
@@ -11692,18 +11848,149 @@ var AIConversationManager = class {
11692
11848
  if (!messageColumns.includes("debug_context")) {
11693
11849
  db.exec("ALTER TABLE ai_message ADD COLUMN debug_context TEXT");
11694
11850
  }
11851
+ if (!messageColumns.includes("parent_id")) {
11852
+ db.exec("ALTER TABLE ai_message ADD COLUMN parent_id TEXT");
11853
+ }
11854
+ if (!messageColumns.includes("sibling_group_id")) {
11855
+ db.exec("ALTER TABLE ai_message ADD COLUMN sibling_group_id TEXT");
11856
+ }
11857
+ if (!messageColumns.includes("branch_index")) {
11858
+ db.exec("ALTER TABLE ai_message ADD COLUMN branch_index INTEGER DEFAULT 0");
11859
+ }
11695
11860
  const convTableInfo = db.pragma("table_info(ai_conversation)");
11696
11861
  const convColumns = convTableInfo.map((col) => col.name);
11862
+ const needsConversationBackfill = !convColumns.includes("active_message_id");
11697
11863
  if (!convColumns.includes("assistant_id")) {
11698
11864
  db.exec(`ALTER TABLE ai_conversation ADD COLUMN assistant_id TEXT DEFAULT '${DEFAULT_GENERAL_ID}'`);
11699
11865
  }
11866
+ if (!convColumns.includes("active_message_id")) {
11867
+ db.exec("ALTER TABLE ai_conversation ADD COLUMN active_message_id TEXT");
11868
+ }
11869
+ if (needsMessageTreeBackfill || needsConversationBackfill || this.hasUnbackfilledMessageTree(db)) {
11870
+ this.backfillMessageTree(db);
11871
+ }
11872
+ db.exec(`
11873
+ CREATE INDEX IF NOT EXISTS idx_ai_message_parent ON ai_message(parent_id);
11874
+ CREATE INDEX IF NOT EXISTS idx_ai_message_sibling ON ai_message(sibling_group_id);
11875
+ `);
11700
11876
  } catch (error) {
11701
11877
  console.error("[AI DB Migration] Migration failed:", error);
11702
11878
  }
11703
11879
  }
11880
+ hasUnbackfilledMessageTree(db) {
11881
+ const row = db.prepare(
11882
+ `SELECT 1
11883
+ FROM ai_conversation c
11884
+ WHERE EXISTS (
11885
+ SELECT 1 FROM ai_message m
11886
+ WHERE m.conversation_id = c.id
11887
+ )
11888
+ AND (
11889
+ c.active_message_id IS NULL
11890
+ OR EXISTS (
11891
+ SELECT 1 FROM ai_message m
11892
+ WHERE m.conversation_id = c.id
11893
+ AND (m.sibling_group_id IS NULL OR m.branch_index IS NULL)
11894
+ )
11895
+ )
11896
+ LIMIT 1`
11897
+ ).get();
11898
+ return !!row;
11899
+ }
11900
+ backfillMessageTree(db) {
11901
+ const conversations = db.prepare("SELECT id FROM ai_conversation").all();
11902
+ const updateMessage = db.prepare(
11903
+ "UPDATE ai_message SET parent_id = ?, sibling_group_id = COALESCE(sibling_group_id, ?), branch_index = COALESCE(branch_index, 0) WHERE id = ?"
11904
+ );
11905
+ const updateConversation = db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?");
11906
+ const tx = db.transaction(() => {
11907
+ for (const conversation of conversations) {
11908
+ const messages = db.prepare(
11909
+ `SELECT id, parent_id as parentId, sibling_group_id as siblingGroupId
11910
+ FROM ai_message WHERE conversation_id = ? ORDER BY timestamp ASC, id ASC`
11911
+ ).all(conversation.id);
11912
+ let previousId = null;
11913
+ for (const message of messages) {
11914
+ const parentId = message.parentId === void 0 ? previousId : message.parentId ?? previousId;
11915
+ updateMessage.run(parentId, message.siblingGroupId ?? message.id, message.id);
11916
+ previousId = message.id;
11917
+ }
11918
+ const conversationRow = db.prepare("SELECT active_message_id as activeMessageId FROM ai_conversation WHERE id = ?").get(conversation.id);
11919
+ if (!conversationRow?.activeMessageId && previousId) {
11920
+ updateConversation.run(previousId, conversation.id);
11921
+ }
11922
+ }
11923
+ });
11924
+ tx();
11925
+ }
11704
11926
  generateId(prefix) {
11705
11927
  return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
11706
11928
  }
11929
+ parseMessageRow(row) {
11930
+ return {
11931
+ id: row.id,
11932
+ conversationId: row.conversationId,
11933
+ role: row.role,
11934
+ content: row.content,
11935
+ timestamp: row.timestamp,
11936
+ parentId: row.parentId ?? null,
11937
+ dataKeywords: row.dataKeywords ? JSON.parse(row.dataKeywords) : void 0,
11938
+ dataMessageCount: row.dataMessageCount ?? void 0,
11939
+ contentBlocks: row.contentBlocks ? JSON.parse(row.contentBlocks) : void 0,
11940
+ tokenUsage: row.tokenUsage ? JSON.parse(row.tokenUsage) : void 0
11941
+ };
11942
+ }
11943
+ getMessageRow(messageId) {
11944
+ const db = this.getDb();
11945
+ const row = db.prepare(
11946
+ `SELECT id, conversation_id as conversationId, role, content, timestamp,
11947
+ parent_id as parentId, sibling_group_id as siblingGroupId, branch_index as branchIndex,
11948
+ data_keywords as dataKeywords, data_message_count as dataMessageCount,
11949
+ content_blocks as contentBlocks, token_usage as tokenUsage
11950
+ FROM ai_message WHERE id = ?`
11951
+ ).get(messageId);
11952
+ return row ?? null;
11953
+ }
11954
+ getActiveMessageId(conversationId) {
11955
+ const db = this.getDb();
11956
+ const row = db.prepare("SELECT active_message_id as activeMessageId FROM ai_conversation WHERE id = ?").get(conversationId);
11957
+ if (row?.activeMessageId) {
11958
+ const activeExists = db.prepare("SELECT 1 FROM ai_message WHERE id = ?").get(row.activeMessageId);
11959
+ if (activeExists) return row.activeMessageId;
11960
+ }
11961
+ const fallback = db.prepare("SELECT id FROM ai_message WHERE conversation_id = ? ORDER BY timestamp DESC, id DESC LIMIT 1").get(conversationId);
11962
+ if (fallback?.id) {
11963
+ db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?").run(fallback.id, conversationId);
11964
+ return fallback.id;
11965
+ }
11966
+ return null;
11967
+ }
11968
+ getAllMessageRows(conversationId) {
11969
+ return this.getDb().prepare(
11970
+ `SELECT id, conversation_id as conversationId, role, content, timestamp,
11971
+ parent_id as parentId, sibling_group_id as siblingGroupId, branch_index as branchIndex,
11972
+ data_keywords as dataKeywords, data_message_count as dataMessageCount,
11973
+ content_blocks as contentBlocks, token_usage as tokenUsage
11974
+ FROM ai_message WHERE conversation_id = ? ORDER BY timestamp ASC, id ASC`
11975
+ ).all(conversationId);
11976
+ }
11977
+ getActivePathRows(conversationId, leafMessageId) {
11978
+ if (leafMessageId === null) return [];
11979
+ const allRows = this.getAllMessageRows(conversationId);
11980
+ if (allRows.length === 0) return [];
11981
+ const rowMap = new Map(allRows.map((row) => [row.id, row]));
11982
+ let currentId = leafMessageId ?? this.getActiveMessageId(conversationId);
11983
+ const path37 = [];
11984
+ const seen = /* @__PURE__ */ new Set();
11985
+ while (currentId && !seen.has(currentId)) {
11986
+ seen.add(currentId);
11987
+ const row = rowMap.get(currentId);
11988
+ if (!row) break;
11989
+ path37.push(row);
11990
+ currentId = row.parentId;
11991
+ }
11992
+ return path37.length > 0 ? path37.reverse() : allRows;
11993
+ }
11707
11994
  // ==================== 生命周期 ====================
11708
11995
  close() {
11709
11996
  if (this.db) {
@@ -11766,7 +12053,7 @@ var AIConversationManager = class {
11766
12053
  `INSERT INTO ai_conversation (id, session_id, title, assistant_id, created_at, updated_at)
11767
12054
  VALUES (?, ?, ?, ?, ?, ?)`
11768
12055
  ).run(id, sessionId, title || null, assistantId, now, now);
11769
- return { id, sessionId, title: title || null, assistantId, createdAt: now, updatedAt: now };
12056
+ return { id, sessionId, title: title || null, assistantId, activeMessageId: null, createdAt: now, updatedAt: now };
11770
12057
  }
11771
12058
  getConversationCountsBySession() {
11772
12059
  const result = /* @__PURE__ */ new Map();
@@ -11784,6 +12071,7 @@ var AIConversationManager = class {
11784
12071
  const db = this.getDb();
11785
12072
  return db.prepare(
11786
12073
  `SELECT id, session_id as sessionId, title, assistant_id as assistantId,
12074
+ active_message_id as activeMessageId,
11787
12075
  created_at as createdAt, updated_at as updatedAt
11788
12076
  FROM ai_conversation WHERE session_id = ? ORDER BY updated_at DESC`
11789
12077
  ).all(sessionId);
@@ -11792,6 +12080,7 @@ var AIConversationManager = class {
11792
12080
  const db = this.getDb();
11793
12081
  const row = db.prepare(
11794
12082
  `SELECT id, session_id as sessionId, title, assistant_id as assistantId,
12083
+ active_message_id as activeMessageId,
11795
12084
  created_at as createdAt, updated_at as updatedAt
11796
12085
  FROM ai_conversation WHERE id = ?`
11797
12086
  ).get(conversationId);
@@ -11814,13 +12103,19 @@ var AIConversationManager = class {
11814
12103
  const db = this.getDb();
11815
12104
  const now = Math.floor(Date.now() / 1e3);
11816
12105
  const id = this.generateId("msg");
12106
+ const parentId = this.getActiveMessageId(conversationId);
12107
+ const siblingGroupId = id;
12108
+ const branchIndex = 0;
11817
12109
  const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(conversationId) : void 0;
11818
12110
  if (pendingDebug) {
11819
12111
  this.pendingDebugContextMap.delete(conversationId);
11820
12112
  }
11821
12113
  db.prepare(
11822
- `INSERT INTO ai_message (id, conversation_id, role, content, timestamp, data_keywords, data_message_count, content_blocks, token_usage, debug_context)
11823
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
12114
+ `INSERT INTO ai_message (
12115
+ id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12116
+ content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12117
+ )
12118
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
11824
12119
  ).run(
11825
12120
  id,
11826
12121
  conversationId,
@@ -11831,15 +12126,23 @@ var AIConversationManager = class {
11831
12126
  dataMessageCount ?? null,
11832
12127
  contentBlocks ? JSON.stringify(contentBlocks) : null,
11833
12128
  tokenUsage ? JSON.stringify(tokenUsage) : null,
11834
- pendingDebug ?? null
12129
+ pendingDebug ?? null,
12130
+ parentId,
12131
+ siblingGroupId,
12132
+ branchIndex
12133
+ );
12134
+ db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12135
+ id,
12136
+ now,
12137
+ conversationId
11835
12138
  );
11836
- db.prepare("UPDATE ai_conversation SET updated_at = ? WHERE id = ?").run(now, conversationId);
11837
12139
  return {
11838
12140
  id,
11839
12141
  conversationId,
11840
12142
  role,
11841
12143
  content,
11842
12144
  timestamp: now,
12145
+ parentId,
11843
12146
  dataKeywords,
11844
12147
  dataMessageCount,
11845
12148
  contentBlocks,
@@ -11847,40 +12150,181 @@ var AIConversationManager = class {
11847
12150
  };
11848
12151
  }
11849
12152
  getMessages(conversationId) {
11850
- const db = this.getDb();
11851
- const rows = db.prepare(
11852
- `SELECT id, conversation_id as conversationId, role, content, timestamp,
11853
- data_keywords as dataKeywords, data_message_count as dataMessageCount,
11854
- content_blocks as contentBlocks, token_usage as tokenUsage
11855
- FROM ai_message WHERE conversation_id = ? ORDER BY timestamp ASC`
11856
- ).all(conversationId);
11857
- return rows.map((row) => ({
11858
- id: row.id,
11859
- conversationId: row.conversationId,
11860
- role: row.role,
11861
- content: row.content,
11862
- timestamp: row.timestamp,
11863
- dataKeywords: row.dataKeywords ? JSON.parse(row.dataKeywords) : void 0,
11864
- dataMessageCount: row.dataMessageCount ?? void 0,
11865
- contentBlocks: row.contentBlocks ? JSON.parse(row.contentBlocks) : void 0,
11866
- tokenUsage: row.tokenUsage ? JSON.parse(row.tokenUsage) : void 0
11867
- }));
12153
+ return this.getActivePathRows(conversationId).map((row) => this.parseMessageRow(row));
11868
12154
  }
11869
12155
  deleteMessage(messageId) {
11870
12156
  const db = this.getDb();
11871
12157
  const result = db.prepare("DELETE FROM ai_message WHERE id = ?").run(messageId);
11872
12158
  return result.changes > 0;
11873
12159
  }
11874
- getConversationTokenUsage(conversationId) {
12160
+ deleteMessagesFrom(conversationId, messageId) {
11875
12161
  const db = this.getDb();
11876
- const row = db.prepare(
11877
- `SELECT
11878
- COALESCE(SUM(json_extract(token_usage, '$.promptTokens')), 0) as promptTokens,
11879
- COALESCE(SUM(json_extract(token_usage, '$.completionTokens')), 0) as completionTokens,
11880
- COALESCE(SUM(json_extract(token_usage, '$.totalTokens')), 0) as totalTokens
11881
- FROM ai_message WHERE conversation_id = ? AND token_usage IS NOT NULL`
11882
- ).get(conversationId);
11883
- return { promptTokens: row.promptTokens, completionTokens: row.completionTokens, totalTokens: row.totalTokens };
12162
+ const target = this.getMessageRow(messageId);
12163
+ if (!target || target.conversationId !== conversationId) {
12164
+ throw new Error("Message not found in conversation");
12165
+ }
12166
+ const activePath = this.getActivePathRows(conversationId);
12167
+ const targetIndex = activePath.findIndex((row) => row.id === messageId);
12168
+ if (targetIndex < 0) {
12169
+ throw new Error("Message not on active path");
12170
+ }
12171
+ const idsToDelete = activePath.slice(targetIndex).map((row) => row.id);
12172
+ const placeholders = idsToDelete.map(() => "?").join(", ");
12173
+ db.prepare(`DELETE FROM ai_message WHERE id IN (${placeholders})`).run(...idsToDelete);
12174
+ const newLeafId = targetIndex > 0 ? activePath[targetIndex - 1].id : null;
12175
+ const now = Math.floor(Date.now() / 1e3);
12176
+ db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12177
+ newLeafId,
12178
+ now,
12179
+ conversationId
12180
+ );
12181
+ }
12182
+ forkConversation(sourceConversationId, upToMessageId, title) {
12183
+ const db = this.getDb();
12184
+ const source = this.getConversation(sourceConversationId);
12185
+ if (!source) {
12186
+ throw new Error("Source conversation not found");
12187
+ }
12188
+ const activePath = this.getActivePathRows(sourceConversationId);
12189
+ const cutIndex = activePath.findIndex((row) => row.id === upToMessageId);
12190
+ if (cutIndex < 0) {
12191
+ throw new Error("Message not on active path");
12192
+ }
12193
+ const messagesToCopy = activePath.slice(0, cutIndex + 1);
12194
+ const now = Math.floor(Date.now() / 1e3);
12195
+ const newConvId = this.generateId("conv");
12196
+ const forkTitle = title || `${source.title || "Untitled"} (fork)`;
12197
+ db.prepare(
12198
+ `INSERT INTO ai_conversation (id, session_id, title, assistant_id, active_message_id, created_at, updated_at)
12199
+ VALUES (?, ?, ?, ?, NULL, ?, ?)`
12200
+ ).run(newConvId, source.sessionId, forkTitle, source.assistantId, now, now);
12201
+ const idMap = /* @__PURE__ */ new Map();
12202
+ let lastNewId = null;
12203
+ for (const row of messagesToCopy) {
12204
+ const newMsgId = this.generateId("msg");
12205
+ idMap.set(row.id, newMsgId);
12206
+ const newParentId = row.parentId ? idMap.get(row.parentId) ?? null : null;
12207
+ db.prepare(
12208
+ `INSERT INTO ai_message (
12209
+ id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12210
+ content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12211
+ )
12212
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, 0)`
12213
+ ).run(
12214
+ newMsgId,
12215
+ newConvId,
12216
+ row.role,
12217
+ row.content,
12218
+ row.timestamp,
12219
+ row.dataKeywords,
12220
+ row.dataMessageCount,
12221
+ row.contentBlocks,
12222
+ row.tokenUsage,
12223
+ newParentId,
12224
+ newMsgId
12225
+ );
12226
+ lastNewId = newMsgId;
12227
+ }
12228
+ if (lastNewId) {
12229
+ db.prepare("UPDATE ai_conversation SET active_message_id = ? WHERE id = ?").run(lastNewId, newConvId);
12230
+ }
12231
+ return {
12232
+ id: newConvId,
12233
+ sessionId: source.sessionId,
12234
+ title: forkTitle,
12235
+ assistantId: source.assistantId,
12236
+ activeMessageId: lastNewId,
12237
+ createdAt: now,
12238
+ updatedAt: now
12239
+ };
12240
+ }
12241
+ updateMessageContent(messageId, newContent) {
12242
+ const db = this.getDb();
12243
+ const result = db.prepare("UPDATE ai_message SET content = ? WHERE id = ?").run(newContent, messageId);
12244
+ if (result.changes === 0) throw new Error("Message not found");
12245
+ }
12246
+ deleteAndRelinkMessage(conversationId, messageId) {
12247
+ const db = this.getDb();
12248
+ const target = this.getMessageRow(messageId);
12249
+ if (!target || target.conversationId !== conversationId) {
12250
+ throw new Error("Message not found in conversation");
12251
+ }
12252
+ db.prepare("UPDATE ai_message SET parent_id = ? WHERE parent_id = ? AND conversation_id = ?").run(
12253
+ target.parentId,
12254
+ messageId,
12255
+ conversationId
12256
+ );
12257
+ const conv = this.getConversation(conversationId);
12258
+ if (conv?.activeMessageId === messageId) {
12259
+ const now = Math.floor(Date.now() / 1e3);
12260
+ db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12261
+ target.parentId,
12262
+ now,
12263
+ conversationId
12264
+ );
12265
+ }
12266
+ db.prepare("DELETE FROM ai_message WHERE id = ?").run(messageId);
12267
+ }
12268
+ insertMessageAfter(conversationId, afterMessageId, role, content, contentBlocks, tokenUsage) {
12269
+ const db = this.getDb();
12270
+ const now = Math.floor(Date.now() / 1e3);
12271
+ const id = this.generateId("msg");
12272
+ const pendingDebug = role === "assistant" ? this.pendingDebugContextMap.get(conversationId) : void 0;
12273
+ if (pendingDebug) {
12274
+ this.pendingDebugContextMap.delete(conversationId);
12275
+ }
12276
+ const childRow = db.prepare("SELECT id FROM ai_message WHERE parent_id = ? AND conversation_id = ? LIMIT 1").get(afterMessageId, conversationId);
12277
+ db.prepare(
12278
+ `INSERT INTO ai_message (
12279
+ id, conversation_id, role, content, timestamp, data_keywords, data_message_count,
12280
+ content_blocks, token_usage, debug_context, parent_id, sibling_group_id, branch_index
12281
+ )
12282
+ VALUES (?, ?, ?, ?, ?, NULL, NULL, ?, ?, ?, ?, ?, 0)`
12283
+ ).run(
12284
+ id,
12285
+ conversationId,
12286
+ role,
12287
+ content,
12288
+ now,
12289
+ contentBlocks ? JSON.stringify(contentBlocks) : null,
12290
+ tokenUsage ? JSON.stringify(tokenUsage) : null,
12291
+ pendingDebug ?? null,
12292
+ afterMessageId,
12293
+ id
12294
+ );
12295
+ if (childRow) {
12296
+ db.prepare("UPDATE ai_message SET parent_id = ? WHERE id = ?").run(id, childRow.id);
12297
+ db.prepare("UPDATE ai_conversation SET updated_at = ? WHERE id = ?").run(now, conversationId);
12298
+ } else {
12299
+ db.prepare("UPDATE ai_conversation SET active_message_id = ?, updated_at = ? WHERE id = ?").run(
12300
+ id,
12301
+ now,
12302
+ conversationId
12303
+ );
12304
+ }
12305
+ return {
12306
+ id,
12307
+ conversationId,
12308
+ role,
12309
+ content,
12310
+ timestamp: now,
12311
+ parentId: afterMessageId,
12312
+ contentBlocks,
12313
+ tokenUsage
12314
+ };
12315
+ }
12316
+ getConversationTokenUsage(conversationId) {
12317
+ const rows = this.getActivePathRows(conversationId);
12318
+ const result = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
12319
+ for (const row of rows) {
12320
+ if (row.tokenUsage) {
12321
+ const usage = JSON.parse(row.tokenUsage);
12322
+ result.promptTokens += usage.promptTokens;
12323
+ result.completionTokens += usage.completionTokens;
12324
+ result.totalTokens += usage.totalTokens;
12325
+ }
12326
+ }
12327
+ return result;
11884
12328
  }
11885
12329
  // ==================== Debug context ====================
11886
12330
  setPendingDebugContext(conversationId, debugContext) {
@@ -11896,8 +12340,8 @@ var AIConversationManager = class {
11896
12340
  return result.changes;
11897
12341
  }
11898
12342
  // ==================== Agent 专用 ====================
11899
- getHistoryForAgent(conversationId, maxMessages) {
11900
- const messages = this.getMessages(conversationId);
12343
+ getHistoryForAgent(conversationId, maxMessages, leafMessageId) {
12344
+ const messages = this.getActivePathRows(conversationId, leafMessageId).map((row) => this.parseMessageRow(row));
11901
12345
  const validMessages = messages.filter(
11902
12346
  (m) => (m.role === "user" || m.role === "assistant" || m.role === "summary") && m.content?.trim()
11903
12347
  );
@@ -11940,8 +12384,6 @@ var AIConversationManager = class {
11940
12384
  }
11941
12385
  // ==================== Summary / 压缩专用 ====================
11942
12386
  addSummaryMessage(conversationId, content, meta) {
11943
- const db = this.getDb();
11944
- db.prepare("DELETE FROM ai_message WHERE conversation_id = ? AND role = 'summary'").run(conversationId);
11945
12387
  const contentBlocks = [
11946
12388
  {
11947
12389
  type: "summary_meta",
@@ -11952,60 +12394,27 @@ var AIConversationManager = class {
11952
12394
  return this.addMessage(conversationId, "summary", content, void 0, void 0, contentBlocks);
11953
12395
  }
11954
12396
  getLatestSummary(conversationId) {
11955
- const db = this.getDb();
11956
- const row = db.prepare(
11957
- `SELECT id, conversation_id as conversationId, role, content, timestamp,
11958
- data_keywords as dataKeywords, data_message_count as dataMessageCount,
11959
- content_blocks as contentBlocks
11960
- FROM ai_message WHERE conversation_id = ? AND role = 'summary'
11961
- ORDER BY timestamp DESC LIMIT 1`
11962
- ).get(conversationId);
11963
- if (!row) return null;
11964
- return {
11965
- id: row.id,
11966
- conversationId: row.conversationId,
11967
- role: row.role,
11968
- content: row.content,
11969
- timestamp: row.timestamp,
11970
- dataKeywords: row.dataKeywords ? JSON.parse(row.dataKeywords) : void 0,
11971
- dataMessageCount: row.dataMessageCount ?? void 0,
11972
- contentBlocks: row.contentBlocks ? JSON.parse(row.contentBlocks) : void 0
11973
- };
12397
+ const row = [...this.getActivePathRows(conversationId)].reverse().find((message) => message.role === "summary");
12398
+ return row ? this.parseMessageRow(row) : null;
11974
12399
  }
11975
12400
  getMessagesAfterSummary(conversationId, summaryTimestamp) {
11976
- const db = this.getDb();
11977
- const rows = db.prepare(
11978
- `SELECT role, content, timestamp FROM ai_message
11979
- WHERE conversation_id = ? AND timestamp > ? AND role IN ('user', 'assistant')
11980
- ORDER BY timestamp ASC`
11981
- ).all(conversationId, summaryTimestamp);
11982
- return rows.map((r) => ({ role: r.role, content: r.content, timestamp: r.timestamp }));
12401
+ 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 }));
11983
12402
  }
11984
12403
  getAllUserAssistantMessages(conversationId) {
11985
- const db = this.getDb();
11986
- const rows = db.prepare(
11987
- `SELECT role, content, timestamp FROM ai_message
11988
- WHERE conversation_id = ? AND role IN ('user', 'assistant')
11989
- ORDER BY timestamp ASC`
11990
- ).all(conversationId);
11991
- return rows.map((r) => ({ role: r.role, content: r.content, timestamp: r.timestamp }));
12404
+ return this.getActivePathRows(conversationId).filter((row) => row.role === "user" || row.role === "assistant").map((row) => ({ role: row.role, content: row.content, timestamp: row.timestamp }));
11992
12405
  }
11993
12406
  getMessageCountAfterSummary(conversationId) {
11994
12407
  const summary = this.getLatestSummary(conversationId);
11995
12408
  if (!summary) {
11996
- const db2 = this.getDb();
11997
- const row2 = db2.prepare("SELECT COUNT(*) as count FROM ai_message WHERE conversation_id = ? AND role IN ('user', 'assistant')").get(conversationId);
11998
- return row2.count;
12409
+ return this.getActivePathRows(conversationId).filter((row) => row.role === "user" || row.role === "assistant").length;
11999
12410
  }
12000
12411
  const metaBlock = summary.contentBlocks?.find(
12001
12412
  (b) => b.type === "summary_meta"
12002
12413
  );
12003
12414
  const boundary = metaBlock?.bufferBoundaryTimestamp ?? summary.timestamp;
12004
- const db = this.getDb();
12005
- const row = db.prepare(
12006
- "SELECT COUNT(*) as count FROM ai_message WHERE conversation_id = ? AND timestamp >= ? AND role IN ('user', 'assistant')"
12007
- ).get(conversationId, boundary);
12008
- return row.count;
12415
+ return this.getActivePathRows(conversationId).filter(
12416
+ (row) => row.timestamp >= boundary && (row.role === "user" || row.role === "assistant")
12417
+ ).length;
12009
12418
  }
12010
12419
  };
12011
12420
 
@@ -13177,7 +13586,8 @@ function applyPreprocessingPipeline(options) {
13177
13586
  // ../../packages/node-runtime/src/ai/agent/core.ts
13178
13587
  import { Agent as PiAgentCore } from "@earendil-works/pi-agent-core";
13179
13588
  import {
13180
- streamSimple as defaultStreamSimple
13589
+ streamSimple as defaultStreamSimple,
13590
+ clampThinkingLevel
13181
13591
  } from "@earendil-works/pi-ai";
13182
13592
  function createEmptyPiUsage() {
13183
13593
  return {
@@ -13241,11 +13651,34 @@ async function runAgentCore(options) {
13241
13651
  if (abortSignal?.aborted) {
13242
13652
  return { usage: totalUsage, finalMessages: [], toolsUsed: [], toolRounds: 0 };
13243
13653
  }
13654
+ const isDefault = !options.thinkingLevel || options.thinkingLevel === "default";
13655
+ const effectiveModel = isDefault ? { ...piModel, reasoning: false, compat: void 0, thinkingLevelMap: void 0 } : piModel;
13656
+ const resolvedThinkingLevel = (() => {
13657
+ if (isDefault) return "off";
13658
+ const level = options.thinkingLevel;
13659
+ if (level === "auto") {
13660
+ if (!piModel.reasoning) return "off";
13661
+ const compat = piModel.compat;
13662
+ if (compat?.thinkingFormat) return "high";
13663
+ if (piModel.thinkingLevelMap) return "high";
13664
+ return void 0;
13665
+ }
13666
+ return clampThinkingLevel(piModel, level);
13667
+ })();
13668
+ const finalThinkingLevel = resolvedThinkingLevel ?? (piModel.reasoning ? void 0 : "off");
13669
+ console.debug("[agent-core] thinkingLevel resolution:", {
13670
+ input: options.thinkingLevel,
13671
+ modelId: piModel.id,
13672
+ modelReasoning: effectiveModel.reasoning,
13673
+ isDefault,
13674
+ resolved: resolvedThinkingLevel,
13675
+ final: finalThinkingLevel
13676
+ });
13244
13677
  const coreAgent = new PiAgentCore({
13245
13678
  initialState: {
13246
13679
  systemPrompt,
13247
- model: piModel,
13248
- thinkingLevel: piModel.reasoning ? "medium" : "off",
13680
+ model: effectiveModel,
13681
+ thinkingLevel: finalThinkingLevel,
13249
13682
  tools: maxToolRounds > 0 ? tools : [],
13250
13683
  messages: toPiHistoryMessages(history)
13251
13684
  },
@@ -14909,11 +15342,17 @@ var aiLocales = { "zh-CN": zh_CN_default, "en-US": en_US_default, "ja-JP": ja_JP
14909
15342
  var MIN_MESSAGE_COUNT = 3;
14910
15343
  var MAX_CONTENT_PER_CALL = 8e3;
14911
15344
  var SEGMENT_THRESHOLD = 8e3;
14912
- function getSummaryLengthLimit(messageCount) {
14913
- if (messageCount <= 10) return 50;
14914
- if (messageCount <= 30) return 80;
14915
- if (messageCount <= 100) return 120;
14916
- return 200;
15345
+ function getSummaryLengthLimit(messageCount, strategy = "standard") {
15346
+ if (strategy === "brief") {
15347
+ if (messageCount <= 10) return 50;
15348
+ if (messageCount <= 30) return 80;
15349
+ if (messageCount <= 100) return 120;
15350
+ return 200;
15351
+ }
15352
+ if (messageCount <= 10) return 100;
15353
+ if (messageCount <= 30) return 200;
15354
+ if (messageCount <= 100) return 350;
15355
+ return 500;
14917
15356
  }
14918
15357
  var MEANINGFUL_SHORT_ZH = ["\u597D\u7684", "\u4E0D\u662F", "\u662F\u7684", "\u53EF\u4EE5", "\u4E0D\u884C", "\u597D\u5427", "\u660E\u767D", "\u77E5\u9053", "\u540C\u610F"];
14919
15358
  var MEANINGLESS_SHORT_EN = [
@@ -15009,34 +15448,55 @@ function splitIntoSegments(messages, maxCharsPerSegment) {
15009
15448
  if (currentSegment.length > 0) segments.push(currentSegment);
15010
15449
  return segments;
15011
15450
  }
15012
- function buildSummaryPrompt(content, lengthLimit, locale) {
15451
+ function buildSummaryPrompt(content, lengthLimit, locale, strategy) {
15452
+ if (strategy === "brief") {
15453
+ if (locale.startsWith("zh")) {
15454
+ return `\u8BF7\u7528\u7B80\u6D01\u7684\u8BED\u8A00\uFF08${lengthLimit}\u5B57\u4EE5\u5185\uFF09\u603B\u7ED3\u4EE5\u4E0B\u5BF9\u8BDD\u7684\u4E3B\u8981\u5185\u5BB9\u6216\u8BDD\u9898\u3002\u53EA\u8F93\u51FA\u6458\u8981\u5185\u5BB9\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u524D\u7F00\u3001\u89E3\u91CA\u6216\u5F15\u53F7\u3002
15455
+
15456
+ ${content}`;
15457
+ }
15458
+ return `Summarize the following conversation concisely (max ${lengthLimit} characters). Output only the summary, no prefix, explanation, or quotes.
15459
+
15460
+ ${content}`;
15461
+ }
15013
15462
  if (locale.startsWith("zh")) {
15014
- return `\u8BF7\u7528\u7B80\u6D01\u7684\u8BED\u8A00\uFF08${lengthLimit}\u5B57\u4EE5\u5185\uFF09\u603B\u7ED3\u4EE5\u4E0B\u5BF9\u8BDD\u7684\u4E3B\u8981\u5185\u5BB9\u6216\u8BDD\u9898\u3002\u53EA\u8F93\u51FA\u6458\u8981\u5185\u5BB9\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u524D\u7F00\u3001\u89E3\u91CA\u6216\u5F15\u53F7\u3002
15463
+ return `\u8BF7\u603B\u7ED3\u4EE5\u4E0B\u804A\u5929\u8BB0\u5F55\uFF08${lengthLimit}\u5B57\u4EE5\u5185\uFF09\uFF0C\u9700\u5305\u542B\uFF1A\u8C01\u53C2\u4E0E\u4E86\u8BA8\u8BBA\u3001\u8BA8\u8BBA\u4E86\u4EC0\u4E48\u4E8B\u60C5\u3001\u5173\u952E\u7EC6\u8282\u548C\u7ED3\u8BBA\u3002\u75282-4\u53E5\u8BDD\u63CF\u8FF0\uFF0C\u4E0D\u8981\u7F57\u5217\u6BCF\u6761\u6D88\u606F\u3002\u53EA\u8F93\u51FA\u6458\u8981\u5185\u5BB9\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u524D\u7F00\u3001\u89E3\u91CA\u6216\u5F15\u53F7\u3002
15015
15464
 
15016
15465
  ${content}`;
15017
15466
  }
15018
- return `Summarize the following conversation concisely (max ${lengthLimit} characters). Output only the summary, no prefix, explanation, or quotes.
15467
+ return `Summarize the following chat log (max ${lengthLimit} characters). Include: who participated, what was discussed, key details and conclusions. Use 2-4 sentences, don't list individual messages. Output only the summary, no prefix, explanation, or quotes.
15019
15468
 
15020
15469
  ${content}`;
15021
15470
  }
15022
- function buildSubSummaryPrompt(content, locale) {
15471
+ function buildSubSummaryPrompt(content, locale, strategy) {
15472
+ const limit = strategy === "brief" ? 50 : 100;
15023
15473
  if (locale.startsWith("zh")) {
15024
- return `\u8BF7\u7528\u4E00\u53E5\u8BDD\uFF08\u4E0D\u8D85\u8FC750\u5B57\uFF09\u6982\u62EC\u4EE5\u4E0B\u5BF9\u8BDD\u7247\u6BB5\u7684\u4E3B\u8981\u5185\u5BB9\u3002\u53EA\u8F93\u51FA\u6458\u8981\u5185\u5BB9\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u524D\u7F00\u3001\u89E3\u91CA\u6216\u5F15\u53F7\u3002
15474
+ return `\u8BF7\u7528\u4E00\u5230\u4E24\u53E5\u8BDD\uFF08\u4E0D\u8D85\u8FC7${limit}\u5B57\uFF09\u6982\u62EC\u4EE5\u4E0B\u5BF9\u8BDD\u7247\u6BB5\u7684\u4E3B\u8981\u5185\u5BB9\uFF0C\u5305\u62EC\u8C01\u8BF4\u4E86\u4EC0\u4E48\u3002\u53EA\u8F93\u51FA\u6458\u8981\u5185\u5BB9\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u524D\u7F00\u3001\u89E3\u91CA\u6216\u5F15\u53F7\u3002
15025
15475
 
15026
15476
  ${content}`;
15027
15477
  }
15028
- return `Summarize this conversation segment in one sentence (max 50 characters). Output only the summary, no prefix or quotes.
15478
+ return `Summarize this conversation segment in 1-2 sentences (max ${limit} characters), including who said what. Output only the summary, no prefix or quotes.
15029
15479
 
15030
15480
  ${content}`;
15031
15481
  }
15032
- function buildMergePrompt(subSummaries, lengthLimit, locale) {
15482
+ function buildMergePrompt(subSummaries, lengthLimit, locale, strategy) {
15033
15483
  const list = subSummaries.map((s, i) => `${i + 1}. ${s}`).join("\n");
15484
+ if (strategy === "brief") {
15485
+ if (locale.startsWith("zh")) {
15486
+ return `\u4EE5\u4E0B\u662F\u4E00\u6BB5\u5BF9\u8BDD\u7684\u591A\u4E2A\u7247\u6BB5\u6458\u8981\uFF0C\u8BF7\u5C06\u5B83\u4EEC\u5408\u5E76\u6210\u4E00\u4E2A\u5B8C\u6574\u7684\u603B\u7ED3\uFF08${lengthLimit}\u5B57\u4EE5\u5185\uFF09\u3002\u53EA\u8F93\u51FA\u6458\u8981\u5185\u5BB9\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u524D\u7F00\u3001\u89E3\u91CA\u6216\u5F15\u53F7\u3002
15487
+
15488
+ ${list}`;
15489
+ }
15490
+ return `Below are summaries of different parts of a conversation. Merge them into one cohesive summary (max ${lengthLimit} characters). Output only the summary, no prefix or quotes.
15491
+
15492
+ ${list}`;
15493
+ }
15034
15494
  if (locale.startsWith("zh")) {
15035
- return `\u4EE5\u4E0B\u662F\u4E00\u6BB5\u5BF9\u8BDD\u7684\u591A\u4E2A\u7247\u6BB5\u6458\u8981\uFF0C\u8BF7\u5C06\u5B83\u4EEC\u5408\u5E76\u6210\u4E00\u4E2A\u5B8C\u6574\u7684\u603B\u7ED3\uFF08${lengthLimit}\u5B57\u4EE5\u5185\uFF09\u3002\u53EA\u8F93\u51FA\u6458\u8981\u5185\u5BB9\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u524D\u7F00\u3001\u89E3\u91CA\u6216\u5F15\u53F7\u3002
15495
+ return `\u4EE5\u4E0B\u662F\u4E00\u6BB5\u5BF9\u8BDD\u7684\u591A\u4E2A\u7247\u6BB5\u6458\u8981\uFF0C\u8BF7\u5C06\u5B83\u4EEC\u5408\u5E76\u6210\u4E00\u4E2A\u5B8C\u6574\u7684\u603B\u7ED3\uFF08${lengthLimit}\u5B57\u4EE5\u5185\uFF09\u3002\u4FDD\u7559\u53C2\u4E0E\u8005\u3001\u4E8B\u4EF6\u548C\u5173\u952E\u7EC6\u8282\u3002\u53EA\u8F93\u51FA\u6458\u8981\u5185\u5BB9\uFF0C\u4E0D\u8981\u6DFB\u52A0\u4EFB\u4F55\u524D\u7F00\u3001\u89E3\u91CA\u6216\u5F15\u53F7\u3002
15036
15496
 
15037
15497
  ${list}`;
15038
15498
  }
15039
- return `Below are summaries of different parts of a conversation. Merge them into one cohesive summary (max ${lengthLimit} characters). Output only the summary, no prefix or quotes.
15499
+ return `Below are summaries of different parts of a conversation. Merge them into one cohesive summary (max ${lengthLimit} characters). Preserve participants, events, and key details. Output only the summary, no prefix or quotes.
15040
15500
 
15041
15501
  ${list}`;
15042
15502
  }
@@ -15052,7 +15512,7 @@ function postProcessSummary(summary, lengthLimit) {
15052
15512
  return result;
15053
15513
  }
15054
15514
  async function generateSessionSummary(deps, chatSessionId, options = {}) {
15055
- const { locale = "zh-CN", forceRegenerate = false } = options;
15515
+ const { locale = "zh-CN", forceRegenerate = false, strategy = "standard" } = options;
15056
15516
  const log = deps.logger;
15057
15517
  try {
15058
15518
  if (!forceRegenerate) {
@@ -15070,18 +15530,20 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
15070
15530
  if (validMessages.length < MIN_MESSAGE_COUNT) {
15071
15531
  return { success: false, error: deps.t("summary.tooFewValidMessages", { count: MIN_MESSAGE_COUNT }) };
15072
15532
  }
15073
- const lengthLimit = getSummaryLengthLimit(validMessages.length);
15533
+ const lengthLimit = getSummaryLengthLimit(validMessages.length, strategy);
15534
+ const maxTokens = strategy === "brief" ? 300 : 600;
15535
+ const subMaxTokens = strategy === "brief" ? 100 : 200;
15074
15536
  const content = formatMessages(validMessages);
15075
15537
  log?.info(
15076
15538
  "Summary",
15077
- `Generating summary: sessionId=${chatSessionId}, raw=${rawMessages.length}, valid=${validMessages.length}, chars=${content.length}`
15539
+ `Generating summary: sessionId=${chatSessionId}, strategy=${strategy}, raw=${rawMessages.length}, valid=${validMessages.length}, chars=${content.length}`
15078
15540
  );
15079
15541
  let summary;
15080
15542
  if (content.length <= SEGMENT_THRESHOLD) {
15081
15543
  summary = await deps.llmComplete(
15082
15544
  deps.t("summary.systemPromptDirect"),
15083
- buildSummaryPrompt(content, lengthLimit, locale),
15084
- { temperature: 0.3, maxTokens: 300 }
15545
+ buildSummaryPrompt(content, lengthLimit, locale, strategy),
15546
+ { temperature: 0.3, maxTokens }
15085
15547
  );
15086
15548
  summary = summary.trim();
15087
15549
  } else {
@@ -15092,8 +15554,8 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
15092
15554
  const segContent = formatMessages(segment2);
15093
15555
  const sub = await deps.llmComplete(
15094
15556
  deps.t("summary.systemPromptDirect"),
15095
- buildSubSummaryPrompt(segContent, locale),
15096
- { temperature: 0.3, maxTokens: 100 }
15557
+ buildSubSummaryPrompt(segContent, locale, strategy),
15558
+ { temperature: 0.3, maxTokens: subMaxTokens }
15097
15559
  );
15098
15560
  subSummaries.push(sub.trim());
15099
15561
  }
@@ -15102,8 +15564,8 @@ async function generateSessionSummary(deps, chatSessionId, options = {}) {
15102
15564
  } else {
15103
15565
  summary = await deps.llmComplete(
15104
15566
  deps.t("summary.systemPromptMerge"),
15105
- buildMergePrompt(subSummaries, lengthLimit, locale),
15106
- { temperature: 0.3, maxTokens: 300 }
15567
+ buildMergePrompt(subSummaries, lengthLimit, locale, strategy),
15568
+ { temperature: 0.3, maxTokens }
15107
15569
  );
15108
15570
  summary = summary.trim();
15109
15571
  }
@@ -15359,6 +15821,20 @@ var BUILTIN_PROVIDER_API = {
15359
15821
  gemini: "google-generative-ai",
15360
15822
  anthropic: "anthropic-messages"
15361
15823
  };
15824
+ function inferReasoning(provider, modelId, modelDef) {
15825
+ const reasoning = modelDef?.capabilities.includes("reasoning") || !modelDef?.builtin && isReasoningModel(provider, modelId);
15826
+ if (!reasoning) return { reasoning: false, compat: void 0 };
15827
+ const thinkingCompat = getThinkingCompat(provider, modelId);
15828
+ if (Object.keys(thinkingCompat).length === 0) {
15829
+ return { reasoning: true, compat: void 0 };
15830
+ }
15831
+ const { thinkingLevelMap, ...compatFields } = thinkingCompat;
15832
+ return {
15833
+ reasoning: true,
15834
+ compat: Object.keys(compatFields).length > 0 ? compatFields : void 0,
15835
+ thinkingLevelMap
15836
+ };
15837
+ }
15362
15838
  function buildPiModel(config, options) {
15363
15839
  const providerDef = BUILTIN_PROVIDERS.find((p) => p.id === config.provider);
15364
15840
  const baseUrl = config.baseUrl || providerDef?.defaultBaseUrl || "";
@@ -15374,7 +15850,7 @@ function buildPiModel(config, options) {
15374
15850
  api: "google-generative-ai",
15375
15851
  provider: "google",
15376
15852
  baseUrl,
15377
- reasoning: false,
15853
+ reasoning: isReasoningModel(config.provider, modelId),
15378
15854
  input: ["text"],
15379
15855
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
15380
15856
  contextWindow,
@@ -15396,6 +15872,7 @@ function buildPiModel(config, options) {
15396
15872
  };
15397
15873
  }
15398
15874
  const resolvedBaseUrl = config.provider === "openai-compatible" && (apiFormat === "openai-completions" || apiFormat === "openai-responses") ? normalizeOpenAICompatibleBaseUrl(baseUrl) : baseUrl;
15875
+ const { reasoning, compat, thinkingLevelMap } = inferReasoning(config.provider, modelId, modelDef);
15399
15876
  return {
15400
15877
  id: modelId,
15401
15878
  name: modelId,
@@ -15403,12 +15880,13 @@ function buildPiModel(config, options) {
15403
15880
  provider: config.provider,
15404
15881
  baseUrl: resolvedBaseUrl,
15405
15882
  headers: options?.headers,
15406
- reasoning: config.isReasoningModel ?? false,
15883
+ reasoning,
15884
+ thinkingLevelMap,
15407
15885
  input: ["text"],
15408
15886
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
15409
15887
  contextWindow,
15410
15888
  maxTokens: config.maxTokens ?? 4096,
15411
- compat: config.disableThinking ? { thinkingFormat: "qwen" } : void 0
15889
+ compat
15412
15890
  };
15413
15891
  }
15414
15892
 
@@ -16842,7 +17320,8 @@ var localeConfigSchema = z.object({
16842
17320
  });
16843
17321
  var uiConfigSchema = z.object({
16844
17322
  default_session_tab: z.enum(["overview", "ai-chat"]).default("overview"),
16845
- session_gap_threshold: z.number().int().min(60).max(86400).default(1800)
17323
+ session_gap_threshold: z.number().int().min(60).max(86400).default(1800),
17324
+ summary_strategy: z.enum(["brief", "standard"]).default("standard")
16846
17325
  });
16847
17326
  var configSchema = z.object({
16848
17327
  llm: llmConfigSchema.default({}),
@@ -18774,6 +19253,7 @@ __export(session_index_service_exports, {
18774
19253
  clearIndex: () => clearIndex,
18775
19254
  generateIncrementalIndex: () => generateIncrementalIndex,
18776
19255
  generateIndex: () => generateIndex,
19256
+ getAllIndexStats: () => getAllIndexStats,
18777
19257
  getFtsStatus: () => getFtsStatus,
18778
19258
  rebuildFts: () => rebuildFts,
18779
19259
  searchFts: () => searchFts
@@ -18802,10 +19282,23 @@ function rebuildFts(adapter, sessionId) {
18802
19282
  const db = adapter.ensureWritable(sessionId);
18803
19283
  return rebuildFtsIndex(db);
18804
19284
  }
19285
+ function getAllIndexStats(adapter) {
19286
+ const ids = adapter.listSessionIds();
19287
+ return ids.map((sessionId) => {
19288
+ try {
19289
+ const db = adapter.ensureReadonly(sessionId);
19290
+ const stats = getSessionIndexStats(db);
19291
+ return { sessionId, hasIndex: stats.hasIndex, sessionCount: stats.sessionCount };
19292
+ } catch {
19293
+ return { sessionId, hasIndex: false, sessionCount: 0 };
19294
+ }
19295
+ });
19296
+ }
18805
19297
 
18806
19298
  // ../../packages/node-runtime/src/services/summary-service.ts
18807
19299
  var summary_service_exports = {};
18808
19300
  __export(summary_service_exports, {
19301
+ checkCanGenerate: () => checkCanGenerate,
18809
19302
  generateAllSummaries: () => generateAllSummaries,
18810
19303
  generateSummary: () => generateSummary
18811
19304
  });
@@ -18863,6 +19356,23 @@ async function generateAllSummaries(adapter, sessionId, serviceDeps, options) {
18863
19356
  }
18864
19357
  return { success, failed, total: chatSessions.length };
18865
19358
  }
19359
+ function checkCanGenerate(adapter, sessionId, chatSessionIds) {
19360
+ const db = adapter.ensureReadonly(sessionId);
19361
+ const deps = {
19362
+ loadMessages(chatSessionId, limit = 500) {
19363
+ const data = getSessionMessages(db, chatSessionId, limit);
19364
+ if (!data) return null;
19365
+ return data.messages.map((m) => ({ senderName: m.senderName, content: m.content }));
19366
+ },
19367
+ t: (key) => key
19368
+ };
19369
+ const resultMap = checkSessionsCanGenerateSummary(deps, chatSessionIds);
19370
+ const result = {};
19371
+ for (const [id, info] of resultMap) {
19372
+ result[id] = info;
19373
+ }
19374
+ return result;
19375
+ }
18866
19376
 
18867
19377
  // ../../packages/node-runtime/src/services/export-service.ts
18868
19378
  var export_service_exports = {};
@@ -19153,4 +19663,4 @@ export {
19153
19663
  export_service_exports,
19154
19664
  createDatabaseManagerAdapter
19155
19665
  };
19156
- //# sourceMappingURL=chunk-QJQJIUUS.mjs.map
19666
+ //# sourceMappingURL=chunk-G7Q7EQ3V.mjs.map