memorix 1.5.1 → 1.6.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.
- package/CHANGELOG.md +47 -0
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/dist/cli/index.js +558 -102
- package/dist/cli/memcode.js +12 -1
- package/dist/index.js +221 -27
- package/dist/maintenance-runner.js +59 -11
- package/dist/media-audio-runner.js +59 -11
- package/dist/media-video-runner.js +59 -11
- package/dist/memcode-runtime/CHANGELOG.md +47 -0
- package/dist/sdk.js +220 -26
- package/dist/vector-backfill-runner.js +59 -11
- package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +1 -1
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +11 -3
- package/docs/API_REFERENCE.md +2 -2
- package/docs/DEVELOPMENT.md +5 -3
- package/docs/DOCKER.md +5 -1
- package/docs/PERFORMANCE.md +2 -2
- package/docs/README.md +3 -1
- package/docs/SETUP.md +8 -2
- package/package.json +1 -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/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 +2 -2
- package/src/cli/commands/agent-integrations.ts +9 -3
- package/src/cli/commands/codegraph.ts +48 -5
- package/src/cli/commands/context.ts +20 -4
- package/src/cli/commands/doctor.ts +46 -15
- package/src/cli/commands/hook.ts +13 -1
- package/src/cli/commands/resume.ts +4 -0
- package/src/cli/commands/serve-http.ts +68 -7
- package/src/cli/heap.ts +76 -0
- package/src/cli/invocation.ts +7 -4
- package/src/codegraph/auto-context.ts +4 -1
- package/src/codegraph/context-pack.ts +3 -1
- package/src/codegraph/external-provider.ts +93 -0
- package/src/embedding/provider.ts +76 -6
- package/src/hooks/handler.ts +9 -18
- package/src/hooks/installers/index.ts +1 -1
- package/src/hooks/lifecycle.ts +173 -0
- package/src/hooks/official-skills.ts +3 -3
- package/src/hooks/rules/memorix-agent-rules.md +1 -1
- package/src/knowledge/context-receipt.ts +41 -0
- package/src/knowledge/workflows.ts +6 -3
- package/src/knowledge/workset.ts +56 -2
- package/src/server.ts +15 -3
|
@@ -3206,6 +3206,7 @@ __export(provider_exports, {
|
|
|
3206
3206
|
MAX_INLINE_EMBEDDING_BYTES: () => MAX_INLINE_EMBEDDING_BYTES,
|
|
3207
3207
|
UnsupportedEmbeddingModalityError: () => UnsupportedEmbeddingModalityError,
|
|
3208
3208
|
getEmbeddingProvider: () => getEmbeddingProvider,
|
|
3209
|
+
getEmbeddingRuntimeHealth: () => getEmbeddingRuntimeHealth,
|
|
3209
3210
|
isEmbeddingExplicitlyDisabled: () => isEmbeddingExplicitlyDisabled,
|
|
3210
3211
|
isVectorSearchAvailable: () => isVectorSearchAvailable,
|
|
3211
3212
|
resetProvider: () => resetProvider,
|
|
@@ -3313,13 +3314,36 @@ function isTemporaryEmbeddingFailure(error) {
|
|
|
3313
3314
|
if (!(error instanceof Error)) return false;
|
|
3314
3315
|
return /embedding api error \(401\)/i.test(error.message) || /invalid_api_key/i.test(error.message) || /incorrect api key/i.test(error.message) || /unauthorized/i.test(error.message) || /embedding api timeout/i.test(error.message) || /embedding api error \((402|429|5\d\d)\)/i.test(error.message) || /quota exceeded/i.test(error.message) || /account balance/i.test(error.message) || /fetch failed/i.test(error.message) || /econnreset/i.test(error.message) || /econnrefused/i.test(error.message) || /temporarily unavailable/i.test(error.message);
|
|
3315
3316
|
}
|
|
3317
|
+
function classifyEmbeddingFailure(error) {
|
|
3318
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3319
|
+
if (/embedding api error \(401\)|invalid_api_key|incorrect api key|unauthorized/i.test(message)) {
|
|
3320
|
+
return "authorization";
|
|
3321
|
+
}
|
|
3322
|
+
if (/embedding api error \(402\)|quota exceeded|account balance/i.test(message)) {
|
|
3323
|
+
return "billing";
|
|
3324
|
+
}
|
|
3325
|
+
if (/embedding api timeout|abort/i.test(message)) {
|
|
3326
|
+
return "timeout";
|
|
3327
|
+
}
|
|
3328
|
+
return "upstream";
|
|
3329
|
+
}
|
|
3316
3330
|
function markTemporaryFailure(reason) {
|
|
3317
3331
|
provider = null;
|
|
3318
3332
|
initPromise = null;
|
|
3319
3333
|
lastInitWasTemporaryFailure = true;
|
|
3320
3334
|
lastFailureTimestamp = Date.now();
|
|
3335
|
+
lastEmbeddingFailure = {
|
|
3336
|
+
kind: classifyEmbeddingFailure(reason),
|
|
3337
|
+
occurredAt: lastFailureTimestamp
|
|
3338
|
+
};
|
|
3321
3339
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
3322
|
-
warnOnce(`[memorix] Embedding provider
|
|
3340
|
+
warnOnce(`[memorix] Embedding provider unavailable at runtime: ${message}`);
|
|
3341
|
+
}
|
|
3342
|
+
function markProviderReady(candidate) {
|
|
3343
|
+
provider = wrapProvider(candidate);
|
|
3344
|
+
lastInitWasTemporaryFailure = false;
|
|
3345
|
+
lastEmbeddingFailure = null;
|
|
3346
|
+
return provider;
|
|
3323
3347
|
}
|
|
3324
3348
|
async function createFastEmbedProvider() {
|
|
3325
3349
|
try {
|
|
@@ -3435,35 +3459,35 @@ async function getEmbeddingProvider(options = {}) {
|
|
|
3435
3459
|
if (mode === "fastembed") {
|
|
3436
3460
|
const initialized = await createFastEmbedProvider();
|
|
3437
3461
|
if (!initialized) return null;
|
|
3438
|
-
provider =
|
|
3462
|
+
provider = markProviderReady(initialized);
|
|
3439
3463
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3440
3464
|
return provider;
|
|
3441
3465
|
}
|
|
3442
3466
|
if (mode === "transformers") {
|
|
3443
3467
|
const initialized = await createTransformersProvider();
|
|
3444
3468
|
if (!initialized) return null;
|
|
3445
|
-
provider =
|
|
3469
|
+
provider = markProviderReady(initialized);
|
|
3446
3470
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3447
3471
|
return provider;
|
|
3448
3472
|
}
|
|
3449
3473
|
if (mode === "api") {
|
|
3450
3474
|
const initialized = await createAPIProvider(options);
|
|
3451
3475
|
if (!initialized) return null;
|
|
3452
|
-
provider =
|
|
3476
|
+
provider = markProviderReady(initialized);
|
|
3453
3477
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3454
3478
|
return provider;
|
|
3455
3479
|
}
|
|
3456
3480
|
if (hasAPIEmbeddingConfig()) {
|
|
3457
3481
|
const initialized = await createAPIProvider(options);
|
|
3458
3482
|
if (initialized) {
|
|
3459
|
-
provider =
|
|
3483
|
+
provider = markProviderReady(initialized);
|
|
3460
3484
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3461
3485
|
return provider;
|
|
3462
3486
|
}
|
|
3463
3487
|
}
|
|
3464
3488
|
const localFallback = await createLocalFallbackProvider();
|
|
3465
3489
|
if (localFallback) {
|
|
3466
|
-
provider =
|
|
3490
|
+
provider = markProviderReady(localFallback);
|
|
3467
3491
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
3468
3492
|
return provider;
|
|
3469
3493
|
}
|
|
@@ -3485,6 +3509,27 @@ async function isVectorSearchAvailable() {
|
|
|
3485
3509
|
const p = await getEmbeddingProvider();
|
|
3486
3510
|
return p !== null;
|
|
3487
3511
|
}
|
|
3512
|
+
function getEmbeddingRuntimeHealth() {
|
|
3513
|
+
const mode = getEmbeddingMode2();
|
|
3514
|
+
if (mode === "off") return { status: "disabled" };
|
|
3515
|
+
if (lastInitWasTemporaryFailure && lastEmbeddingFailure) {
|
|
3516
|
+
return {
|
|
3517
|
+
status: "degraded",
|
|
3518
|
+
failure: {
|
|
3519
|
+
kind: lastEmbeddingFailure.kind,
|
|
3520
|
+
occurredAt: new Date(lastEmbeddingFailure.occurredAt).toISOString(),
|
|
3521
|
+
retryAfterMs: Math.max(0, RETRY_COOLDOWN_MS - (Date.now() - lastEmbeddingFailure.occurredAt))
|
|
3522
|
+
}
|
|
3523
|
+
};
|
|
3524
|
+
}
|
|
3525
|
+
if (provider) {
|
|
3526
|
+
return {
|
|
3527
|
+
status: "ready",
|
|
3528
|
+
provider: { name: provider.name, dimensions: provider.dimensions }
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
return { status: "uninitialized" };
|
|
3532
|
+
}
|
|
3488
3533
|
function isEmbeddingExplicitlyDisabled() {
|
|
3489
3534
|
return getEmbeddingMode2() === "off";
|
|
3490
3535
|
}
|
|
@@ -3493,10 +3538,11 @@ function resetProvider() {
|
|
|
3493
3538
|
initPromise = null;
|
|
3494
3539
|
lastInitWasTemporaryFailure = false;
|
|
3495
3540
|
lastFailureTimestamp = 0;
|
|
3541
|
+
lastEmbeddingFailure = null;
|
|
3496
3542
|
lastEmbeddingWarning = "";
|
|
3497
3543
|
lastEmbeddingWarningAt = 0;
|
|
3498
3544
|
}
|
|
3499
|
-
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
3545
|
+
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
3500
3546
|
var init_provider = __esm({
|
|
3501
3547
|
"src/embedding/provider.ts"() {
|
|
3502
3548
|
"use strict";
|
|
@@ -3521,6 +3567,7 @@ var init_provider = __esm({
|
|
|
3521
3567
|
provider = null;
|
|
3522
3568
|
initPromise = null;
|
|
3523
3569
|
lastInitWasTemporaryFailure = false;
|
|
3570
|
+
lastEmbeddingFailure = null;
|
|
3524
3571
|
lastEmbeddingWarning = "";
|
|
3525
3572
|
lastEmbeddingWarningAt = 0;
|
|
3526
3573
|
WARNING_COOLDOWN_MS = 3e4;
|
|
@@ -147780,6 +147827,7 @@ var init_task_lens = __esm({
|
|
|
147780
147827
|
var workflows_exports = {};
|
|
147781
147828
|
__export(workflows_exports, {
|
|
147782
147829
|
WORKFLOW_ADAPTER_TARGETS: () => WORKFLOW_ADAPTER_TARGETS,
|
|
147830
|
+
WORKFLOW_AGENT_TARGETS: () => WORKFLOW_AGENT_TARGETS,
|
|
147783
147831
|
applyWorkflowAdapter: () => applyWorkflowAdapter,
|
|
147784
147832
|
importWindsurfWorkflows: () => importWindsurfWorkflows,
|
|
147785
147833
|
parseWorkflowMarkdown: () => parseWorkflowMarkdown,
|
|
@@ -147845,7 +147893,7 @@ function optionalStatus(data) {
|
|
|
147845
147893
|
return value;
|
|
147846
147894
|
}
|
|
147847
147895
|
function normalizeAgents(values) {
|
|
147848
|
-
const unknown3 = values.filter((value) => !
|
|
147896
|
+
const unknown3 = values.filter((value) => !WORKFLOW_AGENT_TARGETS.includes(value));
|
|
147849
147897
|
if (unknown3.length > 0) {
|
|
147850
147898
|
throw new Error("workflow frontmatter has unsupported agent: " + unknown3[0]);
|
|
147851
147899
|
}
|
|
@@ -148121,7 +148169,7 @@ function taskScore(workflow, task) {
|
|
|
148121
148169
|
return { score, reasons: [...new Set(reasons)] };
|
|
148122
148170
|
}
|
|
148123
148171
|
function selectWorkflows(input) {
|
|
148124
|
-
const selected = input.workflows.filter((workflow) => workflow.status === "active").map((workflow) => {
|
|
148172
|
+
const selected = input.workflows.filter((workflow) => workflow.status === "active" && (!input.agent || workflow.compatibleAgents.includes(input.agent))).map((workflow) => {
|
|
148125
148173
|
const match2 = taskScore(workflow, input.task);
|
|
148126
148174
|
const cautions = input.store && input.projectId ? input.store.recentFailureCautions(input.projectId, workflow.id) : [];
|
|
148127
148175
|
return {
|
|
@@ -148401,7 +148449,7 @@ async function selectWorkspaceWorkflows(input) {
|
|
|
148401
148449
|
errors: synced.errors
|
|
148402
148450
|
};
|
|
148403
148451
|
}
|
|
148404
|
-
var
|
|
148452
|
+
var WORKFLOW_AGENT_TARGETS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
|
|
148405
148453
|
var init_workflows2 = __esm({
|
|
148406
148454
|
"src/knowledge/workflows.ts"() {
|
|
148407
148455
|
"use strict";
|
|
@@ -148413,7 +148461,7 @@ var init_workflows2 = __esm({
|
|
|
148413
148461
|
init_workflow_store();
|
|
148414
148462
|
init_outcome_store();
|
|
148415
148463
|
init_task_lens();
|
|
148416
|
-
|
|
148464
|
+
WORKFLOW_AGENT_TARGETS = [
|
|
148417
148465
|
"windsurf",
|
|
148418
148466
|
"cursor",
|
|
148419
148467
|
"claude-code",
|
|
@@ -26,7 +26,7 @@ database, a transcript archive, or another default MCP tool.
|
|
|
26
26
|
| Start a new, unrelated task | A bounded task Workset. No automatic prior-session text. |
|
|
27
27
|
| Continue or take over prior work | The same Workset plus a bounded prior-work projection. |
|
|
28
28
|
| MCP is available | Use the existing `memorix_project_context` tool with the real task. |
|
|
29
|
-
| MCP is unavailable | Make one CLI call: `memorix resume "<task>" --json` for continuation or `memorix context "<task>" --json` for a new task. |
|
|
29
|
+
| MCP is unavailable | Make one CLI call: `memorix resume "<task>" --brief-json` for continuation or `memorix context "<task>" --brief-json` for a new task. `--json` remains the detailed diagnostic form. |
|
|
30
30
|
| Claude Code hook is installed | An explicit continuation prompt receives the same bounded prior-work Workset through the official `UserPromptSubmit` context channel, unless `memory.inject = "silent"`. |
|
|
31
31
|
| Read-only assessment | The brief may be read, but Memorix does not persist a new observation unless the user explicitly asks to save one. |
|
|
32
32
|
| Need a deliberate session handoff | Keep using the explicit session/handoff surfaces; this projection does not replace them. |
|
|
@@ -58,7 +58,7 @@ An unbound terminal is intentionally project-scoped: it reads and writes project
|
|
|
58
58
|
|
|
59
59
|
Long-term memory is a reviewed layer, not a generic global note store. `memorix memory long-term add` creates a candidate; `qualify` and `approve` are explicit audit transitions before it can appear in a bounded task Workset. A project-derived observation, code fact, Git fact, test, session, Claim, or workflow stays project-bound. Only manual or user-confirmed `user + portable` records can cross local projects for the same local installation identity. When one is task-matched in a Workset, it is intentionally usable background even if its origin differs; use its `durable:<id>` reference with `memorix_detail` only when the full record is needed. Use `archive` or `supersede` when a durable item is no longer current.
|
|
60
60
|
|
|
61
|
-
`--cwd <git-project>` is the canonical way to run direct CLI work from outside a repository. It changes the command's project anchor only; Git remains the source of truth for final project identity.
|
|
61
|
+
`--cwd <git-project>` is the canonical way to run direct CLI work from outside a repository. It changes the command's project anchor only; Git remains the source of truth for final project identity. Existing directories are canonicalized to their real filesystem paths before binding, which prevents macOS aliases such as `/var` and `/private/var` from splitting one project or hiding a Claude local MCP entry.
|
|
62
62
|
|
|
63
63
|
Use MCP when:
|
|
64
64
|
|
|
@@ -164,7 +164,12 @@ Do not assume the HTTP connection alone tells Memorix which project the user mea
|
|
|
164
164
|
|
|
165
165
|
The HTTP service is normally started with `memorix background start`; the same project-binding rules apply when you run `memorix serve-http --port 3211` in the foreground.
|
|
166
166
|
|
|
167
|
-
HTTP MCP sessions idle out after
|
|
167
|
+
HTTP MCP sessions idle out after 12 hours by default. An expired session now
|
|
168
|
+
receives a fast `404` reinitialize hint instead of a generic transport failure.
|
|
169
|
+
If the user's HTTP MCP client needs a longer idle period, set
|
|
170
|
+
`MEMORIX_SESSION_TIMEOUT_MS` before starting or restarting the service.
|
|
171
|
+
Example: `MEMORIX_SESSION_TIMEOUT_MS=86400000` keeps sessions alive for 24
|
|
172
|
+
hours. Set it to `0` only when the host has its own reliable session cleanup.
|
|
168
173
|
|
|
169
174
|
### Do not confuse project config and global config
|
|
170
175
|
|
|
@@ -702,7 +707,10 @@ If it shows "Not running" or "dead":
|
|
|
702
707
|
memorix background ensure
|
|
703
708
|
```
|
|
704
709
|
|
|
705
|
-
If the client is connected but starts failing after
|
|
710
|
+
If the client is connected but starts failing after a long inactive period,
|
|
711
|
+
check for the explicit stale-session `404` reinitialize hint rather than
|
|
712
|
+
treating it as a project-binding failure. The default idle window is 12 hours;
|
|
713
|
+
restart with a longer window only when the client genuinely needs it:
|
|
706
714
|
|
|
707
715
|
```powershell
|
|
708
716
|
$env:MEMORIX_SESSION_TIMEOUT_MS = "86400000"
|
package/docs/API_REFERENCE.md
CHANGED
|
@@ -115,7 +115,7 @@ MCP:
|
|
|
115
115
|
- `memorix_context_pack` builds a task-specific packet with reliable current memories, lower-trust unbound memories, current code facts, freshness warnings, suggested reads, and suggested verification.
|
|
116
116
|
- `memorix_graph_context` is an advanced compact graph overview. It starts from a small task-matched baseline and can add only directly related, evidence-governed memories. Its `Graph evidence` section includes relation provenance rather than recursively dumping graph neighbors.
|
|
117
117
|
|
|
118
|
-
`memorix context` defaults to `--refresh auto`, so first use can seed Code State without a separate manual `memorix codegraph refresh`. Its brief puts live package/changelog/Git facts before memory hints and flags an old `ACTIVE_WORK.md`, legacy `progress.txt`, or legacy dev-log note as historical when it predates the latest changelog, so agents should treat current facts as the source of truth when files disagree. Task lenses keep the packet shaped to the work: bugfix briefs prefer failing tests and repros, release briefs prefer metadata/changelog/package checks, and onboarding briefs prefer docs and entry points while hiding unrelated suspect details. Continuation delivery is separate from the task lens: continuation language in `memorix_project_context` enables a bounded prior-work projection, and `memorix resume "..."` makes that choice explicit. It includes only the latest useful session summary, up to three readable durable memories, and at most one recent source-labelled compact checkpoint. Checkpoints are host lifecycle evidence, not durable memory or transcript backups; ordinary new tasks do not receive historical-session context. A completed MCP brief is the default retrieval boundary: search, detail, or Context Pack should expand it only for a named missing fact or an explicit request for deeper history. Use `--refresh never` for read-only inspection and `--refresh always` when you want to force a fresh scan.
|
|
118
|
+
`memorix context` defaults to `--refresh auto`, so first use can seed Code State without a separate manual `memorix codegraph refresh`. Its brief puts live package/changelog/Git facts before memory hints and flags an old `ACTIVE_WORK.md`, legacy `progress.txt`, or legacy dev-log note as historical when it predates the latest changelog, so agents should treat current facts as the source of truth when files disagree. Use `--brief-json` for the same bounded brief plus a receipt of selected and omitted evidence; `--json` remains the detailed legacy diagnostic form. Task lenses keep the packet shaped to the work: bugfix briefs prefer failing tests and repros, release briefs prefer metadata/changelog/package checks, and onboarding briefs prefer docs and entry points while hiding unrelated suspect details. Continuation delivery is separate from the task lens: continuation language in `memorix_project_context` enables a bounded prior-work projection, and `memorix resume "..."` makes that choice explicit. It includes only the latest useful session summary, up to three readable durable memories, and at most one recent source-labelled compact checkpoint. Checkpoints are host lifecycle evidence, not durable memory or transcript backups; ordinary new tasks do not receive historical-session context. A completed MCP brief is the default retrieval boundary: search, detail, or Context Pack should expand it only for a named missing fact or an explicit request for deeper history. Use `--refresh never` for read-only inspection and `--refresh always` when you want to force a fresh scan.
|
|
119
119
|
|
|
120
120
|
Project-specific generated, vendored, or cache paths can be excluded from Code State with `[codegraph].exclude_patterns` in `memorix.toml` or `~/.memorix/config.toml` (`codegraph.excludePatterns` in legacy YAML). User patterns extend the built-in excludes and are applied to indexing, Project Context suggested reads, and Context Pack suggested reads.
|
|
121
121
|
|
|
@@ -652,7 +652,7 @@ Coordination state is opt-in project state for tasks, handoff messages, locks, a
|
|
|
652
652
|
|
|
653
653
|
Runtime environment:
|
|
654
654
|
|
|
655
|
-
- `MEMORIX_SESSION_TIMEOUT_MS` — HTTP MCP session idle timeout in milliseconds. Default: `
|
|
655
|
+
- `MEMORIX_SESSION_TIMEOUT_MS` — HTTP MCP session idle timeout in milliseconds. Default: `43200000` (12 hours). Expired session IDs receive a fast `404` with a reinitialize hint. Increase this for long-lived clients, for example `86400000` for 24 hours; set `0` only when the host provides reliable lifecycle cleanup.
|
|
656
656
|
|
|
657
657
|
### `team_manage`
|
|
658
658
|
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -326,9 +326,11 @@ Notes:
|
|
|
326
326
|
- `server.json` is the official MCP Registry record. Keep it synchronized with
|
|
327
327
|
`package.json` by running `npm run sync:mcp-registry`; verify it with
|
|
328
328
|
`npm run check:mcp-registry` before a release.
|
|
329
|
-
- The GitHub publish workflow
|
|
330
|
-
publish the same verified version to the official MCP Registry.
|
|
331
|
-
|
|
329
|
+
- The GitHub publish workflow can publish npm first, then uses GitHub OIDC to
|
|
330
|
+
publish the same verified version to the official MCP Registry. When npm was
|
|
331
|
+
already published manually, run the workflow with `publish_npm=false` to
|
|
332
|
+
publish only the Registry record. It does not require a Registry token or
|
|
333
|
+
private key in repository secrets.
|
|
332
334
|
- `@memorix/ai`, `@memorix/agent-core`, `@memorix/tui`, and `@memorix/memcode` are internal workspaces. Their code is bundled into the root `memorix` distribution and they must stay `private` unless the project deliberately establishes an owned npm scope and a separate package-support contract.
|
|
333
335
|
- npm publish is usually manual, especially when 2FA is enabled
|
|
334
336
|
- GitHub release automation should not be treated as a substitute for manual runtime validation
|
package/docs/DOCKER.md
CHANGED
|
@@ -128,7 +128,11 @@ docker compose ps
|
|
|
128
128
|
|
|
129
129
|
### HTTP session idle timeout
|
|
130
130
|
|
|
131
|
-
The HTTP MCP transport defaults to a
|
|
131
|
+
The HTTP MCP transport defaults to a 12-hour idle timeout. The example
|
|
132
|
+
`compose.yaml` raises this to 24 hours for IDEs that keep one HTTP MCP session
|
|
133
|
+
open while the user reads diffs, runs tests, or switches focus. When a session
|
|
134
|
+
does expire, the service returns a fast `404` so the client can initialize a
|
|
135
|
+
fresh MCP session.
|
|
132
136
|
|
|
133
137
|
To change it:
|
|
134
138
|
|
package/docs/PERFORMANCE.md
CHANGED
|
@@ -71,7 +71,7 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
71
71
|
|
|
72
72
|
| Knob | Default | Use When |
|
|
73
73
|
| --- | --- | --- |
|
|
74
|
-
| `MEMORIX_SESSION_TIMEOUT_MS` | `
|
|
74
|
+
| `MEMORIX_SESSION_TIMEOUT_MS` | `43200000` (12 h) | Set a shorter GC window for supervised clients, or `0` to disable idle session GC |
|
|
75
75
|
| `MEMORIX_FORMATION_TIMEOUT_MS` | `12000` (12 s) | Raise when LLM-backed formation should outlive slow proxy/provider hops |
|
|
76
76
|
| `MEMORIX_LLM_API_KEY` / `OPENAI_API_KEY` | unset | Enable LLM-backed enrichment, extraction, rerank, or skill generation |
|
|
77
77
|
| `MEMORIX_LLM_TIMEOUT_MS` | `30000` (30 s) | Bound a single LLM-backed extraction/resolve call |
|
|
@@ -86,7 +86,7 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
86
86
|
## Operator Guidance
|
|
87
87
|
|
|
88
88
|
- For memory-only use, prefer stdio MCP or a lightweight `memorix_session_start`; do not join orchestration coordination state by default.
|
|
89
|
-
-
|
|
89
|
+
- HTTP sessions now default to 12 hours and return a fast `404` with a reinitialize hint after expiry. Set `MEMORIX_SESSION_TIMEOUT_MS=0` only when the host has its own reliable lifecycle cleanup; otherwise retain a bounded GC window.
|
|
90
90
|
- If LLM-backed formation is timing out against a slow proxy/provider, raise `MEMORIX_FORMATION_TIMEOUT_MS` and keep it higher than `MEMORIX_LLM_TIMEOUT_MS`, because the full pipeline can include multiple LLM-backed stages.
|
|
91
91
|
- For Docker, use it when you want a managed HTTP service. Do not use image size alone as the runtime memory estimate.
|
|
92
92
|
- For orchestrated subagent work, expect CPU and disk activity proportional to the spawned agents and verification commands.
|
package/docs/README.md
CHANGED
|
@@ -117,11 +117,13 @@ Historical/deep-reference documents may describe older designs. If they conflict
|
|
|
117
117
|
|
|
118
118
|
## Current Product Line
|
|
119
119
|
|
|
120
|
-
The released product is on the **1.
|
|
120
|
+
The released product is on the **1.6 line**. The current baseline has:
|
|
121
121
|
|
|
122
122
|
- `memorix setup --agent <agent> --global` is the default agent integration command
|
|
123
123
|
- `memorix serve` is the manual stdio MCP server for external agents
|
|
124
124
|
- `memorix background start` runs the shared HTTP MCP service and dashboard
|
|
125
|
+
- context and CodeGraph commands return bounded task receipts before detailed diagnostics
|
|
126
|
+
- HTTP MCP sessions have a bounded, explicit lifecycle and fail with a reinitialize hint after expiry
|
|
125
127
|
- `memorix integrate --agent <agent>` and `memorix hooks install --agent <agent>` remain manual/fallback generation commands
|
|
126
128
|
- `memorix` / `memcode` open memcode, the bundled terminal agent that uses the same Memorix memory pool
|
|
127
129
|
- `~/.memorix/config.toml` and project `memorix.toml` are the user-facing configuration model
|
package/docs/SETUP.md
CHANGED
|
@@ -42,7 +42,7 @@ Memorix project identity comes from Git. Open or initialize a real Git repositor
|
|
|
42
42
|
git init
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
`projectRoot` and current working directory are detection anchors. The final project identity is derived from the real Git root and remote metadata.
|
|
45
|
+
`projectRoot` and current working directory are detection anchors. The final project identity is derived from the real Git root and remote metadata. Memorix resolves an existing project directory to its real filesystem path before binding it, so equivalent macOS paths such as `/var/...` and `/private/var/...` do not create separate project or Claude local-MCP entries.
|
|
46
46
|
|
|
47
47
|
---
|
|
48
48
|
|
|
@@ -247,13 +247,19 @@ Generic HTTP MCP config:
|
|
|
247
247
|
|
|
248
248
|
For multi-project HTTP usage, agents should call `memorix_session_start(projectRoot=...)` with the absolute workspace path when available. This prevents cross-project drift when the background service is shared.
|
|
249
249
|
|
|
250
|
-
HTTP sessions idle out after
|
|
250
|
+
HTTP sessions idle out after 12 hours by default. An expired session receives a
|
|
251
|
+
fast `404` that tells the client to initialize again, rather than being treated
|
|
252
|
+
as a project-binding failure. For clients that intentionally need a longer
|
|
253
|
+
window:
|
|
251
254
|
|
|
252
255
|
```powershell
|
|
253
256
|
$env:MEMORIX_SESSION_TIMEOUT_MS = "86400000"
|
|
254
257
|
memorix background restart
|
|
255
258
|
```
|
|
256
259
|
|
|
260
|
+
Set `MEMORIX_SESSION_TIMEOUT_MS=0` only when the host has its own reliable
|
|
261
|
+
session cleanup; otherwise keep a bounded idle window.
|
|
262
|
+
|
|
257
263
|
### Option E: Docker HTTP service
|
|
258
264
|
|
|
259
265
|
```bash
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.1",
|
|
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,
|
package/server.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"url": "https://github.com/AVIDS2/memorix",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.6.1",
|
|
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.
|
|
25
|
+
"version": "1.6.1",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
2
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
3
3
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { basename, dirname, resolve } from 'node:path';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
@@ -580,11 +580,17 @@ function sanitizeServer(server: MCPServerEntry): AgentMcpCheck['server'] {
|
|
|
580
580
|
}
|
|
581
581
|
|
|
582
582
|
function normalizeProjectKey(projectRoot: string): string {
|
|
583
|
-
return
|
|
583
|
+
return canonicalProjectPath(projectRoot).toLowerCase();
|
|
584
584
|
}
|
|
585
585
|
|
|
586
586
|
function defaultClaudeProjectKey(projectRoot: string): string {
|
|
587
|
-
return
|
|
587
|
+
return canonicalProjectPath(projectRoot);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function canonicalProjectPath(projectRoot: string): string {
|
|
591
|
+
const resolved = resolve(projectRoot);
|
|
592
|
+
const canonical = existsSync(resolved) ? realpathSync(resolved) : resolved;
|
|
593
|
+
return canonical.replace(/\\/g, '/');
|
|
588
594
|
}
|
|
589
595
|
|
|
590
596
|
function getClaudeLocalConfigPath(): string {
|
|
@@ -5,11 +5,14 @@ import { assembleContextPackForTask, attachTaskWorkset, buildContextPackPrompt }
|
|
|
5
5
|
import { backfillMissingObservationCodeRefs } from '../../codegraph/binder.js';
|
|
6
6
|
import { collectCurrentProjectFacts, formatGitFact } from '../../codegraph/current-facts.js';
|
|
7
7
|
import { resolveTaskLens } from '../../codegraph/task-lens.js';
|
|
8
|
-
import { getExternalCodeGraphContext, inspectExternalCodeGraph } from '../../codegraph/external-provider.js';
|
|
8
|
+
import { getExternalCodeGraphContext, inspectExternalCodeGraph, runExternalCodeGraphLifecycle } from '../../codegraph/external-provider.js';
|
|
9
9
|
import type { CodeGraphProviderQuality } from '../../codegraph/types.js';
|
|
10
10
|
import { getResolvedConfig } from '../../config/resolved-config.js';
|
|
11
|
+
import { buildBoundedContextReceipt } from '../../knowledge/context-receipt.js';
|
|
12
|
+
import { WORKFLOW_AGENT_TARGETS } from '../../knowledge/workflows.js';
|
|
11
13
|
import { getAllObservations } from '../../memory/observations.js';
|
|
12
14
|
import { filterReadableObservations } from '../../memory/visibility.js';
|
|
15
|
+
import type { AgentTarget } from '../../types.js';
|
|
13
16
|
import { emitError, emitResult, getCliProjectContext, parsePositiveInt } from './operator-shared.js';
|
|
14
17
|
|
|
15
18
|
function formatSnapshotStatus(status: ReturnType<CodeGraphStore['status']>): string[] {
|
|
@@ -48,6 +51,7 @@ function formatStatus(status: ReturnType<CodeGraphStore['status']>, quality?: Co
|
|
|
48
51
|
`- Persistent provider: ${status.provider} (heuristic local index)`,
|
|
49
52
|
`- External semantic CodeGraph: ${quality.external.state}`
|
|
50
53
|
+ (quality.external.reason ? ` (${quality.external.reason})` : ''),
|
|
54
|
+
`- Task-scoped provider: ${quality.selected} (${quality.selectedQuality})`,
|
|
51
55
|
]
|
|
52
56
|
: []),
|
|
53
57
|
].join('\n');
|
|
@@ -57,6 +61,8 @@ function formatUsageHint(): string {
|
|
|
57
61
|
return [
|
|
58
62
|
'Usage:',
|
|
59
63
|
' memorix codegraph refresh',
|
|
64
|
+
' memorix codegraph init # explicit local CodeGraph initialization',
|
|
65
|
+
' memorix codegraph sync # explicit local CodeGraph synchronization',
|
|
60
66
|
' memorix codegraph status --json',
|
|
61
67
|
' memorix codegraph diff [--from <snapshot>] [--to <snapshot>]',
|
|
62
68
|
' memorix codegraph impact --path <relative-source-path>',
|
|
@@ -116,25 +122,34 @@ function compactFacts(project: { rootPath: string }): { facts: string[]; dirty:
|
|
|
116
122
|
return { facts, dirty: current.git.dirty };
|
|
117
123
|
}
|
|
118
124
|
|
|
125
|
+
function coerceAgentTarget(input?: string): AgentTarget | undefined {
|
|
126
|
+
const value = input?.trim().toLowerCase();
|
|
127
|
+
if (!value) return undefined;
|
|
128
|
+
if (WORKFLOW_AGENT_TARGETS.includes(value as AgentTarget)) return value as AgentTarget;
|
|
129
|
+
throw new Error('agent must be one of: ' + WORKFLOW_AGENT_TARGETS.join(', '));
|
|
130
|
+
}
|
|
131
|
+
|
|
119
132
|
export default defineCommand({
|
|
120
133
|
meta: {
|
|
121
134
|
name: 'codegraph',
|
|
122
135
|
description: 'Inspect and refresh CodeGraph Memory for the current project',
|
|
123
136
|
},
|
|
124
137
|
args: {
|
|
125
|
-
action: { type: 'string', description: 'Action: status, refresh, diff, impact, or context-pack' },
|
|
138
|
+
action: { type: 'string', description: 'Action: status, refresh, diff, impact, init, sync, or context-pack' },
|
|
126
139
|
task: { type: 'string', description: 'Task text for context-pack' },
|
|
127
140
|
path: { type: 'string', description: 'Indexed relative source path for impact' },
|
|
128
141
|
limit: { type: 'string', description: 'Max active memories to inspect for context-pack' },
|
|
129
142
|
from: { type: 'string', description: 'Earlier CodeGraph snapshot id for diff' },
|
|
130
143
|
to: { type: 'string', description: 'Later CodeGraph snapshot id for diff' },
|
|
131
144
|
impactLimit: { type: 'string', description: 'Maximum graph relations to inspect for diff impact' },
|
|
145
|
+
agent: { type: 'string', description: 'Optional target agent for compatible workflow selection' },
|
|
132
146
|
json: { type: 'boolean', description: 'Emit machine-readable JSON output' },
|
|
147
|
+
briefJson: { type: 'boolean', description: 'Emit only the bounded agent brief and receipt JSON for context-pack' },
|
|
133
148
|
},
|
|
134
149
|
run: async ({ args }) => {
|
|
135
150
|
const positional = (args._ as string[]) ?? [];
|
|
136
151
|
const action = (positional[0] || (args.action as string | undefined) || 'status').toLowerCase();
|
|
137
|
-
const asJson = !!args.json;
|
|
152
|
+
const asJson = !!args.json || !!args.briefJson;
|
|
138
153
|
|
|
139
154
|
try {
|
|
140
155
|
const { project, dataDir, reader } = await getCliProjectContext();
|
|
@@ -206,6 +221,27 @@ export default defineCommand({
|
|
|
206
221
|
return;
|
|
207
222
|
}
|
|
208
223
|
|
|
224
|
+
case 'init':
|
|
225
|
+
case 'sync': {
|
|
226
|
+
const lifecycle = await runExternalCodeGraphLifecycle({
|
|
227
|
+
action,
|
|
228
|
+
projectRoot: project.rootPath,
|
|
229
|
+
mode: codegraphConfig.externalContext,
|
|
230
|
+
command: codegraphConfig.externalCommand,
|
|
231
|
+
timeoutMs: codegraphConfig.externalTimeoutMs,
|
|
232
|
+
});
|
|
233
|
+
emitResult(
|
|
234
|
+
{ project, lifecycle, providerQuality: lifecycle.quality },
|
|
235
|
+
[
|
|
236
|
+
lifecycle.message,
|
|
237
|
+
`- External semantic CodeGraph: ${lifecycle.health.state}`,
|
|
238
|
+
`- Task-scoped provider: ${lifecycle.quality.selected} (${lifecycle.quality.selectedQuality})`,
|
|
239
|
+
].join('\n'),
|
|
240
|
+
asJson,
|
|
241
|
+
);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
209
245
|
case 'diff': {
|
|
210
246
|
const snapshots = store.listSnapshots(project.id, 2);
|
|
211
247
|
const fromSnapshotId = (args.from as string | undefined)?.trim() || snapshots[1]?.id;
|
|
@@ -299,13 +335,20 @@ export default defineCommand({
|
|
|
299
335
|
? { runtimeCautions: [{ kind: 'external-codegraph-fallback' as const, message: external.caution }] }
|
|
300
336
|
: {}),
|
|
301
337
|
reader,
|
|
338
|
+
agent: coerceAgentTarget(args.agent as string | undefined),
|
|
302
339
|
});
|
|
303
|
-
emitResult(
|
|
340
|
+
emitResult(
|
|
341
|
+
args.briefJson && pack.workset
|
|
342
|
+
? buildBoundedContextReceipt({ workset: pack.workset, providerQuality: external.quality })
|
|
343
|
+
: { project, pack, providerQuality: external.quality },
|
|
344
|
+
buildContextPackPrompt(pack),
|
|
345
|
+
asJson,
|
|
346
|
+
);
|
|
304
347
|
return;
|
|
305
348
|
}
|
|
306
349
|
|
|
307
350
|
default:
|
|
308
|
-
emitError(`unknown codegraph action "${action}". Use "status", "refresh", "diff", "impact", or "context-pack".`, asJson);
|
|
351
|
+
emitError(`unknown codegraph action "${action}". Use "status", "refresh", "init", "sync", "diff", "impact", or "context-pack".`, asJson);
|
|
309
352
|
}
|
|
310
353
|
} catch (error) {
|
|
311
354
|
emitError(error instanceof Error ? error.message : String(error), asJson);
|