@memtensor/memos-local-openclaw-plugin 0.1.3 → 0.1.5

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 (117) hide show
  1. package/.env.example +13 -5
  2. package/README.md +283 -91
  3. package/dist/capture/index.d.ts +5 -7
  4. package/dist/capture/index.d.ts.map +1 -1
  5. package/dist/capture/index.js +72 -43
  6. package/dist/capture/index.js.map +1 -1
  7. package/dist/ingest/dedup.d.ts +8 -0
  8. package/dist/ingest/dedup.d.ts.map +1 -1
  9. package/dist/ingest/dedup.js +21 -0
  10. package/dist/ingest/dedup.js.map +1 -1
  11. package/dist/ingest/providers/anthropic.d.ts +16 -0
  12. package/dist/ingest/providers/anthropic.d.ts.map +1 -1
  13. package/dist/ingest/providers/anthropic.js +214 -1
  14. package/dist/ingest/providers/anthropic.js.map +1 -1
  15. package/dist/ingest/providers/bedrock.d.ts +16 -5
  16. package/dist/ingest/providers/bedrock.d.ts.map +1 -1
  17. package/dist/ingest/providers/bedrock.js +210 -6
  18. package/dist/ingest/providers/bedrock.js.map +1 -1
  19. package/dist/ingest/providers/gemini.d.ts +16 -0
  20. package/dist/ingest/providers/gemini.d.ts.map +1 -1
  21. package/dist/ingest/providers/gemini.js +202 -1
  22. package/dist/ingest/providers/gemini.js.map +1 -1
  23. package/dist/ingest/providers/index.d.ts +31 -0
  24. package/dist/ingest/providers/index.d.ts.map +1 -1
  25. package/dist/ingest/providers/index.js +134 -4
  26. package/dist/ingest/providers/index.js.map +1 -1
  27. package/dist/ingest/providers/openai.d.ts +24 -0
  28. package/dist/ingest/providers/openai.d.ts.map +1 -1
  29. package/dist/ingest/providers/openai.js +255 -1
  30. package/dist/ingest/providers/openai.js.map +1 -1
  31. package/dist/ingest/task-processor.d.ts +65 -0
  32. package/dist/ingest/task-processor.d.ts.map +1 -0
  33. package/dist/ingest/task-processor.js +354 -0
  34. package/dist/ingest/task-processor.js.map +1 -0
  35. package/dist/ingest/worker.d.ts +3 -1
  36. package/dist/ingest/worker.d.ts.map +1 -1
  37. package/dist/ingest/worker.js +131 -23
  38. package/dist/ingest/worker.js.map +1 -1
  39. package/dist/recall/engine.d.ts +1 -0
  40. package/dist/recall/engine.d.ts.map +1 -1
  41. package/dist/recall/engine.js +22 -11
  42. package/dist/recall/engine.js.map +1 -1
  43. package/dist/recall/mmr.d.ts.map +1 -1
  44. package/dist/recall/mmr.js +3 -1
  45. package/dist/recall/mmr.js.map +1 -1
  46. package/dist/skill/bundled-memory-guide.d.ts +6 -0
  47. package/dist/skill/bundled-memory-guide.d.ts.map +1 -0
  48. package/dist/skill/bundled-memory-guide.js +95 -0
  49. package/dist/skill/bundled-memory-guide.js.map +1 -0
  50. package/dist/skill/evaluator.d.ts +31 -0
  51. package/dist/skill/evaluator.d.ts.map +1 -0
  52. package/dist/skill/evaluator.js +194 -0
  53. package/dist/skill/evaluator.js.map +1 -0
  54. package/dist/skill/evolver.d.ts +22 -0
  55. package/dist/skill/evolver.d.ts.map +1 -0
  56. package/dist/skill/evolver.js +193 -0
  57. package/dist/skill/evolver.js.map +1 -0
  58. package/dist/skill/generator.d.ts +25 -0
  59. package/dist/skill/generator.d.ts.map +1 -0
  60. package/dist/skill/generator.js +477 -0
  61. package/dist/skill/generator.js.map +1 -0
  62. package/dist/skill/installer.d.ts +16 -0
  63. package/dist/skill/installer.d.ts.map +1 -0
  64. package/dist/skill/installer.js +89 -0
  65. package/dist/skill/installer.js.map +1 -0
  66. package/dist/skill/upgrader.d.ts +19 -0
  67. package/dist/skill/upgrader.d.ts.map +1 -0
  68. package/dist/skill/upgrader.js +263 -0
  69. package/dist/skill/upgrader.js.map +1 -0
  70. package/dist/skill/validator.d.ts +29 -0
  71. package/dist/skill/validator.d.ts.map +1 -0
  72. package/dist/skill/validator.js +227 -0
  73. package/dist/skill/validator.js.map +1 -0
  74. package/dist/storage/sqlite.d.ts +141 -1
  75. package/dist/storage/sqlite.d.ts.map +1 -1
  76. package/dist/storage/sqlite.js +664 -7
  77. package/dist/storage/sqlite.js.map +1 -1
  78. package/dist/types.d.ts +93 -0
  79. package/dist/types.d.ts.map +1 -1
  80. package/dist/types.js +8 -0
  81. package/dist/types.js.map +1 -1
  82. package/dist/viewer/html.d.ts +1 -1
  83. package/dist/viewer/html.d.ts.map +1 -1
  84. package/dist/viewer/html.js +2391 -159
  85. package/dist/viewer/html.js.map +1 -1
  86. package/dist/viewer/server.d.ts +16 -0
  87. package/dist/viewer/server.d.ts.map +1 -1
  88. package/dist/viewer/server.js +346 -3
  89. package/dist/viewer/server.js.map +1 -1
  90. package/index.ts +572 -89
  91. package/openclaw.plugin.json +20 -45
  92. package/package.json +3 -4
  93. package/skill/memos-memory-guide/SKILL.md +86 -0
  94. package/src/capture/index.ts +85 -45
  95. package/src/ingest/dedup.ts +29 -0
  96. package/src/ingest/providers/anthropic.ts +258 -1
  97. package/src/ingest/providers/bedrock.ts +256 -6
  98. package/src/ingest/providers/gemini.ts +252 -1
  99. package/src/ingest/providers/index.ts +156 -8
  100. package/src/ingest/providers/openai.ts +304 -1
  101. package/src/ingest/task-processor.ts +396 -0
  102. package/src/ingest/worker.ts +145 -34
  103. package/src/recall/engine.ts +23 -12
  104. package/src/recall/mmr.ts +3 -1
  105. package/src/skill/bundled-memory-guide.ts +91 -0
  106. package/src/skill/evaluator.ts +220 -0
  107. package/src/skill/evolver.ts +169 -0
  108. package/src/skill/generator.ts +506 -0
  109. package/src/skill/installer.ts +59 -0
  110. package/src/skill/upgrader.ts +257 -0
  111. package/src/skill/validator.ts +227 -0
  112. package/src/storage/sqlite.ts +802 -7
  113. package/src/types.ts +96 -0
  114. package/src/viewer/html.ts +2391 -159
  115. package/src/viewer/server.ts +346 -3
  116. package/SKILL.md +0 -43
  117. package/www/index.html +0 -632
