memento-mcp 0.3.6 → 0.3.8
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/package.json +1 -1
- package/scripts/memento-stop-recall.sh +17 -25
- package/src/index.js +66 -0
- package/src/storage/hosted.js +13 -0
package/package.json
CHANGED
|
@@ -42,7 +42,7 @@ print('true' if hook.get('enabled', True) else 'false')
|
|
|
42
42
|
MEMENTO_WORKSPACE="${MEMENTO_WORKSPACE:-$(echo "$CONFIG_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('workspace',''))" 2>/dev/null)}"
|
|
43
43
|
|
|
44
44
|
RECALL_LIMIT=$(echo "$CONFIG_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('hooks',{}).get('$HOOK_NAME',{}).get('limit',5))" 2>/dev/null)
|
|
45
|
-
RECALL_MAX_LENGTH=$(echo "$CONFIG_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('hooks',{}).get('$HOOK_NAME',{}).get('maxLength',
|
|
45
|
+
RECALL_MAX_LENGTH=$(echo "$CONFIG_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('hooks',{}).get('$HOOK_NAME',{}).get('maxLength',120))" 2>/dev/null)
|
|
46
46
|
fi
|
|
47
47
|
# --- End config block ---
|
|
48
48
|
|
|
@@ -92,34 +92,24 @@ try:
|
|
|
92
92
|
data = json.load(sys.stdin)
|
|
93
93
|
lines = []
|
|
94
94
|
count = 0
|
|
95
|
+
abbrev = {'instruction':'instr','observation':'obs','decision':'dec','preference':'pref'}
|
|
95
96
|
|
|
96
97
|
memories = data.get('memories', {}).get('matches', [])
|
|
97
98
|
if memories:
|
|
98
99
|
for m in memories[:${RECALL_LIMIT:-5}]:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
content
|
|
102
|
-
score = m.get('score', '?')
|
|
103
|
-
date_str = f' {m[\"created_at\"][:10]}' if m.get('created_at') else ''
|
|
104
|
-
lines.append(f' {m[\"id\"]} ({m[\"type\"]}, {score}{date_str}){tag_str} — {content}')
|
|
100
|
+
content = m['content'][:${RECALL_MAX_LENGTH:-120}]
|
|
101
|
+
t = abbrev.get(m['type'], m['type'])
|
|
102
|
+
lines.append(f' · {content} [{m[\"id\"]} {t}]')
|
|
105
103
|
count += 1
|
|
106
104
|
|
|
107
105
|
skip_matches = data.get('skip_matches', [])
|
|
108
106
|
if skip_matches:
|
|
109
|
-
lines.append('')
|
|
110
|
-
lines.append('SKIP LIST WARNINGS:')
|
|
111
107
|
for s in skip_matches:
|
|
112
|
-
lines.append(f'
|
|
108
|
+
lines.append(f' Skip: {s[\"item\"]} — {s[\"reason\"]} (expires {s[\"expires\"]})')
|
|
113
109
|
|
|
114
|
-
# Auto-extracted memories
|
|
115
110
|
extracted = data.get('extracted', [])
|
|
116
111
|
if extracted:
|
|
117
|
-
lines.append('')
|
|
118
|
-
lines.append('MEMORIES STORED:')
|
|
119
|
-
for e in extracted:
|
|
120
|
-
etags = [t for t in e.get('tags', []) if t != 'source:auto-extract']
|
|
121
|
-
etag_str = f' [{\", \".join(etags)}]' if etags else ''
|
|
122
|
-
lines.append(f' {e[\"id\"]} ({e.get(\"type\", \"observation\")}){etag_str} — {e[\"content\"]}')
|
|
112
|
+
lines.append(f' Stored: {len(extracted)} new memories')
|
|
123
113
|
|
|
124
114
|
# Output: recall_count \t extracted_count \t detail
|
|
125
115
|
extracted_count = len(extracted)
|
|
@@ -143,9 +133,11 @@ if [ -z "$SAAS_COUNT" ] || [ "$SAAS_COUNT" = "0" ]; then
|
|
|
143
133
|
"$TOAST" memento "✓ ${EXTRACTED_COUNT} memories stored" &>/dev/null
|
|
144
134
|
else
|
|
145
135
|
"$TOAST" memento "✓ No memories matched" &>/dev/null
|
|
146
|
-
exit 0
|
|
147
136
|
fi
|
|
148
|
-
|
|
137
|
+
exit 0
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if [ "$SAAS_COUNT" != "0" ]; then
|
|
149
141
|
if [ -n "$EXTRACTED_COUNT" ] && [ "$EXTRACTED_COUNT" != "0" ]; then
|
|
150
142
|
"$TOAST" memento "✓ ${SAAS_COUNT} recalled, ${EXTRACTED_COUNT} stored" &>/dev/null
|
|
151
143
|
else
|
|
@@ -154,16 +146,16 @@ else
|
|
|
154
146
|
fi
|
|
155
147
|
|
|
156
148
|
# Build summary line
|
|
157
|
-
SUMMARY="
|
|
149
|
+
SUMMARY="Recall (${SAAS_COUNT})"
|
|
158
150
|
if [ -n "$EXTRACTED_COUNT" ] && [ "$EXTRACTED_COUNT" != "0" ]; then
|
|
159
|
-
SUMMARY="${
|
|
151
|
+
SUMMARY="Recall (${SAAS_COUNT}), stored ${EXTRACTED_COUNT}"
|
|
160
152
|
fi
|
|
161
153
|
|
|
162
|
-
# Block the Stop so
|
|
163
|
-
|
|
154
|
+
# Block the Stop so recalled memories are injected into context.
|
|
155
|
+
# If no memories are relevant, respond with <...> to signal active silence.
|
|
156
|
+
REASON="${SUMMARY}:
|
|
164
157
|
${SAAS_DETAIL}
|
|
165
|
-
|
|
166
|
-
You have absorbed these memories into context. If any recalled memory is stale, wrong, or overlaps with others — update, delete, or consolidate it now. Otherwise continue naturally."
|
|
158
|
+
Stale or wrong? Update or consolidate."
|
|
167
159
|
|
|
168
160
|
python3 -c "
|
|
169
161
|
import json, sys
|
package/src/index.js
CHANGED
|
@@ -433,6 +433,72 @@ This is reconsolidation — like how the brain rebuilds memories on recall. Freq
|
|
|
433
433
|
}
|
|
434
434
|
);
|
|
435
435
|
|
|
436
|
+
// ---------------------------------------------------------------------------
|
|
437
|
+
// Tool: memento_extract
|
|
438
|
+
// ---------------------------------------------------------------------------
|
|
439
|
+
|
|
440
|
+
server.tool(
|
|
441
|
+
"memento_extract",
|
|
442
|
+
`Extract memories from a conversation transcript or the conversation buffer using LLM. Use this to manually trigger memory extraction — e.g., to flush the buffer before ending a session, or to extract from a pasted transcript.
|
|
443
|
+
|
|
444
|
+
Two modes:
|
|
445
|
+
- "distill" (default): Full extraction — facts, decisions, instructions, observations. Up to 20 memories.
|
|
446
|
+
- "seeds": Relational/experiential texture — preferences, emotions, sensory details. Up to 3 memories.
|
|
447
|
+
|
|
448
|
+
If no transcript is provided, reads from the conversation buffer (auto-populated by the context hook). Buffer is cleared after extraction unless keep_buffer is true.`,
|
|
449
|
+
{
|
|
450
|
+
transcript: z.string().optional().describe("Raw conversation text. If omitted, reads from the conversation buffer."),
|
|
451
|
+
mode: z.enum(["distill", "seeds"]).optional().describe("Extraction mode (default: distill)"),
|
|
452
|
+
max_memories: z.number().optional().describe("Override max memories to extract"),
|
|
453
|
+
keep_buffer: z.boolean().optional().describe("If true, don't clear the buffer after reading it"),
|
|
454
|
+
},
|
|
455
|
+
async ({ transcript, mode, max_memories, keep_buffer }) => {
|
|
456
|
+
const result = await storage.extractMemories(null, {
|
|
457
|
+
transcript,
|
|
458
|
+
mode,
|
|
459
|
+
max_memories,
|
|
460
|
+
keep_buffer,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
if (result.error && !result.stored) {
|
|
464
|
+
return {
|
|
465
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
466
|
+
isError: true,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (result.error) {
|
|
471
|
+
// Partial success — extraction ran but had issues
|
|
472
|
+
const summary = result.stored?.length > 0
|
|
473
|
+
? result.stored.map((m) => `- **${m.id}** (${m.type}): ${m.content}`).join("\n")
|
|
474
|
+
: "No memories extracted.";
|
|
475
|
+
return {
|
|
476
|
+
content: [{
|
|
477
|
+
type: "text",
|
|
478
|
+
text: `Extraction completed with warning: ${result.error}\n\n${summary}`,
|
|
479
|
+
}],
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (!result.stored || result.stored.length === 0) {
|
|
484
|
+
return {
|
|
485
|
+
content: [{
|
|
486
|
+
type: "text",
|
|
487
|
+
text: `No novel memories found (mode: ${result.mode || mode || "distill"}, source: ${result.source || "unknown"}).`,
|
|
488
|
+
}],
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const summary = result.stored.map((m) => `- **${m.id}** (${m.type}): ${m.content}`).join("\n");
|
|
493
|
+
return {
|
|
494
|
+
content: [{
|
|
495
|
+
type: "text",
|
|
496
|
+
text: `Extracted ${result.count} memor${result.count === 1 ? "y" : "ies"} (mode: ${result.mode}, source: ${result.source}):\n\n${summary}`,
|
|
497
|
+
}],
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
);
|
|
501
|
+
|
|
436
502
|
// ---------------------------------------------------------------------------
|
|
437
503
|
// Tool: memento_skip_add
|
|
438
504
|
// ---------------------------------------------------------------------------
|
package/src/storage/hosted.js
CHANGED
|
@@ -261,6 +261,19 @@ export class HostedStorageAdapter extends StorageInterface {
|
|
|
261
261
|
return res;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
async extractMemories(_wsPath, { transcript, mode, max_memories, source_tag, keep_buffer }) {
|
|
265
|
+
const body = {};
|
|
266
|
+
if (transcript) body.transcript = transcript;
|
|
267
|
+
if (mode) body.mode = mode;
|
|
268
|
+
if (max_memories) body.max_memories = max_memories;
|
|
269
|
+
if (source_tag) body.source_tag = source_tag;
|
|
270
|
+
if (keep_buffer !== undefined) body.keep_buffer = keep_buffer;
|
|
271
|
+
|
|
272
|
+
const res = await this._fetchJson("POST", "/v1/extract", body);
|
|
273
|
+
if (res.error && !res.stored) return { error: res.error };
|
|
274
|
+
return res;
|
|
275
|
+
}
|
|
276
|
+
|
|
264
277
|
async getIdentity(_wsPath) {
|
|
265
278
|
const { text, isError } = await this._fetch("GET", "/v1/identity");
|
|
266
279
|
if (isError) return { error: text };
|