linksee-memory 0.0.8 → 0.0.10

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.
@@ -37,13 +37,23 @@ const FAILURE_PATTERNS = [
37
37
  // opinions into protected caveats. Tightened to imperative/prohibitive forms
38
38
  // only. Loses some recall, but precision matters more for the "never forget"
39
39
  // layer.
40
+ // Imperative negations: `[ぁ-ん一-龯]ないで` reads as "don't do X" only when
41
+ // what follows is NOT part of a polite negation (「ないです」 = "it is not"),
42
+ // a probability form (「ないでしょう」), a request (「ないでほしい」), or a
43
+ // continuation (「ないでいる」). The negative lookahead `(?![すしいほ])`
44
+ // catches all four cases in one filter (す: です, し: しょう, い: いる/いない,
45
+ // ほ: ほしい). 「ないでください」 still passes because "く" is not excluded.
40
46
  const CAVEAT_PATTERNS = [
41
- // Imperative negations: any verb-stem + ないで is a command "don't X".
42
- // The 「[ぁ-ん一-龯]ないで」 clause catches 触らないで / 消さないで / 使わないで /
43
- // 書かないで etc. while `(?!いる|いない|ほし)` excludes descriptive forms
44
- // like 「やらないでいる」 or 「やらないでほしい」 (state / request).
45
- /気をつけて|注意して|[!!]注意[!!]|避けて(?!いる|いない)|[ぁ-ん一-龯]ないで(?!いる|いない|ほし)|やめて(?!おく|ほし)|禁止|ダメだ|危険/,
46
- /\b(?:don'?t|do\s+not)\s+(?:do|use|run|call|forget|try|send|share|commit|push|paste|edit)\b|\bnever\s+(?:do|use|call|share|commit|paste|run|push|edit)\b|\bavoid(?:ing)?\b|\bwatch\s+out\b/i,
47
+ // `ダメだ(?!った|ろうと|と思)` excludes 過去形 (ダメだった = "it failed") and
48
+ // speculation (ダメだろうと) which are descriptive, not prescriptive.
49
+ // `ないで` only counts as imperative when it ends the clause. We accept:
50
+ // sentence terminators 。!!、 / particles ね・よ / whitespace / ください
51
+ // Anything else (e.g. も = concessive「〜ないでも」, 止まる・いる・ほしい etc.)
52
+ // is treated as descriptive and excluded.
53
+ /気をつけて|注意して|[!!]注意[!!]|避けて(?!いる|いない)|[ぁ-ん一-龯]ないで(?=[。!!、\s]|ください|ね[^い]|よ[^う]|$)|やめて(?!おく|ほし)|禁止|ダメだ(?!った|ろうと|と思)|危険[だです]/,
54
+ // English: require a concrete action after avoid/don't/never — a bare
55
+ // "Avoiding rebuild of unchanged files" in a Vercel log is not a caveat.
56
+ /\b(?:don'?t|do\s+not)\s+(?:do|use|run|call|forget|try|send|share|commit|push|paste|edit)\b|\bnever\s+(?:do|use|call|share|commit|paste|run|push|edit)\b|\bavoid\s+(?:using|running|calling|committing|pushing|sharing|pasting|editing|creating|modifying)\b|\bwatch\s+out\b/i,
47
57
  ];
48
58
  function matchesAny(text, patterns) {
49
59
  return patterns.some((p) => p.test(text));
@@ -118,6 +118,17 @@ export function isPastedExternalContent(text) {
118
118
  // Service status pages / logs
119
119
  if (/^(Service offline|Failed|Success|Deployment|Build|Logs?|Error:)/.test(t.slice(0, 80)))
120
120
  return true;
121
+ // CI/CD and cloud build logs are almost always pasted and contain substrings
122
+ // (Avoiding / Running / never / error) that false-trigger the caveat patterns.
123
+ // Detect by timestamp-prefixed first line (e.g. "17:40:13.469 Running build")
124
+ // or by dense occurrence of ISO-time prefixes.
125
+ if (/^\d{1,2}:\d{2}:\d{2}[.:]\d{2,3}\s/.test(t))
126
+ return true;
127
+ if ((t.match(/\d{1,2}:\d{2}:\d{2}[.:]\d{2,3}/g) || []).length >= 3)
128
+ return true;
129
+ // Deployment-success notice from Vercel / Netlify / etc. (pasted by user)
130
+ if (/^成功しました/.test(t) && /デプロイ|deploy/i.test(t.slice(0, 200)))
131
+ return true;
121
132
  // Extractor-internal duplicate (was generating same caveat per session)
122
133
  if (t === 'Review errors before repeating this workflow.')
123
134
  return true;
@@ -276,16 +276,36 @@ function handleRecall(args) {
276
276
  ok: true,
277
277
  count: top.length,
278
278
  search: searchMethod,
279
- memories: top.map((r) => ({
280
- id: r.id,
281
- entity: { name: r.entity_name, kind: r.entity_kind, momentum: r.momentum_score },
282
- layer: r.layer,
283
- content: r.content,
284
- importance: r.importance,
285
- heat: Number(r.heat_score.toFixed(1)),
286
- band: r.heat_band,
287
- composite: Number(r.composite_score.toFixed(3)),
288
- })),
279
+ memories: top.map((r) => {
280
+ // `content` is stored as JSON stringify-ed so we can enforce schema per
281
+ // layer, but agents receiving it via MCP then have to parse it again —
282
+ // extra tokens and a round-trip. Try to pre-parse; if the stored value
283
+ // isn't JSON (legacy plaintext memory), fall back to the raw string.
284
+ // content_raw preserves backward compatibility for callers who depend
285
+ // on the stringified form.
286
+ let parsedContent = r.content;
287
+ try {
288
+ parsedContent = JSON.parse(r.content);
289
+ }
290
+ catch {
291
+ // leave as string
292
+ }
293
+ return {
294
+ id: r.id,
295
+ entity: {
296
+ name: r.entity_name,
297
+ kind: r.entity_kind,
298
+ momentum: r.momentum_score,
299
+ },
300
+ layer: r.layer,
301
+ content: parsedContent,
302
+ content_raw: r.content,
303
+ importance: r.importance,
304
+ heat: Number(r.heat_score.toFixed(1)),
305
+ band: r.heat_band,
306
+ composite: Number(r.composite_score.toFixed(3)),
307
+ };
308
+ }),
289
309
  });
290
310
  }
291
311
  function handleForget(args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linksee-memory",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "mcpName": "io.github.michielinksee/linksee-memory",
5
5
  "description": "Local-first agent memory MCP — cross-agent brain with 6-layer structured memory + token-saving file diff cache",
6
6
  "type": "module",