memorix 1.2.10 → 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 +15 -0
- package/README.md +28 -2
- 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 +1783 -557
- 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 +15 -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 +3 -1
- package/docs/API_REFERENCE.md +20 -1
- package/docs/ARCHITECTURE.md +34 -1
- package/docs/DEVELOPMENT.md +3 -2
- package/docs/MODULES.md +27 -1
- package/docs/README.md +3 -3
- package/docs/dev-log/progress.txt +22 -0
- package/llms-full.txt +5 -1
- package/llms.txt +2 -0
- package/package.json +2 -1
- 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 +2 -2
- package/src/cli/command-guide.ts +9 -1
- package/src/cli/commands/codegraph.ts +1 -0
- package/src/cli/commands/memory.ts +217 -1
- 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/sdk.js
CHANGED
|
@@ -200,15 +200,15 @@ function loadSqlite() {
|
|
|
200
200
|
throw new Error("[memorix] SQLite is unavailable (better-sqlite3, node:sqlite, and bun:sqlite failed)");
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
|
-
function createDatabase(
|
|
203
|
+
function createDatabase(path27, options) {
|
|
204
204
|
const Sqlite = loadSqlite();
|
|
205
205
|
try {
|
|
206
|
-
return addCompatibility(instantiateDatabase(Sqlite,
|
|
206
|
+
return addCompatibility(instantiateDatabase(Sqlite, path27, options));
|
|
207
207
|
} catch (error) {
|
|
208
208
|
if (driver !== "better-sqlite3" || !isNativeBindingFailure(error)) throw error;
|
|
209
209
|
Database = loadNodeSqlite();
|
|
210
210
|
driver = "node:sqlite";
|
|
211
|
-
return addCompatibility(instantiateDatabase(Database,
|
|
211
|
+
return addCompatibility(instantiateDatabase(Database, path27, options));
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
var Database, driver, requireFromHere;
|
|
@@ -335,7 +335,7 @@ function closeAllDatabases() {
|
|
|
335
335
|
_dbCache.delete(key);
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
|
-
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;
|
|
338
|
+
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;
|
|
339
339
|
var init_sqlite_db = __esm({
|
|
340
340
|
"src/store/sqlite-db.ts"() {
|
|
341
341
|
"use strict";
|
|
@@ -761,6 +761,57 @@ CREATE TABLE IF NOT EXISTS knowledge_claim_events (
|
|
|
761
761
|
" FOREIGN KEY (workflowId) REFERENCES knowledge_workflows(id) ON DELETE CASCADE",
|
|
762
762
|
");"
|
|
763
763
|
].join("\n");
|
|
764
|
+
CREATE_LONG_TERM_MEMORIES_TABLE = `
|
|
765
|
+
CREATE TABLE IF NOT EXISTS long_term_memories (
|
|
766
|
+
id TEXT PRIMARY KEY,
|
|
767
|
+
originProjectId TEXT NOT NULL,
|
|
768
|
+
ownerId TEXT NOT NULL,
|
|
769
|
+
scope TEXT NOT NULL,
|
|
770
|
+
kind TEXT NOT NULL,
|
|
771
|
+
state TEXT NOT NULL,
|
|
772
|
+
portability TEXT NOT NULL DEFAULT 'project-bound',
|
|
773
|
+
title TEXT NOT NULL,
|
|
774
|
+
content TEXT NOT NULL,
|
|
775
|
+
factsJson TEXT NOT NULL DEFAULT '[]',
|
|
776
|
+
tagsJson TEXT NOT NULL DEFAULT '[]',
|
|
777
|
+
applicability TEXT,
|
|
778
|
+
origin TEXT NOT NULL,
|
|
779
|
+
createdAt TEXT NOT NULL,
|
|
780
|
+
updatedAt TEXT NOT NULL,
|
|
781
|
+
qualifiedAt TEXT,
|
|
782
|
+
approvedAt TEXT,
|
|
783
|
+
archivedAt TEXT,
|
|
784
|
+
supersededBy TEXT,
|
|
785
|
+
lastValidatedAt TEXT,
|
|
786
|
+
accessCount INTEGER NOT NULL DEFAULT 0,
|
|
787
|
+
lastAccessedAt TEXT
|
|
788
|
+
);
|
|
789
|
+
`;
|
|
790
|
+
CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE = `
|
|
791
|
+
CREATE TABLE IF NOT EXISTS long_term_memory_evidence (
|
|
792
|
+
id TEXT PRIMARY KEY,
|
|
793
|
+
memoryId TEXT NOT NULL,
|
|
794
|
+
kind TEXT NOT NULL,
|
|
795
|
+
referenceId TEXT NOT NULL,
|
|
796
|
+
relation TEXT NOT NULL,
|
|
797
|
+
locator TEXT,
|
|
798
|
+
capturedHash TEXT,
|
|
799
|
+
createdAt TEXT NOT NULL,
|
|
800
|
+
FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
|
|
801
|
+
);
|
|
802
|
+
`;
|
|
803
|
+
CREATE_LONG_TERM_MEMORY_EVENTS_TABLE = `
|
|
804
|
+
CREATE TABLE IF NOT EXISTS long_term_memory_events (
|
|
805
|
+
id TEXT PRIMARY KEY,
|
|
806
|
+
memoryId TEXT NOT NULL,
|
|
807
|
+
kind TEXT NOT NULL,
|
|
808
|
+
fromState TEXT,
|
|
809
|
+
toState TEXT,
|
|
810
|
+
detail TEXT,
|
|
811
|
+
createdAt TEXT NOT NULL,
|
|
812
|
+
FOREIGN KEY (memoryId) REFERENCES long_term_memories(id) ON DELETE CASCADE
|
|
813
|
+
);
|
|
814
|
+
`;
|
|
764
815
|
CREATE_MAINTENANCE_JOBS_TABLE = `
|
|
765
816
|
CREATE TABLE IF NOT EXISTS maintenance_jobs (
|
|
766
817
|
id TEXT PRIMARY KEY,
|
|
@@ -856,6 +907,11 @@ CREATE INDEX IF NOT EXISTS idx_knowledge_page_claims_claim ON knowledge_page_cla
|
|
|
856
907
|
CREATE INDEX IF NOT EXISTS idx_knowledge_proposals_workspace_status ON knowledge_proposals(workspaceId, status, createdAt DESC);
|
|
857
908
|
CREATE INDEX IF NOT EXISTS idx_knowledge_workflows_workspace_status ON knowledge_workflows(workspaceId, status, updatedAt DESC);
|
|
858
909
|
CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowledge_workflow_runs(projectId, workflowId, startedAt DESC);
|
|
910
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC);
|
|
911
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC);
|
|
912
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC);
|
|
913
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt);
|
|
914
|
+
CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt);
|
|
859
915
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_ready ON maintenance_jobs(status, run_after);
|
|
860
916
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_project ON maintenance_jobs(project_id, status, run_after);
|
|
861
917
|
CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_targets(updated_at);
|
|
@@ -944,6 +1000,19 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
944
1000
|
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)");
|
|
945
1001
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key)");
|
|
946
1002
|
}
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
id: "1.3-long-term-memory",
|
|
1006
|
+
apply: (db2) => {
|
|
1007
|
+
db2.exec(CREATE_LONG_TERM_MEMORIES_TABLE);
|
|
1008
|
+
db2.exec(CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE);
|
|
1009
|
+
db2.exec(CREATE_LONG_TERM_MEMORY_EVENTS_TABLE);
|
|
1010
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_owner_state ON long_term_memories(ownerId, state, updatedAt DESC)");
|
|
1011
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_project_state ON long_term_memories(originProjectId, state, updatedAt DESC)");
|
|
1012
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memories_scope_portability ON long_term_memories(scope, portability, state, updatedAt DESC)");
|
|
1013
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_evidence_memory ON long_term_memory_evidence(memoryId, createdAt)");
|
|
1014
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_long_term_memory_events_memory ON long_term_memory_events(memoryId, createdAt)");
|
|
1015
|
+
}
|
|
947
1016
|
}
|
|
948
1017
|
];
|
|
949
1018
|
_dbCache = /* @__PURE__ */ new Map();
|
|
@@ -2732,9 +2801,9 @@ function parseTomlConfig(content, filePath) {
|
|
|
2732
2801
|
}
|
|
2733
2802
|
return root;
|
|
2734
2803
|
}
|
|
2735
|
-
function ensureTable(root,
|
|
2804
|
+
function ensureTable(root, path27) {
|
|
2736
2805
|
let cursor = root;
|
|
2737
|
-
for (const part of
|
|
2806
|
+
for (const part of path27) {
|
|
2738
2807
|
const value = cursor[part];
|
|
2739
2808
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
2740
2809
|
cursor[part] = {};
|
|
@@ -3610,9 +3679,10 @@ function parseBatchLimit(error) {
|
|
|
3610
3679
|
}
|
|
3611
3680
|
return null;
|
|
3612
3681
|
}
|
|
3613
|
-
async function fetchWithRetry(url, apiKey, body, attempt = 0) {
|
|
3682
|
+
async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0) {
|
|
3614
3683
|
const controller = new AbortController();
|
|
3615
|
-
const
|
|
3684
|
+
const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) ? Math.max(100, Math.min(Math.floor(options.timeoutMs), 6e4)) : 1e4;
|
|
3685
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
3616
3686
|
let response;
|
|
3617
3687
|
try {
|
|
3618
3688
|
response = await fetch(url, {
|
|
@@ -3627,7 +3697,7 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
|
|
|
3627
3697
|
} catch (err) {
|
|
3628
3698
|
clearTimeout(timeout);
|
|
3629
3699
|
if (err instanceof Error && err.name === "AbortError") {
|
|
3630
|
-
throw new Error(`Embedding API timeout after
|
|
3700
|
+
throw new Error(`Embedding API timeout after ${timeoutMs}ms: ${url}`);
|
|
3631
3701
|
}
|
|
3632
3702
|
throw err;
|
|
3633
3703
|
}
|
|
@@ -3635,13 +3705,13 @@ async function fetchWithRetry(url, apiKey, body, attempt = 0) {
|
|
|
3635
3705
|
if (response.ok) {
|
|
3636
3706
|
return response.json();
|
|
3637
3707
|
}
|
|
3638
|
-
if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
|
|
3708
|
+
if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
|
|
3639
3709
|
const delay = BASE_DELAY_MS * Math.pow(2, attempt);
|
|
3640
3710
|
const retryAfter = response.headers.get("retry-after");
|
|
3641
3711
|
const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : delay;
|
|
3642
3712
|
console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
|
|
3643
3713
|
await new Promise((resolve2) => setTimeout(resolve2, waitMs));
|
|
3644
|
-
return fetchWithRetry(url, apiKey, body, attempt + 1);
|
|
3714
|
+
return fetchWithRetry(url, apiKey, body, options, attempt + 1);
|
|
3645
3715
|
}
|
|
3646
3716
|
const errorText2 = await response.text().catch(() => "unknown error");
|
|
3647
3717
|
throw new Error(`Embedding API error (${response.status}): ${errorText2}`);
|
|
@@ -3697,7 +3767,10 @@ var init_api_provider = __esm({
|
|
|
3697
3767
|
}
|
|
3698
3768
|
} else {
|
|
3699
3769
|
if (!allowNetworkProbe) return null;
|
|
3700
|
-
probeDimensions = await _APIEmbeddingProvider.probeAPI(config
|
|
3770
|
+
probeDimensions = await _APIEmbeddingProvider.probeAPI(config, {
|
|
3771
|
+
timeoutMs: options.requestTimeoutMs,
|
|
3772
|
+
retry: options.retry
|
|
3773
|
+
});
|
|
3701
3774
|
console.error(`[memorix] API embedding: ${config.model} @ ${config.baseUrl} (${probeDimensions}d)`);
|
|
3702
3775
|
saveCachedDims(config, probeDimensions).catch(() => {
|
|
3703
3776
|
});
|
|
@@ -3739,7 +3812,7 @@ var init_api_provider = __esm({
|
|
|
3739
3812
|
baseUrl = baseUrl.replace(/\/+$/, "");
|
|
3740
3813
|
return { apiKey, baseUrl, model, requestedDimensions };
|
|
3741
3814
|
}
|
|
3742
|
-
static async probeAPI(config) {
|
|
3815
|
+
static async probeAPI(config, options) {
|
|
3743
3816
|
const body = {
|
|
3744
3817
|
model: config.model,
|
|
3745
3818
|
input: "dimension probe"
|
|
@@ -3750,14 +3823,15 @@ var init_api_provider = __esm({
|
|
|
3750
3823
|
const response = await fetchWithRetry(
|
|
3751
3824
|
`${config.baseUrl}/embeddings`,
|
|
3752
3825
|
config.apiKey,
|
|
3753
|
-
body
|
|
3826
|
+
body,
|
|
3827
|
+
options
|
|
3754
3828
|
);
|
|
3755
3829
|
if (response.data.length === 0 || !response.data[0].embedding) {
|
|
3756
3830
|
throw new Error("API probe returned no embeddings; check model name and API key");
|
|
3757
3831
|
}
|
|
3758
3832
|
return response.data[0].embedding.length;
|
|
3759
3833
|
}
|
|
3760
|
-
async embed(text) {
|
|
3834
|
+
async embed(text, options) {
|
|
3761
3835
|
const normalized = normalizeText(text);
|
|
3762
3836
|
const hash4 = textHash2(normalized, this.cacheKeyNamespace);
|
|
3763
3837
|
if (diskCacheLoaded) {
|
|
@@ -3775,7 +3849,8 @@ var init_api_provider = __esm({
|
|
|
3775
3849
|
const response = await fetchWithRetry(
|
|
3776
3850
|
`${this.config.baseUrl}/embeddings`,
|
|
3777
3851
|
this.config.apiKey,
|
|
3778
|
-
body
|
|
3852
|
+
body,
|
|
3853
|
+
options
|
|
3779
3854
|
);
|
|
3780
3855
|
const embedding2 = response.data[0].embedding;
|
|
3781
3856
|
if (embedding2.length !== this.dimensions) {
|
|
@@ -3809,7 +3884,7 @@ var init_api_provider = __esm({
|
|
|
3809
3884
|
scheduleDiskSave();
|
|
3810
3885
|
return embedding;
|
|
3811
3886
|
}
|
|
3812
|
-
async embedBatch(texts) {
|
|
3887
|
+
async embedBatch(texts, options) {
|
|
3813
3888
|
await ensureDiskCacheLoaded();
|
|
3814
3889
|
const normalizedTexts = texts.map(normalizeText);
|
|
3815
3890
|
const results = new Array(texts.length);
|
|
@@ -3843,7 +3918,8 @@ var init_api_provider = __esm({
|
|
|
3843
3918
|
const response = await fetchWithRetry(
|
|
3844
3919
|
`${this.config.baseUrl}/embeddings`,
|
|
3845
3920
|
this.config.apiKey,
|
|
3846
|
-
body
|
|
3921
|
+
body,
|
|
3922
|
+
options
|
|
3847
3923
|
);
|
|
3848
3924
|
this.trackUsage(response);
|
|
3849
3925
|
for (const item of response.data) {
|
|
@@ -4020,9 +4096,9 @@ function wrapProvider(candidate) {
|
|
|
4020
4096
|
return {
|
|
4021
4097
|
name: candidate.name,
|
|
4022
4098
|
dimensions: candidate.dimensions,
|
|
4023
|
-
async embed(text) {
|
|
4099
|
+
async embed(text, options) {
|
|
4024
4100
|
try {
|
|
4025
|
-
return await candidate.embed(text);
|
|
4101
|
+
return await candidate.embed(text, options);
|
|
4026
4102
|
} catch (error) {
|
|
4027
4103
|
if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
|
|
4028
4104
|
warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
|
|
@@ -4030,7 +4106,7 @@ function wrapProvider(candidate) {
|
|
|
4030
4106
|
if (fallback) {
|
|
4031
4107
|
provider = wrapProvider(fallback);
|
|
4032
4108
|
warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
|
|
4033
|
-
return provider.embed(text);
|
|
4109
|
+
return provider.embed(text, options);
|
|
4034
4110
|
}
|
|
4035
4111
|
}
|
|
4036
4112
|
if (isTemporaryEmbeddingFailure(error)) {
|
|
@@ -4039,9 +4115,9 @@ function wrapProvider(candidate) {
|
|
|
4039
4115
|
throw error;
|
|
4040
4116
|
}
|
|
4041
4117
|
},
|
|
4042
|
-
async embedBatch(texts) {
|
|
4118
|
+
async embedBatch(texts, options) {
|
|
4043
4119
|
try {
|
|
4044
|
-
return await candidate.embedBatch(texts);
|
|
4120
|
+
return await candidate.embedBatch(texts, options);
|
|
4045
4121
|
} catch (error) {
|
|
4046
4122
|
if (kind === "api" && getEmbeddingMode2() === "auto" && isTemporaryEmbeddingFailure(error)) {
|
|
4047
4123
|
warnOnce("[memorix] API embedding temporarily unavailable \u2014 switching to local fallback provider");
|
|
@@ -4049,7 +4125,7 @@ function wrapProvider(candidate) {
|
|
|
4049
4125
|
if (fallback) {
|
|
4050
4126
|
provider = wrapProvider(fallback);
|
|
4051
4127
|
warnOnce(`[memorix] Embedding fallback activated: ${provider.name} (${provider.dimensions}d)`);
|
|
4052
|
-
return provider.embedBatch(texts);
|
|
4128
|
+
return provider.embedBatch(texts, options);
|
|
4053
4129
|
}
|
|
4054
4130
|
}
|
|
4055
4131
|
if (isTemporaryEmbeddingFailure(error)) {
|
|
@@ -4105,14 +4181,14 @@ async function getEmbeddingProvider(options = {}) {
|
|
|
4105
4181
|
return provider;
|
|
4106
4182
|
}
|
|
4107
4183
|
if (mode === "api") {
|
|
4108
|
-
const initialized = await createAPIProvider();
|
|
4184
|
+
const initialized = await createAPIProvider(options);
|
|
4109
4185
|
if (!initialized) return null;
|
|
4110
4186
|
provider = wrapProvider(initialized);
|
|
4111
4187
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
4112
4188
|
return provider;
|
|
4113
4189
|
}
|
|
4114
4190
|
if (hasAPIEmbeddingConfig()) {
|
|
4115
|
-
const initialized = await createAPIProvider();
|
|
4191
|
+
const initialized = await createAPIProvider(options);
|
|
4116
4192
|
if (initialized) {
|
|
4117
4193
|
provider = wrapProvider(initialized);
|
|
4118
4194
|
warnOnce(`[memorix] Embedding provider: ${provider.name} (${provider.dimensions}d)`);
|
|
@@ -8968,12 +9044,12 @@ import { createHash as createHash4 } from "crypto";
|
|
|
8968
9044
|
function digest(input) {
|
|
8969
9045
|
return createHash4("sha256").update(input).digest("hex").slice(0, 16);
|
|
8970
9046
|
}
|
|
8971
|
-
function normalizeCodePath(
|
|
8972
|
-
return
|
|
9047
|
+
function normalizeCodePath(path27) {
|
|
9048
|
+
return path27.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
|
|
8973
9049
|
}
|
|
8974
|
-
function makeCodeFileId(projectId,
|
|
9050
|
+
function makeCodeFileId(projectId, path27) {
|
|
8975
9051
|
return `file:${digest(`${projectId}
|
|
8976
|
-
${normalizeCodePath(
|
|
9052
|
+
${normalizeCodePath(path27)}`)}`;
|
|
8977
9053
|
}
|
|
8978
9054
|
function makeCodeSymbolId(input) {
|
|
8979
9055
|
return `symbol:${digest([
|
|
@@ -9447,8 +9523,8 @@ var init_store = __esm({
|
|
|
9447
9523
|
});
|
|
9448
9524
|
tx();
|
|
9449
9525
|
}
|
|
9450
|
-
getFile(projectId,
|
|
9451
|
-
const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(
|
|
9526
|
+
getFile(projectId, path27) {
|
|
9527
|
+
const row = this.db.prepare(`SELECT * FROM code_files WHERE projectId = ? AND path = ?`).get(projectId, normalizeCodePath(path27));
|
|
9452
9528
|
return row ? rowToFile(row) : null;
|
|
9453
9529
|
}
|
|
9454
9530
|
listFiles(projectId) {
|
|
@@ -9683,30 +9759,30 @@ function normalizeCodeGraphExcludePatterns(exclude) {
|
|
|
9683
9759
|
...(exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean)
|
|
9684
9760
|
])];
|
|
9685
9761
|
}
|
|
9686
|
-
function isCodeGraphExcludedPath(
|
|
9687
|
-
const normalized = normalizeCodePath2(
|
|
9762
|
+
function isCodeGraphExcludedPath(path27, exclude) {
|
|
9763
|
+
const normalized = normalizeCodePath2(path27);
|
|
9688
9764
|
return normalizeCodeGraphExcludePatterns(exclude).some((pattern) => matchesPattern(normalized, normalizeCodePath2(pattern)));
|
|
9689
9765
|
}
|
|
9690
|
-
function normalizeCodePath2(
|
|
9691
|
-
return
|
|
9766
|
+
function normalizeCodePath2(path27) {
|
|
9767
|
+
return path27.replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
9692
9768
|
}
|
|
9693
|
-
function matchesPattern(
|
|
9769
|
+
function matchesPattern(path27, pattern) {
|
|
9694
9770
|
if (pattern.endsWith("/**")) {
|
|
9695
9771
|
const base = pattern.slice(0, -3);
|
|
9696
9772
|
if (base.startsWith("**/")) {
|
|
9697
9773
|
const suffix = base.slice(3);
|
|
9698
|
-
return
|
|
9774
|
+
return path27 === suffix || path27.endsWith(`/${suffix}`) || path27.includes(`/${suffix}/`);
|
|
9699
9775
|
}
|
|
9700
9776
|
if (!base.includes("/")) {
|
|
9701
|
-
return
|
|
9777
|
+
return path27 === base || path27.startsWith(`${base}/`) || path27.includes(`/${base}/`);
|
|
9702
9778
|
}
|
|
9703
|
-
return
|
|
9779
|
+
return path27 === base || path27.startsWith(`${base}/`);
|
|
9704
9780
|
}
|
|
9705
9781
|
if (pattern.startsWith("**/")) {
|
|
9706
9782
|
const suffix = pattern.slice(3);
|
|
9707
|
-
return
|
|
9783
|
+
return path27 === suffix || path27.endsWith(`/${suffix}`);
|
|
9708
9784
|
}
|
|
9709
|
-
return
|
|
9785
|
+
return path27 === pattern || path27.startsWith(`${pattern}/`);
|
|
9710
9786
|
}
|
|
9711
9787
|
var DEFAULT_CODEGRAPH_EXCLUDES;
|
|
9712
9788
|
var init_exclude = __esm({
|
|
@@ -9751,12 +9827,12 @@ import { join as join4, relative } from "path";
|
|
|
9751
9827
|
function hashText(text) {
|
|
9752
9828
|
return createHash6("sha256").update(text).digest("hex");
|
|
9753
9829
|
}
|
|
9754
|
-
function extension(
|
|
9755
|
-
const index =
|
|
9756
|
-
return index === -1 ? "" :
|
|
9830
|
+
function extension(path27) {
|
|
9831
|
+
const index = path27.lastIndexOf(".");
|
|
9832
|
+
return index === -1 ? "" : path27.slice(index);
|
|
9757
9833
|
}
|
|
9758
|
-
function languageForPath(
|
|
9759
|
-
const ext = extension(
|
|
9834
|
+
function languageForPath(path27) {
|
|
9835
|
+
const ext = extension(path27);
|
|
9760
9836
|
return LANGUAGE_BY_EXTENSION.get(ext) ?? "unknown";
|
|
9761
9837
|
}
|
|
9762
9838
|
function resolveMaxFileBytes(value) {
|
|
@@ -10237,7 +10313,7 @@ function resolveObservationCodeRefs(obs, lookup) {
|
|
|
10237
10313
|
}
|
|
10238
10314
|
function createStoreLookup(store, projectId) {
|
|
10239
10315
|
return {
|
|
10240
|
-
findFile: (
|
|
10316
|
+
findFile: (path27) => store.getFile(projectId, path27) ?? void 0,
|
|
10241
10317
|
findSymbols: (names, hintedFileIds) => store.findSymbolsByNames(projectId, names, hintedFileIds)
|
|
10242
10318
|
};
|
|
10243
10319
|
}
|
|
@@ -10250,7 +10326,7 @@ function createSnapshotLookup(files, symbols) {
|
|
|
10250
10326
|
symbolsByName.set(symbol.name, group);
|
|
10251
10327
|
}
|
|
10252
10328
|
return {
|
|
10253
|
-
findFile: (
|
|
10329
|
+
findFile: (path27) => filesByPath.get(normalizeCodePath(path27)),
|
|
10254
10330
|
findSymbols: (names, hintedFileIds) => {
|
|
10255
10331
|
const hinted = new Set(hintedFileIds);
|
|
10256
10332
|
const found = [];
|
|
@@ -11867,8 +11943,8 @@ __export(task_lens_exports, {
|
|
|
11867
11943
|
scoreLensSource: () => scoreLensSource,
|
|
11868
11944
|
shouldShowLensSource: () => shouldShowLensSource
|
|
11869
11945
|
});
|
|
11870
|
-
function normalizePath2(
|
|
11871
|
-
return
|
|
11946
|
+
function normalizePath2(path27) {
|
|
11947
|
+
return path27.replace(/\\/g, "/");
|
|
11872
11948
|
}
|
|
11873
11949
|
function tokenize2(text) {
|
|
11874
11950
|
return (text.toLowerCase().match(/[a-z0-9_./-]+|[\u4e00-\u9fff]+/g) ?? []).map((token) => token.trim()).filter((token) => token.length > 1 && !STOP_WORDS.has(token));
|
|
@@ -11930,8 +12006,8 @@ function isContinuationTask(task) {
|
|
|
11930
12006
|
(keyword) => containsTaskKeyword(normalized, keyword)
|
|
11931
12007
|
);
|
|
11932
12008
|
}
|
|
11933
|
-
function pathKindScore(
|
|
11934
|
-
const normalized = normalizePath2(
|
|
12009
|
+
function pathKindScore(path27, lens) {
|
|
12010
|
+
const normalized = normalizePath2(path27).toLowerCase();
|
|
11935
12011
|
const name = normalized.split("/").pop() ?? normalized;
|
|
11936
12012
|
const inDocs = normalized.startsWith("docs/") || name === "readme.md" || name.includes("readme");
|
|
11937
12013
|
const isTest = /(^|\/)(tests?|__tests__|spec|fixtures?)\//.test(normalized) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized) || /\.(test|spec)\.py$/.test(normalized);
|
|
@@ -11979,8 +12055,8 @@ function sourceTaskMatchScore(source, task) {
|
|
|
11979
12055
|
if (matches === 1) return 24;
|
|
11980
12056
|
return 0;
|
|
11981
12057
|
}
|
|
11982
|
-
function fallbackPathRank(
|
|
11983
|
-
const normalized = normalizePath2(
|
|
12058
|
+
function fallbackPathRank(path27) {
|
|
12059
|
+
const normalized = normalizePath2(path27);
|
|
11984
12060
|
if (normalized.startsWith("src/")) return 0;
|
|
11985
12061
|
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 1;
|
|
11986
12062
|
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
@@ -11989,11 +12065,11 @@ function fallbackPathRank(path26) {
|
|
|
11989
12065
|
}
|
|
11990
12066
|
function rankLensPaths(paths, lens, task) {
|
|
11991
12067
|
const tokens = tokenize2(task ?? "");
|
|
11992
|
-
return [...new Set(paths.map(normalizePath2))].map((
|
|
11993
|
-
path:
|
|
12068
|
+
return [...new Set(paths.map(normalizePath2))].map((path27, index) => ({
|
|
12069
|
+
path: path27,
|
|
11994
12070
|
index,
|
|
11995
|
-
score: pathKindScore(
|
|
11996
|
-
fallback: fallbackPathRank(
|
|
12071
|
+
score: pathKindScore(path27, lens) + tokenScore(path27, tokens),
|
|
12072
|
+
fallback: fallbackPathRank(path27)
|
|
11997
12073
|
})).sort((a, b) => b.score - a.score || a.fallback - b.fallback || a.index - b.index || a.path.localeCompare(b.path)).map((item) => item.path);
|
|
11998
12074
|
}
|
|
11999
12075
|
function lensPathCandidates(lens) {
|
|
@@ -16245,6 +16321,827 @@ var init_session = __esm({
|
|
|
16245
16321
|
}
|
|
16246
16322
|
});
|
|
16247
16323
|
|
|
16324
|
+
// src/memory/owner.ts
|
|
16325
|
+
var owner_exports = {};
|
|
16326
|
+
__export(owner_exports, {
|
|
16327
|
+
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
16328
|
+
});
|
|
16329
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
16330
|
+
import { promises as fs15 } from "fs";
|
|
16331
|
+
import path19 from "path";
|
|
16332
|
+
function ownerPath(dataDir) {
|
|
16333
|
+
const resolved = path19.resolve(dataDir);
|
|
16334
|
+
const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
|
|
16335
|
+
return path19.join(home, "memorix-user.json");
|
|
16336
|
+
}
|
|
16337
|
+
function normalizedOwnerId(value) {
|
|
16338
|
+
const trimmed = value?.trim();
|
|
16339
|
+
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
16340
|
+
}
|
|
16341
|
+
async function readOwnerFile(filePath) {
|
|
16342
|
+
try {
|
|
16343
|
+
const value = JSON.parse(await fs15.readFile(filePath, "utf8"));
|
|
16344
|
+
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
16345
|
+
return id ? { id, source: "local-file" } : void 0;
|
|
16346
|
+
} catch {
|
|
16347
|
+
return void 0;
|
|
16348
|
+
}
|
|
16349
|
+
}
|
|
16350
|
+
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
16351
|
+
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
16352
|
+
if (configured) return { id: configured, source: "environment" };
|
|
16353
|
+
const filePath = ownerPath(dataDir);
|
|
16354
|
+
const existing = await readOwnerFile(filePath);
|
|
16355
|
+
if (existing || !options.create) return existing;
|
|
16356
|
+
await fs15.mkdir(path19.dirname(filePath), { recursive: true });
|
|
16357
|
+
const created = {
|
|
16358
|
+
version: 1,
|
|
16359
|
+
id: "local-" + randomUUID8(),
|
|
16360
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16361
|
+
};
|
|
16362
|
+
try {
|
|
16363
|
+
await fs15.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
16364
|
+
return { id: created.id, source: "local-file" };
|
|
16365
|
+
} catch (error) {
|
|
16366
|
+
if (error?.code !== "EEXIST") throw error;
|
|
16367
|
+
const concurrent = await readOwnerFile(filePath);
|
|
16368
|
+
if (concurrent) return concurrent;
|
|
16369
|
+
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
16370
|
+
}
|
|
16371
|
+
}
|
|
16372
|
+
var ID_PATTERN;
|
|
16373
|
+
var init_owner = __esm({
|
|
16374
|
+
"src/memory/owner.ts"() {
|
|
16375
|
+
"use strict";
|
|
16376
|
+
init_esm_shims();
|
|
16377
|
+
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
16378
|
+
}
|
|
16379
|
+
});
|
|
16380
|
+
|
|
16381
|
+
// src/memory/long-term-store.ts
|
|
16382
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
16383
|
+
function optionalText5(value) {
|
|
16384
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
16385
|
+
}
|
|
16386
|
+
function arrayValue2(value) {
|
|
16387
|
+
if (typeof value !== "string" || !value) return [];
|
|
16388
|
+
try {
|
|
16389
|
+
const parsed = JSON.parse(value);
|
|
16390
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
16391
|
+
} catch {
|
|
16392
|
+
return [];
|
|
16393
|
+
}
|
|
16394
|
+
}
|
|
16395
|
+
function rowToMemory(row) {
|
|
16396
|
+
return {
|
|
16397
|
+
id: row.id,
|
|
16398
|
+
originProjectId: row.originProjectId,
|
|
16399
|
+
ownerId: row.ownerId,
|
|
16400
|
+
scope: row.scope,
|
|
16401
|
+
kind: row.kind,
|
|
16402
|
+
state: row.state,
|
|
16403
|
+
portability: row.portability,
|
|
16404
|
+
title: row.title,
|
|
16405
|
+
content: row.content,
|
|
16406
|
+
facts: arrayValue2(row.factsJson),
|
|
16407
|
+
tags: arrayValue2(row.tagsJson),
|
|
16408
|
+
...optionalText5(row.applicability) ? { applicability: row.applicability } : {},
|
|
16409
|
+
origin: row.origin,
|
|
16410
|
+
createdAt: row.createdAt,
|
|
16411
|
+
updatedAt: row.updatedAt,
|
|
16412
|
+
...optionalText5(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
16413
|
+
...optionalText5(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
16414
|
+
...optionalText5(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
16415
|
+
...optionalText5(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
16416
|
+
...optionalText5(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
16417
|
+
accessCount: Number(row.accessCount ?? 0),
|
|
16418
|
+
...optionalText5(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
16419
|
+
};
|
|
16420
|
+
}
|
|
16421
|
+
function rowToEvidence2(row) {
|
|
16422
|
+
return {
|
|
16423
|
+
id: row.id,
|
|
16424
|
+
memoryId: row.memoryId,
|
|
16425
|
+
kind: row.kind,
|
|
16426
|
+
referenceId: row.referenceId,
|
|
16427
|
+
relation: row.relation,
|
|
16428
|
+
...optionalText5(row.locator) ? { locator: row.locator } : {},
|
|
16429
|
+
...optionalText5(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
16430
|
+
createdAt: row.createdAt
|
|
16431
|
+
};
|
|
16432
|
+
}
|
|
16433
|
+
function rowToEvent2(row) {
|
|
16434
|
+
return {
|
|
16435
|
+
id: row.id,
|
|
16436
|
+
memoryId: row.memoryId,
|
|
16437
|
+
kind: row.kind,
|
|
16438
|
+
...optionalText5(row.fromState) ? { fromState: row.fromState } : {},
|
|
16439
|
+
...optionalText5(row.toState) ? { toState: row.toState } : {},
|
|
16440
|
+
...optionalText5(row.detail) ? { detail: row.detail } : {},
|
|
16441
|
+
createdAt: row.createdAt
|
|
16442
|
+
};
|
|
16443
|
+
}
|
|
16444
|
+
var LongTermMemoryStore;
|
|
16445
|
+
var init_long_term_store = __esm({
|
|
16446
|
+
"src/memory/long-term-store.ts"() {
|
|
16447
|
+
"use strict";
|
|
16448
|
+
init_esm_shims();
|
|
16449
|
+
init_sqlite_db();
|
|
16450
|
+
LongTermMemoryStore = class {
|
|
16451
|
+
db = null;
|
|
16452
|
+
async init(dataDir) {
|
|
16453
|
+
this.db = getDatabase(dataDir);
|
|
16454
|
+
}
|
|
16455
|
+
transaction(fn) {
|
|
16456
|
+
return this.db.transaction(fn)();
|
|
16457
|
+
}
|
|
16458
|
+
get(id) {
|
|
16459
|
+
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
16460
|
+
return row ? rowToMemory(row) : void 0;
|
|
16461
|
+
}
|
|
16462
|
+
list(options = {}) {
|
|
16463
|
+
const limit = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
16464
|
+
const clauses = [];
|
|
16465
|
+
const params = [];
|
|
16466
|
+
if (options.ownerId) {
|
|
16467
|
+
clauses.push("ownerId = ?");
|
|
16468
|
+
params.push(options.ownerId);
|
|
16469
|
+
}
|
|
16470
|
+
if (options.originProjectId) {
|
|
16471
|
+
clauses.push("originProjectId = ?");
|
|
16472
|
+
params.push(options.originProjectId);
|
|
16473
|
+
}
|
|
16474
|
+
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
16475
|
+
const rows = this.db.prepare(
|
|
16476
|
+
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
16477
|
+
).all(...params, limit);
|
|
16478
|
+
return rows.map(rowToMemory);
|
|
16479
|
+
}
|
|
16480
|
+
insert(memory) {
|
|
16481
|
+
this.db.prepare(`
|
|
16482
|
+
INSERT INTO long_term_memories (
|
|
16483
|
+
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
16484
|
+
title, content, factsJson, tagsJson, applicability, origin,
|
|
16485
|
+
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
16486
|
+
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
16487
|
+
) VALUES (
|
|
16488
|
+
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
16489
|
+
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
16490
|
+
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
16491
|
+
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
16492
|
+
)
|
|
16493
|
+
`).run({
|
|
16494
|
+
...memory,
|
|
16495
|
+
factsJson: JSON.stringify(memory.facts),
|
|
16496
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
16497
|
+
applicability: memory.applicability ?? null,
|
|
16498
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
16499
|
+
approvedAt: memory.approvedAt ?? null,
|
|
16500
|
+
archivedAt: memory.archivedAt ?? null,
|
|
16501
|
+
supersededBy: memory.supersededBy ?? null,
|
|
16502
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
16503
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
16504
|
+
});
|
|
16505
|
+
}
|
|
16506
|
+
update(memory) {
|
|
16507
|
+
this.db.prepare(`
|
|
16508
|
+
UPDATE long_term_memories SET
|
|
16509
|
+
scope = @scope,
|
|
16510
|
+
kind = @kind,
|
|
16511
|
+
state = @state,
|
|
16512
|
+
portability = @portability,
|
|
16513
|
+
title = @title,
|
|
16514
|
+
content = @content,
|
|
16515
|
+
factsJson = @factsJson,
|
|
16516
|
+
tagsJson = @tagsJson,
|
|
16517
|
+
applicability = @applicability,
|
|
16518
|
+
updatedAt = @updatedAt,
|
|
16519
|
+
qualifiedAt = @qualifiedAt,
|
|
16520
|
+
approvedAt = @approvedAt,
|
|
16521
|
+
archivedAt = @archivedAt,
|
|
16522
|
+
supersededBy = @supersededBy,
|
|
16523
|
+
lastValidatedAt = @lastValidatedAt,
|
|
16524
|
+
accessCount = @accessCount,
|
|
16525
|
+
lastAccessedAt = @lastAccessedAt
|
|
16526
|
+
WHERE id = @id
|
|
16527
|
+
`).run({
|
|
16528
|
+
...memory,
|
|
16529
|
+
factsJson: JSON.stringify(memory.facts),
|
|
16530
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
16531
|
+
applicability: memory.applicability ?? null,
|
|
16532
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
16533
|
+
approvedAt: memory.approvedAt ?? null,
|
|
16534
|
+
archivedAt: memory.archivedAt ?? null,
|
|
16535
|
+
supersededBy: memory.supersededBy ?? null,
|
|
16536
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
16537
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
16538
|
+
});
|
|
16539
|
+
}
|
|
16540
|
+
insertEvidence(input) {
|
|
16541
|
+
const evidence = {
|
|
16542
|
+
id: randomUUID9(),
|
|
16543
|
+
...input,
|
|
16544
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
16545
|
+
};
|
|
16546
|
+
this.db.prepare(`
|
|
16547
|
+
INSERT INTO long_term_memory_evidence (
|
|
16548
|
+
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
16549
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
16550
|
+
`).run(
|
|
16551
|
+
evidence.id,
|
|
16552
|
+
evidence.memoryId,
|
|
16553
|
+
evidence.kind,
|
|
16554
|
+
evidence.referenceId,
|
|
16555
|
+
evidence.relation,
|
|
16556
|
+
evidence.locator ?? null,
|
|
16557
|
+
evidence.capturedHash ?? null,
|
|
16558
|
+
evidence.createdAt
|
|
16559
|
+
);
|
|
16560
|
+
return evidence;
|
|
16561
|
+
}
|
|
16562
|
+
listEvidence(memoryId) {
|
|
16563
|
+
return this.db.prepare(`
|
|
16564
|
+
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
16565
|
+
`).all(memoryId).map(rowToEvidence2);
|
|
16566
|
+
}
|
|
16567
|
+
recordEvent(input) {
|
|
16568
|
+
const event = {
|
|
16569
|
+
id: randomUUID9(),
|
|
16570
|
+
...input,
|
|
16571
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
16572
|
+
};
|
|
16573
|
+
this.db.prepare(`
|
|
16574
|
+
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
16575
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
16576
|
+
`).run(
|
|
16577
|
+
event.id,
|
|
16578
|
+
event.memoryId,
|
|
16579
|
+
event.kind,
|
|
16580
|
+
event.fromState ?? null,
|
|
16581
|
+
event.toState ?? null,
|
|
16582
|
+
event.detail ?? null,
|
|
16583
|
+
event.createdAt
|
|
16584
|
+
);
|
|
16585
|
+
return event;
|
|
16586
|
+
}
|
|
16587
|
+
listEvents(memoryId) {
|
|
16588
|
+
return this.db.prepare(`
|
|
16589
|
+
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
16590
|
+
`).all(memoryId).map(rowToEvent2);
|
|
16591
|
+
}
|
|
16592
|
+
markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
16593
|
+
const unique2 = [...new Set(ids.filter(Boolean))];
|
|
16594
|
+
if (unique2.length === 0) return;
|
|
16595
|
+
const statement = this.db.prepare(`
|
|
16596
|
+
UPDATE long_term_memories
|
|
16597
|
+
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
16598
|
+
WHERE id = ?
|
|
16599
|
+
`);
|
|
16600
|
+
this.db.transaction(() => {
|
|
16601
|
+
for (const id of unique2) statement.run(at, id);
|
|
16602
|
+
})();
|
|
16603
|
+
}
|
|
16604
|
+
};
|
|
16605
|
+
}
|
|
16606
|
+
});
|
|
16607
|
+
|
|
16608
|
+
// src/memory/long-term-types.ts
|
|
16609
|
+
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
16610
|
+
var init_long_term_types = __esm({
|
|
16611
|
+
"src/memory/long-term-types.ts"() {
|
|
16612
|
+
"use strict";
|
|
16613
|
+
init_esm_shims();
|
|
16614
|
+
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
16615
|
+
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
16616
|
+
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
16617
|
+
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
16618
|
+
"observation",
|
|
16619
|
+
"claim",
|
|
16620
|
+
"workflow",
|
|
16621
|
+
"session",
|
|
16622
|
+
"checkpoint",
|
|
16623
|
+
"git",
|
|
16624
|
+
"code",
|
|
16625
|
+
"test",
|
|
16626
|
+
"run",
|
|
16627
|
+
"document",
|
|
16628
|
+
"manual",
|
|
16629
|
+
"user"
|
|
16630
|
+
];
|
|
16631
|
+
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
16632
|
+
"supports",
|
|
16633
|
+
"verifies",
|
|
16634
|
+
"derives",
|
|
16635
|
+
"user-confirmed"
|
|
16636
|
+
];
|
|
16637
|
+
}
|
|
16638
|
+
});
|
|
16639
|
+
|
|
16640
|
+
// src/memory/long-term.ts
|
|
16641
|
+
var long_term_exports = {};
|
|
16642
|
+
__export(long_term_exports, {
|
|
16643
|
+
approveLongTermMemory: () => approveLongTermMemory,
|
|
16644
|
+
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
16645
|
+
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
16646
|
+
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
16647
|
+
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
16648
|
+
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
16649
|
+
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
16650
|
+
listLongTermMemories: () => listLongTermMemories,
|
|
16651
|
+
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
16652
|
+
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
16653
|
+
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
16654
|
+
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
16655
|
+
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
16656
|
+
});
|
|
16657
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
16658
|
+
function isOneOf(value, values) {
|
|
16659
|
+
return typeof value === "string" && values.includes(value);
|
|
16660
|
+
}
|
|
16661
|
+
function compactText2(value, field, max = MAX_TEXT_LENGTH) {
|
|
16662
|
+
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
16663
|
+
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
16664
|
+
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
16665
|
+
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
16666
|
+
return safe;
|
|
16667
|
+
}
|
|
16668
|
+
function compactList(values, field) {
|
|
16669
|
+
if (!values) return [];
|
|
16670
|
+
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
16671
|
+
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
16672
|
+
}
|
|
16673
|
+
function normalizeEvidence(input) {
|
|
16674
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
16675
|
+
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
16676
|
+
}
|
|
16677
|
+
return input.map((evidence) => {
|
|
16678
|
+
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
16679
|
+
throw new Error("Long-term memory evidence kind is invalid.");
|
|
16680
|
+
}
|
|
16681
|
+
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
16682
|
+
throw new Error("Long-term memory evidence relation is invalid.");
|
|
16683
|
+
}
|
|
16684
|
+
return {
|
|
16685
|
+
kind: evidence.kind,
|
|
16686
|
+
referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
|
|
16687
|
+
relation: evidence.relation,
|
|
16688
|
+
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
|
|
16689
|
+
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
16690
|
+
};
|
|
16691
|
+
});
|
|
16692
|
+
}
|
|
16693
|
+
function assertScope(scope) {
|
|
16694
|
+
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
16695
|
+
}
|
|
16696
|
+
function assertKind(kind) {
|
|
16697
|
+
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
16698
|
+
}
|
|
16699
|
+
function assertPortability(value) {
|
|
16700
|
+
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
16701
|
+
throw new Error("Long-term memory portability is invalid.");
|
|
16702
|
+
}
|
|
16703
|
+
}
|
|
16704
|
+
function assertPortableEvidence(scope, portability, evidence) {
|
|
16705
|
+
if (portability !== "portable") return;
|
|
16706
|
+
if (scope !== "user") {
|
|
16707
|
+
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
16708
|
+
}
|
|
16709
|
+
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
16710
|
+
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.");
|
|
16711
|
+
}
|
|
16712
|
+
}
|
|
16713
|
+
function requireTeamReader(scope, reader) {
|
|
16714
|
+
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
16715
|
+
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
16716
|
+
}
|
|
16717
|
+
}
|
|
16718
|
+
function matchesScope(memory, reader) {
|
|
16719
|
+
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
16720
|
+
if (memory.scope === "team") {
|
|
16721
|
+
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
16722
|
+
}
|
|
16723
|
+
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
16724
|
+
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
16725
|
+
}
|
|
16726
|
+
function canReadLongTermMemory(memory, reader) {
|
|
16727
|
+
return matchesScope(memory, reader);
|
|
16728
|
+
}
|
|
16729
|
+
function isEligibleLongTermMemory(memory) {
|
|
16730
|
+
return memory.state === "qualified" || memory.state === "approved";
|
|
16731
|
+
}
|
|
16732
|
+
function taskTerms(task) {
|
|
16733
|
+
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
16734
|
+
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
16735
|
+
}
|
|
16736
|
+
function kindFitScore(kind, task) {
|
|
16737
|
+
const normalized = task.toLocaleLowerCase("en-US");
|
|
16738
|
+
const hints = {
|
|
16739
|
+
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
16740
|
+
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
16741
|
+
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
16742
|
+
};
|
|
16743
|
+
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
16744
|
+
}
|
|
16745
|
+
function validationFreshnessScore(memory) {
|
|
16746
|
+
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
16747
|
+
if (!validatedAt) return 0;
|
|
16748
|
+
const time = Date.parse(validatedAt);
|
|
16749
|
+
if (!Number.isFinite(time)) return 0;
|
|
16750
|
+
const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
|
|
16751
|
+
if (ageDays <= 30) return 0.35;
|
|
16752
|
+
if (ageDays <= 90) return 0.15;
|
|
16753
|
+
return 0;
|
|
16754
|
+
}
|
|
16755
|
+
function scoreForTask(memory, task) {
|
|
16756
|
+
const terms = taskTerms(task);
|
|
16757
|
+
if (terms.length === 0) return void 0;
|
|
16758
|
+
const title = memory.title.toLocaleLowerCase("en-US");
|
|
16759
|
+
const content = memory.content.toLocaleLowerCase("en-US");
|
|
16760
|
+
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
16761
|
+
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
16762
|
+
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
16763
|
+
let score = memory.state === "approved" ? 0.5 : 0;
|
|
16764
|
+
const matched = [];
|
|
16765
|
+
for (const term of terms) {
|
|
16766
|
+
let termScore = 0;
|
|
16767
|
+
if (title.includes(term)) termScore += 5;
|
|
16768
|
+
if (tags.includes(term)) termScore += 4;
|
|
16769
|
+
if (applicability.includes(term)) termScore += 3;
|
|
16770
|
+
if (facts.includes(term)) termScore += 2;
|
|
16771
|
+
if (content.includes(term)) termScore += 1;
|
|
16772
|
+
if (termScore > 0) {
|
|
16773
|
+
score += termScore;
|
|
16774
|
+
matched.push(term);
|
|
16775
|
+
}
|
|
16776
|
+
}
|
|
16777
|
+
if (matched.length === 0) return void 0;
|
|
16778
|
+
score += kindFitScore(memory.kind, task);
|
|
16779
|
+
score += validationFreshnessScore(memory);
|
|
16780
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
16781
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
16782
|
+
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
16783
|
+
}
|
|
16784
|
+
function semanticText(memory) {
|
|
16785
|
+
return sanitizeCredentials([
|
|
16786
|
+
memory.title,
|
|
16787
|
+
memory.applicability,
|
|
16788
|
+
memory.tags.join(" "),
|
|
16789
|
+
memory.facts.join(" "),
|
|
16790
|
+
memory.content
|
|
16791
|
+
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
16792
|
+
}
|
|
16793
|
+
function cosineSimilarity(left, right) {
|
|
16794
|
+
if (left.length === 0 || left.length !== right.length) return 0;
|
|
16795
|
+
let dot = 0;
|
|
16796
|
+
let leftMagnitude = 0;
|
|
16797
|
+
let rightMagnitude = 0;
|
|
16798
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
16799
|
+
const a = left[index];
|
|
16800
|
+
const b = right[index];
|
|
16801
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
|
|
16802
|
+
dot += a * b;
|
|
16803
|
+
leftMagnitude += a * a;
|
|
16804
|
+
rightMagnitude += b * b;
|
|
16805
|
+
}
|
|
16806
|
+
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
16807
|
+
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
16808
|
+
}
|
|
16809
|
+
function semanticSelection(memory, similarity, task) {
|
|
16810
|
+
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
16811
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
16812
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
16813
|
+
return {
|
|
16814
|
+
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
16815
|
+
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
16816
|
+
};
|
|
16817
|
+
}
|
|
16818
|
+
async function resolveSemanticProvider(explicit) {
|
|
16819
|
+
if (explicit !== void 0) return explicit;
|
|
16820
|
+
try {
|
|
16821
|
+
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
16822
|
+
return await withTimeout(
|
|
16823
|
+
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
16824
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
16825
|
+
"Long-term semantic provider initialization"
|
|
16826
|
+
);
|
|
16827
|
+
} catch {
|
|
16828
|
+
return null;
|
|
16829
|
+
}
|
|
16830
|
+
}
|
|
16831
|
+
function createStore(dataDir) {
|
|
16832
|
+
const store = new LongTermMemoryStore();
|
|
16833
|
+
return store.init(dataDir).then(() => store);
|
|
16834
|
+
}
|
|
16835
|
+
async function createManualLongTermMemory(input) {
|
|
16836
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
16837
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
16838
|
+
assertScope(input.scope);
|
|
16839
|
+
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID10(), relation: "supports" }];
|
|
16840
|
+
return createLongTermMemoryWithDataDir({
|
|
16841
|
+
dataDir: input.dataDir,
|
|
16842
|
+
originProjectId: input.projectId,
|
|
16843
|
+
ownerId: owner.id,
|
|
16844
|
+
scope: input.scope,
|
|
16845
|
+
kind: input.kind,
|
|
16846
|
+
portability: input.portability,
|
|
16847
|
+
title: input.title,
|
|
16848
|
+
content: input.content,
|
|
16849
|
+
facts: input.facts,
|
|
16850
|
+
tags: input.tags,
|
|
16851
|
+
applicability: input.applicability,
|
|
16852
|
+
origin: "manual",
|
|
16853
|
+
evidence,
|
|
16854
|
+
reader: input.reader
|
|
16855
|
+
});
|
|
16856
|
+
}
|
|
16857
|
+
async function promoteObservationToLongTermMemory(input) {
|
|
16858
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
16859
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
16860
|
+
return createLongTermMemoryWithDataDir({
|
|
16861
|
+
dataDir: input.dataDir,
|
|
16862
|
+
originProjectId: input.observation.projectId,
|
|
16863
|
+
ownerId: owner.id,
|
|
16864
|
+
scope: input.scope,
|
|
16865
|
+
kind: input.kind,
|
|
16866
|
+
portability: "project-bound",
|
|
16867
|
+
title: input.observation.title,
|
|
16868
|
+
content: input.observation.narrative,
|
|
16869
|
+
facts: input.observation.facts,
|
|
16870
|
+
tags: input.tags ?? input.observation.concepts,
|
|
16871
|
+
applicability: input.applicability,
|
|
16872
|
+
origin: input.observation.source === "git" ? "git" : "agent",
|
|
16873
|
+
evidence: [{
|
|
16874
|
+
kind: "observation",
|
|
16875
|
+
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
16876
|
+
relation: "derives"
|
|
16877
|
+
}],
|
|
16878
|
+
reader: input.reader
|
|
16879
|
+
});
|
|
16880
|
+
}
|
|
16881
|
+
async function createLongTermMemoryCandidate(input) {
|
|
16882
|
+
return createLongTermMemoryWithDataDir(input);
|
|
16883
|
+
}
|
|
16884
|
+
async function createLongTermMemoryWithDataDir(input) {
|
|
16885
|
+
assertScope(input.scope);
|
|
16886
|
+
assertKind(input.kind);
|
|
16887
|
+
const portability = input.portability ?? "project-bound";
|
|
16888
|
+
assertPortability(portability);
|
|
16889
|
+
requireTeamReader(input.scope, input.reader);
|
|
16890
|
+
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
16891
|
+
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
16892
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
16893
|
+
const memory = {
|
|
16894
|
+
id: randomUUID10(),
|
|
16895
|
+
originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
|
|
16896
|
+
ownerId: compactText2(input.ownerId, "owner id", 160),
|
|
16897
|
+
scope: input.scope,
|
|
16898
|
+
kind: input.kind,
|
|
16899
|
+
state: "candidate",
|
|
16900
|
+
portability,
|
|
16901
|
+
title: compactText2(input.title, "title", 500),
|
|
16902
|
+
content: compactText2(input.content, "content"),
|
|
16903
|
+
facts: compactList(input.facts, "facts"),
|
|
16904
|
+
tags: compactList(input.tags, "tags"),
|
|
16905
|
+
...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
|
|
16906
|
+
origin: input.origin ?? "manual",
|
|
16907
|
+
createdAt: now3,
|
|
16908
|
+
updatedAt: now3,
|
|
16909
|
+
accessCount: 0
|
|
16910
|
+
};
|
|
16911
|
+
const store = await createStore(input.dataDir);
|
|
16912
|
+
const evidence = store.transaction(() => {
|
|
16913
|
+
store.insert(memory);
|
|
16914
|
+
const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
16915
|
+
store.recordEvent({
|
|
16916
|
+
memoryId: memory.id,
|
|
16917
|
+
kind: "created",
|
|
16918
|
+
toState: "candidate",
|
|
16919
|
+
detail: "Created as a source-backed long-term memory candidate.",
|
|
16920
|
+
createdAt: now3
|
|
16921
|
+
});
|
|
16922
|
+
return written;
|
|
16923
|
+
});
|
|
16924
|
+
return { memory, evidence };
|
|
16925
|
+
}
|
|
16926
|
+
function transitionDetail(value) {
|
|
16927
|
+
return compactText2(value, "transition reason", 1e3);
|
|
16928
|
+
}
|
|
16929
|
+
async function transitionMemory(input) {
|
|
16930
|
+
const store = await createStore(input.dataDir);
|
|
16931
|
+
return store.transaction(() => {
|
|
16932
|
+
const current = store.get(input.id);
|
|
16933
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
16934
|
+
if (current.state !== input.expected) {
|
|
16935
|
+
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
16936
|
+
}
|
|
16937
|
+
const evidence = store.listEvidence(current.id);
|
|
16938
|
+
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
16939
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
16940
|
+
const next = {
|
|
16941
|
+
...current,
|
|
16942
|
+
state: input.next,
|
|
16943
|
+
updatedAt: now3,
|
|
16944
|
+
lastValidatedAt: now3,
|
|
16945
|
+
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
16946
|
+
};
|
|
16947
|
+
store.update(next);
|
|
16948
|
+
store.recordEvent({
|
|
16949
|
+
memoryId: next.id,
|
|
16950
|
+
kind: input.event,
|
|
16951
|
+
fromState: current.state,
|
|
16952
|
+
toState: next.state,
|
|
16953
|
+
detail: transitionDetail(input.reason),
|
|
16954
|
+
createdAt: now3
|
|
16955
|
+
});
|
|
16956
|
+
return next;
|
|
16957
|
+
});
|
|
16958
|
+
}
|
|
16959
|
+
async function qualifyLongTermMemory(input) {
|
|
16960
|
+
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
16961
|
+
}
|
|
16962
|
+
async function approveLongTermMemory(input) {
|
|
16963
|
+
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
16964
|
+
}
|
|
16965
|
+
async function archiveLongTermMemory(input) {
|
|
16966
|
+
const store = await createStore(input.dataDir);
|
|
16967
|
+
return store.transaction(() => {
|
|
16968
|
+
const current = store.get(input.id);
|
|
16969
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
16970
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
16971
|
+
throw new Error("Long-term memory is already inactive.");
|
|
16972
|
+
}
|
|
16973
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
16974
|
+
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
16975
|
+
store.update(next);
|
|
16976
|
+
store.recordEvent({
|
|
16977
|
+
memoryId: next.id,
|
|
16978
|
+
kind: "archived",
|
|
16979
|
+
fromState: current.state,
|
|
16980
|
+
toState: "archived",
|
|
16981
|
+
detail: transitionDetail(input.reason),
|
|
16982
|
+
createdAt: now3
|
|
16983
|
+
});
|
|
16984
|
+
return next;
|
|
16985
|
+
});
|
|
16986
|
+
}
|
|
16987
|
+
async function supersedeLongTermMemory(input) {
|
|
16988
|
+
const store = await createStore(input.dataDir);
|
|
16989
|
+
return store.transaction(() => {
|
|
16990
|
+
const current = store.get(input.id);
|
|
16991
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
16992
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
16993
|
+
throw new Error("Long-term memory is already inactive.");
|
|
16994
|
+
}
|
|
16995
|
+
if (current.id === input.supersededBy) {
|
|
16996
|
+
throw new Error("Long-term memory cannot supersede itself.");
|
|
16997
|
+
}
|
|
16998
|
+
const replacement = store.get(input.supersededBy);
|
|
16999
|
+
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
17000
|
+
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
17001
|
+
}
|
|
17002
|
+
if (replacement.scope !== current.scope) {
|
|
17003
|
+
throw new Error("Replacement long-term memory must use the same scope.");
|
|
17004
|
+
}
|
|
17005
|
+
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
17006
|
+
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
17007
|
+
}
|
|
17008
|
+
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
17009
|
+
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
17010
|
+
}
|
|
17011
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
17012
|
+
const next = {
|
|
17013
|
+
...current,
|
|
17014
|
+
state: "superseded",
|
|
17015
|
+
supersededBy: replacement.id,
|
|
17016
|
+
updatedAt: now3
|
|
17017
|
+
};
|
|
17018
|
+
store.update(next);
|
|
17019
|
+
store.recordEvent({
|
|
17020
|
+
memoryId: next.id,
|
|
17021
|
+
kind: "superseded",
|
|
17022
|
+
fromState: current.state,
|
|
17023
|
+
toState: "superseded",
|
|
17024
|
+
detail: transitionDetail(input.reason),
|
|
17025
|
+
createdAt: now3
|
|
17026
|
+
});
|
|
17027
|
+
return next;
|
|
17028
|
+
});
|
|
17029
|
+
}
|
|
17030
|
+
async function listLongTermMemories(input) {
|
|
17031
|
+
const store = await createStore(input.dataDir);
|
|
17032
|
+
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) }));
|
|
17033
|
+
}
|
|
17034
|
+
async function getLongTermMemoryDetail(input) {
|
|
17035
|
+
const store = await createStore(input.dataDir);
|
|
17036
|
+
const memory = store.get(input.id);
|
|
17037
|
+
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
17038
|
+
throw new Error("Long-term memory was not found in the active scope.");
|
|
17039
|
+
}
|
|
17040
|
+
store.markAccess([memory.id]);
|
|
17041
|
+
return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
|
|
17042
|
+
}
|
|
17043
|
+
async function selectLongTermMemoriesForTask(input) {
|
|
17044
|
+
const task = input.task?.trim();
|
|
17045
|
+
if (!task) return [];
|
|
17046
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
17047
|
+
const reader = {
|
|
17048
|
+
projectId: input.projectId,
|
|
17049
|
+
...owner ? { ownerId: owner.id } : {},
|
|
17050
|
+
...input.agentId ? { agentId: input.agentId } : {},
|
|
17051
|
+
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
17052
|
+
};
|
|
17053
|
+
const store = await createStore(input.dataDir);
|
|
17054
|
+
const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
17055
|
+
let selected = eligible.map((memory) => {
|
|
17056
|
+
const ranked = scoreForTask(memory, task);
|
|
17057
|
+
return ranked ? {
|
|
17058
|
+
memory,
|
|
17059
|
+
evidence: store.listEvidence(memory.id),
|
|
17060
|
+
score: ranked.score,
|
|
17061
|
+
reason: ranked.reason
|
|
17062
|
+
} : void 0;
|
|
17063
|
+
}).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));
|
|
17064
|
+
if (selected.length === 0 && eligible.length > 0) {
|
|
17065
|
+
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
17066
|
+
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
17067
|
+
if (provider2 && candidates.length > 0) {
|
|
17068
|
+
try {
|
|
17069
|
+
const vectors = await withTimeout(
|
|
17070
|
+
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
17071
|
+
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
17072
|
+
retry: false
|
|
17073
|
+
}),
|
|
17074
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
17075
|
+
"Long-term semantic retrieval"
|
|
17076
|
+
);
|
|
17077
|
+
const query = vectors[0];
|
|
17078
|
+
if (Array.isArray(query)) {
|
|
17079
|
+
selected = candidates.map((memory, index) => {
|
|
17080
|
+
const vector = vectors[index + 1];
|
|
17081
|
+
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
17082
|
+
return ranked ? {
|
|
17083
|
+
memory,
|
|
17084
|
+
evidence: store.listEvidence(memory.id),
|
|
17085
|
+
score: ranked.score,
|
|
17086
|
+
reason: ranked.reason
|
|
17087
|
+
} : void 0;
|
|
17088
|
+
}).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));
|
|
17089
|
+
}
|
|
17090
|
+
} catch {
|
|
17091
|
+
}
|
|
17092
|
+
}
|
|
17093
|
+
}
|
|
17094
|
+
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
17095
|
+
store.markAccess(selected.map((item) => item.memory.id));
|
|
17096
|
+
return selected;
|
|
17097
|
+
}
|
|
17098
|
+
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
17099
|
+
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
17100
|
+
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
17101
|
+
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
17102
|
+
}
|
|
17103
|
+
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
17104
|
+
var init_long_term = __esm({
|
|
17105
|
+
"src/memory/long-term.ts"() {
|
|
17106
|
+
"use strict";
|
|
17107
|
+
init_esm_shims();
|
|
17108
|
+
init_token_budget();
|
|
17109
|
+
init_timeout();
|
|
17110
|
+
init_secret_filter();
|
|
17111
|
+
init_owner();
|
|
17112
|
+
init_long_term_store();
|
|
17113
|
+
init_long_term_types();
|
|
17114
|
+
MAX_TEXT_LENGTH = 12e3;
|
|
17115
|
+
MAX_LIST_ITEMS = 40;
|
|
17116
|
+
MAX_SEMANTIC_CANDIDATES = 48;
|
|
17117
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
17118
|
+
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
17119
|
+
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
17120
|
+
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
17121
|
+
"the",
|
|
17122
|
+
"and",
|
|
17123
|
+
"for",
|
|
17124
|
+
"with",
|
|
17125
|
+
"from",
|
|
17126
|
+
"that",
|
|
17127
|
+
"this",
|
|
17128
|
+
"into",
|
|
17129
|
+
"when",
|
|
17130
|
+
"what",
|
|
17131
|
+
"\u5982\u4F55",
|
|
17132
|
+
"\u4EC0\u4E48",
|
|
17133
|
+
"\u8FD9\u4E2A",
|
|
17134
|
+
"\u90A3\u4E2A",
|
|
17135
|
+
"\u6211\u4EEC",
|
|
17136
|
+
"\u9879\u76EE",
|
|
17137
|
+
"\u8FDB\u884C",
|
|
17138
|
+
"\u9700\u8981",
|
|
17139
|
+
"\u7EE7\u7EED",
|
|
17140
|
+
"\u5904\u7406"
|
|
17141
|
+
]);
|
|
17142
|
+
}
|
|
17143
|
+
});
|
|
17144
|
+
|
|
16248
17145
|
// src/knowledge/workset.ts
|
|
16249
17146
|
function unique(values) {
|
|
16250
17147
|
return [...new Set(values.filter(Boolean))];
|
|
@@ -16348,6 +17245,7 @@ function receiptOmissionKind(raw) {
|
|
|
16348
17245
|
if (raw.includes("state")) return "code-state";
|
|
16349
17246
|
if (raw.includes("semantic")) return "semantic-code";
|
|
16350
17247
|
if (raw.includes("start")) return "start-here";
|
|
17248
|
+
if (raw.includes("durable-memory")) return "durable-memory";
|
|
16351
17249
|
if (raw.includes("memory")) return "memory";
|
|
16352
17250
|
if (raw.includes("claim")) return "claim";
|
|
16353
17251
|
if (raw.includes("knowledge-page")) return "knowledge-page";
|
|
@@ -16606,6 +17504,26 @@ function renderTaskWorksetPrompt(input) {
|
|
|
16606
17504
|
});
|
|
16607
17505
|
}
|
|
16608
17506
|
}
|
|
17507
|
+
if (input.durableMemory.length > 0) {
|
|
17508
|
+
appendLine(lines, "", maxTokens, omitted, "durable-memory-heading");
|
|
17509
|
+
appendLine(lines, "Durable memory", maxTokens, omitted, "durable-memory-heading");
|
|
17510
|
+
for (const memory of input.durableMemory.slice(0, 3)) {
|
|
17511
|
+
appendLine(
|
|
17512
|
+
lines,
|
|
17513
|
+
"- " + memory.kind + " (" + memory.scope + ", " + memory.state + "; ref durable:" + memory.id + "): " + memory.summary,
|
|
17514
|
+
maxTokens,
|
|
17515
|
+
omitted,
|
|
17516
|
+
"durable-memory",
|
|
17517
|
+
selected,
|
|
17518
|
+
{
|
|
17519
|
+
kind: "durable-memory",
|
|
17520
|
+
id: "durable:" + memory.id,
|
|
17521
|
+
reason: memory.reason,
|
|
17522
|
+
trust: "source-backed"
|
|
17523
|
+
}
|
|
17524
|
+
);
|
|
17525
|
+
}
|
|
17526
|
+
}
|
|
16609
17527
|
if (input.workflows.length > 0) {
|
|
16610
17528
|
appendLine(lines, "", maxTokens, omitted, "workflow-heading");
|
|
16611
17529
|
appendLine(lines, "Project workflow", maxTokens, omitted, "workflow-heading");
|
|
@@ -16675,6 +17593,32 @@ async function buildTaskWorkset(input) {
|
|
|
16675
17593
|
evidenceRefs: evidenceByClaim.get(claim.id).map((item) => item.id),
|
|
16676
17594
|
reason: selection.reasons[claim.id] ?? "source-qualified task match"
|
|
16677
17595
|
}));
|
|
17596
|
+
let durableMemory = [];
|
|
17597
|
+
let durableEvidence = [];
|
|
17598
|
+
if (task) {
|
|
17599
|
+
try {
|
|
17600
|
+
const selected = await selectLongTermMemoriesForTask({
|
|
17601
|
+
dataDir: input.dataDir,
|
|
17602
|
+
projectId: input.projectId,
|
|
17603
|
+
task,
|
|
17604
|
+
...input.reader?.agentId ? { agentId: input.reader.agentId } : {},
|
|
17605
|
+
...input.reader?.isTeamMember ? { isTeamMember: true } : {},
|
|
17606
|
+
limit: 3
|
|
17607
|
+
});
|
|
17608
|
+
durableMemory = selected.map((item) => ({
|
|
17609
|
+
id: item.memory.id,
|
|
17610
|
+
kind: item.memory.kind,
|
|
17611
|
+
scope: item.memory.scope,
|
|
17612
|
+
state: item.memory.state,
|
|
17613
|
+
title: item.memory.title,
|
|
17614
|
+
summary: renderLongTermMemorySummary(item.memory, 20),
|
|
17615
|
+
evidenceRefs: item.evidence.map((evidence) => evidence.id),
|
|
17616
|
+
reason: item.reason
|
|
17617
|
+
}));
|
|
17618
|
+
durableEvidence = selected.flatMap((item) => item.evidence);
|
|
17619
|
+
} catch {
|
|
17620
|
+
}
|
|
17621
|
+
}
|
|
16678
17622
|
let workspace;
|
|
16679
17623
|
let pages = [];
|
|
16680
17624
|
let workflows = [];
|
|
@@ -16714,7 +17658,10 @@ async function buildTaskWorkset(input) {
|
|
|
16714
17658
|
const evidenceIds = unique(selection.claims.flatMap((claim) => evidenceIdsForClaim(
|
|
16715
17659
|
claim,
|
|
16716
17660
|
evidenceByClaim.get(claim.id) ?? []
|
|
16717
|
-
))
|
|
17661
|
+
)).concat(
|
|
17662
|
+
durableMemory.map((memory) => "durable:" + memory.id),
|
|
17663
|
+
durableEvidence.map((evidence) => "durable-evidence:" + evidence.id)
|
|
17664
|
+
));
|
|
16718
17665
|
const verification = unique([
|
|
16719
17666
|
...workflows.flatMap((workflow) => workflow.verificationGates),
|
|
16720
17667
|
...input.verificationHints
|
|
@@ -16740,7 +17687,7 @@ async function buildTaskWorkset(input) {
|
|
|
16740
17687
|
} : {}
|
|
16741
17688
|
} : void 0;
|
|
16742
17689
|
const base = {
|
|
16743
|
-
version: "1.
|
|
17690
|
+
version: "1.3",
|
|
16744
17691
|
task,
|
|
16745
17692
|
lens: input.lens,
|
|
16746
17693
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
@@ -16753,6 +17700,7 @@ async function buildTaskWorkset(input) {
|
|
|
16753
17700
|
hiddenCautionMemoryCount: input.hiddenCautionMemoryCount ?? 0,
|
|
16754
17701
|
claims,
|
|
16755
17702
|
pages,
|
|
17703
|
+
durableMemory,
|
|
16756
17704
|
workflows,
|
|
16757
17705
|
cautions: normalizedCautions,
|
|
16758
17706
|
verification,
|
|
@@ -16769,7 +17717,7 @@ async function buildTaskWorkset(input) {
|
|
|
16769
17717
|
budget: { maxTokens }
|
|
16770
17718
|
});
|
|
16771
17719
|
const receipt = {
|
|
16772
|
-
version: "1.
|
|
17720
|
+
version: "1.3",
|
|
16773
17721
|
target: input.deliveryTarget ?? "project-context",
|
|
16774
17722
|
elapsedMs: Math.max(0, Date.now() - startedAt),
|
|
16775
17723
|
budget: {
|
|
@@ -16804,6 +17752,7 @@ var init_workset = __esm({
|
|
|
16804
17752
|
init_workspace();
|
|
16805
17753
|
init_workflow_store();
|
|
16806
17754
|
init_workflows();
|
|
17755
|
+
init_long_term();
|
|
16807
17756
|
CONTINUATION_DETAIL_TOKEN_BUDGET = 20;
|
|
16808
17757
|
}
|
|
16809
17758
|
});
|
|
@@ -16816,7 +17765,7 @@ __export(current_facts_exports, {
|
|
|
16816
17765
|
});
|
|
16817
17766
|
import { execFileSync } from "child_process";
|
|
16818
17767
|
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
16819
|
-
import
|
|
17768
|
+
import path20 from "path";
|
|
16820
17769
|
function readTextIfExists(filePath) {
|
|
16821
17770
|
try {
|
|
16822
17771
|
if (!existsSync12(filePath)) return null;
|
|
@@ -16826,7 +17775,7 @@ function readTextIfExists(filePath) {
|
|
|
16826
17775
|
}
|
|
16827
17776
|
}
|
|
16828
17777
|
function readPackageVersion(rootPath) {
|
|
16829
|
-
const text = readTextIfExists(
|
|
17778
|
+
const text = readTextIfExists(path20.join(rootPath, "package.json"));
|
|
16830
17779
|
if (!text) return void 0;
|
|
16831
17780
|
try {
|
|
16832
17781
|
const parsed = JSON.parse(text);
|
|
@@ -16836,7 +17785,7 @@ function readPackageVersion(rootPath) {
|
|
|
16836
17785
|
}
|
|
16837
17786
|
}
|
|
16838
17787
|
function readLatestChangelog(rootPath) {
|
|
16839
|
-
const text = readTextIfExists(
|
|
17788
|
+
const text = readTextIfExists(path20.join(rootPath, "CHANGELOG.md"));
|
|
16840
17789
|
if (!text) return void 0;
|
|
16841
17790
|
const match = text.match(/^##\s+\[?v?([^\]\s]+)\]?\s*(?:-\s*(\d{4}-\d{2}-\d{2}))?/m);
|
|
16842
17791
|
if (!match) return void 0;
|
|
@@ -16906,7 +17855,7 @@ function detectStaleProgressNotes(input) {
|
|
|
16906
17855
|
];
|
|
16907
17856
|
const notes = [];
|
|
16908
17857
|
for (const relPath of candidates) {
|
|
16909
|
-
const content = readTextIfExists(
|
|
17858
|
+
const content = readTextIfExists(path20.join(input.rootPath, relPath));
|
|
16910
17859
|
if (!content) continue;
|
|
16911
17860
|
const parsed = parseProgressNote(content);
|
|
16912
17861
|
const olderThanChangelog = compareDateStrings(parsed.lastUpdated, input.latestChangelog?.date);
|
|
@@ -17157,9 +18106,9 @@ async function inspectExternalCodeGraph(input) {
|
|
|
17157
18106
|
return { health, quality: providerQuality({ mode, health }) };
|
|
17158
18107
|
}
|
|
17159
18108
|
function safeRelativePath(projectRoot, value, exclude) {
|
|
17160
|
-
const
|
|
17161
|
-
if (!
|
|
17162
|
-
const absolute = resolve(projectRoot,
|
|
18109
|
+
const path27 = safeText(value, 1024);
|
|
18110
|
+
if (!path27 || isAbsolute(path27)) return void 0;
|
|
18111
|
+
const absolute = resolve(projectRoot, path27);
|
|
17163
18112
|
const fromRoot = relative2(projectRoot, absolute);
|
|
17164
18113
|
if (!fromRoot || fromRoot === ".." || fromRoot.startsWith("..\\") || fromRoot.startsWith("../") || isAbsolute(fromRoot)) return void 0;
|
|
17165
18114
|
const normalized = normalizeCodePath(fromRoot);
|
|
@@ -17434,15 +18383,15 @@ function countLanguages(files) {
|
|
|
17434
18383
|
}
|
|
17435
18384
|
return [...counts.entries()].map(([language, files2]) => ({ language, files: files2 })).sort((a, b) => a.language.localeCompare(b.language));
|
|
17436
18385
|
}
|
|
17437
|
-
function suggestedReadRank(
|
|
17438
|
-
const normalized =
|
|
18386
|
+
function suggestedReadRank(path27) {
|
|
18387
|
+
const normalized = path27.replace(/\\/g, "/");
|
|
17439
18388
|
if (normalized.startsWith("src/")) return 0;
|
|
17440
18389
|
if (normalized.startsWith("tests/") || normalized.startsWith("test/")) return 0;
|
|
17441
18390
|
if (normalized.startsWith("packages/") && normalized.includes("/src/")) return 2;
|
|
17442
18391
|
return 3;
|
|
17443
18392
|
}
|
|
17444
18393
|
function compactSuggestedReads(paths, limit = 8, exclude) {
|
|
17445
|
-
return uniq(paths).filter((
|
|
18394
|
+
return uniq(paths).filter((path27) => !isCodeGraphExcludedPath(path27, exclude)).sort((a, b) => suggestedReadRank(a) - suggestedReadRank(b)).slice(0, limit);
|
|
17446
18395
|
}
|
|
17447
18396
|
function collectGraph(store, projectId, observations2, exclude) {
|
|
17448
18397
|
const files = store.listFiles(projectId);
|
|
@@ -17482,14 +18431,14 @@ function collectGraph(store, projectId, observations2, exclude) {
|
|
|
17482
18431
|
}
|
|
17483
18432
|
if (files.length === 0) {
|
|
17484
18433
|
for (const observation of observations2) {
|
|
17485
|
-
for (const
|
|
17486
|
-
if (!
|
|
17487
|
-
suggestedReads.push(
|
|
18434
|
+
for (const path27 of observation.filesModified ?? []) {
|
|
18435
|
+
if (!path27 || isCodeGraphExcludedPath(path27, exclude)) continue;
|
|
18436
|
+
suggestedReads.push(path27);
|
|
17488
18437
|
sources.push({
|
|
17489
18438
|
observationId: observation.id,
|
|
17490
18439
|
title: observation.title,
|
|
17491
18440
|
type: observation.type,
|
|
17492
|
-
path:
|
|
18441
|
+
path: path27,
|
|
17493
18442
|
status: "unbound",
|
|
17494
18443
|
reason: "Recorded file hint; Code Memory refresh is pending."
|
|
17495
18444
|
});
|
|
@@ -17558,8 +18507,8 @@ var compaction_checkpoint_store_exports = {};
|
|
|
17558
18507
|
__export(compaction_checkpoint_store_exports, {
|
|
17559
18508
|
CompactionCheckpointStore: () => CompactionCheckpointStore
|
|
17560
18509
|
});
|
|
17561
|
-
import { createHash as createHash13, randomUUID as
|
|
17562
|
-
function
|
|
18510
|
+
import { createHash as createHash13, randomUUID as randomUUID11 } from "crypto";
|
|
18511
|
+
function optionalText6(value) {
|
|
17563
18512
|
return typeof value === "string" && value ? value : void 0;
|
|
17564
18513
|
}
|
|
17565
18514
|
function optionalNumber2(value) {
|
|
@@ -17613,15 +18562,15 @@ function rowToCheckpoint(row) {
|
|
|
17613
18562
|
reason: row.reason,
|
|
17614
18563
|
sourceEvent: row.source_event,
|
|
17615
18564
|
sourceKey: row.source_key,
|
|
17616
|
-
...
|
|
18565
|
+
...optionalText6(row.summary) ? { summary: row.summary } : {},
|
|
17617
18566
|
...optionalNumber2(row.tokens_before) !== void 0 ? { tokensBefore: Number(row.tokens_before) } : {},
|
|
17618
|
-
...
|
|
18567
|
+
...optionalText6(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
|
|
17619
18568
|
...parseDetails(row.details_json) ? { details: parseDetails(row.details_json) } : {},
|
|
17620
18569
|
transcriptAvailable: Boolean(row.transcript_available),
|
|
17621
18570
|
status: row.status,
|
|
17622
18571
|
preCapturedAt: row.pre_captured_at,
|
|
17623
|
-
...
|
|
17624
|
-
...
|
|
18572
|
+
...optionalText6(row.completed_at) ? { completedAt: row.completed_at } : {},
|
|
18573
|
+
...optionalText6(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
|
|
17625
18574
|
deliveryCount: Number(row.delivery_count ?? 0),
|
|
17626
18575
|
createdAt: row.created_at,
|
|
17627
18576
|
updatedAt: row.updated_at
|
|
@@ -17659,7 +18608,7 @@ var init_compaction_checkpoint_store = __esm({
|
|
|
17659
18608
|
if (pending) return rowToCheckpoint(pending);
|
|
17660
18609
|
const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
17661
18610
|
const checkpoint = {
|
|
17662
|
-
id:
|
|
18611
|
+
id: randomUUID11(),
|
|
17663
18612
|
projectId: input.projectId,
|
|
17664
18613
|
sessionId: input.sessionId,
|
|
17665
18614
|
agent: input.agent,
|
|
@@ -17667,7 +18616,7 @@ var init_compaction_checkpoint_store = __esm({
|
|
|
17667
18616
|
captureKind: "preflight",
|
|
17668
18617
|
reason: safeReason(input.reason),
|
|
17669
18618
|
sourceEvent: input.sourceEvent,
|
|
17670
|
-
sourceKey: `pre:${
|
|
18619
|
+
sourceKey: `pre:${randomUUID11()}`,
|
|
17671
18620
|
transcriptAvailable: Boolean(input.transcriptAvailable),
|
|
17672
18621
|
status: "active",
|
|
17673
18622
|
preCapturedAt: now3,
|
|
@@ -17733,7 +18682,7 @@ var init_compaction_checkpoint_store = __esm({
|
|
|
17733
18682
|
return this.get(checkpoint2.id);
|
|
17734
18683
|
}
|
|
17735
18684
|
const checkpoint = {
|
|
17736
|
-
id:
|
|
18685
|
+
id: randomUUID11(),
|
|
17737
18686
|
projectId: input.projectId,
|
|
17738
18687
|
sessionId: input.sessionId,
|
|
17739
18688
|
agent: input.agent,
|
|
@@ -17868,7 +18817,7 @@ __export(auto_context_exports, {
|
|
|
17868
18817
|
formatLegacyAutoProjectContextPrompt: () => formatLegacyAutoProjectContextPrompt
|
|
17869
18818
|
});
|
|
17870
18819
|
import { existsSync as existsSync14, statSync as statSync3 } from "fs";
|
|
17871
|
-
import
|
|
18820
|
+
import path21 from "path";
|
|
17872
18821
|
function activeProjectObservations(observations2, projectId) {
|
|
17873
18822
|
return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
|
|
17874
18823
|
}
|
|
@@ -18103,6 +19052,7 @@ async function buildAutoProjectContext(input) {
|
|
|
18103
19052
|
stale: overview.freshness.stale
|
|
18104
19053
|
},
|
|
18105
19054
|
runtimeCautions,
|
|
19055
|
+
...input.reader ? { reader: input.reader } : {},
|
|
18106
19056
|
...input.deliveryTarget ? { deliveryTarget: input.deliveryTarget } : {}
|
|
18107
19057
|
});
|
|
18108
19058
|
return {
|
|
@@ -18147,7 +19097,7 @@ function dedupeSourcesByObservation(sources) {
|
|
|
18147
19097
|
function existingLensCandidates(rootPath, lens) {
|
|
18148
19098
|
const out = [];
|
|
18149
19099
|
for (const candidate of lensPathCandidates(lens)) {
|
|
18150
|
-
const absolute =
|
|
19100
|
+
const absolute = path21.join(rootPath, candidate);
|
|
18151
19101
|
try {
|
|
18152
19102
|
if (!existsSync14(absolute)) continue;
|
|
18153
19103
|
const stat = statSync3(absolute);
|
|
@@ -18257,7 +19207,7 @@ function formatAutoProjectContextSummary(context) {
|
|
|
18257
19207
|
"Start here"
|
|
18258
19208
|
].filter(Boolean);
|
|
18259
19209
|
if (startHere.length > 0) {
|
|
18260
|
-
startHere.forEach((
|
|
19210
|
+
startHere.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
|
|
18261
19211
|
} else {
|
|
18262
19212
|
lines.push("- no code-bound reads yet; inspect the task-relevant files directly");
|
|
18263
19213
|
}
|
|
@@ -18266,6 +19216,14 @@ function formatAutoProjectContextSummary(context) {
|
|
|
18266
19216
|
"Reliable memory",
|
|
18267
19217
|
reliableSources.length > 0 ? `- ${reliableSources.length} current code-bound memory link(s)` : "- none yet"
|
|
18268
19218
|
);
|
|
19219
|
+
if (context.workset.durableMemory.length > 0) {
|
|
19220
|
+
lines.push("", "Durable memory");
|
|
19221
|
+
for (const memory of context.workset.durableMemory.slice(0, 3)) {
|
|
19222
|
+
lines.push(
|
|
19223
|
+
"- " + memory.kind + " (" + memory.scope + ", " + memory.state + "): " + compactContinuationText(memory.summary, 28)
|
|
19224
|
+
);
|
|
19225
|
+
}
|
|
19226
|
+
}
|
|
18269
19227
|
const continuation = context.workset.continuation;
|
|
18270
19228
|
if (continuation?.previousSession || continuation?.memories.length || continuation?.compactCheckpoint) {
|
|
18271
19229
|
lines.push("", "Resume from prior work");
|
|
@@ -18315,7 +19273,7 @@ function formatLegacyAutoProjectContextPrompt(context) {
|
|
|
18315
19273
|
if (startHere.length === 0) {
|
|
18316
19274
|
lines.push("- no code-bound reads yet; inspect the task-relevant code directly");
|
|
18317
19275
|
} else {
|
|
18318
|
-
startHere.forEach((
|
|
19276
|
+
startHere.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
|
|
18319
19277
|
}
|
|
18320
19278
|
const { reliableSources, cautionSources, hiddenReliableCount, hiddenCautionCount } = lensSourceSets(context);
|
|
18321
19279
|
lines.push("", "Reliable memory");
|
|
@@ -18566,6 +19524,7 @@ async function attachTaskWorkset(input) {
|
|
|
18566
19524
|
stale: input.pack.warnings.filter((warning) => warning.status === "stale").length
|
|
18567
19525
|
},
|
|
18568
19526
|
runtimeCautions: input.runtimeCautions,
|
|
19527
|
+
...input.reader ? { reader: input.reader } : {},
|
|
18569
19528
|
deliveryTarget: "context-pack"
|
|
18570
19529
|
});
|
|
18571
19530
|
return { ...input.pack, workset };
|
|
@@ -18576,7 +19535,7 @@ function buildContextPackPrompt(pack) {
|
|
|
18576
19535
|
const unboundMemories = pack.memories.filter((memory) => memory.status === "unbound");
|
|
18577
19536
|
const lines = ["## Task", pack.task, "", "## Reliable Memories"];
|
|
18578
19537
|
const visibleCodeFacts = pack.codeFacts.filter((fact) => !isCodeGraphExcludedPath(fact.path)).slice(0, 5);
|
|
18579
|
-
const visibleSuggestedReads = pack.suggestedReads.filter((
|
|
19538
|
+
const visibleSuggestedReads = pack.suggestedReads.filter((path27) => !isCodeGraphExcludedPath(path27)).slice(0, 5);
|
|
18580
19539
|
if (reliableMemories.length === 0) lines.push("- none");
|
|
18581
19540
|
for (const memory of reliableMemories) {
|
|
18582
19541
|
lines.push(`- #${memory.id} ${memory.status}: [${memory.type}] ${memory.title} (${memory.reason})`);
|
|
@@ -18600,7 +19559,7 @@ function buildContextPackPrompt(pack) {
|
|
|
18600
19559
|
}
|
|
18601
19560
|
lines.push("", "## Suggested Next Reads");
|
|
18602
19561
|
if (visibleSuggestedReads.length === 0) lines.push("- none");
|
|
18603
|
-
visibleSuggestedReads.forEach((
|
|
19562
|
+
visibleSuggestedReads.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
|
|
18604
19563
|
lines.push("", "## Suggested Verification");
|
|
18605
19564
|
if (pack.suggestedVerification.length === 0) lines.push("- none");
|
|
18606
19565
|
for (const command of pack.suggestedVerification) lines.push(`- ${command}`);
|
|
@@ -18992,9 +19951,9 @@ __export(team_store_exports, {
|
|
|
18992
19951
|
isTeamStoreInitialized: () => isTeamStoreInitialized,
|
|
18993
19952
|
resetTeamStore: () => resetTeamStore
|
|
18994
19953
|
});
|
|
18995
|
-
import { randomUUID as
|
|
18996
|
-
import
|
|
18997
|
-
import
|
|
19954
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
19955
|
+
import path22 from "path";
|
|
19956
|
+
import fs16 from "fs";
|
|
18998
19957
|
function getTeamStore() {
|
|
18999
19958
|
if (!_teamStore) {
|
|
19000
19959
|
throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
|
|
@@ -19281,7 +20240,7 @@ var init_team_store = __esm({
|
|
|
19281
20240
|
* reactivate it (preserving agent_id). Otherwise create a new one.
|
|
19282
20241
|
*/
|
|
19283
20242
|
registerAgent(input) {
|
|
19284
|
-
const instanceId = input.instanceId ||
|
|
20243
|
+
const instanceId = input.instanceId || randomUUID12();
|
|
19285
20244
|
const now3 = Date.now();
|
|
19286
20245
|
const existing = this.stmtAgentFindByInstance.get(
|
|
19287
20246
|
input.projectId,
|
|
@@ -19307,7 +20266,7 @@ var init_team_store = __esm({
|
|
|
19307
20266
|
this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
|
|
19308
20267
|
return row2;
|
|
19309
20268
|
}
|
|
19310
|
-
const agentId =
|
|
20269
|
+
const agentId = randomUUID12();
|
|
19311
20270
|
this.stmtAgentUpsert.run({
|
|
19312
20271
|
agent_id: agentId,
|
|
19313
20272
|
project_id: input.projectId,
|
|
@@ -19409,7 +20368,7 @@ var init_team_store = __esm({
|
|
|
19409
20368
|
return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
|
|
19410
20369
|
}
|
|
19411
20370
|
}
|
|
19412
|
-
const id =
|
|
20371
|
+
const id = randomUUID12();
|
|
19413
20372
|
const now3 = Date.now();
|
|
19414
20373
|
const row = {
|
|
19415
20374
|
id,
|
|
@@ -19459,7 +20418,7 @@ var init_team_store = __esm({
|
|
|
19459
20418
|
// Task Operations (with atomic claim semantics)
|
|
19460
20419
|
// ═══════════════════════════════════════════════════════════════════
|
|
19461
20420
|
createTask(input) {
|
|
19462
|
-
const taskId =
|
|
20421
|
+
const taskId = randomUUID12();
|
|
19463
20422
|
const now3 = Date.now();
|
|
19464
20423
|
const row = {
|
|
19465
20424
|
task_id: taskId,
|
|
@@ -19684,19 +20643,19 @@ var init_team_store = __esm({
|
|
|
19684
20643
|
const agentCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM team_agents`).get();
|
|
19685
20644
|
if (agentCount.cnt > 0) return;
|
|
19686
20645
|
const candidates = [
|
|
19687
|
-
|
|
19688
|
-
|
|
20646
|
+
path22.join(this.dataDir, "team-state.json"),
|
|
20647
|
+
path22.join(this.dataDir, "..", "team-state.json")
|
|
19689
20648
|
];
|
|
19690
20649
|
let jsonPath = null;
|
|
19691
20650
|
for (const p of candidates) {
|
|
19692
|
-
if (
|
|
20651
|
+
if (fs16.existsSync(p)) {
|
|
19693
20652
|
jsonPath = p;
|
|
19694
20653
|
break;
|
|
19695
20654
|
}
|
|
19696
20655
|
}
|
|
19697
20656
|
if (!jsonPath) return;
|
|
19698
20657
|
try {
|
|
19699
|
-
const raw =
|
|
20658
|
+
const raw = fs16.readFileSync(jsonPath, "utf-8");
|
|
19700
20659
|
const snap = JSON.parse(raw);
|
|
19701
20660
|
if (snap.version !== 1) return;
|
|
19702
20661
|
console.error(`[memorix] Migrating team-state.json to SQLite...`);
|
|
@@ -19731,7 +20690,7 @@ var init_team_store = __esm({
|
|
|
19731
20690
|
for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
|
|
19732
20691
|
for (const msg of msgs) {
|
|
19733
20692
|
this.stmtMsgInsert.run({
|
|
19734
|
-
id: msg.id ??
|
|
20693
|
+
id: msg.id ?? randomUUID12(),
|
|
19735
20694
|
project_id: "migrated",
|
|
19736
20695
|
sender_agent_id: msg.from ?? "unknown",
|
|
19737
20696
|
recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
|
|
@@ -19790,7 +20749,7 @@ var init_team_store = __esm({
|
|
|
19790
20749
|
`[memorix] Team migration complete: ${agentsMigrated} agents, ${messagesMigrated} messages, ${tasksMigrated} tasks, ${locksMigrated} locks.`
|
|
19791
20750
|
);
|
|
19792
20751
|
try {
|
|
19793
|
-
|
|
20752
|
+
fs16.renameSync(jsonPath, jsonPath + ".migrated");
|
|
19794
20753
|
} catch {
|
|
19795
20754
|
}
|
|
19796
20755
|
} catch (err) {
|
|
@@ -19825,7 +20784,7 @@ var init_team_store = __esm({
|
|
|
19825
20784
|
* Uses projectId derived from the dataDir path.
|
|
19826
20785
|
*/
|
|
19827
20786
|
seedDefaultRoles(dataDir) {
|
|
19828
|
-
const projectId =
|
|
20787
|
+
const projectId = path22.basename(path22.resolve(dataDir, ".."));
|
|
19829
20788
|
const count2 = this.stmtRoleCountByProject.get(projectId).cnt;
|
|
19830
20789
|
if (count2 > 0) return;
|
|
19831
20790
|
const now3 = Date.now();
|
|
@@ -20839,8 +21798,8 @@ __export(server_exports, {
|
|
|
20839
21798
|
startDashboard: () => startDashboard
|
|
20840
21799
|
});
|
|
20841
21800
|
import { createServer } from "http";
|
|
20842
|
-
import { promises as
|
|
20843
|
-
import
|
|
21801
|
+
import { promises as fs17 } from "fs";
|
|
21802
|
+
import path23 from "path";
|
|
20844
21803
|
import { exec } from "child_process";
|
|
20845
21804
|
function sendJson(res, data, status = 200) {
|
|
20846
21805
|
res.writeHead(status, {
|
|
@@ -21318,7 +22277,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
21318
22277
|
const aliasModule = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
|
|
21319
22278
|
canonicalId = await aliasModule.getCanonicalId(effectiveProjectId);
|
|
21320
22279
|
const { promises: fsP } = await import("fs");
|
|
21321
|
-
const registryPath =
|
|
22280
|
+
const registryPath = path23.join(baseDir, ".project-aliases.json");
|
|
21322
22281
|
const raw = await fsP.readFile(registryPath, "utf-8");
|
|
21323
22282
|
const registry = JSON.parse(raw);
|
|
21324
22283
|
aliasGroups = registry.groups || [];
|
|
@@ -21433,14 +22392,14 @@ async function serveStatic(req, res, staticDir) {
|
|
|
21433
22392
|
if (urlPath === "/" || !urlPath.includes(".")) {
|
|
21434
22393
|
urlPath = "/index.html";
|
|
21435
22394
|
}
|
|
21436
|
-
const filePath =
|
|
22395
|
+
const filePath = path23.join(staticDir, urlPath);
|
|
21437
22396
|
if (!filePath.startsWith(staticDir)) {
|
|
21438
22397
|
sendError(res, "Forbidden", 403);
|
|
21439
22398
|
return;
|
|
21440
22399
|
}
|
|
21441
22400
|
try {
|
|
21442
|
-
const data = await
|
|
21443
|
-
const ext =
|
|
22401
|
+
const data = await fs17.readFile(filePath);
|
|
22402
|
+
const ext = path23.extname(filePath);
|
|
21444
22403
|
res.writeHead(200, {
|
|
21445
22404
|
"Content-Type": MIME_TYPES[ext] || "application/octet-stream",
|
|
21446
22405
|
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
@@ -21450,7 +22409,7 @@ async function serveStatic(req, res, staticDir) {
|
|
|
21450
22409
|
res.end(data);
|
|
21451
22410
|
} catch {
|
|
21452
22411
|
try {
|
|
21453
|
-
const indexData = await
|
|
22412
|
+
const indexData = await fs17.readFile(path23.join(staticDir, "index.html"));
|
|
21454
22413
|
res.writeHead(200, {
|
|
21455
22414
|
"Content-Type": "text/html; charset=utf-8",
|
|
21456
22415
|
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
@@ -22163,7 +23122,7 @@ function collectCodeMemory(opts) {
|
|
|
22163
23122
|
if (status.indexedAt) lines.push(`Last scan: ${status.indexedAt}`);
|
|
22164
23123
|
if (suggestedReads.length > 0) {
|
|
22165
23124
|
lines.push("Suggested reads:");
|
|
22166
|
-
suggestedReads.forEach((
|
|
23125
|
+
suggestedReads.forEach((path27, index) => lines.push(`${index + 1}. ${path27}`));
|
|
22167
23126
|
}
|
|
22168
23127
|
return lines.join("\n");
|
|
22169
23128
|
} catch {
|
|
@@ -22188,7 +23147,7 @@ __export(planner_exports, {
|
|
|
22188
23147
|
materializeTaskGraph: () => materializeTaskGraph,
|
|
22189
23148
|
seedAutonomousPipeline: () => seedAutonomousPipeline
|
|
22190
23149
|
});
|
|
22191
|
-
import { randomUUID as
|
|
23150
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
22192
23151
|
function isPlannerTask(metadata) {
|
|
22193
23152
|
if (!metadata) return null;
|
|
22194
23153
|
try {
|
|
@@ -22246,7 +23205,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
|
|
|
22246
23205
|
const maxIterations = config.maxIterations ?? 3;
|
|
22247
23206
|
const taskBudget = config.taskBudget ?? 15;
|
|
22248
23207
|
const structuredPlan = opts?.structuredPlan ?? true;
|
|
22249
|
-
const pipelineId =
|
|
23208
|
+
const pipelineId = randomUUID13();
|
|
22250
23209
|
const meta = {
|
|
22251
23210
|
plannerType: "plan",
|
|
22252
23211
|
pipelineId,
|
|
@@ -22936,15 +23895,15 @@ __export(audit_exports, {
|
|
|
22936
23895
|
removeFile: () => removeFile,
|
|
22937
23896
|
saveAudit: () => saveAudit
|
|
22938
23897
|
});
|
|
22939
|
-
import * as
|
|
22940
|
-
import * as
|
|
23898
|
+
import * as fs18 from "fs/promises";
|
|
23899
|
+
import * as path24 from "path";
|
|
22941
23900
|
import { homedir as homedir24 } from "os";
|
|
22942
23901
|
function getAuditFilePath() {
|
|
22943
|
-
return process.env.MEMORIX_AUDIT_FILE ||
|
|
23902
|
+
return process.env.MEMORIX_AUDIT_FILE || path24.join(homedir24(), ".memorix", "audit.json");
|
|
22944
23903
|
}
|
|
22945
23904
|
async function loadAudit() {
|
|
22946
23905
|
try {
|
|
22947
|
-
const content = await
|
|
23906
|
+
const content = await fs18.readFile(getAuditFilePath(), "utf-8");
|
|
22948
23907
|
return JSON.parse(content);
|
|
22949
23908
|
} catch {
|
|
22950
23909
|
return {
|
|
@@ -22955,9 +23914,9 @@ async function loadAudit() {
|
|
|
22955
23914
|
}
|
|
22956
23915
|
async function saveAudit(data) {
|
|
22957
23916
|
const auditFile = getAuditFilePath();
|
|
22958
|
-
const dir =
|
|
22959
|
-
await
|
|
22960
|
-
await
|
|
23917
|
+
const dir = path24.dirname(auditFile);
|
|
23918
|
+
await fs18.mkdir(dir, { recursive: true });
|
|
23919
|
+
await fs18.writeFile(auditFile, JSON.stringify(data, null, 2), "utf-8");
|
|
22961
23920
|
}
|
|
22962
23921
|
function getProjectId(projectRoot) {
|
|
22963
23922
|
try {
|
|
@@ -23039,8 +23998,8 @@ __export(installers_exports, {
|
|
|
23039
23998
|
resolveOpenCodeHookCommand: () => resolveOpenCodeHookCommand,
|
|
23040
23999
|
uninstallHooks: () => uninstallHooks
|
|
23041
24000
|
});
|
|
23042
|
-
import * as
|
|
23043
|
-
import * as
|
|
24001
|
+
import * as fs19 from "fs/promises";
|
|
24002
|
+
import * as path25 from "path";
|
|
23044
24003
|
import * as os3 from "os";
|
|
23045
24004
|
import { execSync as execSync3 } from "child_process";
|
|
23046
24005
|
function resolveHookCommand() {
|
|
@@ -23056,7 +24015,7 @@ function resolveWindowsMemorixShim() {
|
|
|
23056
24015
|
stdio: ["ignore", "pipe", "ignore"],
|
|
23057
24016
|
windowsHide: true
|
|
23058
24017
|
}));
|
|
23059
|
-
return output.split(/\r?\n/).map((candidate) => candidate.trim()).find((candidate) =>
|
|
24018
|
+
return output.split(/\r?\n/).map((candidate) => candidate.trim()).find((candidate) => path25.isAbsolute(candidate)) ?? null;
|
|
23060
24019
|
} catch {
|
|
23061
24020
|
return null;
|
|
23062
24021
|
}
|
|
@@ -23251,9 +24210,9 @@ async function installOfficialSkillsForAgent(agent, projectRoot, global = false)
|
|
|
23251
24210
|
const root = global ? os3.homedir() : projectRoot;
|
|
23252
24211
|
const skillPaths = [];
|
|
23253
24212
|
for (const skillEntry of OFFICIAL_MEMORIX_SKILLS) {
|
|
23254
|
-
const skillPath =
|
|
23255
|
-
await
|
|
23256
|
-
await
|
|
24213
|
+
const skillPath = path25.join(root, relativeDir, skillEntry.name, "SKILL.md");
|
|
24214
|
+
await fs19.mkdir(path25.dirname(skillPath), { recursive: true });
|
|
24215
|
+
await fs19.writeFile(skillPath, skillEntry.content, "utf-8");
|
|
23257
24216
|
skillPaths.push(skillPath);
|
|
23258
24217
|
try {
|
|
23259
24218
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
@@ -23466,124 +24425,124 @@ async function installOpenCodeSkills(projectRoot, global = false) {
|
|
|
23466
24425
|
function getProjectConfigPath(agent, projectRoot) {
|
|
23467
24426
|
switch (agent) {
|
|
23468
24427
|
case "claude":
|
|
23469
|
-
return
|
|
24428
|
+
return path25.join(projectRoot, ".claude", "settings.local.json");
|
|
23470
24429
|
case "copilot":
|
|
23471
|
-
return
|
|
24430
|
+
return path25.join(projectRoot, ".github", "hooks", "memorix.json");
|
|
23472
24431
|
case "windsurf":
|
|
23473
|
-
return
|
|
24432
|
+
return path25.join(projectRoot, ".windsurf", "hooks.json");
|
|
23474
24433
|
case "cursor":
|
|
23475
|
-
return
|
|
24434
|
+
return path25.join(projectRoot, ".cursor", "hooks.json");
|
|
23476
24435
|
case "kiro":
|
|
23477
|
-
return
|
|
24436
|
+
return path25.join(projectRoot, ".kiro", "hooks", "memorix-agent-stop.kiro.hook");
|
|
23478
24437
|
case "codex":
|
|
23479
|
-
return
|
|
24438
|
+
return path25.join(projectRoot, "AGENTS.md");
|
|
23480
24439
|
case "trae":
|
|
23481
|
-
return
|
|
24440
|
+
return path25.join(projectRoot, ".trae", "rules", "project_rules.md");
|
|
23482
24441
|
case "opencode":
|
|
23483
|
-
return
|
|
24442
|
+
return path25.join(projectRoot, ".opencode", "plugins", "memorix.js");
|
|
23484
24443
|
case "pi":
|
|
23485
|
-
return
|
|
24444
|
+
return path25.join(projectRoot, ".pi", "packages", "memorix", "extensions", "memorix.js");
|
|
23486
24445
|
case "openclaw":
|
|
23487
|
-
return
|
|
24446
|
+
return path25.join(os3.homedir(), ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
|
|
23488
24447
|
case "hermes":
|
|
23489
|
-
return
|
|
24448
|
+
return path25.join(os3.homedir(), ".hermes", "plugins", "memorix", "plugin.yaml");
|
|
23490
24449
|
case "omp":
|
|
23491
|
-
return
|
|
24450
|
+
return path25.join(projectRoot, ".omp", "packages", "memorix", "extensions", "memorix.js");
|
|
23492
24451
|
case "antigravity":
|
|
23493
|
-
return
|
|
24452
|
+
return path25.join(projectRoot, ".agents", "hooks.json");
|
|
23494
24453
|
case "gemini-cli":
|
|
23495
|
-
return
|
|
24454
|
+
return path25.join(projectRoot, ".gemini", "settings.json");
|
|
23496
24455
|
default:
|
|
23497
|
-
return
|
|
24456
|
+
return path25.join(projectRoot, ".memorix", "hooks.json");
|
|
23498
24457
|
}
|
|
23499
24458
|
}
|
|
23500
24459
|
function getGlobalConfigPath(agent) {
|
|
23501
24460
|
const home = os3.homedir();
|
|
23502
24461
|
switch (agent) {
|
|
23503
24462
|
case "claude":
|
|
23504
|
-
return
|
|
24463
|
+
return path25.join(home, ".claude", "settings.json");
|
|
23505
24464
|
case "copilot":
|
|
23506
24465
|
return "";
|
|
23507
24466
|
case "windsurf":
|
|
23508
|
-
return
|
|
24467
|
+
return path25.join(home, ".codeium", "windsurf", "hooks.json");
|
|
23509
24468
|
case "cursor":
|
|
23510
|
-
return
|
|
24469
|
+
return path25.join(home, ".cursor", "hooks.json");
|
|
23511
24470
|
case "antigravity":
|
|
23512
|
-
return
|
|
24471
|
+
return path25.join(home, ".gemini", "config", "hooks.json");
|
|
23513
24472
|
case "gemini-cli":
|
|
23514
|
-
return
|
|
24473
|
+
return path25.join(home, ".gemini", "settings.json");
|
|
23515
24474
|
case "opencode":
|
|
23516
|
-
return
|
|
24475
|
+
return path25.join(home, ".config", "opencode", "plugins", "memorix.js");
|
|
23517
24476
|
case "pi":
|
|
23518
|
-
return
|
|
24477
|
+
return path25.join(home, ".pi", "agent", "packages", "memorix", "extensions", "memorix.js");
|
|
23519
24478
|
case "openclaw":
|
|
23520
|
-
return
|
|
24479
|
+
return path25.join(home, ".openclaw", "extensions", "memorix", "hooks", "memorix", "HOOK.md");
|
|
23521
24480
|
case "hermes":
|
|
23522
|
-
return
|
|
24481
|
+
return path25.join(home, ".hermes", "plugins", "memorix", "plugin.yaml");
|
|
23523
24482
|
case "omp":
|
|
23524
|
-
return
|
|
24483
|
+
return path25.join(home, ".omp", "agent", "packages", "memorix", "extensions", "memorix.js");
|
|
23525
24484
|
case "trae":
|
|
23526
|
-
return
|
|
24485
|
+
return path25.join(home, ".trae", "rules", "project_rules.md");
|
|
23527
24486
|
default:
|
|
23528
|
-
return
|
|
24487
|
+
return path25.join(home, ".memorix", "hooks.json");
|
|
23529
24488
|
}
|
|
23530
24489
|
}
|
|
23531
24490
|
function getAgentRulesPath(agent, root, global = false) {
|
|
23532
24491
|
if (global) {
|
|
23533
24492
|
switch (agent) {
|
|
23534
24493
|
case "windsurf":
|
|
23535
|
-
return
|
|
24494
|
+
return path25.join(root, ".codeium", "windsurf", "rules", "memorix.md");
|
|
23536
24495
|
case "cursor":
|
|
23537
|
-
return
|
|
24496
|
+
return path25.join(root, ".cursor", "rules", "memorix.mdc");
|
|
23538
24497
|
case "claude":
|
|
23539
|
-
return
|
|
24498
|
+
return path25.join(root, ".claude", "CLAUDE.md");
|
|
23540
24499
|
case "codex":
|
|
23541
|
-
return
|
|
24500
|
+
return path25.join(root, ".codex", "AGENTS.md");
|
|
23542
24501
|
case "kiro":
|
|
23543
|
-
return
|
|
24502
|
+
return path25.join(root, ".kiro", "steering", "memorix.md");
|
|
23544
24503
|
case "opencode":
|
|
23545
|
-
return
|
|
24504
|
+
return path25.join(root, ".config", "opencode", "AGENTS.md");
|
|
23546
24505
|
case "antigravity":
|
|
23547
24506
|
case "gemini-cli":
|
|
23548
|
-
return
|
|
24507
|
+
return path25.join(root, ".gemini", "GEMINI.md");
|
|
23549
24508
|
case "trae":
|
|
23550
|
-
return
|
|
24509
|
+
return path25.join(root, ".trae", "rules", "project_rules.md");
|
|
23551
24510
|
default:
|
|
23552
|
-
return
|
|
24511
|
+
return path25.join(root, ".agent", "rules", "memorix.md");
|
|
23553
24512
|
}
|
|
23554
24513
|
}
|
|
23555
24514
|
switch (agent) {
|
|
23556
24515
|
case "windsurf":
|
|
23557
|
-
return
|
|
24516
|
+
return path25.join(root, ".windsurf", "rules", "memorix.md");
|
|
23558
24517
|
case "cursor":
|
|
23559
|
-
return
|
|
24518
|
+
return path25.join(root, ".cursor", "rules", "memorix.mdc");
|
|
23560
24519
|
case "claude":
|
|
23561
|
-
return
|
|
24520
|
+
return path25.join(root, "CLAUDE.md");
|
|
23562
24521
|
case "copilot":
|
|
23563
|
-
return
|
|
24522
|
+
return path25.join(root, ".github", "copilot-instructions.md");
|
|
23564
24523
|
case "codex":
|
|
23565
|
-
return
|
|
24524
|
+
return path25.join(root, "AGENTS.md");
|
|
23566
24525
|
case "kiro":
|
|
23567
|
-
return
|
|
24526
|
+
return path25.join(root, ".kiro", "steering", "memorix.md");
|
|
23568
24527
|
case "opencode":
|
|
23569
|
-
return
|
|
24528
|
+
return path25.join(root, "AGENTS.md");
|
|
23570
24529
|
case "antigravity":
|
|
23571
24530
|
case "gemini-cli":
|
|
23572
|
-
return
|
|
24531
|
+
return path25.join(root, "GEMINI.md");
|
|
23573
24532
|
case "trae":
|
|
23574
|
-
return
|
|
24533
|
+
return path25.join(root, ".trae", "rules", "project_rules.md");
|
|
23575
24534
|
default:
|
|
23576
|
-
return
|
|
24535
|
+
return path25.join(root, ".agent", "rules", "memorix.md");
|
|
23577
24536
|
}
|
|
23578
24537
|
}
|
|
23579
24538
|
async function detectCopilotExtension(home) {
|
|
23580
24539
|
const extDirs = [
|
|
23581
|
-
|
|
23582
|
-
|
|
24540
|
+
path25.join(home, ".vscode", "extensions"),
|
|
24541
|
+
path25.join(home, ".vscode-insiders", "extensions")
|
|
23583
24542
|
];
|
|
23584
24543
|
for (const extDir of extDirs) {
|
|
23585
24544
|
try {
|
|
23586
|
-
const entries = await
|
|
24545
|
+
const entries = await fs19.readdir(extDir);
|
|
23587
24546
|
if (entries.some((e) => e.startsWith("github.copilot-"))) {
|
|
23588
24547
|
return true;
|
|
23589
24548
|
}
|
|
@@ -23595,21 +24554,21 @@ async function detectCopilotExtension(home) {
|
|
|
23595
24554
|
async function detectInstalledAgents() {
|
|
23596
24555
|
const agents = [];
|
|
23597
24556
|
const home = os3.homedir();
|
|
23598
|
-
const claudeDir =
|
|
24557
|
+
const claudeDir = path25.join(home, ".claude");
|
|
23599
24558
|
try {
|
|
23600
|
-
await
|
|
24559
|
+
await fs19.access(claudeDir);
|
|
23601
24560
|
agents.push("claude");
|
|
23602
24561
|
} catch {
|
|
23603
24562
|
}
|
|
23604
|
-
const windsurfDir =
|
|
24563
|
+
const windsurfDir = path25.join(home, ".codeium", "windsurf");
|
|
23605
24564
|
try {
|
|
23606
|
-
await
|
|
24565
|
+
await fs19.access(windsurfDir);
|
|
23607
24566
|
agents.push("windsurf");
|
|
23608
24567
|
} catch {
|
|
23609
24568
|
}
|
|
23610
|
-
const cursorDir =
|
|
24569
|
+
const cursorDir = path25.join(home, ".cursor");
|
|
23611
24570
|
try {
|
|
23612
|
-
await
|
|
24571
|
+
await fs19.access(cursorDir);
|
|
23613
24572
|
agents.push("cursor");
|
|
23614
24573
|
} catch {
|
|
23615
24574
|
}
|
|
@@ -23617,21 +24576,21 @@ async function detectInstalledAgents() {
|
|
|
23617
24576
|
if (copilotDetected) {
|
|
23618
24577
|
agents.push("copilot");
|
|
23619
24578
|
}
|
|
23620
|
-
const kiroConfig =
|
|
24579
|
+
const kiroConfig = path25.join(home, ".kiro");
|
|
23621
24580
|
try {
|
|
23622
|
-
await
|
|
24581
|
+
await fs19.access(kiroConfig);
|
|
23623
24582
|
agents.push("kiro");
|
|
23624
24583
|
} catch {
|
|
23625
24584
|
}
|
|
23626
|
-
const codexDir =
|
|
24585
|
+
const codexDir = path25.join(home, ".codex");
|
|
23627
24586
|
try {
|
|
23628
|
-
await
|
|
24587
|
+
await fs19.access(codexDir);
|
|
23629
24588
|
agents.push("codex");
|
|
23630
24589
|
} catch {
|
|
23631
24590
|
}
|
|
23632
|
-
const antigravityDir =
|
|
24591
|
+
const antigravityDir = path25.join(home, ".gemini", "config");
|
|
23633
24592
|
try {
|
|
23634
|
-
await
|
|
24593
|
+
await fs19.access(antigravityDir);
|
|
23635
24594
|
agents.push("antigravity");
|
|
23636
24595
|
} catch {
|
|
23637
24596
|
}
|
|
@@ -23642,15 +24601,15 @@ async function detectInstalledAgents() {
|
|
|
23642
24601
|
agents.push("gemini-cli");
|
|
23643
24602
|
} catch {
|
|
23644
24603
|
}
|
|
23645
|
-
const opencodeDir =
|
|
24604
|
+
const opencodeDir = path25.join(home, ".config", "opencode");
|
|
23646
24605
|
try {
|
|
23647
|
-
await
|
|
24606
|
+
await fs19.access(opencodeDir);
|
|
23648
24607
|
agents.push("opencode");
|
|
23649
24608
|
} catch {
|
|
23650
24609
|
}
|
|
23651
|
-
const traeDir =
|
|
24610
|
+
const traeDir = path25.join(home, ".trae");
|
|
23652
24611
|
try {
|
|
23653
|
-
await
|
|
24612
|
+
await fs19.access(traeDir);
|
|
23654
24613
|
agents.push("trae");
|
|
23655
24614
|
} catch {
|
|
23656
24615
|
}
|
|
@@ -23684,7 +24643,7 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23684
24643
|
try {
|
|
23685
24644
|
const { access: access2, unlink: unlink2 } = await import("fs/promises");
|
|
23686
24645
|
await access2(entry.path);
|
|
23687
|
-
const basename3 =
|
|
24646
|
+
const basename3 = path25.basename(entry.path);
|
|
23688
24647
|
if (basename3 === "AGENTS.md" || basename3 === "GEMINI.md" || basename3 === "CONTEXT.md") {
|
|
23689
24648
|
continue;
|
|
23690
24649
|
}
|
|
@@ -23739,8 +24698,8 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23739
24698
|
case "opencode": {
|
|
23740
24699
|
const pluginContent = generateOpenCodePlugin();
|
|
23741
24700
|
const pluginPath = global ? getGlobalConfigPath(agent) : getProjectConfigPath(agent, projectRoot);
|
|
23742
|
-
await
|
|
23743
|
-
await
|
|
24701
|
+
await fs19.mkdir(path25.dirname(pluginPath), { recursive: true });
|
|
24702
|
+
await fs19.writeFile(pluginPath, pluginContent, "utf-8");
|
|
23744
24703
|
try {
|
|
23745
24704
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23746
24705
|
await recordFile2(projectRoot, "hook", pluginPath, agent);
|
|
@@ -23769,14 +24728,14 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23769
24728
|
default:
|
|
23770
24729
|
generated = generateClaudeConfig();
|
|
23771
24730
|
}
|
|
23772
|
-
await
|
|
24731
|
+
await fs19.mkdir(path25.dirname(configPath), { recursive: true });
|
|
23773
24732
|
if (agent === "kiro") {
|
|
23774
24733
|
const hookFiles = generateKiroHookFiles();
|
|
23775
|
-
const hooksDir =
|
|
23776
|
-
await
|
|
24734
|
+
const hooksDir = path25.join(path25.dirname(configPath));
|
|
24735
|
+
await fs19.mkdir(hooksDir, { recursive: true });
|
|
23777
24736
|
for (const hf of hookFiles) {
|
|
23778
|
-
const hookPath =
|
|
23779
|
-
await
|
|
24737
|
+
const hookPath = path25.join(hooksDir, hf.filename);
|
|
24738
|
+
await fs19.writeFile(hookPath, hf.content, "utf-8");
|
|
23780
24739
|
try {
|
|
23781
24740
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23782
24741
|
await recordFile2(projectRoot, "hook", hookPath, agent);
|
|
@@ -23786,7 +24745,7 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23786
24745
|
} else {
|
|
23787
24746
|
let existing = {};
|
|
23788
24747
|
try {
|
|
23789
|
-
const content = await
|
|
24748
|
+
const content = await fs19.readFile(configPath, "utf-8");
|
|
23790
24749
|
existing = JSON.parse(content);
|
|
23791
24750
|
} catch {
|
|
23792
24751
|
}
|
|
@@ -23821,7 +24780,7 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
23821
24780
|
const h = merged.hooks;
|
|
23822
24781
|
if (h) delete h.preToolUse;
|
|
23823
24782
|
}
|
|
23824
|
-
await
|
|
24783
|
+
await fs19.writeFile(configPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
23825
24784
|
try {
|
|
23826
24785
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23827
24786
|
await recordFile2(projectRoot, "hook", configPath, agent);
|
|
@@ -23869,18 +24828,18 @@ async function installAgentRules(agent, projectRoot, global = false) {
|
|
|
23869
24828
|
const rulesRoot = global ? os3.homedir() : projectRoot;
|
|
23870
24829
|
const rulesPath = getAgentRulesPath(agent, rulesRoot, global);
|
|
23871
24830
|
try {
|
|
23872
|
-
await
|
|
24831
|
+
await fs19.mkdir(path25.dirname(rulesPath), { recursive: true });
|
|
23873
24832
|
if (agent === "claude" || agent === "codex" || agent === "opencode" || agent === "antigravity" || agent === "gemini-cli") {
|
|
23874
24833
|
try {
|
|
23875
|
-
const existing = await
|
|
23876
|
-
await
|
|
24834
|
+
const existing = await fs19.readFile(rulesPath, "utf-8");
|
|
24835
|
+
await fs19.writeFile(rulesPath, mergeMemorixRulesContent(existing, rulesContent), "utf-8");
|
|
23877
24836
|
try {
|
|
23878
24837
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23879
24838
|
await recordFile2(projectRoot, "rule", rulesPath, agent);
|
|
23880
24839
|
} catch {
|
|
23881
24840
|
}
|
|
23882
24841
|
} catch {
|
|
23883
|
-
await
|
|
24842
|
+
await fs19.writeFile(rulesPath, rulesContent, "utf-8");
|
|
23884
24843
|
try {
|
|
23885
24844
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23886
24845
|
await recordFile2(projectRoot, "rule", rulesPath, agent);
|
|
@@ -23889,9 +24848,9 @@ async function installAgentRules(agent, projectRoot, global = false) {
|
|
|
23889
24848
|
}
|
|
23890
24849
|
} else {
|
|
23891
24850
|
try {
|
|
23892
|
-
await
|
|
24851
|
+
await fs19.access(rulesPath);
|
|
23893
24852
|
} catch {
|
|
23894
|
-
await
|
|
24853
|
+
await fs19.writeFile(rulesPath, rulesContent, "utf-8");
|
|
23895
24854
|
try {
|
|
23896
24855
|
const { recordFile: recordFile2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
23897
24856
|
await recordFile2(projectRoot, "rule", rulesPath, agent);
|
|
@@ -23981,6 +24940,8 @@ alwaysApply: true
|
|
|
23981
24940
|
"- Include `filesModified` when relevant",
|
|
23982
24941
|
"- Use `topicKey` for topics that evolve over time (prevents duplicates)",
|
|
23983
24942
|
'- For "why" decisions, use `memorix_store_reasoning`',
|
|
24943
|
+
'- 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`.',
|
|
24944
|
+
"- 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.",
|
|
23984
24945
|
"",
|
|
23985
24946
|
"**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
|
|
23986
24947
|
"",
|
|
@@ -24017,16 +24978,16 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24017
24978
|
let success = false;
|
|
24018
24979
|
try {
|
|
24019
24980
|
if (agent === "kiro" || agent === "opencode") {
|
|
24020
|
-
await
|
|
24981
|
+
await fs19.unlink(configPath);
|
|
24021
24982
|
success = true;
|
|
24022
24983
|
} else {
|
|
24023
|
-
const content = await
|
|
24984
|
+
const content = await fs19.readFile(configPath, "utf-8");
|
|
24024
24985
|
const config = JSON.parse(content);
|
|
24025
24986
|
delete config.hooks;
|
|
24026
24987
|
if (Object.keys(config).length === 0) {
|
|
24027
|
-
await
|
|
24988
|
+
await fs19.unlink(configPath);
|
|
24028
24989
|
} else {
|
|
24029
|
-
await
|
|
24990
|
+
await fs19.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
24030
24991
|
}
|
|
24031
24992
|
success = true;
|
|
24032
24993
|
}
|
|
@@ -24039,10 +25000,10 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24039
25000
|
const agentFiles = prevFiles.filter((e) => e.agent === agent);
|
|
24040
25001
|
for (const entry of agentFiles) {
|
|
24041
25002
|
try {
|
|
24042
|
-
const basename3 =
|
|
25003
|
+
const basename3 = path25.basename(entry.path);
|
|
24043
25004
|
if (basename3 === "AGENTS.md" || basename3 === "GEMINI.md" || basename3 === "CONTEXT.md") {
|
|
24044
25005
|
try {
|
|
24045
|
-
const content = await
|
|
25006
|
+
const content = await fs19.readFile(entry.path, "utf-8");
|
|
24046
25007
|
const memorixStart = content.indexOf("# Memorix");
|
|
24047
25008
|
if (memorixStart >= 0) {
|
|
24048
25009
|
const afterMemorix = content.substring(memorixStart);
|
|
@@ -24054,9 +25015,9 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24054
25015
|
}
|
|
24055
25016
|
const cleaned = (before + "\n" + after).trim();
|
|
24056
25017
|
if (cleaned.length === 0) {
|
|
24057
|
-
await
|
|
25018
|
+
await fs19.unlink(entry.path);
|
|
24058
25019
|
} else {
|
|
24059
|
-
await
|
|
25020
|
+
await fs19.writeFile(entry.path, cleaned + "\n", "utf-8");
|
|
24060
25021
|
}
|
|
24061
25022
|
}
|
|
24062
25023
|
} catch {
|
|
@@ -24065,7 +25026,7 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24065
25026
|
auditCleaned = true;
|
|
24066
25027
|
continue;
|
|
24067
25028
|
}
|
|
24068
|
-
await
|
|
25029
|
+
await fs19.unlink(entry.path);
|
|
24069
25030
|
await removeFile2(projectRoot, entry.path);
|
|
24070
25031
|
auditCleaned = true;
|
|
24071
25032
|
} catch {
|
|
@@ -24077,14 +25038,14 @@ async function uninstallHooks(agent, projectRoot, global = false) {
|
|
|
24077
25038
|
if (success) {
|
|
24078
25039
|
try {
|
|
24079
25040
|
const { rm } = await import("fs/promises");
|
|
24080
|
-
const dir =
|
|
25041
|
+
const dir = path25.dirname(configPath);
|
|
24081
25042
|
let current = dir;
|
|
24082
25043
|
for (let i = 0; i < 3; i++) {
|
|
24083
25044
|
try {
|
|
24084
|
-
const entries = await
|
|
25045
|
+
const entries = await fs19.readdir(current);
|
|
24085
25046
|
if (entries.length === 0) {
|
|
24086
25047
|
await rm(current, { recursive: true });
|
|
24087
|
-
current =
|
|
25048
|
+
current = path25.dirname(current);
|
|
24088
25049
|
} else {
|
|
24089
25050
|
break;
|
|
24090
25051
|
}
|
|
@@ -24108,12 +25069,12 @@ async function getHookStatus(projectRoot) {
|
|
|
24108
25069
|
let usedPath = projectPath;
|
|
24109
25070
|
const verifiedByDefault = agent !== "opencode" && agent !== "pi";
|
|
24110
25071
|
try {
|
|
24111
|
-
await
|
|
25072
|
+
await fs19.access(projectPath);
|
|
24112
25073
|
installed = true;
|
|
24113
25074
|
} catch {
|
|
24114
25075
|
if (globalPath) {
|
|
24115
25076
|
try {
|
|
24116
|
-
await
|
|
25077
|
+
await fs19.access(globalPath);
|
|
24117
25078
|
installed = true;
|
|
24118
25079
|
usedPath = globalPath;
|
|
24119
25080
|
} catch {
|
|
@@ -24122,7 +25083,7 @@ async function getHookStatus(projectRoot) {
|
|
|
24122
25083
|
}
|
|
24123
25084
|
if (installed && agent === "opencode") {
|
|
24124
25085
|
try {
|
|
24125
|
-
const content = await
|
|
25086
|
+
const content = await fs19.readFile(usedPath, "utf-8");
|
|
24126
25087
|
const match = content.match(/@generated-version\s+(\d+)/);
|
|
24127
25088
|
const installedVersion = match ? parseInt(match[1], 10) : 0;
|
|
24128
25089
|
outdated = installedVersion < OPENCODE_PLUGIN_VERSION;
|
|
@@ -24153,11 +25114,11 @@ var init_installers = __esm({
|
|
|
24153
25114
|
init_official_skills();
|
|
24154
25115
|
OPENCODE_PLUGIN_VERSION = 7;
|
|
24155
25116
|
AGENT_SKILL_DIRS = {
|
|
24156
|
-
cursor: { project:
|
|
24157
|
-
windsurf: { project:
|
|
24158
|
-
kiro: { project:
|
|
24159
|
-
opencode: { project:
|
|
24160
|
-
trae: { project:
|
|
25117
|
+
cursor: { project: path25.join(".cursor", "skills"), global: path25.join(".cursor", "skills") },
|
|
25118
|
+
windsurf: { project: path25.join(".windsurf", "skills"), global: path25.join(".windsurf", "skills") },
|
|
25119
|
+
kiro: { project: path25.join(".kiro", "skills"), global: path25.join(".kiro", "skills") },
|
|
25120
|
+
opencode: { project: path25.join(".opencode", "skills"), global: path25.join(".config", "opencode", "skills") },
|
|
25121
|
+
trae: { project: path25.join(".trae", "skills"), global: path25.join(".trae", "skills") }
|
|
24161
25122
|
};
|
|
24162
25123
|
PACKAGE_OWNED_HOOK_AGENTS = /* @__PURE__ */ new Set(["openclaw", "hermes", "omp"]);
|
|
24163
25124
|
}
|
|
@@ -24171,9 +25132,9 @@ __export(hooks_path_exports, {
|
|
|
24171
25132
|
resolveHooksDir: () => resolveHooksDir
|
|
24172
25133
|
});
|
|
24173
25134
|
import { existsSync as existsSync17, readFileSync as readFileSync13, statSync as statSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
24174
|
-
import
|
|
25135
|
+
import path26 from "path";
|
|
24175
25136
|
function resolveGitDir(projectRoot) {
|
|
24176
|
-
const dotGit =
|
|
25137
|
+
const dotGit = path26.join(projectRoot, ".git");
|
|
24177
25138
|
if (!existsSync17(dotGit)) return null;
|
|
24178
25139
|
const stat = statSync4(dotGit);
|
|
24179
25140
|
if (stat.isDirectory()) {
|
|
@@ -24185,7 +25146,7 @@ function resolveGitDir(projectRoot) {
|
|
|
24185
25146
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
24186
25147
|
if (match) {
|
|
24187
25148
|
const gitdir = match[1].trim();
|
|
24188
|
-
const resolved =
|
|
25149
|
+
const resolved = path26.isAbsolute(gitdir) ? gitdir : path26.resolve(projectRoot, gitdir);
|
|
24189
25150
|
if (existsSync17(resolved)) {
|
|
24190
25151
|
return resolved;
|
|
24191
25152
|
}
|
|
@@ -24198,10 +25159,10 @@ function resolveGitDir(projectRoot) {
|
|
|
24198
25159
|
function resolveHooksDir(projectRoot) {
|
|
24199
25160
|
const gitDir = resolveGitDir(projectRoot);
|
|
24200
25161
|
if (!gitDir) return null;
|
|
24201
|
-
const hooksDir =
|
|
25162
|
+
const hooksDir = path26.join(gitDir, "hooks");
|
|
24202
25163
|
return {
|
|
24203
25164
|
hooksDir,
|
|
24204
|
-
hookPath:
|
|
25165
|
+
hookPath: path26.join(hooksDir, "post-commit")
|
|
24205
25166
|
};
|
|
24206
25167
|
}
|
|
24207
25168
|
function ensureHooksDir(projectRoot) {
|
|
@@ -27353,10 +28314,10 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
|
|
|
27353
28314
|
pathsToCheck.push(join19(homedir21(), ".gemini", "antigravity", "mcp_config.json"));
|
|
27354
28315
|
}
|
|
27355
28316
|
const merged = /* @__PURE__ */ new Map();
|
|
27356
|
-
for (const
|
|
27357
|
-
if (existsSync10(
|
|
28317
|
+
for (const path27 of pathsToCheck) {
|
|
28318
|
+
if (existsSync10(path27)) {
|
|
27358
28319
|
try {
|
|
27359
|
-
const content = readFileSync7(
|
|
28320
|
+
const content = readFileSync7(path27, "utf-8");
|
|
27360
28321
|
const servers = adapter.parse(content);
|
|
27361
28322
|
for (const s of servers) {
|
|
27362
28323
|
if (!merged.has(s.name)) merged.set(s.name, s);
|
|
@@ -28147,7 +29108,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
28147
29108
|
};
|
|
28148
29109
|
const server = existingServer ?? new McpServer({
|
|
28149
29110
|
name: "memorix",
|
|
28150
|
-
version: true ? "1.
|
|
29111
|
+
version: true ? "1.3.0" : "1.0.1"
|
|
28151
29112
|
});
|
|
28152
29113
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
28153
29114
|
server.registerTool = ((name, ...args) => {
|
|
@@ -28227,7 +29188,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
28227
29188
|
"memorix_store",
|
|
28228
29189
|
{
|
|
28229
29190
|
title: "Store Memory",
|
|
28230
|
-
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.",
|
|
29191
|
+
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.",
|
|
28231
29192
|
inputSchema: {
|
|
28232
29193
|
entityName: z2.string().describe('The entity this observation belongs to (e.g., "auth-module", "port-config")'),
|
|
28233
29194
|
type: z2.enum(OBSERVATION_TYPES).describe("Observation type for classification"),
|
|
@@ -28249,30 +29210,43 @@ The path should point to a directory containing a .git folder.`
|
|
|
28249
29210
|
visibility: z2.enum(["personal", "project", "team"]).optional().describe(
|
|
28250
29211
|
"Retrieval scope. Project is the normal shared default; personal/team require memorix_session_start with joinTeam=true."
|
|
28251
29212
|
),
|
|
29213
|
+
longTerm: z2.object({
|
|
29214
|
+
kind: z2.enum(["episodic", "semantic", "procedural"]).describe("Cognitive kind for an additional long-term candidate."),
|
|
29215
|
+
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."),
|
|
29216
|
+
tags: z2.array(z2.string()).optional().describe("Small set of retrieval tags for the candidate."),
|
|
29217
|
+
applicability: z2.string().optional().describe("When this durable fact or procedure applies.")
|
|
29218
|
+
}).optional().describe("Optional explicit request to create a source-backed long-term candidate alongside this observation. Candidates are not automatically injected."),
|
|
28252
29219
|
overrideReadOnly: z2.boolean().optional().describe(
|
|
28253
29220
|
"Use only when the user explicitly asks to save memory during a read-only or no-modification task."
|
|
28254
29221
|
)
|
|
28255
29222
|
}
|
|
28256
29223
|
},
|
|
28257
|
-
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, overrideReadOnly }) => {
|
|
29224
|
+
async ({ entityName: rawEntityName, type: rawType, title: rawTitle, narrative, facts, filesModified, concepts, topicKey, progress, relatedCommits, relatedEntities, visibility, longTerm, overrideReadOnly }) => {
|
|
28258
29225
|
const unresolved = requireResolvedProject("store memory in the current project");
|
|
28259
29226
|
if (unresolved) return unresolved;
|
|
28260
29227
|
const readOnlyBoundary = blockReadOnlyAutopilotWrite(overrideReadOnly);
|
|
28261
29228
|
if (readOnlyBoundary) return readOnlyBoundary;
|
|
28262
|
-
const requestedVisibility =
|
|
29229
|
+
const requestedVisibility = longTerm?.scope === "user" ? "personal" : longTerm?.scope === "team" ? "team" : visibility;
|
|
29230
|
+
const effectiveVisibility = requestedVisibility ?? "project";
|
|
28263
29231
|
const reader = getObservationReader();
|
|
28264
|
-
if (
|
|
29232
|
+
if (effectiveVisibility !== "project" && !currentAgentId) {
|
|
28265
29233
|
return {
|
|
28266
29234
|
content: [{ type: "text", text: "Personal or team memory requires memorix_session_start with joinTeam=true so Memorix can bind an owner." }],
|
|
28267
29235
|
isError: true
|
|
28268
29236
|
};
|
|
28269
29237
|
}
|
|
28270
|
-
if (
|
|
29238
|
+
if (effectiveVisibility === "team" && !reader.isTeamMember) {
|
|
28271
29239
|
return {
|
|
28272
29240
|
content: [{ type: "text", text: "Team memory requires an active coordination membership in the current project." }],
|
|
28273
29241
|
isError: true
|
|
28274
29242
|
};
|
|
28275
29243
|
}
|
|
29244
|
+
if (longTerm?.scope === "team" && !reader.isTeamMember) {
|
|
29245
|
+
return {
|
|
29246
|
+
content: [{ type: "text", text: "Team long-term memory requires an active coordination membership in the current project." }],
|
|
29247
|
+
isError: true
|
|
29248
|
+
};
|
|
29249
|
+
}
|
|
28276
29250
|
try {
|
|
28277
29251
|
return await withFreshIndex(async () => {
|
|
28278
29252
|
let entityName = rawEntityName;
|
|
@@ -28294,7 +29268,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
28294
29268
|
const useFormation = formationMode === "active";
|
|
28295
29269
|
let formationResult = null;
|
|
28296
29270
|
let formationNote = "";
|
|
28297
|
-
if (useFormation && !topicKey && !progress) {
|
|
29271
|
+
if (useFormation && !topicKey && !progress && !longTerm) {
|
|
28298
29272
|
let currentFormationStage = "setup";
|
|
28299
29273
|
const completedFormationStages = {};
|
|
28300
29274
|
const formationStartTime = Date.now();
|
|
@@ -28445,7 +29419,7 @@ ${modeIcon} Formation[active]: ${formationResult.evaluation.category} (${formati
|
|
|
28445
29419
|
}
|
|
28446
29420
|
let compactAction = "";
|
|
28447
29421
|
let compactMerged = false;
|
|
28448
|
-
if (!useFormation && !topicKey && !progress) {
|
|
29422
|
+
if (!useFormation && !topicKey && !progress && !longTerm) {
|
|
28449
29423
|
try {
|
|
28450
29424
|
const searchResult = await compactSearch({
|
|
28451
29425
|
query: `${title} ${narrative.substring(0, 200)}`,
|
|
@@ -28602,9 +29576,33 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
28602
29576
|
sourceDetail: "explicit",
|
|
28603
29577
|
valueCategory: formationResult?.evaluation.category,
|
|
28604
29578
|
createdByAgentId: currentAgentId,
|
|
28605
|
-
visibility,
|
|
29579
|
+
visibility: requestedVisibility,
|
|
28606
29580
|
visibilityReader: reader
|
|
28607
29581
|
});
|
|
29582
|
+
let longTermNote = "";
|
|
29583
|
+
if (longTerm) {
|
|
29584
|
+
try {
|
|
29585
|
+
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
29586
|
+
const promoted = await promoteObservationToLongTermMemory2({
|
|
29587
|
+
dataDir: projectDir2,
|
|
29588
|
+
observation: obs,
|
|
29589
|
+
scope: longTerm.scope,
|
|
29590
|
+
kind: longTerm.kind,
|
|
29591
|
+
tags: longTerm.tags,
|
|
29592
|
+
applicability: longTerm.applicability,
|
|
29593
|
+
reader: {
|
|
29594
|
+
projectId: project.id,
|
|
29595
|
+
...reader.agentId ? { agentId: reader.agentId } : {},
|
|
29596
|
+
...reader.isTeamMember ? { isTeamMember: true } : {}
|
|
29597
|
+
}
|
|
29598
|
+
});
|
|
29599
|
+
longTermNote = `
|
|
29600
|
+
Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
|
|
29601
|
+
} catch (longTermError) {
|
|
29602
|
+
longTermNote = `
|
|
29603
|
+
[WARN] Observation stored, but long-term candidate was not created: ${longTermError instanceof Error ? longTermError.message : "unknown error"}`;
|
|
29604
|
+
}
|
|
29605
|
+
}
|
|
28608
29606
|
await graphManager.addObservations([
|
|
28609
29607
|
{ entityName, contents: [`[#${obs.id}] ${title}`] }
|
|
28610
29608
|
]);
|
|
@@ -28621,7 +29619,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
28621
29619
|
const enrichment = enrichmentParts.length > 0 ? `
|
|
28622
29620
|
Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
28623
29621
|
const action = upserted ? "[UPDATED] Updated" : "[OK] Stored";
|
|
28624
|
-
if (!useFormation && !topicKey && !progress) {
|
|
29622
|
+
if (!useFormation && !topicKey && !progress && !longTerm) {
|
|
28625
29623
|
const shadowFormation = async () => {
|
|
28626
29624
|
let oldCompactDecision = null;
|
|
28627
29625
|
try {
|
|
@@ -28711,7 +29709,7 @@ Auto-enriched: ${enrichmentParts.join(", ")}` : "";
|
|
|
28711
29709
|
{
|
|
28712
29710
|
type: "text",
|
|
28713
29711
|
text: `${action} observation #${obs.id} "${title}" (~${obs.tokens} tokens)
|
|
28714
|
-
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}`
|
|
29712
|
+
Entity: ${entityName} | Type: ${type} | Project: ${project.id}${obs.topicKey ? ` | Topic: ${obs.topicKey}` : ""}${compactAction}${compressionNote}${enrichment}${formationNote}${attributionWarning}${longTermNote}`
|
|
28715
29713
|
}
|
|
28716
29714
|
]
|
|
28717
29715
|
};
|
|
@@ -29084,7 +30082,8 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
29084
30082
|
} : {},
|
|
29085
30083
|
...external.outline ? { semanticCode: external.outline } : {},
|
|
29086
30084
|
providerQuality: external.quality,
|
|
29087
|
-
...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {}
|
|
30085
|
+
...external.caution ? { runtimeCautions: [{ kind: "external-codegraph-fallback", message: external.caution }] } : {},
|
|
30086
|
+
reader: getObservationReader()
|
|
29088
30087
|
});
|
|
29089
30088
|
const text = buildContextPackPrompt2(pack);
|
|
29090
30089
|
return {
|
|
@@ -29758,7 +30757,7 @@ ${actions.join("\n")}`
|
|
|
29758
30757
|
"memorix_detail",
|
|
29759
30758
|
{
|
|
29760
30759
|
title: "Memory Details",
|
|
29761
|
-
description: 'Fetch full observation
|
|
30760
|
+
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.',
|
|
29762
30761
|
inputSchema: {
|
|
29763
30762
|
ids: z2.array(z2.number()).optional().describe("Observation IDs to fetch (legacy, from memorix_search results)"),
|
|
29764
30763
|
refs: z2.array(
|
|
@@ -29767,7 +30766,7 @@ ${actions.join("\n")}`
|
|
|
29767
30766
|
projectId: z2.string().optional().describe("Project ID for global-search disambiguation")
|
|
29768
30767
|
})
|
|
29769
30768
|
).optional().describe("Explicit observation refs. Prefer this for global search results."),
|
|
29770
|
-
typedRefs: z2.array(z2.string()).optional().describe('Typed memory refs from search results, e.g. "obs:42", "skill:3", "obs:42@org/proj"'),
|
|
30769
|
+
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"'),
|
|
29771
30770
|
purpose: z2.string().optional().describe(
|
|
29772
30771
|
"Why this must expand beyond the latest Autopilot brief. Name the missing fact or the user's explicit request."
|
|
29773
30772
|
),
|
|
@@ -29780,14 +30779,19 @@ ${actions.join("\n")}`
|
|
|
29780
30779
|
const safeIds = coerceNumberArray(ids);
|
|
29781
30780
|
const safeRefs = coerceObservationRefs(refs);
|
|
29782
30781
|
const safeTypedRefs = coerceStringArray(typedRefs);
|
|
29783
|
-
const
|
|
30782
|
+
const durableIds = safeTypedRefs.flatMap((ref) => {
|
|
30783
|
+
const match = /^durable:([0-9a-f-]{36})$/i.exec(ref.trim());
|
|
30784
|
+
return match ? [match[1]] : [];
|
|
30785
|
+
});
|
|
30786
|
+
const observationTypedRefs = safeTypedRefs.filter((ref) => !/^durable:/i.test(ref.trim()));
|
|
30787
|
+
const hasCrossProjectRef = safeRefs.some((ref) => ref.projectId && ref.projectId !== project.id) || observationTypedRefs.some((ref) => ref.includes("@") && !ref.endsWith(`@${project.id}`));
|
|
29784
30788
|
if (!hasCrossProjectRef) {
|
|
29785
30789
|
const boundary = requireExplicitAutopilotExpansion("memorix_detail", purpose);
|
|
29786
30790
|
if (boundary) return boundary;
|
|
29787
30791
|
const requestedObservationIds = [
|
|
29788
30792
|
...safeIds,
|
|
29789
30793
|
...safeRefs.map((ref) => ref.id),
|
|
29790
|
-
...
|
|
30794
|
+
...observationTypedRefs.flatMap((ref) => {
|
|
29791
30795
|
const match = /^obs:(\d+)(?:@.+)?$/i.exec(ref.trim());
|
|
29792
30796
|
return match ? [Number(match[1])] : [];
|
|
29793
30797
|
})
|
|
@@ -29795,15 +30799,36 @@ ${actions.join("\n")}`
|
|
|
29795
30799
|
const duplicate = blockCoveredAutopilotEvidence("memorix_detail", requestedObservationIds, force);
|
|
29796
30800
|
if (duplicate) return duplicate;
|
|
29797
30801
|
}
|
|
29798
|
-
|
|
30802
|
+
const formatted = [];
|
|
29799
30803
|
try {
|
|
29800
|
-
|
|
29801
|
-
|
|
29802
|
-
|
|
29803
|
-
|
|
29804
|
-
|
|
29805
|
-
|
|
29806
|
-
|
|
30804
|
+
if (durableIds.length > 0) {
|
|
30805
|
+
const [{ getLongTermMemoryDetail: getLongTermMemoryDetail2 }, { resolveLocalMemoryOwner: resolveLocalMemoryOwner2 }] = await Promise.all([
|
|
30806
|
+
Promise.resolve().then(() => (init_long_term(), long_term_exports)),
|
|
30807
|
+
Promise.resolve().then(() => (init_owner(), owner_exports))
|
|
30808
|
+
]);
|
|
30809
|
+
const owner = await resolveLocalMemoryOwner2(projectDir2, { create: false });
|
|
30810
|
+
const observationReader = getObservationReader();
|
|
30811
|
+
const durableReader = {
|
|
30812
|
+
projectId: project.id,
|
|
30813
|
+
...owner ? { ownerId: owner.id } : {},
|
|
30814
|
+
...observationReader.agentId ? { agentId: observationReader.agentId } : {},
|
|
30815
|
+
...observationReader.isTeamMember ? { isTeamMember: true } : {}
|
|
30816
|
+
};
|
|
30817
|
+
const details = await Promise.all(durableIds.slice(0, 5).map(async (id) => {
|
|
30818
|
+
try {
|
|
30819
|
+
return await getLongTermMemoryDetail2({ dataDir: projectDir2, id, reader: durableReader });
|
|
30820
|
+
} catch (error) {
|
|
30821
|
+
return { error: error instanceof Error ? error.message : String(error), id };
|
|
30822
|
+
}
|
|
30823
|
+
}));
|
|
30824
|
+
formatted.push(JSON.stringify({ durableMemory: details }, null, 2));
|
|
30825
|
+
}
|
|
30826
|
+
if (observationTypedRefs.length > 0 || safeRefs.length > 0 || safeIds.length > 0) {
|
|
30827
|
+
const reader = getObservationReader(hasCrossProjectRef ? "global" : "project");
|
|
30828
|
+
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 });
|
|
30829
|
+
formatted.push(
|
|
30830
|
+
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(", ")}`
|
|
30831
|
+
);
|
|
29807
30832
|
}
|
|
29808
30833
|
} catch (err) {
|
|
29809
30834
|
return { content: [{ type: "text", text: err instanceof Error ? err.message : String(err) }], isError: true };
|
|
@@ -29812,7 +30837,7 @@ ${actions.join("\n")}`
|
|
|
29812
30837
|
content: [
|
|
29813
30838
|
{
|
|
29814
30839
|
type: "text",
|
|
29815
|
-
text:
|
|
30840
|
+
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(", ")}`
|
|
29816
30841
|
}
|
|
29817
30842
|
]
|
|
29818
30843
|
};
|
|
@@ -30532,9 +31557,9 @@ ${skill2.content}` }]
|
|
|
30532
31557
|
lines.push(`- **Description**: ${sk.description}`);
|
|
30533
31558
|
lines.push(`- **Observations**: ${sk.content.split("\n").length} lines of knowledge`);
|
|
30534
31559
|
if (write && target) {
|
|
30535
|
-
const
|
|
30536
|
-
if (
|
|
30537
|
-
lines.push(`- [OK] **Written**: \`${
|
|
31560
|
+
const path27 = engine.writeSkill(sk, target);
|
|
31561
|
+
if (path27) {
|
|
31562
|
+
lines.push(`- [OK] **Written**: \`${path27}\``);
|
|
30538
31563
|
} else {
|
|
30539
31564
|
lines.push(`- [ERROR] Failed to write`);
|
|
30540
31565
|
}
|
|
@@ -32153,11 +33178,11 @@ async function createMemoryClient(options) {
|
|
|
32153
33178
|
`[memorix-sdk] No Git repository found at "${projectRoot}". Memorix requires a Git-tracked project for identity resolution.`
|
|
32154
33179
|
);
|
|
32155
33180
|
}
|
|
32156
|
-
const
|
|
33181
|
+
const path27 = await import("path");
|
|
32157
33182
|
const os4 = await import("os");
|
|
32158
|
-
const dataDir =
|
|
32159
|
-
const
|
|
32160
|
-
|
|
33183
|
+
const dataDir = path27.join(os4.homedir(), ".memorix", "data", project.id.replace(/\//g, path27.sep));
|
|
33184
|
+
const fs20 = await import("fs");
|
|
33185
|
+
fs20.mkdirSync(dataDir, { recursive: true });
|
|
32161
33186
|
const client = new MemoryClient(project.id, projectRoot, dataDir);
|
|
32162
33187
|
await client._init(silent);
|
|
32163
33188
|
return client;
|