memorix 1.2.9 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +30 -4
- package/README.zh-CN.md +28 -2
- package/assets/star-history-dark.svg +30 -0
- package/assets/star-history-light.svg +30 -0
- package/assets/star-history.json +599 -0
- package/dist/cli/index.js +1945 -579
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1292 -267
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.js +97 -21
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +24 -0
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +1294 -269
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +99 -1
- package/dist/types.js.map +1 -1
- package/dist/vector-backfill-runner.js +97 -21
- package/dist/vector-backfill-runner.js.map +1 -1
- package/docs/1.3-MEMORY-ARCHITECTURE.md +236 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +5 -3
- package/docs/API_REFERENCE.md +20 -1
- package/docs/ARCHITECTURE.md +34 -1
- package/docs/DEVELOPMENT.md +10 -3
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/MODULES.md +27 -1
- package/docs/README.md +3 -3
- package/docs/SETUP.md +6 -4
- package/docs/dev-log/progress.txt +22 -0
- package/docs/hooks-architecture.md +1 -1
- package/llms-full.txt +5 -1
- package/llms.txt +2 -0
- package/package.json +7 -2
- package/plugins/antigravity/memorix/rules/memorix.md +2 -0
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/memorix/skills/memorix-memory/SKILL.md +3 -0
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/skills/memorix-memory/SKILL.md +3 -0
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/copilot/memorix/skills/memorix-memory/SKILL.md +3 -0
- package/plugins/cursor/memorix/skills/memorix-memory/SKILL.md +3 -0
- package/plugins/gemini/memorix/GEMINI.md +2 -0
- package/plugins/gemini/memorix/gemini-extension.json +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/plugins/pi/memorix/skills/memorix-memory/SKILL.md +3 -0
- package/server.json +38 -0
- package/src/cli/command-guide.ts +9 -1
- package/src/cli/commands/agent-integrations.ts +12 -6
- package/src/cli/commands/codegraph.ts +1 -0
- package/src/cli/commands/memory.ts +217 -1
- package/src/cli/commands/setup.ts +200 -23
- package/src/codegraph/auto-context.ts +11 -0
- package/src/codegraph/context-pack.ts +3 -0
- package/src/embedding/api-provider.ts +23 -9
- package/src/embedding/provider.ts +21 -10
- package/src/hooks/installers/index.ts +2 -0
- package/src/knowledge/context-assembly.ts +2 -1
- package/src/knowledge/workset.ts +77 -4
- package/src/memory/long-term-store.ts +242 -0
- package/src/memory/long-term-types.ts +136 -0
- package/src/memory/long-term.ts +626 -0
- package/src/memory/owner.ts +75 -0
- package/src/server.ts +112 -30
- package/src/store/sqlite-db.ts +74 -0
- package/src/types.ts +20 -0
package/dist/index.js
CHANGED
|
@@ -149,15 +149,15 @@ function loadSqlite() {
|
|
|
149
149
|
throw new Error("[memorix] SQLite is unavailable (better-sqlite3, node:sqlite, and bun:sqlite failed)");
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
function createDatabase(
|
|
152
|
+
function createDatabase(path27, options) {
|
|
153
153
|
const Sqlite = loadSqlite();
|
|
154
154
|
try {
|
|
155
|
-
return addCompatibility(instantiateDatabase(Sqlite,
|
|
155
|
+
return addCompatibility(instantiateDatabase(Sqlite, path27, options));
|
|
156
156
|
} catch (error) {
|
|
157
157
|
if (driver !== "better-sqlite3" || !isNativeBindingFailure(error)) throw error;
|
|
158
158
|
Database = loadNodeSqlite();
|
|
159
159
|
driver = "node:sqlite";
|
|
160
|
-
return addCompatibility(instantiateDatabase(Database,
|
|
160
|
+
return addCompatibility(instantiateDatabase(Database, path27, options));
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
var Database, driver, requireFromHere;
|
|
@@ -284,7 +284,7 @@ function closeAllDatabases() {
|
|
|
284
284
|
_dbCache.delete(key);
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
|
-
var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
|
|
287
|
+
var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
|
|
288
288
|
var init_sqlite_db = __esm({
|
|
289
289
|
"src/store/sqlite-db.ts"() {
|
|
290
290
|
"use strict";
|
|
@@ -710,6 +710,57 @@ CREATE TABLE IF NOT EXISTS knowledge_claim_events (
|
|
|
710
710
|
" FOREIGN KEY (workflowId) REFERENCES knowledge_workflows(id) ON DELETE CASCADE",
|
|
711
711
|
");"
|
|
712
712
|
].join("\n");
|
|
713
|
+
CREATE_LONG_TERM_MEMORIES_TABLE = `
|
|
714
|
+
CREATE TABLE IF NOT EXISTS long_term_memories (
|
|
715
|
+
id TEXT PRIMARY KEY,
|
|
716
|
+
originProjectId TEXT NOT NULL,
|
|
717
|
+
ownerId TEXT NOT NULL,
|
|
718
|
+
scope TEXT NOT NULL,
|
|
719
|
+
kind TEXT NOT NULL,
|
|
720
|
+
state TEXT NOT NULL,
|
|
721
|
+
portability TEXT NOT NULL DEFAULT 'project-bound',
|
|
722
|
+
title TEXT NOT NULL,
|
|
723
|
+
content TEXT NOT NULL,
|
|
724
|
+
factsJson TEXT NOT NULL DEFAULT '[]',
|
|
725
|
+
tagsJson TEXT NOT NULL DEFAULT '[]',
|
|
726
|
+
applicability TEXT,
|
|
727
|
+
origin TEXT NOT NULL,
|
|
728
|
+
createdAt TEXT NOT NULL,
|
|
729
|
+
updatedAt TEXT NOT NULL,
|
|
730
|
+
qualifiedAt TEXT,
|
|
731
|
+
approvedAt TEXT,
|
|
732
|
+
archivedAt TEXT,
|
|
733
|
+
supersededBy TEXT,
|
|
734
|
+
lastValidatedAt TEXT,
|
|
735
|
+
accessCount INTEGER NOT NULL DEFAULT 0,
|
|
736
|
+
lastAccessedAt TEXT
|
|
737
|
+
);
|
|
738
|
+
`;
|
|
739
|
+
CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE = `
|
|
740
|
+
CREATE TABLE IF NOT EXISTS long_term_memory_evidence (
|
|
741
|
+
id TEXT PRIMARY KEY,
|
|
742
|
+
memoryId TEXT NOT NULL,
|
|
743
|
+
kind TEXT NOT NULL,
|
|
744
|
+
referenceId TEXT NOT NULL,
|
|
745
|
+
relation TEXT NOT NULL,
|
|
746
|
+
locator TEXT,
|
|
747
|
+
capturedHash TEXT,
|
|
748
|
+
createdAt TEXT NOT NULL,
|
|
749
|
+
FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
|
|
750
|
+
);
|
|
751
|
+
`;
|
|
752
|
+
CREATE_LONG_TERM_MEMORY_EVENTS_TABLE = `
|
|
753
|
+
CREATE TABLE IF NOT EXISTS long_term_memory_events (
|
|
754
|
+
id TEXT PRIMARY KEY,
|
|
755
|
+
memoryId TEXT NOT NULL,
|
|
756
|
+
kind TEXT NOT NULL,
|
|
757
|
+
fromState TEXT,
|
|
758
|
+
toState TEXT,
|
|
759
|
+
detail TEXT,
|
|
760
|
+
createdAt TEXT NOT NULL,
|
|
761
|
+
FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
|
|
762
|
+
);
|
|
763
|
+
`;
|
|
713
764
|
CREATE_MAINTENANCE_JOBS_TABLE = `
|
|
714
765
|
CREATE TABLE IF NOT EXISTS maintenance_jobs (
|
|
715
766
|
id TEXT PRIMARY KEY,
|
|
@@ -805,6 +856,11 @@ CREATE INDEX IF NOT EXISTS idx_knowledge_page_claims_claim ON knowledge_page_cla
|
|
|
805
856
|
CREATE INDEX IF NOT EXISTS idx_knowledge_proposals_workspace_status ON knowledge_proposals(workspaceId, status, createdAt DESC);
|
|
806
857
|
CREATE INDEX IF NOT EXISTS idx_knowledge_workflows_workspace_status ON knowledge_workflows(workspaceId, status, updatedAt DESC);
|
|
807
858
|
CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowledge_workflow_runs(projectId, workflowId, startedAt DESC);
|
|
859
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC);
|
|
860
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC);
|
|
861
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC);
|
|
862
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt);
|
|
863
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt);
|
|
808
864
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_ready ON maintenance_jobs(status, run_after);
|
|
809
865
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_project ON maintenance_jobs(project_id, status, run_after);
|
|
810
866
|
CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_targets(updated_at);
|
|
@@ -893,6 +949,19 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
893
949
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC)");
|
|
894
950
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key)");
|
|
895
951
|
}
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
id: "1.3-long-term-memory",
|
|
955
|
+
apply: (db2) => {
|
|
956
|
+
db2.exec(CREATE_LONG_TERM_MEMORIES_TABLE);
|
|
957
|
+
db2.exec(CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE);
|
|
958
|
+
db2.exec(CREATE_LONG_TERM_MEMORY_EVENTS_TABLE);
|
|
959
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC)");
|
|
960
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC)");
|
|
961
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC)");
|
|
962
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt)");
|
|
963
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt)");
|
|
964
|
+
}
|
|
896
965
|
}
|
|
897
966
|
];
|
|
898
967
|
_dbCache = /* @__PURE__ */ new Map();
|
|
@@ -2728,9 +2797,9 @@ function parseTomlConfig(content, filePath) {
|
|
|
2728
2797
|
}
|
|
2729
2798
|
return root;
|
|
2730
2799
|
}
|
|
2731
|
-
function ensureTable(root,
|
|
2800
|
+
function ensureTable(root, path27) {
|
|
2732
2801
|
let cursor = root;
|
|
2733
|
-
for (const part of
|
|
2802
|
+
for (const part of path27) {
|
|
2734
2803
|
const value = cursor[part];
|
|
2735
2804
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2736
2805
|
cursor[part] = {};
|
|
@@ -3606,9 +3675,10 @@ function parseBatchLimit(error) {
|
|
|
3606
3675
|
}
|
|
3607
3676
|
return null;
|
|
3608
3677
|
}
|
|
3609
|
-
async function fetchWithRetry(url, apiKey, body, attempt = 0) {
|
|
3678
|
+
async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0) {
|
|
3610
3679
|
const controller = new AbortController();
|
|
3611
|
-
const
|
|
3680
|
+
const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
|
|
3681
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
3612
3682
|
let response;
|
|
3613
3683
|
try {
|
|
3614
3684
|
response = await fetch(url, {
|
|
@@ -3623,7 +3693,7 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
|
|
|
3623
3693
|
} catch (err) {
|
|
3624
3694
|
clearTimeout(timeout);
|
|
3625
3695
|
if (err instanceof Error && err.name === "AbortError") {
|
|
3626
|
-
throw new Error(`Embedding API timeout after
|
|
3696
|
+
throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
|
|
3627
3697
|
}
|
|
3628
3698
|
throw err;
|
|
3629
3699
|
}
|
|
@@ -3631,13 +3701,13 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
|
|
|
3631
3701
|
if (response.ok) {
|
|
3632
3702
|
return response.json();
|
|
3633
3703
|
}
|
|
3634
|
-
if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
|
|
3704
|
+
if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
|
|
3635
3705
|
const delay = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
3636
3706
|
const retryAfter = response.headers.get("retry-after");
|
|
3637
3707
|
const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay;
|
|
3638
3708
|
console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
|
|
3639
3709
|
await new Promise((resolve2) => setTimeout(resolve2, waitMs));
|
|
3640
|
-
return fetchWithRetry(url, apiKey, body, attempt + 1);
|
|
3710
|
+
return fetchWithRetry(url, apiKey, body, options, attempt + 1);
|
|
3641
3711
|
}
|
|
3642
3712
|
const errorText2 = await response.text().catch(() => "unknown error");
|
|
3643
3713
|
throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
|
|
@@ -3693,7 +3763,10 @@ var init_api_provider = __esm({
|
|
|
3693
3763
|
}
|
|
3694
3764
|
} else {
|
|
3695
3765
|
if (!allowNetworkProbe) return null;
|
|
3696
|
-
probeDimensions = await _APIEmbeddingProvider.probeAPI(config
|
|
3766
|
+
probeDimensions = await _APIEmbeddingProvider.probeAPI(config, {
|
|
3767
|
+
timeoutMs: options.requestTimeoutMs,
|
|
3768
|
+
retry: options.retry
|
|
3769
|
+
});
|
|
3697
3770
|
console.error(`[memorix] API embedding: ${config.model} @ ${config.baseUrl} (${probeDimensions}d)`);
|
|
3698
3771
|
saveCachedDims(config, probeDimensions).catch(() => {
|
|
3699
3772
|
});
|
|
@@ -3735,7 +3808,7 @@ var init_api_provider = __esm({
|
|
|
3735
3808
|
baseUrl = baseUrl.replace(/\/+$/, "");
|
|
3736
3809
|
return { apiKey, baseUrl, model, requestedDimensions };
|
|
3737
3810
|
}
|
|
3738
|
-
static async probeAPI(config) {
|
|
3811
|
+
static async probeAPI(config, options) {
|
|
3739
3812
|
const body = {
|
|
3740
3813
|
model: config.model,
|
|
3741
3814
|
input: "dimension probe"
|
|
@@ -3746,14 +3819,15 @@ var init_api_provider = __esm({
|
|
|
3746
3819
|
const response = await fetchWithRetry(
|
|
3747
3820
|
`${config.baseUrl}/embeddings`,
|
|
3748
3821
|
config.apiKey,
|
|
3749
|
-
body
|
|
3822
|
+
body,
|
|
3823
|
+
options
|
|
3750
3824
|
);
|
|
3751
3825
|
if (response.data.length === 0 || !response.data[0].embedding) {
|
|
3752
3826
|
throw new Error("API probe returned no embeddings; check model name and API key");
|
|
3753
3827
|
}
|
|
3754
3828
|
return response.data[0].embedding.length;
|
|
3755
3829
|
}
|
|
3756
|
-
async embed(text) {
|
|
3830
|
+
async embed(text, options) {
|
|
3757
3831
|
const normalized = normalizeText(text);
|
|
3758
3832
|
const hash4 = textHash2(normalized, this.cacheKeyNamespace);
|
|
3759
3833
|
if (diskCacheLoaded) {
|
|
@@ -3771,7 +3845,8 @@ var init_api_provider = __esm({
|
|
|
3771
3845
|
const response = await fetchWithRetry(
|
|
3772
3846
|
`${this.config.baseUrl}/embeddings`,
|
|
3773
3847
|
this.config.apiKey,
|
|
3774
|
-
body
|
|
3848
|
+
body,
|
|
3849
|
+
options
|
|
3775
3850
|
);
|
|
3776
3851
|
const embedding2 = response.data[0].embedding;
|
|
3777
3852
|
if (embedding2.length !== this.dimensions) {
|
|
@@ -3805,7 +3880,7 @@ var init_api_provider = __esm({
|
|
|
3805
3880
|
scheduleDiskSave();
|
|
3806
3881
|
return embedding;
|
|
3807
3882
|
}
|
|
3808
|
-
async embedBatch(texts) {
|
|
3883
|
+
async embedBatch(texts, options) {
|
|
3809
3884
|
await ensureDiskCacheLoaded();
|
|
3810
3885
|
const normalizedTexts = texts.map(normalizeText);
|
|
3811
3886
|
const results = new Array(texts.length);
|
|
@@ -3839,7 +3914,8 @@ var init_api_provider = __esm({
|
|
|
3839
3914
|
const response = await fetchWithRetry(
|
|
3840
3915
|
`${this.config.baseUrl}/embeddings`,
|
|
3841
3916
|
this.config.apiKey,
|
|
3842
|
-
body
|
|
3917
|
+
body,
|
|
3918
|
+
options
|
|
3843
3919
|
);
|
|
3844
3920
|
this.trackUsage(response);
|
|
3845
3921
|
for (const item of response.data) {
|
|
@@ -4016,9 +4092,9 @@ function wrapProvider(candidate) {
|
|
|
4016
4092
|
return {
|
|
4017
4093
|
name: candidate.name,
|
|
4018
4094
|
dimensions: candidate.dimensions,
|
|
4019
|
-
async embed(text) {
|
|
4095
|
+
async embed(text, options) {
|
|
4020
4096
|
try {
|
|
4021
|
-
return await candidate.embed(text);
|
|
4097
|
+
return await candidate.embed(text, options);
|
|
4022
4098
|
} catch (error) {
|
|
4023
4099
|
if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
|
|
4024
4100
|
warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
|
|
@@ -4026,7 +4102,7 @@ function wrapProvider(candidate) {
|
|
|
4026
4102
|
if (fallback) {
|
|
4027
4103
|
provider = wrapProvider(fallback);
|
|
4028
4104
|
warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
|
|
4029
|
-
return provider.embed(text);
|
|
4105
|
+
return provider.embed(text, options);
|
|
4030
4106
|
}
|
|
4031
4107
|
}
|
|
4032
4108
|
if (isTemporaryEmbeddingFailure(error)) {
|
|
@@ -4035,9 +4111,9 @@ function wrapProvider(candidate) {
|
|
|
4035
4111
|
throw error;
|
|
4036
4112
|
}
|
|
4037
4113
|
},
|
|
4038
|
-
async embedBatch(texts) {
|
|
4114
|
+
async embedBatch(texts, options) {
|
|
4039
4115
|
try {
|
|
4040
|
-
return await candidate.embedBatch(texts);
|
|
4116
|
+
return await candidate.embedBatch(texts, options);
|
|
4041
4117
|
} catch (error) {
|
|
4042
4118
|
if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
|
|
4043
4119
|
warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
|
|
@@ -4045,7 +4121,7 @@ function wrapProvider(candidate) {
|
|
|
4045
4121
|
if (fallback) {
|
|
4046
4122
|
provider = wrapProvider(fallback);
|
|
4047
4123
|
warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
|
|
4048
|
-
return provider.embedBatch(texts);
|
|
4124
|
+
return provider.embedBatch(texts, options);
|
|
4049
4125
|
}
|
|
4050
4126
|
}
|
|
4051
4127
|
if (isTemporaryEmbeddingFailure(error)) {
|
|
@@ -4101,14 +4177,14 @@ async function getEmbeddingProvider(options = {}) {
|
|
|
4101
4177
|
return provider;
|
|
4102
4178
|
}
|
|
4103
4179
|
if (mode === "api") {
|
|
4104
|
-
const initialized = await createAPIProvider();
|
|
4180
|
+
const initialized = await createAPIProvider(options);
|
|
4105
4181
|
if (!initialized) return null;
|
|
4106
4182
|
provider = wrapProvider(initialized);
|
|
4107
4183
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4108
4184
|
return provider;
|
|
4109
4185
|
}
|
|
4110
4186
|
if (hasAPIEmbeddingConfig()) {
|
|
4111
|
-
const initialized = await createAPIProvider();
|
|
4187
|
+
const initialized = await createAPIProvider(options);
|
|
4112
4188
|
if (initialized) {
|
|
4113
4189
|
provider = wrapProvider(initialized);
|
|
4114
4190
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
@@ -8964,12 +9040,12 @@ import { createHash as createHash4 } from "crypto";
|
|
|
8964
9040
|
function digest(input) {
|
|
8965
9041
|
return createHash4("sha256").update(input).digest("hex").slice(0, 16);
|
|
8966
9042
|
}
|
|
8967
|
-
function normalizeCodePath(
|
|
8968
|
-
return
|
|
9043
|
+
function normalizeCodePath(path27) {
|
|
9044
|
+
return path27.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
|
|
8969
9045
|
}
|
|
8970
|
-
function makeCodeFileId(projectId,
|
|
9046
|
+
function makeCodeFileId(projectId, path27) {
|
|
8971
9047
|
return `file:${digest(`${projectId}
|
|
8972
|
-
${normalizeCodePath(
|
|
9048
|
+
${normalizeCodePath(path27)}`)}`;
|
|
8973
9049
|
}
|
|
8974
9050
|
function makeCodeSymbolId(input) {
|
|
8975
9051
|
return `symbol:${digest([
|
|
@@ -9443,8 +9519,8 @@ var init_store = __esm({
|
|
|
9443
9519
|
});
|
|
9444
9520
|
tx();
|
|
9445
9521
|
}
|
|
9446
|
-
getFile(projectId,
|
|
9447
|
-
const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(
|
|
9522
|
+
getFile(projectId, path27) {
|
|
9523
|
+
const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(path27));
|
|
9448
9524
|
return row ? rowToFile(row) : null;
|
|
9449
9525
|
}
|
|
9450
9526
|
listFiles(projectId) {
|
|
@@ -9679,30 +9755,30 @@ function normalizeCodeGraphExcludePatterns(exclude) {
|
|
|
9679
9755
|
...(exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean)
|
|
9680
9756
|
])];
|
|
9681
9757
|
}
|
|
9682
|
-
function isCodeGraphExcludedPath(
|
|
9683
|
-
const normalized = normalizeCodePath2(
|
|
9758
|
+
function isCodeGraphExcludedPath(path27, exclude) {
|
|
9759
|
+
const normalized = normalizeCodePath2(path27);
|
|
9684
9760
|
return normalizeCodeGraphExcludePatterns(exclude).some((pattern) => matchesPattern(normalized, normalizeCodePath2(pattern)));
|
|
9685
9761
|
}
|
|
9686
|
-
function normalizeCodePath2(
|
|
9687
|
-
return
|
|
9762
|
+
function normalizeCodePath2(path27) {
|
|
9763
|
+
return path27.replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
9688
9764
|
}
|
|
9689
|
-
function matchesPattern(
|
|
9765
|
+
function matchesPattern(path27, pattern) {
|
|
9690
9766
|
if (pattern.endsWith("/**")) {
|
|
9691
9767
|
const base = pattern.slice(0, -3);
|
|
9692
9768
|
if (base.startsWith("**/")) {
|
|
9693
9769
|
const suffix = base.slice(3);
|
|
9694
|
-
return
|
|
9770
|
+
return path27 === suffix || path27.endsWith(`/${suffix}`) || path27.includes(`/${suffix}/`);
|
|
9695
9771
|
}
|
|
9696
9772
|
if (!base.includes("/")) {
|
|
9697
|
-
return
|
|
9773
|
+
return path27 === base || path27.startsWith(`${base}/`) || path27.includes(`/${base}/`);
|
|
9698
9774
|
}
|
|
9699
|
-
return
|
|
9775
|
+
return path27 === base || path27.startsWith(`${base}/`);
|
|
9700
9776
|
}
|
|
9701
9777
|
if (pattern.startsWith("**/")) {
|
|
9702
9778
|
const suffix = pattern.slice(3);
|
|
9703
|
-
return
|
|
9779
|
+
return path27 === suffix || path27.endsWith(`/${suffix}`);
|
|
9704
9780
|
}
|
|
9705
|
-
return
|
|
9781
|
+
return path27 === pattern || path27.startsWith(`${pattern}/`);
|
|
9706
9782
|
}
|
|
9707
9783
|
var DEFAULT_CODEGRAPH_EXCLUDES;
|
|
9708
9784
|
var init_exclude = __esm({
|
|
@@ -9747,12 +9823,12 @@ import { join as join4, relative } from "path";
|
|
|
9747
9823
|
function hashText(text) {
|
|
9748
9824
|
return createHash6("sha256").update(text).digest("hex");
|
|
9749
9825
|
}
|
|
9750
|
-
function extension(
|
|
9751
|
-
const index =
|
|
9752
|
-
return index === -1 ? "" :
|
|
9826
|
+
function extension(path27) {
|
|
9827
|
+
const index = path27.lastIndexOf(".");
|
|
9828
|
+
return index === -1 ? "" : path27.slice(index);
|
|
9753
9829
|
}
|
|
9754
|
-
function languageForPath(
|
|
9755
|
-
const ext = extension(
|
|
9830
|
+
function languageForPath(path27) {
|
|
9831
|
+
const ext = extension(path27);
|
|
9756
9832
|
return LANGUAGE_BY_EXTENSION.get(ext) ?? "unknown";
|
|
9757
9833
|
}
|
|
9758
9834
|
function resolveMaxFileBytes(value) {
|
|
@@ -10233,7 +10309,7 @@ function resolveObservationCodeRefs(obs, lookup) {
|
|
|
10233
10309
|
}
|
|
10234
10310
|
function createStoreLookup(store, projectId) {
|
|
10235
10311
|
return {
|
|
10236
|
-
findFile: (
|
|
10312
|
+
findFile: (path27) => store.getFile(projectId, path27) ?? void 0,
|
|
10237
10313
|
findSymbols: (names, hintedFileIds) => store.findSymbolsByNames(projectId, names, hintedFileIds)
|
|
10238
10314
|
};
|
|
10239
10315
|
}
|
|
@@ -10246,7 +10322,7 @@ function createSnapshotLookup(files, symbols) {
|
|
|
10246
10322
|
symbolsByName.set(symbol.name, group);
|
|
10247
10323
|
}
|
|
10248
10324
|
return {
|
|
10249
|
-
findFile: (
|
|
10325
|
+
findFile: (path27) => filesByPath.get(normalizeCodePath(path27)),
|
|
10250
10326
|
findSymbols: (names, hintedFileIds) => {
|
|
10251
10327
|
const hinted = new Set(hintedFileIds);
|
|
10252
10328
|
const found = [];
|
|
@@ -11863,8 +11939,8 @@ __export(task_lens_exports, {
|
|
|
11863
11939
|
scoreLensSource: () => scoreLensSource,
|
|
11864
11940
|
shouldShowLensSource: () => shouldShowLensSource
|
|
11865
11941
|
});
|
|
11866
|
-
function normalizePath2(
|
|
11867
|
-
return
|
|
11942
|
+
function normalizePath2(path27) {
|
|
11943
|
+
return path27.replace(/\\/g, "/");
|
|
11868
11944
|
}
|
|
11869
11945
|
function tokenize2(text) {
|
|
11870
11946
|
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
@@ -11926,8 +12002,8 @@ function isContinuationTask(task) {
|
|
|
11926
12002
|
(keyword) => containsTaskKeyword(normalized, keyword)
|
|
11927
12003
|
);
|
|
11928
12004
|
}
|
|
11929
|
-
function pathKindScore(
|
|
11930
|
-
const normalized = normalizePath2(
|
|
12005
|
+
function pathKindScore(path27, lens) {
|
|
12006
|
+
const normalized = normalizePath2(path27).toLowerCase();
|
|
11931
12007
|
const name = normalized.split("/").pop() ?? normalized;
|
|
11932
12008
|
const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
|
|
11933
12009
|
const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
|
|
@@ -11975,8 +12051,8 @@ function sourceTaskMatchScore(source, task) {
|
|
|
11975
12051
|
if (matches === 1) return 24;
|
|
11976
12052
|
return 0;
|
|
11977
12053
|
}
|
|
11978
|
-
function fallbackPathRank(
|
|
11979
|
-
const normalized = normalizePath2(
|
|
12054
|
+
function fallbackPathRank(path27) {
|
|
12055
|
+
const normalized = normalizePath2(path27);
|
|
11980
12056
|
if (normalized.startsWith("src/")) return 0;
|
|
11981
12057
|
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
|
|
11982
12058
|
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
@@ -11985,11 +12061,11 @@ function fallbackPathRank(path26) {
|
|
|
11985
12061
|
}
|
|
11986
12062
|
function rankLensPaths(paths, lens, task) {
|
|
11987
12063
|
const tokens = tokenize2(task ?? "");
|
|
11988
|
-
return [...new Set(paths.map(normalizePath2))].map((
|
|
11989
|
-
path:
|
|
12064
|
+
return [...new Set(paths.map(normalizePath2))].map((path27, index) => ({
|
|
12065
|
+
path: path27,
|
|
11990
12066
|
index,
|
|
11991
|
-
score: pathKindScore(
|
|
11992
|
-
fallback: fallbackPathRank(
|
|
12067
|
+
score: pathKindScore(path27, lens) + tokenScore(path27, tokens),
|
|
12068
|
+
fallback: fallbackPathRank(path27)
|
|
11993
12069
|
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
11994
12070
|
}
|
|
11995
12071
|
function lensPathCandidates(lens) {
|
|
@@ -16241,6 +16317,827 @@ var init_session = __esm({
|
|
|
16241
16317
|
}
|
|
16242
16318
|
});
|
|
16243
16319
|
|
|
16320
|
+
// src/memory/owner.ts
|
|
16321
|
+
var owner_exports = {};
|
|
16322
|
+
__export(owner_exports, {
|
|
16323
|
+
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
16324
|
+
});
|
|
16325
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
16326
|
+
import { promises as fs15 } from "fs";
|
|
16327
|
+
import path19 from "path";
|
|
16328
|
+
function ownerPath(dataDir) {
|
|
16329
|
+
const resolved = path19.resolve(dataDir);
|
|
16330
|
+
const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
|
|
16331
|
+
return path19.join(home, "memorix-user.json");
|
|
16332
|
+
}
|
|
16333
|
+
function normalizedOwnerId(value) {
|
|
16334
|
+
const trimmed = value?.trim();
|
|
16335
|
+
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
16336
|
+
}
|
|
16337
|
+
async function readOwnerFile(filePath) {
|
|
16338
|
+
try {
|
|
16339
|
+
const value = JSON.parse(await fs15.readFile(filePath, "utf8"));
|
|
16340
|
+
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
16341
|
+
return id ? { id, source: "local-file" } : void 0;
|
|
16342
|
+
} catch {
|
|
16343
|
+
return void 0;
|
|
16344
|
+
}
|
|
16345
|
+
}
|
|
16346
|
+
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
16347
|
+
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
16348
|
+
if (configured) return { id: configured, source: "environment" };
|
|
16349
|
+
const filePath = ownerPath(dataDir);
|
|
16350
|
+
const existing = await readOwnerFile(filePath);
|
|
16351
|
+
if (existing || !options.create) return existing;
|
|
16352
|
+
await fs15.mkdir(path19.dirname(filePath), { recursive: true });
|
|
16353
|
+
const created = {
|
|
16354
|
+
version: 1,
|
|
16355
|
+
id: "local-" + randomUUID8(),
|
|
16356
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16357
|
+
};
|
|
16358
|
+
try {
|
|
16359
|
+
await fs15.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
16360
|
+
return { id: created.id, source: "local-file" };
|
|
16361
|
+
} catch (error) {
|
|
16362
|
+
if (error?.code !== "EEXIST") throw error;
|
|
16363
|
+
const concurrent = await readOwnerFile(filePath);
|
|
16364
|
+
if (concurrent) return concurrent;
|
|
16365
|
+
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
16366
|
+
}
|
|
16367
|
+
}
|
|
16368
|
+
var ID_PATTERN;
|
|
16369
|
+
var init_owner = __esm({
|
|
16370
|
+
"src/memory/owner.ts"() {
|
|
16371
|
+
"use strict";
|
|
16372
|
+
init_esm_shims();
|
|
16373
|
+
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
16374
|
+
}
|
|
16375
|
+
});
|
|
16376
|
+
|
|
16377
|
+
// src/memory/long-term-store.ts
|
|
16378
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
16379
|
+
function optionalText5(value) {
|
|
16380
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
16381
|
+
}
|
|
16382
|
+
function arrayValue2(value) {
|
|
16383
|
+
if (typeof value !== "string" || !value) return [];
|
|
16384
|
+
try {
|
|
16385
|
+
const parsed = JSON.parse(value);
|
|
16386
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
16387
|
+
} catch {
|
|
16388
|
+
return [];
|
|
16389
|
+
}
|
|
16390
|
+
}
|
|
16391
|
+
function rowToMemory(row) {
|
|
16392
|
+
return {
|
|
16393
|
+
id: row.id,
|
|
16394
|
+
originProjectId: row.originProjectId,
|
|
16395
|
+
ownerId: row.ownerId,
|
|
16396
|
+
scope: row.scope,
|
|
16397
|
+
kind: row.kind,
|
|
16398
|
+
state: row.state,
|
|
16399
|
+
portability: row.portability,
|
|
16400
|
+
title: row.title,
|
|
16401
|
+
content: row.content,
|
|
16402
|
+
facts: arrayValue2(row.factsJson),
|
|
16403
|
+
tags: arrayValue2(row.tagsJson),
|
|
16404
|
+
...optionalText5(row.applicability) ? { applicability: row.applicability } : {},
|
|
16405
|
+
origin: row.origin,
|
|
16406
|
+
createdAt: row.createdAt,
|
|
16407
|
+
updatedAt: row.updatedAt,
|
|
16408
|
+
...optionalText5(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
16409
|
+
...optionalText5(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
16410
|
+
...optionalText5(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
16411
|
+
...optionalText5(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
16412
|
+
...optionalText5(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
16413
|
+
accessCount: Number(row.accessCount ?? 0),
|
|
16414
|
+
...optionalText5(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
16415
|
+
};
|
|
16416
|
+
}
|
|
16417
|
+
function rowToEvidence2(row) {
|
|
16418
|
+
return {
|
|
16419
|
+
id: row.id,
|
|
16420
|
+
memoryId: row.memoryId,
|
|
16421
|
+
kind: row.kind,
|
|
16422
|
+
referenceId: row.referenceId,
|
|
16423
|
+
relation: row.relation,
|
|
16424
|
+
...optionalText5(row.locator) ? { locator: row.locator } : {},
|
|
16425
|
+
...optionalText5(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
16426
|
+
createdAt: row.createdAt
|
|
16427
|
+
};
|
|
16428
|
+
}
|
|
16429
|
+
function rowToEvent2(row) {
|
|
16430
|
+
return {
|
|
16431
|
+
id: row.id,
|
|
16432
|
+
memoryId: row.memoryId,
|
|
16433
|
+
kind: row.kind,
|
|
16434
|
+
...optionalText5(row.fromState) ? { fromState: row.fromState } : {},
|
|
16435
|
+
...optionalText5(row.toState) ? { toState: row.toState } : {},
|
|
16436
|
+
...optionalText5(row.detail) ? { detail: row.detail } : {},
|
|
16437
|
+
createdAt: row.createdAt
|
|
16438
|
+
};
|
|
16439
|
+
}
|
|
16440
|
+
var LongTermMemoryStore;
|
|
16441
|
+
var init_long_term_store = __esm({
|
|
16442
|
+
"src/memory/long-term-store.ts"() {
|
|
16443
|
+
"use strict";
|
|
16444
|
+
init_esm_shims();
|
|
16445
|
+
init_sqlite_db();
|
|
16446
|
+
LongTermMemoryStore = class {
|
|
16447
|
+
db = null;
|
|
16448
|
+
async init(dataDir) {
|
|
16449
|
+
this.db = getDatabase(dataDir);
|
|
16450
|
+
}
|
|
16451
|
+
transaction(fn) {
|
|
16452
|
+
return this.db.transaction(fn)();
|
|
16453
|
+
}
|
|
16454
|
+
get(id) {
|
|
16455
|
+
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
16456
|
+
return row ? rowToMemory(row) : void 0;
|
|
16457
|
+
}
|
|
16458
|
+
list(options = {}) {
|
|
16459
|
+
const limit = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
16460
|
+
const clauses = [];
|
|
16461
|
+
const params = [];
|
|
16462
|
+
if (options.ownerId) {
|
|
16463
|
+
clauses.push("ownerId = ?");
|
|
16464
|
+
params.push(options.ownerId);
|
|
16465
|
+
}
|
|
16466
|
+
if (options.originProjectId) {
|
|
16467
|
+
clauses.push("originProjectId = ?");
|
|
16468
|
+
params.push(options.originProjectId);
|
|
16469
|
+
}
|
|
16470
|
+
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
16471
|
+
const rows = this.db.prepare(
|
|
16472
|
+
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
16473
|
+
).all(...params, limit);
|
|
16474
|
+
return rows.map(rowToMemory);
|
|
16475
|
+
}
|
|
16476
|
+
insert(memory) {
|
|
16477
|
+
this.db.prepare(`
|
|
16478
|
+
INSERT INTO long_term_memories (
|
|
16479
|
+
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
16480
|
+
title, content, factsJson, tagsJson, applicability, origin,
|
|
16481
|
+
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
16482
|
+
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
16483
|
+
) VALUES (
|
|
16484
|
+
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
16485
|
+
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
16486
|
+
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
16487
|
+
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
16488
|
+
)
|
|
16489
|
+
`).run({
|
|
16490
|
+
...memory,
|
|
16491
|
+
factsJson: JSON.stringify(memory.facts),
|
|
16492
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
16493
|
+
applicability: memory.applicability ?? null,
|
|
16494
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
16495
|
+
approvedAt: memory.approvedAt ?? null,
|
|
16496
|
+
archivedAt: memory.archivedAt ?? null,
|
|
16497
|
+
supersededBy: memory.supersededBy ?? null,
|
|
16498
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
16499
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
16500
|
+
});
|
|
16501
|
+
}
|
|
16502
|
+
update(memory) {
|
|
16503
|
+
this.db.prepare(`
|
|
16504
|
+
UPDATE long_term_memories SET
|
|
16505
|
+
scope = @scope,
|
|
16506
|
+
kind = @kind,
|
|
16507
|
+
state = @state,
|
|
16508
|
+
portability = @portability,
|
|
16509
|
+
title = @title,
|
|
16510
|
+
content = @content,
|
|
16511
|
+
factsJson = @factsJson,
|
|
16512
|
+
tagsJson = @tagsJson,
|
|
16513
|
+
applicability = @applicability,
|
|
16514
|
+
updatedAt = @updatedAt,
|
|
16515
|
+
qualifiedAt = @qualifiedAt,
|
|
16516
|
+
approvedAt = @approvedAt,
|
|
16517
|
+
archivedAt = @archivedAt,
|
|
16518
|
+
supersededBy = @supersededBy,
|
|
16519
|
+
lastValidatedAt = @lastValidatedAt,
|
|
16520
|
+
accessCount = @accessCount,
|
|
16521
|
+
lastAccessedAt = @lastAccessedAt
|
|
16522
|
+
WHERE id = @id
|
|
16523
|
+
`).run({
|
|
16524
|
+
...memory,
|
|
16525
|
+
factsJson: JSON.stringify(memory.facts),
|
|
16526
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
16527
|
+
applicability: memory.applicability ?? null,
|
|
16528
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
16529
|
+
approvedAt: memory.approvedAt ?? null,
|
|
16530
|
+
archivedAt: memory.archivedAt ?? null,
|
|
16531
|
+
supersededBy: memory.supersededBy ?? null,
|
|
16532
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
16533
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
16534
|
+
});
|
|
16535
|
+
}
|
|
16536
|
+
insertEvidence(input) {
|
|
16537
|
+
const evidence = {
|
|
16538
|
+
id: randomUUID9(),
|
|
16539
|
+
...input,
|
|
16540
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
16541
|
+
};
|
|
16542
|
+
this.db.prepare(`
|
|
16543
|
+
INSERT INTO long_term_memory_evidence (
|
|
16544
|
+
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
16545
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
16546
|
+
`).run(
|
|
16547
|
+
evidence.id,
|
|
16548
|
+
evidence.memoryId,
|
|
16549
|
+
evidence.kind,
|
|
16550
|
+
evidence.referenceId,
|
|
16551
|
+
evidence.relation,
|
|
16552
|
+
evidence.locator ?? null,
|
|
16553
|
+
evidence.capturedHash ?? null,
|
|
16554
|
+
evidence.createdAt
|
|
16555
|
+
);
|
|
16556
|
+
return evidence;
|
|
16557
|
+
}
|
|
16558
|
+
listEvidence(memoryId) {
|
|
16559
|
+
return this.db.prepare(`
|
|
16560
|
+
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
16561
|
+
`).all(memoryId).map(rowToEvidence2);
|
|
16562
|
+
}
|
|
16563
|
+
recordEvent(input) {
|
|
16564
|
+
const event = {
|
|
16565
|
+
id: randomUUID9(),
|
|
16566
|
+
...input,
|
|
16567
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
16568
|
+
};
|
|
16569
|
+
this.db.prepare(`
|
|
16570
|
+
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
16571
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
16572
|
+
`).run(
|
|
16573
|
+
event.id,
|
|
16574
|
+
event.memoryId,
|
|
16575
|
+
event.kind,
|
|
16576
|
+
event.fromState ?? null,
|
|
16577
|
+
event.toState ?? null,
|
|
16578
|
+
event.detail ?? null,
|
|
16579
|
+
event.createdAt
|
|
16580
|
+
);
|
|
16581
|
+
return event;
|
|
16582
|
+
}
|
|
16583
|
+
listEvents(memoryId) {
|
|
16584
|
+
return this.db.prepare(`
|
|
16585
|
+
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
16586
|
+
`).all(memoryId).map(rowToEvent2);
|
|
16587
|
+
}
|
|
16588
|
+
markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
16589
|
+
const unique2 = [...new Set(ids.filter(Boolean))];
|
|
16590
|
+
if (unique2.length === 0) return;
|
|
16591
|
+
const statement = this.db.prepare(`
|
|
16592
|
+
UPDATE long_term_memories
|
|
16593
|
+
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
16594
|
+
WHERE id = ?
|
|
16595
|
+
`);
|
|
16596
|
+
this.db.transaction(() => {
|
|
16597
|
+
for (const id of unique2) statement.run(at, id);
|
|
16598
|
+
})();
|
|
16599
|
+
}
|
|
16600
|
+
};
|
|
16601
|
+
}
|
|
16602
|
+
});
|
|
16603
|
+
|
|
16604
|
+
// src/memory/long-term-types.ts
|
|
16605
|
+
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
16606
|
+
var init_long_term_types = __esm({
|
|
16607
|
+
"src/memory/long-term-types.ts"() {
|
|
16608
|
+
"use strict";
|
|
16609
|
+
init_esm_shims();
|
|
16610
|
+
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
16611
|
+
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
16612
|
+
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
16613
|
+
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
16614
|
+
"observation",
|
|
16615
|
+
"claim",
|
|
16616
|
+
"workflow",
|
|
16617
|
+
"session",
|
|
16618
|
+
"checkpoint",
|
|
16619
|
+
"git",
|
|
16620
|
+
"code",
|
|
16621
|
+
"test",
|
|
16622
|
+
"run",
|
|
16623
|
+
"document",
|
|
16624
|
+
"manual",
|
|
16625
|
+
"user"
|
|
16626
|
+
];
|
|
16627
|
+
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
16628
|
+
"supports",
|
|
16629
|
+
"verifies",
|
|
16630
|
+
"derives",
|
|
16631
|
+
"user-confirmed"
|
|
16632
|
+
];
|
|
16633
|
+
}
|
|
16634
|
+
});
|
|
16635
|
+
|
|
16636
|
+
// src/memory/long-term.ts
|
|
16637
|
+
var long_term_exports = {};
|
|
16638
|
+
__export(long_term_exports, {
|
|
16639
|
+
approveLongTermMemory: () => approveLongTermMemory,
|
|
16640
|
+
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
16641
|
+
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
16642
|
+
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
16643
|
+
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
16644
|
+
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
16645
|
+
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
16646
|
+
listLongTermMemories: () => listLongTermMemories,
|
|
16647
|
+
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
16648
|
+
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
16649
|
+
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
16650
|
+
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
16651
|
+
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
16652
|
+
});
|
|
16653
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
16654
|
+
function isOneOf(value, values) {
|
|
16655
|
+
return typeof value === "string" && values.includes(value);
|
|
16656
|
+
}
|
|
16657
|
+
function compactText2(value, field, max = MAX_TEXT_LENGTH) {
|
|
16658
|
+
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
16659
|
+
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
16660
|
+
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
16661
|
+
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
16662
|
+
return safe;
|
|
16663
|
+
}
|
|
16664
|
+
function compactList(values, field) {
|
|
16665
|
+
if (!values) return [];
|
|
16666
|
+
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
16667
|
+
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
16668
|
+
}
|
|
16669
|
+
function normalizeEvidence(input) {
|
|
16670
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
16671
|
+
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
16672
|
+
}
|
|
16673
|
+
return input.map((evidence) => {
|
|
16674
|
+
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
16675
|
+
throw new Error("Long-term memory evidence kind is invalid.");
|
|
16676
|
+
}
|
|
16677
|
+
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
16678
|
+
throw new Error("Long-term memory evidence relation is invalid.");
|
|
16679
|
+
}
|
|
16680
|
+
return {
|
|
16681
|
+
kind: evidence.kind,
|
|
16682
|
+
referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
|
|
16683
|
+
relation: evidence.relation,
|
|
16684
|
+
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
|
|
16685
|
+
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
16686
|
+
};
|
|
16687
|
+
});
|
|
16688
|
+
}
|
|
16689
|
+
function assertScope(scope) {
|
|
16690
|
+
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
16691
|
+
}
|
|
16692
|
+
function assertKind(kind) {
|
|
16693
|
+
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
16694
|
+
}
|
|
16695
|
+
function assertPortability(value) {
|
|
16696
|
+
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
16697
|
+
throw new Error("Long-term memory portability is invalid.");
|
|
16698
|
+
}
|
|
16699
|
+
}
|
|
16700
|
+
function assertPortableEvidence(scope, portability, evidence) {
|
|
16701
|
+
if (portability !== "portable") return;
|
|
16702
|
+
if (scope !== "user") {
|
|
16703
|
+
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
16704
|
+
}
|
|
16705
|
+
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
16706
|
+
throw new Error("Portable user memory may only use manual or user-confirmed evidence; project code, Git, observation, test, session, claim, and workflow evidence must remain project-bound.");
|
|
16707
|
+
}
|
|
16708
|
+
}
|
|
16709
|
+
function requireTeamReader(scope, reader) {
|
|
16710
|
+
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
16711
|
+
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
16712
|
+
}
|
|
16713
|
+
}
|
|
16714
|
+
function matchesScope(memory, reader) {
|
|
16715
|
+
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
16716
|
+
if (memory.scope === "team") {
|
|
16717
|
+
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
16718
|
+
}
|
|
16719
|
+
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
16720
|
+
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
16721
|
+
}
|
|
16722
|
+
function canReadLongTermMemory(memory, reader) {
|
|
16723
|
+
return matchesScope(memory, reader);
|
|
16724
|
+
}
|
|
16725
|
+
function isEligibleLongTermMemory(memory) {
|
|
16726
|
+
return memory.state === "qualified" || memory.state === "approved";
|
|
16727
|
+
}
|
|
16728
|
+
function taskTerms(task) {
|
|
16729
|
+
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
16730
|
+
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
16731
|
+
}
|
|
16732
|
+
function kindFitScore(kind, task) {
|
|
16733
|
+
const normalized = task.toLocaleLowerCase("en-US");
|
|
16734
|
+
const hints = {
|
|
16735
|
+
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
16736
|
+
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
16737
|
+
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
16738
|
+
};
|
|
16739
|
+
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
16740
|
+
}
|
|
16741
|
+
function validationFreshnessScore(memory) {
|
|
16742
|
+
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
16743
|
+
if (!validatedAt) return 0;
|
|
16744
|
+
const time = Date.parse(validatedAt);
|
|
16745
|
+
if (!Number.isFinite(time)) return 0;
|
|
16746
|
+
const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
|
|
16747
|
+
if (ageDays <= 30) return 0.35;
|
|
16748
|
+
if (ageDays <= 90) return 0.15;
|
|
16749
|
+
return 0;
|
|
16750
|
+
}
|
|
16751
|
+
function scoreForTask(memory, task) {
|
|
16752
|
+
const terms = taskTerms(task);
|
|
16753
|
+
if (terms.length === 0) return void 0;
|
|
16754
|
+
const title = memory.title.toLocaleLowerCase("en-US");
|
|
16755
|
+
const content = memory.content.toLocaleLowerCase("en-US");
|
|
16756
|
+
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
16757
|
+
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
16758
|
+
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
16759
|
+
let score = memory.state === "approved" ? 0.5 : 0;
|
|
16760
|
+
const matched = [];
|
|
16761
|
+
for (const term of terms) {
|
|
16762
|
+
let termScore = 0;
|
|
16763
|
+
if (title.includes(term)) termScore += 5;
|
|
16764
|
+
if (tags.includes(term)) termScore += 4;
|
|
16765
|
+
if (applicability.includes(term)) termScore += 3;
|
|
16766
|
+
if (facts.includes(term)) termScore += 2;
|
|
16767
|
+
if (content.includes(term)) termScore += 1;
|
|
16768
|
+
if (termScore > 0) {
|
|
16769
|
+
score += termScore;
|
|
16770
|
+
matched.push(term);
|
|
16771
|
+
}
|
|
16772
|
+
}
|
|
16773
|
+
if (matched.length === 0) return void 0;
|
|
16774
|
+
score += kindFitScore(memory.kind, task);
|
|
16775
|
+
score += validationFreshnessScore(memory);
|
|
16776
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
16777
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
16778
|
+
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
16779
|
+
}
|
|
16780
|
+
function semanticText(memory) {
|
|
16781
|
+
return sanitizeCredentials([
|
|
16782
|
+
memory.title,
|
|
16783
|
+
memory.applicability,
|
|
16784
|
+
memory.tags.join(" "),
|
|
16785
|
+
memory.facts.join(" "),
|
|
16786
|
+
memory.content
|
|
16787
|
+
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
16788
|
+
}
|
|
16789
|
+
function cosineSimilarity(left, right) {
|
|
16790
|
+
if (left.length === 0 || left.length !== right.length) return 0;
|
|
16791
|
+
let dot = 0;
|
|
16792
|
+
let leftMagnitude = 0;
|
|
16793
|
+
let rightMagnitude = 0;
|
|
16794
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
16795
|
+
const a = left[index];
|
|
16796
|
+
const b = right[index];
|
|
16797
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
|
|
16798
|
+
dot += a * b;
|
|
16799
|
+
leftMagnitude += a * a;
|
|
16800
|
+
rightMagnitude += b * b;
|
|
16801
|
+
}
|
|
16802
|
+
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
16803
|
+
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
16804
|
+
}
|
|
16805
|
+
function semanticSelection(memory, similarity, task) {
|
|
16806
|
+
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
16807
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
16808
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
16809
|
+
return {
|
|
16810
|
+
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
16811
|
+
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
16812
|
+
};
|
|
16813
|
+
}
|
|
16814
|
+
async function resolveSemanticProvider(explicit) {
|
|
16815
|
+
if (explicit !== void 0) return explicit;
|
|
16816
|
+
try {
|
|
16817
|
+
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
16818
|
+
return await withTimeout(
|
|
16819
|
+
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
16820
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
16821
|
+
"Long-term semantic provider initialization"
|
|
16822
|
+
);
|
|
16823
|
+
} catch {
|
|
16824
|
+
return null;
|
|
16825
|
+
}
|
|
16826
|
+
}
|
|
16827
|
+
function createStore(dataDir) {
|
|
16828
|
+
const store = new LongTermMemoryStore();
|
|
16829
|
+
return store.init(dataDir).then(() => store);
|
|
16830
|
+
}
|
|
16831
|
+
async function createManualLongTermMemory(input) {
|
|
16832
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
16833
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
16834
|
+
assertScope(input.scope);
|
|
16835
|
+
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
|
|
16836
|
+
return createLongTermMemoryWithDataDir({
|
|
16837
|
+
dataDir: input.dataDir,
|
|
16838
|
+
originProjectId: input.projectId,
|
|
16839
|
+
ownerId: owner.id,
|
|
16840
|
+
scope: input.scope,
|
|
16841
|
+
kind: input.kind,
|
|
16842
|
+
portability: input.portability,
|
|
16843
|
+
title: input.title,
|
|
16844
|
+
content: input.content,
|
|
16845
|
+
facts: input.facts,
|
|
16846
|
+
tags: input.tags,
|
|
16847
|
+
applicability: input.applicability,
|
|
16848
|
+
origin: "manual",
|
|
16849
|
+
evidence,
|
|
16850
|
+
reader: input.reader
|
|
16851
|
+
});
|
|
16852
|
+
}
|
|
16853
|
+
async function promoteObservationToLongTermMemory(input) {
|
|
16854
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
16855
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
16856
|
+
return createLongTermMemoryWithDataDir({
|
|
16857
|
+
dataDir: input.dataDir,
|
|
16858
|
+
originProjectId: input.observation.projectId,
|
|
16859
|
+
ownerId: owner.id,
|
|
16860
|
+
scope: input.scope,
|
|
16861
|
+
kind: input.kind,
|
|
16862
|
+
portability: "project-bound",
|
|
16863
|
+
title: input.observation.title,
|
|
16864
|
+
content: input.observation.narrative,
|
|
16865
|
+
facts: input.observation.facts,
|
|
16866
|
+
tags: input.tags ?? input.observation.concepts,
|
|
16867
|
+
applicability: input.applicability,
|
|
16868
|
+
origin: input.observation.source === "git" ? "git" : "agent",
|
|
16869
|
+
evidence: [{
|
|
16870
|
+
kind: "observation",
|
|
16871
|
+
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
16872
|
+
relation: "derives"
|
|
16873
|
+
}],
|
|
16874
|
+
reader: input.reader
|
|
16875
|
+
});
|
|
16876
|
+
}
|
|
16877
|
+
async function createLongTermMemoryCandidate(input) {
|
|
16878
|
+
return createLongTermMemoryWithDataDir(input);
|
|
16879
|
+
}
|
|
16880
|
+
async function createLongTermMemoryWithDataDir(input) {
|
|
16881
|
+
assertScope(input.scope);
|
|
16882
|
+
assertKind(input.kind);
|
|
16883
|
+
const portability = input.portability ?? "project-bound";
|
|
16884
|
+
assertPortability(portability);
|
|
16885
|
+
requireTeamReader(input.scope, input.reader);
|
|
16886
|
+
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
16887
|
+
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
16888
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
16889
|
+
const memory = {
|
|
16890
|
+
id: randomUUID10(),
|
|
16891
|
+
originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
|
|
16892
|
+
ownerId: compactText2(input.ownerId, "owner id", 160),
|
|
16893
|
+
scope: input.scope,
|
|
16894
|
+
kind: input.kind,
|
|
16895
|
+
state: "candidate",
|
|
16896
|
+
portability,
|
|
16897
|
+
title: compactText2(input.title, "title", 500),
|
|
16898
|
+
content: compactText2(input.content, "content"),
|
|
16899
|
+
facts: compactList(input.facts, "facts"),
|
|
16900
|
+
tags: compactList(input.tags, "tags"),
|
|
16901
|
+
...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
|
|
16902
|
+
origin: input.origin ?? "manual",
|
|
16903
|
+
createdAt: now3,
|
|
16904
|
+
updatedAt: now3,
|
|
16905
|
+
accessCount: 0
|
|
16906
|
+
};
|
|
16907
|
+
const store = await createStore(input.dataDir);
|
|
16908
|
+
const evidence = store.transaction(() => {
|
|
16909
|
+
store.insert(memory);
|
|
16910
|
+
const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
16911
|
+
store.recordEvent({
|
|
16912
|
+
memoryId: memory.id,
|
|
16913
|
+
kind: "created",
|
|
16914
|
+
toState: "candidate",
|
|
16915
|
+
detail: "Created as a source-backed long-term memory candidate.",
|
|
16916
|
+
createdAt: now3
|
|
16917
|
+
});
|
|
16918
|
+
return written;
|
|
16919
|
+
});
|
|
16920
|
+
return { memory, evidence };
|
|
16921
|
+
}
|
|
16922
|
+
function transitionDetail(value) {
|
|
16923
|
+
return compactText2(value, "transition reason", 1e3);
|
|
16924
|
+
}
|
|
16925
|
+
async function transitionMemory(input) {
|
|
16926
|
+
const store = await createStore(input.dataDir);
|
|
16927
|
+
return store.transaction(() => {
|
|
16928
|
+
const current = store.get(input.id);
|
|
16929
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
16930
|
+
if (current.state !== input.expected) {
|
|
16931
|
+
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
16932
|
+
}
|
|
16933
|
+
const evidence = store.listEvidence(current.id);
|
|
16934
|
+
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
16935
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
16936
|
+
const next = {
|
|
16937
|
+
...current,
|
|
16938
|
+
state: input.next,
|
|
16939
|
+
updatedAt: now3,
|
|
16940
|
+
lastValidatedAt: now3,
|
|
16941
|
+
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
16942
|
+
};
|
|
16943
|
+
store.update(next);
|
|
16944
|
+
store.recordEvent({
|
|
16945
|
+
memoryId: next.id,
|
|
16946
|
+
kind: input.event,
|
|
16947
|
+
fromState: current.state,
|
|
16948
|
+
toState: next.state,
|
|
16949
|
+
detail: transitionDetail(input.reason),
|
|
16950
|
+
createdAt: now3
|
|
16951
|
+
});
|
|
16952
|
+
return next;
|
|
16953
|
+
});
|
|
16954
|
+
}
|
|
16955
|
+
async function qualifyLongTermMemory(input) {
|
|
16956
|
+
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
16957
|
+
}
|
|
16958
|
+
async function approveLongTermMemory(input) {
|
|
16959
|
+
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
16960
|
+
}
|
|
16961
|
+
async function archiveLongTermMemory(input) {
|
|
16962
|
+
const store = await createStore(input.dataDir);
|
|
16963
|
+
return store.transaction(() => {
|
|
16964
|
+
const current = store.get(input.id);
|
|
16965
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
16966
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
16967
|
+
throw new Error("Long-term memory is already inactive.");
|
|
16968
|
+
}
|
|
16969
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
16970
|
+
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
16971
|
+
store.update(next);
|
|
16972
|
+
store.recordEvent({
|
|
16973
|
+
memoryId: next.id,
|
|
16974
|
+
kind: "archived",
|
|
16975
|
+
fromState: current.state,
|
|
16976
|
+
toState: "archived",
|
|
16977
|
+
detail: transitionDetail(input.reason),
|
|
16978
|
+
createdAt: now3
|
|
16979
|
+
});
|
|
16980
|
+
return next;
|
|
16981
|
+
});
|
|
16982
|
+
}
|
|
16983
|
+
async function supersedeLongTermMemory(input) {
|
|
16984
|
+
const store = await createStore(input.dataDir);
|
|
16985
|
+
return store.transaction(() => {
|
|
16986
|
+
const current = store.get(input.id);
|
|
16987
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
16988
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
16989
|
+
throw new Error("Long-term memory is already inactive.");
|
|
16990
|
+
}
|
|
16991
|
+
if (current.id === input.supersededBy) {
|
|
16992
|
+
throw new Error("Long-term memory cannot supersede itself.");
|
|
16993
|
+
}
|
|
16994
|
+
const replacement = store.get(input.supersededBy);
|
|
16995
|
+
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
16996
|
+
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
16997
|
+
}
|
|
16998
|
+
if (replacement.scope !== current.scope) {
|
|
16999
|
+
throw new Error("Replacement long-term memory must use the same scope.");
|
|
17000
|
+
}
|
|
17001
|
+
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
17002
|
+
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
17003
|
+
}
|
|
17004
|
+
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
17005
|
+
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
17006
|
+
}
|
|
17007
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
17008
|
+
const next = {
|
|
17009
|
+
...current,
|
|
17010
|
+
state: "superseded",
|
|
17011
|
+
supersededBy: replacement.id,
|
|
17012
|
+
updatedAt: now3
|
|
17013
|
+
};
|
|
17014
|
+
store.update(next);
|
|
17015
|
+
store.recordEvent({
|
|
17016
|
+
memoryId: next.id,
|
|
17017
|
+
kind: "superseded",
|
|
17018
|
+
fromState: current.state,
|
|
17019
|
+
toState: "superseded",
|
|
17020
|
+
detail: transitionDetail(input.reason),
|
|
17021
|
+
createdAt: now3
|
|
17022
|
+
});
|
|
17023
|
+
return next;
|
|
17024
|
+
});
|
|
17025
|
+
}
|
|
17026
|
+
async function listLongTermMemories(input) {
|
|
17027
|
+
const store = await createStore(input.dataDir);
|
|
17028
|
+
return store.list({ limit: input.limit ?? 100 }).filter((memory) => canReadLongTermMemory(memory, input.reader)).filter((memory) => input.includeInactive || memory.state !== "archived" && memory.state !== "superseded").map((memory) => ({ memory, evidence: store.listEvidence(memory.id) }));
|
|
17029
|
+
}
|
|
17030
|
+
async function getLongTermMemoryDetail(input) {
|
|
17031
|
+
const store = await createStore(input.dataDir);
|
|
17032
|
+
const memory = store.get(input.id);
|
|
17033
|
+
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
17034
|
+
throw new Error("Long-term memory was not found in the active scope.");
|
|
17035
|
+
}
|
|
17036
|
+
store.markAccess([memory.id]);
|
|
17037
|
+
return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
|
|
17038
|
+
}
|
|
17039
|
+
async function selectLongTermMemoriesForTask(input) {
|
|
17040
|
+
const task = input.task?.trim();
|
|
17041
|
+
if (!task) return [];
|
|
17042
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
17043
|
+
const reader = {
|
|
17044
|
+
projectId: input.projectId,
|
|
17045
|
+
...owner ? { ownerId: owner.id } : {},
|
|
17046
|
+
...input.agentId ? { agentId: input.agentId } : {},
|
|
17047
|
+
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
17048
|
+
};
|
|
17049
|
+
const store = await createStore(input.dataDir);
|
|
17050
|
+
const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
17051
|
+
let selected = eligible.map((memory) => {
|
|
17052
|
+
const ranked = scoreForTask(memory, task);
|
|
17053
|
+
return ranked ? {
|
|
17054
|
+
memory,
|
|
17055
|
+
evidence: store.listEvidence(memory.id),
|
|
17056
|
+
score: ranked.score,
|
|
17057
|
+
reason: ranked.reason
|
|
17058
|
+
} : void 0;
|
|
17059
|
+
}).filter((item) => Boolean(item)).sort((left, right) => right.score - left.score || right.memory.updatedAt.localeCompare(left.memory.updatedAt) || left.memory.id.localeCompare(right.memory.id));
|
|
17060
|
+
if (selected.length === 0 && eligible.length > 0) {
|
|
17061
|
+
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
17062
|
+
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
17063
|
+
if (provider2 && candidates.length > 0) {
|
|
17064
|
+
try {
|
|
17065
|
+
const vectors = await withTimeout(
|
|
17066
|
+
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
17067
|
+
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
17068
|
+
retry: false
|
|
17069
|
+
}),
|
|
17070
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
17071
|
+
"Long-term semantic retrieval"
|
|
17072
|
+
);
|
|
17073
|
+
const query = vectors[0];
|
|
17074
|
+
if (Array.isArray(query)) {
|
|
17075
|
+
selected = candidates.map((memory, index) => {
|
|
17076
|
+
const vector = vectors[index + 1];
|
|
17077
|
+
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
17078
|
+
return ranked ? {
|
|
17079
|
+
memory,
|
|
17080
|
+
evidence: store.listEvidence(memory.id),
|
|
17081
|
+
score: ranked.score,
|
|
17082
|
+
reason: ranked.reason
|
|
17083
|
+
} : void 0;
|
|
17084
|
+
}).filter((item) => Boolean(item)).sort((left, right) => right.score - left.score || right.memory.updatedAt.localeCompare(left.memory.updatedAt) || left.memory.id.localeCompare(right.memory.id));
|
|
17085
|
+
}
|
|
17086
|
+
} catch {
|
|
17087
|
+
}
|
|
17088
|
+
}
|
|
17089
|
+
}
|
|
17090
|
+
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
17091
|
+
store.markAccess(selected.map((item) => item.memory.id));
|
|
17092
|
+
return selected;
|
|
17093
|
+
}
|
|
17094
|
+
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
17095
|
+
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
17096
|
+
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
17097
|
+
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
17098
|
+
}
|
|
17099
|
+
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
17100
|
+
var init_long_term = __esm({
|
|
17101
|
+
"src/memory/long-term.ts"() {
|
|
17102
|
+
"use strict";
|
|
17103
|
+
init_esm_shims();
|
|
17104
|
+
init_token_budget();
|
|
17105
|
+
init_timeout();
|
|
17106
|
+
init_secret_filter();
|
|
17107
|
+
init_owner();
|
|
17108
|
+
init_long_term_store();
|
|
17109
|
+
init_long_term_types();
|
|
17110
|
+
MAX_TEXT_LENGTH = 12e3;
|
|
17111
|
+
MAX_LIST_ITEMS = 40;
|
|
17112
|
+
MAX_SEMANTIC_CANDIDATES = 48;
|
|
17113
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
17114
|
+
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
17115
|
+
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
17116
|
+
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
17117
|
+
"the",
|
|
17118
|
+
"and",
|
|
17119
|
+
"for",
|
|
17120
|
+
"with",
|
|
17121
|
+
"from",
|
|
17122
|
+
"that",
|
|
17123
|
+
"this",
|
|
17124
|
+
"into",
|
|
17125
|
+
"when",
|
|
17126
|
+
"what",
|
|
17127
|
+
"\u5982\u4F55",
|
|
17128
|
+
"\u4EC0\u4E48",
|
|
17129
|
+
"\u8FD9\u4E2A",
|
|
17130
|
+
"\u90A3\u4E2A",
|
|
17131
|
+
"\u6211\u4EEC",
|
|
17132
|
+
"\u9879\u76EE",
|
|
17133
|
+
"\u8FDB\u884C",
|
|
17134
|
+
"\u9700\u8981",
|
|
17135
|
+
"\u7EE7\u7EED",
|
|
17136
|
+
"\u5904\u7406"
|
|
17137
|
+
]);
|
|
17138
|
+
}
|
|
17139
|
+
});
|
|
17140
|
+
|
|
16244
17141
|
// src/knowledge/workset.ts
|
|
16245
17142
|
function unique(values) {
|
|
16246
17143
|
return [...new Set(values.filter(Boolean))];
|
|
@@ -16344,6 +17241,7 @@ function receiptOmissionKind(raw) {
|
|
|
16344
17241
|
if (raw.includes("state")) return "code-state";
|
|
16345
17242
|
if (raw.includes("semantic")) return "semantic-code";
|
|
16346
17243
|
if (raw.includes("start")) return "start-here";
|
|
17244
|
+
if (raw.includes("durable-memory")) return "durable-memory";
|
|
16347
17245
|
if (raw.includes("memory")) return "memory";
|
|
16348
17246
|
if (raw.includes("claim")) return "claim";
|
|
16349
17247
|
if (raw.includes("knowledge-page")) return "knowledge-page";
|
|
@@ -16602,6 +17500,26 @@ function renderTaskWorksetPrompt(input) {
|
|
|
16602
17500
|
});
|
|
16603
17501
|
}
|
|
16604
17502
|
}
|
|
17503
|
+
if (input.durableMemory.length > 0) {
|
|
17504
|
+
appendLine(lines, "", maxTokens, omitted, "durable-memory-heading");
|
|
17505
|
+
appendLine(lines, "Durable memory", maxTokens, omitted, "durable-memory-heading");
|
|
17506
|
+
for (const memory of input.durableMemory.slice(0, 3)) {
|
|
17507
|
+
appendLine(
|
|
17508
|
+
lines,
|
|
17509
|
+
"- " + memory.kind + " (" + memory.scope + ", " + memory.state + "; ref durable:" + memory.id + "): " + memory.summary,
|
|
17510
|
+
maxTokens,
|
|
17511
|
+
omitted,
|
|
17512
|
+
"durable-memory",
|
|
17513
|
+
selected,
|
|
17514
|
+
{
|
|
17515
|
+
kind: "durable-memory",
|
|
17516
|
+
id: "durable:" + memory.id,
|
|
17517
|
+
reason: memory.reason,
|
|
17518
|
+
trust: "source-backed"
|
|
17519
|
+
}
|
|
17520
|
+
);
|
|
17521
|
+
}
|
|
17522
|
+
}
|
|
16605
17523
|
if (input.workflows.length > 0) {
|
|
16606
17524
|
appendLine(lines, "", maxTokens, omitted, "workflow-heading");
|
|
16607
17525
|
appendLine(lines, "Project workflow", maxTokens, omitted, "workflow-heading");
|
|
@@ -16671,6 +17589,32 @@ async function buildTaskWorkset(input) {
|
|
|
16671
17589
|
evidenceRefs: evidenceByClaim.get(claim.id).map((item) => item.id),
|
|
16672
17590
|
reason: selection.reasons[claim.id] ?? "source-qualified task match"
|
|
16673
17591
|
}));
|
|
17592
|
+
let durableMemory = [];
|
|
17593
|
+
let durableEvidence = [];
|
|
17594
|
+
if (task) {
|
|
17595
|
+
try {
|
|
17596
|
+
const selected = await selectLongTermMemoriesForTask({
|
|
17597
|
+
dataDir: input.dataDir,
|
|
17598
|
+
projectId: input.projectId,
|
|
17599
|
+
task,
|
|
17600
|
+
...input.reader?.agentId ? { agentId: input.reader.agentId } : {},
|
|
17601
|
+
...input.reader?.isTeamMember ? { isTeamMember: true } : {},
|
|
17602
|
+
limit: 3
|
|
17603
|
+
});
|
|
17604
|
+
durableMemory = selected.map((item) => ({
|
|
17605
|
+
id: item.memory.id,
|
|
17606
|
+
kind: item.memory.kind,
|
|
17607
|
+
scope: item.memory.scope,
|
|
17608
|
+
state: item.memory.state,
|
|
17609
|
+
title: item.memory.title,
|
|
17610
|
+
summary: renderLongTermMemorySummary(item.memory, 20),
|
|
17611
|
+
evidenceRefs: item.evidence.map((evidence) => evidence.id),
|
|
17612
|
+
reason: item.reason
|
|
17613
|
+
}));
|
|
17614
|
+
durableEvidence = selected.flatMap((item) => item.evidence);
|
|
17615
|
+
} catch {
|
|
17616
|
+
}
|
|
17617
|
+
}
|
|
16674
17618
|
let workspace;
|
|
16675
17619
|
let pages = [];
|
|
16676
17620
|
let workflows = [];
|
|
@@ -16710,7 +17654,10 @@ async function buildTaskWorkset(input) {
|
|
|
16710
17654
|
const evidenceIds = unique(selection.claims.flatMap((claim) => evidenceIdsForClaim(
|
|
16711
17655
|
claim,
|
|
16712
17656
|
evidenceByClaim.get(claim.id) ?? []
|
|
16713
|
-
))
|
|
17657
|
+
)).concat(
|
|
17658
|
+
durableMemory.map((memory) => "durable:" + memory.id),
|
|
17659
|
+
durableEvidence.map((evidence) => "durable-evidence:" + evidence.id)
|
|
17660
|
+
));
|
|
16714
17661
|
const verification = unique([
|
|
16715
17662
|
...workflows.flatMap((workflow) => workflow.verificationGates),
|
|
16716
17663
|
...input.verificationHints
|
|
@@ -16736,7 +17683,7 @@ async function buildTaskWorkset(input) {
|
|
|
16736
17683
|
} : {}
|
|
16737
17684
|
} : void 0;
|
|
16738
17685
|
const base = {
|
|
16739
|
-
version: "1.
|
|
17686
|
+
version: "1.3",
|
|
16740
17687
|
task,
|
|
16741
17688
|
lens: input.lens,
|
|
16742
17689
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
@@ -16749,6 +17696,7 @@ async function buildTaskWorkset(input) {
|
|
|
16749
17696
|
hiddenCautionMemoryCount: input.hiddenCautionMemoryCount ?? 0,
|
|
16750
17697
|
claims,
|
|
16751
17698
|
pages,
|
|
17699
|
+
durableMemory,
|
|
16752
17700
|
workflows,
|
|
16753
17701
|
cautions: normalizedCautions,
|
|
16754
17702
|
verification,
|
|
@@ -16765,7 +17713,7 @@ async function buildTaskWorkset(input) {
|
|
|
16765
17713
|
budget: { maxTokens }
|
|
16766
17714
|
});
|
|
16767
17715
|
const receipt = {
|
|
16768
|
-
version: "1.
|
|
17716
|
+
version: "1.3",
|
|
16769
17717
|
target: input.deliveryTarget ?? "project-context",
|
|
16770
17718
|
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
16771
17719
|
budget: {
|
|
@@ -16800,6 +17748,7 @@ var init_workset = __esm({
|
|
|
16800
17748
|
init_workspace();
|
|
16801
17749
|
init_workflow_store();
|
|
16802
17750
|
init_workflows();
|
|
17751
|
+
init_long_term();
|
|
16803
17752
|
CONTINUATION_DETAIL_TOKEN_BUDGET = 20;
|
|
16804
17753
|
}
|
|
16805
17754
|
});
|
|
@@ -16812,7 +17761,7 @@ __export(current_facts_exports, {
|
|
|
16812
17761
|
});
|
|
16813
17762
|
import { execFileSync } from "child_process";
|
|
16814
17763
|
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
16815
|
-
import
|
|
17764
|
+
import path20 from "path";
|
|
16816
17765
|
function readTextIfExists(filePath) {
|
|
16817
17766
|
try {
|
|
16818
17767
|
if (!existsSync12(filePath)) return null;
|
|
@@ -16822,7 +17771,7 @@ function readTextIfExists(filePath) {
|
|
|
16822
17771
|
}
|
|
16823
17772
|
}
|
|
16824
17773
|
function readPackageVersion(rootPath) {
|
|
16825
|
-
const text = readTextIfExists(
|
|
17774
|
+
const text = readTextIfExists(path20.join(rootPath, "package.json"));
|
|
16826
17775
|
if (!text) return void 0;
|
|
16827
17776
|
try {
|
|
16828
17777
|
const parsed = JSON.parse(text);
|
|
@@ -16832,7 +17781,7 @@ function readPackageVersion(rootPath) {
|
|
|
16832
17781
|
}
|
|
16833
17782
|
}
|
|
16834
17783
|
function readLatestChangelog(rootPath) {
|
|
16835
|
-
const text = readTextIfExists(
|
|
17784
|
+
const text = readTextIfExists(path20.join(rootPath, "CHANGELOG.md"));
|
|
16836
17785
|
if (!text) return void 0;
|
|
16837
17786
|
const match = text.match(/^##\s+\[?v?([^\]\s]+)\]?\s*(?:-\s*(\d{4}-\d{2}-\d{2}))?/m);
|
|
16838
17787
|
if (!match) return void 0;
|
|
@@ -16902,7 +17851,7 @@ function detectStaleProgressNotes(input) {
|
|
|
16902
17851
|
];
|
|
16903
17852
|
const notes = [];
|
|
16904
17853
|
for (const relPath of candidates) {
|
|
16905
|
-
const content = readTextIfExists(
|
|
17854
|
+
const content = readTextIfExists(path20.join(input.rootPath, relPath));
|
|
16906
17855
|
if (!content) continue;
|
|
16907
17856
|
const parsed = parseProgressNote(content);
|
|
16908
17857
|
const olderThanChangelog = compareDateStrings(parsed.lastUpdated, input.latestChangelog?.date);
|
|
@@ -17153,9 +18102,9 @@ async function inspectExternalCodeGraph(input) {
|
|
|
17153
18102
|
return { health, quality: providerQuality({ mode, health }) };
|
|
17154
18103
|
}
|
|
17155
18104
|
function safeRelativePath(projectRoot, value, exclude) {
|
|
17156
|
-
const
|
|
17157
|
-
if (!
|
|
17158
|
-
const absolute = resolve(projectRoot,
|
|
18105
|
+
const path27 = safeText(value, 1024);
|
|
18106
|
+
if (!path27 || isAbsolute(path27)) return void 0;
|
|
18107
|
+
const absolute = resolve(projectRoot, path27);
|
|
17159
18108
|
const fromRoot = relative2(projectRoot, absolute);
|
|
17160
18109
|
if (!fromRoot || fromRoot === ".." || fromRoot.startsWith("..\\") || fromRoot.startsWith("../") || isAbsolute(fromRoot)) return void 0;
|
|
17161
18110
|
const normalized = normalizeCodePath(fromRoot);
|
|
@@ -17430,15 +18379,15 @@ function countLanguages(files) {
|
|
|
17430
18379
|
}
|
|
17431
18380
|
return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
|
|
17432
18381
|
}
|
|
17433
|
-
function suggestedReadRank(
|
|
17434
|
-
const normalized =
|
|
18382
|
+
function suggestedReadRank(path27) {
|
|
18383
|
+
const normalized = path27.replace(/\\/g, "/");
|
|
17435
18384
|
if (normalized.startsWith("src/")) return 0;
|
|
17436
18385
|
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
|
|
17437
18386
|
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
17438
18387
|
return 3;
|
|
17439
18388
|
}
|
|
17440
18389
|
function compactSuggestedReads(paths, limit = 8, exclude) {
|
|
17441
|
-
return uniq(paths).filter((
|
|
18390
|
+
return uniq(paths).filter((path27) => !isCodeGraphExcludedPath(path27, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
|
|
17442
18391
|
}
|
|
17443
18392
|
function collectGraph(store, projectId, observations2, exclude) {
|
|
17444
18393
|
const files = store.listFiles(projectId);
|
|
@@ -17478,14 +18427,14 @@ function collectGraph(store, projectId, observations2, exclude) {
|
|
|
17478
18427
|
}
|
|
17479
18428
|
if (files.length === 0) {
|
|
17480
18429
|
for (const observation of observations2) {
|
|
17481
|
-
for (const
|
|
17482
|
-
if (!
|
|
17483
|
-
suggestedReads.push(
|
|
18430
|
+
for (const path27 of observation.filesModified ?? []) {
|
|
18431
|
+
if (!path27 || isCodeGraphExcludedPath(path27, exclude)) continue;
|
|
18432
|
+
suggestedReads.push(path27);
|
|
17484
18433
|
sources.push({
|
|
17485
18434
|
observationId: observation.id,
|
|
17486
18435
|
title: observation.title,
|
|
17487
18436
|
type: observation.type,
|
|
17488
|
-
path:
|
|
18437
|
+
path: path27,
|
|
17489
18438
|
status: "unbound",
|
|
17490
18439
|
reason: "Recorded file hint; Code Memory refresh is pending."
|
|
17491
18440
|
});
|
|
@@ -17554,8 +18503,8 @@ var compaction_checkpoint_store_exports = {};
|
|
|
17554
18503
|
__export(compaction_checkpoint_store_exports, {
|
|
17555
18504
|
CompactionCheckpointStore: () => CompactionCheckpointStore
|
|
17556
18505
|
});
|
|
17557
|
-
import { createHash as createHash13, randomUUID as
|
|
17558
|
-
function
|
|
18506
|
+
import { createHash as createHash13, randomUUID as randomUUID11 } from "crypto";
|
|
18507
|
+
function optionalText6(value) {
|
|
17559
18508
|
return typeof value === "string" && value ? value : void 0;
|
|
17560
18509
|
}
|
|
17561
18510
|
function optionalNumber2(value) {
|
|
@@ -17609,15 +18558,15 @@ function rowToCheckpoint(row) {
|
|
|
17609
18558
|
reason: row.reason,
|
|
17610
18559
|
sourceEvent: row.source_event,
|
|
17611
18560
|
sourceKey: row.source_key,
|
|
17612
|
-
...
|
|
18561
|
+
...optionalText6(row.summary) ? { summary: row.summary } : {},
|
|
17613
18562
|
...optionalNumber2(row.tokens_before) !== void 0 ? { tokensBefore: Number(row.tokens_before) } : {},
|
|
17614
|
-
...
|
|
18563
|
+
...optionalText6(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
|
|
17615
18564
|
...parseDetails(row.details_json) ? { details: parseDetails(row.details_json) } : {},
|
|
17616
18565
|
transcriptAvailable: Boolean(row.transcript_available),
|
|
17617
18566
|
status: row.status,
|
|
17618
18567
|
preCapturedAt: row.pre_captured_at,
|
|
17619
|
-
...
|
|
17620
|
-
...
|
|
18568
|
+
...optionalText6(row.completed_at) ? { completedAt: row.completed_at } : {},
|
|
18569
|
+
...optionalText6(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
|
|
17621
18570
|
deliveryCount: Number(row.delivery_count ?? 0),
|
|
17622
18571
|
createdAt: row.created_at,
|
|
17623
18572
|
updatedAt: row.updated_at
|
|
@@ -17655,7 +18604,7 @@ var init_compaction_checkpoint_store = __esm({
|
|
|
17655
18604
|
if (pending) return rowToCheckpoint(pending);
|
|
17656
18605
|
const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
17657
18606
|
const checkpoint = {
|
|
17658
|
-
id:
|
|
18607
|
+
id: randomUUID11(),
|
|
17659
18608
|
projectId: input.projectId,
|
|
17660
18609
|
sessionId: input.sessionId,
|
|
17661
18610
|
agent: input.agent,
|
|
@@ -17663,7 +18612,7 @@ var init_compaction_checkpoint_store = __esm({
|
|
|
17663
18612
|
captureKind: "preflight",
|
|
17664
18613
|
reason: safeReason(input.reason),
|
|
17665
18614
|
sourceEvent: input.sourceEvent,
|
|
17666
|
-
sourceKey: `pre:${
|
|
18615
|
+
sourceKey: `pre:${randomUUID11()}`,
|
|
17667
18616
|
transcriptAvailable: Boolean(input.transcriptAvailable),
|
|
17668
18617
|
status: "active",
|
|
17669
18618
|
preCapturedAt: now3,
|
|
@@ -17729,7 +18678,7 @@ var init_compaction_checkpoint_store = __esm({
|
|
|
17729
18678
|
return this.get(checkpoint2.id);
|
|
17730
18679
|
}
|
|
17731
18680
|
const checkpoint = {
|
|
17732
|
-
id:
|
|
18681
|
+
id: randomUUID11(),
|
|
17733
18682
|
projectId: input.projectId,
|
|
17734
18683
|
sessionId: input.sessionId,
|
|
17735
18684
|
agent: input.agent,
|
|
@@ -17864,7 +18813,7 @@ __export(auto_context_exports, {
|
|
|
17864
18813
|
formatLegacyAutoProjectContextPrompt: () => formatLegacyAutoProjectContextPrompt
|
|
17865
18814
|
});
|
|
17866
18815
|
import { existsSync as existsSync14, statSync as statSync3 } from "fs";
|
|
17867
|
-
import
|
|
18816
|
+
import path21 from "path";
|
|
17868
18817
|
function activeProjectObservations(observations2, projectId) {
|
|
17869
18818
|
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
17870
18819
|
}
|
|
@@ -18099,6 +19048,7 @@ async function buildAutoProjectContext(input) {
|
|
|
18099
19048
|
stale: overview.freshness.stale
|
|
18100
19049
|
},
|
|
18101
19050
|
runtimeCautions,
|
|
19051
|
+
...input.reader ? { reader: input.reader } : {},
|
|
18102
19052
|
...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
|
|
18103
19053
|
});
|
|
18104
19054
|
return {
|
|
@@ -18143,7 +19093,7 @@ function dedupeSourcesByObservation(sources) {
|
|
|
18143
19093
|
function existingLensCandidates(rootPath, lens) {
|
|
18144
19094
|
const out = [];
|
|
18145
19095
|
for (const candidate of lensPathCandidates(lens)) {
|
|
18146
|
-
const absolute =
|
|
19096
|
+
const absolute = path21.join(rootPath, candidate);
|
|
18147
19097
|
try {
|
|
18148
19098
|
if (!existsSync14(absolute)) continue;
|
|
18149
19099
|
const stat = statSync3(absolute);
|
|
@@ -18253,7 +19203,7 @@ function formatAutoProjectContextSummary(context) {
|
|
|
18253
19203
|
"Start here"
|
|
18254
19204
|
].filter(Boolean);
|
|
18255
19205
|
if (startHere.length > 0) {
|
|
18256
|
-
startHere.forEach((
|
|
19206
|
+
startHere.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
|
|
18257
19207
|
} else {
|
|
18258
19208
|
lines.push("- no code-bound reads yet; inspect the task-relevant files directly");
|
|
18259
19209
|
}
|
|
@@ -18262,6 +19212,14 @@ function formatAutoProjectContextSummary(context) {
|
|
|
18262
19212
|
"Reliable memory",
|
|
18263
19213
|
reliableSources.length > 0 ? `- ${reliableSources.length} current code-bound memory link(s)` : "- none yet"
|
|
18264
19214
|
);
|
|
19215
|
+
if (context.workset.durableMemory.length > 0) {
|
|
19216
|
+
lines.push("", "Durable memory");
|
|
19217
|
+
for (const memory of context.workset.durableMemory.slice(0, 3)) {
|
|
19218
|
+
lines.push(
|
|
19219
|
+
"- " + memory.kind + " (" + memory.scope + ", " + memory.state + "): " + compactContinuationText(memory.summary, 28)
|
|
19220
|
+
);
|
|
19221
|
+
}
|
|
19222
|
+
}
|
|
18265
19223
|
const continuation = context.workset.continuation;
|
|
18266
19224
|
if (continuation?.previousSession || continuation?.memories.length || continuation?.compactCheckpoint) {
|
|
18267
19225
|
lines.push("", "Resume from prior work");
|
|
@@ -18311,7 +19269,7 @@ function formatLegacyAutoProjectContextPrompt(context) {
|
|
|
18311
19269
|
if (startHere.length === 0) {
|
|
18312
19270
|
lines.push("- no code-bound reads yet; inspect the task-relevant code directly");
|
|
18313
19271
|
} else {
|
|
18314
|
-
startHere.forEach((
|
|
19272
|
+
startHere.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
|
|
18315
19273
|
}
|
|
18316
19274
|
const { reliableSources, cautionSources, hiddenReliableCount, hiddenCautionCount } = lensSourceSets(context);
|
|
18317
19275
|
lines.push("", "Reliable memory");
|
|
@@ -18562,6 +19520,7 @@ async function attachTaskWorkset(input) {
|
|
|
18562
19520
|
stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
|
|
18563
19521
|
},
|
|
18564
19522
|
runtimeCautions: input.runtimeCautions,
|
|
19523
|
+
...input.reader ? { reader: input.reader } : {},
|
|
18565
19524
|
deliveryTarget: "context-pack"
|
|
18566
19525
|
});
|
|
18567
19526
|
return { ...input.pack, workset };
|
|
@@ -18572,7 +19531,7 @@ function buildContextPackPrompt(pack) {
|
|
|
18572
19531
|
const unboundMemories = pack.memories.filter((memory) => memory.status === "unbound");
|
|
18573
19532
|
const lines = ["## Task", pack.task, "", "## Reliable Memories"];
|
|
18574
19533
|
const visibleCodeFacts = pack.codeFacts.filter((fact) => !isCodeGraphExcludedPath(fact.path)).slice(0, 5);
|
|
18575
|
-
const visibleSuggestedReads = pack.suggestedReads.filter((
|
|
19534
|
+
const visibleSuggestedReads = pack.suggestedReads.filter((path27) => !isCodeGraphExcludedPath(path27)).slice(0, 5);
|
|
18576
19535
|
if (reliableMemories.length === 0) lines.push("- none");
|
|
18577
19536
|
for (const memory of reliableMemories) {
|
|
18578
19537
|
lines.push(`- #${memory.id} ${memory.status}: [${memory.type}] ${memory.title} (${memory.reason})`);
|
|
@@ -18596,7 +19555,7 @@ function buildContextPackPrompt(pack) {
|
|
|
18596
19555
|
}
|
|
18597
19556
|
lines.push("", "## Suggested Next Reads");
|
|
18598
19557
|
if (visibleSuggestedReads.length === 0) lines.push("- none");
|
|
18599
|
-
visibleSuggestedReads.forEach((
|
|
19558
|
+
visibleSuggestedReads.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
|
|
18600
19559
|
lines.push("", "## Suggested Verification");
|
|
18601
19560
|
if (pack.suggestedVerification.length === 0) lines.push("- none");
|
|
18602
19561
|
for (const command of pack.suggestedVerification) lines.push(`- ${command}`);
|
|
@@ -18988,9 +19947,9 @@ __export(team_store_exports, {
|
|
|
18988
19947
|
isTeamStoreInitialized: () => isTeamStoreInitialized,
|
|
18989
19948
|
resetTeamStore: () => resetTeamStore
|
|
18990
19949
|
});
|
|
18991
|
-
import { randomUUID as
|
|
18992
|
-
import
|
|
18993
|
-
import
|
|
19950
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
19951
|
+
import path22 from "path";
|
|
19952
|
+
import fs16 from "fs";
|
|
18994
19953
|
function getTeamStore() {
|
|
18995
19954
|
if (!_teamStore) {
|
|
18996
19955
|
throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
|
|
@@ -19277,7 +20236,7 @@ var init_team_store = __esm({
|
|
|
19277
20236
|
* reactivate it (preserving agent_id). Otherwise create a new one.
|
|
19278
20237
|
*/
|
|
19279
20238
|
registerAgent(input) {
|
|
19280
|
-
const instanceId = input.instanceId ||
|
|
20239
|
+
const instanceId = input.instanceId || randomUUID12();
|
|
19281
20240
|
const now3 = Date.now();
|
|
19282
20241
|
const existing = this.stmtAgentFindByInstance.get(
|
|
19283
20242
|
input.projectId,
|
|
@@ -19303,7 +20262,7 @@ var init_team_store = __esm({
|
|
|
19303
20262
|
this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
|
|
19304
20263
|
return row2;
|
|
19305
20264
|
}
|
|
19306
|
-
const agentId =
|
|
20265
|
+
const agentId = randomUUID12();
|
|
19307
20266
|
this.stmtAgentUpsert.run({
|
|
19308
20267
|
agent_id: agentId,
|
|
19309
20268
|
project_id: input.projectId,
|
|
@@ -19405,7 +20364,7 @@ var init_team_store = __esm({
|
|
|
19405
20364
|
return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
|
|
19406
20365
|
}
|
|
19407
20366
|
}
|
|
19408
|
-
const id =
|
|
20367
|
+
const id = randomUUID12();
|
|
19409
20368
|
const now3 = Date.now();
|
|
19410
20369
|
const row = {
|
|
19411
20370
|
id,
|
|
@@ -19455,7 +20414,7 @@ var init_team_store = __esm({
|
|
|
19455
20414
|
// Task Operations (with atomic claim semantics)
|
|
19456
20415
|
// ═══════════════════════════════════════════════════════════════════
|
|
19457
20416
|
createTask(input) {
|
|
19458
|
-
const taskId =
|
|
20417
|
+
const taskId = randomUUID12();
|
|
19459
20418
|
const now3 = Date.now();
|
|
19460
20419
|
const row = {
|
|
19461
20420
|
task_id: taskId,
|
|
@@ -19680,19 +20639,19 @@ var init_team_store = __esm({
|
|
|
19680
20639
|
const agentCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM team_agents`).get();
|
|
19681
20640
|
if (agentCount.cnt > 0) return;
|
|
19682
20641
|
const candidates = [
|
|
19683
|
-
|
|
19684
|
-
|
|
20642
|
+
path22.join(this.dataDir, "team-state.json"),
|
|
20643
|
+
path22.join(this.dataDir, "..", "team-state.json")
|
|
19685
20644
|
];
|
|
19686
20645
|
let jsonPath = null;
|
|
19687
20646
|
for (const p of candidates) {
|
|
19688
|
-
if (
|
|
20647
|
+
if (fs16.existsSync(p)) {
|
|
19689
20648
|
jsonPath = p;
|
|
19690
20649
|
break;
|
|
19691
20650
|
}
|
|
19692
20651
|
}
|
|
19693
20652
|
if (!jsonPath) return;
|
|
19694
20653
|
try {
|
|
19695
|
-
const raw =
|
|
20654
|
+
const raw = fs16.readFileSync(jsonPath, "utf-8");
|
|
19696
20655
|
const snap = JSON.parse(raw);
|
|
19697
20656
|
if (snap.version !== 1) return;
|
|
19698
20657
|
console.error(`[memorix] Migrating team-state.json to SQLite...`);
|
|
@@ -19727,7 +20686,7 @@ var init_team_store = __esm({
|
|
|
19727
20686
|
for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
|
|
19728
20687
|
for (const msg of msgs) {
|
|
19729
20688
|
this.stmtMsgInsert.run({
|
|
19730
|
-
id: msg.id ??
|
|
20689
|
+
id: msg.id ?? randomUUID12(),
|
|
19731
20690
|
project_id: "migrated",
|
|
19732
20691
|
sender_agent_id: msg.from ?? "unknown",
|
|
19733
20692
|
recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
|
|
@@ -19786,7 +20745,7 @@ var init_team_store = __esm({
|
|
|
19786
20745
|
`[memorix] Team migration complete: ${agentsMigrated} agents, ${messagesMigrated} messages, ${tasksMigrated} tasks, ${locksMigrated} locks.`
|
|
19787
20746
|
);
|
|
19788
20747
|
try {
|
|
19789
|
-
|
|
20748
|
+
fs16.renameSync(jsonPath, jsonPath + ".migrated");
|
|
19790
20749
|
} catch {
|
|
19791
20750
|
}
|
|
19792
20751
|
} catch (err) {
|
|
@@ -19821,7 +20780,7 @@ var init_team_store = __esm({
|
|
|
19821
20780
|
* Uses projectId derived from the dataDir path.
|
|
19822
20781
|
*/
|
|
19823
20782
|
seedDefaultRoles(dataDir) {
|
|
19824
|
-
const projectId =
|
|
20783
|
+
const projectId = path22.basename(path22.resolve(dataDir, ".."));
|
|
19825
20784
|
const count2 = this.stmtRoleCountByProject.get(projectId).cnt;
|
|
19826
20785
|
if (count2 > 0) return;
|
|
19827
20786
|
const now3 = Date.now();
|
|
@@ -20835,8 +21794,8 @@ __export(server_exports, {
|
|
|
20835
21794
|
startDashboard: () => startDashboard
|
|
20836
21795
|
});
|
|
20837
21796
|
import { createServer } from "http";
|
|
20838
|
-
import { promises as
|
|
20839
|
-
import
|
|
21797
|
+
import { promises as fs17 } from "fs";
|
|
21798
|
+
import path23 from "path";
|
|
20840
21799
|
import { exec } from "child_process";
|
|
20841
21800
|
function sendJson(res, data, status = 200) {
|
|
20842
21801
|
res.writeHead(status, {
|
|
@@ -21314,7 +22273,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
21314
22273
|
const aliasModule = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
|
|
21315
22274
|
canonicalId = await aliasModule.getCanonicalId(effectiveProjectId);
|
|
21316
22275
|
const { promises: fsP } = await import("fs");
|
|
21317
|
-
const registryPath =
|
|
22276
|
+
const registryPath = path23.join(baseDir, ".project-aliases.json");
|
|
21318
22277
|
const raw = await fsP.readFile(registryPath, "utf-8");
|
|
21319
22278
|
const registry = JSON.parse(raw);
|
|
21320
22279
|
aliasGroups = registry.groups || [];
|
|
@@ -21429,14 +22388,14 @@ async function serveStatic(req, res, staticDir) {
|
|
|
21429
22388
|
if (urlPath === "/" || !urlPath.includes(".")) {
|
|
21430
22389
|
urlPath = "/index.html";
|
|
21431
22390
|
}
|
|
21432
|
-
const filePath =
|
|
22391
|
+
const filePath = path23.join(staticDir, urlPath);
|
|
21433
22392
|
if (!filePath.startsWith(staticDir)) {
|
|
21434
22393
|
sendError(res, "Forbidden", 403);
|
|
21435
22394
|
return;
|
|
21436
22395
|
}
|
|
21437
22396
|
try {
|
|
21438
|
-
const data = await
|
|
21439
|
-
const ext =
|
|
22397
|
+
const data = await fs17.readFile(filePath);
|
|
22398
|
+
const ext = path23.extname(filePath);
|
|
21440
22399
|
res.writeHead(200, {
|
|
21441
22400
|
"Content-Type": MIME_TYPES[ext] || "application/octet-stream",
|
|
21442
22401
|
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
@@ -21446,7 +22405,7 @@ async function serveStatic(req, res, staticDir) {
|
|
|
21446
22405
|
res.end(data);
|
|
21447
22406
|
} catch {
|
|
21448
22407
|
try {
|
|
21449
|
-
const indexData = await
|
|
22408
|
+
const indexData = await fs17.readFile(path23.join(staticDir, "index.html"));
|
|
21450
22409
|
res.writeHead(200, {
|
|
21451
22410
|
"Content-Type": "text/html; charset=utf-8",
|
|
21452
22411
|
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
@@ -22159,7 +23118,7 @@ function collectCodeMemory(opts) {
|
|
|
22159
23118
|
if (status.indexedAt) lines.push(`Last scan: ${status.indexedAt}`);
|
|
22160
23119
|
if (suggestedReads.length > 0) {
|
|
22161
23120
|
lines.push("Suggested reads:");
|
|
22162
|
-
suggestedReads.forEach((
|
|
23121
|
+
suggestedReads.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
|
|
22163
23122
|
}
|
|
22164
23123
|
return lines.join("\n");
|
|
22165
23124
|
} catch {
|
|
@@ -22184,7 +23143,7 @@ __export(planner_exports, {
|
|
|
22184
23143
|
materializeTaskGraph: () => materializeTaskGraph,
|
|
22185
23144
|
seedAutonomousPipeline: () => seedAutonomousPipeline
|
|
22186
23145
|
});
|
|
22187
|
-
import { randomUUID as
|
|
23146
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
22188
23147
|
function isPlannerTask(metadata) {
|
|
22189
23148
|
if (!metadata) return null;
|
|
22190
23149
|
try {
|
|
@@ -22242,7 +23201,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
|
|
|
22242
23201
|
const maxIterations = config.maxIterations ?? 3;
|
|
22243
23202
|
const taskBudget = config.taskBudget ?? 15;
|
|
22244
23203
|
const structuredPlan = opts?.structuredPlan ?? true;
|
|
22245
|
-
const pipelineId =
|
|
23204
|
+
const pipelineId = randomUUID13();
|
|
22246
23205
|
const meta = {
|
|
22247
23206
|
plannerType: "plan",
|
|
22248
23207
|
pipelineId,
|
|
@@ -22932,15 +23891,15 @@ __export(audit_exports, {
|
|
|
22932
23891
|
removeFile: () => removeFile,
|
|
22933
23892
|
saveAudit: () => saveAudit
|
|
22934
23893
|
});
|
|
22935
|
-
import * as
|
|
22936
|
-
import * as
|
|
23894
|
+
import * as fs18 from "fs/promises";
|
|
23895
|
+
import * as path24 from "path";
|
|
22937
23896
|
import { homedir as homedir24 } from "os";
|
|
22938
23897
|
function getAuditFilePath() {
|
|
22939
|
-
return process.env.MEMORIX_AUDIT_FILE ||
|
|
23898
|
+
return process.env.MEMORIX_AUDIT_FILE || path24.join(homedir24(), ".memorix", "audit.json");
|
|
22940
23899
|
}
|
|
22941
23900
|
async function loadAudit() {
|
|
22942
23901
|
try {
|
|
22943
|
-
const content = await
|
|
23902
|
+
const content = await fs18.readFile(getAuditFilePath(), "utf-8");
|
|
22944
23903
|
return JSON.parse(content);
|
|
22945
23904
|
} catch {
|
|
22946
23905
|
return {
|
|
@@ -22951,9 +23910,9 @@ async function loadAudit() {
|
|
|
22951
23910
|
}
|
|
22952
23911
|
async function saveAudit(data) {
|
|
22953
23912
|
const auditFile = getAuditFilePath();
|
|
22954
|
-
const dir =
|
|
22955
|
-
await
|
|
22956
|
-
await
|
|
23913
|
+
const dir = path24.dirname(auditFile);
|
|
23914
|
+
await fs18.mkdir(dir, { recursive: true });
|
|
23915
|
+
await fs18.writeFile(auditFile, JSON.stringify(data, null, 2), "utf-8");
|
|
22957
23916
|
}
|
|
22958
23917
|
function getProjectId(projectRoot) {
|
|
22959
23918
|
try {
|
|
@@ -23035,8 +23994,8 @@ __export(installers_exports, {
|
|
|
23035
23994
|
resolveOpenCodeHookCommand: () => resolveOpenCodeHookCommand,
|
|
23036
23995
|
uninstallHooks: () => uninstallHooks
|
|
23037
23996
|
});
|
|
23038
|
-
import * as
|
|
23039
|
-
import * as
|
|
23997
|
+
import * as fs19 from "fs/promises";
|
|
23998
|
+
import * as path25 from "path";
|
|
23040
23999
|
import * as os3 from "os";
|
|
23041
24000
|
import { execSync as execSync3 } from "child_process";
|
|
23042
24001
|
function resolveHookCommand() {
|
|
@@ -23052,7 +24011,7 @@ function resolveWindowsMemorixShim() {
|
|
|
23052
24011
|
stdio: ["ignore", "pipe", "ignore"],
|
|
23053
24012
|
windowsHide: true
|
|
23054
24013
|
}));
|
|
23055
|
-
return output.split(/\r?\n/).map((candidate) => candidate.trim()).find((candidate) =>
|
|
24014
|
+
return output.split(/\r?\n/).map((candidate) => candidate.trim()).find((candidate) => path25.isAbsolute(candidate)) ?? null;
|
|
23056
24015
|
} catch {
|
|
23057
24016
|
return null;
|
|
23058
24017
|
}
|
|
@@ -23247,9 +24206,9 @@ async function installOfficialSkillsForAgent(agent, projectRoot, global = false)
|
|
|
23247
24206
|
const root = global ? os3.homedir() : projectRoot;
|
|
23248
24207
|
const skillPaths = [];
|
|
23249
24208
|
for (const skillEntry of OFFICIAL_MEMORIX_SKILLS) {
|
|
23250
|
-
const skillPath =
|
|
23251
|
-
await
|
|
23252
|
-
await
|
|
24209
|
+
const skillPath = path25.join(root, relativeDir, skillEntry.name, "SKILL.md");
|
|
24210
|
+
await fs19.mkdir(path25.dirname(skillPath), { recursive: true });
|
|
24211
|
+
await fs19.writeFile(skillPath, skillEntry.content, "utf-8");
|
|
23253
24212
|
skillPaths.push(skillPath);
|
|
23254
24213
|
try {
|
|
23255
24214
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
@@ -23462,124 +24421,124 @@ async function installOpenCodeSkills(projectRoot, global = false) {
|
|
|
23462
24421
|
function getProjectConfigPath(agent, projectRoot) {
|
|
23463
24422
|
switch (agent) {
|
|
23464
24423
|
case "claude":
|
|
23465
|
-
return
|
|
24424
|
+
return path25.join(projectRoot, ".claude", "settings.local.json");
|
|
23466
24425
|
case "copilot":
|
|
23467
|
-
return
|
|
24426
|
+
return path25.join(projectRoot, ".github", "hooks", "memorix.json");
|
|
23468
24427
|
case "windsurf":
|
|
23469
|
-
return
|
|
24428
|
+
return path25.join(projectRoot, ".windsurf", "hooks.json");
|
|
23470
24429
|
case "cursor":
|
|
23471
|
-
return
|
|
24430
|
+
return path25.join(projectRoot, ".cursor", "hooks.json");
|
|
23472
24431
|
case "kiro":
|
|
23473
|
-
return
|
|
24432
|
+
return path25.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
|
|
23474
24433
|
case "codex":
|
|
23475
|
-
return
|
|
24434
|
+
return path25.join(projectRoot, "AGENTS.md");
|
|
23476
24435
|
case "trae":
|
|
23477
|
-
return
|
|
24436
|
+
return path25.join(projectRoot, ".trae", "rules", "project_rules.md");
|
|
23478
24437
|
case "opencode":
|
|
23479
|
-
return
|
|
24438
|
+
return path25.join(projectRoot, ".opencode", "plugins", "memorix.js");
|
|
23480
24439
|
case "pi":
|
|
23481
|
-
return
|
|
24440
|
+
return path25.join(projectRoot, ".pi", "packages", "memorix", "extensions", "memorix.js");
|
|
23482
24441
|
case "openclaw":
|
|
23483
|
-
return
|
|
24442
|
+
return path25.join(os3.homedir(), ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
|
|
23484
24443
|
case "hermes":
|
|
23485
|
-
return
|
|
24444
|
+
return path25.join(os3.homedir(), ".hermes", "plugins", "memorix", "plugin.yaml");
|
|
23486
24445
|
case "omp":
|
|
23487
|
-
return
|
|
24446
|
+
return path25.join(projectRoot, ".omp", "packages", "memorix", "extensions", "memorix.js");
|
|
23488
24447
|
case "antigravity":
|
|
23489
|
-
return
|
|
24448
|
+
return path25.join(projectRoot, ".agents", "hooks.json");
|
|
23490
24449
|
case "gemini-cli":
|
|
23491
|
-
return
|
|
24450
|
+
return path25.join(projectRoot, ".gemini", "settings.json");
|
|
23492
24451
|
default:
|
|
23493
|
-
return
|
|
24452
|
+
return path25.join(projectRoot, ".memorix", "hooks.json");
|
|
23494
24453
|
}
|
|
23495
24454
|
}
|
|
23496
24455
|
function getGlobalConfigPath(agent) {
|
|
23497
24456
|
const home = os3.homedir();
|
|
23498
24457
|
switch (agent) {
|
|
23499
24458
|
case "claude":
|
|
23500
|
-
return
|
|
24459
|
+
return path25.join(home, ".claude", "settings.json");
|
|
23501
24460
|
case "copilot":
|
|
23502
24461
|
return "";
|
|
23503
24462
|
case "windsurf":
|
|
23504
|
-
return
|
|
24463
|
+
return path25.join(home, ".codeium", "windsurf", "hooks.json");
|
|
23505
24464
|
case "cursor":
|
|
23506
|
-
return
|
|
24465
|
+
return path25.join(home, ".cursor", "hooks.json");
|
|
23507
24466
|
case "antigravity":
|
|
23508
|
-
return
|
|
24467
|
+
return path25.join(home, ".gemini", "config", "hooks.json");
|
|
23509
24468
|
case "gemini-cli":
|
|
23510
|
-
return
|
|
24469
|
+
return path25.join(home, ".gemini", "settings.json");
|
|
23511
24470
|
case "opencode":
|
|
23512
|
-
return
|
|
24471
|
+
return path25.join(home, ".config", "opencode", "plugins", "memorix.js");
|
|
23513
24472
|
case "pi":
|
|
23514
|
-
return
|
|
24473
|
+
return path25.join(home, ".pi", "agent", "packages", "memorix", "extensions", "memorix.js");
|
|
23515
24474
|
case "openclaw":
|
|
23516
|
-
return
|
|
24475
|
+
return path25.join(home, ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
|
|
23517
24476
|
case "hermes":
|
|
23518
|
-
return
|
|
24477
|
+
return path25.join(home, ".hermes", "plugins", "memorix", "plugin.yaml");
|
|
23519
24478
|
case "omp":
|
|
23520
|
-
return
|
|
24479
|
+
return path25.join(home, ".omp", "agent", "packages", "memorix", "extensions", "memorix.js");
|
|
23521
24480
|
case "trae":
|
|
23522
|
-
return
|
|
24481
|
+
return path25.join(home, ".trae", "rules", "project_rules.md");
|
|
23523
24482
|
default:
|
|
23524
|
-
return
|
|
24483
|
+
return path25.join(home, ".memorix", "hooks.json");
|
|
23525
24484
|
}
|
|
23526
24485
|
}
|
|
23527
24486
|
function getAgentRulesPath(agent, root, global = false) {
|
|
23528
24487
|
if (global) {
|
|
23529
24488
|
switch (agent) {
|
|
23530
24489
|
case "windsurf":
|
|
23531
|
-
return
|
|
24490
|
+
return path25.join(root, ".codeium", "windsurf", "rules", "memorix.md");
|
|
23532
24491
|
case "cursor":
|
|
23533
|
-
return
|
|
24492
|
+
return path25.join(root, ".cursor", "rules", "memorix.mdc");
|
|
23534
24493
|
case "claude":
|
|
23535
|
-
return
|
|
24494
|
+
return path25.join(root, ".claude", "CLAUDE.md");
|
|
23536
24495
|
case "codex":
|
|
23537
|
-
return
|
|
24496
|
+
return path25.join(root, ".codex", "AGENTS.md");
|
|
23538
24497
|
case "kiro":
|
|
23539
|
-
return
|
|
24498
|
+
return path25.join(root, ".kiro", "steering", "memorix.md");
|
|
23540
24499
|
case "opencode":
|
|
23541
|
-
return
|
|
24500
|
+
return path25.join(root, ".config", "opencode", "AGENTS.md");
|
|
23542
24501
|
case "antigravity":
|
|
23543
24502
|
case "gemini-cli":
|
|
23544
|
-
return
|
|
24503
|
+
return path25.join(root, ".gemini", "GEMINI.md");
|
|
23545
24504
|
case "trae":
|
|
23546
|
-
return
|
|
24505
|
+
return path25.join(root, ".trae", "rules", "project_rules.md");
|
|
23547
24506
|
default:
|
|
23548
|
-
return
|
|
24507
|
+
return path25.join(root, ".agent", "rules", "memorix.md");
|
|
23549
24508
|
}
|
|
23550
24509
|
}
|
|
23551
24510
|
switch (agent) {
|
|
23552
24511
|
case "windsurf":
|
|
23553
|
-
return
|
|
24512
|
+
return path25.join(root, ".windsurf", "rules", "memorix.md");
|
|
23554
24513
|
case "cursor":
|
|
23555
|
-
return
|
|
24514
|
+
return path25.join(root, ".cursor", "rules", "memorix.mdc");
|
|
23556
24515
|
case "claude":
|
|
23557
|
-
return
|
|
24516
|
+
return path25.join(root, "CLAUDE.md");
|
|
23558
24517
|
case "copilot":
|
|
23559
|
-
return
|
|
24518
|
+
return path25.join(root, ".github", "copilot-instructions.md");
|
|
23560
24519
|
case "codex":
|
|
23561
|
-
return
|
|
24520
|
+
return path25.join(root, "AGENTS.md");
|
|
23562
24521
|
case "kiro":
|
|
23563
|
-
return
|
|
24522
|
+
return path25.join(root, ".kiro", "steering", "memorix.md");
|
|
23564
24523
|
case "opencode":
|
|
23565
|
-
return
|
|
24524
|
+
return path25.join(root, "AGENTS.md");
|
|
23566
24525
|
case "antigravity":
|
|
23567
24526
|
case "gemini-cli":
|
|
23568
|
-
return
|
|
24527
|
+
return path25.join(root, "GEMINI.md");
|
|
23569
24528
|
case "trae":
|
|
23570
|
-
return
|
|
24529
|
+
return path25.join(root, ".trae", "rules", "project_rules.md");
|
|
23571
24530
|
default:
|
|
23572
|
-
return
|
|
24531
|
+
return path25.join(root, ".agent", "rules", "memorix.md");
|
|
23573
24532
|
}
|
|
23574
24533
|
}
|
|
23575
24534
|
async function detectCopilotExtension(home) {
|
|
23576
24535
|
const extDirs = [
|
|
23577
|
-
|
|
23578
|
-
|
|
24536
|
+
path25.join(home, ".vscode", "extensions"),
|
|
24537
|
+
path25.join(home, ".vscode-insiders", "extensions")
|
|
23579
24538
|
];
|
|
23580
24539
|
for (const extDir of extDirs) {
|
|
23581
24540
|
try {
|
|
23582
|
-
const entries = await
|
|
24541
|
+
const entries = await fs19.readdir(extDir);
|
|
23583
24542
|
if (entries.some((e) => e.startsWith("github.copilot-"))) {
|
|
23584
24543
|
return true;
|
|
23585
24544
|
}
|
|
@@ -23591,21 +24550,21 @@ async function detectCopilotExtension(home) {
|
|
|
23591
24550
|
async function detectInstalledAgents() {
|
|
23592
24551
|
const agents = [];
|
|
23593
24552
|
const home = os3.homedir();
|
|
23594
|
-
const claudeDir =
|
|
24553
|
+
const claudeDir = path25.join(home, ".claude");
|
|
23595
24554
|
try {
|
|
23596
|
-
await
|
|
24555
|
+
await fs19.access(claudeDir);
|
|
23597
24556
|
agents.push("claude");
|
|
23598
24557
|
} catch {
|
|
23599
24558
|
}
|
|
23600
|
-
const windsurfDir =
|
|
24559
|
+
const windsurfDir = path25.join(home, ".codeium", "windsurf");
|
|
23601
24560
|
try {
|
|
23602
|
-
await
|
|
24561
|
+
await fs19.access(windsurfDir);
|
|
23603
24562
|
agents.push("windsurf");
|
|
23604
24563
|
} catch {
|
|
23605
24564
|
}
|
|
23606
|
-
const cursorDir =
|
|
24565
|
+
const cursorDir = path25.join(home, ".cursor");
|
|
23607
24566
|
try {
|
|
23608
|
-
await
|
|
24567
|
+
await fs19.access(cursorDir);
|
|
23609
24568
|
agents.push("cursor");
|
|
23610
24569
|
} catch {
|
|
23611
24570
|
}
|
|
@@ -23613,21 +24572,21 @@ async function detectInstalledAgents() {
|
|
|
23613
24572
|
if (copilotDetected) {
|
|
23614
24573
|
agents.push("copilot");
|
|
23615
24574
|
}
|
|
23616
|
-
const kiroConfig =
|
|
24575
|
+
const kiroConfig = path25.join(home, ".kiro");
|
|
23617
24576
|
try {
|
|
23618
|
-
await
|
|
24577
|
+
await fs19.access(kiroConfig);
|
|
23619
24578
|
agents.push("kiro");
|
|
23620
24579
|
} catch {
|
|
23621
24580
|
}
|
|
23622
|
-
const codexDir =
|
|
24581
|
+
const codexDir = path25.join(home, ".codex");
|
|
23623
24582
|
try {
|
|
23624
|
-
await
|
|
24583
|
+
await fs19.access(codexDir);
|
|
23625
24584
|
agents.push("codex");
|
|
23626
24585
|
} catch {
|
|
23627
24586
|
}
|
|
23628
|
-
const antigravityDir =
|
|
24587
|
+
const antigravityDir = path25.join(home, ".gemini", "config");
|
|
23629
24588
|
try {
|
|
23630
|
-
await
|
|
24589
|
+
await fs19.access(antigravityDir);
|
|
23631
24590
|
agents.push("antigravity");
|
|
23632
24591
|
} catch {
|
|
23633
24592
|
}
|
|
@@ -23638,15 +24597,15 @@ async function detectInstalledAgents() {
|
|
|
23638
24597
|
agents.push("gemini-cli");
|
|
23639
24598
|
} catch {
|
|
23640
24599
|
}
|
|
23641
|
-
const opencodeDir =
|
|
24600
|
+
const opencodeDir = path25.join(home, ".config", "opencode");
|
|
23642
24601
|
try {
|
|
23643
|
-
await
|
|
24602
|
+
await fs19.access(opencodeDir);
|
|
23644
24603
|
agents.push("opencode");
|
|
23645
24604
|
} catch {
|
|
23646
24605
|
}
|
|
23647
|
-
const traeDir =
|
|
24606
|
+
const traeDir = path25.join(home, ".trae");
|
|
23648
24607
|
try {
|
|
23649
|
-
await
|
|
24608
|
+
await fs19.access(traeDir);
|
|
23650
24609
|
agents.push("trae");
|
|
23651
24610
|
} catch {
|
|
23652
24611
|
}
|
|
@@ -23680,7 +24639,7 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23680
24639
|
try {
|
|
23681
24640
|
const { access: access2, unlink: unlink2 } = await import("fs/promises");
|
|
23682
24641
|
await access2(entry.path);
|
|
23683
|
-
const basename3 =
|
|
24642
|
+
const basename3 = path25.basename(entry.path);
|
|
23684
24643
|
if (basename3 === "AGENTS.md" || basename3 === "GEMINI.md" || basename3 === "CONTEXT.md") {
|
|
23685
24644
|
continue;
|
|
23686
24645
|
}
|
|
@@ -23735,8 +24694,8 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23735
24694
|
case "opencode": {
|
|
23736
24695
|
const pluginContent = generateOpenCodePlugin();
|
|
23737
24696
|
const pluginPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
|
|
23738
|
-
await
|
|
23739
|
-
await
|
|
24697
|
+
await fs19.mkdir(path25.dirname(pluginPath), { recursive: true });
|
|
24698
|
+
await fs19.writeFile(pluginPath, pluginContent, "utf-8");
|
|
23740
24699
|
try {
|
|
23741
24700
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23742
24701
|
await recordFile2(projectRoot, "hook", pluginPath, agent);
|
|
@@ -23765,14 +24724,14 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23765
24724
|
default:
|
|
23766
24725
|
generated = generateClaudeConfig();
|
|
23767
24726
|
}
|
|
23768
|
-
await
|
|
24727
|
+
await fs19.mkdir(path25.dirname(configPath), { recursive: true });
|
|
23769
24728
|
if (agent === "kiro") {
|
|
23770
24729
|
const hookFiles = generateKiroHookFiles();
|
|
23771
|
-
const hooksDir =
|
|
23772
|
-
await
|
|
24730
|
+
const hooksDir = path25.join(path25.dirname(configPath));
|
|
24731
|
+
await fs19.mkdir(hooksDir, { recursive: true });
|
|
23773
24732
|
for (const hf of hookFiles) {
|
|
23774
|
-
const hookPath =
|
|
23775
|
-
await
|
|
24733
|
+
const hookPath = path25.join(hooksDir, hf.filename);
|
|
24734
|
+
await fs19.writeFile(hookPath, hf.content, "utf-8");
|
|
23776
24735
|
try {
|
|
23777
24736
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23778
24737
|
await recordFile2(projectRoot, "hook", hookPath, agent);
|
|
@@ -23782,7 +24741,7 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23782
24741
|
} else {
|
|
23783
24742
|
let existing = {};
|
|
23784
24743
|
try {
|
|
23785
|
-
const content = await
|
|
24744
|
+
const content = await fs19.readFile(configPath, "utf-8");
|
|
23786
24745
|
existing = JSON.parse(content);
|
|
23787
24746
|
} catch {
|
|
23788
24747
|
}
|
|
@@ -23817,7 +24776,7 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23817
24776
|
const h = merged.hooks;
|
|
23818
24777
|
if (h) delete h.preToolUse;
|
|
23819
24778
|
}
|
|
23820
|
-
await
|
|
24779
|
+
await fs19.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
23821
24780
|
try {
|
|
23822
24781
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23823
24782
|
await recordFile2(projectRoot, "hook", configPath, agent);
|
|
@@ -23865,18 +24824,18 @@ async function installAgentRules(agent, projectRoot, global = false) {
|
|
|
23865
24824
|
const rulesRoot = global ? os3.homedir() : projectRoot;
|
|
23866
24825
|
const rulesPath = getAgentRulesPath(agent, rulesRoot, global);
|
|
23867
24826
|
try {
|
|
23868
|
-
await
|
|
24827
|
+
await fs19.mkdir(path25.dirname(rulesPath), { recursive: true });
|
|
23869
24828
|
if (agent === "claude" || agent === "codex" || agent === "opencode" || agent === "antigravity" || agent === "gemini-cli") {
|
|
23870
24829
|
try {
|
|
23871
|
-
const existing = await
|
|
23872
|
-
await
|
|
24830
|
+
const existing = await fs19.readFile(rulesPath, "utf-8");
|
|
24831
|
+
await fs19.writeFile(rulesPath, mergeMemorixRulesContent(existing, rulesContent), "utf-8");
|
|
23873
24832
|
try {
|
|
23874
24833
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23875
24834
|
await recordFile2(projectRoot, "rule", rulesPath, agent);
|
|
23876
24835
|
} catch {
|
|
23877
24836
|
}
|
|
23878
24837
|
} catch {
|
|
23879
|
-
await
|
|
24838
|
+
await fs19.writeFile(rulesPath, rulesContent, "utf-8");
|
|
23880
24839
|
try {
|
|
23881
24840
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23882
24841
|
await recordFile2(projectRoot, "rule", rulesPath, agent);
|
|
@@ -23885,9 +24844,9 @@ async function installAgentRules(agent, projectRoot, global = false) {
|
|
|
23885
24844
|
}
|
|
23886
24845
|
} else {
|
|
23887
24846
|
try {
|
|
23888
|
-
await
|
|
24847
|
+
await fs19.access(rulesPath);
|
|
23889
24848
|
} catch {
|
|
23890
|
-
await
|
|
24849
|
+
await fs19.writeFile(rulesPath, rulesContent, "utf-8");
|
|
23891
24850
|
try {
|
|
23892
24851
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23893
24852
|
await recordFile2(projectRoot, "rule", rulesPath, agent);
|
|
@@ -23977,6 +24936,8 @@ alwaysApply: true
|
|
|
23977
24936
|
"- Include `filesModified` when relevant",
|
|
23978
24937
|
"- Use `topicKey` for topics that evolve over time (prevents duplicates)",
|
|
23979
24938
|
'- For "why" decisions, use `memorix_store_reasoning`',
|
|
24939
|
+
'- For a stable fact, reusable procedure, or completed episode that merits deliberate long-term review, include `longTerm` in `memorix_store` with the appropriate kind and normally `scope: "project"`. It creates a candidate only: do not use it for routine updates, do not make project-derived evidence portable user memory, and do not assume it enters context until an operator qualifies and approves it through `memorix memory long-term`.',
|
|
24940
|
+
"- A `user` + `portable` durable memory delivered in a task brief is intentionally available across projects. When it matches the task, use it as reusable background even if its origin differs; do not treat it as a current-project fact. Expand it only when needed with `memorix_detail` using its `durable:<id>` reference and a specific purpose.",
|
|
23980
24941
|
"",
|
|
23981
24942
|
"**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
|
|
23982
24943
|
"",
|
|
@@ -24013,16 +24974,16 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24013
24974
|
let success = false;
|
|
24014
24975
|
try {
|
|
24015
24976
|
if (agent === "kiro" || agent === "opencode") {
|
|
24016
|
-
await
|
|
24977
|
+
await fs19.unlink(configPath);
|
|
24017
24978
|
success = true;
|
|
24018
24979
|
} else {
|
|
24019
|
-
const content = await
|
|
24980
|
+
const content = await fs19.readFile(configPath, "utf-8");
|
|
24020
24981
|
const config = JSON.parse(content);
|
|
24021
24982
|
delete config.hooks;
|
|
24022
24983
|
if (Object.keys(config).length === 0) {
|
|
24023
|
-
await
|
|
24984
|
+
await fs19.unlink(configPath);
|
|
24024
24985
|
} else {
|
|
24025
|
-
await
|
|
24986
|
+
await fs19.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
24026
24987
|
}
|
|
24027
24988
|
success = true;
|
|
24028
24989
|
}
|
|
@@ -24035,10 +24996,10 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24035
24996
|
const agentFiles = prevFiles.filter((e) => e.agent === agent);
|
|
24036
24997
|
for (const entry of agentFiles) {
|
|
24037
24998
|
try {
|
|
24038
|
-
const basename3 =
|
|
24999
|
+
const basename3 = path25.basename(entry.path);
|
|
24039
25000
|
if (basename3 === "AGENTS.md" || basename3 === "GEMINI.md" || basename3 === "CONTEXT.md") {
|
|
24040
25001
|
try {
|
|
24041
|
-
const content = await
|
|
25002
|
+
const content = await fs19.readFile(entry.path, "utf-8");
|
|
24042
25003
|
const memorixStart = content.indexOf("# Memorix");
|
|
24043
25004
|
if (memorixStart >= 0) {
|
|
24044
25005
|
const afterMemorix = content.substring(memorixStart);
|
|
@@ -24050,9 +25011,9 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24050
25011
|
}
|
|
24051
25012
|
const cleaned = (before + "\n" + after).trim();
|
|
24052
25013
|
if (cleaned.length === 0) {
|
|
24053
|
-
await
|
|
25014
|
+
await fs19.unlink(entry.path);
|
|
24054
25015
|
} else {
|
|
24055
|
-
await
|
|
25016
|
+
await fs19.writeFile(entry.path, cleaned + "\n", "utf-8");
|
|
24056
25017
|
}
|
|
24057
25018
|
}
|
|
24058
25019
|
} catch {
|
|
@@ -24061,7 +25022,7 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24061
25022
|
auditCleaned = true;
|
|
24062
25023
|
continue;
|
|
24063
25024
|
}
|
|
24064
|
-
await
|
|
25025
|
+
await fs19.unlink(entry.path);
|
|
24065
25026
|
await removeFile2(projectRoot, entry.path);
|
|
24066
25027
|
auditCleaned = true;
|
|
24067
25028
|
} catch {
|
|
@@ -24073,14 +25034,14 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24073
25034
|
if (success) {
|
|
24074
25035
|
try {
|
|
24075
25036
|
const { rm } = await import("fs/promises");
|
|
24076
|
-
const dir =
|
|
25037
|
+
const dir = path25.dirname(configPath);
|
|
24077
25038
|
let current = dir;
|
|
24078
25039
|
for (let i = 0; i < 3; i++) {
|
|
24079
25040
|
try {
|
|
24080
|
-
const entries = await
|
|
25041
|
+
const entries = await fs19.readdir(current);
|
|
24081
25042
|
if (entries.length === 0) {
|
|
24082
25043
|
await rm(current, { recursive: true });
|
|
24083
|
-
current =
|
|
25044
|
+
current = path25.dirname(current);
|
|
24084
25045
|
} else {
|
|
24085
25046
|
break;
|
|
24086
25047
|
}
|
|
@@ -24104,12 +25065,12 @@ async function getHookStatus(projectRoot) {
|
|
|
24104
25065
|
let usedPath = projectPath;
|
|
24105
25066
|
const verifiedByDefault = agent !== "opencode" && agent !== "pi";
|
|
24106
25067
|
try {
|
|
24107
|
-
await
|
|
25068
|
+
await fs19.access(projectPath);
|
|
24108
25069
|
installed = true;
|
|
24109
25070
|
} catch {
|
|
24110
25071
|
if (globalPath) {
|
|
24111
25072
|
try {
|
|
24112
|
-
await
|
|
25073
|
+
await fs19.access(globalPath);
|
|
24113
25074
|
installed = true;
|
|
24114
25075
|
usedPath = globalPath;
|
|
24115
25076
|
} catch {
|
|
@@ -24118,7 +25079,7 @@ async function getHookStatus(projectRoot) {
|
|
|
24118
25079
|
}
|
|
24119
25080
|
if (installed && agent === "opencode") {
|
|
24120
25081
|
try {
|
|
24121
|
-
const content = await
|
|
25082
|
+
const content = await fs19.readFile(usedPath, "utf-8");
|
|
24122
25083
|
const match = content.match(/@generated-version\s+(\d+)/);
|
|
24123
25084
|
const installedVersion = match ? parseInt(match[1], 10) : 0;
|
|
24124
25085
|
outdated = installedVersion < OPENCODE_PLUGIN_VERSION;
|
|
@@ -24149,11 +25110,11 @@ var init_installers = __esm({
|
|
|
24149
25110
|
init_official_skills();
|
|
24150
25111
|
OPENCODE_PLUGIN_VERSION = 7;
|
|
24151
25112
|
AGENT_SKILL_DIRS = {
|
|
24152
|
-
cursor: { project:
|
|
24153
|
-
windsurf: { project:
|
|
24154
|
-
kiro: { project:
|
|
24155
|
-
opencode: { project:
|
|
24156
|
-
trae: { project:
|
|
25113
|
+
cursor: { project: path25.join(".cursor", "skills"), global: path25.join(".cursor", "skills") },
|
|
25114
|
+
windsurf: { project: path25.join(".windsurf", "skills"), global: path25.join(".windsurf", "skills") },
|
|
25115
|
+
kiro: { project: path25.join(".kiro", "skills"), global: path25.join(".kiro", "skills") },
|
|
25116
|
+
opencode: { project: path25.join(".opencode", "skills"), global: path25.join(".config", "opencode", "skills") },
|
|
25117
|
+
trae: { project: path25.join(".trae", "skills"), global: path25.join(".trae", "skills") }
|
|
24157
25118
|
};
|
|
24158
25119
|
PACKAGE_OWNED_HOOK_AGENTS = /* @__PURE__ */ new Set(["openclaw", "hermes", "omp"]);
|
|
24159
25120
|
}
|
|
@@ -24167,9 +25128,9 @@ __export(hooks_path_exports, {
|
|
|
24167
25128
|
resolveHooksDir: () => resolveHooksDir
|
|
24168
25129
|
});
|
|
24169
25130
|
import { existsSync as existsSync17, readFileSync as readFileSync13, statSync as statSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
24170
|
-
import
|
|
25131
|
+
import path26 from "path";
|
|
24171
25132
|
function resolveGitDir(projectRoot) {
|
|
24172
|
-
const dotGit =
|
|
25133
|
+
const dotGit = path26.join(projectRoot, ".git");
|
|
24173
25134
|
if (!existsSync17(dotGit)) return null;
|
|
24174
25135
|
const stat = statSync4(dotGit);
|
|
24175
25136
|
if (stat.isDirectory()) {
|
|
@@ -24181,7 +25142,7 @@ function resolveGitDir(projectRoot) {
|
|
|
24181
25142
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
24182
25143
|
if (match) {
|
|
24183
25144
|
const gitdir = match[1].trim();
|
|
24184
|
-
const resolved =
|
|
25145
|
+
const resolved = path26.isAbsolute(gitdir) ? gitdir : path26.resolve(projectRoot, gitdir);
|
|
24185
25146
|
if (existsSync17(resolved)) {
|
|
24186
25147
|
return resolved;
|
|
24187
25148
|
}
|
|
@@ -24194,10 +25155,10 @@ function resolveGitDir(projectRoot) {
|
|
|
24194
25155
|
function resolveHooksDir(projectRoot) {
|
|
24195
25156
|
const gitDir = resolveGitDir(projectRoot);
|
|
24196
25157
|
if (!gitDir) return null;
|
|
24197
|
-
const hooksDir =
|
|
25158
|
+
const hooksDir = path26.join(gitDir, "hooks");
|
|
24198
25159
|
return {
|
|
24199
25160
|
hooksDir,
|
|
24200
|
-
hookPath:
|
|
25161
|
+
hookPath: path26.join(hooksDir, "post-commit")
|
|
24201
25162
|
};
|
|
24202
25163
|
}
|
|
24203
25164
|
function ensureHooksDir(projectRoot) {
|
|
@@ -27437,10 +28398,10 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
27437
28398
|
pathsToCheck.push(join19(homedir21(), ".gemini", "antigravity", "mcp_config.json"));
|
|
27438
28399
|
}
|
|
27439
28400
|
const merged = /* @__PURE__ */ new Map();
|
|
27440
|
-
for (const
|
|
27441
|
-
if (existsSync10(
|
|
28401
|
+
for (const path27 of pathsToCheck) {
|
|
28402
|
+
if (existsSync10(path27)) {
|
|
27442
28403
|
try {
|
|
27443
|
-
const content = readFileSync7(
|
|
28404
|
+
const content = readFileSync7(path27, "utf-8");
|
|
27444
28405
|
const servers = adapter.parse(content);
|
|
27445
28406
|
for (const s of servers) {
|
|
27446
28407
|
if (!merged.has(s.name)) merged.set(s.name, s);
|
|
@@ -28231,7 +29192,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
28231
29192
|
};
|
|
28232
29193
|
const server = existingServer ?? new McpServer({
|
|
28233
29194
|
name: "memorix",
|
|
28234
|
-
version: true ? "1.
|
|
29195
|
+
version: true ? "1.3.0" : "1.0.1"
|
|
28235
29196
|
});
|
|
28236
29197
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
28237
29198
|
server.registerTool = ((name, ...args) => {
|
|
@@ -28311,7 +29272,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
28311
29272
|
"memorix_store",
|
|
28312
29273
|
{
|
|
28313
29274
|
title: "Store Memory",
|
|
28314
|
-
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity. For a read-only task, do not store unless the user explicitly asks to save a record.",
|
|
29275
|
+
description: "Store a new observation/memory. Automatically indexed for search. Use type to classify: gotcha ([GOTCHA] critical pitfall), decision ([DECISION] architecture choice), problem-solution ([FIX] bug fix), how-it-works ([INFO] explanation), what-changed ([CHANGE] change), discovery ([DISCOVERY] insight), why-it-exists ([WHY] rationale), trade-off ([TRADEOFF] compromise), session-request ([SESSION] original goal). Project visibility is the default. Personal or team visibility requires an explicitly joined coordination identity. Set longTerm only when the caller explicitly wants an additional source-backed long-term candidate; it is never injected until an operator records a review through the CLI. For a read-only task, do not store unless the user explicitly asks to save a record.",
|
|
28315
29276
|
inputSchema: {
|
|
28316
29277
|
entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
28317
29278
|
type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
|
|
@@ -28333,30 +29294,43 @@ The path should point to a directory containing a .git folder.`
|
|
|
28333
29294
|
visibility: z2.enum(["personal", "project", "team"]).optional().describe(
|
|
28334
29295
|
"Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
|
|
28335
29296
|
),
|
|
29297
|
+
longTerm: z2.object({
|
|
29298
|
+
kind: z2.enum(["episodic", "semantic", "procedural"]).describe("Cognitive kind for an additional long-term candidate."),
|
|
29299
|
+
scope: z2.enum(["project", "user", "team"]).optional().default("project").describe("Long-term scope. User capture is private source evidence and needs a bound agent identity."),
|
|
29300
|
+
tags: z2.array(z2.string()).optional().describe("Small set of retrieval tags for the candidate."),
|
|
29301
|
+
applicability: z2.string().optional().describe("When this durable fact or procedure applies.")
|
|
29302
|
+
}).optional().describe("Optional explicit request to create a source-backed long-term candidate alongside this observation. Candidates are not automatically injected."),
|
|
28336
29303
|
overrideReadOnly: z2.boolean().optional().describe(
|
|
28337
29304
|
"Use only when the user explicitly asks to save memory during a read-only or no-modification task."
|
|
28338
29305
|
)
|
|
28339
29306
|
}
|
|
28340
29307
|
},
|
|
28341
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, overrideReadOnly }) => {
|
|
29308
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, longTerm, overrideReadOnly }) => {
|
|
28342
29309
|
const unresolved = requireResolvedProject("store memory in the current project");
|
|
28343
29310
|
if (unresolved) return unresolved;
|
|
28344
29311
|
const readOnlyBoundary = blockReadOnlyAutopilotWrite(overrideReadOnly);
|
|
28345
29312
|
if (readOnlyBoundary) return readOnlyBoundary;
|
|
28346
|
-
const requestedVisibility =
|
|
29313
|
+
const requestedVisibility = longTerm?.scope === "user" ? "personal" : longTerm?.scope === "team" ? "team" : visibility;
|
|
29314
|
+
const effectiveVisibility = requestedVisibility ?? "project";
|
|
28347
29315
|
const reader = getObservationReader();
|
|
28348
|
-
if (
|
|
29316
|
+
if (effectiveVisibility !== "project" && !currentAgentId) {
|
|
28349
29317
|
return {
|
|
28350
29318
|
content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
|
|
28351
29319
|
isError: true
|
|
28352
29320
|
};
|
|
28353
29321
|
}
|
|
28354
|
-
if (
|
|
29322
|
+
if (effectiveVisibility === "team" && !reader.isTeamMember) {
|
|
28355
29323
|
return {
|
|
28356
29324
|
content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
|
|
28357
29325
|
isError: true
|
|
28358
29326
|
};
|
|
28359
29327
|
}
|
|
29328
|
+
if (longTerm?.scope === "team" && !reader.isTeamMember) {
|
|
29329
|
+
return {
|
|
29330
|
+
content: [{ type: "text", text: "Team long-term memory requires an active coordination membership in the current project." }],
|
|
29331
|
+
isError: true
|
|
29332
|
+
};
|
|
29333
|
+
}
|
|
28360
29334
|
try {
|
|
28361
29335
|
return await withFreshIndex(async () => {
|
|
28362
29336
|
let entityName = rawEntityName;
|
|
@@ -28378,7 +29352,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
28378
29352
|
const useFormation = formationMode === "active";
|
|
28379
29353
|
let formationResult = null;
|
|
28380
29354
|
let formationNote = "";
|
|
28381
|
-
if (useFormation && !topicKey && !progress) {
|
|
29355
|
+
if (useFormation && !topicKey && !progress && !longTerm) {
|
|
28382
29356
|
let currentFormationStage = "setup";
|
|
28383
29357
|
const completedFormationStages = {};
|
|
28384
29358
|
const formationStartTime = Date.now();
|
|
@@ -28529,7 +29503,7 @@ ${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formati
|
|
|
28529
29503
|
}
|
|
28530
29504
|
let compactAction = "";
|
|
28531
29505
|
let compactMerged = false;
|
|
28532
|
-
if (!useFormation && !topicKey && !progress) {
|
|
29506
|
+
if (!useFormation && !topicKey && !progress && !longTerm) {
|
|
28533
29507
|
try {
|
|
28534
29508
|
const searchResult = await compactSearch({
|
|
28535
29509
|
query: `${title} ${narrative.substring(0, 200)}`,
|
|
@@ -28686,9 +29660,33 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
28686
29660
|
sourceDetail: "explicit",
|
|
28687
29661
|
valueCategory: formationResult?.evaluation.category,
|
|
28688
29662
|
createdByAgentId: currentAgentId,
|
|
28689
|
-
visibility,
|
|
29663
|
+
visibility: requestedVisibility,
|
|
28690
29664
|
visibilityReader: reader
|
|
28691
29665
|
});
|
|
29666
|
+
let longTermNote = "";
|
|
29667
|
+
if (longTerm) {
|
|
29668
|
+
try {
|
|
29669
|
+
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
29670
|
+
const promoted = await promoteObservationToLongTermMemory2({
|
|
29671
|
+
dataDir: projectDir2,
|
|
29672
|
+
observation: obs,
|
|
29673
|
+
scope: longTerm.scope,
|
|
29674
|
+
kind: longTerm.kind,
|
|
29675
|
+
tags: longTerm.tags,
|
|
29676
|
+
applicability: longTerm.applicability,
|
|
29677
|
+
reader: {
|
|
29678
|
+
projectId: project.id,
|
|
29679
|
+
...reader.agentId ? { agentId: reader.agentId } : {},
|
|
29680
|
+
...reader.isTeamMember ? { isTeamMember: true } : {}
|
|
29681
|
+
}
|
|
29682
|
+
});
|
|
29683
|
+
longTermNote = `
|
|
29684
|
+
Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
|
|
29685
|
+
} catch (longTermError) {
|
|
29686
|
+
longTermNote = `
|
|
29687
|
+
[WARN] Observation stored, but long-term candidate was not created: ${longTermError instanceof Error ? longTermError.message : "unknown error"}`;
|
|
29688
|
+
}
|
|
29689
|
+
}
|
|
28692
29690
|
await graphManager.addObservations([
|
|
28693
29691
|
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
28694
29692
|
]);
|
|
@@ -28705,7 +29703,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
28705
29703
|
const enrichment = enrichmentParts.length > 0 ? `
|
|
28706
29704
|
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
28707
29705
|
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
28708
|
-
if (!useFormation && !topicKey && !progress) {
|
|
29706
|
+
if (!useFormation && !topicKey && !progress && !longTerm) {
|
|
28709
29707
|
const shadowFormation = async () => {
|
|
28710
29708
|
let oldCompactDecision = null;
|
|
28711
29709
|
try {
|
|
@@ -28795,7 +29793,7 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
28795
29793
|
{
|
|
28796
29794
|
type: "text",
|
|
28797
29795
|
text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
|
|
28798
|
-
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
|
|
29796
|
+
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}${longTermNote}`
|
|
28799
29797
|
}
|
|
28800
29798
|
]
|
|
28801
29799
|
};
|
|
@@ -29168,7 +30166,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
29168
30166
|
} : {},
|
|
29169
30167
|
...external.outline ? { semanticCode: external.outline } : {},
|
|
29170
30168
|
providerQuality: external.quality,
|
|
29171
|
-
...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {}
|
|
30169
|
+
...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {},
|
|
30170
|
+
reader: getObservationReader()
|
|
29172
30171
|
});
|
|
29173
30172
|
const text = buildContextPackPrompt2(pack);
|
|
29174
30173
|
return {
|
|
@@ -29842,7 +30841,7 @@ ${actions.join("\n")}`
|
|
|
29842
30841
|
"memorix_detail",
|
|
29843
30842
|
{
|
|
29844
30843
|
title: "Memory Details",
|
|
29845
|
-
description: 'Fetch full observation
|
|
30844
|
+
description: 'Fetch full observation, mini-skill, or curated durable-memory details \u2014 includes source kind (explicit memory / hook trace / git evidence), value category, and cross-references (~500-1000 tokens each). Do not re-fetch content already covered by a complete memorix_project_context brief unless a specific fact is still missing or the user asks for deeper history; provide purpose when intentionally expanding. Always use memorix_search first to find relevant IDs, then fetch only what you need. Accepts typed refs from search results (e.g. "obs:42", "skill:3") and durable refs from a project brief (e.g. "durable:<uuid>") via the typedRefs field, or legacy numeric ids / object refs for backward compatibility.',
|
|
29846
30845
|
inputSchema: {
|
|
29847
30846
|
ids: z2.array(z2.number()).optional().describe("Observation IDs to fetch (legacy, from memorix_search results)"),
|
|
29848
30847
|
refs: z2.array(
|
|
@@ -29851,7 +30850,7 @@ ${actions.join("\n")}`
|
|
|
29851
30850
|
projectId: z2.string().optional().describe("Project ID for global-search disambiguation")
|
|
29852
30851
|
})
|
|
29853
30852
|
).optional().describe("Explicit observation refs. Prefer this for global search results."),
|
|
29854
|
-
typedRefs: z2.array(z2.string()).optional().describe('Typed memory refs from search results, e.g. "obs:42", "skill:3", "obs:42@org/proj"'),
|
|
30853
|
+
typedRefs: z2.array(z2.string()).optional().describe('Typed memory refs from search results or a project brief, e.g. "obs:42", "skill:3", "durable:<uuid>", "obs:42@org/proj"'),
|
|
29855
30854
|
purpose: z2.string().optional().describe(
|
|
29856
30855
|
"Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user's explicit request."
|
|
29857
30856
|
),
|
|
@@ -29864,14 +30863,19 @@ ${actions.join("\n")}`
|
|
|
29864
30863
|
const safeIds = coerceNumberArray(ids);
|
|
29865
30864
|
const safeRefs = coerceObservationRefs(refs);
|
|
29866
30865
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
29867
|
-
const
|
|
30866
|
+
const durableIds = safeTypedRefs.flatMap((ref) => {
|
|
30867
|
+
const match = /^durable:([0-9a-f-]{36})$/i.exec(ref.trim());
|
|
30868
|
+
return match ? [match[1]] : [];
|
|
30869
|
+
});
|
|
30870
|
+
const observationTypedRefs = safeTypedRefs.filter((ref) => !/^durable:/i.test(ref.trim()));
|
|
30871
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || observationTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
29868
30872
|
if (!hasCrossProjectRef) {
|
|
29869
30873
|
const boundary = requireExplicitAutopilotExpansion("memorix_detail", purpose);
|
|
29870
30874
|
if (boundary) return boundary;
|
|
29871
30875
|
const requestedObservationIds = [
|
|
29872
30876
|
...safeIds,
|
|
29873
30877
|
...safeRefs.map((ref) => ref.id),
|
|
29874
|
-
...
|
|
30878
|
+
...observationTypedRefs.flatMap((ref) => {
|
|
29875
30879
|
const match = /^obs:(\d+)(?:@.+)?$/i.exec(ref.trim());
|
|
29876
30880
|
return match ? [Number(match[1])] : [];
|
|
29877
30881
|
})
|
|
@@ -29879,15 +30883,36 @@ ${actions.join("\n")}`
|
|
|
29879
30883
|
const duplicate = blockCoveredAutopilotEvidence("memorix_detail", requestedObservationIds, force);
|
|
29880
30884
|
if (duplicate) return duplicate;
|
|
29881
30885
|
}
|
|
29882
|
-
|
|
30886
|
+
const formatted = [];
|
|
29883
30887
|
try {
|
|
29884
|
-
|
|
29885
|
-
|
|
29886
|
-
|
|
29887
|
-
|
|
29888
|
-
|
|
29889
|
-
|
|
29890
|
-
|
|
30888
|
+
if (durableIds.length > 0) {
|
|
30889
|
+
const [{ getLongTermMemoryDetail: getLongTermMemoryDetail2 }, { resolveLocalMemoryOwner: resolveLocalMemoryOwner2 }] = await Promise.all([
|
|
30890
|
+
Promise.resolve().then(() => (init_long_term(), long_term_exports)),
|
|
30891
|
+
Promise.resolve().then(() => (init_owner(), owner_exports))
|
|
30892
|
+
]);
|
|
30893
|
+
const owner = await resolveLocalMemoryOwner2(projectDir2, { create: false });
|
|
30894
|
+
const observationReader = getObservationReader();
|
|
30895
|
+
const durableReader = {
|
|
30896
|
+
projectId: project.id,
|
|
30897
|
+
...owner ? { ownerId: owner.id } : {},
|
|
30898
|
+
...observationReader.agentId ? { agentId: observationReader.agentId } : {},
|
|
30899
|
+
...observationReader.isTeamMember ? { isTeamMember: true } : {}
|
|
30900
|
+
};
|
|
30901
|
+
const details = await Promise.all(durableIds.slice(0, 5).map(async (id) => {
|
|
30902
|
+
try {
|
|
30903
|
+
return await getLongTermMemoryDetail2({ dataDir: projectDir2, id, reader: durableReader });
|
|
30904
|
+
} catch (error) {
|
|
30905
|
+
return { error: error instanceof Error ? error.message : String(error), id };
|
|
30906
|
+
}
|
|
30907
|
+
}));
|
|
30908
|
+
formatted.push(JSON.stringify({ durableMemory: details }, null, 2));
|
|
30909
|
+
}
|
|
30910
|
+
if (observationTypedRefs.length > 0 || safeRefs.length > 0 || safeIds.length > 0) {
|
|
30911
|
+
const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
|
|
30912
|
+
const result = observationTypedRefs.length > 0 ? await compactDetail(observationTypedRefs, { reader }) : safeRefs.length > 0 ? await compactDetail(safeRefs, { reader }) : await compactDetail(safeIds.map((id) => ({ id, projectId: project.id })), { reader });
|
|
30913
|
+
formatted.push(
|
|
30914
|
+
result.documents.length > 0 ? result.formatted : observationTypedRefs.length > 0 ? `No memories found for refs: ${observationTypedRefs.join(", ")}` : safeRefs.length > 0 ? `No memories found for refs: ${safeRefs.map((ref) => `${ref.projectId ?? "current"}#${ref.id}`).join(", ")}` : `No memories found for IDs: ${safeIds.join(", ")}`
|
|
30915
|
+
);
|
|
29891
30916
|
}
|
|
29892
30917
|
} catch (err) {
|
|
29893
30918
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
@@ -29896,7 +30921,7 @@ ${actions.join("\n")}`
|
|
|
29896
30921
|
content: [
|
|
29897
30922
|
{
|
|
29898
30923
|
type: "text",
|
|
29899
|
-
text:
|
|
30924
|
+
text: formatted.length > 0 ? formatted.join("\n\n") : safeTypedRefs.length > 0 ? `No memories found for refs: ${safeTypedRefs.join(", ")}` : safeRefs.length > 0 ? `No memories found for refs: ${safeRefs.map((ref) => `${ref.projectId ?? "current"}#${ref.id}`).join(", ")}` : `No memories found for IDs: ${safeIds.join(", ")}`
|
|
29900
30925
|
}
|
|
29901
30926
|
]
|
|
29902
30927
|
};
|
|
@@ -30616,9 +31641,9 @@ ${skill2.content}` }]
|
|
|
30616
31641
|
lines.push(`- **Description**: ${sk.description}`);
|
|
30617
31642
|
lines.push(`- **Observations**: ${sk.content.split("\n").length} lines of knowledge`);
|
|
30618
31643
|
if (write && target) {
|
|
30619
|
-
const
|
|
30620
|
-
if (
|
|
30621
|
-
lines.push(`- [OK] **Written**: \`${
|
|
31644
|
+
const path27 = engine.writeSkill(sk, target);
|
|
31645
|
+
if (path27) {
|
|
31646
|
+
lines.push(`- [OK] **Written**: \`${path27}\``);
|
|
30622
31647
|
} else {
|
|
30623
31648
|
lines.push(`- [ERROR] Failed to write`);
|
|
30624
31649
|
}
|
|
@@ -32049,7 +33074,7 @@ fi
|
|
|
32049
33074
|
async function main2() {
|
|
32050
33075
|
const { homedir: homedir26 } = await import("os");
|
|
32051
33076
|
const { existsSync: existsSync18, readFileSync: readFileSync14 } = await import("fs");
|
|
32052
|
-
const
|
|
33077
|
+
const path27 = await import("path");
|
|
32053
33078
|
const { detectProject: detectProject2, findGitInSubdirs: findGitInSubdirs2, isSystemDirectory: isSystemDirectory2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
32054
33079
|
const { resolveServeProject: resolveServeProject2 } = await Promise.resolve().then(() => (init_serve_shared(), serve_shared_exports));
|
|
32055
33080
|
let safeCwd;
|
|
@@ -32058,7 +33083,7 @@ async function main2() {
|
|
|
32058
33083
|
} catch {
|
|
32059
33084
|
safeCwd = homedir26();
|
|
32060
33085
|
}
|
|
32061
|
-
const lastRootFile =
|
|
33086
|
+
const lastRootFile = path27.join(homedir26(), ".memorix", "last-project-root");
|
|
32062
33087
|
let lastKnownProjectRoot;
|
|
32063
33088
|
if (existsSync18(lastRootFile)) {
|
|
32064
33089
|
try {
|