memorix 1.9.9 → 1.9.11

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 (54) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +2 -0
  3. package/README.zh-CN.md +2 -0
  4. package/dist/cli/index.js +855 -231
  5. package/dist/index.js +284 -77
  6. package/dist/maintenance-runner.js +74 -20
  7. package/dist/media-audio-runner.js +74 -20
  8. package/dist/media-video-runner.js +74 -20
  9. package/dist/memcode/index.js +1532 -1002
  10. package/dist/memcode-runtime/CHANGELOG.md +42 -0
  11. package/dist/memcode-runtime/package.json +4 -4
  12. package/dist/sdk.js +283 -76
  13. package/dist/vector-backfill-runner.js +74 -20
  14. package/docs/CONFIGURATION.md +26 -0
  15. package/docs/DEVELOPMENT.md +3 -2
  16. package/docs/INTEGRATIONS.md +3 -1
  17. package/docs/README.md +2 -1
  18. package/docs/SETUP.md +1 -1
  19. package/docs/hooks-architecture.md +5 -0
  20. package/package.json +18 -11
  21. package/plugins/antigravity/memorix/plugin.json +1 -1
  22. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  23. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  24. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  25. package/plugins/copilot/memorix/plugin.json +1 -1
  26. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  27. package/plugins/hermes/memorix/plugin.yaml +1 -1
  28. package/plugins/omp/memorix/package.json +1 -1
  29. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  30. package/plugins/pi/memorix/package.json +1 -1
  31. package/server.json +2 -2
  32. package/src/cli/commands/hooks-install.ts +2 -0
  33. package/src/cli/commands/init.ts +6 -2
  34. package/src/cli/commands/integrate.ts +2 -0
  35. package/src/cli/commands/setup.ts +1 -1
  36. package/src/cli/index.ts +2 -2
  37. package/src/cli/tui/ConfigureView.tsx +2 -0
  38. package/src/config/dotenv-loader.ts +1 -0
  39. package/src/config/resolved-config.ts +7 -3
  40. package/src/config/yaml-loader.ts +2 -2
  41. package/src/dashboard/server.ts +3 -0
  42. package/src/hooks/installers/index.ts +219 -31
  43. package/src/hooks/lifecycle.ts +4 -9
  44. package/src/hooks/normalizer.ts +6 -1
  45. package/src/hooks/types.ts +3 -1
  46. package/src/llm/provider.ts +2 -1
  47. package/src/memory/observations.ts +11 -0
  48. package/src/memory/session.ts +4 -13
  49. package/src/memory/visibility.ts +8 -2
  50. package/src/multimodal/image-loader.ts +6 -5
  51. package/src/server.ts +8 -4
  52. package/src/store/obs-store.ts +3 -0
  53. package/src/store/orama-store.ts +43 -6
  54. package/src/store/sqlite-store.ts +15 -0
package/dist/index.js CHANGED
@@ -3239,9 +3239,9 @@ function sharedAgentIds(record80) {
3239
3239
  return [];
3240
3240
  }
3241
3241
  }
