memento-mcp 0.3.10 → 0.3.12
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/CHANGELOG.md +3 -0
- package/package.json +1 -1
- package/scripts/memento-precompact-distill.sh +1 -1
- package/scripts/memento-sessionstart-identity.sh +1 -1
- package/scripts/memento-stop-recall.sh +13 -33
- package/scripts/memento-userprompt-recall.sh +20 -51
- package/src/index.js +4 -6
- package/src/storage/hosted.js +1 -2
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version
|
|
|
15
15
|
### Changed
|
|
16
16
|
- `source:distill` tag renamed to `source:distill:llama-3.1-8b` to encode model provenance. The `claude-code` path tags memories `source:distill:claude-code`.
|
|
17
17
|
|
|
18
|
+
### Removed
|
|
19
|
+
- **Passive memory extraction (auto_extract)** — removed conversation buffer, auto-extraction from `/v1/context`, and buffer fallback from `/v1/extract`. Smart models know when to store via `memento_store`; passive extraction produced noisy, low-signal memories. The `memento_extract` tool and `/v1/extract` route still work with explicit transcripts. The `conversation_buffer` table is no longer created for new workspaces.
|
|
20
|
+
|
|
18
21
|
---
|
|
19
22
|
|
|
20
23
|
## [0.2.3] - 2026-02-24
|
package/package.json
CHANGED
|
@@ -73,7 +73,7 @@ if [ -f "$SCRIPT_DIR/../.env" ]; then
|
|
|
73
73
|
fi
|
|
74
74
|
|
|
75
75
|
MEMENTO_API="${MEMENTO_API_URL:-https://memento-api.myrakrusemark.workers.dev}"
|
|
76
|
-
MEMENTO_KEY="${MEMENTO_API_KEY:?MEMENTO_API_KEY not set — check memento-
|
|
76
|
+
MEMENTO_KEY="${MEMENTO_API_KEY:?MEMENTO_API_KEY not set — check memento-mcp/.env or .memento.json}"
|
|
77
77
|
MEMENTO_WS="${MEMENTO_WORKSPACE:-default}"
|
|
78
78
|
|
|
79
79
|
# Parse transcript to readable text (use fathom's parser if available, else raw)
|
|
@@ -69,7 +69,7 @@ if [ -f "$SCRIPT_DIR/../.env" ]; then
|
|
|
69
69
|
fi
|
|
70
70
|
|
|
71
71
|
MEMENTO_API="${MEMENTO_API_URL:-https://memento-api.myrakrusemark.workers.dev}"
|
|
72
|
-
MEMENTO_KEY="${MEMENTO_API_KEY:?MEMENTO_API_KEY not set — check memento-
|
|
72
|
+
MEMENTO_KEY="${MEMENTO_API_KEY:?MEMENTO_API_KEY not set — check memento-mcp/.env or .memento.json}"
|
|
73
73
|
MEMENTO_WS="${MEMENTO_WORKSPACE:-default}"
|
|
74
74
|
|
|
75
75
|
AUTH_HEADER="Authorization: Bearer $MEMENTO_KEY"
|
|
@@ -54,7 +54,7 @@ if [ -f "$SCRIPT_DIR/../.env" ]; then
|
|
|
54
54
|
fi
|
|
55
55
|
|
|
56
56
|
MEMENTO_API="${MEMENTO_API_URL:-https://memento-api.myrakrusemark.workers.dev}"
|
|
57
|
-
MEMENTO_KEY="${MEMENTO_API_KEY:?MEMENTO_API_KEY not set — check memento-
|
|
57
|
+
MEMENTO_KEY="${MEMENTO_API_KEY:?MEMENTO_API_KEY not set — check memento-mcp/.env or .memento.json}"
|
|
58
58
|
MEMENTO_WS="${MEMENTO_WORKSPACE:-default}"
|
|
59
59
|
|
|
60
60
|
INPUT=$(cat)
|
|
@@ -78,13 +78,13 @@ QUERY="${ASSISTANT_MSG:0:500}"
|
|
|
78
78
|
# Toast: start retrieving
|
|
79
79
|
"$TOAST" memento "⏳ Autonomous recall..." &>/dev/null
|
|
80
80
|
|
|
81
|
-
# Call Memento /v1/context
|
|
81
|
+
# Call Memento /v1/context
|
|
82
82
|
RESULT=$(curl -s --max-time 8 \
|
|
83
83
|
-X POST \
|
|
84
84
|
-H "Authorization: Bearer $MEMENTO_KEY" \
|
|
85
85
|
-H "X-Memento-Workspace: $MEMENTO_WS" \
|
|
86
86
|
-H "Content-Type: application/json" \
|
|
87
|
-
-d "{\"message\": $(echo "$QUERY" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'), \"include\": [\"memories\", \"skip_list\"]
|
|
87
|
+
-d "{\"message\": $(echo "$QUERY" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'), \"include\": [\"memories\", \"skip_list\"]}" \
|
|
88
88
|
"$MEMENTO_API/v1/context" 2>/dev/null \
|
|
89
89
|
| python3 -c "
|
|
90
90
|
import json, sys
|
|
@@ -96,8 +96,8 @@ try:
|
|
|
96
96
|
|
|
97
97
|
memories = data.get('memories', {}).get('matches', [])
|
|
98
98
|
if memories:
|
|
99
|
-
for m in memories[:${RECALL_LIMIT:-
|
|
100
|
-
content = m['content']
|
|
99
|
+
for m in memories[:${RECALL_LIMIT:-7}]:
|
|
100
|
+
content = m['content']
|
|
101
101
|
t = abbrev.get(m['type'], m['type'])
|
|
102
102
|
lines.append(f' 🔹 {content} [{m[\"id\"]} {t}]')
|
|
103
103
|
count += 1
|
|
@@ -107,55 +107,35 @@ try:
|
|
|
107
107
|
for s in skip_matches:
|
|
108
108
|
lines.append(f' Skip: {s[\"item\"]} — {s[\"reason\"]} (expires {s[\"expires\"]})')
|
|
109
109
|
|
|
110
|
-
extracted = data.get('extracted', [])
|
|
111
|
-
if extracted:
|
|
112
|
-
lines.append(f' Stored: {len(extracted)} new memories')
|
|
113
|
-
|
|
114
|
-
# Output: recall_count \t extracted_count \t detail
|
|
115
|
-
extracted_count = len(extracted)
|
|
116
110
|
detail = '\n'.join(lines)
|
|
117
|
-
print(f'{count}\t{
|
|
111
|
+
print(f'{count}\t{detail}')
|
|
118
112
|
except Exception:
|
|
119
|
-
print('0\
|
|
113
|
+
print('0\t')
|
|
120
114
|
" 2>/dev/null)
|
|
121
115
|
|
|
122
|
-
# Parse count
|
|
116
|
+
# Parse count and detail
|
|
123
117
|
SAAS_COUNT=$(echo "$RESULT" | head -1 | cut -f1)
|
|
124
|
-
|
|
125
|
-
SAAS_DETAIL=$(echo "$RESULT" | head -1 | cut -f3-)
|
|
118
|
+
SAAS_DETAIL=$(echo "$RESULT" | head -1 | cut -f2-)
|
|
126
119
|
REMAINING=$(echo "$RESULT" | tail -n +2)
|
|
127
120
|
if [ -n "$REMAINING" ]; then
|
|
128
121
|
SAAS_DETAIL="$SAAS_DETAIL"$'\n'"$REMAINING"
|
|
129
122
|
fi
|
|
130
123
|
|
|
131
124
|
if [ -z "$SAAS_COUNT" ] || [ "$SAAS_COUNT" = "0" ]; then
|
|
132
|
-
|
|
133
|
-
"$TOAST" memento "✓ ${EXTRACTED_COUNT} memories stored" &>/dev/null
|
|
134
|
-
else
|
|
135
|
-
"$TOAST" memento "✓ No memories matched" &>/dev/null
|
|
136
|
-
fi
|
|
125
|
+
"$TOAST" memento "✓ No memories matched" &>/dev/null
|
|
137
126
|
exit 0
|
|
138
127
|
fi
|
|
139
128
|
|
|
140
|
-
|
|
141
|
-
if [ -n "$EXTRACTED_COUNT" ] && [ "$EXTRACTED_COUNT" != "0" ]; then
|
|
142
|
-
"$TOAST" memento "✓ ${SAAS_COUNT} recalled, ${EXTRACTED_COUNT} stored" &>/dev/null
|
|
143
|
-
else
|
|
144
|
-
"$TOAST" memento "✓ ${SAAS_COUNT} memories recalled" &>/dev/null
|
|
145
|
-
fi
|
|
146
|
-
fi
|
|
129
|
+
"$TOAST" memento "✓ ${SAAS_COUNT} memories recalled" &>/dev/null
|
|
147
130
|
|
|
148
131
|
# Build summary line
|
|
149
|
-
SUMMARY="Recall (${SAAS_COUNT})"
|
|
150
|
-
if [ -n "$EXTRACTED_COUNT" ] && [ "$EXTRACTED_COUNT" != "0" ]; then
|
|
151
|
-
SUMMARY="Recall (${SAAS_COUNT}), stored ${EXTRACTED_COUNT}"
|
|
152
|
-
fi
|
|
132
|
+
SUMMARY="Memento Recall (${SAAS_COUNT})"
|
|
153
133
|
|
|
154
134
|
# Block the Stop so recalled memories are injected into context.
|
|
155
135
|
# If no memories are relevant, respond with <...> to signal active silence.
|
|
156
136
|
REASON="${SUMMARY}:
|
|
157
137
|
${SAAS_DETAIL}
|
|
158
|
-
Stale or wrong?
|
|
138
|
+
Stale or wrong? memento_memory_delete · memento_consolidate · memento_store. Otherwise <...>."
|
|
159
139
|
|
|
160
140
|
python3 -c "
|
|
161
141
|
import json, sys
|
|
@@ -43,7 +43,7 @@ print('true' if hook.get('enabled', True) else 'false')
|
|
|
43
43
|
MEMENTO_WORKSPACE="${MEMENTO_WORKSPACE:-$(echo "$CONFIG_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('workspace',''))" 2>/dev/null)}"
|
|
44
44
|
|
|
45
45
|
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)
|
|
46
|
-
RECALL_MAX_LENGTH=$(echo "$CONFIG_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('hooks',{}).get('$HOOK_NAME',{}).get('maxLength',
|
|
46
|
+
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)
|
|
47
47
|
fi
|
|
48
48
|
# --- End config block ---
|
|
49
49
|
|
|
@@ -55,7 +55,7 @@ if [ -f "$SCRIPT_DIR/../.env" ]; then
|
|
|
55
55
|
fi
|
|
56
56
|
|
|
57
57
|
MEMENTO_API="${MEMENTO_API_URL:-https://memento-api.myrakrusemark.workers.dev}"
|
|
58
|
-
MEMENTO_KEY="${MEMENTO_API_KEY:?MEMENTO_API_KEY not set — check memento-
|
|
58
|
+
MEMENTO_KEY="${MEMENTO_API_KEY:?MEMENTO_API_KEY not set — check memento-mcp/.env or .memento.json}"
|
|
59
59
|
MEMENTO_WS="${MEMENTO_WORKSPACE:-default}"
|
|
60
60
|
|
|
61
61
|
INPUT=$(cat)
|
|
@@ -70,13 +70,13 @@ QUERY="${USER_MESSAGE:0:500}"
|
|
|
70
70
|
# Toast: start retrieving
|
|
71
71
|
"$TOAST" memento "⏳ Retrieving memories..." &>/dev/null
|
|
72
72
|
|
|
73
|
-
# Call Memento SaaS /v1/context
|
|
73
|
+
# Call Memento SaaS /v1/context
|
|
74
74
|
SAAS_OUTPUT=$(curl -s --max-time 8 \
|
|
75
75
|
-X POST \
|
|
76
76
|
-H "Authorization: Bearer $MEMENTO_KEY" \
|
|
77
77
|
-H "X-Memento-Workspace: $MEMENTO_WS" \
|
|
78
78
|
-H "Content-Type: application/json" \
|
|
79
|
-
-d "{\"message\": $(echo "$QUERY" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'), \"include\": [\"memories\", \"skip_list\"]
|
|
79
|
+
-d "{\"message\": $(echo "$QUERY" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'), \"include\": [\"memories\", \"skip_list\"]}" \
|
|
80
80
|
"$MEMENTO_API/v1/context" 2>/dev/null \
|
|
81
81
|
| python3 -c "
|
|
82
82
|
import json, sys
|
|
@@ -84,75 +84,44 @@ try:
|
|
|
84
84
|
data = json.load(sys.stdin)
|
|
85
85
|
lines = []
|
|
86
86
|
count = 0
|
|
87
|
+
abbrev = {'instruction':'instr','observation':'obs','decision':'dec','preference':'pref'}
|
|
87
88
|
|
|
88
|
-
# Memory matches
|
|
89
89
|
memories = data.get('memories', {}).get('matches', [])
|
|
90
90
|
if memories:
|
|
91
|
-
for m in memories[:${RECALL_LIMIT:-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
content
|
|
95
|
-
score = m.get('score', '?')
|
|
96
|
-
date_str = f' {m[\"created_at\"][:10]}' if m.get('created_at') else ''
|
|
97
|
-
lines.append(f' {m[\"id\"]} ({m[\"type\"]}, {score}{date_str}){tag_str} — {content}')
|
|
91
|
+
for m in memories[:${RECALL_LIMIT:-7}]:
|
|
92
|
+
content = m['content']
|
|
93
|
+
t = abbrev.get(m['type'], m['type'])
|
|
94
|
+
lines.append(f' 🔹 {content} [{m[\"id\"]} {t}]')
|
|
98
95
|
count += 1
|
|
99
96
|
|
|
100
|
-
# Skip matches (WARNING format)
|
|
101
97
|
skip_matches = data.get('skip_matches', [])
|
|
102
98
|
if skip_matches:
|
|
103
|
-
lines.append('')
|
|
104
|
-
lines.append('SKIP LIST WARNINGS:')
|
|
105
99
|
for s in skip_matches:
|
|
106
|
-
lines.append(f'
|
|
107
|
-
|
|
108
|
-
# Auto-extracted memories
|
|
109
|
-
extracted = data.get('extracted', [])
|
|
110
|
-
if extracted:
|
|
111
|
-
lines.append('')
|
|
112
|
-
lines.append('MEMORIES STORED:')
|
|
113
|
-
for e in extracted:
|
|
114
|
-
etags = [t for t in e.get('tags', []) if t != 'source:auto-extract']
|
|
115
|
-
etag_str = f' [{\", \".join(etags)}]' if etags else ''
|
|
116
|
-
lines.append(f' {e[\"id\"]} ({e.get(\"type\", \"observation\")}){etag_str} — {e[\"content\"]}')
|
|
117
|
-
|
|
118
|
-
# Output: recall_count \t extracted_count \t detail
|
|
119
|
-
extracted_count = len(extracted)
|
|
100
|
+
lines.append(f' Skip: {s[\"item\"]} — {s[\"reason\"]} (expires {s[\"expires\"]})')
|
|
101
|
+
|
|
120
102
|
detail = '\n'.join(lines)
|
|
121
|
-
print(f'{count}\t{
|
|
103
|
+
print(f'{count}\t{detail}')
|
|
122
104
|
except Exception:
|
|
123
|
-
print('0\
|
|
105
|
+
print('0\t')
|
|
124
106
|
" 2>/dev/null)
|
|
125
107
|
|
|
126
|
-
# Parse count
|
|
108
|
+
# Parse count and detail
|
|
127
109
|
SAAS_COUNT=$(echo "$SAAS_OUTPUT" | head -1 | cut -f1)
|
|
128
|
-
|
|
129
|
-
SAAS_DETAIL=$(echo "$SAAS_OUTPUT" | head -1 | cut -f3-)
|
|
130
|
-
# Append any remaining lines (skip warnings etc.)
|
|
110
|
+
SAAS_DETAIL=$(echo "$SAAS_OUTPUT" | head -1 | cut -f2-)
|
|
131
111
|
REMAINING=$(echo "$SAAS_OUTPUT" | tail -n +2)
|
|
132
112
|
if [ -n "$REMAINING" ]; then
|
|
133
113
|
SAAS_DETAIL="$SAAS_DETAIL"$'\n'"$REMAINING"
|
|
134
114
|
fi
|
|
135
115
|
|
|
136
116
|
if [ -z "$SAAS_COUNT" ] || [ "$SAAS_COUNT" = "0" ]; then
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
else
|
|
140
|
-
"$TOAST" memento "✓ No memories matched" &>/dev/null
|
|
141
|
-
exit 0
|
|
142
|
-
fi
|
|
143
|
-
else
|
|
144
|
-
if [ -n "$EXTRACTED_COUNT" ] && [ "$EXTRACTED_COUNT" != "0" ]; then
|
|
145
|
-
"$TOAST" memento "✓ ${SAAS_COUNT} recalled, ${EXTRACTED_COUNT} stored" &>/dev/null
|
|
146
|
-
else
|
|
147
|
-
"$TOAST" memento "✓ ${SAAS_COUNT} memories recalled" &>/dev/null
|
|
148
|
-
fi
|
|
117
|
+
"$TOAST" memento "✓ No memories matched" &>/dev/null
|
|
118
|
+
exit 0
|
|
149
119
|
fi
|
|
150
120
|
|
|
121
|
+
"$TOAST" memento "✓ ${SAAS_COUNT} memories recalled" &>/dev/null
|
|
122
|
+
|
|
151
123
|
# Build summary line
|
|
152
|
-
SUMMARY="Memento Recall
|
|
153
|
-
if [ -n "$EXTRACTED_COUNT" ] && [ "$EXTRACTED_COUNT" != "0" ]; then
|
|
154
|
-
SUMMARY="${SUMMARY}, ${EXTRACTED_COUNT} memories stored"
|
|
155
|
-
fi
|
|
124
|
+
SUMMARY="Memento Recall (${SAAS_COUNT})"
|
|
156
125
|
|
|
157
126
|
DETAIL_TEXT="$SUMMARY"
|
|
158
127
|
DETAIL_TEXT="$DETAIL_TEXT"$'\n'"$SAAS_DETAIL"
|
package/src/index.js
CHANGED
|
@@ -439,25 +439,23 @@ This is reconsolidation — like how the brain rebuilds memories on recall. Freq
|
|
|
439
439
|
|
|
440
440
|
server.tool(
|
|
441
441
|
"memento_extract",
|
|
442
|
-
`Extract memories from a conversation transcript
|
|
442
|
+
`Extract memories from a conversation transcript using LLM. Use this to extract memories from a pasted transcript.
|
|
443
443
|
|
|
444
444
|
Two modes:
|
|
445
445
|
- "distill" (default): Full extraction — facts, decisions, instructions, observations. Up to 20 memories.
|
|
446
446
|
- "seeds": Relational/experiential texture — preferences, emotions, sensory details. Up to 3 memories.
|
|
447
447
|
|
|
448
|
-
|
|
448
|
+
A transcript is required.`,
|
|
449
449
|
{
|
|
450
|
-
transcript: z.string().
|
|
450
|
+
transcript: z.string().describe("Raw conversation text to extract memories from."),
|
|
451
451
|
mode: z.enum(["distill", "seeds"]).optional().describe("Extraction mode (default: distill)"),
|
|
452
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
453
|
},
|
|
455
|
-
async ({ transcript, mode, max_memories
|
|
454
|
+
async ({ transcript, mode, max_memories }) => {
|
|
456
455
|
const result = await storage.extractMemories(null, {
|
|
457
456
|
transcript,
|
|
458
457
|
mode,
|
|
459
458
|
max_memories,
|
|
460
|
-
keep_buffer,
|
|
461
459
|
});
|
|
462
460
|
|
|
463
461
|
if (result.error && !result.stored) {
|
package/src/storage/hosted.js
CHANGED
|
@@ -261,13 +261,12 @@ export class HostedStorageAdapter extends StorageInterface {
|
|
|
261
261
|
return res;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
async extractMemories(_wsPath, { transcript, mode, max_memories, source_tag
|
|
264
|
+
async extractMemories(_wsPath, { transcript, mode, max_memories, source_tag }) {
|
|
265
265
|
const body = {};
|
|
266
266
|
if (transcript) body.transcript = transcript;
|
|
267
267
|
if (mode) body.mode = mode;
|
|
268
268
|
if (max_memories) body.max_memories = max_memories;
|
|
269
269
|
if (source_tag) body.source_tag = source_tag;
|
|
270
|
-
if (keep_buffer !== undefined) body.keep_buffer = keep_buffer;
|
|
271
270
|
|
|
272
271
|
const res = await this._fetchJson("POST", "/v1/extract", body);
|
|
273
272
|
if (res.error && !res.stored) return { error: res.error };
|