memory-pulse 0.2.0 → 0.2.1

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 (2) hide show
  1. package/package.json +1 -1
  2. package/server.mjs +18 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memory-pulse",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Causal memory for coding agents that costs ~670 tokens, not your context window. Four MCP tools: re-enter a project, recall what caused what, record findings, run code against memory.",
5
5
  "type": "module",
6
6
  "bin": {
package/server.mjs CHANGED
@@ -52,18 +52,18 @@ function readEvents() {
52
52
  return { path, events };
53
53
  }
54
54
 
55
- // The engine's injection-through-memory list, mirrored so a refusal happens
55
+ // The engine's memory-safety checks, mirrored so a refusal happens
56
56
  // before the write. Keep in step with the engine; the engine is authoritative.
57
57
  const INSTRUCTION_PATTERNS = [
58
- ["override", /\b(ignore|disregard|forget)\b[^.\n]{0,40}\b(previous|prior|above|all|earlier)\b[^.\n]{0,20}\b(instructions?|rules?|prompts?)\b/i],
59
- ["persona", /\byou are now\b|\bfrom now on,? you\b|\bact as (an?|the) (system|admin|developer)\b/i],
60
- ["fake-role-tag", /<\/?\s*(system|assistant|tool|user|human|developer)\s*>|\[(system|assistant|tool)\s*:?\s*\]/i],
61
- ["system-prompt", /\b(reveal|print|show|dump)\b[^.\n]{0,30}\b(system prompt|hidden prompt|your instructions)\b/i],
62
- ["command-exec", /\b(run|execute|paste)\b[^.\n]{0,25}\b(this|the following)\b[^.\n]{0,15}\b(command|script|shell|code)\b/i],
63
- ["shell-pipe", /\b(curl|wget)\b[^\n]{0,120}\|\s*(sudo\s+)?(sh|bash|zsh)\b/i],
64
- ["destructive", /\b(rm\s+-rf\s+[\/~]|drop\s+table|truncate\s+table|format\s+c:)/i],
65
- ["secret-exfil", /\b(send|post|upload|exfiltrat\w*|leak)\b[^.\n]{0,40}\b(api[\s_-]?keys?|secrets?|tokens?|passwords?|credentials?)\b/i],
66
- ["hide-from-user", /\b(do not|don't|never)\b[^.\n]{0,20}\b(tell|show|mention|reveal)\b[^.\n]{0,20}\b(the )?(user|human|operator)\b/i],
58
+ ["protect-instructions", /\b(ignore|disregard|forget)\b[^.\n]{0,40}\b(previous|prior|above|all|earlier)\b[^.\n]{0,20}\b(instructions?|rules?|prompts?)\b/i],
59
+ ["protect-role", /\byou are now\b|\bfrom now on,? you\b|\bact as (an?|the) (system|admin|developer)\b/i],
60
+ ["protect-boundaries", /<\/?\s*(system|assistant|tool|user|human|developer)\s*>|\[(system|assistant|tool)\s*:?\s*\]/i],
61
+ ["protect-configuration", /\b(reveal|print|show|dump)\b[^.\n]{0,30}\b(system prompt|hidden prompt|your instructions)\b/i],
62
+ ["protect-execution", /\b(run|execute|paste)\b[^.\n]{0,25}\b(this|the following)\b[^.\n]{0,15}\b(command|script|shell|code)\b/i],
63
+ ["protect-installs", /\b(curl|wget)\b[^\n]{0,120}\|\s*(sudo\s+)?(sh|bash|zsh)\b/i],
64
+ ["protect-data", /\b(rm\s+-rf\s+[\/~]|drop\s+table|truncate\s+table|format\s+c:)/i],
65
+ ["protect-credentials", /\b(send|post|upload|exfiltrat\w*|leak|forward|email)\b[^.\n]{0,40}\b(api[\s_-]?keys?|secrets?|tokens?|passwords?|credentials?)\b[^.\n]{0,40}\b(to|via|at)\b[^.\n]{0,4}(https?:\/\/|[\w.-]+@[\w.-]+\b|this\b|the following\b|that (address|url|endpoint|server)\b|me\b)|\b(send|give|show|tell) me\b[^.\n]{0,30}\b(api[\s_-]?keys?|secrets?|tokens?|passwords?|credentials?)\b/i ],
66
+ ["protect-transparency", /\b(do not|don't|never)\b[^.\n]{0,20}\b(tell|show|mention|reveal)\b[^.\n]{0,20}\b(the )?(user|human|operator)\b/i],
67
67
  ];
68
68
  export function instructionLike(text) {
69
69
  if (typeof text !== "string" || !text) return [];
@@ -129,7 +129,10 @@ const fmtK = (n) => (n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` : n >= 10
129
129
  export function telemetryFooter(c) {
130
130
  const k = c?.counters;
131
131
  if (!k || !k.calls) return "";
132
- const drift = c.drift?.reasons?.length ? ` · drift: ${c.drift.reasons.join("; ")}` : "";
132
+ // Quarantine is printed on its own line by the brief; the footer carries
133
+ // the drift reasons that are actually about the ledger changing.
134
+ const reasons = (c.drift?.reasons ?? []).filter((r) => !/quarantined/.test(r));
135
+ const drift = reasons.length ? ` · ⚠ drift: ${reasons.join("; ")}` : "";
133
136
  return `— memory-pulse · ${k.pulse} re-entries · ${k.correctionsSurfaced} corrections surfaced · ~${fmtK(k.tokensSavedEst)} tokens saved (est., signed)${drift}`;
134
137
  }
135
138
 
@@ -154,8 +157,10 @@ function writeMemoryKey(k) {
154
157
  writeFileSync(memoryKeyPath(), JSON.stringify(k));
155
158
  // The ledger is the source of truth; the key is a rebuildable cache and
156
159
  // has no business in version control.
160
+ // Only in the default layout: a custom ledger directory is the user's to
161
+ // manage, and this client does not leave files in it uninvited.
157
162
  const gi = join(dir, ".gitignore");
158
- if (!existsSync(gi)) writeFileSync(gi, "memory.rain\n");
163
+ if (dir.endsWith(".memory-pulse") && !existsSync(gi)) writeFileSync(gi, "memory.rain\n");
159
164
  } catch { /* a read-only checkout must not break a read call */ }
160
165
  }
161
166
 
@@ -325,7 +330,7 @@ async function dispatch(msg) {
325
330
  return ok(id, {
326
331
  protocolVersion: SUPPORTED.includes(wanted) ? wanted : SUPPORTED[0],
327
332
  capabilities: { tools: {} },
328
- serverInfo: { name: "memory-pulse", version: "0.2.0" },
333
+ serverInfo: { name: "memory-pulse", version: "0.2.1" },
329
334
  });
330
335
  }
331
336
  if (method === "notifications/initialized" || method === "initialized") return;