3242
- function canReadObservation(record80, reader2) {
3242
+ function canReadObservation(record80, reader2, allowedProjectIds) {
3243
3243
  if (!reader2) return true;
3244
- const sameProject = reader2.projectId === record80.projectId;
3244
+ const sameProject = allowedProjectIds ? allowedProjectIds.includes(record80.projectId) : reader2.projectId === record80.projectId;
3245
3245
  const visibility = resolveObservationVisibility(record80);
3246
3246
  if (visibility === "project") return !reader2.projectId || sameProject;
3247
3247
  if (!sameProject || !reader2.agentId) return false;
@@ -3840,15 +3840,16 @@ function getResolvedConfig(options = {}) {
3840
3840
  const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
3841
3841
  const isAtlasCloud = memoryLlmProvider === "atlascloud";
3842
3842
  const isRequesty = memoryLlmProvider === "requesty";
3843
- const usesPresetKeyOnly = isAtlasCloud || isRequesty;
3843
+ const isApiRoute = memoryLlmProvider === "apiroute";
3844
+ const usesPresetKeyOnly = isAtlasCloud || isRequesty || isApiRoute;
3844
3845
  const memoryLlmApiKey = first(
3845
3846
  process.env.MEMORIX_LLM_API_KEY,
3846
3847
  process.env.MEMORIX_API_KEY,
3847
3848
  toml.memory?.llm?.api_key,
3848
3849
  yaml.llm?.apiKey,
3849
3850
  legacy.llm?.apiKey,
3850
- // Never borrow another provider's credentials for the Atlas Cloud or Requesty presets.
3851
- isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : isRequesty ? process.env.REQUESTY_API_KEY : process.env.OPENAI_API_KEY,
3851
+ // Never borrow another provider's credentials for these gateway presets.
3852
+ isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : isRequesty ? process.env.REQUESTY_API_KEY : isApiRoute ? process.env.API_ROUTE_API_KEY : process.env.OPENAI_API_KEY,
3852
3853
  usesPresetKeyOnly ? void 0 : process.env.ANTHROPIC_API_KEY,
3853
3854
  usesPresetKeyOnly ? void 0 : openRouterMemoryLlmApiKey
3854
3855
  );
@@ -4031,6 +4032,7 @@ function getEnvSourceNames() {
4031
4032
  "MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
4032
4033
  "OPENROUTER_API_KEY",
4033
4034
  "REQUESTY_API_KEY",
4035
+ "API_ROUTE_API_KEY",
4034
4036
  "ATLASCLOUD_API_KEY"
4035
4037
  ].filter((name) => process.env[name]);
4036
4038
  }
@@ -7024,6 +7026,7 @@ var init_provider2 = __esm({
7024
7026
  anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
7025
7027
  openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
7026
7028
  requesty: { baseUrl: "https://router.requesty.ai/v1", model: "openai/gpt-4.1-nano" },
7029
+ apiroute: { baseUrl: "https://global.api-route.com/v1", model: "deepseek-v4.1-flash" },
7027
7030
  atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
7028
7031
  custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
7029
7032
  };
@@ -7518,6 +7521,19 @@ var init_sqlite_store = __esm({
7518
7521
  async listProjectIds() {
7519
7522
  return this.db.prepare("SELECT DISTINCT projectId FROM observations ORDER BY projectId").all().map((row) => row.projectId).filter((projectId) => Boolean(projectId));
7520
7523
  }
7524
+ async migrateProjectIds(aliasIds, canonicalId) {
7525
+ const sourceIds = [...new Set(aliasIds.filter((projectId) => projectId && projectId !== canonicalId))];
7526
+ if (sourceIds.length === 0) return 0;
7527
+ return retrySqliteBusy(async () => {
7528
+ const placeholders = sourceIds.map(() => "?").join(", ");
7529
+ const result = this.db.prepare(
7530
+ `UPDATE observations SET projectId = ? WHERE projectId IN (${placeholders})`
7531
+ ).run(canonicalId, ...sourceIds);
7532
+ const migrated = Number(result.changes ?? 0);
7533
+ if (migrated > 0) this.bumpGeneration();
7534
+ return migrated;
7535
+ }, "Migrate observation project IDs");
7536
+ }
7521
7537
  async searchLexical(options) {
7522
7538
  return searchObservationLexically(this.db, options).map((hit) => ({
7523
7539
  observation: rowToObs(hit.row),
@@ -8563,6 +8579,7 @@ var orama_store_exports = {};
8563
8579
  __export(orama_store_exports, {
8564
8580
  _classifyQueryTier: () => classifyQueryTier,
8565
8581
  batchGenerateEmbeddings: () => batchGenerateEmbeddings,
8582
+ createSearchTokenizer: () => createSearchTokenizer,
8566
8583
  generateEmbedding: () => generateEmbedding,
8567
8584
  generateEmbeddingInput: () => generateEmbeddingInput,
8568
8585
  getActiveSemanticIndexProfile: () => getActiveSemanticIndexProfile,
@@ -8585,7 +8602,7 @@ __export(orama_store_exports, {
8585
8602
  searchObservations: () => searchObservations,
8586
8603
  updateObservationMetadata: () => updateObservationMetadata
8587
8604
  });
8588
- import { create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
8605
+ import { components, create, insert as insert2, insertMultiple, search, remove as remove2, update, count, getByID } from "@orama/orama";
8589
8606
  function rememberSearchMode(key, value) {
8590
8607
  if (!lastSearchModeByProject.has(key) && lastSearchModeByProject.size >= MAX_SEARCH_MODE_KEYS) {
8591
8608
  const oldest = lastSearchModeByProject.keys().next().value;
@@ -8604,6 +8621,26 @@ function getOptionalObservationStore() {
8604
8621
  return null;
8605
8622
  }
8606
8623
  }
8624
+ function createSearchTokenizer() {
8625
+ const defaultTokenizer = components.tokenizer.createTokenizer({ language: "english" });
8626
+ return {
8627
+ language: defaultTokenizer.language,
8628
+ normalizationCache: defaultTokenizer.normalizationCache,
8629
+ tokenize(raw, language, prop, withCache = true) {
8630
+ const defaultTokens = defaultTokenizer.tokenize(raw, language, prop, withCache);
8631
+ const cjkTokens = [];
8632
+ const normalized = raw.normalize("NFKC").toLowerCase();
8633
+ for (const match2 of normalized.matchAll(CJK_RUN)) {
8634
+ const characters = Array.from(match2[0]);
8635
+ cjkTokens.push(...characters);
8636
+ for (let index = 0; index + 1 < characters.length; index++) {
8637
+ cjkTokens.push(`${characters[index]}${characters[index + 1]}`);
8638
+ }
8639
+ }
8640
+ return [.../* @__PURE__ */ new Set([...defaultTokens, ...cjkTokens])];
8641
+ }
8642
+ };
8643
+ }
8607
8644
  async function shouldUsePersistentSearch(projectIds, projectId) {
8608
8645
  const store = getOptionalObservationStore();
8609
8646
  if (!store?.hasLexicalIndex?.()) return false;
@@ -8704,6 +8741,7 @@ async function searchPersistentSemantically(options, projectIds, limit2, queryVe
8704
8741
  const lexical = await searchPersistentLexically(options, projectIds, limit2);
8705
8742
  const store = getObservationStore();
8706
8743
  const documents = /* @__PURE__ */ new Map();
8744
+ const vectorDocumentKeys = /* @__PURE__ */ new Map();
8707
8745
  const addObservation = (observation) => {
8708
8746
  const document2 = observationToSearchDocument(observation);
8709
8747
  documents.set(makeEntryKey(document2.projectId, document2.observationId), document2);
@@ -8719,6 +8757,7 @@ async function searchPersistentSemantically(options, projectIds, limit2, queryVe
8719
8757
  if (options.type && observation.type !== options.type) return null;
8720
8758
  if (options.source && observation.source !== options.source) return null;
8721
8759
  addObservation(observation);
8760
+ vectorDocumentKeys.set(hit.observationId, makeEntryKey(observation.projectId, observation.id));
8722
8761
  return observation;
8723
8762
  }));
8724
8763
  const RRF_K = 60;
@@ -8728,7 +8767,7 @@ async function searchPersistentSemantically(options, projectIds, limit2, queryVe
8728
8767
  scores.set(key, (scores.get(key) ?? 0) + 0.6 / (RRF_K + index + 1));
8729
8768
  }
8730
8769
  for (const [index, hit] of vectorHits.entries()) {
8731
- const key = makeEntryKey(hit.projectId, hit.observationId);
8770
+ const key = vectorDocumentKeys.get(hit.observationId) ?? makeEntryKey(hit.projectId, hit.observationId);
8732
8771
  if (!documents.has(key)) continue;
8733
8772
  scores.set(key, (scores.get(key) ?? 0) + 0.4 / (RRF_K + index + 1));
8734
8773
  }
@@ -8741,12 +8780,13 @@ async function searchPersistentSemantically(options, projectIds, limit2, queryVe
8741
8780
  return {
8742
8781
  count: vectorHits.length,
8743
8782
  hits: vectorHits.map((hit, index) => {
8744
- const key = makeEntryKey(hit.projectId, hit.observationId);
8783
+ const key = vectorDocumentKeys.get(hit.observationId) ?? makeEntryKey(hit.projectId, hit.observationId);
8745
8784
  const document2 = documents.get(key);
8746
8785
  return document2 ? { id: document2.id, score: 0.4 / (RRF_K + index + 1), document: document2 } : null;
8747
8786
  }).filter((hit) => hit !== null).slice(0, limit2)
8748
8787
  };
8749
8788
  }
8789
+ if (hits.length === 0) return null;
8750
8790
  return { count: hits.length, hits };
8751
8791
  } catch {
8752
8792
  return null;
@@ -8821,7 +8861,10 @@ async function initializeDb(options, generation) {
8821
8861
  };
8822
8862
  const dims = nextEmbeddingDimensions ?? 384;
8823
8863
  const schema = nextEmbeddingEnabled ? { ...baseSchema, embedding: `vector[${dims}]` } : baseSchema;
8824
- const nextDb = await create({ schema });
8864
+ const nextDb = await create({
8865
+ schema,
8866
+ components: { tokenizer: createSearchTokenizer() }
8867
+ });
8825
8868
  if (generation !== dbGeneration) {
8826
8869
  return getDb(options);
8827
8870
  }
@@ -9255,7 +9298,7 @@ async function searchObservations(options) {
9255
9298
  if (!projectIds) return true;
9256
9299
  const doc = hit.document;
9257
9300
  return projectIds.includes(doc.projectId);
9258
- }).filter((hit) => canReadObservation(hit.document, options.reader)).filter((hit) => {
9301
+ }).filter((hit) => canReadObservation(hit.document, options.reader, projectIds ?? void 0)).filter((hit) => {
9259
9302
  if (statusFilter === "all") return true;
9260
9303
  const doc = hit.document;
9261
9304
  return (doc.status || "active") === statusFilter;
@@ -9521,7 +9564,7 @@ async function searchObservations(options) {
9521
9564
  let entries = intermediate.map(({ rawTime: _, _isCommandLog: _c, ...rest }) => rest);
9522
9565
  for (const hit of results.hits) {
9523
9566
  const doc = hit.document;
9524
- if (canReadObservation(doc, options.reader)) rememberObservationDoc(doc);
9567
+ if (canReadObservation(doc, options.reader, projectIds ?? void 0)) rememberObservationDoc(doc);
9525
9568
  }
9526
9569
  if (hasQuery && originalQuery) {
9527
9570
  const queryLower = originalQuery.toLowerCase();
@@ -9679,7 +9722,7 @@ function formatTime(isoDate) {
9679
9722
  return isoDate;
9680
9723
  }
9681
9724
  }
9682
- var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration, NON_CJK_HYBRID_SIMILARITY, lastSearchModeByProject, SEARCH_MODE_DEFAULT_KEY, MAX_SEARCH_MODE_KEYS, COMMAND_LOG_TITLE2, COMMAND_STYLE_TITLE, COMMAND_LIKE_QUERY2, COMMAND_INTENT_QUERY, NATURAL_LANGUAGE_MARKERS;
9725
+ var db, dbInitPromise, dbGeneration, embeddingEnabled, embeddingDimensions, indexEmbeddingProvider, deferredCachedVectorHydration, NON_CJK_HYBRID_SIMILARITY, CJK_RUN, lastSearchModeByProject, SEARCH_MODE_DEFAULT_KEY, MAX_SEARCH_MODE_KEYS, COMMAND_LOG_TITLE2, COMMAND_STYLE_TITLE, COMMAND_LIKE_QUERY2, COMMAND_INTENT_QUERY, NATURAL_LANGUAGE_MARKERS;
9683
9726
  var init_orama_store = __esm({
9684
9727
  "src/store/orama-store.ts"() {
9685
9728
  "use strict";
@@ -9703,6 +9746,7 @@ var init_orama_store = __esm({
9703
9746
  indexEmbeddingProvider = null;
9704
9747
  deferredCachedVectorHydration = null;
9705
9748
  NON_CJK_HYBRID_SIMILARITY = 0.45;
9749
+ CJK_RUN = /[\u3400-\u4dbf\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]+/gu;
9706
9750
  lastSearchModeByProject = /* @__PURE__ */ new Map();
9707
9751
  SEARCH_MODE_DEFAULT_KEY = "__global__";
9708
9752
  MAX_SEARCH_MODE_KEYS = 4096;
@@ -73642,16 +73686,16 @@ var require_pluggable_auth_handler = __commonJS({
73642
73686
  * spaces are between quotation marks.
73643
73687
  */
73644
73688
  static parseCommand(command) {
73645
- const components = command.match(/(?:[^\s"]+|"[^"]*")+/g);
73646
- if (!components) {
73689
+ const components2 = command.match(/(?:[^\s"]+|"[^"]*")+/g);
73690
+ if (!components2) {
73647
73691
  throw new Error(`Provided command: "${command}" could not be parsed.`);
73648
73692
  }
73649
- for (let i2 = 0; i2 < components.length; i2++) {
73650
- if (components[i2][0] === '"' && components[i2].slice(-1) === '"') {
73651
- components[i2] = components[i2].slice(1, -1);
73693
+ for (let i2 = 0; i2 < components2.length; i2++) {
73694
+ if (components2[i2][0] === '"' && components2[i2].slice(-1) === '"') {
73695
+ components2[i2] = components2[i2].slice(1, -1);
73652
73696
  }
73653
73697
  }
73654
- return components;
73698
+ return components2;
73655
73699
  }
73656
73700
  };
73657
73701
  exports.PluggableAuthHandler = PluggableAuthHandler;
@@ -154274,6 +154318,16 @@ function getProjectObservations(projectId) {
154274
154318
  async function migrateProjectIds(aliasIds, canonicalId) {
154275
154319
  const nonCanonical = new Set(aliasIds.filter((id) => id !== canonicalId));
154276
154320
  if (nonCanonical.size === 0) return 0;
154321
+ const store = projectDir ? getObservationStore() : null;
154322
+ if (store?.migrateProjectIds) {
154323
+ const migrated2 = await store.migrateProjectIds(aliasIds, canonicalId);
154324
+ if (migrated2 > 0 && corpusLoaded) {
154325
+ for (const observation of observations) {
154326
+ if (nonCanonical.has(observation.projectId)) observation.projectId = canonicalId;
154327
+ }
154328
+ }
154329
+ return migrated2;
154330
+ }
154277
154331
  let migrated = 0;
154278
154332
  const changed = [];
154279
154333
  for (const obs of observations) {
@@ -154284,8 +154338,8 @@ async function migrateProjectIds(aliasIds, canonicalId) {
154284
154338
  }
154285
154339
  }
154286
154340
  if (migrated > 0 && projectDir) {
154287
- const store = getObservationStore();
154288
- await store.atomic(async (tx) => {
154341
+ const store2 = getObservationStore();
154342
+ await store2.atomic(async (tx) => {
154289
154343
  await Promise.all(changed.map((observation) => tx.update(observation)));
154290
154344
  });
154291
154345
  }
@@ -160121,7 +160175,7 @@ var init_request_context = __esm({
160121
160175
  function getMcpServerInfo() {
160122
160176
  return {
160123
160177
  name: "memorix",
160124
- version: true ? "1.9.9" : "1.0.1"
160178
+ version: true ? "1.9.11" : "1.0.1"
160125
160179
  };
160126
160180
  }
160127
160181
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -160580,7 +160634,7 @@ async function getSessionContext(projectDir2, projectId, limit2 = 3, reader2) {
160580
160634
  lines.push("## Key Project Memories");
160581
160635
  lines.push("*Durable working context \u2014 explicit decisions, gotchas, and discoveries.*");
160582
160636
  for (const obs of l2Obs) {
160583
- const emoji = TYPE_EMOJI[obs.type] ?? "[PIN]";
160637
+ const emoji = OBSERVATION_ICONS[obs.type] ?? "[PIN]";
160584
160638
  const fact = obs.facts?.[0] ? ` \u2014 ${redactCredentials(obs.facts[0])}` : "";
160585
160639
  lines.push(`${emoji} ${redactCredentials(obs.title)}${fact}`);
160586
160640
  }
@@ -160628,11 +160682,12 @@ async function getActiveSession(projectDir2, projectId) {
160628
160682
  const sessions = await loadAliasActiveSessions(aliasSet);
160629
160683
  return sessions.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime())[0] ?? null;
160630
160684
  }
160631
- var PRIORITY_TYPES, RESUME_TYPES, TYPE_EMOJI, TYPE_WEIGHTS2, NOISE_PATTERNS2, COMMAND_TRACE_PATTERNS, SYSTEM_SELF_PATTERNS, SESSION_RESUME_OBSERVATION_LIMIT, SESSION_CONTEXT_OBSERVATION_LIMIT, AUTO_RESUME_SUMMARY_LIMIT, AUTO_RESUME_TITLE_LIMIT;
160685
+ var PRIORITY_TYPES, RESUME_TYPES, TYPE_WEIGHTS2, NOISE_PATTERNS2, COMMAND_TRACE_PATTERNS, SYSTEM_SELF_PATTERNS, SESSION_RESUME_OBSERVATION_LIMIT, SESSION_CONTEXT_OBSERVATION_LIMIT, AUTO_RESUME_SUMMARY_LIMIT, AUTO_RESUME_TITLE_LIMIT;
160632
160686
  var init_session = __esm({
160633
160687
  "src/memory/session.ts"() {
160634
160688
  "use strict";
160635
160689
  init_esm_shims();
160690
+ init_types();
160636
160691
  init_admission();
160637
160692
  init_disclosure_policy();
160638
160693
  init_aliases();
@@ -160660,18 +160715,6 @@ var init_session = __esm({
160660
160715
  "what-changed",
160661
160716
  "reasoning"
160662
160717
  ]);
160663
- TYPE_EMOJI = {
160664
- "gotcha": "[DISCOVERY]",
160665
- "decision": "[WHY]",
160666
- "problem-solution": "[FIX]",
160667
- "trade-off": "[TRADEOFF]",
160668
- "discovery": "[DISCOVERY]",
160669
- "how-it-works": "[INFO]",
160670
- "what-changed": "[CHANGE]",
160671
- "why-it-exists": "[DECISION]",
160672
- "session-request": "[SESSION]",
160673
- "probe": "[PROBE]"
160674
- };
160675
160718
  TYPE_WEIGHTS2 = {
160676
160719
  "gotcha": 6,
160677
160720
  "decision": 5.5,
@@ -167591,7 +167634,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
167591
167634
  "OPENAI_API_KEY",
167592
167635
  "ANTHROPIC_API_KEY",
167593
167636
  ...resolved.memory.llm.provider === "openrouter" || resolved.memory.llm.baseUrl?.includes("openrouter.ai") ? ["OPENROUTER_API_KEY"] : [],
167594
- ...resolved.memory.llm.provider === "requesty" ? ["REQUESTY_API_KEY"] : []
167637
+ ...resolved.memory.llm.provider === "requesty" ? ["REQUESTY_API_KEY"] : [],
167638
+ ...resolved.memory.llm.provider === "apiroute" ? ["API_ROUTE_API_KEY"] : []
167595
167639
  ];
167596
167640
  if (llmKey) {
167597
167641
  const src = resolveDashboardKeySource({ value: llmKey, envKeys: memoryKeyEnv, configSource });
@@ -169116,12 +169160,13 @@ __export(image_loader_exports, {
169116
169160
  analyzeImage: () => analyzeImage
169117
169161
  });
169118
169162
  async function callVisionLLM(systemPrompt, imageBase64, mimeType) {
169119
- const apiKey = getLLMApiKey();
169163
+ const config = getLLMConfig();
169164
+ const apiKey = config?.apiKey ?? getLLMApiKey();
169120
169165
  if (!apiKey) {
169121
169166
  throw new Error("No LLM API key configured for image analysis.");
169122
169167
  }
169123
- const baseUrl = normalizeOpenAICompatibleBaseUrl(getLLMBaseUrl("https://api.openai.com/v1"));
169124
- const model = getLLMModel("gpt-4o");
169168
+ const baseUrl = normalizeOpenAICompatibleBaseUrl(config?.baseUrl ?? getLLMBaseUrl("https://api.openai.com/v1"));
169169
+ const model = config?.model ?? getLLMModel("gpt-4o");
169125
169170
  const response = await fetch(`${baseUrl}/chat/completions`, {
169126
169171
  method: "POST",
169127
169172
  headers: {
@@ -169176,7 +169221,7 @@ async function analyzeImage(input) {
169176
169221
  const config = getLLMConfig();
169177
169222
  if (!OPENAI_COMPATIBLE_PROVIDERS.has(config.provider)) {
169178
169223
  throw new Error(
169179
- `Image analysis requires an OpenAI-compatible provider (openai, openrouter, requesty, or custom). Current provider "${config.provider}" uses a different API shape. Set MEMORIX_LLM_PROVIDER=openai or configure an OpenAI-compatible base URL.`
169224
+ `Image analysis requires an OpenAI-compatible provider (openai, openrouter, requesty, apiroute, or custom). Current provider "${config.provider}" uses a different API shape. Set MEMORIX_LLM_PROVIDER=openai or configure an OpenAI-compatible base URL.`
169180
169225
  );
169181
169226
  }
169182
169227
  const bytes = decodeBase64ImagePayload(input.base64, MAX_VISION_IMAGE_BYTES);
@@ -169215,7 +169260,7 @@ var init_image_loader = __esm({
169215
169260
  init_provider2();
169216
169261
  init_secret_filter();
169217
169262
  init_image_payload();
169218
- OPENAI_COMPATIBLE_PROVIDERS = /* @__PURE__ */ new Set(["openai", "openrouter", "requesty", "custom"]);
169263
+ OPENAI_COMPATIBLE_PROVIDERS = /* @__PURE__ */ new Set(["openai", "openrouter", "requesty", "apiroute", "custom"]);
169219
169264
  MAX_VISION_PROMPT_CHARS = 12e3;
169220
169265
  MAX_VISION_DESCRIPTION_CHARS = 12e3;
169221
169266
  MAX_VISION_LABELS = 50;
@@ -169645,6 +169690,26 @@ function generateClaudeConfig() {
169645
169690
  }
169646
169691
  };
169647
169692
  }
169693
+ function generateQwenConfig() {
169694
+ const cmd = `${resolveHookCommand()} hook --agent qwen`;
169695
+ const hookEntry = {
169696
+ type: "command",
169697
+ command: cmd,
169698
+ timeout: 10
169699
+ };
169700
+ return {
169701
+ hooks: {
169702
+ SessionStart: [{ hooks: [hookEntry] }],
169703
+ SessionEnd: [{ hooks: [hookEntry] }],
169704
+ PostToolUse: [{ hooks: [hookEntry] }],
169705
+ PostToolUseFailure: [{ hooks: [hookEntry] }],
169706
+ UserPromptSubmit: [{ hooks: [hookEntry] }],
169707
+ PreCompact: [{ hooks: [hookEntry] }],
169708
+ PostCompact: [{ hooks: [hookEntry] }],
169709
+ Stop: [{ hooks: [hookEntry] }]
169710
+ }
169711
+ };
169712
+ }
169648
169713
  function generateGrokConfig() {
169649
169714
  const cmd = `${resolveHookCommand()} hook --agent grok`;
169650
169715
  const hookEntry = {
@@ -169675,7 +169740,13 @@ function isMemorixGrokCommand(value) {
169675
169740
  function isMemorixGrokHook(value) {
169676
169741
  return isJsonRecord(value) && value.type === "command" && isMemorixGrokCommand(value.command);
169677
169742
  }
169678
- function mergeGrokHookConfig(existing, generated) {
169743
+ function isMemorixQwenCommand(value) {
169744
+ return value === "memorix hook --agent qwen" || value === "memorix.cmd hook --agent qwen";
169745
+ }
169746
+ function isMemorixQwenHook(value) {
169747
+ return isJsonRecord(value) && value.type === "command" && isMemorixQwenCommand(value.command);
169748
+ }
169749
+ function mergeCommandHookConfig(existing, generated, isOwnedHook) {
169679
169750
  const merged = { ...existing };
169680
169751
  const existingHooks = isJsonRecord(existing.hooks) ? { ...existing.hooks } : {};
169681
169752
  const generatedHooks = isJsonRecord(generated.hooks) ? generated.hooks : {};
@@ -169683,7 +169754,7 @@ function mergeGrokHookConfig(existing, generated) {
169683
169754
  const currentGroups = Array.isArray(existingHooks[event]) ? existingHooks[event] : [];
169684
169755
  const preservedGroups = currentGroups.flatMap((group) => {
169685
169756
  if (!isJsonRecord(group) || !Array.isArray(group.hooks)) return [group];
169686
- const preservedHooks = group.hooks.filter((hook) => !isMemorixGrokHook(hook));
169757
+ const preservedHooks = group.hooks.filter((hook) => !isOwnedHook(hook));
169687
169758
  if (preservedHooks.length === 0) return [];
169688
169759
  return [{ ...group, hooks: preservedHooks }];
169689
169760
  });
@@ -169692,7 +169763,13 @@ function mergeGrokHookConfig(existing, generated) {
169692
169763
  merged.hooks = existingHooks;
169693
169764
  return merged;
169694
169765
  }
169695
- function removeMemorixGrokHooks(config) {
169766
+ function mergeGrokHookConfig(existing, generated) {
169767
+ return mergeCommandHookConfig(existing, generated, isMemorixGrokHook);
169768
+ }
169769
+ function mergeQwenHookConfig(existing, generated) {
169770
+ return mergeCommandHookConfig(existing, generated, isMemorixQwenHook);
169771
+ }
169772
+ function removeCommandHooks(config, isOwnedHook) {
169696
169773
  const cleaned = { ...config };
169697
169774
  const hooks = isJsonRecord(config.hooks) ? { ...config.hooks } : null;
169698
169775
  if (!hooks) return cleaned;
@@ -169700,7 +169777,7 @@ function removeMemorixGrokHooks(config) {
169700
169777
  if (!Array.isArray(groups)) continue;
169701
169778
  const preservedGroups = groups.flatMap((group) => {
169702
169779
  if (!isJsonRecord(group) || !Array.isArray(group.hooks)) return [group];
169703
- const preservedHooks = group.hooks.filter((hook) => !isMemorixGrokHook(hook));
169780
+ const preservedHooks = group.hooks.filter((hook) => !isOwnedHook(hook));
169704
169781
  if (preservedHooks.length === 0) return [];
169705
169782
  return [{ ...group, hooks: preservedHooks }];
169706
169783
  });
@@ -169711,6 +169788,12 @@ function removeMemorixGrokHooks(config) {
169711
169788
  else delete cleaned.hooks;
169712
169789
  return cleaned;
169713
169790
  }
169791
+ function removeMemorixGrokHooks(config) {
169792
+ return removeCommandHooks(config, isMemorixGrokHook);
169793
+ }
169794
+ function removeMemorixQwenHooks(config) {
169795
+ return removeCommandHooks(config, isMemorixQwenHook);
169796
+ }
169714
169797
  async function readGrokHookConfig(configPath) {
169715
169798
  let content;
169716
169799
  try {
@@ -169729,6 +169812,24 @@ async function readGrokHookConfig(configPath) {
169729
169812
  throw new Error(`Cannot safely update Grok hook config ${configPath}: ${reason}`);
169730
169813
  }
169731
169814
  }
169815
+ async function readQwenHookConfig(configPath) {
169816
+ let content;
169817
+ try {
169818
+ content = await fs21.readFile(configPath, "utf-8");
169819
+ } catch (error) {
169820
+ if (error.code === "ENOENT") return {};
169821
+ throw error;
169822
+ }
169823
+ if (!content.trim()) return {};
169824
+ try {
169825
+ const parsed = JSON.parse(content);
169826
+ if (!isJsonRecord(parsed)) throw new Error("top-level value must be an object");
169827
+ return parsed;
169828
+ } catch (error) {
169829
+ const reason = error instanceof Error ? error.message : String(error);
169830
+ throw new Error(`Cannot safely update Qwen hook config ${configPath}: ${reason}`);
169831
+ }
169832
+ }
169732
169833
  function generateCopilotConfig() {
169733
169834
  const cmd = `${resolveHookCommand()} hook`;
169734
169835
  const hasPwsh = detectPwsh();
@@ -169930,6 +170031,8 @@ function generateOpenCodePlugin() {
169930
170031
  */
169931
170032
  import { spawnSync } from 'node:child_process';
169932
170033
 
170034
+ const HOOK_TIMEOUT_MS = 30_000;
170035
+
169933
170036
  export const MemorixPlugin = async ({ project, client, $, directory, worktree }) => {
169934
170037
  // Generate a stable session ID for this plugin lifetime
169935
170038
  const sessionId = \`opencode-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2, 8)}\`;
@@ -169955,14 +170058,14 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
169955
170058
  * - spawnSync is simpler: no stream lifecycle, no writer.close() bugs
169956
170059
  * - stdin pipe via input option is reliable cross-platform
169957
170060
  */
169958
- function runHook(payload) {
169959
- payload.session_id = sessionId;
170061
+ function runHook(payload, activeSessionId = sessionId) {
170062
+ payload.session_id = activeSessionId;
169960
170063
  const data = JSON.stringify(payload);
169961
170064
  const eventName = payload.hook_event_name || 'unknown';
169962
170065
  try {
169963
170066
  const result = spawnSync(hookCommand, ['hook'], {
169964
170067
  input: data,
169965
- timeout: 10_000,
170068
+ timeout: HOOK_TIMEOUT_MS,
169966
170069
  encoding: 'utf-8',
169967
170070
  stdio: ['pipe', 'pipe', 'pipe'],
169968
170071
  shell: process.platform === 'win32',
@@ -170007,7 +170110,87 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
170007
170110
  .trim();
170008
170111
  }
170009
170112
 
170113
+ function flushAssistantResponse(activeSessionId = sessionId) {
170114
+ if (!pendingAssistantResponse?.text) return;
170115
+ const deliveryKey = pendingAssistantResponse.id
170116
+ ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
170117
+ : pendingAssistantResponse.text;
170118
+ if (deliveryKey === lastDeliveredAssistantKey) return;
170119
+ runHook({
170120
+ agent: 'opencode',
170121
+ hook_event_name: 'message.updated',
170122
+ ai_response: pendingAssistantResponse.text,
170123
+ message_id: pendingAssistantResponse.id,
170124
+ cwd: directory,
170125
+ }, activeSessionId);
170126
+ lastDeliveredAssistantKey = deliveryKey;
170127
+ }
170128
+
170129
+ function eventSessionId(event) {
170130
+ const properties = event?.properties ?? {};
170131
+ const value = properties.sessionID ?? properties.sessionId ?? properties.info?.id;
170132
+ return typeof value === 'string' && value ? value : sessionId;
170133
+ }
170134
+
170135
+ async function handleEvent({ event }) {
170136
+ const properties = event?.properties ?? {};
170137
+ const activeSessionId = eventSessionId(event);
170138
+ switch (event?.type) {
170139
+ case 'session.created':
170140
+ runHook({
170141
+ agent: 'opencode',
170142
+ hook_event_name: 'session.created',
170143
+ cwd: directory,
170144
+ }, activeSessionId);
170145
+ break;
170146
+ case 'session.idle':
170147
+ flushAssistantResponse(activeSessionId);
170148
+ runHook({
170149
+ agent: 'opencode',
170150
+ hook_event_name: 'session.idle',
170151
+ cwd: directory,
170152
+ }, activeSessionId);
170153
+ break;
170154
+ case 'file.edited':
170155
+ runHook({
170156
+ agent: 'opencode',
170157
+ hook_event_name: 'file.edited',
170158
+ file_path: properties.file ?? properties.path ?? '',
170159
+ cwd: directory,
170160
+ }, activeSessionId);
170161
+ break;
170162
+ case 'command.executed':
170163
+ runHook({
170164
+ agent: 'opencode',
170165
+ hook_event_name: 'command.executed',
170166
+ command: properties.name ?? properties.command ?? '',
170167
+ cwd: directory,
170168
+ }, activeSessionId);
170169
+ break;
170170
+ case 'message.updated': {
170171
+ const message = properties.info ?? properties.message ?? properties;
170172
+ if (message?.role !== 'assistant') break;
170173
+ const text = extractMessageText(message);
170174
+ if (!text) break;
170175
+ pendingAssistantResponse = { id: message.id, text };
170176
+ break;
170177
+ }
170178
+ case 'session.compacted':
170179
+ runHook({
170180
+ agent: 'opencode',
170181
+ hook_event_name: 'session.compacted',
170182
+ cwd: directory,
170183
+ }, activeSessionId);
170184
+ break;
170185
+ default:
170186
+ break;
170187
+ }
170188
+ }
170189
+
170010
170190
  return {
170191
+ // OpenCode 1.x delivers lifecycle/file/command/message events through this
170192
+ // generic hook. The named keys below remain for older compatible hosts.
170193
+ event: handleEvent,
170011
170194
  /** Session created \u2014 record session start */
170012
170195
  'session.created': async ({ session }) => {
170013
170196
  runHook({
@@ -170019,21 +170202,7 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
170019
170202
 
170020
170203
  /** Session idle \u2014 record session end */
170021
170204
  'session.idle': async ({ session }) => {
170022
- if (pendingAssistantResponse?.text) {
170023
- const deliveryKey = pendingAssistantResponse.id
170024
- ? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
170025
- : pendingAssistantResponse.text;
170026
- if (deliveryKey !== lastDeliveredAssistantKey) {
170027
- runHook({
170028
- agent: 'opencode',
170029
- hook_event_name: 'message.updated',
170030
- ai_response: pendingAssistantResponse.text,
170031
- message_id: pendingAssistantResponse.id,
170032
- cwd: directory,
170033
- });
170034
- lastDeliveredAssistantKey = deliveryKey;
170035
- }
170036
- }
170205
+ flushAssistantResponse();
170037
170206
  runHook({
170038
170207
  agent: 'opencode',
170039
170208
  hook_event_name: 'session.idle',
@@ -170141,7 +170310,7 @@ function v2RunHook(payload, directory, sessionId) {
170141
170310
  try {
170142
170311
  const result = spawnSync(v2HookCommand, ['hook'], {
170143
170312
  input: JSON.stringify(nextPayload),
170144
- timeout: 10_000,
170313
+ timeout: HOOK_TIMEOUT_MS,
170145
170314
  encoding: 'utf-8',
170146
170315
  stdio: ['pipe', 'pipe', 'pipe'],
170147
170316
  shell: process.platform === 'win32',
@@ -170265,7 +170434,7 @@ function setup(ctx) {
170265
170434
  return () => controller.abort();
170266
170435
  }
170267
170436
 
170268
- export default { id: 'memorix', setup };
170437
+ export default { id: 'memorix', server: MemorixPlugin, setup };
170269
170438
  `;
170270
170439
  }
170271
170440
  async function installOpenCodeSkills(projectRoot, global2 = false) {
@@ -170275,6 +170444,8 @@ function getProjectConfigPath(agent, projectRoot) {
170275
170444
  switch (agent) {
170276
170445
  case "claude":
170277
170446
  return path34.join(projectRoot, ".claude", "settings.local.json");
170447
+ case "qwen":
170448
+ return path34.join(projectRoot, ".qwen", "settings.json");
170278
170449
  case "copilot":
170279
170450
  return path34.join(projectRoot, ".github", "hooks", "memorix.json");
170280
170451
  case "windsurf":
@@ -170316,6 +170487,8 @@ function getGlobalConfigPath(agent) {
170316
170487
  switch (agent) {
170317
170488
  case "claude":
170318
170489
  return path34.join(home, ".claude", "settings.json");
170490
+ case "qwen":
170491
+ return path34.join(home, ".qwen", "settings.json");
170319
170492
  case "copilot":
170320
170493
  return "";
170321
170494
  case "windsurf":
@@ -170357,6 +170530,8 @@ function getAgentRulesPath(agent, root, global2 = false) {
170357
170530
  return path34.join(root, ".cursor", "rules", "memorix.mdc");
170358
170531
  case "claude":
170359
170532
  return path34.join(root, ".claude", "CLAUDE.md");
170533
+ case "qwen":
170534
+ return path34.join(root, ".qwen", "QWEN.md");
170360
170535
  case "codex":
170361
170536
  return path34.join(root, ".codex", "AGENTS.md");
170362
170537
  case "kiro":
@@ -170385,6 +170560,8 @@ function getAgentRulesPath(agent, root, global2 = false) {
170385
170560
  return path34.join(root, ".cursor", "rules", "memorix.mdc");
170386
170561
  case "claude":
170387
170562
  return path34.join(root, "CLAUDE.md");
170563
+ case "qwen":
170564
+ return path34.join(root, "QWEN.md");
170388
170565
  case "copilot":
170389
170566
  return path34.join(root, ".github", "copilot-instructions.md");
170390
170567
  case "codex":
@@ -170461,6 +170638,12 @@ async function detectInstalledAgents() {
170461
170638
  agents.push("grok");
170462
170639
  } catch {
170463
170640
  }
170641
+ const qwenDir = path34.join(home, ".qwen");
170642
+ try {
170643
+ await fs21.access(qwenDir);
170644
+ agents.push("qwen");
170645
+ } catch {
170646
+ }
170464
170647
  const codexDir = path34.join(home, ".codex");
170465
170648
  try {
170466
170649
  await fs21.access(codexDir);
@@ -170544,10 +170727,10 @@ async function installHooks(agent, projectRoot, global2 = false) {
170544
170727
  const { access: access3, unlink: unlink3 } = await import("fs/promises");
170545
170728
  await access3(entry.path);
170546
170729
  const basename5 = path34.basename(entry.path);
170547
- if (basename5 === "AGENTS.md" || basename5 === "GEMINI.md" || basename5 === "CONTEXT.md") {
170730
+ if (basename5 === "AGENTS.md" || basename5 === "GEMINI.md" || basename5 === "QWEN.md" || basename5 === "CONTEXT.md") {
170548
170731
  continue;
170549
170732
  }
170550
- if (agent === "grok" && path34.resolve(entry.path) === path34.resolve(configPath)) {
170733
+ if ((agent === "grok" || agent === "qwen") && path34.resolve(entry.path) === path34.resolve(configPath)) {
170551
170734
  continue;
170552
170735
  }
170553
170736
  await unlink3(entry.path);
@@ -170562,6 +170745,9 @@ async function installHooks(agent, projectRoot, global2 = false) {
170562
170745
  case "claude":
170563
170746
  generated = generateClaudeConfig();
170564
170747
  break;
170748
+ case "qwen":
170749
+ generated = generateQwenConfig();
170750
+ break;
170565
170751
  case "grok":
170566
170752
  generated = generateGrokConfig();
170567
170753
  break;
@@ -170668,6 +170854,15 @@ async function installHooks(agent, projectRoot, global2 = false) {
170668
170854
  await recordFile2(projectRoot, "hook", configPath, agent);
170669
170855
  } catch {
170670
170856
  }
170857
+ } else if (agent === "qwen") {
170858
+ const existing = await readQwenHookConfig(configPath);
170859
+ const merged = mergeQwenHookConfig(existing, generated);
170860
+ await fs21.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
170861
+ try {
170862
+ const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
170863
+ await recordFile2(projectRoot, "hook", configPath, agent);
170864
+ } catch {
170865
+ }
170671
170866
  } else if (agent === "kiro") {
170672
170867
  const hookFiles = generateKiroHookFiles();
170673
170868
  const hooksDir = path34.join(path34.dirname(configPath));
@@ -170731,6 +170926,9 @@ async function installHooks(agent, projectRoot, global2 = false) {
170731
170926
  case "claude":
170732
170927
  events.push("session_start", "post_tool", "user_prompt", "pre_compact", "session_end");
170733
170928
  break;
170929
+ case "qwen":
170930
+ events.push("session_start", "session_end", "user_prompt", "post_tool", "pre_compact", "post_compact");
170931
+ break;
170734
170932
  case "copilot":
170735
170933
  events.push("session_start", "session_end", "user_prompt", "post_tool");
170736
170934
  break;
@@ -170772,7 +170970,7 @@ async function installAgentRules(agent, projectRoot, global2 = false) {
170772
170970
  const rulesPath = getAgentRulesPath(agent, rulesRoot, global2);
170773
170971
  try {
170774
170972
  await fs21.mkdir(path34.dirname(rulesPath), { recursive: true });
170775
- if (agent === "claude" || agent === "codex" || agent === "opencode" || agent === "antigravity" || agent === "gemini-cli" || agent === "dsh" || agent === "grok") {
170973
+ if (agent === "claude" || agent === "qwen" || agent === "codex" || agent === "opencode" || agent === "antigravity" || agent === "gemini-cli" || agent === "dsh" || agent === "grok") {
170776
170974
  try {
170777
170975
  const existing = await fs21.readFile(rulesPath, "utf-8");
170778
170976
  await fs21.writeFile(rulesPath, mergeMemorixRulesContent(existing, rulesContent), "utf-8");
@@ -170950,6 +171148,15 @@ async function uninstallHooks(agent, projectRoot, global2 = false) {
170950
171148
  await fs21.writeFile(configPath, JSON.stringify(cleaned, null, 2), "utf-8");
170951
171149
  }
170952
171150
  success = true;
171151
+ } else if (agent === "qwen") {
171152
+ const config = await readQwenHookConfig(configPath);
171153
+ const cleaned = removeMemorixQwenHooks(config);
171154
+ if (Object.keys(cleaned).length === 0) {
171155
+ await fs21.unlink(configPath);
171156
+ } else {
171157
+ await fs21.writeFile(configPath, JSON.stringify(cleaned, null, 2), "utf-8");
171158
+ }
171159
+ success = true;
170953
171160
  } else if (agent === "kiro" || agent === "opencode") {
170954
171161
  await fs21.unlink(configPath);
170955
171162
  success = true;
@@ -170973,13 +171180,13 @@ async function uninstallHooks(agent, projectRoot, global2 = false) {
170973
171180
  const agentFiles = prevFiles.filter((e2) => e2.agent === agent);
170974
171181
  for (const entry of agentFiles) {
170975
171182
  try {
170976
- if (agent === "grok" && path34.resolve(entry.path) === path34.resolve(configPath)) {
171183
+ if ((agent === "grok" || agent === "qwen") && path34.resolve(entry.path) === path34.resolve(configPath)) {
170977
171184
  await removeFile2(projectRoot, entry.path);
170978
171185
  auditCleaned = true;
170979
171186
  continue;
170980
171187
  }
170981
171188
  const basename5 = path34.basename(entry.path);
170982
- if (basename5 === "AGENTS.md" || basename5 === "GEMINI.md" || basename5 === "CONTEXT.md") {
171189
+ if (basename5 === "AGENTS.md" || basename5 === "GEMINI.md" || basename5 === "QWEN.md" || basename5 === "CONTEXT.md") {
170983
171190
  try {
170984
171191
  const content = await fs21.readFile(entry.path, "utf-8");
170985
171192
  const memorixStart = content.indexOf("# Memorix");
@@ -171038,7 +171245,7 @@ async function uninstallHooks(agent, projectRoot, global2 = false) {
171038
171245
  }
171039
171246
  async function getHookStatus(projectRoot) {
171040
171247
  const results = [];
171041
- const agents = ["claude", "copilot", "windsurf", "cursor", "kiro", "codex", "antigravity", "gemini-cli", "opencode", "pi", "trae", "grok"];
171248
+ const agents = ["claude", "qwen", "copilot", "windsurf", "cursor", "kiro", "codex", "antigravity", "gemini-cli", "opencode", "pi", "trae", "grok"];
171042
171249
  for (const agent of agents) {
171043
171250
  const projectPath = getProjectConfigPath(agent, projectRoot);
171044
171251
  const globalPath = getGlobalConfigPath(agent);
@@ -171091,7 +171298,7 @@ var init_installers = __esm({
171091
171298
  "use strict";
171092
171299
  init_esm_shims();
171093
171300
  init_official_skills();
171094
- OPENCODE_PLUGIN_VERSION = 8;
171301
+ OPENCODE_PLUGIN_VERSION = 9;
171095
171302
  AGENT_SKILL_DIRS = {
171096
171303
  cursor: { project: path34.join(".cursor", "skills"), global: path34.join(".cursor", "skills") },
171097
171304
  windsurf: { project: path34.join(".windsurf", "skills"), global: path34.join(".windsurf", "skills") },
@@ -171381,8 +171588,8 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
171381
171588
  if (!lightweightUnresolvedSession && !deferProjectRuntimeInit) {
171382
171589
  try {
171383
171590
  const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
171384
- const allObs = getAllObservations2();
171385
- const observedIds = [...new Set(allObs.map((o) => o.projectId))];
171591
+ const store = getObservationStore();
171592
+ const observedIds = store.listProjectIds ? await store.listProjectIds() : [...new Set(getAllObservations2().map((o) => o.projectId))];
171386
171593
  const merged = await autoMergeByBaseName(observedIds);
171387
171594
  if (merged > 0) {
171388
171595
  console.error(`[memorix] Auto-merged ${merged} alias group(s) by base name`);
@@ -171423,8 +171630,8 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
171423
171630
  try {
171424
171631
  const { getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
171425
171632
  await initObservations(projectDir2);
171426
- const allObs = getAllObservations2();
171427
- const observedIds = [...new Set(allObs.map((o) => o.projectId))];
171633
+ const store = getObservationStore();
171634
+ const observedIds = store.listProjectIds ? await store.listProjectIds() : [...new Set(getAllObservations2().map((o) => o.projectId))];
171428
171635
  const merged = await autoMergeByBaseName(observedIds);
171429
171636
  if (merged > 0) {
171430
171637
  console.error(`[memorix] Auto-merged ${merged} alias group(s) by base name`);
@@ -176615,7 +176822,7 @@ var init_serve_shared = __esm({
176615
176822
 
176616
176823
  // src/cli/version.ts
176617
176824
  function getCliVersion() {
176618
- return true ? "1.9.9" : pkg.version;
176825
+ return true ? "1.9.11" : pkg.version;
176619
176826
  }
176620
176827
  var init_version3 = __esm({
176621
176828
  "src/cli/version.ts"() {