@@ -24,6 +24,18 @@ class Summarizer {
24
24
  return ruleFallback(text);
25
25
  }
26
26
  }
27
+ async summarizeTask(text) {
28
+ if (!this.cfg) {
29
+ return taskFallback(text);
30
+ }
31
+ try {
32
+ return await this.callTaskProvider(text);
33
+ }
34
+ catch (err) {
35
+ this.log.warn(`Task summarizer failed, using fallback: ${err}`);
36
+ return taskFallback(text);
37
+ }
38
+ }
27
39
  async callProvider(text) {
28
40
  const cfg = this.cfg;
29
41
  switch (cfg.provider) {
@@ -42,12 +54,130 @@ class Summarizer {
42
54
  throw new Error(`Unknown summarizer provider: ${cfg.provider}`);
43
55
  }
44
56
  }
57
+ /**
58
+ * Ask the LLM whether the new message starts a different topic from the current conversation.
59
+ * Returns true if it's a new topic, false if it continues the current one.
60
+ * Returns null if no summarizer is configured (caller should fall back to heuristic).
61
+ */
62
+ async judgeNewTopic(currentContext, newMessage) {
63
+ if (!this.cfg)
64
+ return null;
65
+ try {
66
+ return await this.callTopicJudge(currentContext, newMessage);
67
+ }
68
+ catch (err) {
69
+ this.log.warn(`Topic judge failed: ${err}`);
70
+ return null;
71
+ }
72
+ }
73
+ async callTopicJudge(currentContext, newMessage) {
74
+ const cfg = this.cfg;
75
+ switch (cfg.provider) {
76
+ case "openai":
77
+ case "openai_compatible":
78
+ case "azure_openai":
79
+ return (0, openai_1.judgeNewTopicOpenAI)(currentContext, newMessage, cfg, this.log);
80
+ case "anthropic":
81
+ return (0, anthropic_1.judgeNewTopicAnthropic)(currentContext, newMessage, cfg, this.log);
82
+ case "gemini":
83
+ return (0, gemini_1.judgeNewTopicGemini)(currentContext, newMessage, cfg, this.log);
84
+ case "bedrock":
85
+ return (0, bedrock_1.judgeNewTopicBedrock)(currentContext, newMessage, cfg, this.log);
86
+ default:
87
+ throw new Error(`Unknown summarizer provider: ${cfg.provider}`);
88
+ }
89
+ }
90
+ /**
91
+ * Filter search results by LLM relevance judgment.
92
+ * Returns { relevant: number[], sufficient: boolean } or null if no summarizer configured.
93
+ */
94
+ async filterRelevant(query, candidates) {
95
+ if (!this.cfg)
96
+ return null;
97
+ if (candidates.length === 0)
98
+ return { relevant: [], sufficient: true };
99
+ try {
100
+ return await this.callFilterRelevant(query, candidates);
101
+ }
102
+ catch (err) {
103
+ this.log.warn(`filterRelevant failed, returning all candidates: ${err}`);
104
+ return null;
105
+ }
106
+ }
107
+ async callFilterRelevant(query, candidates) {
108
+ const cfg = this.cfg;
109
+ switch (cfg.provider) {
110
+ case "openai":
111
+ case "openai_compatible":
112
+ case "azure_openai":
113
+ return (0, openai_1.filterRelevantOpenAI)(query, candidates, cfg, this.log);
114
+ case "anthropic":
115
+ return (0, anthropic_1.filterRelevantAnthropic)(query, candidates, cfg, this.log);
116
+ case "gemini":
117
+ return (0, gemini_1.filterRelevantGemini)(query, candidates, cfg, this.log);
118
+ case "bedrock":
119
+ return (0, bedrock_1.filterRelevantBedrock)(query, candidates, cfg, this.log);
120
+ default:
121
+ throw new Error(`Unknown summarizer provider: ${cfg.provider}`);
122
+ }
123
+ }
124
+ /**
125
+ * Judge whether a new memory is DUPLICATE / UPDATE / NEW relative to similar existing memories.
126
+ * Returns null if no summarizer configured (caller should treat as NEW).
127
+ */
128
+ async judgeDedup(newSummary, candidates) {
129
+ if (!this.cfg)
130
+ return null;
131
+ if (candidates.length === 0)
132
+ return null;
133
+ try {
134
+ return await this.callJudgeDedup(newSummary, candidates);
135
+ }
136
+ catch (err) {
137
+ this.log.warn(`judgeDedup failed, treating as NEW: ${err}`);
138
+ return { action: "NEW", reason: "llm_error" };
139
+ }
140
+ }
141
+ async callJudgeDedup(newSummary, candidates) {
142
+ const cfg = this.cfg;
143
+ switch (cfg.provider) {
144
+ case "openai":
145
+ case "openai_compatible":
146
+ case "azure_openai":
147
+ return (0, openai_1.judgeDedupOpenAI)(newSummary, candidates, cfg, this.log);
148
+ case "anthropic":
149
+ return (0, anthropic_1.judgeDedupAnthropic)(newSummary, candidates, cfg, this.log);
150
+ case "gemini":
151
+ return (0, gemini_1.judgeDedupGemini)(newSummary, candidates, cfg, this.log);
152
+ case "bedrock":
153
+ return (0, bedrock_1.judgeDedupBedrock)(newSummary, candidates, cfg, this.log);
154
+ default:
155
+ throw new Error(`Unknown summarizer provider: ${cfg.provider}`);
156
+ }
157
+ }
158
+ async callTaskProvider(text) {
159
+ const cfg = this.cfg;
160
+ switch (cfg.provider) {
161
+ case "openai":
162
+ case "openai_compatible":
163
+ case "azure_openai":
164
+ return (0, openai_1.summarizeTaskOpenAI)(text, cfg, this.log);
165
+ case "anthropic":
166
+ return (0, anthropic_1.summarizeTaskAnthropic)(text, cfg, this.log);
167
+ case "gemini":
168
+ return (0, gemini_1.summarizeTaskGemini)(text, cfg, this.log);
169
+ case "bedrock":
170
+ return (0, bedrock_1.summarizeTaskBedrock)(text, cfg, this.log);
171
+ default:
172
+ throw new Error(`Unknown summarizer provider: ${cfg.provider}`);
173
+ }
174
+ }
45
175
  }
