chattercatcher 0.1.22 → 0.1.24

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.
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import fs14 from "fs/promises";
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "chattercatcher",
11
- version: "0.1.22",
11
+ version: "0.1.24",
12
12
  description: "\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u5E93\u673A\u5668\u4EBA",
13
13
  type: "module",
14
14
  main: "dist/index.js",
@@ -1623,7 +1623,7 @@ var EpisodeRepository = class {
1623
1623
  endedAt: last.sentAt,
1624
1624
  messages: windowMessages
1625
1625
  };
1626
- const summary = await input2.summarize(window);
1626
+ const summary = await input2.summarize(window, input2.now);
1627
1627
  created.push(this.insertEpisode(window, summary));
1628
1628
  }
1629
1629
  }
@@ -2613,16 +2613,17 @@ async function restoreLocalData(input2) {
2613
2613
  }
2614
2614
 
2615
2615
  // src/episodes/summarizer.ts
2616
- async function summarizeEpisodeWindow(window, model) {
2616
+ async function summarizeEpisodeWindow(window, model, now) {
2617
2617
  const transcript = window.messages.map((message) => `[${message.sentAt}] ${message.senderName}\uFF1A${message.text}`).join("\n");
2618
2618
  const summary = await model.complete([
2619
2619
  {
2620
2620
  role: "system",
2621
- content: "\u4F60\u662F ChatterCatcher \u7684\u4F1A\u8BDD\u8BB0\u5FC6\u6574\u7406\u6A21\u5757\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u628A\u788E\u7247\u5316\u95F2\u804A\u6574\u7406\u6210\u53EF\u68C0\u7D22\u4E8B\u5B9E\uFF0C\u8865\u5168\u77ED\u6D88\u606F\u3001\u4EE3\u8BCD\u3001\u7F29\u5199\u4E0E\u4E0A\u4E0B\u6587\u4E4B\u95F4\u7684\u5173\u7CFB\u3002\u53EA\u603B\u7ED3\u660E\u786E\u4E8B\u5B9E\uFF0C\u4E0D\u8981\u7F16\u9020\u3002\u4FDD\u7559\u91CD\u8981\u6570\u5B57\u3001\u65E5\u671F\u3001\u94FE\u63A5\u548C\u4EE3\u7801\uFF1B\u5982\u679C\u5185\u5BB9\u50CF\u5BC6\u7801\u3001API key\u3001token \u6216\u5BC6\u94A5\uFF0C\u53EA\u63CF\u8FF0\u5176\u4E0A\u4E0B\u6587\u5173\u7CFB\uFF0C\u4E0D\u8981\u5728\u6458\u8981\u4E2D\u590D\u5199\u539F\u6587\u3002"
2621
+ content: "\u4F60\u662F ChatterCatcher \u7684\u4F1A\u8BDD\u8BB0\u5FC6\u6574\u7406\u6A21\u5757\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u628A\u788E\u7247\u5316\u95F2\u804A\u6574\u7406\u6210\u53EF\u68C0\u7D22\u4E8B\u5B9E\uFF0C\u8865\u5168\u77ED\u6D88\u606F\u3001\u4EE3\u8BCD\u3001\u7F29\u5199\u4E0E\u4E0A\u4E0B\u6587\u4E4B\u95F4\u7684\u5173\u7CFB\u3002\u53EA\u603B\u7ED3\u660E\u786E\u4E8B\u5B9E\uFF0C\u4E0D\u8981\u7F16\u9020\u3002\u4FDD\u7559\u91CD\u8981\u6570\u5B57\u3001\u65E5\u671F\u3001\u94FE\u63A5\u548C\u4EE3\u7801\uFF1B\u5982\u679C\u5185\u5BB9\u50CF\u5BC6\u7801\u3001API key\u3001token \u6216\u5BC6\u94A5\uFF0C\u53EA\u63CF\u8FF0\u5176\u4E0A\u4E0B\u6587\u5173\u7CFB\uFF0C\u4E0D\u8981\u5728\u6458\u8981\u4E2D\u590D\u5199\u539F\u6587\u3002\u6D88\u606F\u91CC\u7684\u201C\u4ECA\u5929\u201D\u201C\u660E\u5929\u201D\u201C\u6628\u665A\u201D\u201C\u4E0B\u5468\u4E09\u201D\u7B49\u76F8\u5BF9\u65F6\u95F4\u8868\u8FF0\uFF0C\u8BF7\u57FA\u4E8E\u6BCF\u6761\u6D88\u606F\u524D\u7684\u53D1\u9001\u65F6\u95F4\u6233\u63A8\u5BFC\u4E3A\u5177\u4F53\u65E5\u671F\u5199\u5165\u6458\u8981\u3002\u4F8B\u5982 [2026-05-05T20:00:00.000Z] \u5988\u5988\u8BF4\u201C\u660E\u5929\u8981\u7528\u4E1D\u4E1D\u9732\u201D\uFF0C\u6458\u8981\u5E94\u5199\u4E3A\u201C2026-05-06 \u8981\u7528\u4E1D\u4E1D\u9732\u201D\u3002"
2622
2622
  },
2623
2623
  {
2624
2624
  role: "user",
2625
- content: `\u7FA4\u804A\uFF1A${window.chatName}
2625
+ content: `\u5F53\u524D\u65F6\u95F4\uFF1A${now.toISOString()}
2626
+ \u7FA4\u804A\uFF1A${window.chatName}
2626
2627
  \u65F6\u95F4\uFF1A${window.startedAt} - ${window.endedAt}
2627
2628
 
2628
2629
  \u804A\u5929\u8BB0\u5F55\uFF1A
@@ -2641,7 +2642,7 @@ async function processEpisodesNow(input2) {
2641
2642
  now: input2.now ?? /* @__PURE__ */ new Date(),
2642
2643
  quietMs: input2.config.episodes.quietMinutes * 60 * 1e3,
2643
2644
  windowMs: input2.config.episodes.windowMinutes * 60 * 1e3,
2644
- summarize: (window) => summarizeEpisodeWindow(window, input2.model)
2645
+ summarize: (window, now) => summarizeEpisodeWindow(window, input2.model, now)
2645
2646
  });
2646
2647
  return { created: created.length };
2647
2648
  }
@@ -3772,7 +3773,7 @@ function stripMentions(text, mentions) {
3772
3773
  }
3773
3774
  return result.replace(/@/g, " ").replace(/\s+/g, " ").trim();
3774
3775
  }
3775
- var FEISHU_TOOL_SYSTEM_PROMPT = "\u4F60\u662F\u98DE\u4E66\u7FA4\u804A\u52A9\u624B\u3002\u4F60\u53EF\u4EE5\u5148\u641C\u7D22\u672C\u5730\u77E5\u8BC6\u6765\u56DE\u7B54\u95EE\u9898\uFF1B\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u521B\u5EFA\u3001\u67E5\u770B\u6216\u5220\u9664\u7FA4\u6D88\u606F\u5B9A\u65F6\u4EFB\u52A1\u65F6\uFF0C\u4E5F\u53EF\u4EE5\u8C03\u7528\u5B9A\u65F6\u4EFB\u52A1\u5DE5\u5177\u3002\u5B9A\u65F6\u4EFB\u52A1\u5DE5\u5177\u53EA\u7BA1\u7406\u5F53\u524D\u7FA4\u804A\uFF0C\u4E0D\u80FD\u8DE8\u7FA4\u64CD\u4F5C\u3002\u82E5\u7528\u6237\u7528\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u65F6\u95F4\uFF0C\u4F60\u9700\u8981\u5148\u5C06\u5176\u8F6C\u6362\u4E3A\u4E94\u5B57\u6BB5 cron \u8868\u8FBE\u5F0F\uFF08\u5206 \u65F6 \u65E5 \u6708 \u5468\uFF09\uFF0C\u518D\u8C03\u7528\u5DE5\u5177\u3002\u5BF9\u4E8E\u4E00\u822C\u95EE\u7B54\uFF0C\u5148\u6309\u9700\u8C03\u7528\u641C\u7D22\u5DE5\u5177\uFF0C\u518D\u57FA\u4E8E\u5DE5\u5177\u8FD4\u56DE\u7684\u8BC1\u636E\u76F4\u63A5\u7ED9\u51FA\u6700\u7EC8\u7B54\u6848\uFF1B\u82E5\u5F15\u7528\u4E86\u68C0\u7D22\u7ED3\u679C\uFF0C\u8981\u5728\u7B54\u6848\u91CC\u76F4\u63A5\u5199\u51FA\u5F15\u7528\u5185\u5BB9\u3002\u4E0D\u8981\u58F0\u79F0\u5B8C\u6210\u4E86\u672A\u5B9E\u9645\u8C03\u7528\u7684\u64CD\u4F5C\u3002";
3776
+ var FEISHU_TOOL_SYSTEM_PROMPT = "\u4F60\u662F\u98DE\u4E66\u7FA4\u804A\u52A9\u624B\u3002\u4F60\u53EF\u4EE5\u5148\u641C\u7D22\u672C\u5730\u77E5\u8BC6\u6765\u56DE\u7B54\u95EE\u9898\uFF1B\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u521B\u5EFA\u3001\u67E5\u770B\u6216\u5220\u9664\u7FA4\u6D88\u606F\u5B9A\u65F6\u4EFB\u52A1\u65F6\uFF0C\u4E5F\u53EF\u4EE5\u8C03\u7528\u5B9A\u65F6\u4EFB\u52A1\u5DE5\u5177\u3002\u5B9A\u65F6\u4EFB\u52A1\u5DE5\u5177\u53EA\u7BA1\u7406\u5F53\u524D\u7FA4\u804A\uFF0C\u4E0D\u80FD\u8DE8\u7FA4\u64CD\u4F5C\u3002\u82E5\u7528\u6237\u7528\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u65F6\u95F4\uFF0C\u4F60\u9700\u8981\u5148\u5C06\u5176\u8F6C\u6362\u4E3A\u4E94\u5B57\u6BB5 cron \u8868\u8FBE\u5F0F\uFF08\u5206 \u65F6 \u65E5 \u6708 \u5468\uFF09\uFF0C\u518D\u8C03\u7528\u5DE5\u5177\u3002\u5F53\u524D\u65F6\u95F4\u4F1A\u63D0\u4F9B\u7ED9\u4F60\u3002\u68C0\u7D22\u8BC1\u636E\u4E2D\u7684\u65F6\u95F4\u6233\u662F\u6D88\u606F\u88AB\u53D1\u9001\u65F6\u7684\u771F\u5B9E\u65F6\u95F4\u3002\u56DE\u7B54\u65F6\u82E5\u6D89\u53CA\u76F8\u5BF9\u65F6\u95F4\u8868\u8FF0\uFF08\u5982\u6D88\u606F\u4E2D\u8BF4\u201C\u660E\u5929\u201D\u201C\u4ECA\u665A\u201D\uFF09\uFF0C\u5FC5\u987B\u57FA\u4E8E\u8BC1\u636E\u4E2D\u6BCF\u6761\u6D88\u606F\u7684\u65F6\u95F4\u6233\u63A8\u5BFC\u4E3A\u5177\u4F53\u65E5\u671F\uFF0C\u4E0D\u8981\u7167\u642C\u539F\u6587\u7684\u76F8\u5BF9\u8868\u8FF0\u3002\u5BF9\u4E8E\u4E00\u822C\u95EE\u7B54\uFF0C\u5148\u6309\u9700\u8C03\u7528\u641C\u7D22\u5DE5\u5177\uFF0C\u518D\u57FA\u4E8E\u5DE5\u5177\u8FD4\u56DE\u7684\u8BC1\u636E\u76F4\u63A5\u7ED9\u51FA\u6700\u7EC8\u7B54\u6848\uFF1B\u82E5\u5F15\u7528\u4E86\u68C0\u7D22\u7ED3\u679C\uFF0C\u8981\u5728\u7B54\u6848\u91CC\u76F4\u63A5\u5199\u51FA\u5F15\u7528\u5185\u5BB9\u3002\u4E0D\u8981\u58F0\u79F0\u5B8C\u6210\u4E86\u672A\u5B9E\u9645\u8C03\u7528\u7684\u64CD\u4F5C\u3002";
3776
3777
  var DEFAULT_MAX_MODEL_TURNS = 4;
3777
3778
  var DEFAULT_MAX_TOOL_CALLS = 8;
3778
3779
  var FEISHU_TOOL_LOOP_FALLBACK = "\u5B9A\u65F6\u4EFB\u52A1\u64CD\u4F5C\u5DF2\u63D0\u4EA4\uFF0C\u4F46\u6A21\u578B\u6CA1\u6709\u751F\u6210\u6700\u7EC8\u56DE\u590D\u3002";
@@ -3795,7 +3796,8 @@ async function runFeishuToolLoop(input2) {
3795
3796
  const maxToolCalls = input2.maxToolCalls ?? DEFAULT_MAX_TOOL_CALLS;
3796
3797
  const messages = [
3797
3798
  { role: "system", content: FEISHU_TOOL_SYSTEM_PROMPT },
3798
- { role: "user", content: input2.question }
3799
+ { role: "user", content: `\u5F53\u524D\u65F6\u95F4\uFF1A${input2.now.toISOString()}
3800
+ \u95EE\u9898\uFF1A${input2.question}` }
3799
3801
  ];
3800
3802
  const toolsByName = new Map(input2.tools.map((tool) => [tool.name, tool]));
3801
3803
  let toolCallsUsed = 0;
@@ -3926,6 +3928,7 @@ var FeishuQuestionHandler = class {
3926
3928
  return decision;
3927
3929
  }
3928
3930
  const questionMessageId = payload.event?.message?.message_id;
3931
+ const now = /* @__PURE__ */ new Date();
3929
3932
  const qaLogs = new QaLogRepository(this.options.database);
3930
3933
  await this.acknowledgeQuestion(decision.chatId, questionMessageId);
3931
3934
  const { tools, close } = await createAgenticRagSearchTools({
@@ -3945,6 +3948,7 @@ var FeishuQuestionHandler = class {
3945
3948
  const allTools = [...tools, ...cronTools];
3946
3949
  const answer = await runFeishuToolLoop({
3947
3950
  question: decision.question,
3951
+ now,
3948
3952
  tools: allTools,
3949
3953
  model: this.options.model
3950
3954
  });
@@ -4918,7 +4922,8 @@ function rankEvidenceForPrompt(evidence) {
4918
4922
  return scoreDiff;
4919
4923
  });
4920
4924
  }
4921
- function buildEvidencePrompt(question, evidence, options = {}) {
4925
+ function buildEvidencePrompt(input2, options = {}) {
4926
+ const { question, evidence, now } = input2;
4922
4927
  if (evidence.length === 0) {
4923
4928
  throw new Error("RAG evidence is required before answer generation.");
4924
4929
  }
@@ -4949,11 +4954,12 @@ function buildEvidencePrompt(question, evidence, options = {}) {
4949
4954
  messages: [
4950
4955
  {
4951
4956
  role: "system",
4952
- content: "\u4F60\u662F ChatterCatcher \u7684\u95EE\u7B54\u6A21\u5757\u3002\u53EA\u80FD\u6839\u636E\u63D0\u4F9B\u7684\u68C0\u7D22\u8BC1\u636E\u56DE\u7B54\uFF0C\u5FC5\u987B\u7B80\u77ED\u76F4\u63A5\u3002\u4E8B\u5B9E\u6027\u7ED3\u8BBA\u5FC5\u987B\u5F15\u7528 [S1] \u8FD9\u6837\u7684\u6765\u6E90\u6807\u8BB0\u3002\u8BC1\u636E\u4E0D\u8DB3\u65F6\u8BF4\u4E0D\u77E5\u9053\uFF0C\u4E0D\u8981\u731C\u3002\u82E5\u8BC1\u636E\u4E92\u76F8\u77DB\u76FE\uFF0C\u4F18\u5148\u91C7\u7528\u65F6\u95F4\u66F4\u65B0\u4E14\u8868\u8FF0\u660E\u786E\u7684\u8BC1\u636E\uFF1B\u5982\u679C\u8F83\u65B0\u7684\u8BC1\u636E\u53EA\u662F\u8BA8\u8BBA\u3001\u731C\u6D4B\u6216\u4E0D\u786E\u5B9A\u8868\u8FBE\uFF0C\u4E0D\u8981\u628A\u5B83\u5F53\u4F5C\u786E\u5B9A\u66F4\u65B0\u3002"
4957
+ content: "\u4F60\u662F ChatterCatcher \u7684\u95EE\u7B54\u6A21\u5757\u3002\u53EA\u80FD\u6839\u636E\u63D0\u4F9B\u7684\u68C0\u7D22\u8BC1\u636E\u56DE\u7B54\uFF0C\u5FC5\u987B\u7B80\u77ED\u76F4\u63A5\u3002\u4E8B\u5B9E\u6027\u7ED3\u8BBA\u5FC5\u987B\u5F15\u7528 [S1] \u8FD9\u6837\u7684\u6765\u6E90\u6807\u8BB0\u3002\u8BC1\u636E\u4E0D\u8DB3\u65F6\u8BF4\u4E0D\u77E5\u9053\uFF0C\u4E0D\u8981\u731C\u3002\u82E5\u8BC1\u636E\u4E92\u76F8\u77DB\u76FE\uFF0C\u4F18\u5148\u91C7\u7528\u65F6\u95F4\u66F4\u65B0\u4E14\u8868\u8FF0\u660E\u786E\u7684\u8BC1\u636E\uFF1B\u5982\u679C\u8F83\u65B0\u7684\u8BC1\u636E\u53EA\u662F\u8BA8\u8BBA\u3001\u731C\u6D4B\u6216\u4E0D\u786E\u5B9A\u8868\u8FBE\uFF0C\u4E0D\u8981\u628A\u5B83\u5F53\u4F5C\u786E\u5B9A\u66F4\u65B0\u3002\u68C0\u7D22\u8BC1\u636E\u4E2D\u7684\u65F6\u95F4\u6233\u662F\u6D88\u606F\u88AB\u53D1\u9001\u65F6\u7684\u771F\u5B9E\u65F6\u95F4\u3002\u56DE\u7B54\u65F6\u82E5\u6D89\u53CA\u76F8\u5BF9\u65F6\u95F4\u8868\u8FF0\uFF08\u5982\u6D88\u606F\u4E2D\u8BF4\u201C\u660E\u5929\u201D\u201C\u4ECA\u665A\u201D\uFF09\uFF0C\u5FC5\u987B\u57FA\u4E8E\u8BC1\u636E\u4E2D\u6BCF\u6761\u6D88\u606F\u7684\u65F6\u95F4\u6233\u63A8\u5BFC\u4E3A\u5177\u4F53\u65E5\u671F\uFF08\u5982\u201C2026-05-06\u201D\uFF09\uFF0C\u4E0D\u8981\u7167\u642C\u539F\u6587\u7684\u76F8\u5BF9\u8868\u8FF0\u3002\u8BC1\u636E\u4E2D\u6BCF\u6761\u6D88\u606F\u6807\u6CE8\u4E86\u53D1\u9001\u65F6\u95F4\u3002\u56DE\u7B54\u65F6\u4F18\u5148\u8F93\u51FA\u7EDD\u5BF9\u65E5\u671F\uFF0C\u4E0D\u786E\u5B9A\u65F6\u5F15\u7528\u539F\u6587\u65F6\u95F4\u6233\uFF0C\u4E0D\u8981\u4F7F\u7528\u201C\u4ECA\u5929\u201D\u201C\u660E\u5929\u201D\u7B49\u4F9D\u8D56\u5F53\u524D\u4E0A\u4E0B\u6587\u7684\u6A21\u7CCA\u8868\u8FF0\u3002"
4953
4958
  },
4954
4959
  {
4955
4960
  role: "user",
4956
- content: `\u95EE\u9898\uFF1A${question}
4961
+ content: `\u5F53\u524D\u65F6\u95F4\uFF1A${now.toISOString()}
4962
+ \u95EE\u9898\uFF1A${question}
4957
4963
 
4958
4964
  \u8BC1\u636E\u5904\u7406\u89C4\u5219\uFF1A
4959
4965
  1. \u5148\u5224\u65AD\u8BC1\u636E\u662F\u5426\u8DB3\u4EE5\u56DE\u7B54\u95EE\u9898\u3002
@@ -4967,7 +4973,7 @@ ${evidenceText}`
4967
4973
  };
4968
4974
  }
4969
4975
  async function generateGroundedAnswer(input2) {
4970
- const prompt = buildEvidencePrompt(input2.question, input2.evidence);
4976
+ const prompt = buildEvidencePrompt({ question: input2.question, evidence: input2.evidence, now: input2.now });
4971
4977
  const answer = await input2.model.complete(prompt.messages);
4972
4978
  return {
4973
4979
  answer,
@@ -4977,6 +4983,7 @@ async function generateGroundedAnswer(input2) {
4977
4983
 
4978
4984
  // src/rag/qa-service.ts
4979
4985
  async function askWithRag(input2) {
4986
+ const now = input2.now ?? /* @__PURE__ */ new Date();
4980
4987
  const evidence = await input2.retriever.retrieve(input2.question);
4981
4988
  if (evidence.length === 0) {
4982
4989
  return {
@@ -4987,7 +4994,8 @@ async function askWithRag(input2) {
4987
4994
  return generateGroundedAnswer({
4988
4995
  question: input2.question,
4989
4996
  evidence,
4990
- model: input2.model
4997
+ model: input2.model,
4998
+ now
4991
4999
  });
4992
5000
  }
4993
5001
 
@@ -5540,18 +5548,16 @@ function buildHtml() {
5540
5548
  " <span>" + escapeHtml(item.status) + "</span>",
5541
5549
  " <span>" + escapeHtml(citationCount) + " \u6761\u5F15\u7528</span>",
5542
5550
  " </div>",
5543
- " <div class="message-body"><strong>\u95EE\uFF1A</strong>" + escapeHtml(item.question) + "</div>",
5544
- " <div class="message-body"><strong>\u7B54\uFF1A</strong>" + escapeHtml(item.answer) + "</div>",
5551
+ " <div class=\\"message-body\\"><strong>\u95EE\uFF1A</strong>" + escapeHtml(item.question) + "</div>",
5552
+ " <div class=\\"message-body\\"><strong>\u7B54\uFF1A</strong>" + escapeHtml(item.answer) + "</div>",
5545
5553
  "</article>",
5546
- ].join("
5547
- ");
5554
+ ].join("\\n");
5548
5555
  });
5549
5556
  qaLogs.innerHTML = [
5550
5557
  '<div class="message-list">',
5551
5558
  rows.join(""),
5552
5559
  "</div>",
5553
- ].join("
5554
- ");
5560
+ ].join("\\n");
5555
5561
  }
5556
5562
 
5557
5563
  async function fetchJson(path) {