memorix 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +59 -1
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/assets/star-history-dark.svg +4 -4
  5. package/assets/star-history-light.svg +4 -4
  6. package/assets/star-history.json +47 -3
  7. package/dist/cli/index.js +1801 -254
  8. package/dist/dashboard/static/app.js +88 -4
  9. package/dist/dashboard/static/index.html +10 -0
  10. package/dist/dashboard/static/style.css +86 -0
  11. package/dist/index.js +1241 -224
  12. package/dist/maintenance-runner.js +972 -126
  13. package/dist/media-audio-runner.js +967 -121
  14. package/dist/media-video-runner.js +972 -126
  15. package/dist/memcode-runtime/CHANGELOG.md +59 -1
  16. package/dist/sdk.js +1240 -223
  17. package/dist/types.d.ts +8 -0
  18. package/dist/vector-backfill-runner.js +972 -126
  19. package/docs/1.2.4-PERSISTENT-MEMORY-DELIVERY.md +1 -1
  20. package/docs/1.8.0-RELEASE-CANDIDATE-SPEC.md +75 -0
  21. package/docs/API_REFERENCE.md +37 -1
  22. package/docs/ARCHITECTURE.md +1 -1
  23. package/docs/CONFIGURATION.md +57 -1
  24. package/docs/DASHBOARD.md +10 -0
  25. package/docs/DEVELOPMENT.md +3 -2
  26. package/docs/INTEGRATIONS.md +1 -1
  27. package/docs/PERFORMANCE.md +3 -1
  28. package/docs/README.md +11 -1
  29. package/docs/SETUP.md +2 -1
  30. package/package.json +1 -1
  31. package/plugins/antigravity/memorix/rules/memorix.md +1 -1
  32. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  33. package/plugins/claude/memorix/SKILL.md +1 -1
  34. package/plugins/claude/memorix/skills/memorix-memory/SKILL.md +2 -2
  35. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  36. package/plugins/codebuddy/memorix/skills/memorix-memory/SKILL.md +2 -2
  37. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  38. package/plugins/codex/memorix/skills/memorix-memory/SKILL.md +2 -2
  39. package/plugins/copilot/memorix/plugin.json +1 -1
  40. package/plugins/copilot/memorix/skills/memorix-memory/SKILL.md +2 -2
  41. package/plugins/cursor/memorix/skills/memorix-memory/SKILL.md +2 -2
  42. package/plugins/gemini/memorix/GEMINI.md +1 -1
  43. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  44. package/plugins/omp/memorix/package.json +1 -1
  45. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  46. package/plugins/pi/memorix/package.json +1 -1
  47. package/plugins/pi/memorix/skills/memorix-memory/SKILL.md +2 -2
  48. package/server.json +2 -2
  49. package/src/cli/capability-map.ts +2 -0
  50. package/src/cli/commands/config-migrate.ts +7 -0
  51. package/src/cli/commands/context.ts +11 -0
  52. package/src/cli/commands/doctor.ts +5 -1
  53. package/src/cli/commands/evidence.ts +56 -0
  54. package/src/cli/commands/feedback.ts +58 -0
  55. package/src/cli/commands/init.ts +17 -0
  56. package/src/cli/commands/resume.ts +2 -0
  57. package/src/cli/commands/serve-http.ts +201 -12
  58. package/src/cli/commands/status.ts +2 -0
  59. package/src/cli/fallback-budget.ts +59 -0
  60. package/src/cli/index.ts +5 -1
  61. package/src/compact/engine.ts +26 -1
  62. package/src/config/dotenv-loader.ts +17 -0
  63. package/src/config/resolved-config.ts +110 -12
  64. package/src/config/toml-loader.ts +8 -0
  65. package/src/config/yaml-loader.ts +9 -0
  66. package/src/dashboard/config-provenance.ts +17 -0
  67. package/src/dashboard/evidence.ts +63 -0
  68. package/src/dashboard/server.ts +93 -68
  69. package/src/dashboard/static/index.html +10 -0
  70. package/src/dashboard/static/style.css +86 -0
  71. package/src/hooks/installers/index.ts +1 -1
  72. package/src/hooks/official-skills.ts +3 -3
  73. package/src/hooks/rules/memorix-agent-rules.md +1 -1
  74. package/src/llm/provider.ts +33 -7
  75. package/src/memory/feedback-model.ts +4 -0
  76. package/src/memory/feedback.ts +220 -0
  77. package/src/memory/observations.ts +19 -0
  78. package/src/memory/retention.ts +38 -5
  79. package/src/multimodal/image-loader.ts +6 -3
  80. package/src/rerank/http-provider.ts +125 -0
  81. package/src/rerank/index.ts +81 -0
  82. package/src/rerank/policy.ts +88 -0
  83. package/src/server/mcp-binding-store.ts +79 -0
  84. package/src/server/mcp-diagnostics.ts +40 -0
  85. package/src/server/tool-profile.ts +4 -0
  86. package/src/server.ts +121 -1
  87. package/src/store/evidence-store.ts +261 -0
  88. package/src/store/orama-store.ts +79 -53
  89. package/src/store/sqlite-db.ts +104 -0
  90. package/src/types.ts +8 -0
package/dist/cli/index.js CHANGED
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3232
3232
 
3233
3233
  // src/cli/version.ts
3234
3234
  function getCliVersion() {
3235
- return true ? "1.7.2" : pkg.version;
3235
+ return true ? "1.8.0" : pkg.version;
3236
3236
  }
3237
3237
  var init_version = __esm({
3238
3238
  "src/cli/version.ts"() {
@@ -9465,7 +9465,7 @@ function closeAllDatabases() {
9465
9465
  _dbCache.delete(key);
9466
9466
  }
9467
9467
  }
9468
- var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
9468
+ var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_EVIDENCE_CARDS_TABLE, CREATE_EVIDENCE_CARD_EVENTS_TABLE, CREATE_MEMORY_FEEDBACK_STATES_TABLE, CREATE_MEMORY_FEEDBACK_EVENTS_TABLE, CREATE_MCP_BINDINGS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
9469
9469
  var init_sqlite_db = __esm({
9470
9470
  "src/store/sqlite-db.ts"() {
9471
9471
  "use strict";
@@ -10111,6 +10111,79 @@ CREATE TABLE IF NOT EXISTS compaction_checkpoints (
10111
10111
  created_at TEXT NOT NULL,
10112
10112
  updated_at TEXT NOT NULL
10113
10113
  );
10114
+ `;
10115
+ CREATE_EVIDENCE_CARDS_TABLE = `
10116
+ CREATE TABLE IF NOT EXISTS evidence_cards (
10117
+ id TEXT PRIMARY KEY,
10118
+ project_id TEXT NOT NULL,
10119
+ candidate_kind TEXT NOT NULL,
10120
+ candidate_id TEXT NOT NULL,
10121
+ title TEXT NOT NULL,
10122
+ summary TEXT NOT NULL DEFAULT '',
10123
+ entity_name TEXT,
10124
+ source_kind TEXT NOT NULL,
10125
+ source_ref TEXT NOT NULL,
10126
+ locator TEXT,
10127
+ captured_hash TEXT,
10128
+ session_id TEXT,
10129
+ status TEXT NOT NULL DEFAULT 'active',
10130
+ verification TEXT NOT NULL DEFAULT 'unverified',
10131
+ freshness TEXT NOT NULL DEFAULT 'current',
10132
+ stale_reason TEXT,
10133
+ files_json TEXT NOT NULL DEFAULT '[]',
10134
+ related_entities_json TEXT NOT NULL DEFAULT '[]',
10135
+ created_at TEXT NOT NULL,
10136
+ updated_at TEXT NOT NULL,
10137
+ UNIQUE(project_id, candidate_kind, candidate_id)
10138
+ );
10139
+ `;
10140
+ CREATE_EVIDENCE_CARD_EVENTS_TABLE = `
10141
+ CREATE TABLE IF NOT EXISTS evidence_card_events (
10142
+ id TEXT PRIMARY KEY,
10143
+ project_id TEXT NOT NULL,
10144
+ card_id TEXT NOT NULL,
10145
+ kind TEXT NOT NULL,
10146
+ detail TEXT,
10147
+ created_at TEXT NOT NULL,
10148
+ FOREIGN KEY (card_id) REFERENCES evidence_cards(id) ON DELETE CASCADE
10149
+ );
10150
+ `;
10151
+ CREATE_MEMORY_FEEDBACK_STATES_TABLE = `
10152
+ CREATE TABLE IF NOT EXISTS memory_feedback_states (
10153
+ project_id TEXT NOT NULL,
10154
+ candidate_kind TEXT NOT NULL,
10155
+ candidate_id TEXT NOT NULL,
10156
+ weight REAL NOT NULL DEFAULT 1.0,
10157
+ lifecycle_status TEXT NOT NULL DEFAULT 'active',
10158
+ updated_at TEXT NOT NULL,
10159
+ PRIMARY KEY (project_id, candidate_kind, candidate_id)
10160
+ );
10161
+ `;
10162
+ CREATE_MEMORY_FEEDBACK_EVENTS_TABLE = `
10163
+ CREATE TABLE IF NOT EXISTS memory_feedback_events (
10164
+ id TEXT PRIMARY KEY,
10165
+ project_id TEXT NOT NULL,
10166
+ candidate_kind TEXT NOT NULL,
10167
+ candidate_id TEXT NOT NULL,
10168
+ signal TEXT NOT NULL,
10169
+ actor TEXT,
10170
+ note TEXT,
10171
+ source_ref TEXT NOT NULL,
10172
+ target_event_id TEXT,
10173
+ delta REAL NOT NULL DEFAULT 0,
10174
+ observed_at TEXT NOT NULL
10175
+ );
10176
+ `;
10177
+ CREATE_MCP_BINDINGS_TABLE = `
10178
+ CREATE TABLE IF NOT EXISTS mcp_bindings (
10179
+ handle_id TEXT PRIMARY KEY,
10180
+ project_id TEXT NOT NULL,
10181
+ project_root TEXT NOT NULL,
10182
+ data_dir TEXT NOT NULL,
10183
+ created_at TEXT NOT NULL,
10184
+ last_used_at TEXT NOT NULL,
10185
+ expires_at TEXT
10186
+ );
10114
10187
  `;
10115
10188
  CREATE_INDEXES = `
10116
10189
  CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
@@ -10173,6 +10246,12 @@ CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_target
10173
10246
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
10174
10247
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
10175
10248
  CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
10249
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC);
10250
+ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id);
10251
+ CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
10252
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
10253
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
10254
+ CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
10176
10255
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10177
10256
  ON maintenance_jobs(project_id, kind, dedupe_key)
10178
10257
  WHERE status IN ('pending', 'running', 'retry');
@@ -10328,6 +10407,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10328
10407
  }
10329
10408
  db2.exec("CREATE INDEX IF NOT EXISTS idx_media_jobs_source_asset ON media_jobs(project_id, source_asset_id, status)");
10330
10409
  }
10410
+ },
10411
+ {
10412
+ id: "1.8-context-control-plane",
10413
+ apply: (db2) => {
10414
+ db2.exec(CREATE_EVIDENCE_CARDS_TABLE);
10415
+ db2.exec(CREATE_EVIDENCE_CARD_EVENTS_TABLE);
10416
+ db2.exec(CREATE_MEMORY_FEEDBACK_STATES_TABLE);
10417
+ db2.exec(CREATE_MEMORY_FEEDBACK_EVENTS_TABLE);
10418
+ db2.exec(CREATE_MCP_BINDINGS_TABLE);
10419
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_project ON evidence_cards(project_id, status, updated_at DESC)");
10420
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(project_id, candidate_kind, candidate_id)");
10421
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at)");
10422
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
10423
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC)");
10424
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
10425
+ }
10331
10426
  }
10332
10427
  ];
10333
10428
  _dbCache = /* @__PURE__ */ new Map();
