@zaclinley/dsh-mailharbor 0.9.0 → 0.10.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.
package/lib/index.js CHANGED
@@ -1716,21 +1716,28 @@ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as rea
1716
1716
  import { dirname, join as join4 } from "node:path";
1717
1717
  var TIMEOUT_MS = 9e4;
1718
1718
  var MAX_PROMPT_CHARS = 4e4;
1719
- var SUMMARY_SYSTEM = `\u4F60\u662F\u90AE\u4EF6\u52A9\u7406\uFF0C\u670D\u52A1\u4E8E\u4E2D\u6587\u7528\u6237\u3002\u8BF7\u7528\u7B80\u4F53\u4E2D\u6587\u603B\u7ED3\u90AE\u4EF6\uFF0C\u4E25\u683C\u6309\u4E0B\u9762\u56DB\u6BB5\u8F93\u51FA\uFF0C\u4F7F\u7528\u7ED9\u5B9A\u7684\u5C0F\u6807\u9898\uFF0C\u4E0D\u8981\u8F93\u51FA\u5176\u4ED6\u5185\u5BB9\uFF1A
1719
+ var SUMMARY_SYSTEM = `\u4F60\u662F\u90AE\u4EF6\u52A9\u7406\uFF0C\u670D\u52A1\u4E8E\u4E2D\u6587\u7528\u6237\u3002\u7528\u7B80\u4F53\u4E2D\u6587\uFF0C\u7B80\u6D01\u4F18\u5148\uFF0C\u603B\u957F\u4E0D\u8D85\u8FC7 180 \u5B57\u3002
1720
+
1721
+ \u4E25\u683C\u6309\u4E0B\u9762\u56DB\u4E2A\u5C0F\u6807\u9898\u8F93\u51FA\uFF0C\u4E0D\u8981\u8F93\u51FA\u5176\u4ED6\u5185\u5BB9\uFF1A
1720
1722
 
1721
1723
  \u7ED3\u8BBA
1722
- \uFF081-2 \u53E5\u8BDD\u8BF4\u660E\u8FD9\u5C01\u90AE\u4EF6\u662F\u4EC0\u4E48\u4E8B\uFF09
1724
+ \u4E00\u53E5\u8BDD\uFF08\u4E0D\u8D85\u8FC7 40 \u5B57\uFF09\u8BF4\u660E\u8FD9\u662F\u4EC0\u4E48\u90AE\u4EF6\u3001\u8C01\u8981\u505A\u4EC0\u4E48\u3002
1723
1725
 
1724
1726
  \u9700\u8981\u4F60\u505A\u4EC0\u4E48
1725
- \uFF08\u6CA1\u6709\u5C31\u5199\u300C\u65E0\u9700\u64CD\u4F5C\u300D\uFF1B\u6709\u5C31\u5217 1-3 \u6761\uFF0C\u9879\u76EE\u7B26\u53F7\uFF09
1727
+ - \u6700\u591A 3 \u6761\uFF0C\u6BCF\u6761\u4E0D\u8D85\u8FC7 20 \u5B57\uFF1B\u6CA1\u6709\u5C31\u53EA\u5199\u300C\u65E0\u9700\u64CD\u4F5C\u300D\u3002
1728
+ - \u5982\u679C\u8EAB\u4EFD\u662F\u300C\u4EC5\u6284\u9001\u300D\uFF0C\u53EA\u5199\u300C\u4F60\u53EA\u662F\u88AB\u6284\u9001\uFF0C\u901A\u5E38\u65E0\u9700\u64CD\u4F5C\u300D\u3002
1729
+ - \u5982\u679C\u8EAB\u4EFD\u662F\u300C\u53D1\u4EF6\u4EBA\u300D\uFF0C\u53EA\u5199\u300C\u8FD9\u662F\u4F60\u53D1\u51FA\u7684\u90AE\u4EF6\uFF0C\u65E0\u9700\u64CD\u4F5C\u300D\u3002
1730
+ - \u5982\u679C\u8EAB\u4EFD\u662F\u300C\u4E0D\u5728\u6536\u4EF6\u4EBA/\u6284\u9001\u5217\u8868\u300D\uFF0C\u53EA\u5199\u300C\u4F60\u4E0D\u5728\u6536\u4EF6\u4EBA\u5217\u8868\uFF0C\u901A\u5E38\u65E0\u9700\u64CD\u4F5C\u300D\u3002
1726
1731
 
