memorix 1.8.1 → 1.8.3
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 +36 -0
- package/dist/cli/index.js +577 -58
- package/dist/index.js +832 -321
- package/dist/maintenance-runner.js +31 -25
- package/dist/media-audio-runner.js +31 -25
- package/dist/media-video-runner.js +31 -25
- package/dist/memcode-runtime/CHANGELOG.md +36 -0
- package/dist/sdk.js +585 -315
- package/dist/vector-backfill-runner.js +31 -25
- package/docs/1.2.0-PROVIDER-QUALITY.md +15 -6
- package/docs/1.3.3-MULTIMODAL-MEDIA-AND-MCP-COMPAT.md +7 -6
- package/docs/1.8.0-RELEASE-CANDIDATE-SPEC.md +13 -8
- package/docs/API_REFERENCE.md +14 -11
- package/docs/CONFIGURATION.md +2 -2
- package/docs/DEVELOPMENT.md +3 -2
- package/docs/README.md +13 -7
- package/package.json +2 -1
- package/plugins/antigravity/memorix/plugin.json +2 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +3 -3
- package/src/cli/commands/serve-http.ts +10 -2
- package/src/cli/commands/serve.ts +26 -7
- package/src/codegraph/external-provider.ts +299 -19
- package/src/config/dotenv-loader.ts +9 -4
- package/src/config.ts +16 -16
- package/src/dashboard/maintenance.ts +4 -1
- package/src/llm/provider.ts +17 -4
- package/src/server/mcp-compat.ts +63 -0
- package/src/server/mcp-diagnostics.ts +13 -4
- package/src/server/mcp-discovery.ts +61 -0
- package/src/server/stdio-startup-gate.ts +263 -0
- package/src/server.ts +4 -4
|
@@ -104,8 +104,10 @@ function loadEnvFile(filePath) {
|
|
|
104
104
|
loadedEnvFiles.push(filePath);
|
|
105
105
|
}
|
|
106
106
|
function loadDotenv(projectRoot, options = {}) {
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
const userHomeDir = options.userHomeDir ?? homedir();
|
|
108
|
+
const normalizedProjectRoot = projectRoot ?? null;
|
|
109
|
+
if (dotenvLoaded && dotenvProjectRoot === normalizedProjectRoot && dotenvHomeDir === userHomeDir) return;
|
|
110
|
+
if (dotenvLoaded && (dotenvProjectRoot !== normalizedProjectRoot || dotenvHomeDir !== userHomeDir)) {
|
|
109
111
|
resetDotenv();
|
|
110
112
|
}
|
|
111
113
|
loadedEnvFiles.length = 0;
|
|
@@ -114,9 +116,10 @@ function loadDotenv(projectRoot, options = {}) {
|
|
|
114
116
|
if (projectRoot) {
|
|
115
117
|
loadEnvFile(getProjectDotenvPath(projectRoot));
|
|
116
118
|
}
|
|
117
|
-
loadEnvFile(getGlobalDotenvPath(
|
|
119
|
+
loadEnvFile(getGlobalDotenvPath(userHomeDir));
|
|
118
120
|
dotenvLoaded = true;
|
|
119
|
-
dotenvProjectRoot =
|
|
121
|
+
dotenvProjectRoot = normalizedProjectRoot;
|
|
122
|
+
dotenvHomeDir = userHomeDir;
|
|
120
123
|
}
|
|
121
124
|
function resetDotenv() {
|
|
122
125
|
for (const key of injectedKeys) {
|
|
@@ -125,13 +128,14 @@ function resetDotenv() {
|
|
|
125
128
|
injectedKeys.clear();
|
|
126
129
|
dotenvLoaded = false;
|
|
127
130
|
dotenvProjectRoot = null;
|
|
131
|
+
dotenvHomeDir = null;
|
|
128
132
|
loadedEnvFiles.length = 0;
|
|
129
133
|
loadedEnvKeys.clear();
|
|
130
134
|
}
|
|
131
135
|
function getLoadedEnvFiles() {
|
|
132
136
|
return loadedEnvFiles;
|
|
133
137
|
}
|
|
134
|
-
var dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
|
|
138
|
+
var dotenvLoaded, dotenvProjectRoot, dotenvHomeDir, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
|
|
135
139
|
var init_dotenv_loader = __esm({
|
|
136
140
|
"src/config/dotenv-loader.ts"() {
|
|
137
141
|
"use strict";
|
|
@@ -139,6 +143,7 @@ var init_dotenv_loader = __esm({
|
|
|
139
143
|
init_config_paths();
|
|
140
144
|
dotenvLoaded = false;
|
|
141
145
|
dotenvProjectRoot = null;
|
|
146
|
+
dotenvHomeDir = null;
|
|
142
147
|
loadedEnvFiles = [];
|
|
143
148
|
loadedEnvKeys = /* @__PURE__ */ new Set();
|
|
144
149
|
loadedEnvValues = /* @__PURE__ */ new Map();
|
|
@@ -2372,33 +2377,33 @@ __export(config_exports, {
|
|
|
2372
2377
|
resetConfigCache: () => resetConfigCache,
|
|
2373
2378
|
resetDotenv: () => resetDotenv
|
|
2374
2379
|
});
|
|
2375
|
-
function getLLMApiKey() {
|
|
2376
|
-
return getResolvedMemoryLane().llm.apiKey;
|
|
2380
|
+
function getLLMApiKey(options = {}) {
|
|
2381
|
+
return getResolvedMemoryLane(options).llm.apiKey;
|
|
2377
2382
|
}
|
|
2378
|
-
function getLLMProvider() {
|
|
2379
|
-
const provider2 = getResolvedMemoryLane().llm.provider;
|
|
2383
|
+
function getLLMProvider(options = {}) {
|
|
2384
|
+
const provider2 = getResolvedMemoryLane(options).llm.provider;
|
|
2380
2385
|
if (provider2) return provider2;
|
|
2381
2386
|
if (process.env.ANTHROPIC_API_KEY && !process.env.OPENAI_API_KEY) return "anthropic";
|
|
2382
2387
|
if (process.env.OPENROUTER_API_KEY && !process.env.OPENAI_API_KEY) return "openrouter";
|
|
2383
2388
|
return "openai";
|
|
2384
2389
|
}
|
|
2385
|
-
function getLLMModel(providerDefault) {
|
|
2386
|
-
return getResolvedMemoryLane().llm.model || providerDefault;
|
|
2390
|
+
function getLLMModel(providerDefault, options = {}) {
|
|
2391
|
+
return getResolvedMemoryLane(options).llm.model || providerDefault;
|
|
2387
2392
|
}
|
|
2388
|
-
function getLLMBaseUrl(providerDefault) {
|
|
2389
|
-
return getResolvedMemoryLane().llm.baseUrl || providerDefault;
|
|
2393
|
+
function getLLMBaseUrl(providerDefault, options = {}) {
|
|
2394
|
+
return getResolvedMemoryLane(options).llm.baseUrl || providerDefault;
|
|
2390
2395
|
}
|
|
2391
|
-
function getAgentLLMApiKey() {
|
|
2392
|
-
return getResolvedAgentLane().apiKey;
|
|
2396
|
+
function getAgentLLMApiKey(options = {}) {
|
|
2397
|
+
return getResolvedAgentLane(options).apiKey;
|
|
2393
2398
|
}
|
|
2394
|
-
function getAgentLLMProvider() {
|
|
2395
|
-
return getResolvedAgentLane().provider || getLLMProvider();
|
|
2399
|
+
function getAgentLLMProvider(options = {}) {
|
|
2400
|
+
return getResolvedAgentLane(options).provider || getLLMProvider(options);
|
|
2396
2401
|
}
|
|
2397
|
-
function getAgentLLMModel(providerDefault) {
|
|
2398
|
-
return getResolvedAgentLane().model || getLLMModel(providerDefault);
|
|
2402
|
+
function getAgentLLMModel(providerDefault, options = {}) {
|
|
2403
|
+
return getResolvedAgentLane(options).model || getLLMModel(providerDefault, options);
|
|
2399
2404
|
}
|
|
2400
|
-
function getAgentLLMBaseUrl(providerDefault) {
|
|
2401
|
-
return getResolvedAgentLane().baseUrl || getLLMBaseUrl(providerDefault);
|
|
2405
|
+
function getAgentLLMBaseUrl(providerDefault, options = {}) {
|
|
2406
|
+
return getResolvedAgentLane(options).baseUrl || getLLMBaseUrl(providerDefault, options);
|
|
2402
2407
|
}
|
|
2403
2408
|
function getEmbeddingMode() {
|
|
2404
2409
|
const provider2 = getResolvedEmbeddingLane().provider?.toLowerCase()?.trim();
|
|
@@ -4657,18 +4662,19 @@ async function* callLLMWithToolsStream(messages, tools) {
|
|
|
4657
4662
|
}
|
|
4658
4663
|
function initLLM(options = {}) {
|
|
4659
4664
|
const scope = options.scope ?? "memory";
|
|
4660
|
-
const
|
|
4665
|
+
const configOptions = { projectRoot: options.projectRoot, homeDir: options.homeDir };
|
|
4666
|
+
const apiKey = scope === "agent" ? getAgentLLMApiKey(configOptions) : getLLMApiKey(configOptions);
|
|
4661
4667
|
if (!apiKey) {
|
|
4662
4668
|
currentConfig = null;
|
|
4663
4669
|
return null;
|
|
4664
4670
|
}
|
|
4665
|
-
const provider2 = scope === "agent" ? getAgentLLMProvider() : getLLMProvider();
|
|
4671
|
+
const provider2 = scope === "agent" ? getAgentLLMProvider(configOptions) : getLLMProvider(configOptions);
|
|
4666
4672
|
const defaults2 = PROVIDER_DEFAULTS[provider2] ?? PROVIDER_DEFAULTS.openai;
|
|
4667
4673
|
currentConfig = {
|
|
4668
4674
|
provider: provider2,
|
|
4669
4675
|
apiKey,
|
|
4670
|
-
model: scope === "agent" ? getAgentLLMModel(defaults2.model) : getLLMModel(defaults2.model),
|
|
4671
|
-
baseUrl: scope === "agent" ? getAgentLLMBaseUrl(defaults2.baseUrl) : getLLMBaseUrl(defaults2.baseUrl)
|
|
4676
|
+
model: scope === "agent" ? getAgentLLMModel(defaults2.model, configOptions) : getLLMModel(defaults2.model, configOptions),
|
|
4677
|
+
baseUrl: scope === "agent" ? getAgentLLMBaseUrl(defaults2.baseUrl, configOptions) : getLLMBaseUrl(defaults2.baseUrl, configOptions)
|
|
4672
4678
|
};
|
|
4673
4679
|
return currentConfig;
|
|
4674
4680
|
}
|
|
@@ -58,8 +58,9 @@ Memorix only considers the external provider when all of these are true:
|
|
|
58
58
|
root;
|
|
59
59
|
4. the index has no reported pending changes or worktree mismatch;
|
|
60
60
|
5. the bounded command completes before its configured timeout; and
|
|
61
|
-
6. the returned JSON
|
|
62
|
-
|
|
61
|
+
6. the returned context JSON, or the official v1.5.0 `explore` output after a
|
|
62
|
+
context-command failure, passes Memorix's schema, path, relation, count,
|
|
63
|
+
and output-size validation.
|
|
63
64
|
|
|
64
65
|
Memorix never runs `codegraph init`, `index`, or `sync` on the user's behalf.
|
|
65
66
|
It does not transmit the repository to a remote service. Its default request is
|
|
@@ -90,7 +91,7 @@ external_context = "auto"
|
|
|
90
91
|
# external_command = "C:\\tools\\codegraph.cmd"
|
|
91
92
|
|
|
92
93
|
# A bounded local query. Values outside the safe range are clamped.
|
|
93
|
-
external_timeout_ms =
|
|
94
|
+
external_timeout_ms = 3000
|
|
94
95
|
~~~
|
|
95
96
|
|
|
96
97
|
The equivalent YAML keys are `externalContext`, `externalCommand`, and
|
|
@@ -114,12 +115,19 @@ The status is visible through `memorix codegraph status --json`, `memorix
|
|
|
114
115
|
doctor --json`, and Project Context JSON. Prompt output keeps it compact: it
|
|
115
116
|
adds semantic start points only when a valid external outline is available, and
|
|
116
117
|
adds one caution when a requested external outline was not safe to use.
|
|
118
|
+
An external status of `ready` means only that the index is initialized and
|
|
119
|
+
current; it does not prove that the installed CLI exposes `context`. The task
|
|
120
|
+
diagnostics therefore record `context` and `explore` independently.
|
|
117
121
|
|
|
118
122
|
## Validation and Fallback
|
|
119
123
|
|
|
120
124
|
The external adapter executes without a shell, pins the working directory to
|
|
121
|
-
the actual project root, uses a bounded timeout and stdout limit, and
|
|
122
|
-
|
|
125
|
+
the actual project root, uses a bounded timeout and stdout limit, and tries
|
|
126
|
+
`context` first. CodeGraph v1.5.0 does not register that command, so a failed
|
|
127
|
+
or invalid context result triggers the official `explore --path <root>
|
|
128
|
+
--max-files 4 <task>` command. JSON context output and the stable explore
|
|
129
|
+
outline/relationship markers are accepted only after the same strict path and
|
|
130
|
+
relation validation. It rejects a result when:
|
|
123
131
|
|
|
124
132
|
- the status belongs to another project root;
|
|
125
133
|
- the index is uninitialized, has pending changes, or reports a worktree
|
|
@@ -145,7 +153,8 @@ runner. It verifies:
|
|
|
145
153
|
3. Stale, mismatched, malformed, timed-out, and oversized external output
|
|
146
154
|
safely falls back to Lite.
|
|
147
155
|
4. Raw code blocks and credentials do not enter the default Workset.
|
|
148
|
-
5. `codegraph status` and `doctor` identify the active/fallback provider
|
|
156
|
+
5. `codegraph status` and `doctor` identify the active/fallback provider, and
|
|
157
|
+
distinguish a healthy index from a usable task-query command.
|
|
149
158
|
|
|
150
159
|
The optional real smoke can be run on a Windows project that already has an
|
|
151
160
|
indexed CodeGraph installation:
|
|
@@ -158,15 +158,16 @@ layer, not Memorix product identity.
|
|
|
158
158
|
- Product operations receive a resolved project/workspace binding and explicit
|
|
159
159
|
actor metadata; they do not read an HTTP session map directly.
|
|
160
160
|
- The old `Mcp-Session-Id` map remains isolated in `serve-http` as a legacy
|
|
161
|
-
adapter
|
|
162
|
-
2026-07-28
|
|
161
|
+
adapter. A low-level `server/discover` adapter now covers the required
|
|
162
|
+
2026-07-28 discovery result without upgrading the pinned v1 SDK.
|
|
163
163
|
- Asset IDs, media job IDs, project IDs, and attachment targets are explicit
|
|
164
164
|
durable identifiers. They are never inferred from a live MCP connection.
|
|
165
165
|
- New media tools work through stdio, legacy streamable HTTP, and direct CLI
|
|
166
166
|
invocation with identical project binding behavior.
|
|
167
|
-
- No release depends on
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
- No release depends on the MCP TypeScript SDK v2. The current server remains
|
|
168
|
+
compatible with existing legacy clients and supports versionless list/call
|
|
169
|
+
through the v1 SDK's legacy envelopes. The adapter does not claim modern
|
|
170
|
+
result caching, Tasks, subscriptions, or complete 2026 conformance.
|
|
170
171
|
|
|
171
172
|
## Non-goals
|
|
172
173
|
|
|
@@ -177,7 +178,7 @@ layer, not Memorix product identity.
|
|
|
177
178
|
- No automatic audio/video transcription, frame extraction, or arbitrary media
|
|
178
179
|
understanding in 1.3.3. The asset model makes those future derivations safe.
|
|
179
180
|
- No forced migration to a protocol version that the published MCP TypeScript
|
|
180
|
-
SDK does not yet support.
|
|
181
|
+
SDK does not yet support; discovery is a narrow compatibility adapter.
|
|
181
182
|
- No additional swarm of low-level MCP tools; CLI is the complete management
|
|
182
183
|
surface and MCP exposes only small agent-facing operations.
|
|
183
184
|
|
|
@@ -17,14 +17,19 @@ Status: accepted implementation contract for Memorix 1.8.0.
|
|
|
17
17
|
|
|
18
18
|
## Compatibility Contract
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
Memorix preserves the legacy 2025-era initialize contract, including stdio and
|
|
21
|
+
stateful Streamable HTTP with `Mcp-Session-Id`. Memorix 1.8.3 also registers a
|
|
22
|
+
low-level `server/discover` compatibility adapter for the 2026-07-28 request.
|
|
23
|
+
Its `DiscoverResult` is complete for the required identity, versions,
|
|
24
|
+
capabilities, and private zero-TTL cache hint. Versionless `tools/list` and
|
|
25
|
+
`tools/call` are accepted by the pinned v1 SDK and retain its legacy result
|
|
26
|
+
envelopes. The stdio startup gate reads bounded newline-delimited input before
|
|
27
|
+
project initialization, answers discovery immediately, and replays all other
|
|
28
|
+
messages in order; EOF is deferred until the queued input is released. HTTP
|
|
29
|
+
stateless project handles remain a compatibility adapter: they
|
|
30
|
+
own project routing, but do not turn the v1 SDK into a complete 2026 runtime.
|
|
31
|
+
Tasks, subscriptions, and list-response caching are unsupported and are not
|
|
32
|
+
advertised or simulated.
|
|
28
33
|
|
|
29
34
|
Antigravity is the primary setup target. Gemini CLI remains a compatibility and
|
|
30
35
|
migration path. GitHub issue #204 (WorkBuddy) is intentionally not merged or
|
package/docs/API_REFERENCE.md
CHANGED
|
@@ -147,17 +147,20 @@ preserved. A memory without explicit feedback keeps the legacy relevance and
|
|
|
147
147
|
retention score. The first feedback event opts that candidate into the
|
|
148
148
|
feedback projection.
|
|
149
149
|
|
|
150
|
-
###
|
|
151
|
-
|
|
152
|
-
Legacy clients use stateful Streamable HTTP with `Mcp-Session-Id
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
`
|
|
160
|
-
|
|
150
|
+
### MCP Compatibility
|
|
151
|
+
|
|
152
|
+
Legacy clients use stateful Streamable HTTP with `Mcp-Session-Id` and the
|
|
153
|
+
2025-era `initialize` handshake. The 1.8.3 stdio server starts a bounded
|
|
154
|
+
newline-delimited JSON-RPC gate before project initialization: it answers the
|
|
155
|
+
2026-07-28 `server/discover` request before `initialize`, then replays other
|
|
156
|
+
requests, notifications, and responses in order into the pinned SDK transport.
|
|
157
|
+
Versionless `tools/list` and `tools/call` use the SDK's legacy result envelopes.
|
|
158
|
+
HTTP stateless clients may use a durable
|
|
159
|
+
`Mcp-Project-Handle`; `/protocol-diagnostics` labels this as compatibility-only.
|
|
160
|
+
Modern result metadata is discovery-only with a private zero-TTL hint. Tasks,
|
|
161
|
+
subscriptions, and list-response caching are unsupported. When MCP discovery
|
|
162
|
+
is unavailable, use one bounded `memorix context --fallback` request for a
|
|
163
|
+
task; repeated fallback probing is rejected by the local budget.
|
|
161
164
|
|
|
162
165
|
The built-in Lite provider indexes common code files with lightweight file, symbol, and import facts. It is a structural fallback, not a language-server-quality graph. When a project already has a healthy local CodeGraph index, `[codegraph].external_context = "auto"` may add a validated, bounded semantic outline to the Workset. Memorix never initializes, syncs, or exports that external index, and never stores its raw source output. `memorix codegraph status --json`, `memorix doctor --json`, and Project Context JSON identify the actual provider quality.
|
|
163
166
|
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -74,7 +74,7 @@ max_file_bytes = 2097152
|
|
|
74
74
|
# Optional: use an already-indexed local CodeGraph only when it is healthy.
|
|
75
75
|
external_context = "auto"
|
|
76
76
|
# external_command = "C:\\tools\\codegraph.cmd"
|
|
77
|
-
external_timeout_ms =
|
|
77
|
+
external_timeout_ms = 3000
|
|
78
78
|
|
|
79
79
|
[server]
|
|
80
80
|
transport = "stdio"
|
|
@@ -334,7 +334,7 @@ Common keys:
|
|
|
334
334
|
- `max_file_bytes = 2097152` (2 MiB per source file by default)
|
|
335
335
|
- `external_context = "auto"` (`"off"` keeps the built-in Lite provider only)
|
|
336
336
|
- `external_command = "C:\\tools\\codegraph.cmd"` (optional path when CodeGraph is not on `PATH`)
|
|
337
|
-
- `external_timeout_ms =
|
|
337
|
+
- `external_timeout_ms = 3000` (bounded local semantic-query timeout; values are clamped to 5 seconds)
|
|
338
338
|
|
|
339
339
|
Legacy YAML uses `codegraph.excludePatterns`, `codegraph.maxFileBytes`,
|
|
340
340
|
`codegraph.externalContext`, `codegraph.externalCommand`, and
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -13,8 +13,8 @@ Memorix is a TypeScript project built around:
|
|
|
13
13
|
|
|
14
14
|
## Current Development Baseline
|
|
15
15
|
|
|
16
|
-
The current release work targets the **1.8.
|
|
17
|
-
is kept at `1.8.
|
|
16
|
+
The current release work targets the **1.8.3 release line** and package metadata
|
|
17
|
+
is kept at `1.8.3` for this release commit.
|
|
18
18
|
|
|
19
19
|
Contributors should assume the following areas are part of the current release line:
|
|
20
20
|
|
|
@@ -96,6 +96,7 @@ npm run test:watch
|
|
|
96
96
|
|
|
97
97
|
```bash
|
|
98
98
|
memorix serve
|
|
99
|
+
node scripts/mcp-stdio-smoke.mjs
|
|
99
100
|
memorix background start
|
|
100
101
|
memorix status
|
|
101
102
|
```
|
package/docs/README.md
CHANGED
|
@@ -63,6 +63,7 @@ The public docs are organized by user intent:
|
|
|
63
63
|
| Topic | Document |
|
|
64
64
|
| --- | --- |
|
|
65
65
|
| MCP / CLI commands | [API_REFERENCE.md](API_REFERENCE.md) |
|
|
66
|
+
| MCP protocol discovery and compatibility boundaries | [API_REFERENCE.md](API_REFERENCE.md), [1.3.3 MCP Compatibility](1.3.3-MULTIMODAL-MEDIA-AND-MCP-COMPAT.md), and [1.8.0 Release Specification](1.8.0-RELEASE-CANDIDATE-SPEC.md) |
|
|
66
67
|
| Plugin, rules, hooks, skills, and MCP support matrix | [INTEGRATIONS.md](INTEGRATIONS.md) |
|
|
67
68
|
| TypeScript SDK | [../README.md#sdk](../README.md#sdk) |
|
|
68
69
|
| Workspace and rules sync | [API_REFERENCE.md § Workspace and Rules](API_REFERENCE.md#8-workspace-and-rules-tools) |
|
|
@@ -93,7 +94,7 @@ The public docs are organized by user intent:
|
|
|
93
94
|
| 1.2 honest Lite and optional semantic CodeGraph provider contract | [1.2 Provider Quality](1.2.0-PROVIDER-QUALITY.md) |
|
|
94
95
|
| 1.3 long-term memory model, source boundary, and lifecycle | [1.3 Memory Architecture](1.3-MEMORY-ARCHITECTURE.md) |
|
|
95
96
|
| Active context-control work | [1.2.2 Memory Control Plane](1.2.2-MEMORY-CONTROL-PLANE.md) |
|
|
96
|
-
| 1.8
|
|
97
|
+
| 1.8.3 stdio discovery startup and compatibility acceptance contract | [1.8.0 Release Specification](1.8.0-RELEASE-CANDIDATE-SPEC.md) |
|
|
97
98
|
| Historical cloud sync and multi-agent research | [CLOUD_SYNC_AND_MULTI_AGENT_RESEARCH.md](CLOUD_SYNC_AND_MULTI_AGENT_RESEARCH.md) |
|
|
98
99
|
| Known issues and old roadmap notes | [KNOWN_ISSUES_AND_ROADMAP.md](KNOWN_ISSUES_AND_ROADMAP.md) |
|
|
99
100
|
|
|
@@ -121,7 +122,7 @@ Historical/deep-reference documents may describe older designs. If they conflict
|
|
|
121
122
|
|
|
122
123
|
## Current Product Line
|
|
123
124
|
|
|
124
|
-
The current product line is **1.8.
|
|
125
|
+
The current product line is **1.8.3**. The authoritative acceptance contract is
|
|
125
126
|
[1.8.0 Release Specification](1.8.0-RELEASE-CANDIDATE-SPEC.md).
|
|
126
127
|
The current baseline has:
|
|
127
128
|
|
|
@@ -140,8 +141,13 @@ The current baseline has:
|
|
|
140
141
|
- `~/.memorix/config.toml` and project `memorix.toml` are the user-facing configuration model
|
|
141
142
|
- legacy `memorix.yml`, `.env`, and `config.json` files are still read for compatibility, but new setups should use TOML
|
|
142
143
|
|
|
143
|
-
For MCP compatibility, 1.8 preserves legacy
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
the
|
|
144
|
+
For MCP compatibility, 1.8.3 preserves legacy 2025-era initialize and
|
|
145
|
+
stateful Streamable HTTP behavior. `memorix serve` starts a bounded stdio
|
|
146
|
+
startup gate: pre-initialize 2026-07-28 `server/discover` is answered with a
|
|
147
|
+
complete discovery result, and other early JSON-RPC messages replay in order
|
|
148
|
+
through the pinned SDK transport. Versionless `tools/list` and `tools/call`
|
|
149
|
+
continue through the SDK's legacy result envelopes. Modern result metadata is
|
|
150
|
+
limited to the discovery response (`ttlMs: 0`, private cache scope); Tasks,
|
|
151
|
+
subscriptions, and list-response caching are unsupported. Stateless HTTP
|
|
152
|
+
project handles are a compatibility adapter, not a claim of complete 2026
|
|
153
|
+
conformance.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.3",
|
|
5
5
|
"description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"update-models": "npm --workspace @memorix/ai run update-models",
|
|
63
63
|
"start": "node dist/index.js",
|
|
64
64
|
"test": "vitest run --no-file-parallelism --maxWorkers=1 --minWorkers=1",
|
|
65
|
+
"test:mcp-stdio-smoke": "node scripts/mcp-stdio-smoke.mjs",
|
|
65
66
|
"test:watch": "vitest",
|
|
66
67
|
"test:llm-live": "MEMORIX_RUN_LIVE_LLM_TESTS=1 vitest run tests/integration/formation-llm-quality.test.ts",
|
|
67
68
|
"test:e2e": "vitest run tests/e2e/",
|
package/server.json
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.AVIDS2/memorix",
|
|
4
4
|
"title": "Memorix",
|
|
5
|
-
"description": "Local-first
|
|
5
|
+
"description": "Local-first project memory with legacy MCP and 2026 discovery compatibility.",
|
|
6
6
|
"repository": {
|
|
7
7
|
"url": "https://github.com/AVIDS2/memorix",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.8.
|
|
10
|
+
"version": "1.8.3",
|
|
11
11
|
"websiteUrl": "https://github.com/AVIDS2/memorix#readme",
|
|
12
12
|
"icons": [
|
|
13
13
|
{
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "memorix",
|
|
25
|
-
"version": "1.8.
|
|
25
|
+
"version": "1.8.3",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
|
@@ -40,6 +40,12 @@ export const EXPIRED_SESSION_TTL_MS = 10 * 60 * 1000;
|
|
|
40
40
|
/** Pinned SDK protocol accepted internally while the HTTP boundary speaks the current stateless contract. */
|
|
41
41
|
export const MCP_SDK_COMPAT_PROTOCOL_VERSION = '2025-11-25';
|
|
42
42
|
|
|
43
|
+
/** Modern discovery is allowed before a durable project handle exists. */
|
|
44
|
+
export function isServerDiscoverRequest(body: unknown): boolean {
|
|
45
|
+
return Boolean(body && typeof body === 'object' && !Array.isArray(body)
|
|
46
|
+
&& (body as { method?: unknown }).method === 'server/discover');
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
export function parseSessionTimeoutMs(raw: string | undefined): number {
|
|
44
50
|
const value = raw?.trim();
|
|
45
51
|
if (!value) return DEFAULT_SESSION_TIMEOUT_MS;
|
|
@@ -427,7 +433,7 @@ export default defineCommand({
|
|
|
427
433
|
? req.headers['mcp-project-root']
|
|
428
434
|
: undefined;
|
|
429
435
|
const root = persisted?.projectRoot ?? requestedRoot ?? projectRoot;
|
|
430
|
-
if (!persisted && !isInitializeRequest(body)) {
|
|
436
|
+
if (!persisted && !isInitializeRequest(body) && !isServerDiscoverRequest(body)) {
|
|
431
437
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
432
438
|
res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32000, message: 'Stateless MCP requests require Mcp-Project-Handle; initialize first.' }, id: null }));
|
|
433
439
|
return;
|
|
@@ -505,7 +511,9 @@ export default defineCommand({
|
|
|
505
511
|
|
|
506
512
|
const requestedProtocol = req.headers['mcp-protocol-version'];
|
|
507
513
|
const statelessRequested = req.headers['mcp-stateless'] === 'true'
|
|
508
|
-
|| requestedProtocol === CURRENT_MCP_PROTOCOL_VERSION
|
|
514
|
+
|| requestedProtocol === CURRENT_MCP_PROTOCOL_VERSION
|
|
515
|
+
|| typeof req.headers['mcp-project-handle'] === 'string'
|
|
516
|
+
|| isServerDiscoverRequest(body);
|
|
509
517
|
if (!sessionId && statelessRequested) {
|
|
510
518
|
await handleStatelessPost(req, res, body);
|
|
511
519
|
return;
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { defineCommand } from 'citty';
|
|
6
6
|
import { resolveToolProfile } from '../../server/tool-profile.js';
|
|
7
|
+
import { getMcpServerInfo } from '../../server/mcp-discovery.js';
|
|
8
|
+
import { StdioStartupGate } from '../../server/stdio-startup-gate.js';
|
|
7
9
|
import { mcpFileUriToPath } from '../mcp-root-path.js';
|
|
8
10
|
|
|
9
11
|
export default defineCommand({
|
|
@@ -29,6 +31,28 @@ export default defineCommand({
|
|
|
29
31
|
},
|
|
30
32
|
},
|
|
31
33
|
run: async ({ args }) => {
|
|
34
|
+
// Take ownership of stdin before project/config/runtime initialization. The
|
|
35
|
+
// gate answers handshake-free discovery and queues every other JSON-RPC line
|
|
36
|
+
// until the SDK transport is connected, so cold starts cannot lose requests.
|
|
37
|
+
const startupGate = new StdioStartupGate({
|
|
38
|
+
stdin: process.stdin,
|
|
39
|
+
stdout: process.stdout,
|
|
40
|
+
serverInfo: getMcpServerInfo(),
|
|
41
|
+
onError: (error) => {
|
|
42
|
+
console.error(`[memorix] stdio startup gate error: ${error.message}`);
|
|
43
|
+
// The stream is no longer safe to replay after an input-limit or I/O
|
|
44
|
+
// failure. Exit instead of leaving a half-connected MCP process alive.
|
|
45
|
+
process.exit(1);
|
|
46
|
+
},
|
|
47
|
+
onEnd: () => {
|
|
48
|
+
console.error('[memorix] stdin closed — exiting');
|
|
49
|
+
// The gate has already ended the transport input when ready. Set the
|
|
50
|
+
// exit code and let pending JSON-RPC stdout writes drain naturally.
|
|
51
|
+
process.exitCode = 0;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
startupGate.start();
|
|
55
|
+
|
|
32
56
|
const { StdioServerTransport } = await import(
|
|
33
57
|
'@modelcontextprotocol/sdk/server/stdio.js'
|
|
34
58
|
);
|
|
@@ -37,12 +61,6 @@ export default defineCommand({
|
|
|
37
61
|
const { homedir } = await import('node:os');
|
|
38
62
|
const { resolveServeProject } = await import('./serve-shared.js');
|
|
39
63
|
|
|
40
|
-
// Auto-exit when stdio pipe breaks (IDE closed) to prevent orphaned processes
|
|
41
|
-
process.stdin.on('end', () => {
|
|
42
|
-
console.error('[memorix] stdin closed — exiting');
|
|
43
|
-
process.exit(0);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
64
|
// Priority: explicit --cwd arg > MEMORIX_PROJECT_ROOT env > INIT_CWD (npm lifecycle) > process.cwd()
|
|
47
65
|
let safeCwd: string;
|
|
48
66
|
try { safeCwd = process.cwd(); } catch { safeCwd = homedir(); }
|
|
@@ -85,8 +103,9 @@ export default defineCommand({
|
|
|
85
103
|
? { toolProfile, deferProjectRuntimeInit: true }
|
|
86
104
|
: { allowUntrackedFallback: allowUntracked, deferProjectInitUntilBound: !allowUntracked, deferProjectRuntimeInit: true, toolProfile };
|
|
87
105
|
const { server, projectId, deferredInit, switchProject } = await createMemorixServer(projectRoot, undefined, undefined, serverOptions);
|
|
88
|
-
const transport = new StdioServerTransport();
|
|
106
|
+
const transport = new StdioServerTransport(startupGate.input, process.stdout);
|
|
89
107
|
await server.connect(transport);
|
|
108
|
+
startupGate.markReady();
|
|
90
109
|
|
|
91
110
|
console.error(`[memorix] MCP Server running on stdio (project: ${projectId})`);
|
|
92
111
|
console.error(`[memorix] Project root: ${detected?.rootPath ?? projectRoot}`);
|