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.
Files changed (52) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +3 -3
  3. package/README.zh-CN.md +3 -3
  4. package/dist/cli/index.js +558 -102
  5. package/dist/cli/memcode.js +12 -1
  6. package/dist/index.js +221 -27
  7. package/dist/maintenance-runner.js +59 -11
  8. package/dist/media-audio-runner.js +59 -11
  9. package/dist/media-video-runner.js +59 -11
  10. package/dist/memcode-runtime/CHANGELOG.md +47 -0
  11. package/dist/sdk.js +220 -26
  12. package/dist/vector-backfill-runner.js +59 -11
  13. package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +1 -1
  14. package/docs/AGENT_OPERATOR_PLAYBOOK.md +11 -3
  15. package/docs/API_REFERENCE.md +2 -2
  16. package/docs/DEVELOPMENT.md +5 -3
  17. package/docs/DOCKER.md +5 -1
  18. package/docs/PERFORMANCE.md +2 -2
  19. package/docs/README.md +3 -1
  20. package/docs/SETUP.md +8 -2
  21. package/package.json +1 -1
  22. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  23. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  24. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  25. package/plugins/copilot/memorix/plugin.json +1 -1
  26. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  27. package/plugins/omp/memorix/package.json +1 -1
  28. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  29. package/plugins/pi/memorix/package.json +1 -1
  30. package/server.json +2 -2
  31. package/src/cli/commands/agent-integrations.ts +9 -3
  32. package/src/cli/commands/codegraph.ts +48 -5
  33. package/src/cli/commands/context.ts +20 -4
  34. package/src/cli/commands/doctor.ts +46 -15
  35. package/src/cli/commands/hook.ts +13 -1
  36. package/src/cli/commands/resume.ts +4 -0
  37. package/src/cli/commands/serve-http.ts +68 -7
  38. package/src/cli/heap.ts +76 -0
  39. package/src/cli/invocation.ts +7 -4
  40. package/src/codegraph/auto-context.ts +4 -1
  41. package/src/codegraph/context-pack.ts +3 -1
  42. package/src/codegraph/external-provider.ts +93 -0
  43. package/src/embedding/provider.ts +76 -6
  44. package/src/hooks/handler.ts +9 -18
  45. package/src/hooks/installers/index.ts +1 -1
  46. package/src/hooks/lifecycle.ts +173 -0
  47. package/src/hooks/official-skills.ts +3 -3
  48. package/src/hooks/rules/memorix-agent-rules.md +1 -1
  49. package/src/knowledge/context-receipt.ts +41 -0
  50. package/src/knowledge/workflows.ts +6 -3
  51. package/src/knowledge/workset.ts +56 -2
  52. 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
- let r=__ms(process.execPath,["--max-old-space-size=4096",__fu(import.meta.url),...process.argv.slice(2)],{stdio:"inherit",env:process.env,windowsHide:true});
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.5.1" : pkg.version;
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 projectRoot = resolve2(invocation.projectRoot);
3265
- if (!existsSync2(projectRoot) || !statSync(projectRoot).isDirectory()) {
3266
- throw new Error(`CLI project directory does not exist: ${projectRoot}`);
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 temporarily unavailable at runtime: ${message}`);
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 = wrapProvider(initialized);
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 = wrapProvider(initialized);
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 = wrapProvider(initialized);
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 = wrapProvider(initialized);
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 = wrapProvider(localFallback);
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) => !KNOWN_AGENTS.includes(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 KNOWN_AGENTS, WORKFLOW_ADAPTER_TARGETS, STATUS_VALUES, LENS_TERMS, GENERIC_WORKFLOW_LENSES, GENERIC_WORKFLOW_TERMS;
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
- KNOWN_AGENTS = [
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
- const selected = selectWorkflows({
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 = selected.map(workflowOutput);
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
- json: { type: "boolean", description: "Emit machine-readable JSON output" }
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
- json: { type: "boolean", description: "Emit machine-readable JSON output" }
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
- json: { type: "boolean", description: "Emit machine-readable JSON output" }
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({ project, pack, providerQuality: external.quality }, buildContextPackPrompt(pack), asJson);
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.5.1" : "1.0.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 structured.'
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 === "summary" ? formatAutoProjectContextSummary2(context) : formatAutoProjectContextPrompt2(context);
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 <= 0) return DEFAULT_SESSION_TIMEOUT_MS;
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 = 30 * 60 * 1e3;
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(`[memorix] HTTP session idle timeout: ${Math.round(sessionTimeoutMs / 6e4)}min (${sessionTimeoutMs}ms)`);
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
- res.writeHead(400, { "Content-Type": "text/plain" });
266030
- res.end("Invalid or missing session ID");
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
- res.writeHead(400, { "Content-Type": "text/plain" });
266039
- res.end("Invalid or missing session ID");
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 new Promise((resolve6) => {
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 runHook2(args.agent, args.event);
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 resolve4(projectRoot).replace(/\\/g, "/").toLowerCase();
272755
+ return canonicalProjectPath(projectRoot).toLowerCase();
272328
272756
  }
272329
272757
  function defaultClaudeProjectKey(projectRoot) {
272330
- return resolve4(projectRoot).replace(/\\/g, "/");
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
- const healthy = await new Promise((resolve6) => {
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 (healthy) {
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: true };
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
- const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
273019
- const provider2 = await getEmbeddingProvider2();
273020
- if (provider2) {
273021
- lines.push(ok(`Provider: ${provider2.name} (${provider2.dimensions}d)`));
273022
- lines.push(ok("Status: ready"));
273023
- lines.push(info2("Search: hybrid (BM25 + vector)"));
273024
- report.embedding = { mode, status: "ready", provider: provider2.name, dimensions: provider2.dimensions };
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
- lines.push(warn2("Status: temporarily unavailable"));
273027
- lines.push(info2("Search: degraded to BM25 fulltext (no vector similarity)"));
273028
- issues.push(`Embedding mode is "${mode}" but provider failed to initialize. Check API keys/connectivity.`);
273029
- report.embedding = { mode, status: "temporarily_unavailable" };
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");