1727
1732
  \u5173\u952E\u4FE1\u606F
1728
- \uFF08\u65F6\u95F4\u3001\u91D1\u989D\u3001\u4EBA\u540D\u3001\u5355\u53F7\u3001\u5730\u5740\u7B49\uFF0C\u9879\u76EE\u7B26\u53F7\uFF1B\u6CA1\u6709\u5C31\u5199\u300C\u65E0\u300D\uFF09
1733
+ - \u6700\u591A 5 \u6761\uFF0C\u6BCF\u6761\u4E0D\u8D85\u8FC7 30 \u5B57\uFF1B\u53EA\u5217\u771F\u6B63\u6709\u7528\u7684\u65F6\u95F4\u3001\u91D1\u989D\u3001\u4EBA\u540D\u3001\u5355\u53F7\u3001\u94FE\u63A5\u3002
1734
+ - \u6CA1\u6709\u5C31\u5199\u300C\u65E0\u300D\u3002
1729
1735
 
1730
1736
  \u5EFA\u8BAE\u56DE\u590D\u8981\u70B9
1731
- \uFF081-3 \u6761\u4E2D\u6587\u8981\u70B9\uFF09
1737
+ - \u53EA\u6709\u5F53\u8EAB\u4EFD\u662F\u300C\u76F4\u63A5\u6536\u4EF6\u4EBA\u300D\u4E14\u90AE\u4EF6\u660E\u786E\u8981\u6C42\u4F60\u884C\u52A8\u65F6\u624D\u8F93\u51FA\uFF1B\u6700\u591A 2 \u6761\uFF0C\u6BCF\u6761\u4E0D\u8D85\u8FC7 25 \u5B57\u3002
1738
+ - \u5176\u4ED6\u8EAB\u4EFD\u4E0D\u8981\u8F93\u51FA\u8FD9\u4E2A\u5C0F\u6807\u9898\u3002
1732
1739
 