46
176
  exports.Summarizer = Summarizer;
47
- /**
48
- * Rule-based fallback: produce a single short sentence from the first
49
- * meaningful line, appending any key entities found in the text.
50
- */
177
+ function taskFallback(text) {
178
+ const lines = text.split("\n").filter((l) => l.trim().length > 10);
179
+ return lines.slice(0, 30).join("\n").slice(0, 2000);
180
+ }
51
181
  function ruleFallback(text) {
52
182
  const lines = text.split("\n").filter((l) => l.trim().length > 10);
53
183
  const first = (lines[0] ?? text).trim();
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ingest/providers/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ingest/providers/index.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,27 @@
1
1
  import type { SummarizerConfig, Logger } from "../../types";
2
+ export declare function summarizeTaskOpenAI(text: string, cfg: SummarizerConfig, log: Logger): Promise<string>;
2
3
  export declare function summarizeOpenAI(text: string, cfg: SummarizerConfig, log: Logger): Promise<string>;
4
+ export declare function judgeNewTopicOpenAI(currentContext: string, newMessage: string, cfg: SummarizerConfig, log: Logger): Promise<boolean>;
5
+ export interface FilterResult {
6
+ relevant: number[];
7
+ sufficient: boolean;
8
+ }
9
+ export declare function filterRelevantOpenAI(query: string, candidates: Array<{
10
+ index: number;
11
+ summary: string;
12
+ role: string;
13
+ }>, cfg: SummarizerConfig, log: Logger): Promise<FilterResult>;
14
+ export declare const DEDUP_JUDGE_PROMPT = "You are a memory deduplication system. Given a NEW memory summary and several EXISTING memory summaries, determine the relationship.\n\nFor each EXISTING memory, the NEW memory is either:\n- \"DUPLICATE\": NEW is fully covered by an EXISTING memory \u2014 no new information at all\n- \"UPDATE\": NEW contains information that supplements or updates an EXISTING memory (new data, status change, additional detail)\n- \"NEW\": NEW is a different topic/event despite surface similarity\n\nPick the BEST match among all candidates. If none match well, choose \"NEW\".\n\nOutput a single JSON object:\n- If DUPLICATE: {\"action\":\"DUPLICATE\",\"targetIndex\":2,\"reason\":\"...\"}\n- If UPDATE: {\"action\":\"UPDATE\",\"targetIndex\":3,\"reason\":\"...\",\"mergedSummary\":\"a combined summary preserving all info from both old and new, same language as input\"}\n- If NEW: {\"action\":\"NEW\",\"reason\":\"...\"}\n\nCRITICAL: mergedSummary must use the SAME language as the input. Output ONLY the JSON object.";
15
+ export interface DedupResult {
16
+ action: "DUPLICATE" | "UPDATE" | "NEW";
17
+ targetIndex?: number;
18
+ reason: string;
19
+ mergedSummary?: string;
20
+ }
21
+ export declare function judgeDedupOpenAI(newSummary: string, candidates: Array<{
22
+ index: number;
23
+ summary: string;
24
+ chunkId: string;
25
+ }>, cfg: SummarizerConfig, log: Logger): Promise<DedupResult>;
26
+ export declare function parseDedupResult(raw: string, log: Logger): DedupResult;
3
27
  //# sourceMappingURL=openai.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/ingest/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI5D,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,CAgCjB"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/ingest/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAyC5D,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,CA+BjB;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,CAgCjB;AAeD,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,OAAO,CAAC,CAmClB;AAwBD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EACnE,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,YAAY,CAAC,CAoCvB;AAqBD,eAAO,MAAM,kBAAkB,o/BAc+D,CAAC;AAE/F,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,KAAK,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EACtE,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,WAAW,CAAC,CAoCtB;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAiBtE"}
@@ -1,7 +1,78 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEDUP_JUDGE_PROMPT = void 0;
4
+ exports.summarizeTaskOpenAI = summarizeTaskOpenAI;
3
5
  exports.summarizeOpenAI = summarizeOpenAI;
4
- const SYSTEM_PROMPT = `Summarize the text in ONE concise sentence (max 60 tokens). Preserve exact names, commands, error codes. No bullet points, no preamble — output only the sentence.`;
6
+ exports.judgeNewTopicOpenAI = judgeNewTopicOpenAI;
7
+ exports.filterRelevantOpenAI = filterRelevantOpenAI;
8
+ exports.judgeDedupOpenAI = judgeDedupOpenAI;
9
+ exports.parseDedupResult = parseDedupResult;
10
+ const SYSTEM_PROMPT = `Summarize the text in ONE concise sentence (max 120 characters). IMPORTANT: Use the SAME language as the input text — if the input is Chinese, write Chinese; if English, write English. Preserve exact names, commands, error codes. No bullet points, no preamble — output only the sentence.`;
11
+ const TASK_SUMMARY_PROMPT = `You create a DETAILED task summary from a multi-turn conversation. This summary will be the ONLY record of this conversation, so it must preserve ALL important information.
12
+
13
+ CRITICAL LANGUAGE RULE: You MUST write in the SAME language as the user's messages. Chinese input → Chinese output. English input → English output. NEVER mix languages.
14
+
15
+ Output EXACTLY this structure:
16
+
17
+ 📌 Title
18
+ A short, descriptive title (10-30 characters). Like a chat group name.
19
+
20
+ 🎯 Goal
21
+ One sentence: what the user wanted to accomplish.
22
+
23
+ 📋 Key Steps
24
+ - Describe each meaningful step in detail
25
+ - Include the ACTUAL content produced: code snippets, commands, config blocks, formulas, key paragraphs
26
+ - For code: include the function signature and core logic (up to ~30 lines per block), use fenced code blocks
27
+ - For configs: include the actual config values and structure
28
+ - For lists/instructions: include the actual items, not just "provided a list"
29
+ - Merge only truly trivial back-and-forth (like "ok" / "sure")
30
+ - Do NOT over-summarize: "provided a function" is BAD; show the actual function
31
+
32
+ ✅ Result
33
+ What was the final outcome? Include the final version of any code/config/content produced.
34
+
35
+ 💡 Key Details
36
+ - Decisions made, trade-offs discussed, caveats noted, alternative approaches mentioned
37
+ - Specific values: numbers, versions, thresholds, URLs, file paths, model names
38
+ - Omit this section only if there truly are no noteworthy details
39
+
40
+ RULES:
41
+ - This summary is a KNOWLEDGE BASE ENTRY, not a brief note. Be thorough.
42
+ - PRESERVE verbatim: code, commands, URLs, file paths, error messages, config values, version numbers, names, amounts
43
+ - DISCARD only: greetings, filler, the assistant explaining what it will do before doing it
44
+ - Replace secrets (API keys, tokens, passwords) with [REDACTED]
45
+ - Target length: 30-50% of the original conversation length. Longer conversations need longer summaries.
46
+ - Output summary only, no preamble.`;
47
+ async function summarizeTaskOpenAI(text, cfg, log) {
48
+ const endpoint = normalizeChatEndpoint(cfg.endpoint ?? "https://api.openai.com/v1/chat/completions");
49
+ const model = cfg.model ?? "gpt-4o-mini";
50
+ const headers = {
51
+ "Content-Type": "application/json",
52
+ Authorization: `Bearer ${cfg.apiKey}`,
53
+ ...cfg.headers,
54
+ };
55
+ const resp = await fetch(endpoint, {
56
+ method: "POST",
57
+ headers,
58
+ body: JSON.stringify({
59
+ model,
60
+ temperature: cfg.temperature ?? 0.1,
61
+ max_tokens: 4096,
62
+ messages: [
63
+ { role: "system", content: TASK_SUMMARY_PROMPT },
64
+ { role: "user", content: text },
65
+ ],
66
+ }),
67
+ signal: AbortSignal.timeout(cfg.timeoutMs ?? 60_000),
68
+ });
69
+ if (!resp.ok) {
70
+ const body = await resp.text();
71
+ throw new Error(`OpenAI task-summarize failed (${resp.status}): ${body}`);
72
+ }
73
+ const json = (await resp.json());
74
+ return json.choices[0]?.message?.content?.trim() ?? "";
75
+ }
5
76
  async function summarizeOpenAI(text, cfg, log) {
6
77
  const endpoint = normalizeChatEndpoint(cfg.endpoint ?? "https://api.openai.com/v1/chat/completions");
7
78
  const model = cfg.model ?? "gpt-4o-mini";
@@ -30,6 +101,189 @@ async function summarizeOpenAI(text, cfg, log) {
30
101
  const json = (await resp.json());
31
102
  return json.choices[0]?.message?.content?.trim() ?? "";
32
103
  }
104
+ const TOPIC_JUDGE_PROMPT = `You are a conversation topic boundary detector. Given a summary of the CURRENT conversation and a NEW user message, determine if the new message starts a DIFFERENT topic/task.
105
+
106
+ Answer ONLY "NEW" or "SAME".
107
+
108
+ Rules:
109
+ - "NEW" = the new message is about a completely different subject, project, or task
110
+ - "SAME" = the new message continues, follows up on, or is closely related to the current topic
111
+ - Follow-up questions, clarifications, refinements, bug fixes, or next steps on the same task = SAME
112
+ - Greetings or meta-questions like "你好" or "谢谢" without new substance = SAME
113
+ - A clearly unrelated request (e.g., current topic is deployment, new message asks about cooking) = NEW
114
+
115
+ Output exactly one word: NEW or SAME`;
116
+ async function judgeNewTopicOpenAI(currentContext, newMessage, cfg, log) {
117
+ const endpoint = normalizeChatEndpoint(cfg.endpoint ?? "https://api.openai.com/v1/chat/completions");
118
+ const model = cfg.model ?? "gpt-4o-mini";
119
+ const headers = {
120
+ "Content-Type": "application/json",
121
+ Authorization: `Bearer ${cfg.apiKey}`,
122
+ ...cfg.headers,
123
+ };
124
+ const userContent = `CURRENT CONVERSATION SUMMARY:\n${currentContext}\n\nNEW USER MESSAGE:\n${newMessage}`;
125
+ const resp = await fetch(endpoint, {
126
+ method: "POST",
127
+ headers,
128
+ body: JSON.stringify({
129
+ model,
130
+ temperature: 0,
131
+ max_tokens: 10,
132
+ messages: [
133
+ { role: "system", content: TOPIC_JUDGE_PROMPT },
134
+ { role: "user", content: userContent },
135
+ ],
136
+ }),
137
+ signal: AbortSignal.timeout(cfg.timeoutMs ?? 15_000),
138
+ });
139
+ if (!resp.ok) {
140
+ const body = await resp.text();
141
+ throw new Error(`OpenAI topic-judge failed (${resp.status}): ${body}`);
142
+ }
143
+ const json = (await resp.json());
144
+ const answer = json.choices[0]?.message?.content?.trim().toUpperCase() ?? "";
145
+ log.debug(`Topic judge result: "${answer}"`);
146
+ return answer.startsWith("NEW");
147
+ }
148
+ const FILTER_RELEVANT_PROMPT = `You are a memory relevance judge. Given a user's QUERY and a list of CANDIDATE memory summaries, do two things:
149
+
150
+ 1. Select ALL candidates that could be useful for answering the query. When in doubt, INCLUDE the candidate.
151
+ - For questions about lists, history, or "what/where/who" across multiple items (e.g. "which companies did I work at"), include ALL matching items — do NOT stop at the first match.
152
+ - For factual lookups (e.g. "what is the SSH port"), a single direct answer is enough.
153
+ 2. Judge whether the selected memories are SUFFICIENT to fully answer the query WITHOUT fetching additional context.
154
+
155
+ IMPORTANT for "sufficient" judgment:
156
+ - sufficient=true ONLY when the memories contain a concrete ANSWER, fact, decision, or actionable information that directly addresses the query.
157
+ - sufficient=false when:
158
+ - The memories only repeat the same question the user asked before (echo, not answer).
159
+ - The memories show related topics but lack the specific detail needed.
160
+ - The memories contain partial information that would benefit from full task context, timeline, or related skills.
161
+
162
+ Output a JSON object with exactly two fields:
163
+ {"relevant":[1,3,5],"sufficient":true}
164
+
165
+ - "relevant": array of candidate numbers that are useful. Empty array [] if none are relevant.
166
+ - "sufficient": true ONLY if the memories contain a direct answer; false otherwise.
167
+
168
+ Output ONLY the JSON object, nothing else.`;
169
+ async function filterRelevantOpenAI(query, candidates, cfg, log) {
170
+ const endpoint = normalizeChatEndpoint(cfg.endpoint ?? "https://api.openai.com/v1/chat/completions");
171
+ const model = cfg.model ?? "gpt-4o-mini";
172
+ const headers = {
173
+ "Content-Type": "application/json",
174
+ Authorization: `Bearer ${cfg.apiKey}`,
175
+ ...cfg.headers,
176
+ };
177
+ const candidateText = candidates
178
+ .map((c) => `${c.index}. [${c.role}] ${c.summary}`)
179
+ .join("\n");
180
+ const resp = await fetch(endpoint, {
181
+ method: "POST",
182
+ headers,
183
+ body: JSON.stringify({
184
+ model,
185
+ temperature: 0,
186
+ max_tokens: 200,
187
+ messages: [
188
+ { role: "system", content: FILTER_RELEVANT_PROMPT },
189
+ { role: "user", content: `QUERY: ${query}\n\nCANDIDATES:\n${candidateText}` },
190
+ ],
191
+ }),
192
+ signal: AbortSignal.timeout(cfg.timeoutMs ?? 15_000),
193
+ });
194
+ if (!resp.ok) {
195
+ const body = await resp.text();
196
+ throw new Error(`OpenAI filter-relevant failed (${resp.status}): ${body}`);
197
+ }
198
+ const json = (await resp.json());
199
+ const raw = json.choices[0]?.message?.content?.trim() ?? "{}";
200
+ return parseFilterResult(raw, log);
201
+ }
202
+ function parseFilterResult(raw, log) {
203
+ try {
204
+ const match = raw.match(/\{[\s\S]*\}/);
205
+ if (match) {
206
+ const obj = JSON.parse(match[0]);
207
+ if (obj && Array.isArray(obj.relevant)) {
208
+ return {
209
+ relevant: obj.relevant.filter((n) => typeof n === "number"),
210
+ sufficient: obj.sufficient === true,
211
+ };
212
+ }
213
+ }
214
+ }
215
+ catch { }
216
+ log.warn(`filterRelevant: failed to parse LLM output: "${raw}", fallback to all+insufficient`);
217
+ return { relevant: [], sufficient: false };
218
+ }
219
+ // ─── Smart Dedup: judge whether new memory is DUPLICATE / UPDATE / NEW ───
220
+ exports.DEDUP_JUDGE_PROMPT = `You are a memory deduplication system. Given a NEW memory summary and several EXISTING memory summaries, determine the relationship.
221
+
222
+ For each EXISTING memory, the NEW memory is either:
223
+ - "DUPLICATE": NEW is fully covered by an EXISTING memory — no new information at all
224
+ - "UPDATE": NEW contains information that supplements or updates an EXISTING memory (new data, status change, additional detail)
225
+ - "NEW": NEW is a different topic/event despite surface similarity
226
+
227
+ Pick the BEST match among all candidates. If none match well, choose "NEW".
228
+
229
+ Output a single JSON object:
230
+ - If DUPLICATE: {"action":"DUPLICATE","targetIndex":2,"reason":"..."}
231
+ - If UPDATE: {"action":"UPDATE","targetIndex":3,"reason":"...","mergedSummary":"a combined summary preserving all info from both old and new, same language as input"}
232
+ - If NEW: {"action":"NEW","reason":"..."}
233
+
234
+ CRITICAL: mergedSummary must use the SAME language as the input. Output ONLY the JSON object.`;
235
+ async function judgeDedupOpenAI(newSummary, candidates, cfg, log) {
236
+ const endpoint = normalizeChatEndpoint(cfg.endpoint ?? "https://api.openai.com/v1/chat/completions");
237
+ const model = cfg.model ?? "gpt-4o-mini";
238
+ const headers = {
239
+ "Content-Type": "application/json",
240
+ Authorization: `Bearer ${cfg.apiKey}`,
241
+ ...cfg.headers,
242
+ };
243
+ const candidateText = candidates
244
+ .map((c) => `${c.index}. ${c.summary}`)
245
+ .join("\n");
246
+ const resp = await fetch(endpoint, {
247
+ method: "POST",
248
+ headers,
249
+ body: JSON.stringify({
250
+ model,
251
+ temperature: 0,
252
+ max_tokens: 300,
253
+ messages: [
254
+ { role: "system", content: exports.DEDUP_JUDGE_PROMPT },
255
+ { role: "user", content: `NEW MEMORY:\n${newSummary}\n\nEXISTING MEMORIES:\n${candidateText}` },
256
+ ],
257
+ }),
258
+ signal: AbortSignal.timeout(cfg.timeoutMs ?? 15_000),
259
+ });
260
+ if (!resp.ok) {
261
+ const body = await resp.text();
262
+ throw new Error(`OpenAI dedup-judge failed (${resp.status}): ${body}`);
263
+ }
264
+ const json = (await resp.json());
265
+ const raw = json.choices[0]?.message?.content?.trim() ?? "{}";
266
+ return parseDedupResult(raw, log);
267
+ }
268
+ function parseDedupResult(raw, log) {
269
+ try {
270
+ const match = raw.match(/\{[\s\S]*\}/);
271
+ if (match) {
272
+ const obj = JSON.parse(match[0]);
273
+ if (obj && typeof obj.action === "string") {
274
+ return {
275
+ action: obj.action,
276
+ targetIndex: typeof obj.targetIndex === "number" ? obj.targetIndex : undefined,
277
+ reason: obj.reason || "",
278
+ mergedSummary: obj.mergedSummary || undefined,
279
+ };
280
+ }
281
+ }
282
+ }
283
+ catch { }
284
+ log.warn(`judgeDedup: failed to parse LLM output: "${raw}", fallback to NEW`);
285
+ return { action: "NEW", reason: "parse_failed" };
286
+ }
33
287
  function normalizeChatEndpoint(url) {
34
288
  const stripped = url.replace(/\/+$/, "");
35
289
  if (stripped.endsWith("/chat/completions"))
@@ -1 +1 @@
1
- {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/ingest/providers/openai.ts"],"names":[],"mappings":";;AAIA,0CAoCC;AAtCD,MAAM,aAAa,GAAG,oKAAoK,CAAC;AAEpL,KAAK,UAAU,eAAe,CACnC,IAAY,EACZ,GAAqB,EACrB,GAAW;IAEX,MAAM,QAAQ,GAAG,qBAAqB,CAAC,GAAG,CAAC,QAAQ,IAAI,4CAA4C,CAAC,CAAC;IACrG,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,aAAa,CAAC;IACzC,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,aAAa,EAAE,UAAU,GAAG,CAAC,MAAM,EAAE;QACrC,GAAG,GAAG,CAAC,OAAO;KACf,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QACjC,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK;YACL,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,CAAC;YACjC,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE;gBAC1C,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;aAChC;SACF,CAAC;QACF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC;KACrD,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAE9B,CAAC;IACF,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACzD,CAAC;AAED,SAAS,qBAAqB,CAAC,GAAW;IACxC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzC,IAAI,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC5D,IAAI,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,QAAQ,CAAC;IACvD,OAAO,GAAG,QAAQ,mBAAmB,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../src/ingest/providers/openai.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { SqliteStore } from "../storage/sqlite";
2
+ import type { PluginContext, Task } from "../types";
3
+ /**
4
+ * Asynchronous task-level processor.
5
+ *
6
+ * After each ingestion batch, checks whether the current conversation
7
+ * constitutes a "new task" compared to the previous one. If so:
8
+ * 1. Finalizes the previous task (generates a detailed summary).
9
+ * 2. Creates a new active task for incoming chunks.
10
+ *
11
+ * Task boundary detection:
12
+ * - Session change → always new task
13
+ * - Time gap > 2h → always new task
14
+ * - LLM judges whether new user message starts a different topic
15
+ */
16
+ export declare class TaskProcessor {
17
+ private store;
18
+ private ctx;
19
+ private summarizer;
20
+ private processing;
21
+ private onTaskCompletedCallback?;
22
+ constructor(store: SqliteStore, ctx: PluginContext);
23
+ onTaskCompleted(cb: (task: Task) => void): void;
24
+ /**
25
+ * Called after new chunks are ingested.
26
+ * Determines if a new task boundary was crossed and handles transition.
27
+ */
28
+ onChunksIngested(sessionKey: string, latestTimestamp: number): Promise<void>;
29
+ private detectAndProcess;
30
+ private isTaskBoundary;
31
+ /**
32
+ * Build a concise context string from existing task chunks for the LLM topic judge.
33
+ * Takes recent user/assistant summaries to keep token usage low.
34
+ */
35
+ private buildContextSummary;
36
+ private createNewTask;
37
+ private assignUnassignedChunks;
38
+ finalizeTask(task: Task): Promise<void>;
39
+ /**
40
+ * Determine if a task is too trivial to warrant an LLM summary call.
41
+ * Returns a skip reason string, or null if summary should proceed.
42
+ *
43
+ * Skip conditions (any one triggers skip):
44
+ * 1. Total chunks < 4 — too few messages to form a meaningful task
45
+ * 2. Real conversation turns < 2 — no back-and-forth dialogue
46
+ * 3. No user messages — purely system/tool generated, no user intent
47
+ * 4. Total content < 200 chars — not enough substance
48
+ * 5. User content is trivial/test data — "hello", "test", "ok" etc.
49
+ * 6. All messages are tool results — automated output, no conversation
50
+ * 7. High content repetition — user repeated the same thing (debug loops)
51
+ */
52
+ private shouldSkipSummary;
53
+ private looksLikeTrivialContent;
54
+ private buildConversationText;
55
+ /**
56
+ * Extract the LLM-generated title from the summary output.
57
+ * The LLM is prompted to output "📌 Title\n<title text>" as the first section.
58
+ * Returns the title and the remaining body (with the title section stripped).
59
+ */
60
+ private parseTitleFromSummary;
61
+ private extractTitle;
62
+ private humanReadableSkipReason;
63
+ private fallbackSummary;
64
+ }
65
+ //# sourceMappingURL=task-processor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-processor.d.ts","sourceRoot":"","sources":["../../src/ingest/task-processor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,IAAI,EAAS,MAAM,UAAU,CAAC;AAc3D;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAa;IAMtB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG;IANb,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,uBAAuB,CAAC,CAAuB;gBAG7C,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,aAAa;IAK5B,eAAe,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,IAAI;IAI/C;;;OAGG;IACG,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAgBpE,gBAAgB;YA+BhB,cAAc;IA2C5B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;YAUb,aAAa;IAiB3B,OAAO,CAAC,sBAAsB;IAUxB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAsD7C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,iBAAiB;IA6DzB,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,qBAAqB;IAS7B;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,uBAAuB;IA4B/B,OAAO,CAAC,eAAe;CAcxB"}