memorix 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1607,6 +1607,7 @@ var init_mini_skills = __esm({
1607
1607
  // src/config/yaml-loader.ts
1608
1608
  var yaml_loader_exports = {};
1609
1609
  __export(yaml_loader_exports, {
1610
+ clearProjectRoot: () => clearProjectRoot,
1610
1611
  initProjectRoot: () => initProjectRoot,
1611
1612
  loadYamlConfig: () => loadYamlConfig,
1612
1613
  resetYamlConfigCache: () => resetYamlConfigCache
@@ -1618,6 +1619,9 @@ function initProjectRoot(root) {
1618
1619
  globalProjectRoot = root;
1619
1620
  configCache.delete(root);
1620
1621
  }
1622
+ function clearProjectRoot() {
1623
+ globalProjectRoot = null;
1624
+ }
1621
1625
  function loadYamlConfig(projectRoot) {
1622
1626
  const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
1623
1627
  const cached2 = configCache.get(resolvedRoot ?? null);
@@ -1645,6 +1649,7 @@ function loadYamlConfig(projectRoot) {
1645
1649
  ...projectConfig,
1646
1650
  // Deep merge for nested objects where both exist
1647
1651
  llm: { ...userConfig.llm, ...projectConfig.llm },
1652
+ agent: { ...userConfig.agent, ...projectConfig.agent },
1648
1653
  embedding: { ...userConfig.embedding, ...projectConfig.embedding },
1649
1654
  git: { ...userConfig.git, ...projectConfig.git },
1650
1655
  behavior: { ...userConfig.behavior, ...projectConfig.behavior },
@@ -1747,6 +1752,10 @@ var init_dotenv_loader = __esm({
1747
1752
  // src/config.ts
1748
1753
  var config_exports = {};
1749
1754
  __export(config_exports, {
1755
+ getAgentLLMApiKey: () => getAgentLLMApiKey,
1756
+ getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
1757
+ getAgentLLMModel: () => getAgentLLMModel,
1758
+ getAgentLLMProvider: () => getAgentLLMProvider,
1750
1759
  getEmbeddingApiKey: () => getEmbeddingApiKey,
1751
1760
  getEmbeddingBaseUrl: () => getEmbeddingBaseUrl,
1752
1761
  getEmbeddingDimensions: () => getEmbeddingDimensions,
@@ -1806,6 +1815,23 @@ function getLLMModel(providerDefault) {
1806
1815
  function getLLMBaseUrl(providerDefault) {
1807
1816
  return process.env.MEMORIX_LLM_BASE_URL || loadYamlConfig().llm?.baseUrl || loadFileConfig().llm?.baseUrl || providerDefault;
1808
1817
  }
1818
+ function getAgentLLMApiKey() {
1819
+ return process.env.MEMORIX_AGENT_LLM_API_KEY || loadYamlConfig().agent?.apiKey || loadFileConfig().agent?.apiKey || getLLMApiKey();
1820
+ }
1821
+ function getAgentLLMProvider() {
1822
+ if (process.env.MEMORIX_AGENT_LLM_PROVIDER) return process.env.MEMORIX_AGENT_LLM_PROVIDER;
1823
+ const yml = loadYamlConfig();
1824
+ if (yml.agent?.provider) return yml.agent.provider;
1825
+ const cfg = loadFileConfig();
1826
+ if (cfg.agent?.provider) return cfg.agent.provider;
1827
+ return getLLMProvider();
1828
+ }
1829
+ function getAgentLLMModel(providerDefault) {
1830
+ return process.env.MEMORIX_AGENT_LLM_MODEL || loadYamlConfig().agent?.model || loadFileConfig().agent?.model || getLLMModel(providerDefault);
1831
+ }
1832
+ function getAgentLLMBaseUrl(providerDefault) {
1833
+ return process.env.MEMORIX_AGENT_LLM_BASE_URL || loadYamlConfig().agent?.baseUrl || loadFileConfig().agent?.baseUrl || getLLMBaseUrl(providerDefault);
1834
+ }
1809
1835
  function getEmbeddingMode() {
1810
1836
  const env = process.env.MEMORIX_EMBEDDING?.toLowerCase()?.trim();
1811
1837
  if (env === "fastembed" || env === "transformers" || env === "api" || env === "auto") return env;
@@ -3095,20 +3121,20 @@ async function* callLLMWithToolsStream(messages, tools) {
3095
3121
  }
3096
3122
  yield* callOpenAIWithToolsStream(messages, tools);
3097
3123
  }
3098
- function initLLM() {
3099
- const { getLLMApiKey: getLLMApiKey2, getLLMProvider: getLLMProvider2, getLLMModel: getLLMModel2, getLLMBaseUrl: getLLMBaseUrl2 } = (init_config(), __toCommonJS(config_exports));
3100
- const apiKey = getLLMApiKey2();
3124
+ function initLLM(options = {}) {
3125
+ const scope = options.scope ?? "memory";
3126
+ const apiKey = scope === "agent" ? getAgentLLMApiKey() : getLLMApiKey();
3101
3127
  if (!apiKey) {
3102
3128
  currentConfig = null;
3103
3129
  return null;
3104
3130
  }
3105
- const provider2 = getLLMProvider2();
3131
+ const provider2 = scope === "agent" ? getAgentLLMProvider() : getLLMProvider();
3106
3132
  const defaults = PROVIDER_DEFAULTS[provider2] ?? PROVIDER_DEFAULTS.openai;
3107
3133
  currentConfig = {
3108
3134
  provider: provider2,
3109
3135
  apiKey,
3110
- model: getLLMModel2(defaults.model),
3111
- baseUrl: getLLMBaseUrl2(defaults.baseUrl)
3136
+ model: scope === "agent" ? getAgentLLMModel(defaults.model) : getLLMModel(defaults.model),
3137
+ baseUrl: scope === "agent" ? getAgentLLMBaseUrl(defaults.baseUrl) : getLLMBaseUrl(defaults.baseUrl)
3112
3138
  };
3113
3139
  return currentConfig;
3114
3140
  }
@@ -3615,6 +3641,7 @@ var init_provider2 = __esm({
3615
3641
  "src/llm/provider.ts"() {
3616
3642
  "use strict";
3617
3643
  init_esm_shims();
3644
+ init_config();
3618
3645
  LLM_TIMEOUT_DEFAULT_MS = 3e4;
3619
3646
  LLM_TIMEOUT_MIN_MS = 1e3;
3620
3647
  LLM_TIMEOUT_MAX_MS = 3e5;
@@ -10279,6 +10306,7 @@ var init_knowledge_graph = __esm({
10279
10306
  // src/dashboard/server.ts
10280
10307
  var server_exports = {};
10281
10308
  __export(server_exports, {
10309
+ prepareDashboardConfig: () => prepareDashboardConfig,
10282
10310
  startDashboard: () => startDashboard
10283
10311
  });
10284
10312
  import { createServer } from "http";
@@ -10304,6 +10332,25 @@ function isActiveStatus(status) {
10304
10332
  function filterActiveByProject(items, projectId) {
10305
10333
  return items.filter((item) => item.projectId === projectId && isActiveStatus(item.status));
10306
10334
  }
10335
+ function prepareDashboardConfig(projectRoot) {
10336
+ if (!projectRoot) {
10337
+ if (preparedDashboardProjectRoot !== null) {
10338
+ resetDotenv();
10339
+ preparedDashboardProjectRoot = null;
10340
+ }
10341
+ clearProjectRoot();
10342
+ return;
10343
+ }
10344
+ try {
10345
+ if (preparedDashboardProjectRoot !== null && preparedDashboardProjectRoot !== projectRoot) {
10346
+ resetDotenv();
10347
+ }
10348
+ initProjectRoot(projectRoot);
10349
+ loadDotenv(projectRoot);
10350
+ preparedDashboardProjectRoot = projectRoot;
10351
+ } catch {
10352
+ }
10353
+ }
10307
10354
  function computeProjectGraphCounts(allEntities, allRelations, projectObs) {
10308
10355
  const entityNames = new Set(
10309
10356
  projectObs.filter((o) => (o.status ?? "active") === "active" && o.entityName).map((o) => o.entityName)
@@ -10329,6 +10376,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
10329
10376
  effectiveProjectResolved = true;
10330
10377
  effectiveProjectRoot = null;
10331
10378
  }
10379
+ prepareDashboardConfig(effectiveProjectRoot);
10332
10380
  try {
10333
10381
  switch (apiPath) {
10334
10382
  case "/projects": {
@@ -10583,7 +10631,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
10583
10631
  const configProjectRoot = effectiveProjectRoot;
10584
10632
  try {
10585
10633
  const { loadYamlConfig: loadYamlConfig2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
10586
- yml = loadYamlConfig2();
10634
+ yml = configProjectRoot ? loadYamlConfig2(configProjectRoot) : loadYamlConfig2(null);
10587
10635
  } catch {
10588
10636
  }
10589
10637
  if (configProjectRoot) {
@@ -10646,6 +10694,19 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
10646
10694
  } else {
10647
10695
  values.push({ key: "llm.apiKey", value: "not set", source: "none" });
10648
10696
  }
10697
+ const agentProvider = process.env.MEMORIX_AGENT_LLM_PROVIDER || yml.agent?.provider;
10698
+ if (agentProvider) values.push({ key: "agent.llm.provider", value: agentProvider, source: await getEnvSource("MEMORIX_AGENT_LLM_PROVIDER", yml.agent?.provider ? "memorix.yml" : void 0) });
10699
+ const agentModel = process.env.MEMORIX_AGENT_LLM_MODEL || yml.agent?.model;
10700
+ if (agentModel) values.push({ key: "agent.llm.model", value: agentModel, source: await getEnvSource("MEMORIX_AGENT_LLM_MODEL", yml.agent?.model ? "memorix.yml" : void 0) });
10701
+ const agentKey = process.env.MEMORIX_AGENT_LLM_API_KEY || yml.agent?.apiKey;
10702
+ if (agentKey) {
10703
+ let src = "unknown";
10704
+ if (process.env.MEMORIX_AGENT_LLM_API_KEY) src = await getEnvSource("MEMORIX_AGENT_LLM_API_KEY");
10705
+ else if (yml.agent?.apiKey) src = "memorix.yml (move to .env!)";
10706
+ values.push({ key: "agent.llm.apiKey", value: "****" + agentKey.slice(-4), source: src, sensitive: true });
10707
+ } else {
10708
+ values.push({ key: "agent.llm.apiKey", value: "fallback to llm.apiKey", source: "default" });
10709
+ }
10649
10710
  const embProvider = process.env.MEMORIX_EMBEDDING || yml.embedding?.provider || "off";
10650
10711
  values.push({ key: "embedding.provider", value: embProvider, source: await getEnvSource("MEMORIX_EMBEDDING", yml.embedding?.provider ? "memorix.yml" : void 0) });
10651
10712
  values.push({ key: "git.autoHook", value: String(yml.git?.autoHook ?? false), source: yml.git?.autoHook !== void 0 ? "memorix.yml" : "default" });
@@ -11081,7 +11142,7 @@ async function startDashboard(dataDir, port, staticDir, projectId, projectName,
11081
11142
  });
11082
11143
  });
11083
11144
  }
11084
- var MIME_TYPES;
11145
+ var MIME_TYPES, preparedDashboardProjectRoot;
11085
11146
  var init_server = __esm({
11086
11147
  "src/dashboard/server.ts"() {
11087
11148
  "use strict";
@@ -11090,6 +11151,10 @@ var init_server = __esm({
11090
11151
  init_obs_store();
11091
11152
  init_session_store();
11092
11153
  init_graph_store();
11154
+ init_dotenv_loader();
11155
+ init_dotenv_loader();
11156
+ init_yaml_loader();
11157
+ init_yaml_loader();
11093
11158
  MIME_TYPES = {
11094
11159
  ".html": "text/html; charset=utf-8",
11095
11160
  ".css": "text/css; charset=utf-8",
@@ -11099,6 +11164,7 @@ var init_server = __esm({
11099
11164
  ".png": "image/png",
11100
11165
  ".ico": "image/x-icon"
11101
11166
  };
11167
+ preparedDashboardProjectRoot = null;
11102
11168
  }
11103
11169
  });
11104
11170
 
@@ -12255,7 +12321,7 @@ function generateKiroHookFiles() {
12255
12321
  when: { type: "promptSubmit" },
12256
12322
  then: {
12257
12323
  type: "askAgent",
12258
- prompt: "Before responding, load context:\n1. Call memorix_session_start to get previous session summary and key memories\n2. Call memorix_search with a query related to the user's prompt for additional context\n3. If search results are found, use memorix_detail to fetch the most relevant ones\n4. Reference relevant memories naturally in your response"
12324
+ prompt: "Before responding, load useful project context:\n1. Call memorix_search with a query related to the user's prompt for relevant memories\n2. If search results are found, use memorix_detail to fetch the most relevant ones\n3. If memorix_search says this is a fresh project with no Memorix memories yet, do not repeat memorix_search again in the same turn unless the user explicitly asks for history/context or new memories were written\n4. Call memorix_session_start only when explicit session semantics are useful, such as handoff, long-running work, team coordination, or HTTP project binding\n5. Reference relevant memories naturally in your response"
12259
12325
  }
12260
12326
  }, null, 2)
12261
12327
  },
@@ -12866,20 +12932,18 @@ alwaysApply: true
12866
12932
 
12867
12933
  You have access to Memorix memory tools. Follow these rules to maintain persistent context across sessions.
12868
12934
 
12869
- ## RULE 1: Session Start \u2014 Bind Project, Then Load Context
12935
+ ## RULE 1: Use Memory When Useful
12870
12936
 
12871
- At the **beginning of every conversation**, BEFORE responding to the user:
12937
+ At the beginning of a conversation, use Memorix when prior project context would materially help the task. Do not require a session bind for every conversation.
12872
12938
 
12873
- 1. Call \`memorix_session_start\` with parameters:
12874
- - \`agent\`: your agent identifier (e.g. "windsurf", "codex", "antigravity")
12875
- - \`projectRoot\`: the **absolute path** of the current workspace or repo root
12876
- This binds the session to the correct project. Without \`projectRoot\`, memories may go to the wrong bucket.
12877
- 2. Then call \`memorix_search\` with a query related to the user's first message for additional context
12878
- 3. If search results are found, use \`memorix_detail\` to fetch the most relevant ones
12879
- 4. Reference relevant memories naturally \u2014 the user should feel you "remember" them
12939
+ 1. Call \`memorix_search\` with a query related to the user's first message or current task.
12940
+ 2. If search results are found, use \`memorix_detail\` to fetch the most relevant ones.
12941
+ 3. If \`memorix_search\` says this is a fresh project with no Memorix memories yet, treat that as a successful cold-start signal. Do not repeat \`memorix_search\` again in the same turn unless the user explicitly asks for history/context or new memories were written.
12942
+ 4. Call \`memorix_session_start\` only when explicit session semantics are useful: handoff, long-running work, team coordination, restoring prior session context, or HTTP project binding.
12943
+ 5. Reference relevant memories naturally \u2014 the user should feel you understand the project, not that you are following a ritual.
12880
12944
 
12881
12945
  **Important:** \`projectRoot\` is a detection anchor only; Git remains the source of truth for project identity.
12882
- In HTTP control-plane mode (\`memorix serve-http\` / \`memorix background start\`), explicit \`projectRoot\` binding is required for correct multi-project isolation.
12946
+ In HTTP control-plane mode (\`memorix serve-http\` / \`memorix background start\`), explicit \`projectRoot\` binding is recommended when the workspace path is available and required for correct multi-project isolation.
12883
12947
  \`memorix_session_start\` is lightweight by default: it starts memory/session context only. Do not set \`joinTeam\` unless the user explicitly needs autonomous Agent Team tasks, messages, file locks, or orchestrated CLI-agent workflows.
12884
12948
 
12885
12949
  ## RULE 2: Store Important Context
@@ -13724,7 +13788,18 @@ init_mini_skills();
13724
13788
  init_secret_filter();
13725
13789
  async function compactSearch(options) {
13726
13790
  const entries = await searchObservations(options);
13727
- const formatted = formatIndexTable(entries, options.query, !options.projectId);
13791
+ let formatted = formatIndexTable(entries, options.query, !options.projectId);
13792
+ if (entries.length === 0 && options.projectId) {
13793
+ const allObservations = getAllObservations();
13794
+ const projectHasStoredMemory = allObservations.some((obs) => obs.projectId === options.projectId);
13795
+ if (!projectHasStoredMemory) {
13796
+ formatted = `This project does not have any Memorix memories yet.
13797
+
13798
+ It looks like a fresh project: the tool call worked, but there is nothing stored to retrieve yet.
13799
+
13800
+ Memories will start appearing after observations, session summaries, hook captures, or git-memory are written.`;
13801
+ }
13802
+ }
13728
13803
  const totalTokens = countTextTokens(formatted);
13729
13804
  return { entries, formatted, totalTokens };
13730
13805
  }
@@ -16392,7 +16467,7 @@ The path should point to a directory containing a .git folder.`
16392
16467
  };
16393
16468
  const server = existingServer ?? new McpServer({
16394
16469
  name: "memorix",
16395
- version: true ? "1.0.9" : "1.0.1"
16470
+ version: true ? "1.0.10" : "1.0.1"
16396
16471
  });
16397
16472
  const originalRegisterTool = server.registerTool.bind(server);
16398
16473
  server.registerTool = ((name, ...args) => {