@@ -20748,6 +20843,7 @@ function loadYamlConfig(projectRoot) {
20748
20843
  llm: { ...userConfig.llm, ...projectConfig.llm },
20749
20844
  agent: { ...userConfig.agent, ...projectConfig.agent },
20750
20845
  embedding: { ...userConfig.embedding, ...projectConfig.embedding },
20846
+ rerank: { ...userConfig.rerank, ...projectConfig.rerank },
20751
20847
  git: { ...userConfig.git, ...projectConfig.git },
20752
20848
  codegraph: { ...userConfig.codegraph, ...projectConfig.codegraph },
20753
20849
  behavior: { ...userConfig.behavior, ...projectConfig.behavior },
@@ -21024,6 +21120,7 @@ function mergeTomlConfig(base, override) {
21024
21120
  llm: { ...base.memory?.llm, ...override.memory?.llm }
21025
21121
  },
21026
21122
  embedding: { ...base.embedding, ...override.embedding },
21123
+ rerank: { ...base.rerank, ...override.rerank },
21027
21124
  hooks: { ...base.hooks, ...override.hooks },
21028
21125
  git: { ...base.git, ...override.git },
21029
21126
  codegraph: { ...base.codegraph, ...override.codegraph },
@@ -21512,6 +21609,8 @@ var require_main = __commonJS({
21512
21609
  var dotenv_loader_exports = {};
21513
21610
  __export(dotenv_loader_exports, {
21514
21611
  getLoadedEnvFiles: () => getLoadedEnvFiles,
21612
+ getLoadedEnvKeys: () => getLoadedEnvKeys,
21613
+ getLoadedEnvValue: () => getLoadedEnvValue,
21515
21614
  loadDotenv: () => loadDotenv,
21516
21615
  resetDotenv: () => resetDotenv
21517
21616
  });
@@ -21522,6 +21621,8 @@ function loadEnvFile(filePath) {
21522
21621
  const parsed = (0, import_dotenv.parse)(readFileSync7(filePath, "utf-8"));
21523
21622
  for (const [key, value] of Object.entries(parsed)) {
21524
21623
  if (!(key in process.env)) {
21624
+ loadedEnvKeys.add(key);
21625
+ loadedEnvValues.set(key, value);
21525
21626
  process.env[key] = value;
21526
21627
  injectedKeys.add(key);
21527
21628
  }
@@ -21534,6 +21635,8 @@ function loadDotenv(projectRoot, options = {}) {
21534
21635
  resetDotenv();
21535
21636
  }
21536
21637
  loadedEnvFiles.length = 0;
21638
+ loadedEnvKeys.clear();
21639
+ loadedEnvValues.clear();
21537
21640
  if (projectRoot) {
21538
21641
  loadEnvFile(getProjectDotenvPath(projectRoot));
21539
21642
  }
@@ -21549,11 +21652,18 @@ function resetDotenv() {
21549
21652
  dotenvLoaded = false;
21550
21653
  dotenvProjectRoot = null;
21551
21654
  loadedEnvFiles.length = 0;
21655
+ loadedEnvKeys.clear();
21552
21656
  }
21553
21657
  function getLoadedEnvFiles() {
21554
21658
  return loadedEnvFiles;
21555
21659
  }
21556
- var import_dotenv, dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, injectedKeys;
21660
+ function getLoadedEnvKeys() {
21661
+ return loadedEnvKeys;
21662
+ }
21663
+ function getLoadedEnvValue(key) {
21664
+ return loadedEnvValues.get(key);
21665
+ }
21666
+ var import_dotenv, dotenvLoaded, dotenvProjectRoot, loadedEnvFiles, loadedEnvKeys, loadedEnvValues, injectedKeys;
21557
21667
  var init_dotenv_loader = __esm({
21558
21668
  "src/config/dotenv-loader.ts"() {
21559
21669
  "use strict";
@@ -21563,6 +21673,8 @@ var init_dotenv_loader = __esm({
21563
21673
  dotenvLoaded = false;
21564
21674
  dotenvProjectRoot = null;
21565
21675
  loadedEnvFiles = [];
21676
+ loadedEnvKeys = /* @__PURE__ */ new Set();
21677
+ loadedEnvValues = /* @__PURE__ */ new Map();
21566
21678
  injectedKeys = /* @__PURE__ */ new Set();
21567
21679
  }
21568
21680
  });
@@ -21575,6 +21687,7 @@ __export(resolved_config_exports, {
21575
21687
  getResolvedConfigForCwd: () => getResolvedConfigForCwd,
21576
21688
  getResolvedEmbeddingLane: () => getResolvedEmbeddingLane,
21577
21689
  getResolvedMemoryLane: () => getResolvedMemoryLane,
21690
+ getResolvedRerankLane: () => getResolvedRerankLane,
21578
21691
  resetResolvedConfigCache: () => resetResolvedConfigCache
21579
21692
  });
21580
21693
  import { homedir as homedir5 } from "os";
@@ -21607,6 +21720,16 @@ function getResolvedConfig(options = {}) {
21607
21720
  legacy.llm?.baseUrl
21608
21721
  );
21609
21722
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
21723
+ const memoryLlmApiKey = first(
21724
+ process.env.MEMORIX_LLM_API_KEY,
21725
+ process.env.MEMORIX_API_KEY,
21726
+ toml.memory?.llm?.api_key,
21727
+ yaml.llm?.apiKey,
21728
+ legacy.llm?.apiKey,
21729
+ process.env.OPENAI_API_KEY,
21730
+ process.env.ANTHROPIC_API_KEY,
21731
+ openRouterMemoryLlmApiKey
21732
+ );
21610
21733
  const resolved = {
21611
21734
  agent: {
21612
21735
  provider: first(
@@ -21647,16 +21770,7 @@ function getResolvedConfig(options = {}) {
21647
21770
  provider: memoryLlmProvider,
21648
21771
  model: memoryLlmModel,
21649
21772
  baseUrl: memoryLlmBaseUrl,
21650
- apiKey: first(
21651
- process.env.MEMORIX_LLM_API_KEY,
21652
- process.env.MEMORIX_API_KEY,
21653
- toml.memory?.llm?.api_key,
21654
- yaml.llm?.apiKey,
21655
- legacy.llm?.apiKey,
21656
- process.env.OPENAI_API_KEY,
21657
- process.env.ANTHROPIC_API_KEY,
21658
- openRouterMemoryLlmApiKey
21659
- )
21773
+ apiKey: memoryLlmApiKey
21660
21774
  }
21661
21775
  },
21662
21776
  embedding: {
@@ -21666,6 +21780,12 @@ function getResolvedConfig(options = {}) {
21666
21780
  apiKey: first(process.env.MEMORIX_EMBEDDING_API_KEY, toml.embedding?.api_key, yaml.embedding?.apiKey, legacy.embeddingApi?.apiKey, openRouterEmbeddingApiKey),
21667
21781
  dimensions: firstNumber(parseNumber(process.env.MEMORIX_EMBEDDING_DIMENSIONS), toml.embedding?.dimensions, yaml.embedding?.dimensions, legacy.embeddingApi?.dimensions)
21668
21782
  },
21783
+ rerank: resolveRerankLane({
21784
+ toml,
21785
+ yaml,
21786
+ memoryLlmApiKey,
21787
+ memoryLlmBaseUrl
21788
+ }),
21669
21789
  git: {
21670
21790
  autoHook: firstBool(toml.git?.auto_hook, yaml.git?.autoHook),
21671
21791
  ingestOnCommit: firstBool(toml.git?.ingest_on_commit, yaml.git?.ingestOnCommit),
@@ -21735,6 +21855,9 @@ function getResolvedMemoryLane(options = {}) {
21735
21855
  function getResolvedEmbeddingLane(options = {}) {
21736
21856
  return getResolvedConfig(options).embedding;
21737
21857
  }
21858
+ function getResolvedRerankLane(options = {}) {
21859
+ return getResolvedConfig(options).rerank;
21860
+ }
21738
21861
  function resetResolvedConfigCache() {
21739
21862
  }
21740
21863
  function first(...values) {
@@ -21777,6 +21900,10 @@ function getEnvSourceNames() {
21777
21900
  "MEMORIX_EMBEDDING_BASE_URL",
21778
21901
  "MEMORIX_EMBEDDING_MODEL",
21779
21902
  "MEMORIX_EMBEDDING_DIMENSIONS",
21903
+ "MEMORIX_RERANK_PROVIDER",
21904
+ "MEMORIX_RERANK_MODEL",
21905
+ "MEMORIX_RERANK_BASE_URL",
21906
+ "MEMORIX_RERANK_API_KEY",
21780
21907
  "MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
21781
21908
  "MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
21782
21909
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
@@ -21800,6 +21927,63 @@ function normalizeExternalContext(value) {
21800
21927
  if (normalized === "auto" || normalized === "off") return normalized;
21801
21928
  return void 0;
21802
21929
  }
21930
+ function normalizeRerankProvider(value) {
21931
+ const normalized = value?.trim().toLowerCase();
21932
+ if (normalized === "http") return "http";
21933
+ return "off";
21934
+ }
21935
+ function resolveRerankLane(args) {
21936
+ const provider2 = normalizeRerankProvider(first(
21937
+ process.env.MEMORIX_RERANK_PROVIDER,
21938
+ args.toml.rerank?.provider,
21939
+ args.yaml.rerank?.provider,
21940
+ "off"
21941
+ ));
21942
+ const model = first(
21943
+ process.env.MEMORIX_RERANK_MODEL,
21944
+ args.toml.rerank?.model,
21945
+ args.yaml.rerank?.model
21946
+ );
21947
+ const explicitBaseUrl = first(
21948
+ process.env.MEMORIX_RERANK_BASE_URL,
21949
+ args.toml.rerank?.base_url,
21950
+ args.yaml.rerank?.baseUrl
21951
+ );
21952
+ const baseUrl = provider2 === "http" ? first(explicitBaseUrl, args.memoryLlmBaseUrl) : explicitBaseUrl;
21953
+ const dedicatedApiKey = first(
21954
+ process.env.MEMORIX_RERANK_API_KEY,
21955
+ args.toml.rerank?.api_key,
21956
+ args.yaml.rerank?.apiKey
21957
+ );
21958
+ const sameEndpoint = provider2 === "http" && sameTrustedEndpoint(baseUrl, args.memoryLlmBaseUrl);
21959
+ const inheritedApiKey = sameEndpoint ? args.memoryLlmApiKey : void 0;
21960
+ const apiKey = first(dedicatedApiKey, inheritedApiKey);
21961
+ const canSendRequest = provider2 === "http" && Boolean(baseUrl) && (Boolean(apiKey) || !args.memoryLlmApiKey || sameEndpoint);
21962
+ return { provider: provider2, model, baseUrl, apiKey, canSendRequest };
21963
+ }
21964
+ function sameTrustedEndpoint(left, right) {
21965
+ const a4 = normalizeTrustedEndpoint(left);
21966
+ const b4 = normalizeTrustedEndpoint(right);
21967
+ return Boolean(a4 && b4 && a4 === b4);
21968
+ }
21969
+ function normalizeTrustedEndpoint(value) {
21970
+ const trimmed = value?.trim();
21971
+ if (!trimmed) return void 0;
21972
+ try {
21973
+ const url2 = new URL(trimmed);
21974
+ const protocol = url2.protocol.toLowerCase();
21975
+ const hostname2 = url2.hostname.toLowerCase();
21976
+ const port = url2.port;
21977
+ let pathname = url2.pathname.replace(/\/+$/, "");
21978
+ if (pathname.toLowerCase().endsWith("/rerank")) {
21979
+ pathname = pathname.slice(0, -"/rerank".length).replace(/\/+$/, "");
21980
+ }
21981
+ return `${protocol}//${hostname2}${port ? `:${port}` : ""}${pathname}`;
21982
+ } catch {
21983
+ const fallback = trimmed.replace(/\/+$/, "").replace(/\/rerank$/i, "").replace(/\/+$/, "");
21984
+ return fallback.toLowerCase() || void 0;
21985
+ }
21986
+ }
21803
21987
  var init_resolved_config = __esm({
21804
21988
  "src/config/resolved-config.ts"() {
21805
21989
  "use strict";
@@ -24035,9 +24219,27 @@ __export(provider_exports2, {
24035
24219
  getLLMConfig: () => getLLMConfig,
24036
24220
  initLLM: () => initLLM,
24037
24221
  isLLMEnabled: () => isLLMEnabled,
24222
+ normalizeOpenAICompatibleBaseUrl: () => normalizeOpenAICompatibleBaseUrl,
24038
24223
  parseLLMTimeoutMs: () => parseLLMTimeoutMs,
24039
24224
  setLLMConfig: () => setLLMConfig
24040
24225
  });
24226
+ function normalizeOpenAICompatibleBaseUrl(baseUrl) {
24227
+ const base = baseUrl.trim().replace(/\/+$/, "");
24228
+ if (!base) return base;
24229
+ let pathname = "";
24230
+ try {
24231
+ pathname = new URL(base).pathname.replace(/\/+$/, "");
24232
+ } catch {
24233
+ pathname = base.split(/[?#]/, 1)[0].replace(/\/+$/, "");
24234
+ }
24235
+ const lastSegment = pathname.split("/").filter(Boolean).at(-1);
24236
+ if (lastSegment && OPENAI_API_VERSION_SEGMENT.test(lastSegment)) {
24237
+ return base;
24238
+ }
24239
+ const queryOrHash = base.match(/[?#].*$/)?.[0] ?? "";
24240
+ const pathWithoutQuery = base.slice(0, base.length - queryOrHash.length).replace(/\/+$/, "");
24241
+ return `${pathWithoutQuery}/v1${queryOrHash}`;
24242
+ }
24041
24243
  function parseLLMTimeoutMs(raw) {
24042
24244
  if (raw === void 0 || raw.trim() === "") return LLM_TIMEOUT_DEFAULT_MS;
24043
24245
  const parsed = Number(raw);
@@ -24141,8 +24343,7 @@ async function callLLM(systemPrompt, userMessage, signal) {
24141
24343
  }
24142
24344
  async function callOpenAICompatible(systemPrompt, userMessage, signal) {
24143
24345
  const config2 = currentConfig;
24144
- let base = config2.baseUrl.replace(/\/+$/, "");
24145
- if (!base.endsWith("/v1")) base += "/v1";
24346
+ const base = normalizeOpenAICompatibleBaseUrl(config2.baseUrl);
24146
24347
  const url2 = `${base}/chat/completions`;
24147
24348
  const fetchSignal = signal ? AbortSignal.any([signal, AbortSignal.timeout(LLM_CALL_TIMEOUT_MS)]) : AbortSignal.timeout(LLM_CALL_TIMEOUT_MS);
24148
24349
  const response = await fetch(url2, {
@@ -24221,8 +24422,7 @@ async function callLLMWithTools(messages, tools, signal) {
24221
24422
  }
24222
24423
  async function callOpenAIWithTools(messages, tools, signal) {
24223
24424
  const config2 = currentConfig;
24224
- let base = config2.baseUrl.replace(/\/+$/, "");
24225
- if (!base.endsWith("/v1")) base += "/v1";
24425
+ const base = normalizeOpenAICompatibleBaseUrl(config2.baseUrl);
24226
24426
  const url2 = `${base}/chat/completions`;
24227
24427
  const openaiMessages = messages.map((msg) => {
24228
24428
  if (msg.role === "tool") {
@@ -24372,8 +24572,7 @@ async function callAnthropicWithTools(messages, tools, signal) {
24372
24572
  }
24373
24573
  async function* callOpenAIWithToolsStream(messages, tools) {
24374
24574
  const config2 = currentConfig;
24375
- let base = config2.baseUrl.replace(/\/+$/, "");
24376
- if (!base.endsWith("/v1")) base += "/v1";
24575
+ const base = normalizeOpenAICompatibleBaseUrl(config2.baseUrl);
24377
24576
  const url2 = `${base}/chat/completions`;
24378
24577
  const openaiMessages = messages.map((msg) => {
24379
24578
  if (msg.role === "tool") {
@@ -24621,7 +24820,7 @@ async function* callAnthropicWithToolsStream(messages, tools) {
24621
24820
  }
24622
24821
  };
24623
24822
  }
24624
- var LLM_TIMEOUT_DEFAULT_MS, LLM_TIMEOUT_MIN_MS, LLM_TIMEOUT_MAX_MS, MAX_LLM_RESPONSE_BYTES, LLM_CALL_TIMEOUT_MS, PROVIDER_DEFAULTS, currentConfig;
24823
+ var LLM_TIMEOUT_DEFAULT_MS, LLM_TIMEOUT_MIN_MS, LLM_TIMEOUT_MAX_MS, MAX_LLM_RESPONSE_BYTES, OPENAI_API_VERSION_SEGMENT, LLM_CALL_TIMEOUT_MS, PROVIDER_DEFAULTS, currentConfig;
24625
24824
  var init_provider2 = __esm({
24626
24825
  "src/llm/provider.ts"() {
24627
24826
  "use strict";
@@ -24631,6 +24830,7 @@ var init_provider2 = __esm({
24631
24830
  LLM_TIMEOUT_MIN_MS = 1e3;
24632
24831
  LLM_TIMEOUT_MAX_MS = 3e5;
24633
24832
  MAX_LLM_RESPONSE_BYTES = 2 * 1024 * 1024;
24833
+ OPENAI_API_VERSION_SEGMENT = /^v\d+(?:[a-z][a-z0-9-]*)?$/i;
24634
24834
  LLM_CALL_TIMEOUT_MS = parseLLMTimeoutMs(process.env.MEMORIX_LLM_TIMEOUT_MS);
24635
24835
  PROVIDER_DEFAULTS = {
24636
24836
  openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
@@ -24690,6 +24890,48 @@ Rules:
24690
24890
  }
24691
24891
  });
24692
24892
 
24893
+ // src/rerank/policy.ts
24894
+ function parseRerankTimeoutMs(raw) {
24895
+ const value = raw?.trim();
24896
+ if (!value) return DEFAULT_RERANK_TIMEOUT_MS;
24897
+ const parsed = Number(value);
24898
+ if (!Number.isInteger(parsed) || Number.isNaN(parsed)) {
24899
+ console.warn(
24900
+ `[memorix] MEMORIX_RERANK_TIMEOUT_MS="${raw}" is invalid (must be a positive integer between ${RERANK_TIMEOUT_MIN_MS}-${RERANK_TIMEOUT_MAX_MS}ms). Using default ${DEFAULT_RERANK_TIMEOUT_MS}ms.`
24901
+ );
24902
+ return DEFAULT_RERANK_TIMEOUT_MS;
24903
+ }
24904
+ if (parsed < RERANK_TIMEOUT_MIN_MS) return RERANK_TIMEOUT_MIN_MS;
24905
+ if (parsed > RERANK_TIMEOUT_MAX_MS) return RERANK_TIMEOUT_MAX_MS;
24906
+ return parsed;
24907
+ }
24908
+ function resolveRerankTimeoutMs(explicitTimeoutMs) {
24909
+ if (typeof explicitTimeoutMs === "number" && Number.isFinite(explicitTimeoutMs) && explicitTimeoutMs > 0) {
24910
+ return explicitTimeoutMs;
24911
+ }
24912
+ return parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
24913
+ }
24914
+ function shouldAttemptHttpRerank(input) {
24915
+ return input.quality === "thorough" && input.hasQuery && input.candidateCount >= MIN_RERANK_CANDIDATES;
24916
+ }
24917
+ function shouldAttemptLlmRerank(input) {
24918
+ if (input.httpRerankConfigured) return false;
24919
+ if (input.quality !== "thorough" || input.tier !== "heavy" || !input.hasQuery) return false;
24920
+ if (input.candidateCount < MIN_RERANK_CANDIDATES) return false;
24921
+ return input.topScore > 0 && input.secondScore / input.topScore > 0.7;
24922
+ }
24923
+ var DEFAULT_RERANK_TIMEOUT_MS, RERANK_TIMEOUT_MIN_MS, RERANK_TIMEOUT_MAX_MS, MIN_RERANK_CANDIDATES;
24924
+ var init_policy = __esm({
24925
+ "src/rerank/policy.ts"() {
24926
+ "use strict";
24927
+ init_esm_shims();
24928
+ DEFAULT_RERANK_TIMEOUT_MS = 3e4;
24929
+ RERANK_TIMEOUT_MIN_MS = 1e3;
24930
+ RERANK_TIMEOUT_MAX_MS = 3e5;
24931
+ MIN_RERANK_CANDIDATES = 3;
24932
+ }
24933
+ });
24934
+
24693
24935
  // src/timeout.ts
24694
24936
  function withTimeout(promise2, ms2, label) {
24695
24937
  return new Promise((resolve6, reject) => {
@@ -24975,6 +25217,148 @@ var init_aliases = __esm({
24975
25217
  }
24976
25218
  });
24977
25219
 
25220
+ // src/rerank/http-provider.ts
25221
+ function buildRerankUrl(baseUrl) {
25222
+ const trimmed = baseUrl.trim().replace(/\/+$/, "");
25223
+ if (trimmed.endsWith("/rerank")) return trimmed;
25224
+ return `${trimmed}/rerank`;
25225
+ }
25226
+ function parseRerankResponse(payload) {
25227
+ const rows = extractRows(payload);
25228
+ if (rows.length === 0) {
25229
+ throw new Error("Neural rerank response contained no ranked results");
25230
+ }
25231
+ return rows.sort((a4, b4) => b4.score - a4.score);
25232
+ }
25233
+ function extractRows(payload) {
25234
+ if (Array.isArray(payload)) {
25235
+ return payload.map(rowFromUnknown).filter((row) => row !== null);
25236
+ }
25237
+ if (payload && typeof payload === "object" && "results" in payload) {
25238
+ const results = payload.results;
25239
+ if (Array.isArray(results)) {
25240
+ return results.map(rowFromUnknown).filter((row) => row !== null);
25241
+ }
25242
+ }
25243
+ throw new Error("Neural rerank response was not Cohere or TEI shaped");
25244
+ }
25245
+ function rowFromUnknown(value) {
25246
+ if (!value || typeof value !== "object") return null;
25247
+ const row = value;
25248
+ if (typeof row.index !== "number" || !Number.isFinite(row.index)) return null;
25249
+ const score = typeof row.relevance_score === "number" ? row.relevance_score : typeof row.score === "number" ? row.score : null;
25250
+ if (score === null || !Number.isFinite(score)) return null;
25251
+ return { index: row.index, score };
25252
+ }
25253
+ function buildRerankBody(query, documents, model) {
25254
+ const body = {
25255
+ query,
25256
+ documents,
25257
+ top_n: documents.length,
25258
+ return_documents: false
25259
+ };
25260
+ const trimmed = model?.trim();
25261
+ if (trimmed) body.model = trimmed;
25262
+ return body;
25263
+ }
25264
+ async function rerankViaHttp(query, documents, config2) {
25265
+ if (documents.length === 0) return [];
25266
+ const url2 = buildRerankUrl(config2.baseUrl);
25267
+ const headers = {
25268
+ "content-type": "application/json"
25269
+ };
25270
+ if (config2.apiKey) {
25271
+ headers.authorization = `Bearer ${config2.apiKey}`;
25272
+ }
25273
+ const controller = new AbortController();
25274
+ const timeoutMs = resolveRerankTimeoutMs(config2.timeoutMs);
25275
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
25276
+ try {
25277
+ const fetchImpl = config2.fetchImpl ?? fetch;
25278
+ const response = await fetchImpl(url2, {
25279
+ method: "POST",
25280
+ headers,
25281
+ signal: controller.signal,
25282
+ body: JSON.stringify(buildRerankBody(query, documents, config2.model))
25283
+ });
25284
+ if (!response.ok) {
25285
+ throw new Error(`Neural rerank HTTP ${response.status} from ${url2}`);
25286
+ }
25287
+ return parseRerankResponse(await response.json());
25288
+ } finally {
25289
+ clearTimeout(timer);
25290
+ }
25291
+ }
25292
+ var init_http_provider = __esm({
25293
+ "src/rerank/http-provider.ts"() {
25294
+ "use strict";
25295
+ init_esm_shims();
25296
+ init_policy();
25297
+ }
25298
+ });
25299
+
25300
+ // src/rerank/index.ts
25301
+ var rerank_exports = {};
25302
+ __export(rerank_exports, {
25303
+ DEFAULT_RERANK_TIMEOUT_MS: () => DEFAULT_RERANK_TIMEOUT_MS,
25304
+ MIN_RERANK_CANDIDATES: () => MIN_RERANK_CANDIDATES,
25305
+ isNeuralRerankEnabled: () => isNeuralRerankEnabled,
25306
+ neuralRerankCandidates: () => neuralRerankCandidates,
25307
+ parseRerankTimeoutMs: () => parseRerankTimeoutMs,
25308
+ resolveRerankTimeoutMs: () => resolveRerankTimeoutMs,
25309
+ shouldAttemptHttpRerank: () => shouldAttemptHttpRerank,
25310
+ shouldAttemptLlmRerank: () => shouldAttemptLlmRerank
25311
+ });
25312
+ function isNeuralRerankEnabled(options = {}) {
25313
+ const lane = getResolvedRerankLane(options);
25314
+ return lane.provider === "http" && Boolean(lane.baseUrl);
25315
+ }
25316
+ async function neuralRerankCandidates(query, candidates, options = {}) {
25317
+ if (!isNeuralRerankEnabled(options) || candidates.length === 0) return null;
25318
+ const lane = getResolvedRerankLane(options);
25319
+ if (!lane.canSendRequest || !lane.baseUrl) return null;
25320
+ const toRerank = candidates.slice(0, MAX_RERANK);
25321
+ const rest = candidates.slice(MAX_RERANK);
25322
+ const documents = toRerank.map((candidate2) => candidateText(candidate2));
25323
+ const ranked = await rerankViaHttp(query, documents, {
25324
+ provider: "http",
25325
+ model: lane.model,
25326
+ baseUrl: lane.baseUrl,
25327
+ apiKey: lane.apiKey,
25328
+ timeoutMs: resolveRerankTimeoutMs()
25329
+ });
25330
+ const seen = /* @__PURE__ */ new Set();
25331
+ const reranked = [];
25332
+ for (const row of ranked) {
25333
+ const candidate2 = toRerank[row.index];
25334
+ if (!candidate2 || seen.has(row.index)) continue;
25335
+ reranked.push(candidate2);
25336
+ seen.add(row.index);
25337
+ }
25338
+ for (const [index, candidate2] of toRerank.entries()) {
25339
+ if (!seen.has(index)) reranked.push(candidate2);
25340
+ }
25341
+ reranked.push(...rest);
25342
+ return reranked;
25343
+ }
25344
+ function candidateText(candidate2) {
25345
+ const narrative = candidate2.narrative?.trim();
25346
+ if (narrative) return `${candidate2.title} \u2014 ${narrative.slice(0, 200)}`;
25347
+ return candidate2.title;
25348
+ }
25349
+ var MAX_RERANK;
25350
+ var init_rerank = __esm({
25351
+ "src/rerank/index.ts"() {
25352
+ "use strict";
25353
+ init_esm_shims();
25354
+ init_resolved_config();
25355
+ init_http_provider();
25356
+ init_policy();
25357
+ init_policy();
25358
+ MAX_RERANK = 10;
25359
+ }
25360
+ });
25361
+
24978
25362
  // src/llm/quality.ts
24979
25363
  var quality_exports = {};
24980
25364
  __export(quality_exports, {
@@ -25014,9 +25398,9 @@ async function rerankResults(query, candidates) {
25014
25398
  if (!isLLMEnabled() || candidates.length <= 2) {
25015
25399
  return { reranked: candidates, usedLLM: false };
25016
25400
  }
25017
- const MAX_RERANK = 10;
25018
- const toRerank = candidates.slice(0, MAX_RERANK);
25019
- const rest = candidates.slice(MAX_RERANK);
25401
+ const MAX_RERANK2 = 10;
25402
+ const toRerank = candidates.slice(0, MAX_RERANK2);
25403
+ const rest = candidates.slice(MAX_RERANK2);
25020
25404
  try {
25021
25405
  const candidateList = toRerank.map(
25022
25406
  (c6) => `[ID: ${c6.id}] (${c6.type}) ${c6.title}${c6.narrative ? ` \u2014 ${c6.narrative.substring(0, 100)}` : ""}`
@@ -26334,6 +26718,234 @@ var init_lifecycle = __esm({
26334
26718
  }
26335
26719
  });
26336
26720
 
26721
+ // src/store/evidence-store.ts
26722
+ var evidence_store_exports = {};
26723
+ __export(evidence_store_exports, {
26724
+ EvidenceCardStore: () => EvidenceCardStore,
26725
+ buildEvidenceCardId: () => buildEvidenceCardId
26726
+ });
26727
+ import { createHash as createHash3, randomUUID as randomUUID3 } from "crypto";
26728
+ function safeJson(value, fallback) {
26729
+ if (typeof value !== "string" || value.length === 0) return fallback;
26730
+ try {
26731
+ return JSON.parse(value);
26732
+ } catch {
26733
+ return fallback;
26734
+ }
26735
+ }
26736
+ function rowToCard(row) {
26737
+ return {
26738
+ id: String(row.id),
26739
+ projectId: String(row.project_id),
26740
+ candidateKind: row.candidate_kind,
26741
+ candidateId: String(row.candidate_id),
26742
+ title: String(row.title),
26743
+ summary: String(row.summary ?? ""),
26744
+ ...row.entity_name ? { entityName: String(row.entity_name) } : {},
26745
+ sourceKind: String(row.source_kind),
26746
+ sourceRef: String(row.source_ref),
26747
+ ...row.locator ? { locator: String(row.locator) } : {},
26748
+ ...row.captured_hash ? { capturedHash: String(row.captured_hash) } : {},
26749
+ ...row.session_id ? { sessionId: String(row.session_id) } : {},
26750
+ status: String(row.status ?? "active"),
26751
+ verification: row.verification,
26752
+ freshness: row.freshness,
26753
+ ...row.stale_reason ? { staleReason: String(row.stale_reason) } : {},
26754
+ files: safeJson(row.files_json, []),
26755
+ relatedEntities: safeJson(row.related_entities_json, []),
26756
+ createdAt: String(row.created_at),
26757
+ updatedAt: String(row.updated_at)
26758
+ };
26759
+ }
26760
+ function observationHash(observation) {
26761
+ return createHash3("sha256").update(JSON.stringify({
26762
+ projectId: observation.projectId,
26763
+ id: observation.id,
26764
+ title: observation.title,
26765
+ narrative: observation.narrative,
26766
+ facts: observation.facts,
26767
+ files: observation.filesModified,
26768
+ relatedEntities: observation.relatedEntities
26769
+ })).digest("hex");
26770
+ }
26771
+ function sourceKind(observation) {
26772
+ if (observation.source === "git" || observation.sourceDetail === "git-ingest") return "git";
26773
+ if (observation.sourceDetail === "hook") return "hook";
26774
+ return observation.source ?? "agent";
26775
+ }
26776
+ function buildEvidenceCardId(projectId, candidateKind, candidateId) {
26777
+ return `evidence:${candidateKind}:${projectId}:${candidateId}`;
26778
+ }
26779
+ var EvidenceCardStore;
26780
+ var init_evidence_store = __esm({
26781
+ "src/store/evidence-store.ts"() {
26782
+ "use strict";
26783
+ init_esm_shims();
26784
+ init_sqlite_db();
26785
+ EvidenceCardStore = class {
26786
+ db = null;
26787
+ async init(dataDir) {
26788
+ this.db = getDatabase(dataDir);
26789
+ }
26790
+ requireDb() {
26791
+ if (!this.db) throw new Error("EvidenceCardStore is not initialized.");
26792
+ return this.db;
26793
+ }
26794
+ upsertObservation(observation) {
26795
+ const db2 = this.requireDb();
26796
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
26797
+ const card = {
26798
+ id: `evidence:observation:${observation.projectId}:${observation.id}`,
26799
+ projectId: observation.projectId,
26800
+ candidateKind: "observation",
26801
+ candidateId: String(observation.id),
26802
+ title: observation.title,
26803
+ summary: observation.narrative.slice(0, 500),
26804
+ entityName: observation.entityName,
26805
+ sourceKind: sourceKind(observation),
26806
+ sourceRef: `observation:${observation.id}`,
26807
+ locator: observation.filesModified?.join(", ") || `observation:${observation.id}`,
26808
+ capturedHash: observationHash(observation),
26809
+ ...observation.sessionId ? { sessionId: observation.sessionId } : {},
26810
+ status: observation.status ?? "active",
26811
+ verification: observation.status === "resolved" ? "verified" : "unverified",
26812
+ freshness: "current",
26813
+ files: observation.filesModified ?? [],
26814
+ relatedEntities: observation.relatedEntities ?? [],
26815
+ createdAt: observation.createdAt,
26816
+ updatedAt: now3
26817
+ };
26818
+ db2.prepare(`
26819
+ INSERT INTO evidence_cards (
26820
+ id, project_id, candidate_kind, candidate_id, title, summary, entity_name,
26821
+ source_kind, source_ref, locator, captured_hash, session_id, status,
26822
+ verification, freshness, stale_reason, files_json, related_entities_json,
26823
+ created_at, updated_at
26824
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?)
26825
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
26826
+ title = excluded.title,
26827
+ summary = excluded.summary,
26828
+ entity_name = excluded.entity_name,
26829
+ source_kind = excluded.source_kind,
26830
+ source_ref = excluded.source_ref,
26831
+ locator = excluded.locator,
26832
+ captured_hash = excluded.captured_hash,
26833
+ session_id = excluded.session_id,
26834
+ status = excluded.status,
26835
+ freshness = 'current',
26836
+ stale_reason = NULL,
26837
+ files_json = excluded.files_json,
26838
+ related_entities_json = excluded.related_entities_json,
26839
+ updated_at = excluded.updated_at
26840
+ `).run(
26841
+ card.id,
26842
+ card.projectId,
26843
+ card.candidateKind,
26844
+ card.candidateId,
26845
+ card.title,
26846
+ card.summary,
26847
+ card.entityName ?? null,
26848
+ card.sourceKind,
26849
+ card.sourceRef,
26850
+ card.locator ?? null,
26851
+ card.capturedHash ?? null,
26852
+ card.sessionId ?? null,
26853
+ card.status,
26854
+ card.verification,
26855
+ card.freshness,
26856
+ JSON.stringify(card.files),
26857
+ JSON.stringify(card.relatedEntities),
26858
+ card.createdAt,
26859
+ card.updatedAt
26860
+ );
26861
+ return this.get(card.projectId, card.candidateKind, card.candidateId) ?? card;
26862
+ }
26863
+ get(projectId, candidateKind, candidateId) {
26864
+ const row = this.requireDb().prepare(`
26865
+ SELECT * FROM evidence_cards WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
26866
+ `).get(projectId, candidateKind, candidateId);
26867
+ return row ? rowToCard(row) : void 0;
26868
+ }
26869
+ list(projectId, options = {}) {
26870
+ const limit2 = Math.min(100, Math.max(1, Math.floor(options.limit ?? 20)));
26871
+ const clauses = ["project_id = ?"];
26872
+ const args = [projectId];
26873
+ if (options.freshness) {
26874
+ clauses.push("freshness = ?");
26875
+ args.push(options.freshness);
26876
+ }
26877
+ if (options.status) {
26878
+ clauses.push("status = ?");
26879
+ args.push(options.status);
26880
+ }
26881
+ args.push(limit2);
26882
+ const rows = this.requireDb().prepare(`
26883
+ SELECT * FROM evidence_cards WHERE ${clauses.join(" AND ")} ORDER BY updated_at DESC, id DESC LIMIT ?
26884
+ `).all(...args);
26885
+ return rows.map(rowToCard);
26886
+ }
26887
+ syncObservations(observations2) {
26888
+ return observations2.map((observation) => this.upsertObservation(observation));
26889
+ }
26890
+ markStaleForPaths(projectId, paths, reason = "source file changed") {
26891
+ const normalized = new Set(paths.map((value) => value.replace(/\\/g, "/").toLowerCase()));
26892
+ if (normalized.size === 0) return 0;
26893
+ const db2 = this.requireDb();
26894
+ const rows = db2.prepare(`SELECT * FROM evidence_cards WHERE project_id = ? AND freshness != 'stale'`).all(projectId);
26895
+ const update2 = db2.prepare(`
26896
+ UPDATE evidence_cards SET freshness = 'stale', stale_reason = ?, updated_at = ?
26897
+ WHERE id = ?
26898
+ `);
26899
+ let changed = 0;
26900
+ const now3 = (/* @__PURE__ */ new Date()).toISOString();
26901
+ const run = db2.transaction(() => {
26902
+ for (const row of rows) {
26903
+ const files = safeJson(row.files_json, []);
26904
+ if (!files.some((file2) => normalized.has(file2.replace(/\\/g, "/").toLowerCase()))) continue;
26905
+ update2.run(reason, now3, row.id);
26906
+ changed++;
26907
+ }
26908
+ });
26909
+ run();
26910
+ return changed;
26911
+ }
26912
+ setVerification(projectId, cardId, verification) {
26913
+ this.requireDb().prepare(`
26914
+ UPDATE evidence_cards SET verification = ?, updated_at = ? WHERE project_id = ? AND id = ?
26915
+ `).run(verification, (/* @__PURE__ */ new Date()).toISOString(), projectId, cardId);
26916
+ }
26917
+ recordEvent(projectId, cardId, kind, detail) {
26918
+ const event = {
26919
+ id: randomUUID3(),
26920
+ projectId,
26921
+ cardId,
26922
+ kind,
26923
+ ...detail ? { detail: detail.slice(0, 2e3) } : {},
26924
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
26925
+ };
26926
+ this.requireDb().prepare(`
26927
+ INSERT INTO evidence_card_events (id, project_id, card_id, kind, detail, created_at)
26928
+ VALUES (?, ?, ?, ?, ?, ?)
26929
+ `).run(event.id, event.projectId, event.cardId, event.kind, event.detail ?? null, event.createdAt);
26930
+ return event;
26931
+ }
26932
+ listEvents(cardId, limit2 = 50) {
26933
+ const rows = this.requireDb().prepare(`
26934
+ SELECT * FROM evidence_card_events WHERE card_id = ? ORDER BY created_at DESC LIMIT ?
26935
+ `).all(cardId, Math.min(200, Math.max(1, Math.floor(limit2))));
26936
+ return rows.map((row) => ({
26937
+ id: String(row.id),
26938
+ projectId: String(row.project_id),
26939
+ cardId: String(row.card_id),
26940
+ kind: String(row.kind),
26941
+ ...row.detail ? { detail: String(row.detail) } : {},
26942
+ createdAt: String(row.created_at)
26943
+ }));
26944
+ }
26945
+ };
26946
+ }
26947
+ });
26948
+
26337
26949
  // src/runtime/isolated-maintenance.ts
26338
26950
  import { spawn } from "child_process";
26339
26951
  import { existsSync as existsSync10 } from "fs";
@@ -26495,7 +27107,7 @@ var workspace_store_exports = {};
26495
27107
  __export(workspace_store_exports, {
26496
27108
  KnowledgeWorkspaceStore: () => KnowledgeWorkspaceStore
26497
27109
  });
26498
- import { randomUUID as randomUUID3 } from "crypto";
27110
+ import { randomUUID as randomUUID4 } from "crypto";
26499
27111
  function arrayValue(raw) {
26500
27112
  if (typeof raw !== "string" || !raw) return [];
26501
27113
  try {
@@ -26636,7 +27248,7 @@ var init_workspace_store = __esm({
26636
27248
  }
26637
27249
  createProposal(input) {
26638
27250
  const proposal = {
26639
- id: input.id ?? randomUUID3(),
27251
+ id: input.id ?? randomUUID4(),
26640
27252
  ...input,
26641
27253
  status: input.status ?? "pending",
26642
27254
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -26689,13 +27301,13 @@ __export(workspace_exports, {
26689
27301
  loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
26690
27302
  resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
26691
27303
  });
26692
- import { createHash as createHash3 } from "crypto";
27304
+ import { createHash as createHash4 } from "crypto";
26693
27305
  import { execFile } from "child_process";
26694
27306
  import { promises as fs8 } from "fs";
26695
27307
  import path12 from "path";
26696
27308
  import { promisify } from "util";
26697
27309
  function hash(value) {
26698
- return createHash3("sha256").update(value).digest("hex");
27310
+ return createHash4("sha256").update(value).digest("hex");
26699
27311
  }
26700
27312
  function workspaceId(projectId, mode) {
26701
27313
  return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
@@ -27295,7 +27907,7 @@ var media_store_exports = {};
27295
27907
  __export(media_store_exports, {
27296
27908
  MediaStore: () => MediaStore
27297
27909
  });
27298
- import { randomUUID as randomUUID4 } from "crypto";
27910
+ import { randomUUID as randomUUID5 } from "crypto";
27299
27911
  function asOptionalText(value) {
27300
27912
  return typeof value === "string" && value.length > 0 ? value : void 0;
27301
27913
  }
@@ -27465,7 +28077,7 @@ var init_media_store = __esm({
27465
28077
  return { asset: this.getAsset(input.projectId, existing.id), deduplicated: true };
27466
28078
  }
27467
28079
  const asset = {
27468
- id: randomUUID4(),
28080
+ id: randomUUID5(),
27469
28081
  ...input,
27470
28082
  createdAt: now3,
27471
28083
  updatedAt: now3
@@ -27545,7 +28157,7 @@ var init_media_store = __esm({
27545
28157
  `).get(input.assetId, input.observationId ?? null, input.role);
27546
28158
  if (existing) return rowToLink(existing);
27547
28159
  const link = {
27548
- id: randomUUID4(),
28160
+ id: randomUUID5(),
27549
28161
  assetId: input.assetId,
27550
28162
  projectId: input.projectId,
27551
28163
  ...input.observationId !== void 0 ? { observationId: input.observationId } : {},
@@ -27584,7 +28196,7 @@ var init_media_store = __esm({
27584
28196
  return rowToDerivation(this.db.prepare("SELECT * FROM media_derivations WHERE id = ?").get(existing.id));
27585
28197
  }
27586
28198
  const derivation = {
27587
- id: randomUUID4(),
28199
+ id: randomUUID5(),
27588
28200
  ...input,
27589
28201
  content,
27590
28202
  ...error40 ? { error: error40 } : {},
@@ -27671,7 +28283,7 @@ var init_media_store = __esm({
27671
28283
  createJob(input) {
27672
28284
  const now3 = Date.now();
27673
28285
  const job = {
27674
- id: randomUUID4(),
28286
+ id: randomUUID5(),
27675
28287
  projectId: input.projectId,
27676
28288
  kind: input.kind,
27677
28289
  status: "queued",
@@ -27847,14 +28459,14 @@ __export(asset_store_exports, {
27847
28459
  readMediaAsset: () => readMediaAsset,
27848
28460
  removeMediaAsset: () => removeMediaAsset
27849
28461
  });
27850
- import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
28462
+ import { createHash as createHash5, randomUUID as randomUUID6 } from "crypto";
27851
28463
  import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
27852
28464
  import path13 from "path";
27853
28465
  function mediaRoot(dataDir) {
27854
28466
  return path13.resolve(dataDir, "media");
27855
28467
  }
27856
28468
  function projectStorageKey(projectId) {
27857
- return createHash4("sha256").update(projectId).digest("hex").slice(0, 16);
28469
+ return createHash5("sha256").update(projectId).digest("hex").slice(0, 16);
27858
28470
  }
27859
28471
  function validateUnderRoot(root, candidate2) {
27860
28472
  const resolved = path13.resolve(candidate2);
@@ -27914,14 +28526,14 @@ async function readHeader(filePath, bytes = 32) {
27914
28526
  }
27915
28527
  async function sha256File(filePath) {
27916
28528
  const bytes = await readFile3(filePath);
27917
- return createHash4("sha256").update(bytes).digest("hex");
28529
+ return createHash5("sha256").update(bytes).digest("hex");
27918
28530
  }
27919
28531
  function destinationFor(root, projectId, sha2562, extension3) {
27920
28532
  const relative3 = path13.join("assets", projectStorageKey(projectId), sha2562.slice(0, 2), `${sha2562}${extension3}`);
27921
28533
  return { relative: relative3, absolute: validateUnderRoot(root, path13.join(root, relative3)) };
27922
28534
  }
27923
28535
  function deletionStagingPath(root, assetId) {
27924
- return validateUnderRoot(root, path13.join(root, ".trash", `${assetId}.${randomUUID5()}.pending-delete`));
28536
+ return validateUnderRoot(root, path13.join(root, ".trash", `${assetId}.${randomUUID6()}.pending-delete`));
27925
28537
  }
27926
28538
  async function destinationExists(destination) {
27927
28539
  try {
@@ -27935,7 +28547,7 @@ async function destinationExists(destination) {
27935
28547
  async function persistFile(source, destination) {
27936
28548
  await mkdir3(path13.dirname(destination), { recursive: true });
27937
28549
  if (await destinationExists(destination)) return false;
27938
- const temporary = `${destination}.${randomUUID5()}.tmp`;
28550
+ const temporary = `${destination}.${randomUUID6()}.tmp`;
27939
28551
  try {
27940
28552
  await copyFile(source, temporary);
27941
28553
  await rename2(temporary, destination);
@@ -27951,7 +28563,7 @@ async function persistFile(source, destination) {
27951
28563
  async function persistBuffer(bytes, destination) {
27952
28564
  await mkdir3(path13.dirname(destination), { recursive: true });
27953
28565
  if (await destinationExists(destination)) return false;
27954
- const temporary = `${destination}.${randomUUID5()}.tmp`;
28566
+ const temporary = `${destination}.${randomUUID6()}.tmp`;
27955
28567
  try {
27956
28568
  await writeFile3(temporary, bytes, { flag: "wx" });
27957
28569
  await rename2(temporary, destination);
@@ -28020,7 +28632,7 @@ async function importMediaBuffer(input) {
28020
28632
  if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
28021
28633
  if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
28022
28634
  const detected = detectMedia(input.bytes.subarray(0, 32));
28023
- const sha2562 = createHash4("sha256").update(input.bytes).digest("hex");
28635
+ const sha2562 = createHash5("sha256").update(input.bytes).digest("hex");
28024
28636
  return importKnownMedia({
28025
28637
  dataDir: input.dataDir,
28026
28638
  projectId: input.projectId,
@@ -28038,7 +28650,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
28038
28650
  if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
28039
28651
  const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
28040
28652
  if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
28041
- const sha2562 = createHash4("sha256").update(bytes).digest("hex");
28653
+ const sha2562 = createHash5("sha256").update(bytes).digest("hex");
28042
28654
  if (sha2562 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
28043
28655
  return bytes;
28044
28656
  }
@@ -80908,7 +81520,7 @@ var require_gaxios = __commonJS({
80908
81520
  var retry_js_1 = require_retry3();
80909
81521
  var stream_1 = __require("stream");
80910
81522
  var interceptor_js_1 = require_interceptor();
80911
- var randomUUID17 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
81523
+ var randomUUID20 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
80912
81524
  var HTTP_STATUS_NO_CONTENT = 204;
80913
81525
  var Gaxios = class {
80914
81526
  agentCache = /* @__PURE__ */ new Map();
@@ -81181,7 +81793,7 @@ var require_gaxios = __commonJS({
81181
81793
  */
81182
81794
  ["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
81183
81795
  if (opts.multipart?.length) {
81184
- const boundary = await randomUUID17();
81796
+ const boundary = await randomUUID20();
81185
81797
  preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
81186
81798
  opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
81187
81799
  } else if (shouldDirectlyPassData) {
@@ -93408,7 +94020,7 @@ var require_websocket = __commonJS({
93408
94020
  var http3 = __require("http");
93409
94021
  var net = __require("net");
93410
94022
  var tls = __require("tls");
93411
- var { randomBytes: randomBytes3, createHash: createHash20 } = __require("crypto");
94023
+ var { randomBytes: randomBytes3, createHash: createHash22 } = __require("crypto");
93412
94024
  var { Duplex, Readable: Readable3 } = __require("stream");
93413
94025
  var { URL: URL2 } = __require("url");
93414
94026
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -94076,7 +94688,7 @@ var require_websocket = __commonJS({
94076
94688
  abortHandshake(websocket, socket, "Invalid Upgrade header");
94077
94689
  return;
94078
94690
  }
94079
- const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
94691
+ const digest2 = createHash22("sha1").update(key + GUID).digest("base64");
94080
94692
  if (res.headers["sec-websocket-accept"] !== digest2) {
94081
94693
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
94082
94694
  return;
@@ -94448,7 +95060,7 @@ var require_websocket_server = __commonJS({
94448
95060
  var EventEmitter2 = __require("events");
94449
95061
  var http3 = __require("http");
94450
95062
  var { Duplex } = __require("stream");
94451
- var { createHash: createHash20 } = __require("crypto");
95063
+ var { createHash: createHash22 } = __require("crypto");
94452
95064
  var extension3 = require_extension();
94453
95065
  var PerMessageDeflate2 = require_permessage_deflate();
94454
95066
  var subprotocol2 = require_subprotocol();
@@ -94755,7 +95367,7 @@ var require_websocket_server = __commonJS({
94755
95367
  );
94756
95368
  }
94757
95369
  if (this._state > RUNNING) return abortHandshake(socket, 503);
94758
- const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
95370
+ const digest2 = createHash22("sha1").update(key + GUID).digest("base64");
94759
95371
  const headers = [
94760
95372
  "HTTP/1.1 101 Switching Protocols",
94761
95373
  "Upgrade: websocket",
@@ -168158,8 +168770,8 @@ var init_session_resources = __esm({
168158
168770
  });
168159
168771
 
168160
168772
  // packages/ai/dist/utils/abort-signals.js
168161
- function combineAbortSignals(signals) {
168162
- const activeSignals = signals.filter((signal) => signal !== void 0);
168773
+ function combineAbortSignals(signals2) {
168774
+ const activeSignals = signals2.filter((signal) => signal !== void 0);
168163
168775
  if (activeSignals.length === 0) {
168164
168776
  return { cleanup: () => {
168165
168777
  } };
@@ -177872,6 +178484,186 @@ var init_audio_jobs = __esm({
177872
178484
  }
177873
178485
  });
177874
178486
 
178487
+ // src/memory/feedback-model.ts
178488
+ function feedbackWeightMultiplier(weight) {
178489
+ return Math.max(0.35, Math.min(1.4, 0.35 + Math.max(0, Math.min(2, weight)) * 0.525));
178490
+ }
178491
+ var init_feedback_model = __esm({
178492
+ "src/memory/feedback-model.ts"() {
178493
+ "use strict";
178494
+ init_esm_shims();
178495
+ }
178496
+ });
178497
+
178498
+ // src/memory/feedback.ts
178499
+ var feedback_exports = {};
178500
+ __export(feedback_exports, {
178501
+ FEEDBACK_DELTAS: () => FEEDBACK_DELTAS,
178502
+ MemoryFeedbackStore: () => MemoryFeedbackStore,
178503
+ applyMemoryFeedback: () => applyMemoryFeedback,
178504
+ feedbackWeightMultiplier: () => feedbackWeightMultiplier
178505
+ });
178506
+ import { randomUUID as randomUUID7 } from "crypto";
178507
+ function applyMemoryFeedback(current, event) {
178508
+ const delta = event.signal === "revoke" ? 0 : FEEDBACK_DELTAS[event.signal];
178509
+ const weight = Math.max(0, Math.min(2, current.weight + delta));
178510
+ const status = (event.signal === "code-conflict" || event.signal === "user-correction" || event.signal === "weaken") && weight < 0.5 ? "archived" : current.status;
178511
+ return { weight, status, audit: [...current.audit, { ...event }] };
178512
+ }
178513
+ function rowToEvent(row) {
178514
+ return {
178515
+ id: String(row.id),
178516
+ projectId: String(row.project_id),
178517
+ candidateKind: row.candidate_kind,
178518
+ candidateId: String(row.candidate_id),
178519
+ signal: row.signal,
178520
+ ...row.observed_at ? { at: String(row.observed_at) } : { at: (/* @__PURE__ */ new Date()).toISOString() },
178521
+ ...row.actor ? { actor: String(row.actor) } : {},
178522
+ ...row.note ? { note: String(row.note) } : {},
178523
+ ...row.source_ref ? { sourceRef: String(row.source_ref) } : {},
178524
+ ...row.target_event_id ? { targetEventId: String(row.target_event_id) } : {},
178525
+ delta: Number(row.delta ?? 0)
178526
+ };
178527
+ }
178528
+ var FEEDBACK_DELTAS, MemoryFeedbackStore;
178529
+ var init_feedback = __esm({
178530
+ "src/memory/feedback.ts"() {
178531
+ "use strict";
178532
+ init_esm_shims();
178533
+ init_sqlite_db();
178534
+ init_feedback_model();
178535
+ FEEDBACK_DELTAS = {
178536
+ "user-correction": -0.35,
178537
+ "verification-success": 0.25,
178538
+ "verification-failure": -0.25,
178539
+ "code-change": -0.15,
178540
+ used: 0.1,
178541
+ "not-used": -0.05,
178542
+ "code-conflict": -0.4,
178543
+ strengthen: 0.2,
178544
+ weaken: -0.2
178545
+ };
178546
+ MemoryFeedbackStore = class {
178547
+ db = null;
178548
+ async init(dataDir) {
178549
+ this.db = getDatabase(dataDir);
178550
+ }
178551
+ requireDb() {
178552
+ if (!this.db) throw new Error("MemoryFeedbackStore is not initialized.");
178553
+ return this.db;
178554
+ }
178555
+ loadEvents(projectId, candidateKind, candidateId) {
178556
+ return this.requireDb().prepare(`
178557
+ SELECT * FROM memory_feedback_events
178558
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
178559
+ ORDER BY observed_at ASC, rowid ASC
178560
+ `).all(projectId, candidateKind, candidateId).map(rowToEvent);
178561
+ }
178562
+ rebuild(projectId, candidateKind, candidateId) {
178563
+ const events = this.loadEvents(projectId, candidateKind, candidateId);
178564
+ const revoked = new Set(events.filter((event) => event.signal === "revoke" && event.targetEventId).map((event) => event.targetEventId));
178565
+ let state = { weight: 1, status: "active", audit: [] };
178566
+ for (const event of events) {
178567
+ if (event.signal === "revoke" || revoked.has(event.id)) {
178568
+ state.audit.push(event);
178569
+ continue;
178570
+ }
178571
+ state = applyMemoryFeedback(state, event);
178572
+ }
178573
+ this.requireDb().prepare(`
178574
+ INSERT INTO memory_feedback_states (project_id, candidate_kind, candidate_id, weight, lifecycle_status, updated_at)
178575
+ VALUES (?, ?, ?, ?, ?, ?)
178576
+ ON CONFLICT(project_id, candidate_kind, candidate_id) DO UPDATE SET
178577
+ weight = excluded.weight, lifecycle_status = excluded.lifecycle_status, updated_at = excluded.updated_at
178578
+ `).run(projectId, candidateKind, candidateId, state.weight, state.status, (/* @__PURE__ */ new Date()).toISOString());
178579
+ return state;
178580
+ }
178581
+ getState(projectId, candidateKind, candidateId) {
178582
+ const row = this.requireDb().prepare(`
178583
+ SELECT weight, lifecycle_status FROM memory_feedback_states
178584
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
178585
+ `).get(projectId, candidateKind, candidateId);
178586
+ if (!row) return { weight: 1, status: "active", audit: [] };
178587
+ return {
178588
+ weight: Number(row.weight ?? 1),
178589
+ status: row.lifecycle_status ?? "active",
178590
+ audit: this.loadEvents(projectId, candidateKind, candidateId)
178591
+ };
178592
+ }
178593
+ listStates(projectId, candidateKind = "observation") {
178594
+ return this.requireDb().prepare(`
178595
+ SELECT candidate_id, weight, lifecycle_status, updated_at
178596
+ FROM memory_feedback_states WHERE project_id = ? AND candidate_kind = ?
178597
+ ORDER BY updated_at DESC
178598
+ `).all(projectId, candidateKind).map((row) => ({
178599
+ candidateId: String(row.candidate_id),
178600
+ weight: Number(row.weight),
178601
+ status: row.lifecycle_status,
178602
+ updatedAt: String(row.updated_at)
178603
+ }));
178604
+ }
178605
+ weights(projectId, candidateKind = "observation") {
178606
+ const states = /* @__PURE__ */ new Map();
178607
+ for (const row of this.listStates(projectId, candidateKind)) {
178608
+ states.set(row.candidateId, this.getState(projectId, candidateKind, row.candidateId));
178609
+ }
178610
+ return states;
178611
+ }
178612
+ record(input) {
178613
+ if (input.signal === "revoke" && !input.targetEventId) {
178614
+ throw new Error("A revoke feedback event requires targetEventId.");
178615
+ }
178616
+ if (input.signal === "revoke") {
178617
+ const target = this.requireDb().prepare(`
178618
+ SELECT 1 FROM memory_feedback_events
178619
+ WHERE id = ? AND project_id = ? AND candidate_kind = ? AND candidate_id = ?
178620
+ `).get(input.targetEventId, input.projectId, input.candidateKind, input.candidateId);
178621
+ if (!target) throw new Error("The feedback event to revoke was not found in the same candidate scope.");
178622
+ }
178623
+ const event = {
178624
+ id: randomUUID7(),
178625
+ projectId: input.projectId,
178626
+ candidateKind: input.candidateKind,
178627
+ candidateId: input.candidateId,
178628
+ signal: input.signal,
178629
+ sourceRef: input.sourceRef,
178630
+ ...input.actor ? { actor: input.actor } : {},
178631
+ ...input.note ? { note: input.note.slice(0, 2e3) } : {},
178632
+ ...input.targetEventId ? { targetEventId: input.targetEventId } : {},
178633
+ at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
178634
+ delta: input.signal === "revoke" ? 0 : FEEDBACK_DELTAS[input.signal]
178635
+ };
178636
+ this.requireDb().prepare(`
178637
+ INSERT INTO memory_feedback_events (
178638
+ id, project_id, candidate_kind, candidate_id, signal, actor, note,
178639
+ source_ref, target_event_id, delta, observed_at
178640
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
178641
+ `).run(
178642
+ event.id,
178643
+ event.projectId,
178644
+ event.candidateKind,
178645
+ event.candidateId,
178646
+ event.signal,
178647
+ event.actor ?? null,
178648
+ event.note ?? null,
178649
+ event.sourceRef,
178650
+ event.targetEventId ?? null,
178651
+ event.delta,
178652
+ event.at
178653
+ );
178654
+ return { event, state: this.rebuild(input.projectId, input.candidateKind, input.candidateId) };
178655
+ }
178656
+ audit(projectId, candidateKind, candidateId, limit2 = 100) {
178657
+ return this.requireDb().prepare(`
178658
+ SELECT * FROM memory_feedback_events
178659
+ WHERE project_id = ? AND candidate_kind = ? AND candidate_id = ?
178660
+ ORDER BY observed_at DESC, rowid DESC LIMIT ?
178661
+ `).all(projectId, candidateKind, candidateId, Math.min(200, Math.max(1, Math.floor(limit2)))).map(rowToEvent);
178662
+ }
178663
+ };
178664
+ }
178665
+ });
178666
+
177875
178667
  // src/memory/retention.ts
177876
178668
  var retention_exports = {};
177877
178669
  __export(retention_exports, {
@@ -177913,6 +178705,7 @@ function getEffectiveRetentionDays(doc) {
177913
178705
  }
177914
178706
  function isImmune(doc) {
177915
178707
  if (doc.type === "probe") return false;
178708
+ if (doc.feedbackStatus === "archived") return false;
177916
178709
  if (doc.admissionState === "candidate" || doc.admissionState === "ephemeral") return false;
177917
178710
  if (doc.valueCategory === "core") return true;
177918
178711
  const importance = getImportanceLevel(doc);
@@ -177945,7 +178738,8 @@ function calculateRelevance(doc, referenceTime) {
177945
178738
  const decayFactor = Math.exp(-ageDays / retention);
177946
178739
  const accessCount = doc.accessCount ?? 0;
177947
178740
  const accessBoost = Math.min(2, 1 + 0.1 * accessCount);
177948
- let totalScore = base * decayFactor * accessBoost;
178741
+ const feedbackMultiplier = doc.feedbackWeight === void 0 ? 1 : feedbackWeightMultiplier(doc.feedbackWeight);
178742
+ let totalScore = base * decayFactor * accessBoost * feedbackMultiplier;
177949
178743
  const immune = isImmune(doc);
177950
178744
  if (immune) {
177951
178745
  totalScore = Math.max(totalScore, 0.5);
@@ -177957,7 +178751,8 @@ function calculateRelevance(doc, referenceTime) {
177957
178751
  decayFactor,
177958
178752
  accessBoost,
177959
178753
  ageDays,
177960
- isImmune: immune
178754
+ isImmune: immune,
178755
+ feedbackMultiplier
177961
178756
  };
177962
178757
  }
177963
178758
  function rankByRelevance(docs, referenceTime) {
@@ -177965,6 +178760,7 @@ function rankByRelevance(docs, referenceTime) {
177965
178760
  }
177966
178761
  function getRetentionZone(doc, referenceTime) {
177967
178762
  const now3 = referenceTime ?? /* @__PURE__ */ new Date();
178763
+ if (doc.feedbackStatus === "archived") return "archive-candidate";
177968
178764
  const importance = getImportanceLevel(doc);
177969
178765
  const retention = getEffectiveRetentionDays(doc);
177970
178766
  const createdAt = new Date(doc.createdAt);
@@ -178036,7 +178832,7 @@ function explainRetention(doc, referenceTime) {
178036
178832
  summary: parts.join(" ; ")
178037
178833
  };
178038
178834
  }
178039
- function toRetentionDocument(obs, accessMap) {
178835
+ function toRetentionDocument(obs, accessMap, feedback) {
178040
178836
  const access4 = accessMap?.get(obs.id);
178041
178837
  return {
178042
178838
  id: `obs-${obs.id}`,
@@ -178058,11 +178854,12 @@ function toRetentionDocument(obs, accessMap) {
178058
178854
  sourceDetail: obs.sourceDetail ?? "",
178059
178855
  valueCategory: obs.valueCategory ?? "",
178060
178856
  admissionState: obs.admissionState ?? "",
178061
- admissionReason: obs.admissionReason ?? ""
178857
+ admissionReason: obs.admissionReason ?? "",
178858
+ ...feedback ? { feedbackWeight: feedback.weight, feedbackStatus: feedback.status } : {}
178062
178859
  };
178063
178860
  }
178064
178861
  function projectObservationRetention(observation, options = {}) {
178065
- const document2 = toRetentionDocument(observation, options.accessMap);
178862
+ const document2 = toRetentionDocument(observation, options.accessMap, options.feedback);
178066
178863
  const relevance = calculateRelevance(document2, options.referenceTime);
178067
178864
  const explanation = explainRetention(document2, options.referenceTime);
178068
178865
  return {
@@ -178087,7 +178884,17 @@ function summarizeRetentionProjections(projections) {
178087
178884
  }
178088
178885
  return { active, stale, archiveCandidates, immune };
178089
178886
  }
178090
- async function archiveExpiredBatch(_projectDir, options) {
178887
+ async function loadFeedbackStore(dataDir) {
178888
+ try {
178889
+ const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
178890
+ const store2 = new MemoryFeedbackStore2();
178891
+ await store2.init(dataDir);
178892
+ return store2;
178893
+ } catch {
178894
+ return void 0;
178895
+ }
178896
+ }
178897
+ async function archiveExpiredBatch(projectDir2, options) {
178091
178898
  const store2 = getObservationStore();
178092
178899
  const limit2 = Math.min(500, Math.max(1, Math.floor(options.limit ?? 100)));
178093
178900
  const afterId = Math.max(0, Math.floor(options.afterId ?? 0));
@@ -178096,11 +178903,13 @@ async function archiveExpiredBatch(_projectDir, options) {
178096
178903
  afterId,
178097
178904
  limit: limit2 + 1
178098
178905
  });
178906
+ const feedback = await loadFeedbackStore(projectDir2);
178099
178907
  const hasMore = page.length > limit2;
178100
178908
  const scanned = hasMore ? page.slice(0, limit2) : page;
178101
178909
  const candidateIds = scanned.filter((observation) => !options.reader || canManageObservation(observation, options.reader)).filter((observation) => projectObservationRetention(observation, {
178102
178910
  accessMap: options.accessMap,
178103
- referenceTime: options.referenceTime
178911
+ referenceTime: options.referenceTime,
178912
+ ...feedback ? { feedback: feedback.getState(options.projectId, "observation", String(observation.id)) } : {}
178104
178913
  }).zone === "archive-candidate").map((observation) => observation.id);
178105
178914
  const archived = candidateIds.length === 0 ? 0 : await store2.atomic(async (tx) => {
178106
178915
  const updates = await Promise.all(
@@ -178131,6 +178940,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
178131
178940
  const remaining = reader2 ? remainingObservations.filter((observation) => canManageObservation(observation, reader2)).length : remainingObservations.length;
178132
178941
  return { archived, remaining };
178133
178942
  }
178943
+ const feedback = await loadFeedbackStore(projectDir2);
178134
178944
  return await store2.atomic(async (tx) => {
178135
178945
  const allObs = await tx.loadAll();
178136
178946
  const activeObs = allObs.filter(
@@ -178138,7 +178948,11 @@ async function archiveExpired(projectDir2, referenceTime, accessMap, projectId,
178138
178948
  );
178139
178949
  const archivedIds = [];
178140
178950
  for (const obs of activeObs) {
178141
- const retention = projectObservationRetention(obs, { accessMap, referenceTime });
178951
+ const retention = projectObservationRetention(obs, {
178952
+ accessMap,
178953
+ referenceTime,
178954
+ ...feedback ? { feedback: feedback.getState(obs.projectId, "observation", String(obs.id)) } : {}
178955
+ });
178142
178956
  if (retention.zone === "archive-candidate") {
178143
178957
  archivedIds.push(obs.id);
178144
178958
  }
@@ -178158,6 +178972,7 @@ var init_retention = __esm({
178158
178972
  init_esm_shims();
178159
178973
  init_obs_store();
178160
178974
  init_visibility();
178975
+ init_feedback_model();
178161
178976
  RETENTION_DAYS = {
178162
178977
  critical: 365,
178163
178978
  high: 180,
@@ -178198,7 +179013,7 @@ var owner_exports = {};
178198
179013
  __export(owner_exports, {
178199
179014
  resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
178200
179015
  });
178201
- import { randomUUID as randomUUID6 } from "crypto";
179016
+ import { randomUUID as randomUUID8 } from "crypto";
178202
179017
  import { promises as fs11 } from "fs";
178203
179018
  import path19 from "path";
178204
179019
  function ownerPath(dataDir) {
@@ -178228,7 +179043,7 @@ async function resolveLocalMemoryOwner(dataDir, options = {}) {
178228
179043
  await fs11.mkdir(path19.dirname(filePath), { recursive: true });
178229
179044
  const created = {
178230
179045
  version: 1,
178231
- id: "local-" + randomUUID6(),
179046
+ id: "local-" + randomUUID8(),
178232
179047
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
178233
179048
  };
178234
179049
  try {
@@ -178255,7 +179070,7 @@ var long_term_store_exports = {};
178255
179070
  __export(long_term_store_exports, {
178256
179071
  LongTermMemoryStore: () => LongTermMemoryStore
178257
179072
  });
178258
- import { randomUUID as randomUUID7 } from "crypto";
179073
+ import { randomUUID as randomUUID9 } from "crypto";
178259
179074
  function optionalText2(value) {
178260
179075
  return typeof value === "string" && value.trim() ? value : void 0;
178261
179076
  }
@@ -178306,7 +179121,7 @@ function rowToEvidence(row) {
178306
179121
  createdAt: row.createdAt
178307
179122
  };
178308
179123
  }
178309
- function rowToEvent(row) {
179124
+ function rowToEvent2(row) {
178310
179125
  return {
178311
179126
  id: row.id,
178312
179127
  memoryId: row.memoryId,
@@ -178415,7 +179230,7 @@ var init_long_term_store = __esm({
178415
179230
  }
178416
179231
  insertEvidence(input) {
178417
179232
  const evidence = {
178418
- id: randomUUID7(),
179233
+ id: randomUUID9(),
178419
179234
  ...input,
178420
179235
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
178421
179236
  };
@@ -178442,7 +179257,7 @@ var init_long_term_store = __esm({
178442
179257
  }
178443
179258
  recordEvent(input) {
178444
179259
  const event = {
178445
- id: randomUUID7(),
179260
+ id: randomUUID9(),
178446
179261
  ...input,
178447
179262
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
178448
179263
  };
@@ -178463,7 +179278,7 @@ var init_long_term_store = __esm({
178463
179278
  listEvents(memoryId) {
178464
179279
  return this.db.prepare(`
178465
179280
  SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
178466
- `).all(memoryId).map(rowToEvent);
179281
+ `).all(memoryId).map(rowToEvent2);
178467
179282
  }
178468
179283
  markAccess(ids, at2 = (/* @__PURE__ */ new Date()).toISOString()) {
178469
179284
  const unique3 = [...new Set(ids.filter(Boolean))];
@@ -178532,7 +179347,7 @@ __export(long_term_exports, {
178532
179347
  selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
178533
179348
  supersedeLongTermMemory: () => supersedeLongTermMemory
178534
179349
  });
178535
- import { randomUUID as randomUUID8 } from "crypto";
179350
+ import { randomUUID as randomUUID10 } from "crypto";
178536
179351
  function isOneOf(value, values) {
178537
179352
  return typeof value === "string" && values.includes(value);
178538
179353
  }
@@ -178714,7 +179529,7 @@ async function createManualLongTermMemory(input) {
178714
179529
  const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
178715
179530
  if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
178716
179531
  assertScope(input.scope);
178717
- const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
179532
+ const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
178718
179533
  return createLongTermMemoryWithDataDir({
178719
179534
  dataDir: input.dataDir,
178720
179535
  originProjectId: input.projectId,
@@ -178769,7 +179584,7 @@ async function createLongTermMemoryWithDataDir(input) {
178769
179584
  assertPortableEvidence(input.scope, portability, evidenceInputs);
178770
179585
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
178771
179586
  const memory = {
178772
- id: randomUUID8(),
179587
+ id: randomUUID10(),
178773
179588
  originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
178774
179589
  ownerId: compactText(input.ownerId, "owner id", 160),
178775
179590
  scope: input.scope,
@@ -179087,9 +179902,9 @@ var init_long_term = __esm({
179087
179902
  });
179088
179903
 
179089
179904
  // src/codegraph/ids.ts
179090
- import { createHash as createHash5 } from "crypto";
179905
+ import { createHash as createHash6 } from "crypto";
179091
179906
  function digest(input) {
179092
- return createHash5("sha256").update(input).digest("hex").slice(0, 16);
179907
+ return createHash6("sha256").update(input).digest("hex").slice(0, 16);
179093
179908
  }
179094
179909
  function normalizeCodePath(path48) {
179095
179910
  return path48.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
@@ -179124,7 +179939,7 @@ var store_exports = {};
179124
179939
  __export(store_exports, {
179125
179940
  CodeGraphStore: () => CodeGraphStore
179126
179941
  });
179127
- import { randomUUID as randomUUID9 } from "crypto";
179942
+ import { randomUUID as randomUUID11 } from "crypto";
179128
179943
  import { posix as pathPosix } from "path";
179129
179944
  function rowToFile(row) {
179130
179945
  return {
@@ -179841,7 +180656,7 @@ var init_store = __esm({
179841
180656
  * so an interrupted scan cannot be advertised as complete.
179842
180657
  */
179843
180658
  recordCodeStateSnapshot(input) {
179844
- const id = randomUUID9();
180659
+ const id = randomUUID11();
179845
180660
  let snapshot;
179846
180661
  const tx = this.db.transaction(() => {
179847
180662
  const previous = this.db.prepare(
@@ -179919,7 +180734,7 @@ var init_store = __esm({
179919
180734
 
179920
180735
  // src/codegraph/code-state.ts
179921
180736
  import { execFile as execFile2 } from "child_process";
179922
- import { createHash as createHash6 } from "crypto";
180737
+ import { createHash as createHash7 } from "crypto";
179923
180738
  import { promisify as promisify3 } from "util";
179924
180739
  async function runGit(rootPath, args) {
179925
180740
  try {
@@ -179948,7 +180763,7 @@ function changedPathCount(status) {
179948
180763
  return count3;
179949
180764
  }
179950
180765
  function fingerprint(baseRevision, status) {
179951
- return createHash6("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
180766
+ return createHash7("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
179952
180767
  }
179953
180768
  async function collectCodeStateSnapshot(input) {
179954
180769
  const [revision, status] = await Promise.all([
@@ -180048,11 +180863,11 @@ __export(lite_provider_exports, {
180048
180863
  indexProjectLite: () => indexProjectLite,
180049
180864
  refreshProjectLite: () => refreshProjectLite
180050
180865
  });
180051
- import { createHash as createHash7 } from "crypto";
180866
+ import { createHash as createHash8 } from "crypto";
180052
180867
  import { readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
180053
180868
  import { join as join5, relative } from "path";
180054
180869
  function hashText(text2) {
180055
- return createHash7("sha256").update(text2).digest("hex");
180870
+ return createHash8("sha256").update(text2).digest("hex");
180056
180871
  }
180057
180872
  function extension2(path48) {
180058
180873
  const index = path48.lastIndexOf(".");
@@ -180619,7 +181434,7 @@ var claim_store_exports = {};
180619
181434
  __export(claim_store_exports, {
180620
181435
  ClaimStore: () => ClaimStore
180621
181436
  });
180622
- import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
181437
+ import { createHash as createHash9, randomUUID as randomUUID12 } from "crypto";
180623
181438
  function optionalText3(value) {
180624
181439
  return typeof value === "string" && value ? value : void 0;
180625
181440
  }
@@ -180658,7 +181473,7 @@ function rowToEvidence2(row) {
180658
181473
  createdAt: row.createdAt
180659
181474
  };
180660
181475
  }
180661
- function rowToEvent2(row) {
181476
+ function rowToEvent3(row) {
180662
181477
  return {
180663
181478
  id: row.id,
180664
181479
  projectId: row.projectId,
@@ -180672,7 +181487,7 @@ function rowToEvent2(row) {
180672
181487
  };
180673
181488
  }
180674
181489
  function evidenceKey(input) {
180675
- return createHash8("sha256").update(JSON.stringify([
181490
+ return createHash9("sha256").update(JSON.stringify([
180676
181491
  input.evidenceKind,
180677
181492
  input.evidenceId,
180678
181493
  input.relation,
@@ -180803,7 +181618,7 @@ var init_claim_store = __esm({
180803
181618
  locator, capturedHash, evidenceKey, createdAt
180804
181619
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
180805
181620
  `).run(
180806
- randomUUID10(),
181621
+ randomUUID12(),
180807
181622
  input.claimId,
180808
181623
  input.evidenceKind,
180809
181624
  input.evidenceId,
@@ -180825,7 +181640,7 @@ var init_claim_store = __esm({
180825
181640
  }
180826
181641
  recordEvent(input) {
180827
181642
  const event = {
180828
- id: randomUUID10(),
181643
+ id: randomUUID12(),
180829
181644
  ...input,
180830
181645
  ...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
180831
181646
  createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
@@ -180853,7 +181668,7 @@ var init_claim_store = __esm({
180853
181668
  SELECT * FROM knowledge_claim_events
180854
181669
  WHERE claimId = ?
180855
181670
  ORDER BY createdAt ASC, id
180856
- `).all(claimId).map(rowToEvent2);
181671
+ `).all(claimId).map(rowToEvent3);
180857
181672
  }
180858
181673
  };
180859
181674
  }
@@ -180870,7 +181685,7 @@ __export(claims_exports, {
180870
181685
  supersedeClaim: () => supersedeClaim,
180871
181686
  writeClaim: () => writeClaim
180872
181687
  });
180873
- import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
181688
+ import { createHash as createHash10, randomUUID as randomUUID13 } from "crypto";
180874
181689
  function compactText2(value, field) {
180875
181690
  if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
180876
181691
  const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
@@ -180882,7 +181697,7 @@ function normalizedTerm(value) {
180882
181697
  return compactText2(value, "identity").toLocaleLowerCase("en-US");
180883
181698
  }
180884
181699
  function hashIdentity(parts) {
180885
- return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
181700
+ return createHash10("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
180886
181701
  }
180887
181702
  function buildClaimIdentity(input) {
180888
181703
  const subject = normalizedTerm(input.subject);
@@ -180956,7 +181771,7 @@ function normalizeClaimInput(input) {
180956
181771
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
180957
181772
  return {
180958
181773
  claim: {
180959
- id: randomUUID11(),
181774
+ id: randomUUID13(),
180960
181775
  projectId,
180961
181776
  subject,
180962
181777
  predicate,
@@ -181123,7 +181938,7 @@ function supersedeClaim(store2, input) {
181123
181938
  });
181124
181939
  }
181125
181940
  function hashObservation(observation) {
181126
- return createHash9("sha256").update(JSON.stringify([
181941
+ return createHash10("sha256").update(JSON.stringify([
181127
181942
  observation.title,
181128
181943
  observation.narrative,
181129
181944
  observation.facts,
@@ -181367,11 +182182,11 @@ var init_frontmatter = __esm({
181367
182182
  });
181368
182183
 
181369
182184
  // src/knowledge/markdown.ts
181370
- import { createHash as createHash10 } from "crypto";
182185
+ import { createHash as createHash11 } from "crypto";
181371
182186
  import { promises as fs12 } from "fs";
181372
182187
  import path20 from "path";
181373
182188
  function hashContent(content) {
181374
- return createHash10("sha256").update(content).digest("hex");
182189
+ return createHash11("sha256").update(content).digest("hex");
181375
182190
  }
181376
182191
  function stringField(data, key) {
181377
182192
  const value = data[key];
@@ -181497,11 +182312,11 @@ __export(wiki_exports, {
181497
182312
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
181498
182313
  readKnowledgePage: () => readKnowledgePage
181499
182314
  });
181500
- import { createHash as createHash11 } from "crypto";
182315
+ import { createHash as createHash12 } from "crypto";
181501
182316
  import { promises as fs13 } from "fs";
181502
182317
  import path21 from "path";
181503
182318
  function hash2(value) {
181504
- return createHash11("sha256").update(value).digest("hex");
182319
+ return createHash12("sha256").update(value).digest("hex");
181505
182320
  }
181506
182321
  function now() {
181507
182322
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -182045,7 +182860,7 @@ var workflow_store_exports = {};
182045
182860
  __export(workflow_store_exports, {
182046
182861
  WorkflowStore: () => WorkflowStore
182047
182862
  });
182048
- import { randomUUID as randomUUID12 } from "crypto";
182863
+ import { randomUUID as randomUUID14 } from "crypto";
182049
182864
  function parseJson(value, fallback) {
182050
182865
  if (typeof value !== "string" || !value) return fallback;
182051
182866
  try {
@@ -182145,7 +182960,7 @@ var init_workflow_store = __esm({
182145
182960
  }
182146
182961
  recordRun(input) {
182147
182962
  const run = {
182148
- id: randomUUID12(),
182963
+ id: randomUUID14(),
182149
182964
  workflowId: input.workflowId,
182150
182965
  projectId: input.projectId,
182151
182966
  task: input.task.trim(),
@@ -182223,7 +183038,7 @@ var init_outcome_types = __esm({
182223
183038
  });
182224
183039
 
182225
183040
  // src/knowledge/outcome-store.ts
182226
- import { randomUUID as randomUUID13 } from "crypto";
183041
+ import { randomUUID as randomUUID15 } from "crypto";
182227
183042
  function optionalText5(value) {
182228
183043
  return typeof value === "string" && value.trim() ? value : void 0;
182229
183044
  }
@@ -182267,7 +183082,7 @@ var init_outcome_store = __esm({
182267
183082
  }
182268
183083
  record(input) {
182269
183084
  const signal = {
182270
- id: randomUUID13(),
183085
+ id: randomUUID15(),
182271
183086
  projectId: text(input.projectId, "project id"),
182272
183087
  candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
182273
183088
  candidateId: text(input.candidateId, "candidate id"),
@@ -182694,11 +183509,11 @@ __export(workflows_exports, {
182694
183509
  syncCanonicalWorkflows: () => syncCanonicalWorkflows,
182695
183510
  writeCanonicalWorkflow: () => writeCanonicalWorkflow
182696
183511
  });
182697
- import { createHash as createHash12 } from "crypto";
183512
+ import { createHash as createHash13 } from "crypto";
182698
183513
  import { promises as fs14 } from "fs";
182699
183514
  import path22 from "path";
182700
183515
  function hash3(value) {
182701
- return createHash12("sha256").update(value).digest("hex");
183516
+ return createHash13("sha256").update(value).digest("hex");
182702
183517
  }
182703
183518
  function now2() {
182704
183519
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -184095,6 +184910,16 @@ async function bindObservationCodeRefsBestEffort(observation) {
184095
184910
  } catch {
184096
184911
  }
184097
184912
  }
184913
+ async function persistEvidenceCard(observation) {
184914
+ if (!projectDir) return;
184915
+ try {
184916
+ const evidence = new EvidenceCardStore();
184917
+ await evidence.init(projectDir);
184918
+ evidence.upsertObservation(observation);
184919
+ } catch (error40) {
184920
+ console.error(`[memorix] Evidence card projection failed for obs-${observation.id}: ${error40 instanceof Error ? error40.message : String(error40)}`);
184921
+ }
184922
+ }
184098
184923
  function queueClaimDerivation(observation) {
184099
184924
  const dataDir = projectDir;
184100
184925
  const isExplicit = observation.sourceDetail === "explicit";
@@ -184441,6 +185266,7 @@ async function storeObservation(input) {
184441
185266
  }
184442
185267
  if (!persistedCallbackInvoked) await input.onPersisted?.(observation);
184443
185268
  await bindObservationCodeRefsBestEffort(observation);
185269
+ await persistEvidenceCard(observation);
184444
185270
  queueObservationQualification(observation);
184445
185271
  if (resolveObservationVisibility(observation) === "project") {
184446
185272
  queueClaimDerivation(observation);
@@ -184533,6 +185359,7 @@ async function upsertObservation(existing, input, now3) {
184533
185359
  await store2.update(existing);
184534
185360
  }
184535
185361
  await bindObservationCodeRefsBestEffort(existing);
185362
+ await persistEvidenceCard(existing);
184536
185363
  queueObservationQualification(existing);
184537
185364
  if (resolveObservationVisibility(existing) === "project") {
184538
185365
  queueClaimDerivation(existing);
@@ -184666,6 +185493,7 @@ async function resolveObservations(ids, status = "resolved") {
184666
185493
  await Promise.all(changed.map((observation) => tx.update(observation)));
184667
185494
  });
184668
185495
  }
185496
+ for (const observation of changed) await persistEvidenceCard(observation);
184669
185497
  return { resolved, notFound };
184670
185498
  }
184671
185499
  function getProjectObservations(projectId) {
@@ -185003,6 +185831,7 @@ var init_observations = __esm({
185003
185831
  init_secret_filter();
185004
185832
  init_lifecycle();
185005
185833
  init_visibility();
185834
+ init_evidence_store();
185006
185835
  observations = [];
185007
185836
  nextId = 1;
185008
185837
  projectDir = null;
@@ -185792,46 +186621,78 @@ async function searchObservations(options) {
185792
186621
  }
185793
186622
  }
185794
186623
  }
185795
- const shouldRerank = quality === "thorough" && tier === "heavy" && hasQuery && intermediate.length > 2 && (() => {
185796
- const top = intermediate[0]?.score ?? 0;
185797
- const second2 = intermediate[1]?.score ?? 0;
185798
- return top > 0 && second2 / top > 0.7;
185799
- })();
185800
- if (shouldRerank) {
185801
- try {
185802
- const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
185803
- const narrativeMap = /* @__PURE__ */ new Map();
185804
- for (const hit of results.hits) {
185805
- const doc = hit.document;
185806
- narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
185807
- }
185808
- const RERANK_TOP_K = 5;
185809
- const toRerank = intermediate.slice(0, RERANK_TOP_K);
185810
- const candidates = toRerank.map((e5, index) => ({
185811
- id: `r${index + 1}`,
185812
- title: e5.title,
185813
- type: e5.type,
185814
- score: e5.score,
185815
- narrative: narrativeMap.get(makeEntryKey(e5.projectId, e5.id))
185816
- }));
185817
- const _parsedRerank = parseInt(process.env.MEMORIX_RERANK_TIMEOUT_MS || "", 10);
185818
- const RERANK_TIMEOUT_MS = Number.isFinite(_parsedRerank) && _parsedRerank > 0 ? _parsedRerank : 5e3;
185819
- const { reranked, usedLLM } = await withTimeout(
185820
- rerankResults2(originalQuery, candidates),
185821
- RERANK_TIMEOUT_MS,
185822
- "LLM rerank"
185823
- );
185824
- mark(`rerank(usedLLM=${usedLLM})`);
185825
- if (usedLLM) {
185826
- lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
185827
- const candidateMap = new Map(candidates.map((candidate2, index) => [candidate2.id, toRerank[index]]));
185828
- const rerankedTop = reranked.map((r6) => candidateMap.get(r6.id)).filter((e5) => e5 != null);
185829
- if (rerankedTop.length > 0) {
185830
- intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
186624
+ const httpGate = shouldAttemptHttpRerank({
186625
+ quality,
186626
+ hasQuery: Boolean(hasQuery),
186627
+ candidateCount: intermediate.length
186628
+ });
186629
+ const llmGateWithoutHttp = shouldAttemptLlmRerank({
186630
+ quality,
186631
+ tier,
186632
+ hasQuery: Boolean(hasQuery),
186633
+ candidateCount: intermediate.length,
186634
+ topScore: intermediate[0]?.score ?? 0,
186635
+ secondScore: intermediate[1]?.score ?? 0,
186636
+ httpRerankConfigured: false
186637
+ });
186638
+ if (httpGate || llmGateWithoutHttp) {
186639
+ const narrativeMap = /* @__PURE__ */ new Map();
186640
+ for (const hit of results.hits) {
186641
+ const doc = hit.document;
186642
+ narrativeMap.set(makeEntryKey(doc.projectId, doc.observationId), doc.narrative);
186643
+ }
186644
+ const RERANK_TOP_K = 5;
186645
+ const toRerank = intermediate.slice(0, RERANK_TOP_K);
186646
+ const candidates = toRerank.map((e5, index) => ({
186647
+ id: `r${index + 1}`,
186648
+ title: e5.title,
186649
+ type: e5.type,
186650
+ score: e5.score,
186651
+ narrative: narrativeMap.get(makeEntryKey(e5.projectId, e5.id))
186652
+ }));
186653
+ const RERANK_TIMEOUT_MS = parseRerankTimeoutMs(process.env.MEMORIX_RERANK_TIMEOUT_MS);
186654
+ const candidateMap = new Map(candidates.map((candidate2, index) => [candidate2.id, toRerank[index]]));
186655
+ const { isNeuralRerankEnabled: isNeuralRerankEnabled2, neuralRerankCandidates: neuralRerankCandidates2 } = await Promise.resolve().then(() => (init_rerank(), rerank_exports));
186656
+ const httpConfigured = isNeuralRerankEnabled2();
186657
+ if (httpConfigured && httpGate) {
186658
+ try {
186659
+ const neural = await withTimeout(
186660
+ neuralRerankCandidates2(originalQuery, candidates),
186661
+ RERANK_TIMEOUT_MS,
186662
+ "neural rerank"
186663
+ );
186664
+ if (neural && neural.length > 0) {
186665
+ const rerankedTop = neural.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
186666
+ if (rerankedTop.length > 0) {
186667
+ intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
186668
+ lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + neural rerank");
186669
+ mark("rerank(usedNeural=true)");
186670
+ }
185831
186671
  }
186672
+ } catch {
186673
+ console.error("[memorix] neural rerank failed or timed out; keeping original order");
185832
186674
  }
185833
- } catch (error40) {
185834
- console.error("[memorix] LLM rerank failed or timed out, using original order");
186675
+ } else if (!httpConfigured && llmGateWithoutHttp) {
186676
+ try {
186677
+ const { rerankResults: rerankResults2 } = await Promise.resolve().then(() => (init_quality(), quality_exports));
186678
+ const { reranked, usedLLM } = await withTimeout(
186679
+ rerankResults2(originalQuery, candidates),
186680
+ RERANK_TIMEOUT_MS,
186681
+ "LLM rerank"
186682
+ );
186683
+ mark(`rerank(usedLLM=${usedLLM})`);
186684
+ if (usedLLM) {
186685
+ lastSearchModeByProject.set(modeKey, (lastSearchModeByProject.get(modeKey) ?? "fulltext") + " + LLM rerank");
186686
+ const rerankedTop = reranked.map((row) => candidateMap.get(row.id)).filter((entry) => entry != null);
186687
+ if (rerankedTop.length > 0) {
186688
+ intermediate = [...rerankedTop, ...intermediate.slice(RERANK_TOP_K)];
186689
+ }
186690
+ }
186691
+ } catch {
186692
+ console.error("[memorix] LLM rerank failed or timed out, using original order");
186693
+ }
186694
+ } else {
186695
+ mark(`rerank(skipped,tier=${tier})`);
185835
186696
  }
185836
186697
  } else {
185837
186698
  mark(`rerank(skipped,tier=${tier})`);
@@ -186013,6 +186874,7 @@ var init_orama_store = __esm({
186013
186874
  init_project_affinity();
186014
186875
  init_intent_detector();
186015
186876
  init_query_expansion();
186877
+ init_policy();
186016
186878
  init_timeout();
186017
186879
  db = null;
186018
186880
  dbInitPromise = null;
@@ -186402,9 +187264,27 @@ function normalizeMemoryBrowseQuery(query) {
186402
187264
  async function compactSearch(options, surface = "mcp") {
186403
187265
  await ensureFreshIndex();
186404
187266
  const searchOptions = { ...options, query: normalizeMemoryBrowseQuery(options.query) };
186405
- const entries = (await searchObservations(searchOptions)).map(
187267
+ let entries = (await searchObservations(searchOptions)).map(
186406
187268
  (entry) => entry.projectId || !options.projectId ? entry : { ...entry, projectId: options.projectId }
186407
187269
  );
187270
+ if (options.projectId) {
187271
+ try {
187272
+ const feedback = new MemoryFeedbackStore();
187273
+ await feedback.init(await getProjectDataDir(options.projectId));
187274
+ const states = feedback.weights(options.projectId, "observation");
187275
+ entries = entries.map((entry) => {
187276
+ const state = states.get(String(entry.id));
187277
+ if (!state) return entry;
187278
+ return {
187279
+ ...entry,
187280
+ score: (entry.score ?? 0) * feedbackWeightMultiplier(state.weight),
187281
+ feedbackWeight: state.weight,
187282
+ feedbackStatus: state.status
187283
+ };
187284
+ }).filter((entry) => entry.feedbackStatus !== "archived" || options.status === "all").sort((a4, b4) => (b4.score ?? 0) - (a4.score ?? 0));
187285
+ } catch {
187286
+ }
187287
+ }
186408
187288
  let formatted = formatIndexTable(entries, searchOptions.query, !options.projectId, surface);
186409
187289
  if (entries.length === 0 && options.projectId) {
186410
187290
  const allObservations = filterReadableObservations(getAllObservations(), options.reader);
@@ -186723,6 +187603,8 @@ var init_engine3 = __esm({
186723
187603
  init_mini_skills();
186724
187604
  init_secret_filter();
186725
187605
  init_visibility();
187606
+ init_persistence();
187607
+ init_feedback();
186726
187608
  }
186727
187609
  });
186728
187610
 
@@ -186737,7 +187619,7 @@ __export(team_store_exports, {
186737
187619
  isTeamStoreInitialized: () => isTeamStoreInitialized,
186738
187620
  resetTeamStore: () => resetTeamStore
186739
187621
  });
186740
- import { randomUUID as randomUUID14 } from "crypto";
187622
+ import { randomUUID as randomUUID16 } from "crypto";
186741
187623
  import path24 from "path";
186742
187624
  import fs15 from "fs";
186743
187625
  function getTeamStore() {
@@ -187026,7 +187908,7 @@ var init_team_store = __esm({
187026
187908
  * reactivate it (preserving agent_id). Otherwise create a new one.
187027
187909
  */
187028
187910
  registerAgent(input) {
187029
- const instanceId = input.instanceId || randomUUID14();
187911
+ const instanceId = input.instanceId || randomUUID16();
187030
187912
  const now3 = Date.now();
187031
187913
  const existing = this.stmtAgentFindByInstance.get(
187032
187914
  input.projectId,
@@ -187052,7 +187934,7 @@ var init_team_store = __esm({
187052
187934
  this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
187053
187935
  return row2;
187054
187936
  }
187055
- const agentId = randomUUID14();
187937
+ const agentId = randomUUID16();
187056
187938
  this.stmtAgentUpsert.run({
187057
187939
  agent_id: agentId,
187058
187940
  project_id: input.projectId,
@@ -187154,7 +188036,7 @@ var init_team_store = __esm({
187154
188036
  return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
187155
188037
  }
187156
188038
  }
187157
- const id = randomUUID14();
188039
+ const id = randomUUID16();
187158
188040
  const now3 = Date.now();
187159
188041
  const row = {
187160
188042
  id,
@@ -187204,7 +188086,7 @@ var init_team_store = __esm({
187204
188086
  // Task Operations (with atomic claim semantics)
187205
188087
  // ═══════════════════════════════════════════════════════════════════
187206
188088
  createTask(input) {
187207
- const taskId = randomUUID14();
188089
+ const taskId = randomUUID16();
187208
188090
  const now3 = Date.now();
187209
188091
  const row = {
187210
188092
  task_id: taskId,
@@ -187476,7 +188358,7 @@ var init_team_store = __esm({
187476
188358
  for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
187477
188359
  for (const msg of msgs) {
187478
188360
  this.stmtMsgInsert.run({
187479
- id: msg.id ?? randomUUID14(),
188361
+ id: msg.id ?? randomUUID16(),
187480
188362
  project_id: "migrated",
187481
188363
  sender_agent_id: msg.from ?? "unknown",
187482
188364
  recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
@@ -190318,6 +191200,16 @@ function buildInitTomlConfig(options) {
190318
191200
  }
190319
191201
  }
190320
191202
  lines.push("");
191203
+ lines.push("[rerank]");
191204
+ lines.push("# Optional HTTP rerank against any Cohere-compatible /rerank API.");
191205
+ lines.push('# provider = "http"');
191206
+ lines.push('# model = "rerank-model"');
191207
+ lines.push("# base_url inherits [memory.llm] when unset.");
191208
+ if (isGlobal) {
191209
+ lines.push("# Bearer inherits the memory LLM key only when that endpoint matches.");
191210
+ lines.push("# A different rerank URL needs MEMORIX_RERANK_API_KEY.");
191211
+ }
191212
+ lines.push("");
190321
191213
  lines.push("[git]");
190322
191214
  lines.push(`auto_hook = ${options.gitAutoHook}`);
190323
191215
  lines.push("ingest_on_commit = true");
@@ -190490,6 +191382,12 @@ var init_init = __esm({
190490
191382
  envLines.push("# MEMORIX_EMBEDDING_BASE_URL=https://api.openai.com/v1");
190491
191383
  envLines.push("");
190492
191384
  }
191385
+ envLines.push("# Optional HTTP rerank (Cohere-compatible POST {base_url}/rerank)");
191386
+ envLines.push("# MEMORIX_RERANK_PROVIDER=http");
191387
+ envLines.push("# MEMORIX_RERANK_MODEL=rerank-model");
191388
+ envLines.push("# MEMORIX_RERANK_BASE_URL=https://api.example.com/v1");
191389
+ envLines.push("# MEMORIX_RERANK_API_KEY= # required when BASE_URL is not the memory LLM endpoint");
191390
+ envLines.push("");
190493
191391
  envLines.push("# Optional memory LLM simple key (does not apply to embedding or agent lanes)");
190494
191392
  envLines.push("# MEMORIX_API_KEY=sk-your-key-here");
190495
191393
  envLines.push("");
@@ -191893,8 +192791,8 @@ var init_official_skills = __esm({
191893
192791
  "",
191894
192792
  "| Situation | Prefer | CLI fallback |",
191895
192793
  "|---|---|---|",
191896
- '| 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` |',
191897
- '| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --brief-json` |',
192794
+ '| Starting a coding task and needing the best task-lensed project map | `memorix_project_context` with the user\'s task | `memorix context "<task>" --fallback --brief-json` |',
192795
+ '| Continuing prior project work | `memorix_project_context` with the user\'s task | `memorix resume "<task>" --fallback --brief-json` |',
191898
192796
  '| Need structured refs/freshness for code-bound memories | `memorix_context_pack` | `memorix codegraph context-pack --task "<topic>"` |',
191899
192797
  '| Explicit memory graph question | `memorix_graph_context` | `memorix memory graph-context --query "<topic>"` |',
191900
192798
  '| Specific past decision, bug, file, or change | `memorix_search` | `memorix memory search --query "<topic>"` |',
@@ -191907,7 +192805,7 @@ var init_official_skills = __esm({
191907
192805
  "",
191908
192806
  "- 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.",
191909
192807
  "- 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.",
191910
- '- 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.',
192808
+ '- 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>" --fallback --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>" --fallback --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.',
191911
192809
  "- 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.",
191912
192810
  "- 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.",
191913
192811
  "- 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.",
@@ -193166,7 +194064,7 @@ alwaysApply: true
193166
194064
  "",
193167
194065
  `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.`,
193168
194066
  "",
193169
- '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.',
194067
+ '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>" --fallback --brief-json`. For a new task, use `memorix context "<task>" --fallback --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.',
193170
194068
  "",
193171
194069
  "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.",
193172
194070
  "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.",
@@ -194916,6 +195814,12 @@ function serializeResolvedConfigToToml(config2, options = {}) {
194916
195814
  api_key: includeSecrets ? config2.embedding.apiKey : void 0,
194917
195815
  dimensions: config2.embedding.dimensions
194918
195816
  });
195817
+ writeSection(lines, "rerank", {
195818
+ provider: config2.rerank.provider,
195819
+ model: config2.rerank.model,
195820
+ base_url: config2.rerank.baseUrl,
195821
+ api_key: includeSecrets ? config2.rerank.apiKey : void 0
195822
+ });
194919
195823
  writeSection(lines, "memory", {
194920
195824
  inject: config2.memory.inject,
194921
195825
  formation: config2.memory.formation,
@@ -197668,7 +198572,7 @@ var compaction_checkpoint_store_exports = {};
197668
198572
  __export(compaction_checkpoint_store_exports, {
197669
198573
  CompactionCheckpointStore: () => CompactionCheckpointStore
197670
198574
  });
197671
- import { createHash as createHash13, randomUUID as randomUUID15 } from "crypto";
198575
+ import { createHash as createHash14, randomUUID as randomUUID17 } from "crypto";
197672
198576
  function optionalText7(value) {
197673
198577
  return typeof value === "string" && value ? value : void 0;
197674
198578
  }
@@ -197710,7 +198614,7 @@ function sourceKeyFor(input, summary) {
197710
198614
  input.tokensBefore ?? "",
197711
198615
  input.firstKeptEntryId ?? ""
197712
198616
  ].join("\0");
197713
- return `derived:${createHash13("sha256").update(material).digest("hex").slice(0, 32)}`;
198617
+ return `derived:${createHash14("sha256").update(material).digest("hex").slice(0, 32)}`;
197714
198618
  }
197715
198619
  function rowToCheckpoint(row) {
197716
198620
  return {
@@ -197769,7 +198673,7 @@ var init_compaction_checkpoint_store = __esm({
197769
198673
  if (pending) return rowToCheckpoint(pending);
197770
198674
  const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
197771
198675
  const checkpoint = {
197772
- id: randomUUID15(),
198676
+ id: randomUUID17(),
197773
198677
  projectId: input.projectId,
197774
198678
  sessionId: input.sessionId,
197775
198679
  agent: input.agent,
@@ -197777,7 +198681,7 @@ var init_compaction_checkpoint_store = __esm({
197777
198681
  captureKind: "preflight",
197778
198682
  reason: safeReason(input.reason),
197779
198683
  sourceEvent: input.sourceEvent,
197780
- sourceKey: `pre:${randomUUID15()}`,
198684
+ sourceKey: `pre:${randomUUID17()}`,
197781
198685
  transcriptAvailable: Boolean(input.transcriptAvailable),
197782
198686
  status: "active",
197783
198687
  preCapturedAt: now3,
@@ -197843,7 +198747,7 @@ var init_compaction_checkpoint_store = __esm({
197843
198747
  return this.get(checkpoint2.id);
197844
198748
  }
197845
198749
  const checkpoint = {
197846
- id: randomUUID15(),
198750
+ id: randomUUID17(),
197847
198751
  projectId: input.projectId,
197848
198752
  sessionId: input.sessionId,
197849
198753
  agent: input.agent,
@@ -198591,6 +199495,59 @@ var init_context_receipt = __esm({
198591
199495
  }
198592
199496
  });
198593
199497
 
199498
+ // src/cli/fallback-budget.ts
199499
+ import { createHash as createHash15 } from "crypto";
199500
+ function fallbackRequestKey(projectId, task) {
199501
+ const normalized = (task ?? "").trim().toLowerCase().replace(/\s+/g, " ");
199502
+ return createHash15("sha256").update(`${projectId}
199503
+ ${normalized}`).digest("hex").slice(0, 32);
199504
+ }
199505
+ var DEFAULT_FALLBACK_TTL_MS, CliFallbackBudget;
199506
+ var init_fallback_budget = __esm({
199507
+ "src/cli/fallback-budget.ts"() {
199508
+ "use strict";
199509
+ init_esm_shims();
199510
+ init_sqlite_db();
199511
+ DEFAULT_FALLBACK_TTL_MS = 10 * 60 * 1e3;
199512
+ CliFallbackBudget = class {
199513
+ db = null;
199514
+ async init(dataDir) {
199515
+ this.db = getDatabase(dataDir);
199516
+ this.db.exec(`
199517
+ CREATE TABLE IF NOT EXISTS cli_fallback_attempts (
199518
+ project_id TEXT NOT NULL,
199519
+ request_key TEXT NOT NULL,
199520
+ count INTEGER NOT NULL DEFAULT 0,
199521
+ last_attempt_at INTEGER NOT NULL,
199522
+ expires_at INTEGER NOT NULL,
199523
+ PRIMARY KEY (project_id, request_key)
199524
+ )
199525
+ `);
199526
+ }
199527
+ claim(projectId, task, now3 = Date.now(), ttlMs = DEFAULT_FALLBACK_TTL_MS) {
199528
+ if (!this.db) throw new Error("CliFallbackBudget is not initialized.");
199529
+ const key = fallbackRequestKey(projectId, task);
199530
+ const current = this.db.prepare(`
199531
+ SELECT count, expires_at FROM cli_fallback_attempts WHERE project_id = ? AND request_key = ?
199532
+ `).get(projectId, key);
199533
+ const count3 = Number(current?.count ?? 0);
199534
+ if (current && Number(current.expires_at) > now3 && count3 >= 1) {
199535
+ return { allowed: false, key, count: count3, reason: "Fallback budget exhausted for this task. Stop probing and continue with the current project files." };
199536
+ }
199537
+ this.db.prepare(`
199538
+ INSERT INTO cli_fallback_attempts (project_id, request_key, count, last_attempt_at, expires_at)
199539
+ VALUES (?, ?, 1, ?, ?)
199540
+ ON CONFLICT(project_id, request_key) DO UPDATE SET
199541
+ count = CASE WHEN excluded.expires_at > cli_fallback_attempts.expires_at THEN 1 ELSE cli_fallback_attempts.count + 1 END,
199542
+ last_attempt_at = excluded.last_attempt_at,
199543
+ expires_at = CASE WHEN excluded.expires_at > cli_fallback_attempts.expires_at THEN excluded.expires_at ELSE cli_fallback_attempts.expires_at END
199544
+ `).run(projectId, key, now3, now3 + Math.max(1e3, ttlMs));
199545
+ return { allowed: true, key, count: 1 };
199546
+ }
199547
+ };
199548
+ }
199549
+ });
199550
+
198594
199551
  // src/cli/commands/context.ts
198595
199552
  var context_exports = {};
198596
199553
  __export(context_exports, {
@@ -198619,6 +199576,7 @@ var init_context2 = __esm({
198619
199576
  init_context_receipt();
198620
199577
  init_workflows2();
198621
199578
  init_operator_shared();
199579
+ init_fallback_budget();
198622
199580
  context_default = defineCommand({
198623
199581
  meta: {
198624
199582
  name: "context",
@@ -198635,13 +199593,23 @@ var init_context2 = __esm({
198635
199593
  refresh: { type: "string", description: "Project scan policy: auto, always, or never" },
198636
199594
  agent: { type: "string", description: "Optional target agent for compatible workflow selection" },
198637
199595
  json: { type: "boolean", description: "Emit machine-readable JSON output" },
198638
- briefJson: { type: "boolean", description: "Emit only the bounded agent brief and receipt JSON" }
199596
+ briefJson: { type: "boolean", description: "Emit only the bounded agent brief and receipt JSON" },
199597
+ fallback: { type: "boolean", description: "Mark this as the one-shot MCP-unavailable fallback for the task" }
198639
199598
  },
198640
199599
  run: async ({ args }) => {
198641
199600
  const asJson = !!args.json || !!args.briefJson;
198642
199601
  const task = args.task?.trim() || args.input?.trim() || void 0;
198643
199602
  try {
198644
199603
  const { project, dataDir, reader: reader2 } = await getCliProjectContext();
199604
+ if (args.fallback) {
199605
+ const budget = new CliFallbackBudget();
199606
+ await budget.init(dataDir);
199607
+ const claim = budget.claim(project.id, task);
199608
+ if (!claim.allowed) {
199609
+ emitError(claim.reason ?? "CLI fallback budget exhausted for this task.", asJson);
199610
+ return;
199611
+ }
199612
+ }
198645
199613
  const context = await buildAutoProjectContext({
198646
199614
  project,
198647
199615
  dataDir,
@@ -198699,7 +199667,8 @@ var init_resume = __esm({
198699
199667
  refresh: { type: "string", description: "Project scan policy: auto, always, or never" },
198700
199668
  agent: { type: "string", description: "Optional target agent for compatible workflow selection" },
198701
199669
  json: { type: "boolean", description: "Emit machine-readable JSON output" },
198702
- briefJson: { type: "boolean", description: "Emit only the bounded agent brief and receipt JSON" }
199670
+ briefJson: { type: "boolean", description: "Emit only the bounded agent brief and receipt JSON" },
199671
+ fallback: { type: "boolean", description: "Mark this as the one-shot MCP-unavailable fallback for the task" }
198703
199672
  },
198704
199673
  async run({ args }) {
198705
199674
  await context_default.run?.({
@@ -198710,6 +199679,7 @@ var init_resume = __esm({
198710
199679
  agent: args.agent,
198711
199680
  json: args.json,
198712
199681
  briefJson: args.briefJson,
199682
+ fallback: args.fallback,
198713
199683
  resume: true
198714
199684
  },
198715
199685
  rawArgs: [],
@@ -199965,6 +200935,166 @@ var init_knowledge = __esm({
199965
200935
  }
199966
200936
  });
199967
200937
 
200938
+ // src/cli/commands/evidence.ts
200939
+ var evidence_exports = {};
200940
+ __export(evidence_exports, {
200941
+ default: () => evidence_default
200942
+ });
200943
+ var evidence_default;
200944
+ var init_evidence = __esm({
200945
+ "src/cli/commands/evidence.ts"() {
200946
+ "use strict";
200947
+ init_esm_shims();
200948
+ init_dist2();
200949
+ init_obs_store();
200950
+ init_evidence_store();
200951
+ init_operator_shared();
200952
+ evidence_default = defineCommand({
200953
+ meta: { name: "evidence", description: "Inspect project-scoped evidence cards and provenance" },
200954
+ args: {
200955
+ action: { type: "positional", description: "list, get, sync, stale, or events", required: false },
200956
+ id: { type: "string", description: "Candidate id, usually an observation id" },
200957
+ paths: { type: "string", description: "Comma-separated changed paths for stale" },
200958
+ reason: { type: "string", description: "Why the source became stale" },
200959
+ query: { type: "string", description: "Search title/source fields" },
200960
+ limit: { type: "string", description: "Maximum cards/events" },
200961
+ json: { type: "boolean", description: "Emit JSON" }
200962
+ },
200963
+ async run({ args }) {
200964
+ const action = args.action || "list";
200965
+ const asJson = !!args.json;
200966
+ try {
200967
+ const context = action === "list" || action === "get" || action === "events" ? await getCliReadContext() : await getCliProjectContext({ searchIndex: false });
200968
+ const { project, dataDir } = context;
200969
+ const store2 = new EvidenceCardStore();
200970
+ await store2.init(dataDir);
200971
+ const limit2 = Math.min(100, Math.max(1, Number(args.limit || 20)));
200972
+ if (action === "sync") {
200973
+ const synced = store2.syncObservations(await getObservationStore().loadByProject(project.id));
200974
+ emitResult({ project, synced: synced.length }, `Synced ${synced.length} evidence card(s).`, asJson);
200975
+ return;
200976
+ }
200977
+ if (action === "stale") {
200978
+ const paths = String(args.paths || "").split(",").map((value) => value.trim()).filter(Boolean);
200979
+ const changed = store2.markStaleForPaths(project.id, paths, String(args.reason || "source file changed"));
200980
+ emitResult({ project, staleMarked: changed, paths }, `Marked ${changed} evidence card(s) stale.`, asJson);
200981
+ return;
200982
+ }
200983
+ const id = String(args.id || "").trim();
200984
+ if (action === "get" || action === "events") {
200985
+ if (!id) {
200986
+ emitError(`--id is required for evidence ${action}.`, asJson);
200987
+ return;
200988
+ }
200989
+ const card = store2.get(project.id, "observation", id);
200990
+ if (!card) {
200991
+ emitError(`Evidence card for observation ${id} was not found.`, asJson);
200992
+ return;
200993
+ }
200994
+ const result = action === "events" ? store2.listEvents(card.id, limit2) : { card, events: store2.listEvents(card.id) };
200995
+ emitResult({ project, result }, JSON.stringify(result, null, 2), asJson);
200996
+ return;
200997
+ }
200998
+ if (action !== "list") {
200999
+ emitError("action must be list, get, sync, stale, or events.", asJson);
201000
+ return;
201001
+ }
201002
+ const query = String(args.query || "").trim().toLowerCase();
201003
+ const cards = store2.list(project.id, { limit: 100 }).filter((card) => !query || [card.title, card.summary, card.sourceRef, card.locator || ""].join(" ").toLowerCase().includes(query)).slice(0, limit2);
201004
+ emitResult({ project, cards }, cards.length === 0 ? "No evidence cards found." : cards.map((card) => `- ${card.candidateId} ${card.title} [${card.freshness}] ${card.sourceRef}`).join("\n"), asJson);
201005
+ } catch (error40) {
201006
+ emitError(error40 instanceof Error ? error40.message : String(error40), asJson);
201007
+ }
201008
+ }
201009
+ });
201010
+ }
201011
+ });
201012
+
201013
+ // src/cli/commands/feedback.ts
201014
+ var feedback_exports2 = {};
201015
+ __export(feedback_exports2, {
201016
+ default: () => feedback_default
201017
+ });
201018
+ var signals, feedback_default;
201019
+ var init_feedback2 = __esm({
201020
+ "src/cli/commands/feedback.ts"() {
201021
+ "use strict";
201022
+ init_esm_shims();
201023
+ init_dist2();
201024
+ init_feedback();
201025
+ init_operator_shared();
201026
+ signals = ["user-correction", "verification-success", "verification-failure", "code-change", "used", "not-used", "code-conflict", "strengthen", "weaken", "revoke"];
201027
+ feedback_default = defineCommand({
201028
+ meta: { name: "feedback", description: "Record and audit memory feedback" },
201029
+ args: {
201030
+ action: { type: "positional", description: "record, show, or audit", required: false },
201031
+ id: { type: "string", description: "Candidate id" },
201032
+ kind: { type: "string", description: "observation, claim, durable-memory, or workflow" },
201033
+ signal: { type: "string", description: "Feedback signal" },
201034
+ source: { type: "string", description: "Source reference for the feedback" },
201035
+ actor: { type: "string", description: "Actor id or human label" },
201036
+ note: { type: "string", description: "Short audit note" },
201037
+ targetEventId: { type: "string", description: "Event id to revoke" },
201038
+ limit: { type: "string", description: "Maximum audit rows" },
201039
+ json: { type: "boolean", description: "Emit JSON" }
201040
+ },
201041
+ async run({ args }) {
201042
+ const action = args.action || "show";
201043
+ const asJson = !!args.json;
201044
+ try {
201045
+ const context = action === "show" || action === "audit" ? await getCliReadContext() : await getCliProjectContext({ searchIndex: false });
201046
+ const { project, dataDir } = context;
201047
+ const id = String(args.id || "").trim();
201048
+ const kind = String(args.kind || "observation").trim() || "observation";
201049
+ if (!id) {
201050
+ emitError("--id is required.", asJson);
201051
+ return;
201052
+ }
201053
+ if (!["observation", "claim", "durable-memory", "workflow"].includes(kind)) {
201054
+ emitError("Invalid --kind.", asJson);
201055
+ return;
201056
+ }
201057
+ const store2 = new MemoryFeedbackStore();
201058
+ await store2.init(dataDir);
201059
+ if (action === "show") {
201060
+ const result2 = store2.getState(project.id, kind, id);
201061
+ emitResult({ project, result: result2 }, JSON.stringify(result2, null, 2), asJson);
201062
+ return;
201063
+ }
201064
+ if (action === "audit") {
201065
+ const result2 = store2.audit(project.id, kind, id, Number(args.limit || 100));
201066
+ emitResult({ project, result: result2 }, JSON.stringify(result2, null, 2), asJson);
201067
+ return;
201068
+ }
201069
+ if (action !== "record") {
201070
+ emitError("action must be record, show, or audit.", asJson);
201071
+ return;
201072
+ }
201073
+ const signal = String(args.signal || "").trim();
201074
+ const sourceRef = String(args.source || "").trim();
201075
+ if (!signals.includes(signal) || !sourceRef) {
201076
+ emitError(`record requires --signal (${signals.join(", ")}) and --source.`, asJson);
201077
+ return;
201078
+ }
201079
+ const result = store2.record({
201080
+ projectId: project.id,
201081
+ candidateKind: kind,
201082
+ candidateId: id,
201083
+ signal,
201084
+ sourceRef,
201085
+ ...args.actor ? { actor: String(args.actor) } : {},
201086
+ ...args.note ? { note: String(args.note) } : {},
201087
+ ...args.targetEventId ? { targetEventId: String(args.targetEventId) } : {}
201088
+ });
201089
+ emitResult({ project, result }, `Recorded ${signal} for ${kind}:${id}; weight=${result.state.weight.toFixed(2)}.`, asJson);
201090
+ } catch (error40) {
201091
+ emitError(error40 instanceof Error ? error40.message : String(error40), asJson);
201092
+ }
201093
+ }
201094
+ });
201095
+ }
201096
+ });
201097
+
199968
201098
  // src/cli/commands/reasoning.ts
199969
201099
  var reasoning_exports = {};
199970
201100
  __export(reasoning_exports, {
@@ -203165,7 +204295,7 @@ __export(planner_exports, {
203165
204295
  materializeTaskGraph: () => materializeTaskGraph,
203166
204296
  seedAutonomousPipeline: () => seedAutonomousPipeline
203167
204297
  });
203168
- import { randomUUID as randomUUID16 } from "crypto";
204298
+ import { randomUUID as randomUUID18 } from "crypto";
203169
204299
  function isPlannerTask(metadata) {
203170
204300
  if (!metadata) return null;
203171
204301
  try {
@@ -203223,7 +204353,7 @@ function seedAutonomousPipeline(teamStore, projectId, config2, opts) {
203223
204353
  const maxIterations = config2.maxIterations ?? 3;
203224
204354
  const taskBudget = config2.taskBudget ?? 15;
203225
204355
  const structuredPlan = opts?.structuredPlan ?? true;
203226
- const pipelineId = randomUUID16();
204356
+ const pipelineId = randomUUID18();
203227
204357
  const meta = {
203228
204358
  plannerType: "plan",
203229
204359
  pipelineId,
@@ -204142,11 +205272,11 @@ __export(receipt_service_exports, {
204142
205272
  buildHandoffReceipt: () => buildHandoffReceipt,
204143
205273
  formatHandoffReceipt: () => formatHandoffReceipt
204144
205274
  });
204145
- import { createHash as createHash14 } from "crypto";
205275
+ import { createHash as createHash16 } from "crypto";
204146
205276
  import { existsSync as existsSync21, readFileSync as readFileSync13 } from "fs";
204147
205277
  import path36 from "path";
204148
205278
  function sha256(value) {
204149
- return `sha256:${createHash14("sha256").update(value).digest("hex")}`;
205279
+ return `sha256:${createHash16("sha256").update(value).digest("hex")}`;
204150
205280
  }
204151
205281
  function hashId(projectId, id) {
204152
205282
  return sha256(`${projectId}:obs:${id}`);
@@ -204367,6 +205497,10 @@ var init_tool_profile = __esm({
204367
205497
  // multimodal support to one additional schema rather than enabling lite.
204368
205498
  memorix_session_start: ["micro", "lite", "team", "full"],
204369
205499
  memorix_media: ["micro", "lite", "team", "full"],
205500
+ // Evidence and feedback are deliberate operator/context-control surfaces;
205501
+ // keep the default micro profile at its nine-tool token budget.
205502
+ memorix_evidence: ["lite", "team", "full"],
205503
+ memorix_feedback: ["lite", "team", "full"],
204370
205504
  // ── lite: extended solo memory surface ────────────────────────────
204371
205505
  memorix_graph_context: ["lite", "team", "full"],
204372
205506
  memorix_timeline: ["lite", "team", "full"],
@@ -216321,9 +217455,9 @@ var init_graph_scope = __esm({
216321
217455
  });
216322
217456
 
216323
217457
  // src/rules/utils.ts
216324
- import { createHash as createHash15 } from "crypto";
217458
+ import { createHash as createHash17 } from "crypto";
216325
217459
  function hashContent2(content) {
216326
- return createHash15("sha256").update(content.trim()).digest("hex").substring(0, 16);
217460
+ return createHash17("sha256").update(content.trim()).digest("hex").substring(0, 16);
216327
217461
  }
216328
217462
  function generateRuleId(source, filePath) {
216329
217463
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -218027,6 +219161,18 @@ var init_cleanup = __esm({
218027
219161
  });
218028
219162
 
218029
219163
  // src/dashboard/maintenance.ts
219164
+ var maintenance_exports = {};
219165
+ __export(maintenance_exports, {
219166
+ DashboardMaintenanceError: () => DashboardMaintenanceError,
219167
+ executeCleanup: () => executeCleanup,
219168
+ executeConsolidate: () => executeConsolidate,
219169
+ executeDeduplicate: () => executeDeduplicate,
219170
+ executeRetentionArchive: () => executeRetentionArchive,
219171
+ previewCleanup: () => previewCleanup,
219172
+ previewConsolidate: () => previewConsolidate,
219173
+ previewDeduplicate: () => previewDeduplicate,
219174
+ previewRetentionArchive: () => previewRetentionArchive
219175
+ });
218030
219176
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
218031
219177
  function reader(projectId) {
218032
219178
  return { projectId };
@@ -218268,6 +219414,23 @@ var init_maintenance = __esm({
218268
219414
  }
218269
219415
  });
218270
219416
 
219417
+ // src/dashboard/config-provenance.ts
219418
+ function resolveDashboardKeySource({ value, envKeys, configSource }) {
219419
+ for (const envKey of envKeys) {
219420
+ const envValue = process.env[envKey];
219421
+ if (envValue !== value) continue;
219422
+ return getLoadedEnvValue(envKey) === envValue ? ".env" : `env:${envKey}`;
219423
+ }
219424
+ return configSource;
219425
+ }
219426
+ var init_config_provenance = __esm({
219427
+ "src/dashboard/config-provenance.ts"() {
219428
+ "use strict";
219429
+ init_esm_shims();
219430
+ init_dotenv_loader();
219431
+ }
219432
+ });
219433
+
218271
219434
  // src/dashboard/project-classification.ts
218272
219435
  var project_classification_exports = {};
218273
219436
  __export(project_classification_exports, {
@@ -219047,6 +220210,20 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
219047
220210
  sendJson(res, observations2);
219048
220211
  break;
219049
220212
  }
220213
+ case "/evidence": {
220214
+ const observations2 = filterDashboardObservations(
220215
+ await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
220216
+ effectiveProjectId
220217
+ );
220218
+ const evidenceStore = new EvidenceCardStore();
220219
+ await evidenceStore.init(effectiveDataDir);
220220
+ evidenceStore.syncObservations(observations2);
220221
+ const query = url2.searchParams.get("q") || "";
220222
+ const limit2 = Number(url2.searchParams.get("limit") || 20);
220223
+ const cards = evidenceStore.list(effectiveProjectId, { limit: Math.min(100, Math.max(1, limit2)) }).filter((card) => !query || [card.title, card.summary, card.sourceRef, card.locator ?? ""].join(" ").toLowerCase().includes(query.toLowerCase()));
220224
+ sendJson(res, { cards, projectId: effectiveProjectId });
220225
+ break;
220226
+ }
219050
220227
  case "/sessions": {
219051
220228
  const allSessions = await getSessionStore().loadAll();
219052
220229
  const sessions = filterByProject(allSessions, effectiveProjectId);
@@ -219094,8 +220271,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
219094
220271
  filesModified: o4.filesModified
219095
220272
  }));
219096
220273
  const retentionReferenceTime = new Date(now3);
220274
+ const feedback = new MemoryFeedbackStore();
220275
+ await feedback.init(effectiveDataDir).catch(() => void 0);
219097
220276
  const retention = summarizeRetentionProjections(
219098
- observations2.filter((observation) => observation.type !== "probe").map((observation) => projectObservationRetention(observation, { referenceTime: retentionReferenceTime }))
220277
+ observations2.filter((observation) => observation.type !== "probe").map((observation) => projectObservationRetention(observation, {
220278
+ referenceTime: retentionReferenceTime,
220279
+ feedback: feedback.getState(effectiveProjectId, "observation", String(observation.id))
220280
+ }))
219099
220281
  );
219100
220282
  const retentionSummary = {
219101
220283
  active: retention.active,
@@ -219189,9 +220371,14 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
219189
220371
  effectiveProjectId
219190
220372
  );
219191
220373
  const referenceTime = /* @__PURE__ */ new Date();
220374
+ const feedback = new MemoryFeedbackStore();
220375
+ await feedback.init(effectiveDataDir).catch(() => void 0);
219192
220376
  const rows = observations2.filter((observation) => observation.type !== "probe").map((observation) => ({
219193
220377
  observation,
219194
- retention: projectObservationRetention(observation, { referenceTime })
220378
+ retention: projectObservationRetention(observation, {
220379
+ referenceTime,
220380
+ feedback: feedback.getState(effectiveProjectId, "observation", String(observation.id))
220381
+ })
219195
220382
  })).sort((a4, b4) => b4.retention.displayScore - a4.retention.displayScore);
219196
220383
  const summary = summarizeRetentionProjections(rows.map((row) => row.retention));
219197
220384
  const scored = rows.map(({ observation, retention }) => ({
@@ -219262,23 +220449,13 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
219262
220449
  const os9 = await import("os");
219263
220450
  const { existsSync: existsSync34 } = await import("fs");
219264
220451
  const { join: join34 } = await import("path");
219265
- let yml = {};
219266
220452
  const configProjectRoot = effectiveProjectRoot;
219267
- try {
219268
- const { loadYamlConfig: loadYamlConfig2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
219269
- yml = configProjectRoot ? loadYamlConfig2(configProjectRoot) : loadYamlConfig2(null);
219270
- } catch {
219271
- }
219272
- if (configProjectRoot) {
219273
- try {
219274
- const { loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
219275
- loadDotenv2(configProjectRoot);
219276
- } catch {
219277
- }
219278
- }
220453
+ const resolved = getResolvedConfig({ projectRoot: configProjectRoot });
219279
220454
  const files = {
219280
220455
  "project memorix.yml": { exists: false, path: "", unavailable: !configProjectRoot },
219281
220456
  "user memorix.yml": { exists: false, path: "" },
220457
+ "project memorix.toml": { exists: false, path: "", unavailable: !configProjectRoot },
220458
+ "user config.toml": { exists: false, path: "" },
219282
220459
  "project .env": { exists: false, path: "", unavailable: !configProjectRoot },
219283
220460
  "user .env": { exists: false, path: "" },
219284
220461
  "legacy config.json": { exists: false, path: "" }
@@ -219288,6 +220465,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
219288
220465
  const paths = {
219289
220466
  "project memorix.yml": configProjectRoot ? join34(configProjectRoot, "memorix.yml") : null,
219290
220467
  "user memorix.yml": join34(home, ".memorix", "memorix.yml"),
220468
+ "project memorix.toml": configProjectRoot ? join34(configProjectRoot, "memorix.toml") : null,
220469
+ "user config.toml": join34(home, ".memorix", "config.toml"),
219291
220470
  "project .env": configProjectRoot ? join34(configProjectRoot, ".env") : null,
219292
220471
  "user .env": join34(home, ".memorix", ".env"),
219293
220472
  "legacy config.json": join34(home, ".memorix", "config.json")
@@ -219302,67 +220481,61 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
219302
220481
  } catch {
219303
220482
  }
219304
220483
  const values = [];
219305
- const getEnvSource = async (envKey, ymlSource) => {
219306
- if (process.env[envKey]) {
219307
- try {
219308
- const { getLoadedEnvFiles: getLoadedEnvFiles2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
219309
- const envFiles = getLoadedEnvFiles2();
219310
- if (envFiles.length > 0) return `.env (${envKey})`;
219311
- } catch {
219312
- }
219313
- return `env:${envKey}`;
219314
- }
219315
- return ymlSource ?? "default";
219316
- };
219317
- const llmProvider = process.env.MEMORIX_LLM_PROVIDER || yml.llm?.provider;
219318
- if (llmProvider) values.push({ key: "llm.provider", value: llmProvider, source: await getEnvSource("MEMORIX_LLM_PROVIDER", yml.llm?.provider ? "memorix.yml" : void 0) });
219319
- const llmModel = process.env.MEMORIX_LLM_MODEL || yml.llm?.model;
219320
- if (llmModel) values.push({ key: "llm.model", value: llmModel, source: await getEnvSource("MEMORIX_LLM_MODEL", yml.llm?.model ? "memorix.yml" : void 0) });
219321
- const llmKey = process.env.MEMORIX_LLM_API_KEY || process.env.MEMORIX_API_KEY || yml.llm?.apiKey || process.env.OPENAI_API_KEY;
220484
+ const tomlSource = configProjectRoot && files["project memorix.toml"].exists ? "memorix.toml" : files["user config.toml"].exists ? "config.toml" : "default";
220485
+ const legacySource = files["project memorix.yml"].exists || files["user memorix.yml"].exists ? "memorix.yml" : files["legacy config.json"].exists ? "config.json" : "default";
220486
+ const configSource = resolved.sources.toml.length > 0 ? tomlSource : resolved.sources.legacy.length > 0 ? legacySource : "default";
220487
+ const source = (envKey) => resolveDashboardKeySource({
220488
+ value: process.env[envKey] ?? "",
220489
+ envKeys: [envKey],
220490
+ configSource
220491
+ });
220492
+ if (resolved.memory.llm.provider) values.push({ key: "llm.provider", value: resolved.memory.llm.provider, source: source("MEMORIX_LLM_PROVIDER") });
220493
+ if (resolved.memory.llm.model) values.push({ key: "llm.model", value: resolved.memory.llm.model, source: source("MEMORIX_LLM_MODEL") });
220494
+ const llmKey = resolved.memory.llm.apiKey;
220495
+ const memoryKeyEnv = [
220496
+ "MEMORIX_LLM_API_KEY",
220497
+ "MEMORIX_API_KEY",
220498
+ "OPENAI_API_KEY",
220499
+ "ANTHROPIC_API_KEY",
220500
+ ...resolved.memory.llm.provider === "openrouter" || resolved.memory.llm.baseUrl?.includes("openrouter.ai") ? ["OPENROUTER_API_KEY"] : []
220501
+ ];
219322
220502
  if (llmKey) {
219323
- let src2 = "unknown";
219324
- if (process.env.MEMORIX_LLM_API_KEY) src2 = await getEnvSource("MEMORIX_LLM_API_KEY");
219325
- else if (process.env.MEMORIX_API_KEY) src2 = await getEnvSource("MEMORIX_API_KEY");
219326
- else if (yml.llm?.apiKey) src2 = "memorix.yml (move to .env!)";
219327
- else if (process.env.OPENAI_API_KEY) src2 = await getEnvSource("OPENAI_API_KEY");
220503
+ const src2 = resolveDashboardKeySource({ value: llmKey, envKeys: memoryKeyEnv, configSource });
219328
220504
  values.push({ key: "llm.apiKey", value: "****" + llmKey.slice(-4), source: src2, sensitive: true });
219329
220505
  } else {
219330
220506
  values.push({ key: "llm.apiKey", value: "not set", source: "none" });
219331
220507
  }
219332
- const agentProvider = process.env.MEMORIX_AGENT_PROVIDER || process.env.MEMORIX_AGENT_LLM_PROVIDER || yml.agent?.provider;
219333
- if (agentProvider) {
219334
- let source;
219335
- if (process.env.MEMORIX_AGENT_PROVIDER) source = await getEnvSource("MEMORIX_AGENT_PROVIDER");
219336
- else if (process.env.MEMORIX_AGENT_LLM_PROVIDER) source = `${await getEnvSource("MEMORIX_AGENT_LLM_PROVIDER")} (legacy)`;
219337
- else source = yml.agent?.provider ? "memorix.yml" : "default";
219338
- values.push({ key: "agent.provider", value: agentProvider, source });
219339
- }
219340
- const agentModel = process.env.MEMORIX_AGENT_MODEL || process.env.MEMORIX_AGENT_LLM_MODEL || yml.agent?.model;
219341
- if (agentModel) {
219342
- let source;
219343
- if (process.env.MEMORIX_AGENT_MODEL) source = await getEnvSource("MEMORIX_AGENT_MODEL");
219344
- else if (process.env.MEMORIX_AGENT_LLM_MODEL) source = `${await getEnvSource("MEMORIX_AGENT_LLM_MODEL")} (legacy)`;
219345
- else source = yml.agent?.model ? "memorix.yml" : "default";
219346
- values.push({ key: "agent.model", value: agentModel, source });
219347
- }
219348
- const agentKey = process.env.MEMORIX_AGENT_API_KEY || process.env.MEMORIX_AGENT_LLM_API_KEY || yml.agent?.apiKey;
220508
+ if (resolved.agent.provider) {
220509
+ values.push({ key: "agent.provider", value: resolved.agent.provider, source: source("MEMORIX_AGENT_PROVIDER") });
220510
+ }
220511
+ if (resolved.agent.model) {
220512
+ values.push({ key: "agent.model", value: resolved.agent.model, source: source("MEMORIX_AGENT_MODEL") });
220513
+ }
220514
+ const agentKey = resolved.agent.apiKey;
219349
220515
  if (agentKey) {
219350
- let src2 = "unknown";
219351
- if (process.env.MEMORIX_AGENT_API_KEY) src2 = await getEnvSource("MEMORIX_AGENT_API_KEY");
219352
- else if (process.env.MEMORIX_AGENT_LLM_API_KEY) src2 = `${await getEnvSource("MEMORIX_AGENT_LLM_API_KEY")} (legacy)`;
219353
- else if (yml.agent?.apiKey) src2 = "memorix.yml (move to .env!)";
220516
+ const src2 = resolveDashboardKeySource({
220517
+ value: agentKey,
220518
+ envKeys: ["MEMORIX_AGENT_API_KEY", "MEMORIX_AGENT_LLM_API_KEY"],
220519
+ configSource
220520
+ });
219354
220521
  values.push({ key: "agent.apiKey", value: "****" + agentKey.slice(-4), source: src2, sensitive: true });
219355
220522
  } else {
219356
- values.push({ key: "agent.apiKey", value: "fallback to llm.apiKey", source: "default" });
219357
- }
219358
- const embProvider = process.env.MEMORIX_EMBEDDING || yml.embedding?.provider || "off";
219359
- values.push({ key: "embedding.provider", value: embProvider, source: await getEnvSource("MEMORIX_EMBEDDING", yml.embedding?.provider ? "memorix.yml" : void 0) });
219360
- values.push({ key: "git.autoHook", value: String(yml.git?.autoHook ?? false), source: yml.git?.autoHook !== void 0 ? "memorix.yml" : "default" });
219361
- values.push({ key: "git.skipMergeCommits", value: String(yml.git?.skipMergeCommits ?? true), source: yml.git?.skipMergeCommits !== void 0 ? "memorix.yml" : "default" });
219362
- if (yml.behavior?.formationMode) values.push({ key: "behavior.formationMode", value: yml.behavior.formationMode, source: "memorix.yml" });
219363
- if (yml.behavior?.sessionInject) values.push({ key: "behavior.sessionInject", value: yml.behavior.sessionInject, source: "memorix.yml" });
219364
- values.push({ key: "server.transport", value: yml.server?.transport || "stdio", source: yml.server?.transport ? "memorix.yml" : "default" });
219365
- values.push({ key: "server.dashboard", value: String(yml.server?.dashboard ?? true), source: yml.server?.dashboard !== void 0 ? "memorix.yml" : "default" });
220523
+ values.push({
220524
+ key: "agent.apiKey",
220525
+ value: "fallback to llm.apiKey",
220526
+ source: llmKey ? resolveDashboardKeySource({ value: llmKey, envKeys: memoryKeyEnv, configSource }) : "default"
220527
+ });
220528
+ }
220529
+ values.push({ key: "embedding.provider", value: resolved.embedding.provider || "off", source: source("MEMORIX_EMBEDDING") });
220530
+ if (resolved.embedding.model) values.push({ key: "embedding.model", value: resolved.embedding.model, source: source("MEMORIX_EMBEDDING_MODEL") });
220531
+ if (resolved.rerank.provider !== "off") {
220532
+ values.push({ key: "rerank.provider", value: resolved.rerank.provider, source: source("MEMORIX_RERANK_PROVIDER") });
220533
+ if (resolved.rerank.model) values.push({ key: "rerank.model", value: resolved.rerank.model, source: source("MEMORIX_RERANK_MODEL") });
220534
+ }
220535
+ values.push({ key: "git.autoHook", value: String(resolved.git.autoHook ?? false), source: configSource });
220536
+ values.push({ key: "git.skipMergeCommits", value: String(resolved.git.skipMergeCommits ?? true), source: configSource });
220537
+ values.push({ key: "server.transport", value: resolved.server.transport || "stdio", source: configSource });
220538
+ values.push({ key: "server.dashboard", value: String(resolved.server.dashboard ?? true), source: configSource });
219366
220539
  sendJson(res, { files, values });
219367
220540
  break;
219368
220541
  }
@@ -219810,10 +220983,14 @@ var init_server3 = __esm({
219810
220983
  init_dotenv_loader();
219811
220984
  init_yaml_loader();
219812
220985
  init_yaml_loader();
220986
+ init_resolved_config();
219813
220987
  init_graph_scope();
219814
220988
  init_retention();
219815
220989
  init_visibility();
220990
+ init_evidence_store();
220991
+ init_feedback();
219816
220992
  init_maintenance();
220993
+ init_config_provenance();
219817
220994
  MIME_TYPES = {
219818
220995
  ".html": "text/html; charset=utf-8",
219819
220996
  ".css": "text/css; charset=utf-8",
@@ -259680,8 +260857,7 @@ async function callVisionLLM(systemPrompt, imageBase64, mimeType) {
259680
260857
  if (!apiKey) {
259681
260858
  throw new Error("No LLM API key configured for image analysis.");
259682
260859
  }
259683
- let baseUrl = getLLMBaseUrl("https://api.openai.com/v1").replace(/\/+$/, "");
259684
- if (!baseUrl.endsWith("/v1")) baseUrl += "/v1";
260860
+ const baseUrl = normalizeOpenAICompatibleBaseUrl(getLLMBaseUrl("https://api.openai.com/v1"));
259685
260861
  const model = getLLMModel("gpt-4o");
259686
260862
  const response = await fetch(`${baseUrl}/chat/completions`, {
259687
260863
  method: "POST",
@@ -259846,7 +261022,7 @@ __export(server_exports2, {
259846
261022
  createMemorixServer: () => createMemorixServer,
259847
261023
  shouldAwaitProjectRuntime: () => shouldAwaitProjectRuntime
259848
261024
  });
259849
- import { createHash as createHash16 } from "crypto";
261025
+ import { createHash as createHash18 } from "crypto";
259850
261026
  function formatFormationStageDurations(stageDurationsMs) {
259851
261027
  const orderedStages = ["extract", "resolve", "evaluate"];
259852
261028
  const parts = orderedStages.filter((stage) => stageDurationsMs[stage] !== void 0).map((stage) => `${stage}=${stageDurationsMs[stage]}ms`);
@@ -259928,7 +261104,7 @@ function coerceObjectArray(val) {
259928
261104
  return [];
259929
261105
  }
259930
261106
  function createDeterministicInstanceId(projectId, agentType, agentName) {
259931
- const digest2 = createHash16("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
261107
+ const digest2 = createHash18("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
259932
261108
  return `auto-${digest2}`;
259933
261109
  }
259934
261110
  function shouldAwaitProjectRuntime(toolName) {
@@ -260220,7 +261396,7 @@ The path should point to a directory containing a .git folder.`
260220
261396
  };
260221
261397
  const server = existingServer ?? new McpServer({
260222
261398
  name: "memorix",
260223
- version: true ? "1.7.2" : "1.0.1"
261399
+ version: true ? "1.8.0" : "1.0.1"
260224
261400
  });
260225
261401
  const originalRegisterTool = server.registerTool.bind(server);
260226
261402
  server.registerTool = ((name, ...args) => {
@@ -263399,7 +264575,7 @@ ${json4}
263399
264575
  dashboardRunning = true;
263400
264576
  const { exec: execCmd } = await import("child_process");
263401
264577
  const openCmd = process.platform === "win32" ? `start "" "${url2}"` : process.platform === "darwin" ? `open "${url2}"` : `xdg-open "${url2}"`;
263402
- execCmd(openCmd, () => {
264578
+ execCmd(openCmd, { windowsHide: true }, () => {
263403
264579
  });
263404
264580
  return {
263405
264581
  content: [{
@@ -263423,6 +264599,113 @@ ${json4}
263423
264599
  }
263424
264600
  }
263425
264601
  );
264602
+ server.registerTool(
264603
+ "memorix_evidence",
264604
+ {
264605
+ title: "Evidence Cards",
264606
+ description: "Inspect the bounded, project-scoped provenance cards behind Memorix context. Use list for a compact index, get/events for one source, sync to repair the projection, and stale when known source files changed. Cards expose sourceRef, locator, capturedHash, and freshness.",
264607
+ inputSchema: {
264608
+ action: external_exports2.enum(["list", "get", "sync", "stale", "events"]).describe("Evidence operation"),
264609
+ candidateKind: external_exports2.enum(["observation", "code", "git", "test", "document", "workflow", "graph"]).optional().default("observation"),
264610
+ candidateId: external_exports2.string().optional().describe("Candidate id, usually an observation id"),
264611
+ query: external_exports2.string().optional().describe("Optional title/source query for list"),
264612
+ paths: external_exports2.array(external_exports2.string()).max(200).optional().describe("Changed source paths for stale"),
264613
+ reason: external_exports2.string().max(500).optional().describe("Why evidence became stale"),
264614
+ limit: external_exports2.number().int().positive().max(100).optional().default(20)
264615
+ }
264616
+ },
264617
+ async ({ action, candidateKind, candidateId, query, paths, reason, limit: limit2 }) => {
264618
+ const unresolved = requireResolvedProject("inspect evidence cards for the current project");
264619
+ if (unresolved) return unresolved;
264620
+ const cards = new EvidenceCardStore();
264621
+ await cards.init(projectDir2);
264622
+ if (action === "sync") {
264623
+ const observations2 = await getObservationStore().loadByProject(project.id);
264624
+ const synced = cards.syncObservations(observations2);
264625
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, synced: synced.length, cards: synced.slice(0, limit2 ?? 20) }, null, 2) }] };
264626
+ }
264627
+ if (action === "stale") {
264628
+ const changed = cards.markStaleForPaths(project.id, paths ?? [], reason ?? "source file changed");
264629
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, staleMarked: changed, paths: paths ?? [] }, null, 2) }] };
264630
+ }
264631
+ if (action === "get") {
264632
+ if (!candidateId?.trim()) return { content: [{ type: "text", text: "candidateId is required for evidence get." }], isError: true };
264633
+ const card = cards.get(project.id, candidateKind ?? "observation", candidateId.trim());
264634
+ if (!card) return { content: [{ type: "text", text: "Evidence card not found." }], isError: true };
264635
+ return { content: [{ type: "text", text: JSON.stringify({ card, events: cards.listEvents(card.id), projectId: project.id }, null, 2) }] };
264636
+ }
264637
+ if (action === "events") {
264638
+ if (!candidateId?.trim()) return { content: [{ type: "text", text: "candidateId is required for evidence events." }], isError: true };
264639
+ const card = cards.get(project.id, candidateKind ?? "observation", candidateId.trim());
264640
+ if (!card) return { content: [{ type: "text", text: "Evidence card not found." }], isError: true };
264641
+ return { content: [{ type: "text", text: JSON.stringify(cards.listEvents(card.id, limit2), null, 2) }] };
264642
+ }
264643
+ const needle = query?.trim().toLowerCase();
264644
+ const listed = cards.list(project.id, { limit: Math.max(limit2 ?? 20, 100) }).filter((card) => !needle || [card.title, card.summary, card.sourceRef, card.locator ?? ""].join(" ").toLowerCase().includes(needle)).slice(0, limit2 ?? 20);
264645
+ return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, cards: listed }, null, 2) }] };
264646
+ }
264647
+ );
264648
+ server.registerTool(
264649
+ "memorix_feedback",
264650
+ {
264651
+ title: "Memory Feedback",
264652
+ description: "Record or inspect auditable memory outcomes. Positive verification/use strengthens recall; correction, conflict, source change, or weaken lowers it. Use revoke with targetEventId to undo one prior signal without deleting audit history.",
264653
+ inputSchema: {
264654
+ action: external_exports2.enum(["record", "show", "audit"]).describe("Feedback operation"),
264655
+ candidateKind: external_exports2.enum(["observation", "claim", "durable-memory", "workflow"]).optional().default("observation"),
264656
+ candidateId: external_exports2.string().describe("Memory or knowledge candidate id"),
264657
+ signal: external_exports2.enum(["user-correction", "verification-success", "verification-failure", "code-change", "used", "not-used", "code-conflict", "strengthen", "weaken", "revoke"]).optional(),
264658
+ sourceRef: external_exports2.string().max(500).optional().describe("Source of this feedback, such as test:run-42 or user:chat"),
264659
+ actor: external_exports2.string().max(200).optional(),
264660
+ note: external_exports2.string().max(2e3).optional(),
264661
+ targetEventId: external_exports2.string().optional().describe("Existing event id when action=record and signal=revoke"),
264662
+ limit: external_exports2.number().int().positive().max(200).optional().default(100)
264663
+ }
264664
+ },
264665
+ async ({ action, candidateKind, candidateId, signal, sourceRef, actor, note, targetEventId, limit: limit2 }) => {
264666
+ const unresolved = requireResolvedProject("record memory feedback for the current project");
264667
+ if (unresolved) return unresolved;
264668
+ const feedback = new MemoryFeedbackStore();
264669
+ await feedback.init(projectDir2);
264670
+ const kind = candidateKind ?? "observation";
264671
+ if (action === "show") {
264672
+ return { content: [{ type: "text", text: JSON.stringify(feedback.getState(project.id, kind, candidateId), null, 2) }] };
264673
+ }
264674
+ if (action === "audit") {
264675
+ return { content: [{ type: "text", text: JSON.stringify(feedback.audit(project.id, kind, candidateId, limit2), null, 2) }] };
264676
+ }
264677
+ if (!signal || !sourceRef?.trim()) {
264678
+ return { content: [{ type: "text", text: "record requires signal and sourceRef." }], isError: true };
264679
+ }
264680
+ try {
264681
+ const result = feedback.record({
264682
+ projectId: project.id,
264683
+ candidateKind: kind,
264684
+ candidateId,
264685
+ signal,
264686
+ sourceRef,
264687
+ ...actor || currentAgentId ? { actor: actor ?? currentAgentId } : {},
264688
+ ...note ? { note } : {},
264689
+ ...targetEventId ? { targetEventId } : {}
264690
+ });
264691
+ if (kind === "observation") {
264692
+ const cards = new EvidenceCardStore();
264693
+ await cards.init(projectDir2);
264694
+ const card = cards.get(project.id, "observation", candidateId);
264695
+ if (card) {
264696
+ const verification = signal === "verification-success" || signal === "strengthen" ? "verified" : signal === "user-correction" || signal === "code-conflict" || signal === "weaken" ? "conflicted" : card.verification;
264697
+ cards.recordEvent(project.id, card.id, `feedback:${signal}`, note);
264698
+ if (verification !== card.verification) {
264699
+ cards.setVerification(project.id, card.id, verification);
264700
+ }
264701
+ }
264702
+ }
264703
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
264704
+ } catch (error40) {
264705
+ return { content: [{ type: "text", text: error40 instanceof Error ? error40.message : String(error40) }], isError: true };
264706
+ }
264707
+ }
264708
+ );
263426
264709
  if (teamFeaturesEnabled) {
263427
264710
  const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
263428
264711
  initTeamStoreForProject = initTeamStore2;
@@ -264478,6 +265761,8 @@ var init_server4 = __esm({
264478
265761
  init_launch_root();
264479
265762
  init_aliases();
264480
265763
  init_persistence();
265764
+ init_evidence_store();
265765
+ init_feedback();
264481
265766
  init_syncer();
264482
265767
  init_engine5();
264483
265768
  init_tool_profile();
@@ -264964,6 +266249,35 @@ var init_serve = __esm({
264964
266249
  }
264965
266250
  });
264966
266251
 
266252
+ // src/server/mcp-diagnostics.ts
266253
+ function getMcpDiagnostics() {
266254
+ return {
266255
+ serverName: MCP_SERVER_NAME,
266256
+ protocol: {
266257
+ current: CURRENT_MCP_PROTOCOL_VERSION,
266258
+ legacy: LEGACY_MCP_PROTOCOL_VERSION,
266259
+ statelessRequests: "supported",
266260
+ statefulStreamableHttp: "supported"
266261
+ },
266262
+ capabilities: {
266263
+ tasks: "unsupported",
266264
+ polling: "unsupported",
266265
+ listCache: "supported",
266266
+ explicitProjectHandle: "supported"
266267
+ }
266268
+ };
266269
+ }
266270
+ var CURRENT_MCP_PROTOCOL_VERSION, LEGACY_MCP_PROTOCOL_VERSION, MCP_SERVER_NAME;
266271
+ var init_mcp_diagnostics = __esm({
266272
+ "src/server/mcp-diagnostics.ts"() {
266273
+ "use strict";
266274
+ init_esm_shims();
266275
+ CURRENT_MCP_PROTOCOL_VERSION = "2026-07-28";
266276
+ LEGACY_MCP_PROTOCOL_VERSION = "2024-11-05";
266277
+ MCP_SERVER_NAME = "io.github.AVIDS2/memorix";
266278
+ }
266279
+ });
266280
+
264967
266281
  // src/cli/port.ts
264968
266282
  function parseTcpPort(value, defaultPort) {
264969
266283
  const raw = value?.trim() || String(defaultPort);
@@ -267060,6 +268374,79 @@ var init_streamableHttp = __esm({
267060
268374
  }
267061
268375
  });
267062
268376
 
268377
+ // src/server/mcp-binding-store.ts
268378
+ var mcp_binding_store_exports = {};
268379
+ __export(mcp_binding_store_exports, {
268380
+ McpBindingStore: () => McpBindingStore
268381
+ });
268382
+ import { randomUUID as randomUUID19 } from "crypto";
268383
+ function rowToBinding(row) {
268384
+ return {
268385
+ handleId: String(row.handle_id),
268386
+ projectId: String(row.project_id),
268387
+ projectRoot: String(row.project_root),
268388
+ dataDir: String(row.data_dir),
268389
+ createdAt: String(row.created_at),
268390
+ lastUsedAt: String(row.last_used_at),
268391
+ ...row.expires_at ? { expiresAt: String(row.expires_at) } : {}
268392
+ };
268393
+ }
268394
+ var McpBindingStore;
268395
+ var init_mcp_binding_store = __esm({
268396
+ "src/server/mcp-binding-store.ts"() {
268397
+ "use strict";
268398
+ init_esm_shims();
268399
+ init_sqlite_db();
268400
+ McpBindingStore = class {
268401
+ db = null;
268402
+ async init(dataDir) {
268403
+ this.db = getDatabase(dataDir);
268404
+ }
268405
+ requireDb() {
268406
+ if (!this.db) throw new Error("McpBindingStore is not initialized.");
268407
+ return this.db;
268408
+ }
268409
+ create(input) {
268410
+ const now3 = /* @__PURE__ */ new Date();
268411
+ const binding = {
268412
+ handleId: `mxh_${randomUUID19().replace(/-/g, "")}`,
268413
+ projectId: input.projectId,
268414
+ projectRoot: input.projectRoot,
268415
+ dataDir: input.dataDir,
268416
+ createdAt: now3.toISOString(),
268417
+ lastUsedAt: now3.toISOString(),
268418
+ ...input.ttlMs && input.ttlMs > 0 ? { expiresAt: new Date(now3.getTime() + input.ttlMs).toISOString() } : {}
268419
+ };
268420
+ this.requireDb().prepare(`
268421
+ INSERT INTO mcp_bindings (handle_id, project_id, project_root, data_dir, created_at, last_used_at, expires_at)
268422
+ VALUES (?, ?, ?, ?, ?, ?, ?)
268423
+ `).run(binding.handleId, binding.projectId, binding.projectRoot, binding.dataDir, binding.createdAt, binding.lastUsedAt, binding.expiresAt ?? null);
268424
+ return binding;
268425
+ }
268426
+ get(handleId) {
268427
+ const row = this.requireDb().prepare(`SELECT * FROM mcp_bindings WHERE handle_id = ?`).get(handleId);
268428
+ if (!row) return void 0;
268429
+ const binding = rowToBinding(row);
268430
+ if (binding.expiresAt && Date.parse(binding.expiresAt) <= Date.now()) {
268431
+ this.delete(handleId);
268432
+ return void 0;
268433
+ }
268434
+ return binding;
268435
+ }
268436
+ touch(handleId) {
268437
+ const binding = this.get(handleId);
268438
+ if (!binding) return void 0;
268439
+ const lastUsedAt = (/* @__PURE__ */ new Date()).toISOString();
268440
+ this.requireDb().prepare(`UPDATE mcp_bindings SET last_used_at = ? WHERE handle_id = ?`).run(lastUsedAt, handleId);
268441
+ return { ...binding, lastUsedAt };
268442
+ }
268443
+ delete(handleId) {
268444
+ this.requireDb().prepare(`DELETE FROM mcp_bindings WHERE handle_id = ?`).run(handleId);
268445
+ }
268446
+ };
268447
+ }
268448
+ });
268449
+
267063
268450
  // src/runtime/control-plane-maintenance.ts
267064
268451
  var control_plane_maintenance_exports = {};
267065
268452
  __export(control_plane_maintenance_exports, {
@@ -267127,6 +268514,7 @@ var serve_http_exports = {};
267127
268514
  __export(serve_http_exports, {
267128
268515
  DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
267129
268516
  EXPIRED_SESSION_TTL_MS: () => EXPIRED_SESSION_TTL_MS,
268517
+ MCP_SDK_COMPAT_PROTOCOL_VERSION: () => MCP_SDK_COMPAT_PROTOCOL_VERSION,
267130
268518
  _testing: () => _testing2,
267131
268519
  default: () => serve_http_default,
267132
268520
  parseSessionTimeoutMs: () => parseSessionTimeoutMs,
@@ -267148,7 +268536,7 @@ function resolveServeHttpDefaults(argsPort, configured) {
267148
268536
  const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
267149
268537
  return { port, transportNotice };
267150
268538
  }
267151
- var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, serve_http_default, _testing2;
268539
+ var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, MCP_SDK_COMPAT_PROTOCOL_VERSION, serve_http_default, _testing2;
267152
268540
  var init_serve_http = __esm({
267153
268541
  "src/cli/commands/serve-http.ts"() {
267154
268542
  "use strict";
@@ -267158,10 +268546,12 @@ var init_serve_http = __esm({
267158
268546
  init_graph_scope();
267159
268547
  init_retention();
267160
268548
  init_visibility();
268549
+ init_mcp_diagnostics();
267161
268550
  init_port();
267162
268551
  init_mcp_root_path();
267163
268552
  DEFAULT_SESSION_TIMEOUT_MS = 12 * 60 * 60 * 1e3;
267164
268553
  EXPIRED_SESSION_TTL_MS = 10 * 60 * 1e3;
268554
+ MCP_SDK_COMPAT_PROTOCOL_VERSION = "2025-11-25";
267165
268555
  serve_http_default = defineCommand({
267166
268556
  meta: {
267167
268557
  name: "serve-http",
@@ -267191,7 +268581,7 @@ var init_serve_http = __esm({
267191
268581
  },
267192
268582
  run: async ({ args }) => {
267193
268583
  const { createServer: createServer3 } = await import("http");
267194
- const { randomUUID: randomUUID17 } = await import("crypto");
268584
+ const { randomUUID: randomUUID20 } = await import("crypto");
267195
268585
  const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp(), streamableHttp_exports));
267196
268586
  const { isInitializeRequest: isInitializeRequest2 } = await Promise.resolve().then(() => (init_types8(), types_exports3));
267197
268587
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
@@ -267199,6 +268589,8 @@ var init_serve_http = __esm({
267199
268589
  const { findGitInSubdirs: findGitInSubdirs2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
267200
268590
  const { writeFileSync: writeFileSync12, mkdirSync: mkdirSync12 } = await import("fs");
267201
268591
  const { homedir: homedir36 } = await import("os");
268592
+ const { McpBindingStore: McpBindingStore2 } = await Promise.resolve().then(() => (init_mcp_binding_store(), mcp_binding_store_exports));
268593
+ const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
267202
268594
  let configuredDefaults = {};
267203
268595
  try {
267204
268596
  const { getServerConfig: getServerConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -267251,9 +268643,8 @@ var init_serve_http = __esm({
267251
268643
  teamStoreCache.set(dataDir, store2);
267252
268644
  return store2;
267253
268645
  }
267254
- const detectedProj = serveResolution.detectedProject ?? detectorMod.detectProject(projectRoot);
267255
- const teamDataDir = await persistMod.getProjectDataDir(detectedProj?.id ?? "__unresolved__");
267256
- const sharedTeamStore = await getTeamStore2(teamDataDir);
268646
+ const statelessBindingStore = new McpBindingStore2();
268647
+ await statelessBindingStore.init(persistMod.getBaseDataDir());
267257
268648
  const sessions = /* @__PURE__ */ new Map();
267258
268649
  const sessionLastActivity = /* @__PURE__ */ new Map();
267259
268650
  const expiredSessions = /* @__PURE__ */ new Map();
@@ -267393,12 +268784,91 @@ var init_serve_http = __esm({
267393
268784
  res.setHeader("Vary", "Origin");
267394
268785
  }
267395
268786
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
267396
- res.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, Mcp-Session-Id, Last-Event-Id");
267397
- res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id");
268787
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, Mcp-Session-Id, Mcp-Project-Handle, Mcp-Project-Root, Mcp-Stateless, Last-Event-Id");
268788
+ res.setHeader("Access-Control-Expose-Headers", "Mcp-Session-Id, Mcp-Project-Handle, Mcp-Protocol-Version, Mcp-Method, Mcp-Name");
268789
+ res.setHeader("Mcp-Protocol-Version", req.headers["mcp-protocol-version"] || CURRENT_MCP_PROTOCOL_VERSION);
268790
+ res.setHeader("Mcp-Method", req.method || "UNKNOWN");
268791
+ res.setHeader("Mcp-Name", MCP_SERVER_NAME);
268792
+ }
268793
+ async function handleStatelessPost(req, res, body) {
268794
+ const handleId = typeof req.headers["mcp-project-handle"] === "string" ? req.headers["mcp-project-handle"] : void 0;
268795
+ const persisted = handleId ? statelessBindingStore.touch(handleId) : void 0;
268796
+ if (handleId && !persisted) {
268797
+ res.writeHead(404, { "Content-Type": "application/json" });
268798
+ res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32001, message: "MCP project handle not found or expired; initialize again." }, id: null }));
268799
+ return;
268800
+ }
268801
+ const requestedRoot = typeof req.headers["mcp-project-root"] === "string" ? req.headers["mcp-project-root"] : void 0;
268802
+ const root = persisted?.projectRoot ?? requestedRoot ?? projectRoot;
268803
+ if (!persisted && !isInitializeRequest2(body)) {
268804
+ res.writeHead(400, { "Content-Type": "application/json" });
268805
+ res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32e3, message: "Stateless MCP requests require Mcp-Project-Handle; initialize first." }, id: null }));
268806
+ return;
268807
+ }
268808
+ const { createMemorixServer: createMemorixServer3 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
268809
+ const { createProjectBindingController: createProjectBindingController3 } = await Promise.resolve().then(() => (init_request_context(), request_context_exports));
268810
+ const binding = createProjectBindingController3(root);
268811
+ const { server, projectId, handleTransportClose } = await createMemorixServer3(
268812
+ root,
268813
+ void 0,
268814
+ void 0,
268815
+ {
268816
+ allowUntrackedFallback: false,
268817
+ deferProjectInitUntilBound: true,
268818
+ deferProjectRuntimeInit: true,
268819
+ dashboardMode: "control-plane",
268820
+ dashboardPort: port,
268821
+ toolProfile,
268822
+ projectBinding: binding
268823
+ }
268824
+ );
268825
+ const transport = new StreamableHTTPServerTransport2({
268826
+ // No Mcp-Session-Id: the protocol state is carried by our explicit
268827
+ // durable project handle and reconstructed on every request.
268828
+ sessionIdGenerator: void 0,
268829
+ enableJsonResponse: true
268830
+ });
268831
+ try {
268832
+ await server.connect(transport);
268833
+ let effectiveHandle = persisted;
268834
+ if (!effectiveHandle && projectId !== "__unresolved__") {
268835
+ effectiveHandle = statelessBindingStore.create({
268836
+ projectId,
268837
+ projectRoot: root,
268838
+ dataDir: await persistMod.getProjectDataDir(projectId)
268839
+ });
268840
+ }
268841
+ if (effectiveHandle) res.setHeader("Mcp-Project-Handle", effectiveHandle.handleId);
268842
+ const sdkBody = body && typeof body === "object" ? JSON.parse(JSON.stringify(body)) : body;
268843
+ const sdkRequest = sdkBody;
268844
+ if (sdkRequest && typeof sdkRequest === "object" && sdkRequest.method === "initialize" && sdkRequest.params) {
268845
+ sdkRequest.params.protocolVersion = MCP_SDK_COMPAT_PROTOCOL_VERSION;
268846
+ }
268847
+ if (req.headers["mcp-protocol-version"]) {
268848
+ req.headers["mcp-protocol-version"] = MCP_SDK_COMPAT_PROTOCOL_VERSION;
268849
+ for (let i4 = 0; i4 < req.rawHeaders.length; i4 += 2) {
268850
+ if (req.rawHeaders[i4].toLowerCase() === "mcp-protocol-version") {
268851
+ req.rawHeaders[i4 + 1] = MCP_SDK_COMPAT_PROTOCOL_VERSION;
268852
+ break;
268853
+ }
268854
+ }
268855
+ }
268856
+ patchMcpResponse(res);
268857
+ await transport.handleRequest(req, res, sdkBody);
268858
+ } finally {
268859
+ handleTransportClose();
268860
+ await transport.close().catch(() => void 0);
268861
+ }
267398
268862
  }
267399
268863
  async function handlePost(req, res) {
267400
268864
  const sessionId = req.headers["mcp-session-id"];
267401
268865
  const body = await parseBody(req);
268866
+ const requestedProtocol = req.headers["mcp-protocol-version"];
268867
+ const statelessRequested = req.headers["mcp-stateless"] === "true" || requestedProtocol === CURRENT_MCP_PROTOCOL_VERSION;
268868
+ if (!sessionId && statelessRequested) {
268869
+ await handleStatelessPost(req, res, body);
268870
+ return;
268871
+ }
267402
268872
  if (sessionId && sessions.has(sessionId)) {
267403
268873
  sessionLastActivity.set(sessionId, Date.now());
267404
268874
  patchMcpResponse(res);
@@ -267412,7 +268882,7 @@ var init_serve_http = __esm({
267412
268882
  if (!sessionId && isInitializeRequest2(body)) {
267413
268883
  let createdState = null;
267414
268884
  const transport = new StreamableHTTPServerTransport2({
267415
- sessionIdGenerator: () => randomUUID17(),
268885
+ sessionIdGenerator: () => randomUUID20(),
267416
268886
  enableJsonResponse: true,
267417
268887
  onsessioninitialized: (sid) => {
267418
268888
  if (createdState) sessions.set(sid, createdState);
@@ -267445,7 +268915,7 @@ var init_serve_http = __esm({
267445
268915
  const { server, switchProject, handleTransportClose } = await createMemorixServer2(
267446
268916
  projectRoot,
267447
268917
  void 0,
267448
- { teamStore: sharedTeamStore },
268918
+ void 0,
267449
268919
  {
267450
268920
  allowUntrackedFallback: false,
267451
268921
  deferProjectInitUntilBound: true,
@@ -267587,6 +269057,18 @@ var init_serve_http = __esm({
267587
269057
  { projectId }
267588
269058
  );
267589
269059
  }
269060
+ async function loadProjectFeedback(dataDir, projectId) {
269061
+ const result = /* @__PURE__ */ new Map();
269062
+ try {
269063
+ const feedback = new MemoryFeedbackStore2();
269064
+ await feedback.init(dataDir);
269065
+ for (const state of feedback.listStates(projectId, "observation")) {
269066
+ result.set(state.candidateId, { weight: state.weight, status: state.status });
269067
+ }
269068
+ } catch {
269069
+ }
269070
+ return result;
269071
+ }
267590
269072
  const cliDir = pathModule.default.dirname(fileURLToPath10(import.meta.url));
267591
269073
  const dashStaticDir = pathModule.default.join(cliDir, "..", "dashboard", "static");
267592
269074
  const MIME_TYPES2 = {
@@ -267645,9 +269127,41 @@ var init_serve_http = __esm({
267645
269127
  sendJson2({ ok: true, scope: "per-request" });
267646
269128
  return;
267647
269129
  }
269130
+ const maintenanceMatch = apiPath.match(/^\/maintenance\/(cleanup|deduplicate|consolidate|retention)\/(preview|execute)$/);
269131
+ if (maintenanceMatch) {
269132
+ if (req.method !== "POST") {
269133
+ sendJson2({ error: "Maintenance actions require POST." }, 405);
269134
+ return;
269135
+ }
269136
+ const bodyValue = await parseBody(req);
269137
+ const body = bodyValue && typeof bodyValue === "object" && !Array.isArray(bodyValue) ? bodyValue : {};
269138
+ const { projectId: maintenanceProjectId, dataDir: maintenanceDataDir } = await resolveRequestProject(url2);
269139
+ const maintenanceStore = await getDashboardObservationStore(maintenanceDataDir);
269140
+ const context = {
269141
+ dataDir: maintenanceDataDir,
269142
+ projectId: maintenanceProjectId,
269143
+ projectRoot: maintenanceProjectId === defaultProject.id ? projectRoot : null,
269144
+ store: maintenanceStore
269145
+ };
269146
+ const maintenance = await Promise.resolve().then(() => (init_maintenance(), maintenance_exports));
269147
+ const action = maintenanceMatch[1];
269148
+ const phase = maintenanceMatch[2];
269149
+ let result;
269150
+ if (action === "cleanup") {
269151
+ result = phase === "preview" ? await maintenance.previewCleanup(context, body.includeNoise === true) : await maintenance.executeCleanup(context, body.payload, body.token);
269152
+ } else if (action === "deduplicate") {
269153
+ result = phase === "preview" ? await maintenance.previewDeduplicate(context) : await maintenance.executeDeduplicate(context, body.payload, body.token);
269154
+ } else if (action === "consolidate") {
269155
+ result = phase === "preview" ? await maintenance.previewConsolidate(context) : await maintenance.executeConsolidate(context, body.payload, body.token);
269156
+ } else {
269157
+ result = phase === "preview" ? await maintenance.previewRetentionArchive(context) : await maintenance.executeRetentionArchive(context, body.payload, body.token);
269158
+ }
269159
+ sendJson2(result);
269160
+ return;
269161
+ }
267648
269162
  if (apiPath === "/team") {
267649
- const { projectId: teamProjectId } = await resolveRequestProject(url2);
267650
- const ts2 = sharedTeamStore;
269163
+ const { projectId: teamProjectId, dataDir: teamDataDir } = await resolveRequestProject(url2);
269164
+ const ts2 = await getTeamStore2(teamDataDir);
267651
269165
  const scope = url2.searchParams.get("scope") || "project";
267652
269166
  const effectiveTeamProjectId = scope === "global" ? void 0 : teamProjectId;
267653
269167
  const agents = effectiveTeamProjectId ? ts2.listAgents(effectiveTeamProjectId) : ts2.listAllAgents();
@@ -267748,6 +269262,7 @@ var init_serve_http = __esm({
267748
269262
  await initGraphStore2(statsDataDir);
267749
269263
  const graph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
267750
269264
  const observations2 = await loadDashboardProjectObservations(statsDataDir, statsProjectId, "active");
269265
+ const feedback = await loadProjectFeedback(statsDataDir, statsProjectId);
267751
269266
  const statsStore = await getDashboardObservationStore(statsDataDir);
267752
269267
  const nextId2 = await statsStore.loadIdCounter();
267753
269268
  const typeCounts = {};
@@ -267782,7 +269297,10 @@ var init_serve_http = __esm({
267782
269297
  filesModified: o4.filesModified
267783
269298
  }));
267784
269299
  const retention = summarizeRetentionProjections(
267785
- observations2.filter((observation) => observation.type !== "probe").map((observation) => projectObservationRetention(observation, { referenceTime: new Date(now3) }))
269300
+ observations2.filter((observation) => observation.type !== "probe").map((observation) => projectObservationRetention(observation, {
269301
+ referenceTime: new Date(now3),
269302
+ feedback: feedback.get(String(observation.id))
269303
+ }))
267786
269304
  );
267787
269305
  const retentionSummary = {
267788
269306
  active: retention.active,
@@ -267889,6 +269407,19 @@ var init_serve_http = __esm({
267889
269407
  sendJson2(await loadDashboardProjectObservations(obsDataDir, obsProjectId, "active"));
267890
269408
  return;
267891
269409
  }
269410
+ if (apiPath === "/evidence") {
269411
+ const { projectId: evidenceProjectId, dataDir: evidenceDataDir } = await resolveRequestProject(url2);
269412
+ const { EvidenceCardStore: EvidenceCardStore2 } = await Promise.resolve().then(() => (init_evidence_store(), evidence_store_exports));
269413
+ const evidenceStore = new EvidenceCardStore2();
269414
+ await evidenceStore.init(evidenceDataDir);
269415
+ const observations2 = await loadDashboardProjectObservations(evidenceDataDir, evidenceProjectId, "active");
269416
+ evidenceStore.syncObservations(observations2);
269417
+ const query = (url2.searchParams.get("q") || "").trim().toLowerCase();
269418
+ const limit2 = Math.min(100, Math.max(1, Number(url2.searchParams.get("limit") || 20)));
269419
+ const cards = evidenceStore.list(evidenceProjectId, { limit: 100 }).filter((card) => !query || [card.title, card.summary, card.sourceRef, card.locator ?? ""].join(" ").toLowerCase().includes(query)).slice(0, limit2).map((card) => ({ ...card, events: evidenceStore.listEvents(card.id, 8) }));
269420
+ sendJson2({ projectId: evidenceProjectId, cards });
269421
+ return;
269422
+ }
267892
269423
  if (apiPath === "/graph") {
267893
269424
  const { projectId: graphProjectId, dataDir: graphDataDir } = await resolveRequestProject(url2);
267894
269425
  const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
@@ -267910,10 +269441,11 @@ var init_serve_http = __esm({
267910
269441
  if (apiPath === "/retention") {
267911
269442
  const { projectId: retProjectId, dataDir: retDataDir } = await resolveRequestProject(url2);
267912
269443
  const observations2 = await loadDashboardProjectObservations(retDataDir, retProjectId, "active");
269444
+ const feedback = await loadProjectFeedback(retDataDir, retProjectId);
267913
269445
  const referenceTime = /* @__PURE__ */ new Date();
267914
269446
  const rows = observations2.filter((observation) => observation.type !== "probe").map((observation) => ({
267915
269447
  observation,
267916
- retention: projectObservationRetention(observation, { referenceTime })
269448
+ retention: projectObservationRetention(observation, { referenceTime, feedback: feedback.get(String(observation.id)) })
267917
269449
  })).sort((a4, b4) => b4.retention.displayScore - a4.retention.displayScore);
267918
269450
  const summary = summarizeRetentionProjections(rows.map((row) => row.retention));
267919
269451
  const items = rows.map(({ observation, retention }) => ({
@@ -268290,6 +269822,11 @@ var init_serve_http = __esm({
268290
269822
  }));
268291
269823
  return;
268292
269824
  }
269825
+ if (url2.pathname === "/protocol-diagnostics") {
269826
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "public, max-age=60" });
269827
+ res.end(JSON.stringify(getMcpDiagnostics()));
269828
+ return;
269829
+ }
268293
269830
  if (url2.pathname === "/mcp") {
268294
269831
  const accept = req.headers["accept"] || "";
268295
269832
  const needs = [];
@@ -268586,6 +270123,7 @@ var init_status = __esm({
268586
270123
  diagLines.push(` Agent lane: ${formatLane(agent.provider, agent.model, agent.baseUrl, agent.apiKey)}`);
268587
270124
  diagLines.push(` Memory LLM lane: ${formatLane(resolved.memory.llm.provider, resolved.memory.llm.model, resolved.memory.llm.baseUrl, resolved.memory.llm.apiKey)}`);
268588
270125
  diagLines.push(` Embedding lane: ${formatLane(resolved.embedding.provider, resolved.embedding.model, resolved.embedding.baseUrl, resolved.embedding.apiKey)}`);
270126
+ diagLines.push(` Rerank lane: ${formatLane(resolved.rerank.provider, resolved.rerank.model, resolved.rerank.baseUrl, resolved.rerank.apiKey)}`);
268589
270127
  diagLines.push(` Memory behavior: inject=${resolved.memory.inject ?? "default"}, formation=${resolved.memory.formation ?? "default"}, autoCleanup=${resolved.memory.autoCleanup ?? "default"}`);
268590
270128
  diagLines.push(` Git behavior: autoHook=${resolved.git.autoHook ?? "default"}, ingestOnCommit=${resolved.git.ingestOnCommit ?? "default"}, maxDiffSize=${resolved.git.maxDiffSize ?? "default"}, skipMergeCommits=${resolved.git.skipMergeCommits ?? "default"}`);
268591
270129
  diagLines.push(` Server: transport=${resolved.server.transport ?? "default"}, dashboard=${resolved.server.dashboard ?? "default"}, dashboardPort=${resolved.server.dashboardPort ?? "default"}`);
@@ -268619,7 +270157,8 @@ var init_status = __esm({
268619
270157
  lanes: {
268620
270158
  agent: summarizeLane(agent.provider, agent.model, agent.baseUrl, agent.apiKey),
268621
270159
  memoryLlm: summarizeLane(resolved.memory.llm.provider, resolved.memory.llm.model, resolved.memory.llm.baseUrl, resolved.memory.llm.apiKey),
268622
- embedding: summarizeLane(resolved.embedding.provider, resolved.embedding.model, resolved.embedding.baseUrl, resolved.embedding.apiKey)
270160
+ embedding: summarizeLane(resolved.embedding.provider, resolved.embedding.model, resolved.embedding.baseUrl, resolved.embedding.apiKey),
270161
+ rerank: summarizeLane(resolved.rerank.provider, resolved.rerank.model, resolved.rerank.baseUrl, resolved.rerank.apiKey)
268623
270162
  },
268624
270163
  memory: {
268625
270164
  inject: resolved.memory.inject ?? "default",
@@ -270162,10 +271701,10 @@ __export(handler_exports, {
270162
271701
  resetCooldowns: () => resetCooldowns,
270163
271702
  runHook: () => runHook
270164
271703
  });
270165
- import { createHash as createHash17 } from "crypto";
271704
+ import { createHash as createHash19 } from "crypto";
270166
271705
  function deriveHookActorId(input) {
270167
271706
  const material = `${input.agent ?? "unknown"}\0${input.sessionId ?? "unknown"}`;
270168
- return `hook:${createHash17("sha256").update(material).digest("hex").slice(0, 24)}`;
271707
+ return `hook:${createHash19("sha256").update(material).digest("hex").slice(0, 24)}`;
270169
271708
  }
270170
271709
  function classifyTool(input) {
270171
271710
  if (input.event === "post_edit") return "file_modify";
@@ -272110,10 +273649,10 @@ var init_ingest = __esm({
272110
273649
  });
272111
273650
 
272112
273651
  // src/media/embedding.ts
272113
- import { createHash as createHash18 } from "crypto";
273652
+ import { createHash as createHash20 } from "crypto";
272114
273653
  function profileKey(provider2, asset) {
272115
273654
  const identity = `${provider2.name}\0${provider2.dimensions}\0${asset.kind}`;
272116
- return `media:${createHash18("sha256").update(identity).digest("hex").slice(0, 20)}`;
273655
+ return `media:${createHash20("sha256").update(identity).digest("hex").slice(0, 20)}`;
272117
273656
  }
272118
273657
  function supportsAsset(provider2, asset) {
272119
273658
  return !!provider2.embedInput && (provider2.supportedModalities?.includes(asset.kind) ?? false);
@@ -274942,7 +276481,9 @@ var init_doctor = __esm({
274942
276481
  if (isLLMEnabled2()) {
274943
276482
  const config2 = getLLMConfig2();
274944
276483
  lines.push(ok(`Provider: ${config2?.provider}/${config2?.model}`));
274945
- lines.push(info2("Capabilities: fact extraction, auto-dedup, LLM rerank"));
276484
+ const { isNeuralRerankEnabled: isNeuralRerankEnabled2 } = await Promise.resolve().then(() => (init_rerank(), rerank_exports));
276485
+ const rerankCap = isNeuralRerankEnabled2() ? "HTTP rerank" : "LLM rerank";
276486
+ lines.push(info2(`Capabilities: fact extraction, auto-dedup, ${rerankCap}`));
274946
276487
  report.llm = { enabled: true, provider: config2?.provider, model: config2?.model };
274947
276488
  } else {
274948
276489
  lines.push(info2("LLM mode: off (heuristic dedup only)"));
@@ -277724,7 +279265,7 @@ function safeWrite(path48, content) {
277724
279265
  } catch {
277725
279266
  }
277726
279267
  }
277727
- var init_evidence = __esm({
279268
+ var init_evidence2 = __esm({
277728
279269
  "src/orchestrate/evidence.ts"() {
277729
279270
  "use strict";
277730
279271
  init_esm_shims();
@@ -277850,7 +279391,7 @@ var init_permission = __esm({
277850
279391
  });
277851
279392
 
277852
279393
  // src/orchestrate/memorix-bridge.ts
277853
- import { createHash as createHash19 } from "crypto";
279394
+ import { createHash as createHash21 } from "crypto";
277854
279395
  async function getStoreObservation() {
277855
279396
  if (!_storeObservation) {
277856
279397
  const mod = await Promise.resolve().then(() => (init_observations(), observations_exports));
@@ -277866,7 +279407,7 @@ async function getSearchObservations() {
277866
279407
  return _searchObservations;
277867
279408
  }
277868
279409
  function automaticActorId(...parts) {
277869
- return `orchestrator:${createHash19("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
279410
+ return `orchestrator:${createHash21("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
277870
279411
  }
277871
279412
  function isEligibleAutomaticLesson(entry) {
277872
279413
  return entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral";
@@ -278013,7 +279554,7 @@ function sanitizeErrorPattern(text2) {
278013
279554
  }
278014
279555
  function hashErrorPattern(errorOutput) {
278015
279556
  const sanitized = sanitizeErrorPattern(errorOutput).slice(0, 500);
278016
- return createHash19("sha256").update(sanitized).digest("hex").slice(0, 12);
279557
+ return createHash21("sha256").update(sanitized).digest("hex").slice(0, 12);
278017
279558
  }
278018
279559
  function sleep6(ms2) {
278019
279560
  return new Promise((resolve6) => setTimeout(() => resolve6([]), ms2));
@@ -279019,7 +280560,7 @@ var init_coordinator = __esm({
279019
280560
  init_verify_gate();
279020
280561
  init_error_recovery();
279021
280562
  init_cost_tracker();
279022
- init_evidence();
280563
+ init_evidence2();
279023
280564
  init_permission();
279024
280565
  init_memorix_bridge();
279025
280566
  }
@@ -284270,6 +285811,8 @@ var main2 = defineCommand({
284270
285811
  codegraph: () => Promise.resolve().then(() => (init_codegraph(), codegraph_exports)).then((m6) => m6.default),
284271
285812
  checkpoint: () => Promise.resolve().then(() => (init_checkpoint2(), checkpoint_exports)).then((m6) => m6.default),
284272
285813
  knowledge: () => Promise.resolve().then(() => (init_knowledge(), knowledge_exports)).then((m6) => m6.default),
285814
+ evidence: () => Promise.resolve().then(() => (init_evidence(), evidence_exports)).then((m6) => m6.default),
285815
+ feedback: () => Promise.resolve().then(() => (init_feedback2(), feedback_exports2)).then((m6) => m6.default),
284273
285816
  reasoning: () => Promise.resolve().then(() => (init_reasoning(), reasoning_exports)).then((m6) => m6.default),
284274
285817
  retention: () => Promise.resolve().then(() => (init_retention2(), retention_exports2)).then((m6) => m6.default),
284275
285818
  formation: () => Promise.resolve().then(() => (init_formation2(), formation_exports2)).then((m6) => m6.default),
@@ -284368,6 +285911,8 @@ var main2 = defineCommand({
284368
285911
  "lock",
284369
285912
  "handoff",
284370
285913
  "poll",
285914
+ "evidence",
285915
+ "feedback",
284371
285916
  "receipt",
284372
285917
  "serve",
284373
285918
  "serve-http",
@@ -284425,6 +285970,8 @@ var main2 = defineCommand({
284425
285970
  console.error(" codegraph Refresh/status/context-pack for CodeGraph Memory");
284426
285971
  console.error(" checkpoint Inspect native compact continuity checkpoints");
284427
285972
  console.error(" knowledge Review source-backed knowledge pages and project workflows");
285973
+ console.error(" evidence Inspect source-backed evidence cards");
285974
+ console.error(" feedback Record or audit memory feedback");
284428
285975
  console.error(" reasoning Store/search decision rationale");
284429
285976
  console.error(" retention Inspect stale/archive status");
284430
285977
  console.error(" formation Inspect Memory Formation metrics");