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
package/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
import {spawnSync as __ms} from "node:child_process";
|
|
3
3
|
import {fileURLToPath as __fu} from "node:url";
|
|
4
4
|
if(!process.env.__MEMORIX_HEAP){process.env.__MEMORIX_HEAP="1";
|
|
5
|
-
|
|
5
|
+
const __args=process.argv.slice(2);
|
|
6
|
+
let __cmd;
|
|
7
|
+
for(let __i=0;__i<__args.length;__i++){
|
|
8
|
+
const __a=__args[__i];
|
|
9
|
+
if(__a==="--")break;
|
|
10
|
+
if(__a.startsWith("-")){if(!__a.includes("=")&&__args[__i+1]&&!__args[__i+1].startsWith("-"))__i++;continue;}
|
|
11
|
+
__cmd=__a;break;}
|
|
12
|
+
const __fallback=__cmd==="hook"?512:4096;
|
|
13
|
+
const __raw=__cmd==="hook"?process.env.MEMORIX_HOOK_HEAP_MB:process.env.MEMORIX_HEAP_MB;
|
|
14
|
+
const __n=Number(__raw);
|
|
15
|
+
const __heap=(Number.isFinite(__n)&&__n>=64&&__n<=16384)?String(Math.floor(__n)):String(__fallback);
|
|
16
|
+
let r=__ms(process.execPath,["--max-old-space-size="+__heap,__fu(import.meta.url),...process.argv.slice(2)],{stdio:"inherit",env:process.env,windowsHide:true});
|
|
6
17
|
process.exit(r.status??1);}
|
|
7
18
|
import {createRequire as __memorix_cjsRequire} from "module";
|
|
8
19
|
const require = __memorix_cjsRequire(import.meta.url);
|
|
@@ -3221,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
|
|
|
3221
3232
|
|
|
3222
3233
|
// src/cli/version.ts
|
|
3223
3234
|
function getCliVersion() {
|
|
3224
|
-
return true ? "1.
|
|
3235
|
+
return true ? "1.6.1" : pkg.version;
|
|
3225
3236
|
}
|
|
3226
3237
|
var init_version = __esm({
|
|
3227
3238
|
"src/cli/version.ts"() {
|
|
@@ -3231,7 +3242,7 @@ var init_version = __esm({
|
|
|
3231
3242
|
});
|
|
3232
3243
|
|
|
3233
3244
|
// src/cli/invocation.ts
|
|
3234
|
-
import { existsSync as existsSync2, statSync } from "fs";
|
|
3245
|
+
import { existsSync as existsSync2, realpathSync, statSync } from "fs";
|
|
3235
3246
|
import { resolve as resolve2 } from "path";
|
|
3236
3247
|
function readOption(argument) {
|
|
3237
3248
|
const equals = argument.indexOf("=");
|
|
@@ -3261,10 +3272,11 @@ function normalizeCliInvocation(argv2 = process.argv) {
|
|
|
3261
3272
|
if (inlineValue === void 0) index += 1;
|
|
3262
3273
|
}
|
|
3263
3274
|
if (invocation.projectRoot) {
|
|
3264
|
-
const
|
|
3265
|
-
if (!existsSync2(
|
|
3266
|
-
throw new Error(`CLI project directory does not exist: ${
|
|
3275
|
+
const resolvedProjectRoot = resolve2(invocation.projectRoot);
|
|
3276
|
+
if (!existsSync2(resolvedProjectRoot) || !statSync(resolvedProjectRoot).isDirectory()) {
|
|
3277
|
+
throw new Error(`CLI project directory does not exist: ${resolvedProjectRoot}`);
|
|
3267
3278
|
}
|
|
3279
|
+
const projectRoot = realpathSync(resolvedProjectRoot);
|
|
3268
3280
|
process.chdir(projectRoot);
|
|
3269
3281
|
process.env.MEMORIX_CLI_PROJECT_ROOT = projectRoot;
|
|
3270
3282
|
invocation.projectRoot = projectRoot;
|
|
@@ -22778,6 +22790,7 @@ __export(provider_exports, {
|
|
|
22778
22790
|
MAX_INLINE_EMBEDDING_BYTES: () => MAX_INLINE_EMBEDDING_BYTES,
|
|
22779
22791
|
UnsupportedEmbeddingModalityError: () => UnsupportedEmbeddingModalityError,
|
|
22780
22792
|
getEmbeddingProvider: () => getEmbeddingProvider,
|
|
22793
|
+
getEmbeddingRuntimeHealth: () => getEmbeddingRuntimeHealth,
|
|
22781
22794
|
isEmbeddingExplicitlyDisabled: () => isEmbeddingExplicitlyDisabled,
|
|
22782
22795
|
isVectorSearchAvailable: () => isVectorSearchAvailable,
|
|
22783
22796
|
resetProvider: () => resetProvider,
|
|
@@ -22885,13 +22898,36 @@ function isTemporaryEmbeddingFailure(error40) {
|
|
|
22885
22898
|
if (!(error40 instanceof Error)) return false;
|
|
22886
22899
|
return /embedding api error \(401\)/i.test(error40.message) || /invalid_api_key/i.test(error40.message) || /incorrect api key/i.test(error40.message) || /unauthorized/i.test(error40.message) || /embedding api timeout/i.test(error40.message) || /embedding api error \((402|429|5\d\d)\)/i.test(error40.message) || /quota exceeded/i.test(error40.message) || /account balance/i.test(error40.message) || /fetch failed/i.test(error40.message) || /econnreset/i.test(error40.message) || /econnrefused/i.test(error40.message) || /temporarily unavailable/i.test(error40.message);
|
|
22887
22900
|
}
|
|
22901
|
+
function classifyEmbeddingFailure(error40) {
|
|
22902
|
+
const message = error40 instanceof Error ? error40.message : String(error40);
|
|
22903
|
+
if (/embedding api error \(401\)|invalid_api_key|incorrect api key|unauthorized/i.test(message)) {
|
|
22904
|
+
return "authorization";
|
|
22905
|
+
}
|
|
22906
|
+
if (/embedding api error \(402\)|quota exceeded|account balance/i.test(message)) {
|
|
22907
|
+
return "billing";
|
|
22908
|
+
}
|
|
22909
|
+
if (/embedding api timeout|abort/i.test(message)) {
|
|
22910
|
+
return "timeout";
|
|
22911
|
+
}
|
|
22912
|
+
return "upstream";
|
|
22913
|
+
}
|
|
22888
22914
|
function markTemporaryFailure(reason) {
|
|
22889
22915
|
provider = null;
|
|
22890
22916
|
initPromise = null;
|
|
22891
22917
|
lastInitWasTemporaryFailure = true;
|
|
22892
22918
|
lastFailureTimestamp = Date.now();
|
|
22919
|
+
lastEmbeddingFailure = {
|
|
22920
|
+
kind: classifyEmbeddingFailure(reason),
|
|
22921
|
+
occurredAt: lastFailureTimestamp
|
|
22922
|
+
};
|
|
22893
22923
|
const message = reason instanceof Error ? reason.message : String(reason);
|
|
22894
|
-
warnOnce(`[memorix] Embedding provider
|
|
22924
|
+
warnOnce(`[memorix] Embedding provider unavailable at runtime: ${message}`);
|
|
22925
|
+
}
|
|
22926
|
+
function markProviderReady(candidate2) {
|
|
22927
|
+
provider = wrapProvider(candidate2);
|
|
22928
|
+
lastInitWasTemporaryFailure = false;
|
|
22929
|
+
lastEmbeddingFailure = null;
|
|
22930
|
+
return provider;
|
|
22895
22931
|
}
|
|
22896
22932
|
async function createFastEmbedProvider() {
|
|
22897
22933
|
try {
|
|
@@ -23007,35 +23043,35 @@ async function getEmbeddingProvider(options = {}) {
|
|
|
23007
23043
|
if (mode === "fastembed") {
|
|
23008
23044
|
const initialized = await createFastEmbedProvider();
|
|
23009
23045
|
if (!initialized) return null;
|
|
23010
|
-
provider =
|
|
23046
|
+
provider = markProviderReady(initialized);
|
|
23011
23047
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
23012
23048
|
return provider;
|
|
23013
23049
|
}
|
|
23014
23050
|
if (mode === "transformers") {
|
|
23015
23051
|
const initialized = await createTransformersProvider();
|
|
23016
23052
|
if (!initialized) return null;
|
|
23017
|
-
provider =
|
|
23053
|
+
provider = markProviderReady(initialized);
|
|
23018
23054
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
23019
23055
|
return provider;
|
|
23020
23056
|
}
|
|
23021
23057
|
if (mode === "api") {
|
|
23022
23058
|
const initialized = await createAPIProvider(options);
|
|
23023
23059
|
if (!initialized) return null;
|
|
23024
|
-
provider =
|
|
23060
|
+
provider = markProviderReady(initialized);
|
|
23025
23061
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
23026
23062
|
return provider;
|
|
23027
23063
|
}
|
|
23028
23064
|
if (hasAPIEmbeddingConfig()) {
|
|
23029
23065
|
const initialized = await createAPIProvider(options);
|
|
23030
23066
|
if (initialized) {
|
|
23031
|
-
provider =
|
|
23067
|
+
provider = markProviderReady(initialized);
|
|
23032
23068
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
23033
23069
|
return provider;
|
|
23034
23070
|
}
|
|
23035
23071
|
}
|
|
23036
23072
|
const localFallback = await createLocalFallbackProvider();
|
|
23037
23073
|
if (localFallback) {
|
|
23038
|
-
provider =
|
|
23074
|
+
provider = markProviderReady(localFallback);
|
|
23039
23075
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
23040
23076
|
return provider;
|
|
23041
23077
|
}
|
|
@@ -23057,6 +23093,27 @@ async function isVectorSearchAvailable() {
|
|
|
23057
23093
|
const p3 = await getEmbeddingProvider();
|
|
23058
23094
|
return p3 !== null;
|
|
23059
23095
|
}
|
|
23096
|
+
function getEmbeddingRuntimeHealth() {
|
|
23097
|
+
const mode = getEmbeddingMode2();
|
|
23098
|
+
if (mode === "off") return { status: "disabled" };
|
|
23099
|
+
if (lastInitWasTemporaryFailure && lastEmbeddingFailure) {
|
|
23100
|
+
return {
|
|
23101
|
+
status: "degraded",
|
|
23102
|
+
failure: {
|
|
23103
|
+
kind: lastEmbeddingFailure.kind,
|
|
23104
|
+
occurredAt: new Date(lastEmbeddingFailure.occurredAt).toISOString(),
|
|
23105
|
+
retryAfterMs: Math.max(0, RETRY_COOLDOWN_MS - (Date.now() - lastEmbeddingFailure.occurredAt))
|
|
23106
|
+
}
|
|
23107
|
+
};
|
|
23108
|
+
}
|
|
23109
|
+
if (provider) {
|
|
23110
|
+
return {
|
|
23111
|
+
status: "ready",
|
|
23112
|
+
provider: { name: provider.name, dimensions: provider.dimensions }
|
|
23113
|
+
};
|
|
23114
|
+
}
|
|
23115
|
+
return { status: "uninitialized" };
|
|
23116
|
+
}
|
|
23060
23117
|
function isEmbeddingExplicitlyDisabled() {
|
|
23061
23118
|
return getEmbeddingMode2() === "off";
|
|
23062
23119
|
}
|
|
@@ -23065,10 +23122,11 @@ function resetProvider() {
|
|
|
23065
23122
|
initPromise = null;
|
|
23066
23123
|
lastInitWasTemporaryFailure = false;
|
|
23067
23124
|
lastFailureTimestamp = 0;
|
|
23125
|
+
lastEmbeddingFailure = null;
|
|
23068
23126
|
lastEmbeddingWarning = "";
|
|
23069
23127
|
lastEmbeddingWarningAt = 0;
|
|
23070
23128
|
}
|
|
23071
|
-
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
23129
|
+
var EmbeddingInputError, UnsupportedEmbeddingModalityError, MAX_INLINE_EMBEDDING_BYTES, provider, initPromise, lastInitWasTemporaryFailure, lastEmbeddingFailure, lastEmbeddingWarning, lastEmbeddingWarningAt, WARNING_COOLDOWN_MS, RETRY_COOLDOWN_MS, lastFailureTimestamp;
|
|
23072
23130
|
var init_provider = __esm({
|
|
23073
23131
|
"src/embedding/provider.ts"() {
|
|
23074
23132
|
"use strict";
|
|
@@ -23093,6 +23151,7 @@ var init_provider = __esm({
|
|
|
23093
23151
|
provider = null;
|
|
23094
23152
|
initPromise = null;
|
|
23095
23153
|
lastInitWasTemporaryFailure = false;
|
|
23154
|
+
lastEmbeddingFailure = null;
|
|
23096
23155
|
lastEmbeddingWarning = "";
|
|
23097
23156
|
lastEmbeddingWarningAt = 0;
|
|
23098
23157
|
WARNING_COOLDOWN_MS = 3e4;
|
|
@@ -182078,6 +182137,7 @@ var init_task_lens = __esm({
|
|
|
182078
182137
|
var workflows_exports = {};
|
|
182079
182138
|
__export(workflows_exports, {
|
|
182080
182139
|
WORKFLOW_ADAPTER_TARGETS: () => WORKFLOW_ADAPTER_TARGETS,
|
|
182140
|
+
WORKFLOW_AGENT_TARGETS: () => WORKFLOW_AGENT_TARGETS,
|
|
182081
182141
|
applyWorkflowAdapter: () => applyWorkflowAdapter,
|
|
182082
182142
|
importWindsurfWorkflows: () => importWindsurfWorkflows,
|
|
182083
182143
|
parseWorkflowMarkdown: () => parseWorkflowMarkdown,
|
|
@@ -182143,7 +182203,7 @@ function optionalStatus(data) {
|
|
|
182143
182203
|
return value;
|
|
182144
182204
|
}
|
|
182145
182205
|
function normalizeAgents(values) {
|
|
182146
|
-
const unknown2 = values.filter((value) => !
|
|
182206
|
+
const unknown2 = values.filter((value) => !WORKFLOW_AGENT_TARGETS.includes(value));
|
|
182147
182207
|
if (unknown2.length > 0) {
|
|
182148
182208
|
throw new Error("workflow frontmatter has unsupported agent: " + unknown2[0]);
|
|
182149
182209
|
}
|
|
@@ -182419,7 +182479,7 @@ function taskScore(workflow, task) {
|
|
|
182419
182479
|
return { score, reasons: [...new Set(reasons)] };
|
|
182420
182480
|
}
|
|
182421
182481
|
function selectWorkflows(input) {
|
|
182422
|
-
const selected = input.workflows.filter((workflow) => workflow.status === "active").map((workflow) => {
|
|
182482
|
+
const selected = input.workflows.filter((workflow) => workflow.status === "active" && (!input.agent || workflow.compatibleAgents.includes(input.agent))).map((workflow) => {
|
|
182423
182483
|
const match2 = taskScore(workflow, input.task);
|
|
182424
182484
|
const cautions = input.store && input.projectId ? input.store.recentFailureCautions(input.projectId, workflow.id) : [];
|
|
182425
182485
|
return {
|
|
@@ -182699,7 +182759,7 @@ async function selectWorkspaceWorkflows(input) {
|
|
|
182699
182759
|
errors: synced.errors
|
|
182700
182760
|
};
|
|
182701
182761
|
}
|
|
182702
|
-
var
|
|
182762
|
+
var WORKFLOW_AGENT_TARGETS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
|
|
182703
182763
|
var init_workflows2 = __esm({
|
|
182704
182764
|
"src/knowledge/workflows.ts"() {
|
|
182705
182765
|
"use strict";
|
|
@@ -182711,7 +182771,7 @@ var init_workflows2 = __esm({
|
|
|
182711
182771
|
init_workflow_store();
|
|
182712
182772
|
init_outcome_store();
|
|
182713
182773
|
init_task_lens();
|
|
182714
|
-
|
|
182774
|
+
WORKFLOW_AGENT_TARGETS = [
|
|
182715
182775
|
"windsurf",
|
|
182716
182776
|
"cursor",
|
|
182717
182777
|
"claude-code",
|
|
@@ -191131,8 +191191,8 @@ var init_official_skills = __esm({
|
|
|
191131
191191
|
"",
|
|
191132
191192
|
"| Situation | Prefer | CLI fallback |",
|
|
191133
191193
|
"|---|---|---|",
|
|
191134
|
-
'| Starting a coding task and needing the best task-lensed project map | `memorix_project_context` with the user\'s task | `memorix context "<task>" --json` |',
|
|
191135
|
-
'| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --json` |',
|
|
191194
|
+
'| Starting a coding task and needing the best task-lensed project map | `memorix_project_context` with the user\'s task | `memorix context "<task>" --brief-json` |',
|
|
191195
|
+
'| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --brief-json` |',
|
|
191136
191196
|
'| Need structured refs/freshness for code-bound memories | `memorix_context_pack` | `memorix codegraph context-pack --task "<topic>"` |',
|
|
191137
191197
|
'| Explicit memory graph question | `memorix_graph_context` | `memorix memory graph-context --query "<topic>"` |',
|
|
191138
191198
|
'| Specific past decision, bug, file, or change | `memorix_search` | `memorix memory search --query "<topic>"` |',
|
|
@@ -191145,7 +191205,7 @@ var init_official_skills = __esm({
|
|
|
191145
191205
|
"",
|
|
191146
191206
|
"- Default first step for non-trivial coding work: call `memorix_project_context` before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Pass the user's actual task so Memorix can choose a bugfix, feature, release, onboarding, refactor, docs, test, or general brief. Start by reading its suggested files.",
|
|
191147
191207
|
"- In Claude Code headless/print-mode, an initial MCP `pending` status is not a failure by itself. Prefer MCP first: use the client's tool discovery/dynamic loading to find `memorix_project_context` when it is not in the first visible tool list.",
|
|
191148
|
-
'- Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run `memorix resume "<task>" --json` exactly once before inspecting files, Git history, progress notes, or guessing. The absence of `.memorix` or visible memory files never proves project memory is empty. For a new task, use `memorix context "<task>" --json` instead. If that one command fails, report it and proceed normally; do not probe help, enumerate commands, or chain broad searches.',
|
|
191208
|
+
'- Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run `memorix resume "<task>" --brief-json` exactly once before inspecting files, Git history, progress notes, or guessing. The absence of `.memorix` or visible memory files never proves project memory is empty. For a new task, use `memorix context "<task>" --brief-json` instead. Use `--json` only when a diagnostic needs the detailed legacy payload. If that one command fails, report it and proceed normally; do not probe help, enumerate commands, or chain broad searches.',
|
|
191149
191209
|
"- After a successful `memorix_project_context`, the brief is the default retrieval boundary. Do not call more Memorix retrieval tools after a complete brief. Use Context Pack, search, or detail only when the brief lacks a specific reference, freshness field, or fact needed for the task, or when the user explicitly asks for deeper history. In MCP, name that missing fact in `purpose` when intentionally expanding beyond the brief. Do not retrieve the same decision twice just to confirm an already-complete brief.",
|
|
191150
191210
|
"- If the user asks for read-only work or says not to modify files, do not call `memorix_store` just to record an assessment. Store only when the user explicitly asks to preserve it.",
|
|
191151
191211
|
"- Use `memorix_search` for a specific decision, bug, file, or prior change that the project context did not answer. Fetch detail only for a result you still need after the brief.",
|
|
@@ -192404,7 +192464,7 @@ alwaysApply: true
|
|
|
192404
192464
|
"",
|
|
192405
192465
|
`Default first step for non-trivial coding work: call \`memorix_project_context\` with the user's actual task before progress files, dev-log reads, ad-hoc file reads, or git archaeology. Memorix will choose a task-lensed brief (bugfix, feature, release, onboarding, refactor, docs, test, or general). When the task is continuing prior work, the same brief also includes a bounded prior-work projection. Treat its "Start here" files as the first ${contextNoun} files to inspect.`,
|
|
192406
192466
|
"",
|
|
192407
|
-
'If the MCP tool is not visible yet but the client supports tool discovery or dynamic loading, search/select `memorix_project_context` first. Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run exactly one CLI brief with the user\'s real task before inspecting files, Git history, progress notes, or guessing: `memorix resume "<task>" --json`. For a new task, use `memorix context "<task>" --json` instead. The absence of `.memorix` or visible memory files never proves project memory is empty. If that one command fails, report it and proceed normally. Do not probe help, enumerate commands, chain broad searches, wait indefinitely on MCP startup, or hand-write tool-call syntax.',
|
|
192467
|
+
'If the MCP tool is not visible yet but the client supports tool discovery or dynamic loading, search/select `memorix_project_context` first. Continuation fallback is mandatory: when the user asks to continue, resume, take over, or explain prior work and MCP cannot be called in this turn, run exactly one CLI brief with the user\'s real task before inspecting files, Git history, progress notes, or guessing: `memorix resume "<task>" --brief-json`. For a new task, use `memorix context "<task>" --brief-json` instead. The absence of `.memorix` or visible memory files never proves project memory is empty. Use `--json` only when a diagnostic needs the detailed legacy payload. If that one command fails, report it and proceed normally. Do not probe help, enumerate commands, chain broad searches, wait indefinitely on MCP startup, or hand-write tool-call syntax.',
|
|
192408
192468
|
"",
|
|
192409
192469
|
"After a successful `memorix_project_context` result, the brief is the default retrieval boundary. Do not call more Memorix retrieval tools after a complete brief. Use `memorix_context_pack`, `memorix_search`, or `memorix_detail` only when the brief lacks a specific reference, freshness field, or fact needed for the task, or when the user explicitly asks for deeper history. In MCP, name that missing fact in `purpose` when intentionally expanding beyond the brief. Do not retrieve the same decision twice just to confirm an already-complete brief.",
|
|
192410
192470
|
"If the user asks for read-only work or says not to modify files, do not call `memorix_store` just to record an assessment. Store only when the user explicitly asks to preserve it.",
|
|
@@ -194446,6 +194506,15 @@ function workflowOutput(selection) {
|
|
|
194446
194506
|
cautions: selection.cautions.map((caution) => short(caution, 22))
|
|
194447
194507
|
};
|
|
194448
194508
|
}
|
|
194509
|
+
function workflowLoadout(agent, selections) {
|
|
194510
|
+
if (!agent) return void 0;
|
|
194511
|
+
return {
|
|
194512
|
+
agent,
|
|
194513
|
+
workflowIds: selections.map((selection) => selection.workflow.id),
|
|
194514
|
+
requiredContext: unique(selections.flatMap((selection) => selection.workflow.requiredContext)).map((item) => short(item, 18)).slice(0, 4),
|
|
194515
|
+
allowedTools: unique(selections.flatMap((selection) => selection.workflow.allowedTools)).map((item) => short(item, 12)).slice(0, 6)
|
|
194516
|
+
};
|
|
194517
|
+
}
|
|
194449
194518
|
function appendLine(lines, candidate2, maxTokens, omitted, omittedKind, selected, receiptSelection) {
|
|
194450
194519
|
const next = lines.length ? lines.join("\n") + "\n" + candidate2 : candidate2;
|
|
194451
194520
|
if (countTextTokens(next) <= maxTokens) {
|
|
@@ -194906,6 +194975,22 @@ function renderTaskWorksetPrompt(input) {
|
|
|
194906
194975
|
);
|
|
194907
194976
|
}
|
|
194908
194977
|
}
|
|
194978
|
+
if (input.agentLoadout) {
|
|
194979
|
+
appendLine(lines, "", maxTokens, omitted, "agent-loadout-heading");
|
|
194980
|
+
appendLine(lines, "Agent loadout (" + input.agentLoadout.agent + ")", maxTokens, omitted, "agent-loadout-heading");
|
|
194981
|
+
for (const required2 of input.agentLoadout.requiredContext.slice(0, 2)) {
|
|
194982
|
+
appendLine(lines, "- Need: " + required2, maxTokens, omitted, "agent-loadout-context");
|
|
194983
|
+
}
|
|
194984
|
+
if (input.agentLoadout.allowedTools.length > 0) {
|
|
194985
|
+
appendLine(
|
|
194986
|
+
lines,
|
|
194987
|
+
"- Workflow tools: " + input.agentLoadout.allowedTools.join(", "),
|
|
194988
|
+
maxTokens,
|
|
194989
|
+
omitted,
|
|
194990
|
+
"agent-loadout-tools"
|
|
194991
|
+
);
|
|
194992
|
+
}
|
|
194993
|
+
}
|
|
194909
194994
|
if (input.verification.length > 0) {
|
|
194910
194995
|
appendLine(lines, "", maxTokens, omitted, "verification-heading");
|
|
194911
194996
|
appendLine(lines, "Verify", maxTokens, omitted, "verification-heading");
|
|
@@ -194997,6 +195082,7 @@ async function buildTaskWorkset(input) {
|
|
|
194997
195082
|
let workspace;
|
|
194998
195083
|
let pages = [];
|
|
194999
195084
|
let workflows = [];
|
|
195085
|
+
let workflowSelections = [];
|
|
195000
195086
|
try {
|
|
195001
195087
|
workspace = await preferredWorkspace(input.projectId, input.dataDir);
|
|
195002
195088
|
if (workspace) {
|
|
@@ -195013,14 +195099,15 @@ async function buildTaskWorkset(input) {
|
|
|
195013
195099
|
if (task) {
|
|
195014
195100
|
const workflowStore = new WorkflowStore();
|
|
195015
195101
|
await workflowStore.init(input.dataDir);
|
|
195016
|
-
|
|
195102
|
+
workflowSelections = selectWorkflows({
|
|
195017
195103
|
workflows: workflowStore.listWorkflows(workspace.id, "active"),
|
|
195018
195104
|
task,
|
|
195019
195105
|
projectId: input.projectId,
|
|
195020
195106
|
store: workflowStore,
|
|
195107
|
+
...input.agent ? { agent: input.agent } : {},
|
|
195021
195108
|
limit: 2
|
|
195022
195109
|
});
|
|
195023
|
-
workflows =
|
|
195110
|
+
workflows = workflowSelections.map(workflowOutput);
|
|
195024
195111
|
for (const workflow of workflows) {
|
|
195025
195112
|
for (const caution of workflow.cautions) {
|
|
195026
195113
|
cautions.push({ kind: "workflow-failed-verification", message: caution });
|
|
@@ -195042,6 +195129,10 @@ async function buildTaskWorkset(input) {
|
|
|
195042
195129
|
workflows
|
|
195043
195130
|
});
|
|
195044
195131
|
const governedDurableIds = new Set(governed.durableMemory.map((memory) => memory.id));
|
|
195132
|
+
const agentLoadout = workflowLoadout(
|
|
195133
|
+
input.agent,
|
|
195134
|
+
workflowSelections.filter((selection2) => governed.workflows.some((workflow) => workflow.id === selection2.workflow.id))
|
|
195135
|
+
);
|
|
195045
195136
|
const evidenceIds = unique(governed.claims.flatMap((claim) => evidenceIdsForClaim(
|
|
195046
195137
|
claim,
|
|
195047
195138
|
evidenceByClaim.get(claim.id) ?? []
|
|
@@ -195086,6 +195177,7 @@ async function buildTaskWorkset(input) {
|
|
|
195086
195177
|
pages: pages.filter((page) => page.claimIds.some((claimId) => governed.claims.some((claim) => claim.id === claimId))),
|
|
195087
195178
|
durableMemory: governed.durableMemory,
|
|
195088
195179
|
workflows: governed.workflows,
|
|
195180
|
+
...agentLoadout ? { agentLoadout } : {},
|
|
195089
195181
|
cautions: normalizedCautions,
|
|
195090
195182
|
verification,
|
|
195091
195183
|
evidenceIds,
|
|
@@ -195304,10 +195396,12 @@ var init_current_facts = __esm({
|
|
|
195304
195396
|
var external_provider_exports = {};
|
|
195305
195397
|
__export(external_provider_exports, {
|
|
195306
195398
|
DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS: () => DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS,
|
|
195399
|
+
EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS: () => EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS,
|
|
195307
195400
|
MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES: () => MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES,
|
|
195308
195401
|
MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS: () => MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS,
|
|
195309
195402
|
getExternalCodeGraphContext: () => getExternalCodeGraphContext,
|
|
195310
|
-
inspectExternalCodeGraph: () => inspectExternalCodeGraph
|
|
195403
|
+
inspectExternalCodeGraph: () => inspectExternalCodeGraph,
|
|
195404
|
+
runExternalCodeGraphLifecycle: () => runExternalCodeGraphLifecycle
|
|
195311
195405
|
});
|
|
195312
195406
|
import { spawn as spawn5 } from "child_process";
|
|
195313
195407
|
import { existsSync as existsSync16, readFileSync as readFileSync9 } from "fs";
|
|
@@ -195506,6 +195600,66 @@ async function inspectExternalCodeGraph(input) {
|
|
|
195506
195600
|
const health = healthFromStatus(status);
|
|
195507
195601
|
return { health, quality: providerQuality({ mode, health }) };
|
|
195508
195602
|
}
|
|
195603
|
+
async function runExternalCodeGraphLifecycle(input) {
|
|
195604
|
+
const mode = input.mode ?? "auto";
|
|
195605
|
+
if (mode === "off") {
|
|
195606
|
+
const health = { state: "disabled", reason: "External CodeGraph is disabled by configuration." };
|
|
195607
|
+
return {
|
|
195608
|
+
action: input.action,
|
|
195609
|
+
performed: false,
|
|
195610
|
+
message: "External CodeGraph is disabled by configuration.",
|
|
195611
|
+
health,
|
|
195612
|
+
quality: providerQuality({ mode, health })
|
|
195613
|
+
};
|
|
195614
|
+
}
|
|
195615
|
+
const indexPath = join23(input.projectRoot, ".codegraph");
|
|
195616
|
+
if (input.action === "sync" && !existsSync16(indexPath)) {
|
|
195617
|
+
const health = { state: "not-detected", reason: "No local .codegraph index is present for this project." };
|
|
195618
|
+
return {
|
|
195619
|
+
action: input.action,
|
|
195620
|
+
performed: false,
|
|
195621
|
+
message: 'Initialize CodeGraph first with "memorix codegraph init".',
|
|
195622
|
+
health,
|
|
195623
|
+
quality: providerQuality({ mode, health })
|
|
195624
|
+
};
|
|
195625
|
+
}
|
|
195626
|
+
if (input.action === "init" && existsSync16(indexPath)) {
|
|
195627
|
+
const inspected2 = await inspectExternalCodeGraph(input);
|
|
195628
|
+
return {
|
|
195629
|
+
action: input.action,
|
|
195630
|
+
performed: false,
|
|
195631
|
+
message: "A local CodeGraph directory already exists; no initialization was performed.",
|
|
195632
|
+
...inspected2
|
|
195633
|
+
};
|
|
195634
|
+
}
|
|
195635
|
+
const result = await runSafely(input.runner ?? defaultRunner, {
|
|
195636
|
+
command: input.command,
|
|
195637
|
+
args: input.action === "init" ? ["init", input.projectRoot] : ["sync", input.projectRoot, "--quiet"],
|
|
195638
|
+
cwd: input.projectRoot,
|
|
195639
|
+
// A context read must stay fast; an explicit user-requested index sync is
|
|
195640
|
+
// a different operation and gets its own bounded maintenance window.
|
|
195641
|
+
timeoutMs: EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS,
|
|
195642
|
+
maxOutputBytes: MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES
|
|
195643
|
+
});
|
|
195644
|
+
if (!result.ok) {
|
|
195645
|
+
const health = unavailableHealth(result);
|
|
195646
|
+
return {
|
|
195647
|
+
action: input.action,
|
|
195648
|
+
performed: false,
|
|
195649
|
+
message: "CodeGraph " + input.action + " did not complete: " + (health.reason ?? "unknown local error"),
|
|
195650
|
+
health,
|
|
195651
|
+
quality: providerQuality({ mode, health })
|
|
195652
|
+
};
|
|
195653
|
+
}
|
|
195654
|
+
const inspected = await inspectExternalCodeGraph(input);
|
|
195655
|
+
const remainsStale = inspected.health.state === "stale";
|
|
195656
|
+
return {
|
|
195657
|
+
action: input.action,
|
|
195658
|
+
performed: true,
|
|
195659
|
+
message: input.action === "init" ? 'Local CodeGraph initialization completed. Run "memorix codegraph sync" when source changes.' : remainsStale ? "Local CodeGraph synchronization completed, but the graph remains stale against the current worktree; this task will use Lite evidence." : "Local CodeGraph synchronization completed and is ready for task-scoped semantic context.",
|
|
195660
|
+
...inspected
|
|
195661
|
+
};
|
|
195662
|
+
}
|
|
195509
195663
|
function safeRelativePath(projectRoot, value, exclude) {
|
|
195510
195664
|
const path47 = safeText(value, 1024);
|
|
195511
195665
|
if (!path47 || isAbsolute2(path47)) return void 0;
|
|
@@ -195631,7 +195785,7 @@ async function getExternalCodeGraphContext(input) {
|
|
|
195631
195785
|
quality: providerQuality({ mode, health: inspected.health, ...contributes ? { selected: "external" } : {} })
|
|
195632
195786
|
};
|
|
195633
195787
|
}
|
|
195634
|
-
var DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES, MAX_EXTERNAL_NODE_COUNT, MAX_EXTERNAL_EDGE_COUNT, MAX_EXTERNAL_FILE_COUNT, MAX_EXTERNAL_TEXT_LENGTH, LITE_SUPPORTED_LANGUAGES, defaultRunner;
|
|
195788
|
+
var DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS, EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS, MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES, MAX_EXTERNAL_NODE_COUNT, MAX_EXTERNAL_EDGE_COUNT, MAX_EXTERNAL_FILE_COUNT, MAX_EXTERNAL_TEXT_LENGTH, LITE_SUPPORTED_LANGUAGES, defaultRunner;
|
|
195635
195789
|
var init_external_provider = __esm({
|
|
195636
195790
|
"src/codegraph/external-provider.ts"() {
|
|
195637
195791
|
"use strict";
|
|
@@ -195641,6 +195795,7 @@ var init_external_provider = __esm({
|
|
|
195641
195795
|
init_ids();
|
|
195642
195796
|
DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS = 1200;
|
|
195643
195797
|
MAX_EXTERNAL_CODEGRAPH_TIMEOUT_MS = 5e3;
|
|
195798
|
+
EXTERNAL_CODEGRAPH_LIFECYCLE_TIMEOUT_MS = 3e4;
|
|
195644
195799
|
MAX_EXTERNAL_CODEGRAPH_OUTPUT_BYTES = 256 * 1024;
|
|
195645
195800
|
MAX_EXTERNAL_NODE_COUNT = 16;
|
|
195646
195801
|
MAX_EXTERNAL_EDGE_COUNT = 24;
|
|
@@ -197363,6 +197518,7 @@ async function buildAutoProjectContext(input) {
|
|
|
197363
197518
|
projectId: input.project.id,
|
|
197364
197519
|
dataDir: input.dataDir,
|
|
197365
197520
|
...task ? { task } : {},
|
|
197521
|
+
...input.agent ? { agent: input.agent } : {},
|
|
197366
197522
|
lens: lens.id,
|
|
197367
197523
|
startHere,
|
|
197368
197524
|
...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
|
|
@@ -197699,6 +197855,34 @@ var init_auto_context = __esm({
|
|
|
197699
197855
|
}
|
|
197700
197856
|
});
|
|
197701
197857
|
|
|
197858
|
+
// src/knowledge/context-receipt.ts
|
|
197859
|
+
var context_receipt_exports = {};
|
|
197860
|
+
__export(context_receipt_exports, {
|
|
197861
|
+
buildBoundedContextReceipt: () => buildBoundedContextReceipt
|
|
197862
|
+
});
|
|
197863
|
+
function buildBoundedContextReceipt(input) {
|
|
197864
|
+
const provider2 = input.providerQuality ?? input.workset.provenance.codeProvider;
|
|
197865
|
+
return {
|
|
197866
|
+
schemaVersion: "1",
|
|
197867
|
+
task: input.workset.task,
|
|
197868
|
+
brief: input.workset.prompt,
|
|
197869
|
+
receipt: input.workset.receipt,
|
|
197870
|
+
code: {
|
|
197871
|
+
selected: provider2?.selected ?? "lite",
|
|
197872
|
+
quality: provider2?.selectedQuality ?? "heuristic",
|
|
197873
|
+
externalState: provider2?.external.state ?? "not-detected",
|
|
197874
|
+
...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
|
|
197875
|
+
},
|
|
197876
|
+
...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
|
|
197877
|
+
};
|
|
197878
|
+
}
|
|
197879
|
+
var init_context_receipt = __esm({
|
|
197880
|
+
"src/knowledge/context-receipt.ts"() {
|
|
197881
|
+
"use strict";
|
|
197882
|
+
init_esm_shims();
|
|
197883
|
+
}
|
|
197884
|
+
});
|
|
197885
|
+
|
|
197702
197886
|
// src/cli/commands/context.ts
|
|
197703
197887
|
var context_exports = {};
|
|
197704
197888
|
__export(context_exports, {
|
|
@@ -197709,6 +197893,12 @@ function coerceRefreshMode(input) {
|
|
|
197709
197893
|
if (value === "always" || value === "never" || value === "auto") return value;
|
|
197710
197894
|
throw new Error("refresh must be one of: auto, always, never");
|
|
197711
197895
|
}
|
|
197896
|
+
function coerceAgentTarget(input) {
|
|
197897
|
+
const value = input?.trim().toLowerCase();
|
|
197898
|
+
if (!value) return void 0;
|
|
197899
|
+
if (WORKFLOW_AGENT_TARGETS.includes(value)) return value;
|
|
197900
|
+
throw new Error("agent must be one of: " + WORKFLOW_AGENT_TARGETS.join(", "));
|
|
197901
|
+
}
|
|
197712
197902
|
var context_default;
|
|
197713
197903
|
var init_context2 = __esm({
|
|
197714
197904
|
"src/cli/commands/context.ts"() {
|
|
@@ -197718,6 +197908,8 @@ var init_context2 = __esm({
|
|
|
197718
197908
|
init_auto_context();
|
|
197719
197909
|
init_observations();
|
|
197720
197910
|
init_visibility();
|
|
197911
|
+
init_context_receipt();
|
|
197912
|
+
init_workflows2();
|
|
197721
197913
|
init_operator_shared();
|
|
197722
197914
|
context_default = defineCommand({
|
|
197723
197915
|
meta: {
|
|
@@ -197733,10 +197925,12 @@ var init_context2 = __esm({
|
|
|
197733
197925
|
},
|
|
197734
197926
|
resume: { type: "boolean", description: "Always include the bounded prior-work projection" },
|
|
197735
197927
|
refresh: { type: "string", description: "Project scan policy: auto, always, or never" },
|
|
197736
|
-
|
|
197928
|
+
agent: { type: "string", description: "Optional target agent for compatible workflow selection" },
|
|
197929
|
+
json: { type: "boolean", description: "Emit machine-readable JSON output" },
|
|
197930
|
+
briefJson: { type: "boolean", description: "Emit only the bounded agent brief and receipt JSON" }
|
|
197737
197931
|
},
|
|
197738
197932
|
run: async ({ args }) => {
|
|
197739
|
-
const asJson = !!args.json;
|
|
197933
|
+
const asJson = !!args.json || !!args.briefJson;
|
|
197740
197934
|
const task = args.task?.trim() || args.input?.trim() || void 0;
|
|
197741
197935
|
try {
|
|
197742
197936
|
const { project, dataDir, reader } = await getCliProjectContext();
|
|
@@ -197745,23 +197939,25 @@ var init_context2 = __esm({
|
|
|
197745
197939
|
dataDir,
|
|
197746
197940
|
observations: filterReadableObservations(getAllObservations(), reader),
|
|
197747
197941
|
task,
|
|
197942
|
+
agent: coerceAgentTarget(args.agent),
|
|
197748
197943
|
refresh: coerceRefreshMode(args.refresh),
|
|
197749
197944
|
reader,
|
|
197750
197945
|
...args.resume ? { continuation: "always" } : {}
|
|
197751
197946
|
});
|
|
197947
|
+
const detailed = {
|
|
197948
|
+
project,
|
|
197949
|
+
lens: context.lens,
|
|
197950
|
+
brief: buildAutoProjectBrief(context),
|
|
197951
|
+
currentFacts: context.currentFacts,
|
|
197952
|
+
overview: context.overview,
|
|
197953
|
+
refresh: context.refresh,
|
|
197954
|
+
providerQuality: context.providerQuality,
|
|
197955
|
+
workset: context.workset,
|
|
197956
|
+
...context.task ? { task: context.task } : {},
|
|
197957
|
+
...context.continuation ? { continuation: context.continuation } : {}
|
|
197958
|
+
};
|
|
197752
197959
|
emitResult(
|
|
197753
|
-
{
|
|
197754
|
-
project,
|
|
197755
|
-
lens: context.lens,
|
|
197756
|
-
brief: buildAutoProjectBrief(context),
|
|
197757
|
-
currentFacts: context.currentFacts,
|
|
197758
|
-
overview: context.overview,
|
|
197759
|
-
refresh: context.refresh,
|
|
197760
|
-
providerQuality: context.providerQuality,
|
|
197761
|
-
workset: context.workset,
|
|
197762
|
-
...context.task ? { task: context.task } : {},
|
|
197763
|
-
...context.continuation ? { continuation: context.continuation } : {}
|
|
197764
|
-
},
|
|
197960
|
+
args.briefJson ? buildBoundedContextReceipt({ workset: context.workset, providerQuality: context.providerQuality }) : detailed,
|
|
197765
197961
|
formatAutoProjectContextPrompt(context),
|
|
197766
197962
|
asJson
|
|
197767
197963
|
);
|
|
@@ -197793,7 +197989,9 @@ var init_resume = __esm({
|
|
|
197793
197989
|
args: {
|
|
197794
197990
|
task: { type: "positional", description: "Current continuation task", required: false },
|
|
197795
197991
|
refresh: { type: "string", description: "Project scan policy: auto, always, or never" },
|
|
197796
|
-
|
|
197992
|
+
agent: { type: "string", description: "Optional target agent for compatible workflow selection" },
|
|
197993
|
+
json: { type: "boolean", description: "Emit machine-readable JSON output" },
|
|
197994
|
+
briefJson: { type: "boolean", description: "Emit only the bounded agent brief and receipt JSON" }
|
|
197797
197995
|
},
|
|
197798
197996
|
async run({ args }) {
|
|
197799
197997
|
await context_default.run?.({
|
|
@@ -197801,7 +197999,9 @@ var init_resume = __esm({
|
|
|
197801
197999
|
_: [],
|
|
197802
198000
|
input: args.task,
|
|
197803
198001
|
refresh: args.refresh,
|
|
198002
|
+
agent: args.agent,
|
|
197804
198003
|
json: args.json,
|
|
198004
|
+
briefJson: args.briefJson,
|
|
197805
198005
|
resume: true
|
|
197806
198006
|
},
|
|
197807
198007
|
rawArgs: [],
|
|
@@ -198081,6 +198281,7 @@ async function attachTaskWorkset(input) {
|
|
|
198081
198281
|
projectId: input.projectId,
|
|
198082
198282
|
dataDir: input.dataDir,
|
|
198083
198283
|
task: input.pack.task,
|
|
198284
|
+
...input.agent ? { agent: input.agent } : {},
|
|
198084
198285
|
lens: input.lens,
|
|
198085
198286
|
currentFacts: input.currentFacts ?? [],
|
|
198086
198287
|
...input.codeState ? { codeState: input.codeState } : {},
|
|
@@ -198189,7 +198390,8 @@ function formatStatus(status, quality) {
|
|
|
198189
198390
|
status.indexedAt ? `- Indexed at: ${status.indexedAt}` : "- Indexed at: never",
|
|
198190
198391
|
...quality ? [
|
|
198191
198392
|
`- Persistent provider: ${status.provider} (heuristic local index)`,
|
|
198192
|
-
`- External semantic CodeGraph: ${quality.external.state}` + (quality.external.reason ? ` (${quality.external.reason})` : "")
|
|
198393
|
+
`- External semantic CodeGraph: ${quality.external.state}` + (quality.external.reason ? ` (${quality.external.reason})` : ""),
|
|
198394
|
+
`- Task-scoped provider: ${quality.selected} (${quality.selectedQuality})`
|
|
198193
198395
|
] : []
|
|
198194
198396
|
].join("\n");
|
|
198195
198397
|
}
|
|
@@ -198197,6 +198399,8 @@ function formatUsageHint() {
|
|
|
198197
198399
|
return [
|
|
198198
198400
|
"Usage:",
|
|
198199
198401
|
" memorix codegraph refresh",
|
|
198402
|
+
" memorix codegraph init # explicit local CodeGraph initialization",
|
|
198403
|
+
" memorix codegraph sync # explicit local CodeGraph synchronization",
|
|
198200
198404
|
" memorix codegraph status --json",
|
|
198201
198405
|
" memorix codegraph diff [--from <snapshot>] [--to <snapshot>]",
|
|
198202
198406
|
" memorix codegraph impact --path <relative-source-path>",
|
|
@@ -198251,6 +198455,12 @@ function compactFacts(project) {
|
|
|
198251
198455
|
facts.push(formatGitFact(current.git));
|
|
198252
198456
|
return { facts, dirty: current.git.dirty };
|
|
198253
198457
|
}
|
|
198458
|
+
function coerceAgentTarget2(input) {
|
|
198459
|
+
const value = input?.trim().toLowerCase();
|
|
198460
|
+
if (!value) return void 0;
|
|
198461
|
+
if (WORKFLOW_AGENT_TARGETS.includes(value)) return value;
|
|
198462
|
+
throw new Error("agent must be one of: " + WORKFLOW_AGENT_TARGETS.join(", "));
|
|
198463
|
+
}
|
|
198254
198464
|
var codegraph_default;
|
|
198255
198465
|
var init_codegraph = __esm({
|
|
198256
198466
|
"src/cli/commands/codegraph.ts"() {
|
|
@@ -198265,6 +198475,8 @@ var init_codegraph = __esm({
|
|
|
198265
198475
|
init_task_lens();
|
|
198266
198476
|
init_external_provider();
|
|
198267
198477
|
init_resolved_config();
|
|
198478
|
+
init_context_receipt();
|
|
198479
|
+
init_workflows2();
|
|
198268
198480
|
init_observations();
|
|
198269
198481
|
init_visibility();
|
|
198270
198482
|
init_operator_shared();
|
|
@@ -198274,19 +198486,21 @@ var init_codegraph = __esm({
|
|
|
198274
198486
|
description: "Inspect and refresh CodeGraph Memory for the current project"
|
|
198275
198487
|
},
|
|
198276
198488
|
args: {
|
|
198277
|
-
action: { type: "string", description: "Action: status, refresh, diff, impact, or context-pack" },
|
|
198489
|
+
action: { type: "string", description: "Action: status, refresh, diff, impact, init, sync, or context-pack" },
|
|
198278
198490
|
task: { type: "string", description: "Task text for context-pack" },
|
|
198279
198491
|
path: { type: "string", description: "Indexed relative source path for impact" },
|
|
198280
198492
|
limit: { type: "string", description: "Max active memories to inspect for context-pack" },
|
|
198281
198493
|
from: { type: "string", description: "Earlier CodeGraph snapshot id for diff" },
|
|
198282
198494
|
to: { type: "string", description: "Later CodeGraph snapshot id for diff" },
|
|
198283
198495
|
impactLimit: { type: "string", description: "Maximum graph relations to inspect for diff impact" },
|
|
198284
|
-
|
|
198496
|
+
agent: { type: "string", description: "Optional target agent for compatible workflow selection" },
|
|
198497
|
+
json: { type: "boolean", description: "Emit machine-readable JSON output" },
|
|
198498
|
+
briefJson: { type: "boolean", description: "Emit only the bounded agent brief and receipt JSON for context-pack" }
|
|
198285
198499
|
},
|
|
198286
198500
|
run: async ({ args }) => {
|
|
198287
198501
|
const positional = args._ ?? [];
|
|
198288
198502
|
const action = (positional[0] || args.action || "status").toLowerCase();
|
|
198289
|
-
const asJson = !!args.json;
|
|
198503
|
+
const asJson = !!args.json || !!args.briefJson;
|
|
198290
198504
|
try {
|
|
198291
198505
|
const { project, dataDir, reader } = await getCliProjectContext();
|
|
198292
198506
|
const store2 = new CodeGraphStore();
|
|
@@ -198353,6 +198567,26 @@ ${formatUsageHint()}`;
|
|
|
198353
198567
|
);
|
|
198354
198568
|
return;
|
|
198355
198569
|
}
|
|
198570
|
+
case "init":
|
|
198571
|
+
case "sync": {
|
|
198572
|
+
const lifecycle = await runExternalCodeGraphLifecycle({
|
|
198573
|
+
action,
|
|
198574
|
+
projectRoot: project.rootPath,
|
|
198575
|
+
mode: codegraphConfig.externalContext,
|
|
198576
|
+
command: codegraphConfig.externalCommand,
|
|
198577
|
+
timeoutMs: codegraphConfig.externalTimeoutMs
|
|
198578
|
+
});
|
|
198579
|
+
emitResult(
|
|
198580
|
+
{ project, lifecycle, providerQuality: lifecycle.quality },
|
|
198581
|
+
[
|
|
198582
|
+
lifecycle.message,
|
|
198583
|
+
`- External semantic CodeGraph: ${lifecycle.health.state}`,
|
|
198584
|
+
`- Task-scoped provider: ${lifecycle.quality.selected} (${lifecycle.quality.selectedQuality})`
|
|
198585
|
+
].join("\n"),
|
|
198586
|
+
asJson
|
|
198587
|
+
);
|
|
198588
|
+
return;
|
|
198589
|
+
}
|
|
198356
198590
|
case "diff": {
|
|
198357
198591
|
const snapshots = store2.listSnapshots(project.id, 2);
|
|
198358
198592
|
const fromSnapshotId = args.from?.trim() || snapshots[1]?.id;
|
|
@@ -198437,13 +198671,18 @@ ${formatUsageHint()}`;
|
|
|
198437
198671
|
...external.outline ? { semanticCode: external.outline } : {},
|
|
198438
198672
|
providerQuality: external.quality,
|
|
198439
198673
|
...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {},
|
|
198440
|
-
reader
|
|
198674
|
+
reader,
|
|
198675
|
+
agent: coerceAgentTarget2(args.agent)
|
|
198441
198676
|
});
|
|
198442
|
-
emitResult(
|
|
198677
|
+
emitResult(
|
|
198678
|
+
args.briefJson && pack.workset ? buildBoundedContextReceipt({ workset: pack.workset, providerQuality: external.quality }) : { project, pack, providerQuality: external.quality },
|
|
198679
|
+
buildContextPackPrompt(pack),
|
|
198680
|
+
asJson
|
|
198681
|
+
);
|
|
198443
198682
|
return;
|
|
198444
198683
|
}
|
|
198445
198684
|
default:
|
|
198446
|
-
emitError(`unknown codegraph action "${action}". Use "status", "refresh", "diff", "impact", or "context-pack".`, asJson);
|
|
198685
|
+
emitError(`unknown codegraph action "${action}". Use "status", "refresh", "init", "sync", "diff", "impact", or "context-pack".`, asJson);
|
|
198447
198686
|
}
|
|
198448
198687
|
} catch (error40) {
|
|
198449
198688
|
emitError(error40 instanceof Error ? error40.message : String(error40), asJson);
|
|
@@ -258844,7 +259083,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
258844
259083
|
};
|
|
258845
259084
|
const server = existingServer ?? new McpServer({
|
|
258846
259085
|
name: "memorix",
|
|
258847
|
-
version: true ? "1.
|
|
259086
|
+
version: true ? "1.6.1" : "1.0.1"
|
|
258848
259087
|
});
|
|
258849
259088
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
258850
259089
|
server.registerTool = ((name, ...args) => {
|
|
@@ -259678,13 +259917,28 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
259678
259917
|
refresh: external_exports2.enum(["auto", "always", "never"]).optional().default("auto").describe(
|
|
259679
259918
|
"Code Memory refresh policy. auto refreshes only when missing or stale."
|
|
259680
259919
|
),
|
|
259681
|
-
format: external_exports2.enum(["prompt", "summary", "json"]).optional().default("prompt").describe(
|
|
259682
|
-
'Output format. "prompt" is agent-ready; "summary" is human-readable; "json" is
|
|
259920
|
+
format: external_exports2.enum(["prompt", "summary", "json", "receipt"]).optional().default("prompt").describe(
|
|
259921
|
+
'Output format. "prompt" is agent-ready; "summary" is human-readable; "receipt" is bounded JSON; "json" is detailed diagnostics.'
|
|
259683
259922
|
),
|
|
259923
|
+
agent: external_exports2.enum([
|
|
259924
|
+
"windsurf",
|
|
259925
|
+
"cursor",
|
|
259926
|
+
"claude-code",
|
|
259927
|
+
"codex",
|
|
259928
|
+
"copilot",
|
|
259929
|
+
"antigravity",
|
|
259930
|
+
"gemini-cli",
|
|
259931
|
+
"openclaw",
|
|
259932
|
+
"hermes",
|
|
259933
|
+
"omp",
|
|
259934
|
+
"kiro",
|
|
259935
|
+
"opencode",
|
|
259936
|
+
"trae"
|
|
259937
|
+
]).optional().describe("Optional target agent for compatible workflow selection."),
|
|
259684
259938
|
limit: external_exports2.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
|
|
259685
259939
|
}
|
|
259686
259940
|
},
|
|
259687
|
-
async ({ task, refresh, format }) => {
|
|
259941
|
+
async ({ task, refresh, format, agent }) => {
|
|
259688
259942
|
const unresolved = requireResolvedProject("build project context for the current project");
|
|
259689
259943
|
if (unresolved) return unresolved;
|
|
259690
259944
|
const [
|
|
@@ -259696,12 +259950,14 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
259696
259950
|
},
|
|
259697
259951
|
{ getObservationStore: getObservationStore2 },
|
|
259698
259952
|
{ MaintenanceJobStore: MaintenanceJobStore2 },
|
|
259699
|
-
{ enqueueCodegraphRefresh: enqueueCodegraphRefresh2 }
|
|
259953
|
+
{ enqueueCodegraphRefresh: enqueueCodegraphRefresh2 },
|
|
259954
|
+
{ buildBoundedContextReceipt: buildBoundedContextReceipt2 }
|
|
259700
259955
|
] = await Promise.all([
|
|
259701
259956
|
Promise.resolve().then(() => (init_auto_context(), auto_context_exports)),
|
|
259702
259957
|
Promise.resolve().then(() => (init_obs_store(), obs_store_exports)),
|
|
259703
259958
|
Promise.resolve().then(() => (init_maintenance_jobs(), maintenance_jobs_exports)),
|
|
259704
|
-
Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports))
|
|
259959
|
+
Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports)),
|
|
259960
|
+
Promise.resolve().then(() => (init_context_receipt(), context_receipt_exports))
|
|
259705
259961
|
]);
|
|
259706
259962
|
const observations2 = filterReadableObservations(
|
|
259707
259963
|
await getObservationStore2().loadByProject(project.id, { status: "active" }),
|
|
@@ -259712,6 +259968,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
259712
259968
|
dataDir: projectDir2,
|
|
259713
259969
|
observations: observations2,
|
|
259714
259970
|
task,
|
|
259971
|
+
agent,
|
|
259715
259972
|
refresh: refresh ?? "auto",
|
|
259716
259973
|
reader: getObservationReader(),
|
|
259717
259974
|
enqueueRefresh: () => {
|
|
@@ -259724,7 +259981,10 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
259724
259981
|
});
|
|
259725
259982
|
}
|
|
259726
259983
|
});
|
|
259727
|
-
const text2 = format === "json" ? JSON.stringify({ ...context, brief: buildAutoProjectBrief2(context) }, null, 2) : format === "
|
|
259984
|
+
const text2 = format === "json" ? JSON.stringify({ ...context, brief: buildAutoProjectBrief2(context) }, null, 2) : format === "receipt" ? JSON.stringify(buildBoundedContextReceipt2({
|
|
259985
|
+
workset: context.workset,
|
|
259986
|
+
providerQuality: context.providerQuality
|
|
259987
|
+
}), null, 2) : format === "summary" ? formatAutoProjectContextSummary2(context) : formatAutoProjectContextPrompt2(context);
|
|
259728
259988
|
autopilotRetrievalBoundary = {
|
|
259729
259989
|
projectId: project.id,
|
|
259730
259990
|
issuedAt: Date.now(),
|
|
@@ -265691,6 +265951,7 @@ var init_control_plane_maintenance = __esm({
|
|
|
265691
265951
|
var serve_http_exports = {};
|
|
265692
265952
|
__export(serve_http_exports, {
|
|
265693
265953
|
DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
|
|
265954
|
+
EXPIRED_SESSION_TTL_MS: () => EXPIRED_SESSION_TTL_MS,
|
|
265694
265955
|
_testing: () => _testing2,
|
|
265695
265956
|
default: () => serve_http_default,
|
|
265696
265957
|
parseSessionTimeoutMs: () => parseSessionTimeoutMs,
|
|
@@ -265700,7 +265961,7 @@ function parseSessionTimeoutMs(raw) {
|
|
|
265700
265961
|
const value = raw?.trim();
|
|
265701
265962
|
if (!value) return DEFAULT_SESSION_TIMEOUT_MS;
|
|
265702
265963
|
const parsed = Number(value);
|
|
265703
|
-
if (!Number.isFinite(parsed) || parsed
|
|
265964
|
+
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_SESSION_TIMEOUT_MS;
|
|
265704
265965
|
return Math.floor(parsed);
|
|
265705
265966
|
}
|
|
265706
265967
|
function resolveServeHttpDefaults(argsPort, configured) {
|
|
@@ -265712,7 +265973,7 @@ function resolveServeHttpDefaults(argsPort, configured) {
|
|
|
265712
265973
|
const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
|
|
265713
265974
|
return { port, transportNotice };
|
|
265714
265975
|
}
|
|
265715
|
-
var DEFAULT_SESSION_TIMEOUT_MS, serve_http_default, _testing2;
|
|
265976
|
+
var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, serve_http_default, _testing2;
|
|
265716
265977
|
var init_serve_http = __esm({
|
|
265717
265978
|
"src/cli/commands/serve-http.ts"() {
|
|
265718
265979
|
"use strict";
|
|
@@ -265724,7 +265985,8 @@ var init_serve_http = __esm({
|
|
|
265724
265985
|
init_visibility();
|
|
265725
265986
|
init_port();
|
|
265726
265987
|
init_mcp_root_path();
|
|
265727
|
-
DEFAULT_SESSION_TIMEOUT_MS =
|
|
265988
|
+
DEFAULT_SESSION_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
|
|
265989
|
+
EXPIRED_SESSION_TTL_MS = 10 * 60 * 1e3;
|
|
265728
265990
|
serve_http_default = defineCommand({
|
|
265729
265991
|
meta: {
|
|
265730
265992
|
name: "serve-http",
|
|
@@ -265783,7 +266045,9 @@ var init_serve_http = __esm({
|
|
|
265783
266045
|
console.error(`[memorix] HTTP transport starting on ${host}:${port}`);
|
|
265784
266046
|
console.error(`[memorix] Project root: ${projectRoot}`);
|
|
265785
266047
|
const sessionTimeoutMs = parseSessionTimeoutMs(process.env.MEMORIX_SESSION_TIMEOUT_MS);
|
|
265786
|
-
console.error(
|
|
266048
|
+
console.error(
|
|
266049
|
+
sessionTimeoutMs === 0 ? "[memorix] HTTP session idle timeout: disabled" : `[memorix] HTTP session idle timeout: ${Math.round(sessionTimeoutMs / 6e4)}min (${sessionTimeoutMs}ms)`
|
|
266050
|
+
);
|
|
265787
266051
|
const teamStoreCache = /* @__PURE__ */ new Map();
|
|
265788
266052
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
265789
266053
|
async function getTeamStore2(dataDir) {
|
|
@@ -265800,6 +266064,7 @@ var init_serve_http = __esm({
|
|
|
265800
266064
|
const sharedTeamStore = await getTeamStore2(teamDataDir);
|
|
265801
266065
|
const sessions = /* @__PURE__ */ new Map();
|
|
265802
266066
|
const sessionLastActivity = /* @__PURE__ */ new Map();
|
|
266067
|
+
const expiredSessions = /* @__PURE__ */ new Map();
|
|
265803
266068
|
let suppressedProbeCount = 0;
|
|
265804
266069
|
let lastProbeSummaryTime = Date.now();
|
|
265805
266070
|
const PROBE_SUMMARY_INTERVAL_MS = 6e4;
|
|
@@ -265812,6 +266077,26 @@ var init_serve_http = __esm({
|
|
|
265812
266077
|
lastProbeSummaryTime = Date.now();
|
|
265813
266078
|
}
|
|
265814
266079
|
}
|
|
266080
|
+
function forgetExpiredSessions(now3 = Date.now()) {
|
|
266081
|
+
for (const [sessionId, expiresAt] of expiredSessions) {
|
|
266082
|
+
if (expiresAt <= now3) expiredSessions.delete(sessionId);
|
|
266083
|
+
}
|
|
266084
|
+
}
|
|
266085
|
+
function sendUnknownSession(res, sessionId, jsonRpc = false) {
|
|
266086
|
+
const expired = Boolean(sessionId && expiredSessions.has(sessionId));
|
|
266087
|
+
const message = expired ? "MCP session expired after inactivity. Initialize a new session and retry the request." : "MCP session not found. Initialize a new session and retry the request.";
|
|
266088
|
+
if (jsonRpc) {
|
|
266089
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
266090
|
+
res.end(JSON.stringify({
|
|
266091
|
+
jsonrpc: "2.0",
|
|
266092
|
+
error: { code: -32001, message },
|
|
266093
|
+
id: null
|
|
266094
|
+
}));
|
|
266095
|
+
return;
|
|
266096
|
+
}
|
|
266097
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
266098
|
+
res.end(message);
|
|
266099
|
+
}
|
|
265815
266100
|
function parseBody(req) {
|
|
265816
266101
|
return new Promise((resolve6, reject) => {
|
|
265817
266102
|
const chunks = [];
|
|
@@ -265928,6 +266213,10 @@ var init_serve_http = __esm({
|
|
|
265928
266213
|
await sessions.get(sessionId).transport.handleRequest(req, res, body);
|
|
265929
266214
|
return;
|
|
265930
266215
|
}
|
|
266216
|
+
if (sessionId) {
|
|
266217
|
+
sendUnknownSession(res, sessionId, true);
|
|
266218
|
+
return;
|
|
266219
|
+
}
|
|
265931
266220
|
if (!sessionId && isInitializeRequest2(body)) {
|
|
265932
266221
|
let createdState = null;
|
|
265933
266222
|
const transport = new StreamableHTTPServerTransport2({
|
|
@@ -266026,8 +266315,12 @@ var init_serve_http = __esm({
|
|
|
266026
266315
|
async function handleGet(req, res) {
|
|
266027
266316
|
const sessionId = req.headers["mcp-session-id"];
|
|
266028
266317
|
if (!sessionId || !sessions.has(sessionId)) {
|
|
266029
|
-
|
|
266030
|
-
|
|
266318
|
+
if (!sessionId) {
|
|
266319
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
266320
|
+
res.end("Missing MCP session ID");
|
|
266321
|
+
return;
|
|
266322
|
+
}
|
|
266323
|
+
sendUnknownSession(res, sessionId);
|
|
266031
266324
|
return;
|
|
266032
266325
|
}
|
|
266033
266326
|
await sessions.get(sessionId).transport.handleRequest(req, res);
|
|
@@ -266035,8 +266328,12 @@ var init_serve_http = __esm({
|
|
|
266035
266328
|
async function handleDelete(req, res) {
|
|
266036
266329
|
const sessionId = req.headers["mcp-session-id"];
|
|
266037
266330
|
if (!sessionId || !sessions.has(sessionId)) {
|
|
266038
|
-
|
|
266039
|
-
|
|
266331
|
+
if (!sessionId) {
|
|
266332
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
266333
|
+
res.end("Missing MCP session ID");
|
|
266334
|
+
return;
|
|
266335
|
+
}
|
|
266336
|
+
sendUnknownSession(res, sessionId);
|
|
266040
266337
|
return;
|
|
266041
266338
|
}
|
|
266042
266339
|
await sessions.get(sessionId).transport.handleRequest(req, res);
|
|
@@ -266785,13 +267082,15 @@ var init_serve_http = __esm({
|
|
|
266785
267082
|
}
|
|
266786
267083
|
const url2 = new URL(req.url || "/", `http://localhost:${port}`);
|
|
266787
267084
|
if (url2.pathname === "/health") {
|
|
267085
|
+
const { getEmbeddingRuntimeHealth: getEmbeddingRuntimeHealth2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
266788
267086
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
266789
267087
|
res.end(JSON.stringify({
|
|
266790
267088
|
status: "ok",
|
|
266791
267089
|
mode: "control-plane",
|
|
266792
267090
|
port,
|
|
266793
267091
|
uptime: Math.round(process.uptime()),
|
|
266794
|
-
pid: process.pid
|
|
267092
|
+
pid: process.pid,
|
|
267093
|
+
embedding: getEmbeddingRuntimeHealth2()
|
|
266795
267094
|
}));
|
|
266796
267095
|
return;
|
|
266797
267096
|
}
|
|
@@ -266849,6 +267148,8 @@ var init_serve_http = __esm({
|
|
|
266849
267148
|
}
|
|
266850
267149
|
await serveDashStatic(req, res);
|
|
266851
267150
|
});
|
|
267151
|
+
httpServer.keepAliveTimeout = 6e4;
|
|
267152
|
+
httpServer.headersTimeout = 65e3;
|
|
266852
267153
|
httpServer.listen(port, host, () => {
|
|
266853
267154
|
try {
|
|
266854
267155
|
const memorixDir = (process.env.HOME || process.env.USERPROFILE || "").replace(/\\/g, "/") + "/.memorix";
|
|
@@ -266872,10 +267173,13 @@ var init_serve_http = __esm({
|
|
|
266872
267173
|
const gcInterval = setInterval(() => {
|
|
266873
267174
|
maybeProbeSummary();
|
|
266874
267175
|
const now3 = Date.now();
|
|
267176
|
+
forgetExpiredSessions(now3);
|
|
267177
|
+
if (SESSION_TIMEOUT_MS === 0) return;
|
|
266875
267178
|
for (const [sid, state] of sessions) {
|
|
266876
267179
|
const lastActive = sessionLastActivity.get(sid) ?? 0;
|
|
266877
267180
|
if (now3 - lastActive > SESSION_TIMEOUT_MS) {
|
|
266878
267181
|
console.error(`[memorix] Session ${sid.slice(0, 8)}\u2026 timed out (idle ${Math.round((now3 - lastActive) / 6e4)}min), closing`);
|
|
267182
|
+
expiredSessions.set(sid, now3 + EXPIRED_SESSION_TTL_MS);
|
|
266879
267183
|
state.transport.close().catch(() => {
|
|
266880
267184
|
});
|
|
266881
267185
|
sessions.delete(sid);
|
|
@@ -268514,6 +268818,144 @@ var init_pattern_detector = __esm({
|
|
|
268514
268818
|
}
|
|
268515
268819
|
});
|
|
268516
268820
|
|
|
268821
|
+
// src/hooks/lifecycle.ts
|
|
268822
|
+
var lifecycle_exports2 = {};
|
|
268823
|
+
__export(lifecycle_exports2, {
|
|
268824
|
+
DEFAULT_HOOK_STDIN_TIMEOUT_MS: () => DEFAULT_HOOK_STDIN_TIMEOUT_MS,
|
|
268825
|
+
DEFAULT_HOOK_TIMEOUT_MS: () => DEFAULT_HOOK_TIMEOUT_MS,
|
|
268826
|
+
MAX_HOOK_STDIN_TIMEOUT_MS: () => MAX_HOOK_STDIN_TIMEOUT_MS,
|
|
268827
|
+
MAX_HOOK_TIMEOUT_MS: () => MAX_HOOK_TIMEOUT_MS,
|
|
268828
|
+
MIN_HOOK_STDIN_TIMEOUT_MS: () => MIN_HOOK_STDIN_TIMEOUT_MS,
|
|
268829
|
+
MIN_HOOK_TIMEOUT_MS: () => MIN_HOOK_TIMEOUT_MS,
|
|
268830
|
+
hookObservationRuntimeOptions: () => hookObservationRuntimeOptions,
|
|
268831
|
+
readHookStdin: () => readHookStdin,
|
|
268832
|
+
resolveHookStdinTimeoutMs: () => resolveHookStdinTimeoutMs,
|
|
268833
|
+
resolveHookTimeoutMs: () => resolveHookTimeoutMs,
|
|
268834
|
+
runHookWithDeadline: () => runHookWithDeadline
|
|
268835
|
+
});
|
|
268836
|
+
function parseBoundedMs(raw, fallback, min, max) {
|
|
268837
|
+
const value = raw?.trim();
|
|
268838
|
+
if (!value) return fallback;
|
|
268839
|
+
const parsed = Number(value);
|
|
268840
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
|
|
268841
|
+
return Math.max(min, Math.min(max, Math.floor(parsed)));
|
|
268842
|
+
}
|
|
268843
|
+
function resolveHookTimeoutMs(env3 = process.env) {
|
|
268844
|
+
return parseBoundedMs(
|
|
268845
|
+
env3.MEMORIX_HOOK_TIMEOUT_MS,
|
|
268846
|
+
DEFAULT_HOOK_TIMEOUT_MS,
|
|
268847
|
+
MIN_HOOK_TIMEOUT_MS,
|
|
268848
|
+
MAX_HOOK_TIMEOUT_MS
|
|
268849
|
+
);
|
|
268850
|
+
}
|
|
268851
|
+
function resolveHookStdinTimeoutMs(env3 = process.env) {
|
|
268852
|
+
return parseBoundedMs(
|
|
268853
|
+
env3.MEMORIX_HOOK_STDIN_TIMEOUT_MS,
|
|
268854
|
+
DEFAULT_HOOK_STDIN_TIMEOUT_MS,
|
|
268855
|
+
MIN_HOOK_STDIN_TIMEOUT_MS,
|
|
268856
|
+
MAX_HOOK_STDIN_TIMEOUT_MS
|
|
268857
|
+
);
|
|
268858
|
+
}
|
|
268859
|
+
function hookObservationRuntimeOptions(projectRoot) {
|
|
268860
|
+
return {
|
|
268861
|
+
embeddingWriteMode: "deferred",
|
|
268862
|
+
projectRoot
|
|
268863
|
+
};
|
|
268864
|
+
}
|
|
268865
|
+
async function readHookStdin(stdin2, timeoutMs) {
|
|
268866
|
+
return new Promise((resolve6) => {
|
|
268867
|
+
const chunks = [];
|
|
268868
|
+
let settled = false;
|
|
268869
|
+
const finish = () => {
|
|
268870
|
+
if (settled) return;
|
|
268871
|
+
settled = true;
|
|
268872
|
+
clearTimeout(timer);
|
|
268873
|
+
stdin2.removeListener("data", onData);
|
|
268874
|
+
stdin2.removeListener("end", finish);
|
|
268875
|
+
stdin2.removeListener("error", finish);
|
|
268876
|
+
stdin2.pause?.();
|
|
268877
|
+
if (!stdin2.isTTY) {
|
|
268878
|
+
stdin2.destroy?.();
|
|
268879
|
+
} else {
|
|
268880
|
+
stdin2.unref?.();
|
|
268881
|
+
}
|
|
268882
|
+
resolve6(Buffer.concat(chunks).toString("utf-8").trim());
|
|
268883
|
+
};
|
|
268884
|
+
const onData = (chunk) => {
|
|
268885
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
268886
|
+
};
|
|
268887
|
+
const timer = setTimeout(finish, timeoutMs);
|
|
268888
|
+
stdin2.on("data", onData);
|
|
268889
|
+
stdin2.on("end", finish);
|
|
268890
|
+
stdin2.on("error", finish);
|
|
268891
|
+
});
|
|
268892
|
+
}
|
|
268893
|
+
function writeContinue(stdout2) {
|
|
268894
|
+
try {
|
|
268895
|
+
stdout2.write(JSON.stringify({ continue: true }));
|
|
268896
|
+
} catch {
|
|
268897
|
+
}
|
|
268898
|
+
}
|
|
268899
|
+
async function runHookWithDeadline(options) {
|
|
268900
|
+
const stdout2 = options.stdout ?? process.stdout;
|
|
268901
|
+
const stderr = options.stderr ?? process.stderr;
|
|
268902
|
+
const exit = options.exit ?? ((code) => {
|
|
268903
|
+
process.exit(code);
|
|
268904
|
+
});
|
|
268905
|
+
await new Promise((resolve6) => {
|
|
268906
|
+
let finished2 = false;
|
|
268907
|
+
const settle = () => {
|
|
268908
|
+
if (finished2) return;
|
|
268909
|
+
finished2 = true;
|
|
268910
|
+
clearTimeout(timer);
|
|
268911
|
+
resolve6();
|
|
268912
|
+
};
|
|
268913
|
+
const timer = setTimeout(() => {
|
|
268914
|
+
if (finished2) return;
|
|
268915
|
+
try {
|
|
268916
|
+
stderr.write(`[memorix] hook timed out after ${options.timeoutMs}ms
|
|
268917
|
+
`);
|
|
268918
|
+
} catch {
|
|
268919
|
+
}
|
|
268920
|
+
writeContinue(stdout2);
|
|
268921
|
+
exit(0);
|
|
268922
|
+
settle();
|
|
268923
|
+
}, options.timeoutMs);
|
|
268924
|
+
options.run().then(
|
|
268925
|
+
() => {
|
|
268926
|
+
if (finished2) return;
|
|
268927
|
+
exit(0);
|
|
268928
|
+
settle();
|
|
268929
|
+
},
|
|
268930
|
+
(error40) => {
|
|
268931
|
+
if (finished2) return;
|
|
268932
|
+
try {
|
|
268933
|
+
const message = error40 instanceof Error ? error40.message : String(error40);
|
|
268934
|
+
stderr.write(`[memorix] hook failed: ${message}
|
|
268935
|
+
`);
|
|
268936
|
+
} catch {
|
|
268937
|
+
}
|
|
268938
|
+
writeContinue(stdout2);
|
|
268939
|
+
exit(0);
|
|
268940
|
+
settle();
|
|
268941
|
+
}
|
|
268942
|
+
);
|
|
268943
|
+
});
|
|
268944
|
+
}
|
|
268945
|
+
var DEFAULT_HOOK_TIMEOUT_MS, MIN_HOOK_TIMEOUT_MS, MAX_HOOK_TIMEOUT_MS, DEFAULT_HOOK_STDIN_TIMEOUT_MS, MIN_HOOK_STDIN_TIMEOUT_MS, MAX_HOOK_STDIN_TIMEOUT_MS;
|
|
268946
|
+
var init_lifecycle2 = __esm({
|
|
268947
|
+
"src/hooks/lifecycle.ts"() {
|
|
268948
|
+
"use strict";
|
|
268949
|
+
init_esm_shims();
|
|
268950
|
+
DEFAULT_HOOK_TIMEOUT_MS = 2e4;
|
|
268951
|
+
MIN_HOOK_TIMEOUT_MS = 1e3;
|
|
268952
|
+
MAX_HOOK_TIMEOUT_MS = 12e4;
|
|
268953
|
+
DEFAULT_HOOK_STDIN_TIMEOUT_MS = 3e3;
|
|
268954
|
+
MIN_HOOK_STDIN_TIMEOUT_MS = 100;
|
|
268955
|
+
MAX_HOOK_STDIN_TIMEOUT_MS = 3e4;
|
|
268956
|
+
}
|
|
268957
|
+
});
|
|
268958
|
+
|
|
268517
268959
|
// src/hooks/handler.ts
|
|
268518
268960
|
var handler_exports = {};
|
|
268519
268961
|
__export(handler_exports, {
|
|
@@ -268993,26 +269435,7 @@ function formatHookOutput(agent, rawEventName, output) {
|
|
|
268993
269435
|
return finalOutput;
|
|
268994
269436
|
}
|
|
268995
269437
|
async function runHook(agentOverride, eventOverride) {
|
|
268996
|
-
const rawInput = await
|
|
268997
|
-
const chunks = [];
|
|
268998
|
-
const finish = () => resolve6(Buffer.concat(chunks).toString("utf-8").trim());
|
|
268999
|
-
const timer = setTimeout(() => {
|
|
269000
|
-
process.stdin.removeAllListeners("data");
|
|
269001
|
-
process.stdin.removeAllListeners("end");
|
|
269002
|
-
finish();
|
|
269003
|
-
}, 3e3);
|
|
269004
|
-
process.stdin.on("data", (chunk) => {
|
|
269005
|
-
chunks.push(chunk);
|
|
269006
|
-
});
|
|
269007
|
-
process.stdin.on("end", () => {
|
|
269008
|
-
clearTimeout(timer);
|
|
269009
|
-
finish();
|
|
269010
|
-
});
|
|
269011
|
-
process.stdin.on("error", () => {
|
|
269012
|
-
clearTimeout(timer);
|
|
269013
|
-
finish();
|
|
269014
|
-
});
|
|
269015
|
-
});
|
|
269438
|
+
const rawInput = await readHookStdin(process.stdin, resolveHookStdinTimeoutMs());
|
|
269016
269439
|
if (!rawInput) {
|
|
269017
269440
|
process.stdout.write(JSON.stringify({ continue: true }));
|
|
269018
269441
|
return;
|
|
@@ -269062,7 +269485,7 @@ async function runHook(agentOverride, eventOverride) {
|
|
|
269062
269485
|
await initObservationStore2(dataDir);
|
|
269063
269486
|
await initMSStore(dataDir);
|
|
269064
269487
|
await initSessStore(dataDir);
|
|
269065
|
-
await initObservations2(dataDir);
|
|
269488
|
+
await initObservations2(dataDir, hookObservationRuntimeOptions(rawProject.rootPath));
|
|
269066
269489
|
await storeObservation2({ ...observation, projectId, sourceDetail: "hook" });
|
|
269067
269490
|
} catch (storeErr) {
|
|
269068
269491
|
console.error("[memorix] hook store failed:", storeErr?.message ?? storeErr);
|
|
@@ -269108,6 +269531,7 @@ var init_handler = __esm({
|
|
|
269108
269531
|
init_normalizer();
|
|
269109
269532
|
init_pattern_detector();
|
|
269110
269533
|
init_significance_filter();
|
|
269534
|
+
init_lifecycle2();
|
|
269111
269535
|
TYPE_EMOJI2 = {
|
|
269112
269536
|
"gotcha": "[GOTCHA]",
|
|
269113
269537
|
"decision": "[DECISION]",
|
|
@@ -269164,7 +269588,11 @@ var init_hook = __esm({
|
|
|
269164
269588
|
},
|
|
269165
269589
|
run: async ({ args }) => {
|
|
269166
269590
|
const { runHook: runHook2 } = await Promise.resolve().then(() => (init_handler(), handler_exports));
|
|
269167
|
-
await
|
|
269591
|
+
const { resolveHookTimeoutMs: resolveHookTimeoutMs2, runHookWithDeadline: runHookWithDeadline2 } = await Promise.resolve().then(() => (init_lifecycle2(), lifecycle_exports2));
|
|
269592
|
+
await runHookWithDeadline2({
|
|
269593
|
+
timeoutMs: resolveHookTimeoutMs2(),
|
|
269594
|
+
run: () => runHook2(args.agent, args.event)
|
|
269595
|
+
});
|
|
269168
269596
|
}
|
|
269169
269597
|
});
|
|
269170
269598
|
}
|
|
@@ -271930,7 +272358,7 @@ __export(agent_integrations_exports, {
|
|
|
271930
272358
|
repairAgentIntegrations: () => repairAgentIntegrations
|
|
271931
272359
|
});
|
|
271932
272360
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
271933
|
-
import { existsSync as existsSync28 } from "fs";
|
|
272361
|
+
import { existsSync as existsSync28, realpathSync as realpathSync2 } from "fs";
|
|
271934
272362
|
import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
|
|
271935
272363
|
import { basename as basename5, dirname as dirname8, resolve as resolve4 } from "path";
|
|
271936
272364
|
import { homedir as homedir34 } from "os";
|
|
@@ -272324,10 +272752,15 @@ function sanitizeServer(server) {
|
|
|
272324
272752
|
};
|
|
272325
272753
|
}
|
|
272326
272754
|
function normalizeProjectKey(projectRoot) {
|
|
272327
|
-
return
|
|
272755
|
+
return canonicalProjectPath(projectRoot).toLowerCase();
|
|
272328
272756
|
}
|
|
272329
272757
|
function defaultClaudeProjectKey(projectRoot) {
|
|
272330
|
-
return
|
|
272758
|
+
return canonicalProjectPath(projectRoot);
|
|
272759
|
+
}
|
|
272760
|
+
function canonicalProjectPath(projectRoot) {
|
|
272761
|
+
const resolved = resolve4(projectRoot);
|
|
272762
|
+
const canonical = existsSync28(resolved) ? realpathSync2(resolved) : resolved;
|
|
272763
|
+
return canonical.replace(/\\/g, "/");
|
|
272331
272764
|
}
|
|
272332
272765
|
function getClaudeLocalConfigPath() {
|
|
272333
272766
|
return `${homedir34()}/.claude.json`;
|
|
@@ -272936,6 +273369,7 @@ var init_doctor = __esm({
|
|
|
272936
273369
|
let bgRunning = false;
|
|
272937
273370
|
let bgPort = 0;
|
|
272938
273371
|
let bgPid = 0;
|
|
273372
|
+
let backgroundHealthy = false;
|
|
272939
273373
|
try {
|
|
272940
273374
|
if (existsSync33(bgStatePath)) {
|
|
272941
273375
|
const bgState = JSON.parse(readFileSync22(bgStatePath, "utf-8"));
|
|
@@ -272953,7 +273387,7 @@ var init_doctor = __esm({
|
|
|
272953
273387
|
lines.push(ok(`Background control plane running (PID ${bgPid}, port ${bgPort})`));
|
|
272954
273388
|
try {
|
|
272955
273389
|
const http3 = await import("http");
|
|
272956
|
-
|
|
273390
|
+
backgroundHealthy = await new Promise((resolve6) => {
|
|
272957
273391
|
const req = http3.request({ hostname: "127.0.0.1", port: bgPort, path: "/api/team", timeout: 3e3 }, (res) => {
|
|
272958
273392
|
res.resume();
|
|
272959
273393
|
resolve6(res.statusCode === 200);
|
|
@@ -272965,7 +273399,7 @@ var init_doctor = __esm({
|
|
|
272965
273399
|
});
|
|
272966
273400
|
req.end();
|
|
272967
273401
|
});
|
|
272968
|
-
if (
|
|
273402
|
+
if (backgroundHealthy) {
|
|
272969
273403
|
lines.push(ok("Health check: OK"));
|
|
272970
273404
|
} else {
|
|
272971
273405
|
lines.push(warn2("Health check: FAILED (process alive but not responding)"));
|
|
@@ -272973,8 +273407,9 @@ var init_doctor = __esm({
|
|
|
272973
273407
|
}
|
|
272974
273408
|
} catch {
|
|
272975
273409
|
lines.push(warn2("Health check: could not connect"));
|
|
273410
|
+
issues.push('Background process is alive but its health endpoint could not be reached. Try "memorix background restart".');
|
|
272976
273411
|
}
|
|
272977
|
-
report.mode = { type: "background", pid: bgPid, port: bgPort, healthy:
|
|
273412
|
+
report.mode = { type: "background", pid: bgPid, port: bgPort, healthy: backgroundHealthy };
|
|
272978
273413
|
} else {
|
|
272979
273414
|
lines.push(info2("Background control plane: not running"));
|
|
272980
273415
|
lines.push(info2("Current invocation: CLI (stdio)"));
|
|
@@ -273015,18 +273450,39 @@ var init_doctor = __esm({
|
|
|
273015
273450
|
report.embedding = { mode: "off", status: "disabled" };
|
|
273016
273451
|
} else {
|
|
273017
273452
|
lines.push(ok(`Mode: ${mode}`));
|
|
273018
|
-
|
|
273019
|
-
|
|
273020
|
-
|
|
273021
|
-
|
|
273022
|
-
|
|
273023
|
-
|
|
273024
|
-
|
|
273453
|
+
let controlPlaneEmbedding;
|
|
273454
|
+
if (bgRunning) {
|
|
273455
|
+
try {
|
|
273456
|
+
const response = await fetch(`http://127.0.0.1:${bgPort}/health`, { signal: AbortSignal.timeout(3e3) });
|
|
273457
|
+
if (response.ok) {
|
|
273458
|
+
const health = await response.json();
|
|
273459
|
+
controlPlaneEmbedding = health.embedding;
|
|
273460
|
+
}
|
|
273461
|
+
} catch {
|
|
273462
|
+
}
|
|
273463
|
+
}
|
|
273464
|
+
if (controlPlaneEmbedding?.status === "degraded") {
|
|
273465
|
+
const kind = controlPlaneEmbedding.failure?.kind ?? "upstream";
|
|
273466
|
+
const retryAfterMs = controlPlaneEmbedding.failure?.retryAfterMs;
|
|
273467
|
+
const retry2 = typeof retryAfterMs === "number" && retryAfterMs > 0 ? `; retry in ${Math.ceil(retryAfterMs / 1e3)}s` : "";
|
|
273468
|
+
lines.push(warn2(`Status: degraded in the running control plane (${kind}${retry2})`));
|
|
273469
|
+
lines.push(info2("Search: degraded to BM25 fulltext until the provider recovers"));
|
|
273470
|
+
issues.push("The running control plane has an unavailable embedding provider. Check provider credentials, billing, or connectivity.");
|
|
273471
|
+
report.embedding = { mode, status: "degraded", failure: kind };
|
|
273025
273472
|
} else {
|
|
273026
|
-
|
|
273027
|
-
|
|
273028
|
-
|
|
273029
|
-
|
|
273473
|
+
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
273474
|
+
const provider2 = await getEmbeddingProvider2();
|
|
273475
|
+
if (provider2) {
|
|
273476
|
+
lines.push(ok(`Provider: ${provider2.name} (${provider2.dimensions}d)`));
|
|
273477
|
+
lines.push(ok("Status: ready"));
|
|
273478
|
+
lines.push(info2("Search: hybrid (BM25 + vector)"));
|
|
273479
|
+
report.embedding = { mode, status: "ready", provider: provider2.name, dimensions: provider2.dimensions };
|
|
273480
|
+
} else {
|
|
273481
|
+
lines.push(warn2("Status: temporarily unavailable"));
|
|
273482
|
+
lines.push(info2("Search: degraded to BM25 fulltext (no vector similarity)"));
|
|
273483
|
+
issues.push(`Embedding mode is "${mode}" but provider failed to initialize. Check API keys/connectivity.`);
|
|
273484
|
+
report.embedding = { mode, status: "temporarily_unavailable" };
|
|
273485
|
+
}
|
|
273030
273486
|
}
|
|
273031
273487
|
try {
|
|
273032
273488
|
const cachePath = join34(memorixDir, "embedding-cache.json");
|