1733
- \u53EA\u4F9D\u636E\u90AE\u4EF6\u5185\u5BB9\uFF0C\u4E0D\u8981\u7F16\u9020\uFF0C\u4E0D\u8981\u8F93\u51FA\u601D\u8003\u8FC7\u7A0B\u3002`;
1740
+ \u4E0D\u8981\u590D\u8FF0\u539F\u6587\uFF0C\u4E0D\u8981\u89E3\u91CA\uFF0C\u4E0D\u8981\u8F93\u51FA\u601D\u8003\u8FC7\u7A0B\u3002`;
1734
1741
  var REPLY_SYSTEM = (language, style) => `\u4F60\u662F\u90AE\u4EF6\u5199\u4F5C\u52A9\u7406\u3002\u6839\u636E\u539F\u59CB\u90AE\u4EF6\u548C\u7528\u6237\u7684\u8981\u6C42\uFF0C\u5199\u4E00\u5C01\u53EF\u4EE5\u76F4\u63A5\u53D1\u9001\u7684\u56DE\u590D\u6B63\u6587\u3002
1735
1742
 
1736
1743
  \u8981\u6C42\uFF1A
@@ -1837,12 +1844,30 @@ function bodyText(message) {
1837
1844
  if (message.text.trim() !== "") return message.text;
1838
1845
  return (message.html ?? "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|tr|h[1-6])>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").trim();
1839
1846
  }
1840
- function contextOf(message) {
1847
+ function roleOf(message, email) {
1848
+ const target = String(email ?? "").trim().toLowerCase();
1849
+ if (target === "") return "unknown";
1850
+ const has = (list) => list.some((item) => item.address.trim().toLowerCase() === target);
1851
+ if (has(message.to)) return "to";
1852
+ if (has(message.cc)) return "cc";
1853
+ if (message.from.address.trim().toLowerCase() === target) return "sender";
1854
+ return "other";
1855
+ }
1856
+ var ROLE_TEXT = {
1857
+ to: "\u76F4\u63A5\u6536\u4EF6\u4EBA",
1858
+ cc: "\u4EC5\u6284\u9001\uFF08\u4E0D\u5728\u6536\u4EF6\u4EBA\u91CC\uFF09",
1859
+ sender: "\u53D1\u4EF6\u4EBA\uFF08\u4F60\u53D1\u51FA\u7684\u90AE\u4EF6\uFF09",
1860
+ other: "\u4E0D\u5728\u6536\u4EF6\u4EBA/\u6284\u9001\u5217\u8868\uFF08\u53EF\u80FD\u662F\u90AE\u4EF6\u5217\u8868\u6216\u5BC6\u9001\uFF09",
1861
+ unknown: "\u672A\u77E5"
1862
+ };
1863
+ function contextOf(message, email) {
1864
+ const role = roleOf(message, email);
1841
1865
  const lines = [
1842
1866
  `\u4E3B\u9898: ${message.subject}`,
1843
1867
  `\u53D1\u4EF6\u4EBA: ${message.from.name !== "" ? `${message.from.name} <${message.from.address}>` : message.from.address}`,
1844
1868
  `\u6536\u4EF6\u4EBA: ${message.to.map((item) => item.name !== "" ? `${item.name} <${item.address}>` : item.address).join(", ")}`,
1845
1869
  ...message.cc.length === 0 ? [] : [`\u6284\u9001: ${message.cc.map((item) => item.address).join(", ")}`],
1870
+ ...role === "unknown" ? [] : [`\u4F60\u7684\u8EAB\u4EFD: ${ROLE_TEXT[role]}`],
1846
1871
  `\u65F6\u95F4: ${message.date}`,
1847
1872
  "",
1848
1873
  clip(bodyText(message))
@@ -1941,8 +1966,11 @@ var AiService = class {
1941
1966
  if (trimmed === "") throw new Error("\u6A21\u578B\u6CA1\u6709\u8FD4\u56DE\u5185\u5BB9");
1942
1967
  return trimmed;
1943
1968
  }
1944
- async summarize(message, signal) {
1945
- return this.complete(SUMMARY_SYSTEM, contextOf(message), signal);
1969
+ async summarize(message, email, signal) {
1970
+ const output = await this.complete(SUMMARY_SYSTEM, contextOf(message, email), signal);
1971
+ const compact = compactSummary(output);
1972
+ const role = roleOf(message, email);
1973
+ return role === "to" || role === "unknown" ? compact : stripReplySuggestions(compact);
1946
1974
  }
1947
1975
  async reply(account, message, options = {}, signal) {
1948
1976
  const style = this.style(account)?.profile ?? "";
@@ -1961,14 +1989,14 @@ var AiService = class {
1961
1989
  return this.complete(TRANSLATE_SYSTEM(target), clip(text2, 2e4), signal);
1962
1990
  }
1963
1991
  /** Free-form question about a mail, with up to three earlier AI outputs as context. */
1964
- async ask(message, question, history, signal) {
1992
+ async ask(message, question, history, email, signal) {
1965
1993
  const prompt = (question ?? "").trim();
1966
1994
  if (prompt === "") throw new Error("\u95EE\u9898\u4E0D\u80FD\u4E3A\u7A7A");
1967
1995
  const earlier = history.filter((item) => item.trim() !== "").slice(-3).map((item, index) => `\u3010\u5DF2\u6709\u8BB0\u5F55 ${String(index + 1)}\u3011
