memoryrouter-codex 1.4.0 → 1.5.0
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/README.md +2 -1
- package/package.json +1 -1
- package/runtime/api.cjs +8 -1
- package/runtime/constants.cjs +1 -1
- package/runtime/mcp-stdio.cjs +63 -4
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ printf '%s' "$MEMORYROUTER_API_KEY" | \
|
|
|
77
77
|
This uses a local stdio MCP server and the MemoryRouter API. It does not put the memory key in Codex config. The fallback tools are:
|
|
78
78
|
|
|
79
79
|
- `memory_search`
|
|
80
|
+
- `memory_date_search`
|
|
80
81
|
- `memory_store`
|
|
81
82
|
- `memory_status`
|
|
82
83
|
- `memory_forget`
|
|
@@ -161,7 +162,7 @@ MemoryRouter's Reflection Hierarchy consolidates raw memories into higher-tier r
|
|
|
161
162
|
- **CLI:** `memoryrouter-codex reflect [--tier 1|2] [--batches N|all] [--max-tokens N] [--dry-run]`. The loop checks out a batch of unconsolidated memories under a 15 minute server lease, runs Codex itself headlessly (`codex exec`, you pay your own inference) with server-authored instructions, and commits the resulting reflection entries. It keeps no local state: if anything dies mid-loop, the lease expires server-side and the memories return to the pool, so re-running always resumes from the oldest unconsolidated memory. `--dry-run` prints the checkout and prompt and deliberately lets the lease expire (this bills one checkout).
|
|
162
163
|
- **Local stdio MCP:** `memory_consolidate` (checkout, returns memory texts plus the server's reflection instructions) and `memory_commit_reflections` (submit entries for the batch, idempotent on replay). The model never sees memory row IDs; it works with bare texts and one opaque `cb_` batch id.
|
|
163
164
|
|
|
164
|
-
`memory_search` accepts optional `tiers` (subset of `[1, 2, 3]`: raw, reflections, high-level reflections) and `importance` (integer 1 to 10 minimum threshold; only reflections carry ratings). `memory_status` reports the current reflection debt. When the server reports that consolidation is suggested, hook-injected context appends a one-line suggestion on every injection until the debt is consolidated.
|
|
165
|
+
`memory_search` accepts optional `tiers` (subset of `[1, 2, 3]`: raw, reflections, high-level reflections) and `importance` (integer 1 to 10 minimum threshold; only reflections carry ratings). `memory_date_search` retrieves memories from a specific time window (`from` required ISO 8601, optional `to`, `query`, `tiers`, `importance`, `max_tokens`): use it for questions like 'what happened last week' or 'most important things this month or lately', setting importance to 7 or higher with tiers `[2, 3]` for importance questions, omitting `query` for a chronological review. `memory_status` reports the current reflection debt. When the server reports that consolidation is suggested, hook-injected context appends a one-line suggestion on every injection until the debt is consolidated.
|
|
165
166
|
|
|
166
167
|
Reflection prompts are always server-authored. This package never writes its own consolidation instructions and refuses to reflect if the server response carries none. See https://docs.memoryrouter.ai/reflections for endpoint semantics, lease rules, and billing.
|
|
167
168
|
|
package/package.json
CHANGED
package/runtime/api.cjs
CHANGED
|
@@ -129,10 +129,17 @@ function deleteIds(key, ids, options = {}) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
function dateSearch(key, params = {}, options = {}) {
|
|
132
|
+
const body = {};
|
|
133
|
+
if (params.from !== undefined) body.from = params.from;
|
|
134
|
+
if (params.to !== undefined) body.to = params.to;
|
|
135
|
+
if (params.query !== undefined) body.query = params.query;
|
|
136
|
+
if (params.max_tokens !== undefined) body.max_tokens = params.max_tokens;
|
|
137
|
+
if (Array.isArray(params.tiers) && params.tiers.length) body.tiers = params.tiers;
|
|
138
|
+
if (params.importance !== undefined) body.importance = params.importance;
|
|
132
139
|
return request('POST', '/v1/memory/date-search', {
|
|
133
140
|
...options,
|
|
134
141
|
key,
|
|
135
|
-
body
|
|
142
|
+
body,
|
|
136
143
|
timeoutMs: options.timeoutMs || 15000,
|
|
137
144
|
retries: options.retries ?? 1,
|
|
138
145
|
});
|
package/runtime/constants.cjs
CHANGED
package/runtime/mcp-stdio.cjs
CHANGED
|
@@ -5,7 +5,7 @@ const readline = require('readline');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const { loadConfig, getApiKey, configPath, auditPath, recordAudit, healthPath } = require('./config.cjs');
|
|
7
7
|
const { resolveProject } = require('./project.cjs');
|
|
8
|
-
const { searchScoped, storeScoped, forgetLocally } = require('./memory.cjs');
|
|
8
|
+
const { searchScoped, storeScoped, forgetLocally, parseEnvelope } = require('./memory.cjs');
|
|
9
9
|
const api = require('./api.cjs');
|
|
10
10
|
const { contractFrom, validateEntries } = require('./reflect.cjs');
|
|
11
11
|
const { INTEGRATION_VERSION } = require('./constants.cjs');
|
|
@@ -15,19 +15,36 @@ const SERVER_INSTRUCTIONS = 'MemoryRouter provides durable cross-machine, cross-
|
|
|
15
15
|
const TOOLS = [
|
|
16
16
|
{
|
|
17
17
|
name: 'memory_search',
|
|
18
|
-
description:
|
|
18
|
+
description: "Search durable MemoryRouter memories. Defaults to the current git project and filters other projects out. For questions like 'what are the most important things' set importance to 7 or higher with tiers [2, 3], since only reflections carry importance ratings. For time-window questions like 'what happened last week' or 'lately' use memory_date_search instead.",
|
|
19
19
|
inputSchema: {
|
|
20
20
|
type: 'object',
|
|
21
21
|
properties: {
|
|
22
22
|
query: { type: 'string', description: 'Semantic search query.' },
|
|
23
23
|
limit: { type: 'integer', minimum: 1, maximum: 50, description: 'Candidate results before project filtering.' },
|
|
24
|
-
tiers: { type: 'array', items: { type: 'integer', enum: [1, 2, 3] }, minItems: 1, uniqueItems: true, description: 'Restrict results to these memory tiers. 1 raw memories, 2 reflections, 3 high-level reflections. Omit to search all tiers blended.' },
|
|
25
|
-
importance: { type: 'integer', minimum: 1, maximum: 10, description:
|
|
24
|
+
tiers: { type: 'array', items: { type: 'integer', enum: [1, 2, 3] }, minItems: 1, uniqueItems: true, description: 'Restrict results to these memory tiers. 1 raw memories, 2 reflections, 3 high-level reflections. Only tiers 2 and 3 carry importance ratings. Omit to search all tiers blended.' },
|
|
25
|
+
importance: { type: 'integer', minimum: 1, maximum: 10, description: "Minimum importance threshold, 1 to 10. Only reflections (tiers 2 and 3) carry ratings, so raw memories are excluded whenever this is set. For 'most important' questions use 7 or higher with tiers [2, 3]." },
|
|
26
26
|
},
|
|
27
27
|
required: ['query'],
|
|
28
28
|
},
|
|
29
29
|
annotations: { title: 'Search MemoryRouter', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
30
30
|
},
|
|
31
|
+
{
|
|
32
|
+
name: 'memory_date_search',
|
|
33
|
+
description: "Retrieve memories from a specific time window. Use for questions like 'what happened last week' or 'most important things this month or lately'. For importance questions set importance to 7 or higher with tiers [2, 3], since only reflections carry importance ratings. Omit query for a chronological review; include query to rank by relevance within the window. Resolve relative phrases like 'lately' to concrete ISO dates before calling.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
from: { type: 'string', description: 'Window start, ISO 8601 date or datetime (e.g. 2026-08-10 or 2026-08-10T00:00:00Z). Required. Must be a concrete date, not a relative phrase.' },
|
|
38
|
+
to: { type: 'string', description: 'Window end, ISO 8601 date or datetime. Optional, defaults to now.' },
|
|
39
|
+
query: { type: 'string', description: 'Optional relevance query. Omit for a pure chronological browse; include to rank results within the window.' },
|
|
40
|
+
tiers: { type: 'array', items: { type: 'integer', enum: [1, 2, 3] }, minItems: 1, uniqueItems: true, description: 'Restrict results to these memory tiers. 1 raw memories, 2 reflections, 3 high-level reflections. Only tiers 2 and 3 carry importance ratings. Omit to include all tiers.' },
|
|
41
|
+
importance: { type: 'integer', minimum: 1, maximum: 10, description: "Minimum importance threshold, 1 to 10. Only reflections (tiers 2 and 3) carry ratings, so raw memories are excluded whenever this is set. For 'most important' questions use 7 or higher with tiers [2, 3]." },
|
|
42
|
+
max_tokens: { type: 'integer', minimum: 1000, maximum: 200000, description: 'Token budget for returned results. Server default 50000; use a smaller value like 5000 for a quick look.' },
|
|
43
|
+
},
|
|
44
|
+
required: ['from'],
|
|
45
|
+
},
|
|
46
|
+
annotations: { title: 'Date search MemoryRouter', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
47
|
+
},
|
|
31
48
|
{
|
|
32
49
|
name: 'memory_store',
|
|
33
50
|
description: 'Store a concise durable project memory. Returns a stable content-derived handle and deduplicates exact repeats.',
|
|
@@ -139,6 +156,48 @@ async function callTool(name, args = {}, options = {}) {
|
|
|
139
156
|
}
|
|
140
157
|
return textResult(text);
|
|
141
158
|
}
|
|
159
|
+
if (name === 'memory_date_search') {
|
|
160
|
+
if (!args.from || typeof args.from !== 'string') return textResult('from is required (ISO 8601 date or datetime)', true);
|
|
161
|
+
if (args.tiers !== undefined) {
|
|
162
|
+
if (!Array.isArray(args.tiers) || args.tiers.length < 1 || args.tiers.some(tier => ![1, 2, 3].includes(tier))) {
|
|
163
|
+
return textResult('tiers must be a non-empty array drawn from [1, 2, 3]', true);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (args.importance !== undefined && (!Number.isInteger(args.importance) || args.importance < 1 || args.importance > 10)) {
|
|
167
|
+
return textResult('importance must be an integer 1 to 10', true);
|
|
168
|
+
}
|
|
169
|
+
if (args.max_tokens !== undefined && (!Number.isInteger(args.max_tokens) || args.max_tokens < 1000 || args.max_tokens > 200000)) {
|
|
170
|
+
return textResult('max_tokens must be an integer between 1000 and 200000', true);
|
|
171
|
+
}
|
|
172
|
+
const params = { from: args.from };
|
|
173
|
+
if (typeof args.to === 'string' && args.to) params.to = args.to;
|
|
174
|
+
if (typeof args.query === 'string' && args.query.trim()) params.query = args.query.trim();
|
|
175
|
+
if (args.tiers !== undefined) params.tiers = args.tiers;
|
|
176
|
+
if (args.importance !== undefined) params.importance = args.importance;
|
|
177
|
+
if (args.max_tokens !== undefined) params.max_tokens = args.max_tokens;
|
|
178
|
+
const response = await api.dateSearch(key, params, { apiBase: config.apiBase, timeoutMs: 15000 });
|
|
179
|
+
if (!response.ok) {
|
|
180
|
+
const detail = response.data && response.data.error ? ` ${response.data.error}` : '';
|
|
181
|
+
return textResult(`Date search failed (HTTP ${response.status}).${detail}`, true);
|
|
182
|
+
}
|
|
183
|
+
const data = response.data || {};
|
|
184
|
+
const rows = Array.isArray(data.results) ? data.results : [];
|
|
185
|
+
if (!rows.length) return textResult('No memories in that window matched the filters.');
|
|
186
|
+
const lines = rows.map(row => {
|
|
187
|
+
const parsed = parseEnvelope(row.content);
|
|
188
|
+
const body = parsed ? parsed.body : String(row.content || '');
|
|
189
|
+
const tierTag = row.tier != null ? ` | tier ${row.tier}` : '';
|
|
190
|
+
const importanceTag = row.importance != null ? ` | importance ${row.importance}` : '';
|
|
191
|
+
return `[${row.date || 'unknown date'}${tierTag}${importanceTag}]\n${body}`;
|
|
192
|
+
});
|
|
193
|
+
const header = data.window
|
|
194
|
+
? `Window ${data.window.from || params.from} to ${data.window.to || 'now'} (${rows.length} memories${data.truncated ? ', truncated' : ''}).`
|
|
195
|
+
: `${rows.length} memories${data.truncated ? ' (truncated)' : ''}.`;
|
|
196
|
+
const footer = data.truncated
|
|
197
|
+
? `\n\nTruncated. Continue with ${data.next_to ? `to = ${data.next_to}` : data.next_from ? `from = ${data.next_from}` : 'a narrower window'}.`
|
|
198
|
+
: '';
|
|
199
|
+
return textResult(`${header}\n\n${lines.join('\n\n')}${footer}`);
|
|
200
|
+
}
|
|
142
201
|
if (name === 'memory_store') {
|
|
143
202
|
if (!args.content) return textResult('content is required', true);
|
|
144
203
|
const result = await storeScoped(args.content, {
|