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.
- package/CHANGELOG.md +42 -0
- package/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/cli/index.js +855 -231
- package/dist/index.js +284 -77
- package/dist/maintenance-runner.js +74 -20
- package/dist/media-audio-runner.js +74 -20
- package/dist/media-video-runner.js +74 -20
- package/dist/memcode/index.js +1532 -1002
- package/dist/memcode-runtime/CHANGELOG.md +42 -0
- package/dist/memcode-runtime/package.json +4 -4
- package/dist/sdk.js +283 -76
- package/dist/vector-backfill-runner.js +74 -20
- package/docs/CONFIGURATION.md +26 -0
- package/docs/DEVELOPMENT.md +3 -2
- package/docs/INTEGRATIONS.md +3 -1
- package/docs/README.md +2 -1
- package/docs/SETUP.md +1 -1
- package/docs/hooks-architecture.md +5 -0
- package/package.json +18 -11
- package/plugins/antigravity/memorix/plugin.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/commands/hooks-install.ts +2 -0
- package/src/cli/commands/init.ts +6 -2
- package/src/cli/commands/integrate.ts +2 -0
- package/src/cli/commands/setup.ts +1 -1
- package/src/cli/index.ts +2 -2
- package/src/cli/tui/ConfigureView.tsx +2 -0
- package/src/config/dotenv-loader.ts +1 -0
- package/src/config/resolved-config.ts +7 -3
- package/src/config/yaml-loader.ts +2 -2
- package/src/dashboard/server.ts +3 -0
- package/src/hooks/installers/index.ts +219 -31
- package/src/hooks/lifecycle.ts +4 -9
- package/src/hooks/normalizer.ts +6 -1
- package/src/hooks/types.ts +3 -1
- package/src/llm/provider.ts +2 -1
- package/src/memory/observations.ts +11 -0
- package/src/memory/session.ts +4 -13
- package/src/memory/visibility.ts +8 -2
- package/src/multimodal/image-loader.ts +6 -5
- package/src/server.ts +8 -4
- package/src/store/obs-store.ts +3 -0
- package/src/store/orama-store.ts +43 -6
- 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
|
|
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
|
|
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({
|
|
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
|
|
73646
|
-
if (!
|
|
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 <
|
|
73650
|
-
if (
|
|
73651
|
-
|
|
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
|
|
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
|
|
154288
|
-
await
|
|
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.
|
|
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 =
|
|
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,
|
|
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
|
|
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
|
|
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) => !
|
|
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
|
|
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) => !
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
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
|
|
171385
|
-
const observedIds = [...new Set(
|
|
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
|
|
171427
|
-
const observedIds = [...new Set(
|
|
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.
|
|
176825
|
+
return true ? "1.9.11" : pkg.version;
|
|
176619
176826
|
}
|
|
176620
176827
|
var init_version3 = __esm({
|
|
176621
176828
|
"src/cli/version.ts"() {
|