1968
1996
  ${clip(item, 4e3)}`).join("\n\n");
1969
1997
  const user = [
1970
1998
  "\u90AE\u4EF6\uFF1A",
1971
- contextOf(message),
1999
+ contextOf(message, email),
1972
2000
  ...earlier === "" ? [] : ["", "\u5DF2\u6709 AI \u8BB0\u5F55\uFF1A", earlier],
1973
2001
  "",
1974
2002
  `\u7528\u6237\u7684\u95EE\u9898\uFF1A${prompt}`
@@ -2103,6 +2131,39 @@ function parseTaskJson(text2) {
2103
2131
  }
2104
2132
  return result.slice(0, 30);
2105
2133
  }
2134
+ function compactSummary(text2) {
2135
+ const sections = [];
2136
+ let current = { title: "", lines: [] };
2137
+ for (const raw of text2.split(/\r?\n/)) {
2138
+ const line2 = raw.trim();
2139
+ const heading = /^#{0,4}\s*(结论|需要你做什么|关键信息|建议回复要点)\s*[::]?\s*$/.exec(line2);
2140
+ if (heading !== null) {
2141
+ if (current.title !== "" || current.lines.length > 0) sections.push(current);
2142
+ current = { title: heading[1] ?? "", lines: [] };
2143
+ } else if (line2 !== "") {
2144
+ current.lines.push(line2.replace(/^[-*•]\s*/, "").trim());
2145
+ }
2146
+ }
2147
+ if (current.title !== "" || current.lines.length > 0) sections.push(current);
2148
+ if (sections.length === 0) return text2.trim();
2149
+ const limits = { \u7ED3\u8BBA: 2, \u9700\u8981\u4F60\u505A\u4EC0\u4E48: 3, \u5173\u952E\u4FE1\u606F: 5, \u5EFA\u8BAE\u56DE\u590D\u8981\u70B9: 2 };
2150
+ const output = [];
2151
+ for (const section of sections) {
2152
+ if (section.title === "") continue;
2153
+ const limit = limits[section.title] ?? 3;
2154
+ const lines = section.lines.filter((line2) => line2 !== "").slice(0, limit);
2155
+ if (lines.length === 0) continue;
2156
+ output.push(`${section.title}
2157
+ ${lines.join("\n")}`);
2158
+ }
2159
+ return output.join("\n\n");
2160
+ }
2161
+ function stripReplySuggestions(text2) {
2162
+ const lines = text2.split(/\r?\n/);
2163
+ const index = lines.findIndex((line2) => /^#{0,4}\s*建议回复要点\s*[::]?\s*$/.test(line2.trim()));
2164
+ if (index < 0) return text2.trimEnd();
2165
+ return lines.slice(0, index).join("\n").trimEnd();
2166
+ }
2106
2167
  function summaryBlocks(text2) {
2107
2168
  const blocks = [];
2108
2169
  let current = { title: "", lines: [] };
@@ -2349,7 +2410,7 @@ ${conversation.trim()}
2349
2410
  }
2350
2411
 
2351
2412
  // src/host/version.ts
2352
- var VERSION = "0.9.0";
2413
+ var VERSION = "0.10.0";
2353
2414
 
2354
2415
  // src/host/api.ts
2355
2416
  init_smtp();
@@ -2708,7 +2769,7 @@ function buildRouter(runtime) {
2708
2769
  let html;
2709
2770
  let meta;
2710
2771
  if (kind === "summary") {
2711
- output = await ai.summarize(detail);
2772
+ output = await ai.summarize(detail, account.email);
2712
2773
  } else if (kind === "reply") {
2713
2774
  output = await ai.reply(account, detail, { instruction, language });
2714
2775
  html = textToHtml(output);
@@ -2727,7 +2788,7 @@ function buildRouter(runtime) {
2727
2788
  meta = { target };
2728
2789
  } else {
2729
2790
  const earlier = runtime.aiThreads(account).get(key)?.cards.filter((card2) => card2.kind === "answer").map((card2) => card2.output) ?? [];
2730
- output = await ai.ask(detail, instruction, earlier);
2791
+ output = await ai.ask(detail, instruction, earlier, account.email);
2731
2792
  }
2732
2793
  const card = {
2733
2794
  id: randomUUID3(),
@@ -2830,7 +2891,7 @@ function buildRouter(runtime) {
2830
2891
  mailboxOfPayload(payload)
2831
2892
  );
2832
2893
  try {
2833
- const summary = await runtime.ai().summarize(detail);
2894
+ const summary = await runtime.ai().summarize(detail, account.email);
2834
2895
  return { summary, blocks: summaryBlocks(summary) };
2835
2896
  } catch (error) {
2836
2897
  throw aiHttpError(error);