memorix 1.4.5 → 1.5.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 +78 -0
- package/CLAUDE.md +7 -3
- package/README.md +5 -2
- package/dist/cli/index.js +1470 -1014
- package/dist/index.js +1279 -989
- package/dist/{maintenance-jobs-B1-W0ssz.d.ts → maintenance-jobs-DVRF-46r.d.ts} +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +1769 -830
- package/dist/media-audio-runner.js +1774 -836
- package/dist/media-video-runner.js +1769 -831
- package/dist/memcode-runtime/CHANGELOG.md +78 -0
- package/dist/sdk.js +1271 -988
- package/dist/types.d.ts +6 -0
- package/dist/vector-backfill-runner.d.ts +1 -1
- package/dist/vector-backfill-runner.js +1765 -827
- package/docs/1.4.5-AGENT-AUTO-MEMORY-RESEARCH.md +95 -0
- package/docs/API_REFERENCE.md +23 -4
- package/docs/CONFIGURATION.md +29 -3
- package/docs/SETUP.md +5 -3
- package/package.json +1 -1
- package/plugins/antigravity/memorix/mcp_config.json +3 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/memorix/.mcp.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.mcp.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.mcp.json +1 -1
- package/plugins/copilot/memorix/.mcp.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +4 -2
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/openclaw/memorix/.mcp.json +3 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/commands/dashboard.ts +14 -3
- package/src/cli/commands/ingest-commit.ts +47 -3
- package/src/cli/commands/memory.ts +17 -4
- package/src/cli/commands/purge.ts +120 -0
- package/src/cli/commands/serve-http.ts +39 -5
- package/src/cli/commands/setup.ts +3 -1
- package/src/cli/index.ts +3 -804
- package/src/codegraph/auto-context.ts +82 -1
- package/src/config/yaml-loader.ts +3 -8
- package/src/config.ts +23 -7
- package/src/evaluation/session-replay.ts +98 -0
- package/src/git/extractor.ts +5 -3
- package/src/hooks/installers/index.ts +15 -0
- package/src/knowledge/workset.ts +58 -0
- package/src/memory/long-term.ts +97 -2
- package/src/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +19 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/project-maintenance.ts +8 -0
- package/src/search/surfaced-registry.ts +62 -0
- package/src/server.ts +74 -14
- package/src/store/orama-store.ts +17 -1
- package/src/types.ts +6 -0
- package/src/cli/commands/audit-list.ts +0 -89
- package/src/cli/commands/purge-all-memory.ts +0 -85
- package/src/cli/commands/purge-project-memory.ts +0 -83
- package/src/cli/commands/uninstall-project-artifacts.ts +0 -86
- package/src/cli/tui/Sidebar.tsx +0 -179
- package/src/cli/tui/interactive-commands.ts +0 -261
- package/src/cli/tui/markdown-render.tsx +0 -371
- package/src/cli/tui/use-mouse.ts +0 -157
- package/src/cli/tui/useNavigation.ts +0 -56
- package/src/orchestrate/context-compact.ts +0 -188
- package/src/team/file-locks.ts +0 -173
- package/src/team/index.ts +0 -23
- package/src/team/messages.ts +0 -203
- package/src/team/persistence.ts +0 -96
- package/src/team/registry.ts +0 -158
- package/src/team/tasks.ts +0 -211
package/dist/index.js
CHANGED
|
@@ -3373,6 +3373,7 @@ var init_resolved_config = __esm({
|
|
|
3373
3373
|
// src/config.ts
|
|
3374
3374
|
var config_exports = {};
|
|
3375
3375
|
__export(config_exports, {
|
|
3376
|
+
defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
|
|
3376
3377
|
getAgentLLMApiKey: () => getAgentLLMApiKey,
|
|
3377
3378
|
getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
|
|
3378
3379
|
getAgentLLMModel: () => getAgentLLMModel,
|
|
@@ -3389,7 +3390,6 @@ __export(config_exports, {
|
|
|
3389
3390
|
getLLMProvider: () => getLLMProvider,
|
|
3390
3391
|
getLoadedEnvFiles: () => getLoadedEnvFiles,
|
|
3391
3392
|
getServerConfig: () => getServerConfig,
|
|
3392
|
-
getTeamConfig: () => getTeamConfig,
|
|
3393
3393
|
loadDotenv: () => loadDotenv,
|
|
3394
3394
|
loadFileConfig: () => loadFileConfig,
|
|
3395
3395
|
loadYamlConfig: () => loadYamlConfig,
|
|
@@ -3436,7 +3436,19 @@ function getEmbeddingBaseUrl() {
|
|
|
3436
3436
|
return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
|
|
3437
3437
|
}
|
|
3438
3438
|
function getEmbeddingModel() {
|
|
3439
|
-
|
|
3439
|
+
const lane = getResolvedEmbeddingLane();
|
|
3440
|
+
return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
|
|
3441
|
+
}
|
|
3442
|
+
function defaultEmbeddingModelFor(baseUrl, explicitModel) {
|
|
3443
|
+
if (explicitModel) return explicitModel;
|
|
3444
|
+
try {
|
|
3445
|
+
const url = baseUrl ? new URL(baseUrl) : null;
|
|
3446
|
+
if (url && (url.hostname === "openrouter.ai" || url.hostname.endsWith(".openrouter.ai"))) {
|
|
3447
|
+
return "qwen/qwen3-embedding-8b";
|
|
3448
|
+
}
|
|
3449
|
+
} catch {
|
|
3450
|
+
}
|
|
3451
|
+
return "text-embedding-3-small";
|
|
3440
3452
|
}
|
|
3441
3453
|
function getEmbeddingDimensions() {
|
|
3442
3454
|
return getResolvedEmbeddingLane().dimensions ?? null;
|
|
@@ -3461,14 +3473,10 @@ function getServerConfig() {
|
|
|
3461
3473
|
port: server.port
|
|
3462
3474
|
};
|
|
3463
3475
|
}
|
|
3464
|
-
function getTeamConfig() {
|
|
3465
|
-
return loadYamlConfig().team ?? {};
|
|
3466
|
-
}
|
|
3467
3476
|
var init_config = __esm({
|
|
3468
3477
|
"src/config.ts"() {
|
|
3469
3478
|
"use strict";
|
|
3470
3479
|
init_esm_shims();
|
|
3471
|
-
init_yaml_loader();
|
|
3472
3480
|
init_resolved_config();
|
|
3473
3481
|
init_dotenv_loader();
|
|
3474
3482
|
init_legacy_loader();
|
|
@@ -5753,9 +5761,9 @@ async function saveRegistry(baseDir) {
|
|
|
5753
5761
|
await fs7.mkdir(path9.dirname(filePath), { recursive: true });
|
|
5754
5762
|
await fs7.writeFile(filePath, JSON.stringify(registryCache, null, 2), "utf-8");
|
|
5755
5763
|
}
|
|
5756
|
-
function findMatchingGroup(
|
|
5764
|
+
function findMatchingGroup(registry2, projectInfo) {
|
|
5757
5765
|
const normalizedRoot = normalizePath(projectInfo.rootPath);
|
|
5758
|
-
for (const group of
|
|
5766
|
+
for (const group of registry2.groups) {
|
|
5759
5767
|
if (group.aliases.includes(projectInfo.id)) return group;
|
|
5760
5768
|
if (group.rootPaths.some((rp) => rp === normalizedRoot)) return group;
|
|
5761
5769
|
if (projectInfo.gitRemote && group.gitRemote && group.gitRemote === projectInfo.gitRemote) {
|
|
@@ -5768,9 +5776,9 @@ function selectCanonical(aliases) {
|
|
|
5768
5776
|
return [...aliases].sort((a, b) => idPriority(b) - idPriority(a))[0];
|
|
5769
5777
|
}
|
|
5770
5778
|
async function registerAlias(projectInfo, baseDir) {
|
|
5771
|
-
const
|
|
5779
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5772
5780
|
const normalizedRoot = normalizePath(projectInfo.rootPath);
|
|
5773
|
-
const existingGroup = findMatchingGroup(
|
|
5781
|
+
const existingGroup = findMatchingGroup(registry2, projectInfo);
|
|
5774
5782
|
if (existingGroup) {
|
|
5775
5783
|
let changed = false;
|
|
5776
5784
|
if (!existingGroup.aliases.includes(projectInfo.id)) {
|
|
@@ -5801,13 +5809,13 @@ async function registerAlias(projectInfo, baseDir) {
|
|
|
5801
5809
|
rootPaths: [normalizedRoot],
|
|
5802
5810
|
...projectInfo.gitRemote ? { gitRemote: projectInfo.gitRemote } : {}
|
|
5803
5811
|
};
|
|
5804
|
-
|
|
5812
|
+
registry2.groups.push(newGroup);
|
|
5805
5813
|
await saveRegistry(baseDir);
|
|
5806
5814
|
return newGroup.canonical;
|
|
5807
5815
|
}
|
|
5808
5816
|
async function resolveAliases(projectId, baseDir) {
|
|
5809
|
-
const
|
|
5810
|
-
for (const group of
|
|
5817
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5818
|
+
for (const group of registry2.groups) {
|
|
5811
5819
|
if (group.aliases.includes(projectId) || group.canonical === projectId) {
|
|
5812
5820
|
return [...group.aliases];
|
|
5813
5821
|
}
|
|
@@ -5815,8 +5823,8 @@ async function resolveAliases(projectId, baseDir) {
|
|
|
5815
5823
|
return [projectId];
|
|
5816
5824
|
}
|
|
5817
5825
|
async function getCanonicalId(projectId, baseDir) {
|
|
5818
|
-
const
|
|
5819
|
-
for (const group of
|
|
5826
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5827
|
+
for (const group of registry2.groups) {
|
|
5820
5828
|
if (group.aliases.includes(projectId) || group.canonical === projectId) {
|
|
5821
5829
|
return group.canonical;
|
|
5822
5830
|
}
|
|
@@ -5824,12 +5832,12 @@ async function getCanonicalId(projectId, baseDir) {
|
|
|
5824
5832
|
return projectId;
|
|
5825
5833
|
}
|
|
5826
5834
|
async function getAllAliasGroups(baseDir) {
|
|
5827
|
-
const
|
|
5828
|
-
return
|
|
5835
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5836
|
+
return registry2.groups;
|
|
5829
5837
|
}
|
|
5830
5838
|
async function autoMergeByBaseName(observedIds, baseDir) {
|
|
5831
5839
|
if (observedIds.length <= 1) return 0;
|
|
5832
|
-
const
|
|
5840
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5833
5841
|
const byBaseName = /* @__PURE__ */ new Map();
|
|
5834
5842
|
for (const id of observedIds) {
|
|
5835
5843
|
const baseName = id.split("/").pop() ?? id;
|
|
@@ -5842,7 +5850,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5842
5850
|
const existingGroups = /* @__PURE__ */ new Set();
|
|
5843
5851
|
const ungroupedIds = [];
|
|
5844
5852
|
for (const id of ids) {
|
|
5845
|
-
const groupIdx =
|
|
5853
|
+
const groupIdx = registry2.groups.findIndex(
|
|
5846
5854
|
(g) => g.aliases.includes(id) || g.canonical === id
|
|
5847
5855
|
);
|
|
5848
5856
|
if (groupIdx >= 0) {
|
|
@@ -5856,7 +5864,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5856
5864
|
const canonical = selectCanonical(allIdsInGroup);
|
|
5857
5865
|
if (existingGroups.size > 0) {
|
|
5858
5866
|
const primaryIdx = [...existingGroups][0];
|
|
5859
|
-
const primaryGroup =
|
|
5867
|
+
const primaryGroup = registry2.groups[primaryIdx];
|
|
5860
5868
|
for (const id of allIdsInGroup) {
|
|
5861
5869
|
if (!primaryGroup.aliases.includes(id)) {
|
|
5862
5870
|
primaryGroup.aliases.push(id);
|
|
@@ -5864,7 +5872,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5864
5872
|
}
|
|
5865
5873
|
const otherIdxs = [...existingGroups].slice(1).sort((a, b) => b - a);
|
|
5866
5874
|
for (const idx of otherIdxs) {
|
|
5867
|
-
const other =
|
|
5875
|
+
const other = registry2.groups[idx];
|
|
5868
5876
|
for (const alias of other.aliases) {
|
|
5869
5877
|
if (!primaryGroup.aliases.includes(alias)) {
|
|
5870
5878
|
primaryGroup.aliases.push(alias);
|
|
@@ -5878,12 +5886,12 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5878
5886
|
if (other.gitRemote && !primaryGroup.gitRemote) {
|
|
5879
5887
|
primaryGroup.gitRemote = other.gitRemote;
|
|
5880
5888
|
}
|
|
5881
|
-
|
|
5889
|
+
registry2.groups.splice(idx, 1);
|
|
5882
5890
|
}
|
|
5883
5891
|
primaryGroup.canonical = selectCanonical(primaryGroup.aliases);
|
|
5884
5892
|
mergeCount++;
|
|
5885
5893
|
} else {
|
|
5886
|
-
|
|
5894
|
+
registry2.groups.push({
|
|
5887
5895
|
canonical,
|
|
5888
5896
|
aliases: allIdsInGroup,
|
|
5889
5897
|
rootPaths: []
|
|
@@ -6519,7 +6527,7 @@ async function searchObservations(options) {
|
|
|
6519
6527
|
const expandedEmbeddingQuery = quality === "thorough" && tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
|
|
6520
6528
|
mark("queryExpansion");
|
|
6521
6529
|
const intentResult = quality === "fast" || !hasQuery ? null : detectQueryIntent(originalQuery);
|
|
6522
|
-
const requestLimit = projectIds ? (options.limit ?? 20) * 3 : options.limit ?? 20;
|
|
6530
|
+
const requestLimit = projectIds || options.surfacedIds && options.surfacedIds.length > 0 ? (options.limit ?? 20) * 3 : options.limit ?? 20;
|
|
6523
6531
|
const defaultBoost = {
|
|
6524
6532
|
title: 3,
|
|
6525
6533
|
entityName: 2,
|
|
@@ -6751,6 +6759,14 @@ async function searchObservations(options) {
|
|
|
6751
6759
|
});
|
|
6752
6760
|
intermediate.sort((a, b) => b.score - a.score);
|
|
6753
6761
|
}
|
|
6762
|
+
if (options.surfacedIds && options.surfacedIds.length > 0) {
|
|
6763
|
+
const SURFACED_PENALTY = 0.5;
|
|
6764
|
+
const surfaced = new Set(options.surfacedIds);
|
|
6765
|
+
intermediate = intermediate.map(
|
|
6766
|
+
(entry) => surfaced.has(entry.id) ? { ...entry, score: (entry.score ?? 0) * SURFACED_PENALTY } : entry
|
|
6767
|
+
);
|
|
6768
|
+
intermediate.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
|
6769
|
+
}
|
|
6754
6770
|
if (options.since) {
|
|
6755
6771
|
const sinceDate = new Date(options.since).getTime();
|
|
6756
6772
|
intermediate = intermediate.filter((e2) => new Date(e2.rawTime).getTime() >= sinceDate);
|
|
@@ -7742,7 +7758,8 @@ var init_maintenance_jobs = __esm({
|
|
|
7742
7758
|
"claim-requalification",
|
|
7743
7759
|
"knowledge-compile",
|
|
7744
7760
|
"knowledge-lint",
|
|
7745
|
-
"workflow-index"
|
|
7761
|
+
"workflow-index",
|
|
7762
|
+
"long-term-maintenance"
|
|
7746
7763
|
];
|
|
7747
7764
|
DEFAULT_MAX_ATTEMPTS = 8;
|
|
7748
7765
|
DEFAULT_LEASE_MS = 3e4;
|
|
@@ -8151,6 +8168,7 @@ __export(lifecycle_exports, {
|
|
|
8151
8168
|
enqueueClaimRequalification: () => enqueueClaimRequalification,
|
|
8152
8169
|
enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
|
|
8153
8170
|
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
|
|
8171
|
+
enqueueLongTermMaintenance: () => enqueueLongTermMaintenance,
|
|
8154
8172
|
enqueueObservationQualification: () => enqueueObservationQualification
|
|
8155
8173
|
});
|
|
8156
8174
|
function queueFor(input) {
|
|
@@ -8194,6 +8212,14 @@ function enqueueObservationQualification(input) {
|
|
|
8194
8212
|
payload: { source: input.source, limit: 100 }
|
|
8195
8213
|
});
|
|
8196
8214
|
}
|
|
8215
|
+
function enqueueLongTermMaintenance(input) {
|
|
8216
|
+
queueFor(input).enqueue({
|
|
8217
|
+
projectId: input.projectId,
|
|
8218
|
+
kind: "long-term-maintenance",
|
|
8219
|
+
dedupeKey: "long-term-maintenance",
|
|
8220
|
+
payload: { source: input.source }
|
|
8221
|
+
});
|
|
8222
|
+
}
|
|
8197
8223
|
function enqueueKnowledgeFollowups(input) {
|
|
8198
8224
|
const queue = queueFor(input);
|
|
8199
8225
|
const payload = {
|
|
@@ -8373,7 +8399,8 @@ var init_isolated_maintenance = __esm({
|
|
|
8373
8399
|
"claim-requalification",
|
|
8374
8400
|
"knowledge-compile",
|
|
8375
8401
|
"knowledge-lint",
|
|
8376
|
-
"workflow-index"
|
|
8402
|
+
"workflow-index",
|
|
8403
|
+
"long-term-maintenance"
|
|
8377
8404
|
];
|
|
8378
8405
|
DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
8379
8406
|
MAX_CHILD_OUTPUT_BYTES = 1e6;
|
|
@@ -144220,6 +144247,889 @@ var init_retention = __esm({
|
|
|
144220
144247
|
}
|
|
144221
144248
|
});
|
|
144222
144249
|
|
|
144250
|
+
// src/memory/owner.ts
|
|
144251
|
+
var owner_exports = {};
|
|
144252
|
+
__export(owner_exports, {
|
|
144253
|
+
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
144254
|
+
});
|
|
144255
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
144256
|
+
import { promises as fs12 } from "fs";
|
|
144257
|
+
import path19 from "path";
|
|
144258
|
+
function ownerPath(dataDir) {
|
|
144259
|
+
const resolved = path19.resolve(dataDir);
|
|
144260
|
+
const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
|
|
144261
|
+
return path19.join(home, "memorix-user.json");
|
|
144262
|
+
}
|
|
144263
|
+
function normalizedOwnerId(value) {
|
|
144264
|
+
const trimmed = value?.trim();
|
|
144265
|
+
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
144266
|
+
}
|
|
144267
|
+
async function readOwnerFile(filePath) {
|
|
144268
|
+
try {
|
|
144269
|
+
const value = JSON.parse(await fs12.readFile(filePath, "utf8"));
|
|
144270
|
+
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
144271
|
+
return id ? { id, source: "local-file" } : void 0;
|
|
144272
|
+
} catch {
|
|
144273
|
+
return void 0;
|
|
144274
|
+
}
|
|
144275
|
+
}
|
|
144276
|
+
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
144277
|
+
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
144278
|
+
if (configured) return { id: configured, source: "environment" };
|
|
144279
|
+
const filePath = ownerPath(dataDir);
|
|
144280
|
+
const existing = await readOwnerFile(filePath);
|
|
144281
|
+
if (existing || !options.create) return existing;
|
|
144282
|
+
await fs12.mkdir(path19.dirname(filePath), { recursive: true });
|
|
144283
|
+
const created = {
|
|
144284
|
+
version: 1,
|
|
144285
|
+
id: "local-" + randomUUID6(),
|
|
144286
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
144287
|
+
};
|
|
144288
|
+
try {
|
|
144289
|
+
await fs12.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
144290
|
+
return { id: created.id, source: "local-file" };
|
|
144291
|
+
} catch (error) {
|
|
144292
|
+
if (error?.code !== "EEXIST") throw error;
|
|
144293
|
+
const concurrent = await readOwnerFile(filePath);
|
|
144294
|
+
if (concurrent) return concurrent;
|
|
144295
|
+
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
144296
|
+
}
|
|
144297
|
+
}
|
|
144298
|
+
var ID_PATTERN;
|
|
144299
|
+
var init_owner = __esm({
|
|
144300
|
+
"src/memory/owner.ts"() {
|
|
144301
|
+
"use strict";
|
|
144302
|
+
init_esm_shims();
|
|
144303
|
+
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
144304
|
+
}
|
|
144305
|
+
});
|
|
144306
|
+
|
|
144307
|
+
// src/memory/long-term-store.ts
|
|
144308
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
144309
|
+
function optionalText2(value) {
|
|
144310
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
144311
|
+
}
|
|
144312
|
+
function arrayValue2(value) {
|
|
144313
|
+
if (typeof value !== "string" || !value) return [];
|
|
144314
|
+
try {
|
|
144315
|
+
const parsed = JSON.parse(value);
|
|
144316
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
144317
|
+
} catch {
|
|
144318
|
+
return [];
|
|
144319
|
+
}
|
|
144320
|
+
}
|
|
144321
|
+
function rowToMemory(row) {
|
|
144322
|
+
return {
|
|
144323
|
+
id: row.id,
|
|
144324
|
+
originProjectId: row.originProjectId,
|
|
144325
|
+
ownerId: row.ownerId,
|
|
144326
|
+
scope: row.scope,
|
|
144327
|
+
kind: row.kind,
|
|
144328
|
+
state: row.state,
|
|
144329
|
+
portability: row.portability,
|
|
144330
|
+
title: row.title,
|
|
144331
|
+
content: row.content,
|
|
144332
|
+
facts: arrayValue2(row.factsJson),
|
|
144333
|
+
tags: arrayValue2(row.tagsJson),
|
|
144334
|
+
...optionalText2(row.applicability) ? { applicability: row.applicability } : {},
|
|
144335
|
+
origin: row.origin,
|
|
144336
|
+
createdAt: row.createdAt,
|
|
144337
|
+
updatedAt: row.updatedAt,
|
|
144338
|
+
...optionalText2(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
144339
|
+
...optionalText2(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
144340
|
+
...optionalText2(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
144341
|
+
...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
144342
|
+
...optionalText2(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
144343
|
+
accessCount: Number(row.accessCount ?? 0),
|
|
144344
|
+
...optionalText2(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
144345
|
+
};
|
|
144346
|
+
}
|
|
144347
|
+
function rowToEvidence(row) {
|
|
144348
|
+
return {
|
|
144349
|
+
id: row.id,
|
|
144350
|
+
memoryId: row.memoryId,
|
|
144351
|
+
kind: row.kind,
|
|
144352
|
+
referenceId: row.referenceId,
|
|
144353
|
+
relation: row.relation,
|
|
144354
|
+
...optionalText2(row.locator) ? { locator: row.locator } : {},
|
|
144355
|
+
...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
144356
|
+
createdAt: row.createdAt
|
|
144357
|
+
};
|
|
144358
|
+
}
|
|
144359
|
+
function rowToEvent(row) {
|
|
144360
|
+
return {
|
|
144361
|
+
id: row.id,
|
|
144362
|
+
memoryId: row.memoryId,
|
|
144363
|
+
kind: row.kind,
|
|
144364
|
+
...optionalText2(row.fromState) ? { fromState: row.fromState } : {},
|
|
144365
|
+
...optionalText2(row.toState) ? { toState: row.toState } : {},
|
|
144366
|
+
...optionalText2(row.detail) ? { detail: row.detail } : {},
|
|
144367
|
+
createdAt: row.createdAt
|
|
144368
|
+
};
|
|
144369
|
+
}
|
|
144370
|
+
var LongTermMemoryStore;
|
|
144371
|
+
var init_long_term_store = __esm({
|
|
144372
|
+
"src/memory/long-term-store.ts"() {
|
|
144373
|
+
"use strict";
|
|
144374
|
+
init_esm_shims();
|
|
144375
|
+
init_sqlite_db();
|
|
144376
|
+
LongTermMemoryStore = class {
|
|
144377
|
+
db = null;
|
|
144378
|
+
async init(dataDir) {
|
|
144379
|
+
this.db = getDatabase(dataDir);
|
|
144380
|
+
}
|
|
144381
|
+
transaction(fn) {
|
|
144382
|
+
return this.db.transaction(fn)();
|
|
144383
|
+
}
|
|
144384
|
+
get(id) {
|
|
144385
|
+
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
144386
|
+
return row ? rowToMemory(row) : void 0;
|
|
144387
|
+
}
|
|
144388
|
+
list(options = {}) {
|
|
144389
|
+
const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
144390
|
+
const clauses = [];
|
|
144391
|
+
const params = [];
|
|
144392
|
+
if (options.ownerId) {
|
|
144393
|
+
clauses.push("ownerId = ?");
|
|
144394
|
+
params.push(options.ownerId);
|
|
144395
|
+
}
|
|
144396
|
+
if (options.originProjectId) {
|
|
144397
|
+
clauses.push("originProjectId = ?");
|
|
144398
|
+
params.push(options.originProjectId);
|
|
144399
|
+
}
|
|
144400
|
+
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
144401
|
+
const rows = this.db.prepare(
|
|
144402
|
+
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
144403
|
+
).all(...params, limit2);
|
|
144404
|
+
return rows.map(rowToMemory);
|
|
144405
|
+
}
|
|
144406
|
+
insert(memory) {
|
|
144407
|
+
this.db.prepare(`
|
|
144408
|
+
INSERT INTO long_term_memories (
|
|
144409
|
+
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
144410
|
+
title, content, factsJson, tagsJson, applicability, origin,
|
|
144411
|
+
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
144412
|
+
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
144413
|
+
) VALUES (
|
|
144414
|
+
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
144415
|
+
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
144416
|
+
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
144417
|
+
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
144418
|
+
)
|
|
144419
|
+
`).run({
|
|
144420
|
+
...memory,
|
|
144421
|
+
factsJson: JSON.stringify(memory.facts),
|
|
144422
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
144423
|
+
applicability: memory.applicability ?? null,
|
|
144424
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
144425
|
+
approvedAt: memory.approvedAt ?? null,
|
|
144426
|
+
archivedAt: memory.archivedAt ?? null,
|
|
144427
|
+
supersededBy: memory.supersededBy ?? null,
|
|
144428
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
144429
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
144430
|
+
});
|
|
144431
|
+
}
|
|
144432
|
+
update(memory) {
|
|
144433
|
+
this.db.prepare(`
|
|
144434
|
+
UPDATE long_term_memories SET
|
|
144435
|
+
scope = @scope,
|
|
144436
|
+
kind = @kind,
|
|
144437
|
+
state = @state,
|
|
144438
|
+
portability = @portability,
|
|
144439
|
+
title = @title,
|
|
144440
|
+
content = @content,
|
|
144441
|
+
factsJson = @factsJson,
|
|
144442
|
+
tagsJson = @tagsJson,
|
|
144443
|
+
applicability = @applicability,
|
|
144444
|
+
updatedAt = @updatedAt,
|
|
144445
|
+
qualifiedAt = @qualifiedAt,
|
|
144446
|
+
approvedAt = @approvedAt,
|
|
144447
|
+
archivedAt = @archivedAt,
|
|
144448
|
+
supersededBy = @supersededBy,
|
|
144449
|
+
lastValidatedAt = @lastValidatedAt,
|
|
144450
|
+
accessCount = @accessCount,
|
|
144451
|
+
lastAccessedAt = @lastAccessedAt
|
|
144452
|
+
WHERE id = @id
|
|
144453
|
+
`).run({
|
|
144454
|
+
...memory,
|
|
144455
|
+
factsJson: JSON.stringify(memory.facts),
|
|
144456
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
144457
|
+
applicability: memory.applicability ?? null,
|
|
144458
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
144459
|
+
approvedAt: memory.approvedAt ?? null,
|
|
144460
|
+
archivedAt: memory.archivedAt ?? null,
|
|
144461
|
+
supersededBy: memory.supersededBy ?? null,
|
|
144462
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
144463
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
144464
|
+
});
|
|
144465
|
+
}
|
|
144466
|
+
insertEvidence(input) {
|
|
144467
|
+
const evidence = {
|
|
144468
|
+
id: randomUUID7(),
|
|
144469
|
+
...input,
|
|
144470
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
144471
|
+
};
|
|
144472
|
+
this.db.prepare(`
|
|
144473
|
+
INSERT INTO long_term_memory_evidence (
|
|
144474
|
+
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
144475
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
144476
|
+
`).run(
|
|
144477
|
+
evidence.id,
|
|
144478
|
+
evidence.memoryId,
|
|
144479
|
+
evidence.kind,
|
|
144480
|
+
evidence.referenceId,
|
|
144481
|
+
evidence.relation,
|
|
144482
|
+
evidence.locator ?? null,
|
|
144483
|
+
evidence.capturedHash ?? null,
|
|
144484
|
+
evidence.createdAt
|
|
144485
|
+
);
|
|
144486
|
+
return evidence;
|
|
144487
|
+
}
|
|
144488
|
+
listEvidence(memoryId) {
|
|
144489
|
+
return this.db.prepare(`
|
|
144490
|
+
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
144491
|
+
`).all(memoryId).map(rowToEvidence);
|
|
144492
|
+
}
|
|
144493
|
+
recordEvent(input) {
|
|
144494
|
+
const event = {
|
|
144495
|
+
id: randomUUID7(),
|
|
144496
|
+
...input,
|
|
144497
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
144498
|
+
};
|
|
144499
|
+
this.db.prepare(`
|
|
144500
|
+
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
144501
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
144502
|
+
`).run(
|
|
144503
|
+
event.id,
|
|
144504
|
+
event.memoryId,
|
|
144505
|
+
event.kind,
|
|
144506
|
+
event.fromState ?? null,
|
|
144507
|
+
event.toState ?? null,
|
|
144508
|
+
event.detail ?? null,
|
|
144509
|
+
event.createdAt
|
|
144510
|
+
);
|
|
144511
|
+
return event;
|
|
144512
|
+
}
|
|
144513
|
+
listEvents(memoryId) {
|
|
144514
|
+
return this.db.prepare(`
|
|
144515
|
+
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
144516
|
+
`).all(memoryId).map(rowToEvent);
|
|
144517
|
+
}
|
|
144518
|
+
markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
144519
|
+
const unique2 = [...new Set(ids.filter(Boolean))];
|
|
144520
|
+
if (unique2.length === 0) return;
|
|
144521
|
+
const statement = this.db.prepare(`
|
|
144522
|
+
UPDATE long_term_memories
|
|
144523
|
+
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
144524
|
+
WHERE id = ?
|
|
144525
|
+
`);
|
|
144526
|
+
this.db.transaction(() => {
|
|
144527
|
+
for (const id of unique2) statement.run(at, id);
|
|
144528
|
+
})();
|
|
144529
|
+
}
|
|
144530
|
+
};
|
|
144531
|
+
}
|
|
144532
|
+
});
|
|
144533
|
+
|
|
144534
|
+
// src/memory/long-term-types.ts
|
|
144535
|
+
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
144536
|
+
var init_long_term_types = __esm({
|
|
144537
|
+
"src/memory/long-term-types.ts"() {
|
|
144538
|
+
"use strict";
|
|
144539
|
+
init_esm_shims();
|
|
144540
|
+
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
144541
|
+
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
144542
|
+
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
144543
|
+
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
144544
|
+
"observation",
|
|
144545
|
+
"claim",
|
|
144546
|
+
"workflow",
|
|
144547
|
+
"session",
|
|
144548
|
+
"checkpoint",
|
|
144549
|
+
"git",
|
|
144550
|
+
"code",
|
|
144551
|
+
"test",
|
|
144552
|
+
"run",
|
|
144553
|
+
"document",
|
|
144554
|
+
"manual",
|
|
144555
|
+
"user"
|
|
144556
|
+
];
|
|
144557
|
+
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
144558
|
+
"supports",
|
|
144559
|
+
"verifies",
|
|
144560
|
+
"derives",
|
|
144561
|
+
"user-confirmed"
|
|
144562
|
+
];
|
|
144563
|
+
}
|
|
144564
|
+
});
|
|
144565
|
+
|
|
144566
|
+
// src/memory/long-term.ts
|
|
144567
|
+
var long_term_exports = {};
|
|
144568
|
+
__export(long_term_exports, {
|
|
144569
|
+
approveLongTermMemory: () => approveLongTermMemory,
|
|
144570
|
+
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
144571
|
+
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
144572
|
+
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
144573
|
+
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
144574
|
+
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
144575
|
+
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
144576
|
+
listLongTermMemories: () => listLongTermMemories,
|
|
144577
|
+
maintainLongTermMemories: () => maintainLongTermMemories,
|
|
144578
|
+
maybeAutoQualifyLongTermMemory: () => maybeAutoQualifyLongTermMemory,
|
|
144579
|
+
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
144580
|
+
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
144581
|
+
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
144582
|
+
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
144583
|
+
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
144584
|
+
});
|
|
144585
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
144586
|
+
function isOneOf(value, values) {
|
|
144587
|
+
return typeof value === "string" && values.includes(value);
|
|
144588
|
+
}
|
|
144589
|
+
function compactText(value, field, max = MAX_TEXT_LENGTH) {
|
|
144590
|
+
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
144591
|
+
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
144592
|
+
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
144593
|
+
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
144594
|
+
return safe;
|
|
144595
|
+
}
|
|
144596
|
+
function compactList(values, field) {
|
|
144597
|
+
if (!values) return [];
|
|
144598
|
+
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
144599
|
+
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
144600
|
+
}
|
|
144601
|
+
function normalizeEvidence(input) {
|
|
144602
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
144603
|
+
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
144604
|
+
}
|
|
144605
|
+
return input.map((evidence) => {
|
|
144606
|
+
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
144607
|
+
throw new Error("Long-term memory evidence kind is invalid.");
|
|
144608
|
+
}
|
|
144609
|
+
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
144610
|
+
throw new Error("Long-term memory evidence relation is invalid.");
|
|
144611
|
+
}
|
|
144612
|
+
return {
|
|
144613
|
+
kind: evidence.kind,
|
|
144614
|
+
referenceId: compactText(evidence.referenceId, "evidence reference", 1e3),
|
|
144615
|
+
relation: evidence.relation,
|
|
144616
|
+
...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator", 1e3) } : {},
|
|
144617
|
+
...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
144618
|
+
};
|
|
144619
|
+
});
|
|
144620
|
+
}
|
|
144621
|
+
function assertScope(scope) {
|
|
144622
|
+
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
144623
|
+
}
|
|
144624
|
+
function assertKind(kind) {
|
|
144625
|
+
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
144626
|
+
}
|
|
144627
|
+
function assertPortability(value) {
|
|
144628
|
+
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
144629
|
+
throw new Error("Long-term memory portability is invalid.");
|
|
144630
|
+
}
|
|
144631
|
+
}
|
|
144632
|
+
function assertPortableEvidence(scope, portability, evidence) {
|
|
144633
|
+
if (portability !== "portable") return;
|
|
144634
|
+
if (scope !== "user") {
|
|
144635
|
+
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
144636
|
+
}
|
|
144637
|
+
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
144638
|
+
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.");
|
|
144639
|
+
}
|
|
144640
|
+
}
|
|
144641
|
+
function requireTeamReader(scope, reader) {
|
|
144642
|
+
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
144643
|
+
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
144644
|
+
}
|
|
144645
|
+
}
|
|
144646
|
+
function matchesScope(memory, reader) {
|
|
144647
|
+
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
144648
|
+
if (memory.scope === "team") {
|
|
144649
|
+
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
144650
|
+
}
|
|
144651
|
+
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
144652
|
+
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
144653
|
+
}
|
|
144654
|
+
function canReadLongTermMemory(memory, reader) {
|
|
144655
|
+
return matchesScope(memory, reader);
|
|
144656
|
+
}
|
|
144657
|
+
function isEligibleLongTermMemory(memory) {
|
|
144658
|
+
return memory.state === "qualified" || memory.state === "approved";
|
|
144659
|
+
}
|
|
144660
|
+
function taskTerms(task) {
|
|
144661
|
+
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
144662
|
+
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
144663
|
+
}
|
|
144664
|
+
function kindFitScore(kind, task) {
|
|
144665
|
+
const normalized = task.toLocaleLowerCase("en-US");
|
|
144666
|
+
const hints = {
|
|
144667
|
+
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
144668
|
+
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
144669
|
+
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
144670
|
+
};
|
|
144671
|
+
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
144672
|
+
}
|
|
144673
|
+
function validationFreshnessScore(memory) {
|
|
144674
|
+
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
144675
|
+
if (!validatedAt) return 0;
|
|
144676
|
+
const time = Date.parse(validatedAt);
|
|
144677
|
+
if (!Number.isFinite(time)) return 0;
|
|
144678
|
+
const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
|
|
144679
|
+
if (ageDays <= 30) return 0.35;
|
|
144680
|
+
if (ageDays <= 90) return 0.15;
|
|
144681
|
+
return 0;
|
|
144682
|
+
}
|
|
144683
|
+
function scoreForTask(memory, task) {
|
|
144684
|
+
const terms = taskTerms(task);
|
|
144685
|
+
if (terms.length === 0) return void 0;
|
|
144686
|
+
const title = memory.title.toLocaleLowerCase("en-US");
|
|
144687
|
+
const content = memory.content.toLocaleLowerCase("en-US");
|
|
144688
|
+
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
144689
|
+
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
144690
|
+
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
144691
|
+
let score = memory.state === "approved" ? 0.5 : 0;
|
|
144692
|
+
const matched = [];
|
|
144693
|
+
for (const term of terms) {
|
|
144694
|
+
let termScore = 0;
|
|
144695
|
+
if (title.includes(term)) termScore += 5;
|
|
144696
|
+
if (tags.includes(term)) termScore += 4;
|
|
144697
|
+
if (applicability.includes(term)) termScore += 3;
|
|
144698
|
+
if (facts.includes(term)) termScore += 2;
|
|
144699
|
+
if (content.includes(term)) termScore += 1;
|
|
144700
|
+
if (termScore > 0) {
|
|
144701
|
+
score += termScore;
|
|
144702
|
+
matched.push(term);
|
|
144703
|
+
}
|
|
144704
|
+
}
|
|
144705
|
+
if (matched.length === 0) return void 0;
|
|
144706
|
+
score += kindFitScore(memory.kind, task);
|
|
144707
|
+
score += validationFreshnessScore(memory);
|
|
144708
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
144709
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
144710
|
+
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
144711
|
+
}
|
|
144712
|
+
function semanticText(memory) {
|
|
144713
|
+
return sanitizeCredentials([
|
|
144714
|
+
memory.title,
|
|
144715
|
+
memory.applicability,
|
|
144716
|
+
memory.tags.join(" "),
|
|
144717
|
+
memory.facts.join(" "),
|
|
144718
|
+
memory.content
|
|
144719
|
+
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
144720
|
+
}
|
|
144721
|
+
function cosineSimilarity(left, right) {
|
|
144722
|
+
if (left.length === 0 || left.length !== right.length) return 0;
|
|
144723
|
+
let dot = 0;
|
|
144724
|
+
let leftMagnitude = 0;
|
|
144725
|
+
let rightMagnitude = 0;
|
|
144726
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
144727
|
+
const a = left[index];
|
|
144728
|
+
const b = right[index];
|
|
144729
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
|
|
144730
|
+
dot += a * b;
|
|
144731
|
+
leftMagnitude += a * a;
|
|
144732
|
+
rightMagnitude += b * b;
|
|
144733
|
+
}
|
|
144734
|
+
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
144735
|
+
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
144736
|
+
}
|
|
144737
|
+
function semanticSelection(memory, similarity, task) {
|
|
144738
|
+
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
144739
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
144740
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
144741
|
+
return {
|
|
144742
|
+
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
144743
|
+
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
144744
|
+
};
|
|
144745
|
+
}
|
|
144746
|
+
async function resolveSemanticProvider(explicit) {
|
|
144747
|
+
if (explicit !== void 0) return explicit;
|
|
144748
|
+
try {
|
|
144749
|
+
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
144750
|
+
return await withTimeout(
|
|
144751
|
+
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
144752
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
144753
|
+
"Long-term semantic provider initialization"
|
|
144754
|
+
);
|
|
144755
|
+
} catch {
|
|
144756
|
+
return null;
|
|
144757
|
+
}
|
|
144758
|
+
}
|
|
144759
|
+
function createStore(dataDir) {
|
|
144760
|
+
const store = new LongTermMemoryStore();
|
|
144761
|
+
return store.init(dataDir).then(() => store);
|
|
144762
|
+
}
|
|
144763
|
+
async function createManualLongTermMemory(input) {
|
|
144764
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
144765
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
144766
|
+
assertScope(input.scope);
|
|
144767
|
+
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
|
|
144768
|
+
return createLongTermMemoryWithDataDir({
|
|
144769
|
+
dataDir: input.dataDir,
|
|
144770
|
+
originProjectId: input.projectId,
|
|
144771
|
+
ownerId: owner.id,
|
|
144772
|
+
scope: input.scope,
|
|
144773
|
+
kind: input.kind,
|
|
144774
|
+
portability: input.portability,
|
|
144775
|
+
title: input.title,
|
|
144776
|
+
content: input.content,
|
|
144777
|
+
facts: input.facts,
|
|
144778
|
+
tags: input.tags,
|
|
144779
|
+
applicability: input.applicability,
|
|
144780
|
+
origin: "manual",
|
|
144781
|
+
evidence,
|
|
144782
|
+
reader: input.reader
|
|
144783
|
+
});
|
|
144784
|
+
}
|
|
144785
|
+
async function promoteObservationToLongTermMemory(input) {
|
|
144786
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
144787
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
144788
|
+
return createLongTermMemoryWithDataDir({
|
|
144789
|
+
dataDir: input.dataDir,
|
|
144790
|
+
originProjectId: input.observation.projectId,
|
|
144791
|
+
ownerId: owner.id,
|
|
144792
|
+
scope: input.scope,
|
|
144793
|
+
kind: input.kind,
|
|
144794
|
+
portability: "project-bound",
|
|
144795
|
+
title: input.observation.title,
|
|
144796
|
+
content: input.observation.narrative,
|
|
144797
|
+
facts: input.observation.facts,
|
|
144798
|
+
tags: input.tags ?? input.observation.concepts,
|
|
144799
|
+
applicability: input.applicability,
|
|
144800
|
+
origin: input.observation.source === "git" ? "git" : "agent",
|
|
144801
|
+
evidence: [{
|
|
144802
|
+
kind: "observation",
|
|
144803
|
+
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
144804
|
+
relation: "derives"
|
|
144805
|
+
}],
|
|
144806
|
+
reader: input.reader
|
|
144807
|
+
});
|
|
144808
|
+
}
|
|
144809
|
+
async function createLongTermMemoryCandidate(input) {
|
|
144810
|
+
return createLongTermMemoryWithDataDir(input);
|
|
144811
|
+
}
|
|
144812
|
+
async function createLongTermMemoryWithDataDir(input) {
|
|
144813
|
+
assertScope(input.scope);
|
|
144814
|
+
assertKind(input.kind);
|
|
144815
|
+
const portability = input.portability ?? "project-bound";
|
|
144816
|
+
assertPortability(portability);
|
|
144817
|
+
requireTeamReader(input.scope, input.reader);
|
|
144818
|
+
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
144819
|
+
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
144820
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144821
|
+
const memory = {
|
|
144822
|
+
id: randomUUID8(),
|
|
144823
|
+
originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
|
|
144824
|
+
ownerId: compactText(input.ownerId, "owner id", 160),
|
|
144825
|
+
scope: input.scope,
|
|
144826
|
+
kind: input.kind,
|
|
144827
|
+
state: "candidate",
|
|
144828
|
+
portability,
|
|
144829
|
+
title: compactText(input.title, "title", 500),
|
|
144830
|
+
content: compactText(input.content, "content"),
|
|
144831
|
+
facts: compactList(input.facts, "facts"),
|
|
144832
|
+
tags: compactList(input.tags, "tags"),
|
|
144833
|
+
...input.applicability ? { applicability: compactText(input.applicability, "applicability", 1e3) } : {},
|
|
144834
|
+
origin: input.origin ?? "manual",
|
|
144835
|
+
createdAt: now3,
|
|
144836
|
+
updatedAt: now3,
|
|
144837
|
+
accessCount: 0
|
|
144838
|
+
};
|
|
144839
|
+
const store = await createStore(input.dataDir);
|
|
144840
|
+
const evidence = store.transaction(() => {
|
|
144841
|
+
store.insert(memory);
|
|
144842
|
+
const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
144843
|
+
store.recordEvent({
|
|
144844
|
+
memoryId: memory.id,
|
|
144845
|
+
kind: "created",
|
|
144846
|
+
toState: "candidate",
|
|
144847
|
+
detail: "Created as a source-backed long-term memory candidate.",
|
|
144848
|
+
createdAt: now3
|
|
144849
|
+
});
|
|
144850
|
+
return written;
|
|
144851
|
+
});
|
|
144852
|
+
return { memory, evidence };
|
|
144853
|
+
}
|
|
144854
|
+
function transitionDetail(value) {
|
|
144855
|
+
return compactText(value, "transition reason", 1e3);
|
|
144856
|
+
}
|
|
144857
|
+
async function transitionMemory(input) {
|
|
144858
|
+
const store = await createStore(input.dataDir);
|
|
144859
|
+
return store.transaction(() => {
|
|
144860
|
+
const current = store.get(input.id);
|
|
144861
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144862
|
+
if (current.state !== input.expected) {
|
|
144863
|
+
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
144864
|
+
}
|
|
144865
|
+
const evidence = store.listEvidence(current.id);
|
|
144866
|
+
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
144867
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144868
|
+
const next = {
|
|
144869
|
+
...current,
|
|
144870
|
+
state: input.next,
|
|
144871
|
+
updatedAt: now3,
|
|
144872
|
+
lastValidatedAt: now3,
|
|
144873
|
+
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
144874
|
+
};
|
|
144875
|
+
store.update(next);
|
|
144876
|
+
store.recordEvent({
|
|
144877
|
+
memoryId: next.id,
|
|
144878
|
+
kind: input.event,
|
|
144879
|
+
fromState: current.state,
|
|
144880
|
+
toState: next.state,
|
|
144881
|
+
detail: transitionDetail(input.reason),
|
|
144882
|
+
createdAt: now3
|
|
144883
|
+
});
|
|
144884
|
+
return next;
|
|
144885
|
+
});
|
|
144886
|
+
}
|
|
144887
|
+
async function qualifyLongTermMemory(input) {
|
|
144888
|
+
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
144889
|
+
}
|
|
144890
|
+
async function approveLongTermMemory(input) {
|
|
144891
|
+
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
144892
|
+
}
|
|
144893
|
+
async function archiveLongTermMemory(input) {
|
|
144894
|
+
const store = await createStore(input.dataDir);
|
|
144895
|
+
return store.transaction(() => {
|
|
144896
|
+
const current = store.get(input.id);
|
|
144897
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144898
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
144899
|
+
throw new Error("Long-term memory is already inactive.");
|
|
144900
|
+
}
|
|
144901
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144902
|
+
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
144903
|
+
store.update(next);
|
|
144904
|
+
store.recordEvent({
|
|
144905
|
+
memoryId: next.id,
|
|
144906
|
+
kind: "archived",
|
|
144907
|
+
fromState: current.state,
|
|
144908
|
+
toState: "archived",
|
|
144909
|
+
detail: transitionDetail(input.reason),
|
|
144910
|
+
createdAt: now3
|
|
144911
|
+
});
|
|
144912
|
+
return next;
|
|
144913
|
+
});
|
|
144914
|
+
}
|
|
144915
|
+
async function supersedeLongTermMemory(input) {
|
|
144916
|
+
const store = await createStore(input.dataDir);
|
|
144917
|
+
return store.transaction(() => {
|
|
144918
|
+
const current = store.get(input.id);
|
|
144919
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144920
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
144921
|
+
throw new Error("Long-term memory is already inactive.");
|
|
144922
|
+
}
|
|
144923
|
+
if (current.id === input.supersededBy) {
|
|
144924
|
+
throw new Error("Long-term memory cannot supersede itself.");
|
|
144925
|
+
}
|
|
144926
|
+
const replacement = store.get(input.supersededBy);
|
|
144927
|
+
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
144928
|
+
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
144929
|
+
}
|
|
144930
|
+
if (replacement.scope !== current.scope) {
|
|
144931
|
+
throw new Error("Replacement long-term memory must use the same scope.");
|
|
144932
|
+
}
|
|
144933
|
+
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
144934
|
+
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
144935
|
+
}
|
|
144936
|
+
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
144937
|
+
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
144938
|
+
}
|
|
144939
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144940
|
+
const next = {
|
|
144941
|
+
...current,
|
|
144942
|
+
state: "superseded",
|
|
144943
|
+
supersededBy: replacement.id,
|
|
144944
|
+
updatedAt: now3
|
|
144945
|
+
};
|
|
144946
|
+
store.update(next);
|
|
144947
|
+
store.recordEvent({
|
|
144948
|
+
memoryId: next.id,
|
|
144949
|
+
kind: "superseded",
|
|
144950
|
+
fromState: current.state,
|
|
144951
|
+
toState: "superseded",
|
|
144952
|
+
detail: transitionDetail(input.reason),
|
|
144953
|
+
createdAt: now3
|
|
144954
|
+
});
|
|
144955
|
+
return next;
|
|
144956
|
+
});
|
|
144957
|
+
}
|
|
144958
|
+
async function maybeAutoQualifyLongTermMemory(input) {
|
|
144959
|
+
if (input.source !== "manual" && input.sourceDetail !== "explicit") return null;
|
|
144960
|
+
try {
|
|
144961
|
+
return await qualifyLongTermMemory({
|
|
144962
|
+
dataDir: input.dataDir,
|
|
144963
|
+
id: input.id,
|
|
144964
|
+
reason: "auto-qualified: explicit request carries its own source evidence"
|
|
144965
|
+
});
|
|
144966
|
+
} catch {
|
|
144967
|
+
return null;
|
|
144968
|
+
}
|
|
144969
|
+
}
|
|
144970
|
+
async function maintainLongTermMemories(input) {
|
|
144971
|
+
const now3 = input.now ?? /* @__PURE__ */ new Date();
|
|
144972
|
+
const decayDays = input.decayDays ?? 30;
|
|
144973
|
+
const cutoff = now3.getTime() - decayDays * 24 * 60 * 60 * 1e3;
|
|
144974
|
+
const result = { archived: 0, superseded: 0 };
|
|
144975
|
+
const store = await createStore(input.dataDir);
|
|
144976
|
+
const all = store.list({ originProjectId: input.projectId });
|
|
144977
|
+
for (const memory of all) {
|
|
144978
|
+
if (memory.state !== "qualified" && memory.state !== "approved") continue;
|
|
144979
|
+
const lastActivity = Date.parse(memory.lastAccessedAt ?? memory.updatedAt);
|
|
144980
|
+
if (!Number.isFinite(lastActivity) || lastActivity >= cutoff) continue;
|
|
144981
|
+
try {
|
|
144982
|
+
await archiveLongTermMemory({
|
|
144983
|
+
dataDir: input.dataDir,
|
|
144984
|
+
id: memory.id,
|
|
144985
|
+
reason: "auto-archive: no activity since " + new Date(lastActivity).toISOString().slice(0, 10)
|
|
144986
|
+
});
|
|
144987
|
+
result.archived += 1;
|
|
144988
|
+
} catch {
|
|
144989
|
+
}
|
|
144990
|
+
}
|
|
144991
|
+
const groups = /* @__PURE__ */ new Map();
|
|
144992
|
+
for (const memory of store.list({ originProjectId: input.projectId })) {
|
|
144993
|
+
if (memory.state !== "qualified" && memory.state !== "approved") continue;
|
|
144994
|
+
const key = memory.scope + "\0" + memory.kind + "\0" + memory.title.trim().toLowerCase();
|
|
144995
|
+
const group = groups.get(key) ?? [];
|
|
144996
|
+
group.push(memory);
|
|
144997
|
+
groups.set(key, group);
|
|
144998
|
+
}
|
|
144999
|
+
for (const group of groups.values()) {
|
|
145000
|
+
if (group.length < 2) continue;
|
|
145001
|
+
group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
|
|
145002
|
+
const newest = group[0];
|
|
145003
|
+
for (const older of group.slice(1)) {
|
|
145004
|
+
try {
|
|
145005
|
+
await supersedeLongTermMemory({
|
|
145006
|
+
dataDir: input.dataDir,
|
|
145007
|
+
id: older.id,
|
|
145008
|
+
supersededBy: newest.id,
|
|
145009
|
+
reason: "auto-supersede: a newer record shares the same title"
|
|
145010
|
+
});
|
|
145011
|
+
result.superseded += 1;
|
|
145012
|
+
} catch {
|
|
145013
|
+
}
|
|
145014
|
+
}
|
|
145015
|
+
}
|
|
145016
|
+
return result;
|
|
145017
|
+
}
|
|
145018
|
+
async function listLongTermMemories(input) {
|
|
145019
|
+
const store = await createStore(input.dataDir);
|
|
145020
|
+
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) }));
|
|
145021
|
+
}
|
|
145022
|
+
async function getLongTermMemoryDetail(input) {
|
|
145023
|
+
const store = await createStore(input.dataDir);
|
|
145024
|
+
const memory = store.get(input.id);
|
|
145025
|
+
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
145026
|
+
throw new Error("Long-term memory was not found in the active scope.");
|
|
145027
|
+
}
|
|
145028
|
+
store.markAccess([memory.id]);
|
|
145029
|
+
return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
|
|
145030
|
+
}
|
|
145031
|
+
async function selectLongTermMemoriesForTask(input) {
|
|
145032
|
+
const task = input.task?.trim();
|
|
145033
|
+
if (!task) return [];
|
|
145034
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
145035
|
+
const reader = {
|
|
145036
|
+
projectId: input.projectId,
|
|
145037
|
+
...owner ? { ownerId: owner.id } : {},
|
|
145038
|
+
...input.agentId ? { agentId: input.agentId } : {},
|
|
145039
|
+
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
145040
|
+
};
|
|
145041
|
+
const store = await createStore(input.dataDir);
|
|
145042
|
+
const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
145043
|
+
let selected = eligible.map((memory) => {
|
|
145044
|
+
const ranked = scoreForTask(memory, task);
|
|
145045
|
+
return ranked ? {
|
|
145046
|
+
memory,
|
|
145047
|
+
evidence: store.listEvidence(memory.id),
|
|
145048
|
+
score: ranked.score,
|
|
145049
|
+
reason: ranked.reason
|
|
145050
|
+
} : void 0;
|
|
145051
|
+
}).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));
|
|
145052
|
+
if (selected.length === 0 && eligible.length > 0) {
|
|
145053
|
+
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
145054
|
+
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
145055
|
+
if (provider2 && candidates.length > 0) {
|
|
145056
|
+
try {
|
|
145057
|
+
const vectors = await withTimeout(
|
|
145058
|
+
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
145059
|
+
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
145060
|
+
retry: false
|
|
145061
|
+
}),
|
|
145062
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
145063
|
+
"Long-term semantic retrieval"
|
|
145064
|
+
);
|
|
145065
|
+
const query = vectors[0];
|
|
145066
|
+
if (Array.isArray(query)) {
|
|
145067
|
+
selected = candidates.map((memory, index) => {
|
|
145068
|
+
const vector = vectors[index + 1];
|
|
145069
|
+
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
145070
|
+
return ranked ? {
|
|
145071
|
+
memory,
|
|
145072
|
+
evidence: store.listEvidence(memory.id),
|
|
145073
|
+
score: ranked.score,
|
|
145074
|
+
reason: ranked.reason
|
|
145075
|
+
} : void 0;
|
|
145076
|
+
}).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));
|
|
145077
|
+
}
|
|
145078
|
+
} catch {
|
|
145079
|
+
}
|
|
145080
|
+
}
|
|
145081
|
+
}
|
|
145082
|
+
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
145083
|
+
store.markAccess(selected.map((item) => item.memory.id));
|
|
145084
|
+
return selected;
|
|
145085
|
+
}
|
|
145086
|
+
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
145087
|
+
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
145088
|
+
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
145089
|
+
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
145090
|
+
}
|
|
145091
|
+
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
145092
|
+
var init_long_term = __esm({
|
|
145093
|
+
"src/memory/long-term.ts"() {
|
|
145094
|
+
"use strict";
|
|
145095
|
+
init_esm_shims();
|
|
145096
|
+
init_token_budget();
|
|
145097
|
+
init_timeout();
|
|
145098
|
+
init_secret_filter();
|
|
145099
|
+
init_owner();
|
|
145100
|
+
init_long_term_store();
|
|
145101
|
+
init_long_term_types();
|
|
145102
|
+
MAX_TEXT_LENGTH = 12e3;
|
|
145103
|
+
MAX_LIST_ITEMS = 40;
|
|
145104
|
+
MAX_SEMANTIC_CANDIDATES = 48;
|
|
145105
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
145106
|
+
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
145107
|
+
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
145108
|
+
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
145109
|
+
"the",
|
|
145110
|
+
"and",
|
|
145111
|
+
"for",
|
|
145112
|
+
"with",
|
|
145113
|
+
"from",
|
|
145114
|
+
"that",
|
|
145115
|
+
"this",
|
|
145116
|
+
"into",
|
|
145117
|
+
"when",
|
|
145118
|
+
"what",
|
|
145119
|
+
"\u5982\u4F55",
|
|
145120
|
+
"\u4EC0\u4E48",
|
|
145121
|
+
"\u8FD9\u4E2A",
|
|
145122
|
+
"\u90A3\u4E2A",
|
|
145123
|
+
"\u6211\u4EEC",
|
|
145124
|
+
"\u9879\u76EE",
|
|
145125
|
+
"\u8FDB\u884C",
|
|
145126
|
+
"\u9700\u8981",
|
|
145127
|
+
"\u7EE7\u7EED",
|
|
145128
|
+
"\u5904\u7406"
|
|
145129
|
+
]);
|
|
145130
|
+
}
|
|
145131
|
+
});
|
|
145132
|
+
|
|
144223
145133
|
// src/codegraph/ids.ts
|
|
144224
145134
|
import { createHash as createHash5 } from "crypto";
|
|
144225
145135
|
function digest(input) {
|
|
@@ -144258,7 +145168,7 @@ var store_exports = {};
|
|
|
144258
145168
|
__export(store_exports, {
|
|
144259
145169
|
CodeGraphStore: () => CodeGraphStore
|
|
144260
145170
|
});
|
|
144261
|
-
import { randomUUID as
|
|
145171
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
144262
145172
|
import { posix as pathPosix } from "path";
|
|
144263
145173
|
function rowToFile(row) {
|
|
144264
145174
|
return {
|
|
@@ -144975,7 +145885,7 @@ var init_store = __esm({
|
|
|
144975
145885
|
* so an interrupted scan cannot be advertised as complete.
|
|
144976
145886
|
*/
|
|
144977
145887
|
recordCodeStateSnapshot(input) {
|
|
144978
|
-
const id =
|
|
145888
|
+
const id = randomUUID9();
|
|
144979
145889
|
let snapshot;
|
|
144980
145890
|
const tx = this.db.transaction(() => {
|
|
144981
145891
|
const previous = this.db.prepare(
|
|
@@ -145753,8 +146663,8 @@ var claim_store_exports = {};
|
|
|
145753
146663
|
__export(claim_store_exports, {
|
|
145754
146664
|
ClaimStore: () => ClaimStore
|
|
145755
146665
|
});
|
|
145756
|
-
import { createHash as createHash8, randomUUID as
|
|
145757
|
-
function
|
|
146666
|
+
import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
|
|
146667
|
+
function optionalText3(value) {
|
|
145758
146668
|
return typeof value === "string" && value ? value : void 0;
|
|
145759
146669
|
}
|
|
145760
146670
|
function rowToClaim(row) {
|
|
@@ -145770,38 +146680,38 @@ function rowToClaim(row) {
|
|
|
145770
146680
|
status: row.status,
|
|
145771
146681
|
confidence: Number(row.confidence),
|
|
145772
146682
|
observedAt: row.observedAt,
|
|
145773
|
-
...
|
|
145774
|
-
...
|
|
145775
|
-
...
|
|
146683
|
+
...optionalText3(row.validFrom) ? { validFrom: row.validFrom } : {},
|
|
146684
|
+
...optionalText3(row.validTo) ? { validTo: row.validTo } : {},
|
|
146685
|
+
...optionalText3(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
145776
146686
|
reviewState: row.reviewState,
|
|
145777
146687
|
origin: row.origin,
|
|
145778
146688
|
createdAt: row.createdAt,
|
|
145779
146689
|
updatedAt: row.updatedAt
|
|
145780
146690
|
};
|
|
145781
146691
|
}
|
|
145782
|
-
function
|
|
146692
|
+
function rowToEvidence2(row) {
|
|
145783
146693
|
return {
|
|
145784
146694
|
id: row.id,
|
|
145785
146695
|
claimId: row.claimId,
|
|
145786
146696
|
evidenceKind: row.evidenceKind,
|
|
145787
146697
|
evidenceId: row.evidenceId,
|
|
145788
146698
|
relation: row.relation,
|
|
145789
|
-
...
|
|
145790
|
-
...
|
|
145791
|
-
...
|
|
146699
|
+
...optionalText3(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
|
|
146700
|
+
...optionalText3(row.locator) ? { locator: row.locator } : {},
|
|
146701
|
+
...optionalText3(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
145792
146702
|
createdAt: row.createdAt
|
|
145793
146703
|
};
|
|
145794
146704
|
}
|
|
145795
|
-
function
|
|
146705
|
+
function rowToEvent2(row) {
|
|
145796
146706
|
return {
|
|
145797
146707
|
id: row.id,
|
|
145798
146708
|
projectId: row.projectId,
|
|
145799
146709
|
claimId: row.claimId,
|
|
145800
146710
|
kind: row.kind,
|
|
145801
|
-
...
|
|
145802
|
-
...
|
|
145803
|
-
...
|
|
145804
|
-
...
|
|
146711
|
+
...optionalText3(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
|
|
146712
|
+
...optionalText3(row.toStatus) ? { toStatus: row.toStatus } : {},
|
|
146713
|
+
...optionalText3(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
|
|
146714
|
+
...optionalText3(row.detail) ? { detail: row.detail } : {},
|
|
145805
146715
|
createdAt: row.createdAt
|
|
145806
146716
|
};
|
|
145807
146717
|
}
|
|
@@ -145937,7 +146847,7 @@ var init_claim_store = __esm({
|
|
|
145937
146847
|
locator, capturedHash, evidenceKey, createdAt
|
|
145938
146848
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
145939
146849
|
`).run(
|
|
145940
|
-
|
|
146850
|
+
randomUUID10(),
|
|
145941
146851
|
input.claimId,
|
|
145942
146852
|
input.evidenceKind,
|
|
145943
146853
|
input.evidenceId,
|
|
@@ -145955,11 +146865,11 @@ var init_claim_store = __esm({
|
|
|
145955
146865
|
SELECT * FROM knowledge_claim_evidence
|
|
145956
146866
|
WHERE claimId = ?
|
|
145957
146867
|
ORDER BY createdAt ASC, id
|
|
145958
|
-
`).all(claimId).map(
|
|
146868
|
+
`).all(claimId).map(rowToEvidence2);
|
|
145959
146869
|
}
|
|
145960
146870
|
recordEvent(input) {
|
|
145961
146871
|
const event = {
|
|
145962
|
-
id:
|
|
146872
|
+
id: randomUUID10(),
|
|
145963
146873
|
...input,
|
|
145964
146874
|
...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
|
|
145965
146875
|
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -145987,7 +146897,7 @@ var init_claim_store = __esm({
|
|
|
145987
146897
|
SELECT * FROM knowledge_claim_events
|
|
145988
146898
|
WHERE claimId = ?
|
|
145989
146899
|
ORDER BY createdAt ASC, id
|
|
145990
|
-
`).all(claimId).map(
|
|
146900
|
+
`).all(claimId).map(rowToEvent2);
|
|
145991
146901
|
}
|
|
145992
146902
|
};
|
|
145993
146903
|
}
|
|
@@ -146004,8 +146914,8 @@ __export(claims_exports, {
|
|
|
146004
146914
|
supersedeClaim: () => supersedeClaim,
|
|
146005
146915
|
writeClaim: () => writeClaim
|
|
146006
146916
|
});
|
|
146007
|
-
import { createHash as createHash9, randomUUID as
|
|
146008
|
-
function
|
|
146917
|
+
import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
|
|
146918
|
+
function compactText2(value, field) {
|
|
146009
146919
|
if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
|
|
146010
146920
|
const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
|
|
146011
146921
|
if (!sanitized) throw new Error("Claim " + field + " is required");
|
|
@@ -146013,7 +146923,7 @@ function compactText(value, field) {
|
|
|
146013
146923
|
return sanitized;
|
|
146014
146924
|
}
|
|
146015
146925
|
function normalizedTerm(value) {
|
|
146016
|
-
return
|
|
146926
|
+
return compactText2(value, "identity").toLocaleLowerCase("en-US");
|
|
146017
146927
|
}
|
|
146018
146928
|
function hashIdentity(parts) {
|
|
146019
146929
|
return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
|
|
@@ -146042,7 +146952,7 @@ function validateEvidence(input) {
|
|
|
146042
146952
|
if (!evidence || typeof evidence !== "object") {
|
|
146043
146953
|
throw new Error("Claim evidence must be an object");
|
|
146044
146954
|
}
|
|
146045
|
-
const evidenceId =
|
|
146955
|
+
const evidenceId = compactText2(evidence.evidenceId, "evidence id");
|
|
146046
146956
|
if (evidenceId.length > MAX_EVIDENCE_ID_LENGTH) {
|
|
146047
146957
|
throw new Error("Claim evidence id is too long");
|
|
146048
146958
|
}
|
|
@@ -146056,9 +146966,9 @@ function validateEvidence(input) {
|
|
|
146056
146966
|
evidenceKind: evidence.evidenceKind,
|
|
146057
146967
|
evidenceId,
|
|
146058
146968
|
relation: evidence.relation,
|
|
146059
|
-
...evidence.snapshotId ? { snapshotId:
|
|
146060
|
-
...evidence.locator ? { locator:
|
|
146061
|
-
...evidence.capturedHash ? { capturedHash:
|
|
146969
|
+
...evidence.snapshotId ? { snapshotId: compactText2(evidence.snapshotId, "evidence snapshot id") } : {},
|
|
146970
|
+
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator") } : {},
|
|
146971
|
+
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash") } : {}
|
|
146062
146972
|
};
|
|
146063
146973
|
});
|
|
146064
146974
|
}
|
|
@@ -146066,10 +146976,10 @@ function normalizeClaimInput(input) {
|
|
|
146066
146976
|
if (!["project", "workspace", "team", "workflow", "task"].includes(input.scope)) {
|
|
146067
146977
|
throw new Error("Claim scope is invalid");
|
|
146068
146978
|
}
|
|
146069
|
-
const projectId =
|
|
146070
|
-
const subject =
|
|
146071
|
-
const predicate =
|
|
146072
|
-
const objectValue =
|
|
146979
|
+
const projectId = compactText2(input.projectId, "project id");
|
|
146980
|
+
const subject = compactText2(input.subject, "subject");
|
|
146981
|
+
const predicate = compactText2(input.predicate, "predicate");
|
|
146982
|
+
const objectValue = compactText2(input.objectValue, "object value");
|
|
146073
146983
|
const identity = buildClaimIdentity({ subject, predicate, objectValue, scope: input.scope });
|
|
146074
146984
|
const origin = input.origin ?? "explicit";
|
|
146075
146985
|
let reviewState = input.reviewState ?? "approved";
|
|
@@ -146090,7 +147000,7 @@ function normalizeClaimInput(input) {
|
|
|
146090
147000
|
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
146091
147001
|
return {
|
|
146092
147002
|
claim: {
|
|
146093
|
-
id:
|
|
147003
|
+
id: randomUUID11(),
|
|
146094
147004
|
projectId,
|
|
146095
147005
|
subject,
|
|
146096
147006
|
predicate,
|
|
@@ -146190,7 +147100,7 @@ function reviewClaim(store, input) {
|
|
|
146190
147100
|
if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
|
|
146191
147101
|
throw new Error("A claim review must approve or reject the claim");
|
|
146192
147102
|
}
|
|
146193
|
-
const detail =
|
|
147103
|
+
const detail = compactText2(input.detail, "review detail");
|
|
146194
147104
|
return store.transaction(() => {
|
|
146195
147105
|
const claim = store.getClaim(input.claimId);
|
|
146196
147106
|
if (!claim) throw new Error("Claim was not found");
|
|
@@ -146502,8 +147412,8 @@ var init_frontmatter = __esm({
|
|
|
146502
147412
|
|
|
146503
147413
|
// src/knowledge/markdown.ts
|
|
146504
147414
|
import { createHash as createHash10 } from "crypto";
|
|
146505
|
-
import { promises as
|
|
146506
|
-
import
|
|
147415
|
+
import { promises as fs13 } from "fs";
|
|
147416
|
+
import path20 from "path";
|
|
146507
147417
|
function hashContent(content) {
|
|
146508
147418
|
return createHash10("sha256").update(content).digest("hex");
|
|
146509
147419
|
}
|
|
@@ -146561,8 +147471,8 @@ function extractInternalMarkdownLinks(body) {
|
|
|
146561
147471
|
return [...links].sort();
|
|
146562
147472
|
}
|
|
146563
147473
|
function resolvePageLink(sourceRelativePath, target) {
|
|
146564
|
-
const sourceDir =
|
|
146565
|
-
const normalized =
|
|
147474
|
+
const sourceDir = path20.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
|
|
147475
|
+
const normalized = path20.posix.normalize(path20.posix.join(sourceDir, target.replace(/\\/g, "/")));
|
|
146566
147476
|
if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
|
|
146567
147477
|
return normalized;
|
|
146568
147478
|
}
|
|
@@ -146584,13 +147494,13 @@ function renderKnowledgePage(frontmatter, body) {
|
|
|
146584
147494
|
});
|
|
146585
147495
|
}
|
|
146586
147496
|
async function readKnowledgePage(absolutePath, workspaceRoot) {
|
|
146587
|
-
const raw = await
|
|
147497
|
+
const raw = await fs13.readFile(absolutePath, "utf8");
|
|
146588
147498
|
try {
|
|
146589
147499
|
const parsed = frontmatter_default(raw);
|
|
146590
147500
|
const frontmatter = validateKnowledgePageFrontmatter(parsed.data);
|
|
146591
147501
|
return {
|
|
146592
147502
|
absolutePath,
|
|
146593
|
-
relativePath: workspaceRoot ?
|
|
147503
|
+
relativePath: workspaceRoot ? path20.relative(workspaceRoot, absolutePath).split(path20.sep).join("/") : path20.basename(absolutePath),
|
|
146594
147504
|
frontmatter,
|
|
146595
147505
|
body: parsed.content.trim(),
|
|
146596
147506
|
contentHash: hashContent(raw),
|
|
@@ -146602,7 +147512,7 @@ async function readKnowledgePage(absolutePath, workspaceRoot) {
|
|
|
146602
147512
|
}
|
|
146603
147513
|
}
|
|
146604
147514
|
async function writeKnowledgePage(absolutePath, content) {
|
|
146605
|
-
await
|
|
147515
|
+
await fs13.mkdir(path20.dirname(absolutePath), { recursive: true });
|
|
146606
147516
|
await atomicWriteFile(absolutePath, content);
|
|
146607
147517
|
return hashContent(content);
|
|
146608
147518
|
}
|
|
@@ -146632,8 +147542,8 @@ __export(wiki_exports, {
|
|
|
146632
147542
|
readKnowledgePage: () => readKnowledgePage
|
|
146633
147543
|
});
|
|
146634
147544
|
import { createHash as createHash11 } from "crypto";
|
|
146635
|
-
import { promises as
|
|
146636
|
-
import
|
|
147545
|
+
import { promises as fs14 } from "fs";
|
|
147546
|
+
import path21 from "path";
|
|
146637
147547
|
function hash2(value) {
|
|
146638
147548
|
return createHash11("sha256").update(value).digest("hex");
|
|
146639
147549
|
}
|
|
@@ -146765,7 +147675,7 @@ function candidatePage(workspace, title, claims, store) {
|
|
|
146765
147675
|
}
|
|
146766
147676
|
async function fileHashIfPresent(filePath) {
|
|
146767
147677
|
try {
|
|
146768
|
-
return pageContentHash(await
|
|
147678
|
+
return pageContentHash(await fs14.readFile(filePath, "utf8"));
|
|
146769
147679
|
} catch (error) {
|
|
146770
147680
|
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
146771
147681
|
if (code === "ENOENT") return void 0;
|
|
@@ -146789,8 +147699,8 @@ async function writeIndex(workspace, store) {
|
|
|
146789
147699
|
lines.push("No pending proposals.");
|
|
146790
147700
|
} else {
|
|
146791
147701
|
for (const proposal of pending) {
|
|
146792
|
-
const relative3 =
|
|
146793
|
-
lines.push("- [" +
|
|
147702
|
+
const relative3 = path21.relative(paths.root, proposal.proposalPath).split(path21.sep).join("/");
|
|
147703
|
+
lines.push("- [" + path21.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
|
|
146794
147704
|
}
|
|
146795
147705
|
}
|
|
146796
147706
|
lines.push("");
|
|
@@ -146798,7 +147708,7 @@ async function writeIndex(workspace, store) {
|
|
|
146798
147708
|
}
|
|
146799
147709
|
async function appendLog(workspace, line) {
|
|
146800
147710
|
const logPath = getKnowledgeWorkspacePaths(workspace).log;
|
|
146801
|
-
await
|
|
147711
|
+
await fs14.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
|
|
146802
147712
|
}
|
|
146803
147713
|
function toPageRecord(workspace, page, status, contentHash, manualContentHash) {
|
|
146804
147714
|
return {
|
|
@@ -146883,9 +147793,9 @@ async function applyKnowledgeProposal(input) {
|
|
|
146883
147793
|
}
|
|
146884
147794
|
if (proposal.status !== "pending") throw new Error("Knowledge proposal is not pending review");
|
|
146885
147795
|
const root = getKnowledgeWorkspacePaths(input.workspace).root;
|
|
146886
|
-
const targetPath =
|
|
146887
|
-
const proposalPath =
|
|
146888
|
-
if (!targetPath.startsWith(root +
|
|
147796
|
+
const targetPath = path21.resolve(proposal.targetPath);
|
|
147797
|
+
const proposalPath = path21.resolve(proposal.proposalPath);
|
|
147798
|
+
if (!targetPath.startsWith(root + path21.sep) || !proposalPath.startsWith(root + path21.sep)) {
|
|
146889
147799
|
throw new Error("Knowledge proposal path escapes its workspace");
|
|
146890
147800
|
}
|
|
146891
147801
|
await withFileLock(root, async () => {
|
|
@@ -146923,12 +147833,12 @@ async function markdownFiles(directory) {
|
|
|
146923
147833
|
const files = [];
|
|
146924
147834
|
let entries;
|
|
146925
147835
|
try {
|
|
146926
|
-
entries = await
|
|
147836
|
+
entries = await fs14.readdir(directory, { withFileTypes: true });
|
|
146927
147837
|
} catch {
|
|
146928
147838
|
return files;
|
|
146929
147839
|
}
|
|
146930
147840
|
for (const entry of entries) {
|
|
146931
|
-
const filePath =
|
|
147841
|
+
const filePath = path21.join(directory, entry.name);
|
|
146932
147842
|
if (entry.isDirectory()) {
|
|
146933
147843
|
files.push(...await markdownFiles(filePath));
|
|
146934
147844
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
@@ -146951,13 +147861,13 @@ async function lintKnowledgeWorkspace(input) {
|
|
|
146951
147861
|
kind: "malformed-frontmatter",
|
|
146952
147862
|
severity: "error",
|
|
146953
147863
|
message: error instanceof Error ? error.message : "Malformed knowledge page.",
|
|
146954
|
-
relativePath:
|
|
147864
|
+
relativePath: path21.relative(paths.root, filePath).split(path21.sep).join("/")
|
|
146955
147865
|
});
|
|
146956
147866
|
}
|
|
146957
147867
|
}
|
|
146958
147868
|
const indexed = /* @__PURE__ */ new Set();
|
|
146959
147869
|
try {
|
|
146960
|
-
const index = await
|
|
147870
|
+
const index = await fs14.readFile(paths.index, "utf8");
|
|
146961
147871
|
for (const link of extractInternalMarkdownLinks(index)) {
|
|
146962
147872
|
const resolved = resolvePageLink("index.md", link);
|
|
146963
147873
|
if (resolved) indexed.add(resolved);
|
|
@@ -147046,7 +147956,7 @@ async function lintKnowledgeWorkspace(input) {
|
|
|
147046
147956
|
kind: "manual-edit-protected",
|
|
147047
147957
|
severity: "warning",
|
|
147048
147958
|
message: "A newer proposal was held because the target page has manual edits.",
|
|
147049
|
-
relativePath:
|
|
147959
|
+
relativePath: path21.relative(paths.root, proposal.targetPath).split(path21.sep).join("/")
|
|
147050
147960
|
});
|
|
147051
147961
|
}
|
|
147052
147962
|
}
|
|
@@ -147179,7 +148089,7 @@ var workflow_store_exports = {};
|
|
|
147179
148089
|
__export(workflow_store_exports, {
|
|
147180
148090
|
WorkflowStore: () => WorkflowStore
|
|
147181
148091
|
});
|
|
147182
|
-
import { randomUUID as
|
|
148092
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
147183
148093
|
function parseJson(value, fallback) {
|
|
147184
148094
|
if (typeof value !== "string" || !value) return fallback;
|
|
147185
148095
|
try {
|
|
@@ -147188,7 +148098,7 @@ function parseJson(value, fallback) {
|
|
|
147188
148098
|
return fallback;
|
|
147189
148099
|
}
|
|
147190
148100
|
}
|
|
147191
|
-
function
|
|
148101
|
+
function optionalText4(value) {
|
|
147192
148102
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
147193
148103
|
}
|
|
147194
148104
|
function rowToWorkflow(row) {
|
|
@@ -147225,14 +148135,14 @@ function rowToRun(row) {
|
|
|
147225
148135
|
workflowId: row.workflowId,
|
|
147226
148136
|
projectId: row.projectId,
|
|
147227
148137
|
task: row.task,
|
|
147228
|
-
...
|
|
148138
|
+
...optionalText4(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
|
|
147229
148139
|
selectedEvidence: parseJson(row.selectedEvidenceJson, []),
|
|
147230
148140
|
phaseState: parseJson(row.phaseStateJson, {}),
|
|
147231
148141
|
outcome: row.outcome,
|
|
147232
148142
|
verificationVerdict: row.verificationVerdict,
|
|
147233
|
-
...
|
|
148143
|
+
...optionalText4(row.failureReason) ? { failureReason: row.failureReason } : {},
|
|
147234
148144
|
startedAt: row.startedAt,
|
|
147235
|
-
...
|
|
148145
|
+
...optionalText4(row.completedAt) ? { completedAt: row.completedAt } : {}
|
|
147236
148146
|
};
|
|
147237
148147
|
}
|
|
147238
148148
|
var WorkflowStore;
|
|
@@ -147279,7 +148189,7 @@ var init_workflow_store = __esm({
|
|
|
147279
148189
|
}
|
|
147280
148190
|
recordRun(input) {
|
|
147281
148191
|
const run = {
|
|
147282
|
-
id:
|
|
148192
|
+
id: randomUUID12(),
|
|
147283
148193
|
workflowId: input.workflowId,
|
|
147284
148194
|
projectId: input.projectId,
|
|
147285
148195
|
task: input.task.trim(),
|
|
@@ -147357,8 +148267,8 @@ var init_outcome_types = __esm({
|
|
|
147357
148267
|
});
|
|
147358
148268
|
|
|
147359
148269
|
// src/knowledge/outcome-store.ts
|
|
147360
|
-
import { randomUUID as
|
|
147361
|
-
function
|
|
148270
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
148271
|
+
function optionalText5(value) {
|
|
147362
148272
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
147363
148273
|
}
|
|
147364
148274
|
function text(value, field, max = 1e3) {
|
|
@@ -147381,8 +148291,8 @@ function rowToSignal(row) {
|
|
|
147381
148291
|
candidateId: row.candidateId,
|
|
147382
148292
|
kind: row.kind,
|
|
147383
148293
|
sourceRef: row.sourceRef,
|
|
147384
|
-
...
|
|
147385
|
-
...
|
|
148294
|
+
...optionalText5(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
|
|
148295
|
+
...optionalText5(row.detail) ? { detail: row.detail } : {},
|
|
147386
148296
|
observedAt: row.observedAt
|
|
147387
148297
|
};
|
|
147388
148298
|
}
|
|
@@ -147401,7 +148311,7 @@ var init_outcome_store = __esm({
|
|
|
147401
148311
|
}
|
|
147402
148312
|
record(input) {
|
|
147403
148313
|
const signal = {
|
|
147404
|
-
id:
|
|
148314
|
+
id: randomUUID13(),
|
|
147405
148315
|
projectId: text(input.projectId, "project id"),
|
|
147406
148316
|
candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
|
|
147407
148317
|
candidateId: text(input.candidateId, "candidate id"),
|
|
@@ -147828,8 +148738,8 @@ __export(workflows_exports, {
|
|
|
147828
148738
|
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
147829
148739
|
});
|
|
147830
148740
|
import { createHash as createHash12 } from "crypto";
|
|
147831
|
-
import { promises as
|
|
147832
|
-
import
|
|
148741
|
+
import { promises as fs15 } from "fs";
|
|
148742
|
+
import path22 from "path";
|
|
147833
148743
|
function hash3(value) {
|
|
147834
148744
|
return createHash12("sha256").update(value).digest("hex");
|
|
147835
148745
|
}
|
|
@@ -147850,7 +148760,7 @@ function requiredText(data, key) {
|
|
|
147850
148760
|
}
|
|
147851
148761
|
return value.trim();
|
|
147852
148762
|
}
|
|
147853
|
-
function
|
|
148763
|
+
function optionalText6(data, key) {
|
|
147854
148764
|
const value = data[key];
|
|
147855
148765
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
147856
148766
|
if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
|
|
@@ -147893,11 +148803,11 @@ function phaseFromValue(value, index) {
|
|
|
147893
148803
|
}
|
|
147894
148804
|
const data = value;
|
|
147895
148805
|
const title = requiredText(data, "title");
|
|
147896
|
-
const phaseId =
|
|
148806
|
+
const phaseId = optionalText6(data, "id") ?? slug(title) + "-" + (index + 1);
|
|
147897
148807
|
return {
|
|
147898
148808
|
id: phaseId,
|
|
147899
148809
|
title,
|
|
147900
|
-
instructions:
|
|
148810
|
+
instructions: optionalText6(data, "instructions") ?? "",
|
|
147901
148811
|
branches: optionalStringArray(data, "branches"),
|
|
147902
148812
|
expectedOutputs: optionalStringArray(data, "expectedOutputs"),
|
|
147903
148813
|
verificationGates: optionalStringArray(data, "verificationGates")
|
|
@@ -147932,7 +148842,7 @@ function phasesFromBody(body) {
|
|
|
147932
148842
|
}
|
|
147933
148843
|
function normalizeSourcePath(sourcePath) {
|
|
147934
148844
|
const normalized = sourcePath.replace(/\\/g, "/");
|
|
147935
|
-
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") ||
|
|
148845
|
+
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path22.posix.normalize(normalized) !== normalized) {
|
|
147936
148846
|
throw new Error("workflow source path must be a safe workflows/*.md path");
|
|
147937
148847
|
}
|
|
147938
148848
|
return normalized;
|
|
@@ -148006,7 +148916,7 @@ function parsedWorkflow(data, body, input) {
|
|
|
148006
148916
|
id: requiredText(data, "id"),
|
|
148007
148917
|
workspaceId: input.workspaceId,
|
|
148008
148918
|
title: requiredText(data, "title"),
|
|
148009
|
-
description:
|
|
148919
|
+
description: optionalText6(data, "description") ?? "",
|
|
148010
148920
|
status: optionalStatus(data),
|
|
148011
148921
|
version: optionalNumber(data, "version", 1),
|
|
148012
148922
|
taskLenses: optionalStringArray(data, "taskLenses"),
|
|
@@ -148025,9 +148935,9 @@ function parsedWorkflow(data, body, input) {
|
|
|
148025
148935
|
sourcePath: normalizeSourcePath(input.sourcePath),
|
|
148026
148936
|
sourceHash: "",
|
|
148027
148937
|
contentHash: input.contentHash,
|
|
148028
|
-
createdAt:
|
|
148029
|
-
updatedAt:
|
|
148030
|
-
...
|
|
148938
|
+
createdAt: optionalText6(data, "createdAt") ?? now2(),
|
|
148939
|
+
updatedAt: optionalText6(data, "updatedAt") ?? now2(),
|
|
148940
|
+
...optionalText6(data, "importedFrom") ? { importedFrom: optionalText6(data, "importedFrom") } : {}
|
|
148031
148941
|
};
|
|
148032
148942
|
if (candidate.phases.length === 0) {
|
|
148033
148943
|
throw new Error("workflow must contain at least one phase or a non-empty body");
|
|
@@ -148081,7 +148991,7 @@ async function writeCanonicalWorkflow(input) {
|
|
|
148081
148991
|
const filePath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
|
|
148082
148992
|
const content = renderWorkflowMarkdown(workflow);
|
|
148083
148993
|
await withFileLock(paths.root, async () => {
|
|
148084
|
-
await
|
|
148994
|
+
await fs15.mkdir(path22.dirname(filePath), { recursive: true });
|
|
148085
148995
|
await atomicWriteFile(filePath, content);
|
|
148086
148996
|
});
|
|
148087
148997
|
const store = await storeFor2(input.workspace);
|
|
@@ -148090,13 +149000,13 @@ async function writeCanonicalWorkflow(input) {
|
|
|
148090
149000
|
async function markdownFiles2(directory) {
|
|
148091
149001
|
let entries;
|
|
148092
149002
|
try {
|
|
148093
|
-
entries = await
|
|
149003
|
+
entries = await fs15.readdir(directory, { withFileTypes: true });
|
|
148094
149004
|
} catch {
|
|
148095
149005
|
return [];
|
|
148096
149006
|
}
|
|
148097
149007
|
const files = [];
|
|
148098
149008
|
for (const entry of entries) {
|
|
148099
|
-
const absolutePath =
|
|
149009
|
+
const absolutePath = path22.join(directory, entry.name);
|
|
148100
149010
|
if (entry.isDirectory()) {
|
|
148101
149011
|
files.push(...await markdownFiles2(absolutePath));
|
|
148102
149012
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
@@ -148111,9 +149021,9 @@ async function syncCanonicalWorkflows(workspace) {
|
|
|
148111
149021
|
const workflows = [];
|
|
148112
149022
|
const errors = [];
|
|
148113
149023
|
for (const absolutePath of await markdownFiles2(paths.workflows)) {
|
|
148114
|
-
const sourcePath =
|
|
149024
|
+
const sourcePath = path22.relative(paths.root, absolutePath).split(path22.sep).join("/");
|
|
148115
149025
|
try {
|
|
148116
|
-
const raw = await
|
|
149026
|
+
const raw = await fs15.readFile(absolutePath, "utf8");
|
|
148117
149027
|
const workflow = parseWorkflowMarkdown(raw, { workspaceId: workspace.id, sourcePath });
|
|
148118
149028
|
workflows.push(store.upsertWorkflow(workflow));
|
|
148119
149029
|
} catch (error) {
|
|
@@ -148225,21 +149135,21 @@ function importedWorkflowSpec(input) {
|
|
|
148225
149135
|
return materializeWorkflow(spec);
|
|
148226
149136
|
}
|
|
148227
149137
|
async function importWindsurfWorkflows(input) {
|
|
148228
|
-
const sourceDirectory =
|
|
149138
|
+
const sourceDirectory = path22.join(path22.resolve(input.projectRoot), ".windsurf", "workflows");
|
|
148229
149139
|
const paths = getKnowledgeWorkspacePaths(input.workspace);
|
|
148230
149140
|
const imported = [];
|
|
148231
149141
|
const skipped = [];
|
|
148232
149142
|
let entries;
|
|
148233
149143
|
try {
|
|
148234
|
-
entries = await
|
|
149144
|
+
entries = await fs15.readdir(sourceDirectory, { withFileTypes: true });
|
|
148235
149145
|
} catch {
|
|
148236
149146
|
return { imported, skipped };
|
|
148237
149147
|
}
|
|
148238
149148
|
for (const entry of entries.filter((item) => item.isFile() && item.name.toLowerCase().endsWith(".md"))) {
|
|
148239
|
-
const originalPath =
|
|
148240
|
-
const sourcePath =
|
|
149149
|
+
const originalPath = path22.join(sourceDirectory, entry.name);
|
|
149150
|
+
const sourcePath = path22.relative(path22.resolve(input.projectRoot), originalPath).split(path22.sep).join("/");
|
|
148241
149151
|
try {
|
|
148242
|
-
const raw = await
|
|
149152
|
+
const raw = await fs15.readFile(originalPath, "utf8");
|
|
148243
149153
|
const workflow = importedWorkflowSpec({
|
|
148244
149154
|
workspace: input.workspace,
|
|
148245
149155
|
sourceName: entry.name,
|
|
@@ -148248,7 +149158,7 @@ async function importWindsurfWorkflows(input) {
|
|
|
148248
149158
|
});
|
|
148249
149159
|
const targetPath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
|
|
148250
149160
|
try {
|
|
148251
|
-
await
|
|
149161
|
+
await fs15.access(targetPath);
|
|
148252
149162
|
skipped.push({ sourcePath, reason: "canonical workflow already exists and was preserved" });
|
|
148253
149163
|
continue;
|
|
148254
149164
|
} catch {
|
|
@@ -148261,12 +149171,12 @@ async function importWindsurfWorkflows(input) {
|
|
|
148261
149171
|
});
|
|
148262
149172
|
}
|
|
148263
149173
|
}
|
|
148264
|
-
await
|
|
149174
|
+
await fs15.mkdir(paths.workflows, { recursive: true });
|
|
148265
149175
|
return { imported, skipped };
|
|
148266
149176
|
}
|
|
148267
149177
|
function isWithin2(root, candidate) {
|
|
148268
|
-
const relative3 =
|
|
148269
|
-
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." +
|
|
149178
|
+
const relative3 = path22.relative(root, candidate);
|
|
149179
|
+
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path22.sep) && !path22.isAbsolute(relative3);
|
|
148270
149180
|
}
|
|
148271
149181
|
function adapterRelativePath(workflow, agent) {
|
|
148272
149182
|
const name = "memorix-workflow-" + slug(workflow.id.replace(/^workflow:/, ""));
|
|
@@ -148352,15 +149262,15 @@ async function previewWorkflowAdapter(input) {
|
|
|
148352
149262
|
reason: "The canonical workflow does not declare compatibility with this agent."
|
|
148353
149263
|
};
|
|
148354
149264
|
}
|
|
148355
|
-
const projectRoot =
|
|
148356
|
-
const targetPath =
|
|
149265
|
+
const projectRoot = path22.resolve(input.projectRoot);
|
|
149266
|
+
const targetPath = path22.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
|
|
148357
149267
|
if (!isWithin2(projectRoot, targetPath)) {
|
|
148358
149268
|
throw new Error("Workflow adapter target escapes the project root");
|
|
148359
149269
|
}
|
|
148360
149270
|
const content = adapterContent(input.workflow, input.agent);
|
|
148361
149271
|
let existing;
|
|
148362
149272
|
try {
|
|
148363
|
-
existing = await
|
|
149273
|
+
existing = await fs15.readFile(targetPath, "utf8");
|
|
148364
149274
|
} catch (error) {
|
|
148365
149275
|
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
148366
149276
|
if (code !== "ENOENT") throw error;
|
|
@@ -148381,11 +149291,11 @@ async function applyWorkflowAdapter(input) {
|
|
|
148381
149291
|
if (initial.status === "unsupported" || initial.status === "conflict" || initial.status === "unchanged") {
|
|
148382
149292
|
return initial;
|
|
148383
149293
|
}
|
|
148384
|
-
const projectRoot =
|
|
149294
|
+
const projectRoot = path22.resolve(input.projectRoot);
|
|
148385
149295
|
return withFileLock(projectRoot, async () => {
|
|
148386
149296
|
const current = await previewWorkflowAdapter(input);
|
|
148387
149297
|
if (current.status !== "create" && current.status !== "update") return current;
|
|
148388
|
-
await
|
|
149298
|
+
await fs15.mkdir(path22.dirname(current.targetPath), { recursive: true });
|
|
148389
149299
|
await atomicWriteFile(current.targetPath, current.content);
|
|
148390
149300
|
return current;
|
|
148391
149301
|
});
|
|
@@ -148675,6 +149585,11 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
148675
149585
|
}
|
|
148676
149586
|
return { action: "complete" };
|
|
148677
149587
|
}
|
|
149588
|
+
if (job.kind === "long-term-maintenance") {
|
|
149589
|
+
const { maintainLongTermMemories: maintainLongTermMemories2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
149590
|
+
await maintainLongTermMemories2({ dataDir: projectDir2, projectId });
|
|
149591
|
+
return { action: "reschedule", delayMs: 24 * 60 * 60 * 1e3, resetAttempts: true };
|
|
149592
|
+
}
|
|
148678
149593
|
if (job.kind === "consolidation") {
|
|
148679
149594
|
const limit2 = consolidationBatchSize(job.payload);
|
|
148680
149595
|
const result2 = await runAutomaticConsolidation(projectId, projectDir2, {
|
|
@@ -149016,15 +149931,15 @@ __export(vector_backfill_runner_exports, {
|
|
|
149016
149931
|
});
|
|
149017
149932
|
import { spawn as spawn4 } from "child_process";
|
|
149018
149933
|
import { existsSync as existsSync10 } from "fs";
|
|
149019
|
-
import
|
|
149934
|
+
import path23 from "path";
|
|
149020
149935
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
149021
149936
|
function isNonEmptyString(value) {
|
|
149022
149937
|
return typeof value === "string" && value.trim().length > 0;
|
|
149023
149938
|
}
|
|
149024
149939
|
function resolveVectorBackfillRunnerPath(moduleUrl = import.meta.url) {
|
|
149025
|
-
const moduleDir =
|
|
149026
|
-
const distDir =
|
|
149027
|
-
return
|
|
149940
|
+
const moduleDir = path23.dirname(fileURLToPath5(moduleUrl));
|
|
149941
|
+
const distDir = path23.basename(moduleDir) === "cli" ? path23.dirname(moduleDir) : path23.basename(moduleDir) === "runtime" && path23.basename(path23.dirname(moduleDir)) === "src" ? path23.join(path23.dirname(path23.dirname(moduleDir)), "dist") : moduleDir;
|
|
149942
|
+
return path23.join(distDir, "vector-backfill-runner.js");
|
|
149028
149943
|
}
|
|
149029
149944
|
function parseVectorBackfillRequest(raw) {
|
|
149030
149945
|
let value;
|
|
@@ -149033,7 +149948,7 @@ function parseVectorBackfillRequest(raw) {
|
|
|
149033
149948
|
} catch {
|
|
149034
149949
|
throw new Error("Vector backfill runner received invalid JSON input");
|
|
149035
149950
|
}
|
|
149036
|
-
if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !
|
|
149951
|
+
if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path23.isAbsolute(value.projectRoot) || !path23.isAbsolute(value.dataDir)) {
|
|
149037
149952
|
throw new Error("Vector backfill runner received an invalid request");
|
|
149038
149953
|
}
|
|
149039
149954
|
return {
|
|
@@ -150115,8 +151030,16 @@ var init_observations = __esm({
|
|
|
150115
151030
|
});
|
|
150116
151031
|
|
|
150117
151032
|
// src/store/session-store.ts
|
|
150118
|
-
|
|
150119
|
-
|
|
151033
|
+
var session_store_exports = {};
|
|
151034
|
+
__export(session_store_exports, {
|
|
151035
|
+
SessionGracefulDegrade: () => SessionGracefulDegrade,
|
|
151036
|
+
SessionSqliteStore: () => SessionSqliteStore,
|
|
151037
|
+
getSessionStore: () => getSessionStore,
|
|
151038
|
+
initSessionStore: () => initSessionStore,
|
|
151039
|
+
resetSessionStore: () => resetSessionStore
|
|
151040
|
+
});
|
|
151041
|
+
import path24 from "path";
|
|
151042
|
+
import fs16 from "fs";
|
|
150120
151043
|
function sessionToRow(session) {
|
|
150121
151044
|
return {
|
|
150122
151045
|
id: session.id,
|
|
@@ -150145,6 +151068,10 @@ function getSessionStore() {
|
|
|
150145
151068
|
}
|
|
150146
151069
|
return _store3;
|
|
150147
151070
|
}
|
|
151071
|
+
function resetSessionStore() {
|
|
151072
|
+
_store3 = null;
|
|
151073
|
+
_storeDataDir3 = null;
|
|
151074
|
+
}
|
|
150148
151075
|
async function initSessionStore(dataDir) {
|
|
150149
151076
|
if (_store3 && _storeDataDir3 === dataDir) return _store3;
|
|
150150
151077
|
_store3 = null;
|
|
@@ -150198,10 +151125,10 @@ var init_session_store = __esm({
|
|
|
150198
151125
|
async migrateFromJsonIfNeeded() {
|
|
150199
151126
|
const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM sessions`).get();
|
|
150200
151127
|
if (count2.cnt > 0) return;
|
|
150201
|
-
const jsonPath =
|
|
150202
|
-
if (!
|
|
151128
|
+
const jsonPath = path24.join(this.dataDir, "sessions.json");
|
|
151129
|
+
if (!fs16.existsSync(jsonPath)) return;
|
|
150203
151130
|
try {
|
|
150204
|
-
const raw =
|
|
151131
|
+
const raw = fs16.readFileSync(jsonPath, "utf-8");
|
|
150205
151132
|
const sessions = JSON.parse(raw);
|
|
150206
151133
|
if (!Array.isArray(sessions) || sessions.length === 0) return;
|
|
150207
151134
|
console.error(`[memorix] Migrating ${sessions.length} sessions from JSON to SQLite...`);
|
|
@@ -152555,8 +153482,8 @@ var init_trae = __esm({
|
|
|
152555
153482
|
});
|
|
152556
153483
|
|
|
152557
153484
|
// src/rules/syncer.ts
|
|
152558
|
-
import { promises as
|
|
152559
|
-
import
|
|
153485
|
+
import { promises as fs17 } from "fs";
|
|
153486
|
+
import path25 from "path";
|
|
152560
153487
|
var RulesSyncer;
|
|
152561
153488
|
var init_syncer = __esm({
|
|
152562
153489
|
"src/rules/syncer.ts"() {
|
|
@@ -152601,8 +153528,8 @@ var init_syncer = __esm({
|
|
|
152601
153528
|
const found = await this.findFiles(scanPath);
|
|
152602
153529
|
for (const filePath of found) {
|
|
152603
153530
|
try {
|
|
152604
|
-
const content = await
|
|
152605
|
-
const relativePath =
|
|
153531
|
+
const content = await fs17.readFile(filePath, "utf-8");
|
|
153532
|
+
const relativePath = path25.relative(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
152606
153533
|
const parsed = entry.adapter.parse(relativePath, content);
|
|
152607
153534
|
rules.push(...parsed);
|
|
152608
153535
|
} catch {
|
|
@@ -152670,7 +153597,7 @@ var init_syncer = __esm({
|
|
|
152670
153597
|
const entries = [];
|
|
152671
153598
|
for (const adapter of this.adapters.values()) {
|
|
152672
153599
|
const absolutePaths = adapter.filePatterns.map(
|
|
152673
|
-
(p) =>
|
|
153600
|
+
(p) => path25.join(this.projectRoot, p)
|
|
152674
153601
|
);
|
|
152675
153602
|
entries.push({ adapter, paths: absolutePaths });
|
|
152676
153603
|
}
|
|
@@ -152678,13 +153605,13 @@ var init_syncer = __esm({
|
|
|
152678
153605
|
}
|
|
152679
153606
|
/** Find files matching a glob-like path (simple implementation) */
|
|
152680
153607
|
async findFiles(pattern) {
|
|
152681
|
-
const dir =
|
|
152682
|
-
const fileGlob =
|
|
153608
|
+
const dir = path25.dirname(pattern);
|
|
153609
|
+
const fileGlob = path25.basename(pattern);
|
|
152683
153610
|
try {
|
|
152684
|
-
const stat4 = await
|
|
153611
|
+
const stat4 = await fs17.stat(dir);
|
|
152685
153612
|
if (!stat4.isDirectory()) {
|
|
152686
153613
|
try {
|
|
152687
|
-
await
|
|
153614
|
+
await fs17.access(pattern);
|
|
152688
153615
|
return [pattern];
|
|
152689
153616
|
} catch {
|
|
152690
153617
|
return [];
|
|
@@ -152692,7 +153619,7 @@ var init_syncer = __esm({
|
|
|
152692
153619
|
}
|
|
152693
153620
|
} catch {
|
|
152694
153621
|
try {
|
|
152695
|
-
await
|
|
153622
|
+
await fs17.access(pattern);
|
|
152696
153623
|
return [pattern];
|
|
152697
153624
|
} catch {
|
|
152698
153625
|
return [];
|
|
@@ -152700,16 +153627,16 @@ var init_syncer = __esm({
|
|
|
152700
153627
|
}
|
|
152701
153628
|
if (fileGlob.includes("*")) {
|
|
152702
153629
|
try {
|
|
152703
|
-
const files = await
|
|
153630
|
+
const files = await fs17.readdir(dir);
|
|
152704
153631
|
const ext = fileGlob.replace("*", "");
|
|
152705
|
-
return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) =>
|
|
153632
|
+
return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) => path25.join(dir, f3));
|
|
152706
153633
|
} catch {
|
|
152707
153634
|
return [];
|
|
152708
153635
|
}
|
|
152709
153636
|
}
|
|
152710
153637
|
try {
|
|
152711
|
-
await
|
|
152712
|
-
return [
|
|
153638
|
+
await fs17.access(path25.join(dir, fileGlob));
|
|
153639
|
+
return [path25.join(dir, fileGlob)];
|
|
152713
153640
|
} catch {
|
|
152714
153641
|
return [];
|
|
152715
153642
|
}
|
|
@@ -155288,12 +156215,52 @@ var init_formation_timeout = __esm({
|
|
|
155288
156215
|
}
|
|
155289
156216
|
});
|
|
155290
156217
|
|
|
156218
|
+
// src/search/surfaced-registry.ts
|
|
156219
|
+
function touch(key) {
|
|
156220
|
+
const ids = registry.get(key);
|
|
156221
|
+
if (!ids) return [];
|
|
156222
|
+
registry.delete(key);
|
|
156223
|
+
registry.set(key, ids);
|
|
156224
|
+
return ids;
|
|
156225
|
+
}
|
|
156226
|
+
function getSurfacedIds(key) {
|
|
156227
|
+
return [...touch(key)];
|
|
156228
|
+
}
|
|
156229
|
+
function recordSurfacedIds(key, ids) {
|
|
156230
|
+
if (ids.length === 0) return;
|
|
156231
|
+
let list = registry.get(key) ?? [];
|
|
156232
|
+
const known = new Set(list);
|
|
156233
|
+
for (const id of ids) {
|
|
156234
|
+
if (known.has(id)) continue;
|
|
156235
|
+
known.add(id);
|
|
156236
|
+
list.push(id);
|
|
156237
|
+
}
|
|
156238
|
+
if (list.length > MAX_IDS) {
|
|
156239
|
+
list = list.slice(list.length - MAX_IDS);
|
|
156240
|
+
}
|
|
156241
|
+
registry.set(key, list);
|
|
156242
|
+
if (registry.size > MAX_KEYS) {
|
|
156243
|
+
const oldestKey = registry.keys().next().value;
|
|
156244
|
+
if (oldestKey !== void 0 && oldestKey !== key) registry.delete(oldestKey);
|
|
156245
|
+
}
|
|
156246
|
+
}
|
|
156247
|
+
var MAX_KEYS, MAX_IDS, registry;
|
|
156248
|
+
var init_surfaced_registry = __esm({
|
|
156249
|
+
"src/search/surfaced-registry.ts"() {
|
|
156250
|
+
"use strict";
|
|
156251
|
+
init_esm_shims();
|
|
156252
|
+
MAX_KEYS = 64;
|
|
156253
|
+
MAX_IDS = 200;
|
|
156254
|
+
registry = /* @__PURE__ */ new Map();
|
|
156255
|
+
}
|
|
156256
|
+
});
|
|
156257
|
+
|
|
155291
156258
|
// src/server/request-context.ts
|
|
155292
|
-
import
|
|
156259
|
+
import path26 from "path";
|
|
155293
156260
|
function resolveRoot(projectRoot) {
|
|
155294
156261
|
const value = projectRoot.trim();
|
|
155295
156262
|
if (!value) throw new Error("Project binding requires a non-empty project root");
|
|
155296
|
-
return
|
|
156263
|
+
return path26.resolve(value);
|
|
155297
156264
|
}
|
|
155298
156265
|
function createProjectBindingController(projectRoot) {
|
|
155299
156266
|
return new ProjectBindingController(projectRoot);
|
|
@@ -155942,827 +156909,6 @@ var init_session = __esm({
|
|
|
155942
156909
|
}
|
|
155943
156910
|
});
|
|
155944
156911
|
|
|
155945
|
-
// src/memory/owner.ts
|
|
155946
|
-
var owner_exports = {};
|
|
155947
|
-
__export(owner_exports, {
|
|
155948
|
-
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
155949
|
-
});
|
|
155950
|
-
import { randomUUID as randomUUID11 } from "crypto";
|
|
155951
|
-
import { promises as fs17 } from "fs";
|
|
155952
|
-
import path26 from "path";
|
|
155953
|
-
function ownerPath(dataDir) {
|
|
155954
|
-
const resolved = path26.resolve(dataDir);
|
|
155955
|
-
const home = path26.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path26.dirname(resolved) : resolved;
|
|
155956
|
-
return path26.join(home, "memorix-user.json");
|
|
155957
|
-
}
|
|
155958
|
-
function normalizedOwnerId(value) {
|
|
155959
|
-
const trimmed = value?.trim();
|
|
155960
|
-
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
155961
|
-
}
|
|
155962
|
-
async function readOwnerFile(filePath) {
|
|
155963
|
-
try {
|
|
155964
|
-
const value = JSON.parse(await fs17.readFile(filePath, "utf8"));
|
|
155965
|
-
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
155966
|
-
return id ? { id, source: "local-file" } : void 0;
|
|
155967
|
-
} catch {
|
|
155968
|
-
return void 0;
|
|
155969
|
-
}
|
|
155970
|
-
}
|
|
155971
|
-
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
155972
|
-
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
155973
|
-
if (configured) return { id: configured, source: "environment" };
|
|
155974
|
-
const filePath = ownerPath(dataDir);
|
|
155975
|
-
const existing = await readOwnerFile(filePath);
|
|
155976
|
-
if (existing || !options.create) return existing;
|
|
155977
|
-
await fs17.mkdir(path26.dirname(filePath), { recursive: true });
|
|
155978
|
-
const created = {
|
|
155979
|
-
version: 1,
|
|
155980
|
-
id: "local-" + randomUUID11(),
|
|
155981
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
155982
|
-
};
|
|
155983
|
-
try {
|
|
155984
|
-
await fs17.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
155985
|
-
return { id: created.id, source: "local-file" };
|
|
155986
|
-
} catch (error) {
|
|
155987
|
-
if (error?.code !== "EEXIST") throw error;
|
|
155988
|
-
const concurrent = await readOwnerFile(filePath);
|
|
155989
|
-
if (concurrent) return concurrent;
|
|
155990
|
-
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
155991
|
-
}
|
|
155992
|
-
}
|
|
155993
|
-
var ID_PATTERN;
|
|
155994
|
-
var init_owner = __esm({
|
|
155995
|
-
"src/memory/owner.ts"() {
|
|
155996
|
-
"use strict";
|
|
155997
|
-
init_esm_shims();
|
|
155998
|
-
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
155999
|
-
}
|
|
156000
|
-
});
|
|
156001
|
-
|
|
156002
|
-
// src/memory/long-term-store.ts
|
|
156003
|
-
import { randomUUID as randomUUID12 } from "crypto";
|
|
156004
|
-
function optionalText6(value) {
|
|
156005
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
156006
|
-
}
|
|
156007
|
-
function arrayValue2(value) {
|
|
156008
|
-
if (typeof value !== "string" || !value) return [];
|
|
156009
|
-
try {
|
|
156010
|
-
const parsed = JSON.parse(value);
|
|
156011
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
156012
|
-
} catch {
|
|
156013
|
-
return [];
|
|
156014
|
-
}
|
|
156015
|
-
}
|
|
156016
|
-
function rowToMemory(row) {
|
|
156017
|
-
return {
|
|
156018
|
-
id: row.id,
|
|
156019
|
-
originProjectId: row.originProjectId,
|
|
156020
|
-
ownerId: row.ownerId,
|
|
156021
|
-
scope: row.scope,
|
|
156022
|
-
kind: row.kind,
|
|
156023
|
-
state: row.state,
|
|
156024
|
-
portability: row.portability,
|
|
156025
|
-
title: row.title,
|
|
156026
|
-
content: row.content,
|
|
156027
|
-
facts: arrayValue2(row.factsJson),
|
|
156028
|
-
tags: arrayValue2(row.tagsJson),
|
|
156029
|
-
...optionalText6(row.applicability) ? { applicability: row.applicability } : {},
|
|
156030
|
-
origin: row.origin,
|
|
156031
|
-
createdAt: row.createdAt,
|
|
156032
|
-
updatedAt: row.updatedAt,
|
|
156033
|
-
...optionalText6(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
156034
|
-
...optionalText6(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
156035
|
-
...optionalText6(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
156036
|
-
...optionalText6(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
156037
|
-
...optionalText6(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
156038
|
-
accessCount: Number(row.accessCount ?? 0),
|
|
156039
|
-
...optionalText6(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
156040
|
-
};
|
|
156041
|
-
}
|
|
156042
|
-
function rowToEvidence2(row) {
|
|
156043
|
-
return {
|
|
156044
|
-
id: row.id,
|
|
156045
|
-
memoryId: row.memoryId,
|
|
156046
|
-
kind: row.kind,
|
|
156047
|
-
referenceId: row.referenceId,
|
|
156048
|
-
relation: row.relation,
|
|
156049
|
-
...optionalText6(row.locator) ? { locator: row.locator } : {},
|
|
156050
|
-
...optionalText6(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
156051
|
-
createdAt: row.createdAt
|
|
156052
|
-
};
|
|
156053
|
-
}
|
|
156054
|
-
function rowToEvent2(row) {
|
|
156055
|
-
return {
|
|
156056
|
-
id: row.id,
|
|
156057
|
-
memoryId: row.memoryId,
|
|
156058
|
-
kind: row.kind,
|
|
156059
|
-
...optionalText6(row.fromState) ? { fromState: row.fromState } : {},
|
|
156060
|
-
...optionalText6(row.toState) ? { toState: row.toState } : {},
|
|
156061
|
-
...optionalText6(row.detail) ? { detail: row.detail } : {},
|
|
156062
|
-
createdAt: row.createdAt
|
|
156063
|
-
};
|
|
156064
|
-
}
|
|
156065
|
-
var LongTermMemoryStore;
|
|
156066
|
-
var init_long_term_store = __esm({
|
|
156067
|
-
"src/memory/long-term-store.ts"() {
|
|
156068
|
-
"use strict";
|
|
156069
|
-
init_esm_shims();
|
|
156070
|
-
init_sqlite_db();
|
|
156071
|
-
LongTermMemoryStore = class {
|
|
156072
|
-
db = null;
|
|
156073
|
-
async init(dataDir) {
|
|
156074
|
-
this.db = getDatabase(dataDir);
|
|
156075
|
-
}
|
|
156076
|
-
transaction(fn) {
|
|
156077
|
-
return this.db.transaction(fn)();
|
|
156078
|
-
}
|
|
156079
|
-
get(id) {
|
|
156080
|
-
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
156081
|
-
return row ? rowToMemory(row) : void 0;
|
|
156082
|
-
}
|
|
156083
|
-
list(options = {}) {
|
|
156084
|
-
const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
156085
|
-
const clauses = [];
|
|
156086
|
-
const params = [];
|
|
156087
|
-
if (options.ownerId) {
|
|
156088
|
-
clauses.push("ownerId = ?");
|
|
156089
|
-
params.push(options.ownerId);
|
|
156090
|
-
}
|
|
156091
|
-
if (options.originProjectId) {
|
|
156092
|
-
clauses.push("originProjectId = ?");
|
|
156093
|
-
params.push(options.originProjectId);
|
|
156094
|
-
}
|
|
156095
|
-
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
156096
|
-
const rows = this.db.prepare(
|
|
156097
|
-
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
156098
|
-
).all(...params, limit2);
|
|
156099
|
-
return rows.map(rowToMemory);
|
|
156100
|
-
}
|
|
156101
|
-
insert(memory) {
|
|
156102
|
-
this.db.prepare(`
|
|
156103
|
-
INSERT INTO long_term_memories (
|
|
156104
|
-
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
156105
|
-
title, content, factsJson, tagsJson, applicability, origin,
|
|
156106
|
-
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
156107
|
-
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
156108
|
-
) VALUES (
|
|
156109
|
-
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
156110
|
-
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
156111
|
-
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
156112
|
-
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
156113
|
-
)
|
|
156114
|
-
`).run({
|
|
156115
|
-
...memory,
|
|
156116
|
-
factsJson: JSON.stringify(memory.facts),
|
|
156117
|
-
tagsJson: JSON.stringify(memory.tags),
|
|
156118
|
-
applicability: memory.applicability ?? null,
|
|
156119
|
-
qualifiedAt: memory.qualifiedAt ?? null,
|
|
156120
|
-
approvedAt: memory.approvedAt ?? null,
|
|
156121
|
-
archivedAt: memory.archivedAt ?? null,
|
|
156122
|
-
supersededBy: memory.supersededBy ?? null,
|
|
156123
|
-
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
156124
|
-
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
156125
|
-
});
|
|
156126
|
-
}
|
|
156127
|
-
update(memory) {
|
|
156128
|
-
this.db.prepare(`
|
|
156129
|
-
UPDATE long_term_memories SET
|
|
156130
|
-
scope = @scope,
|
|
156131
|
-
kind = @kind,
|
|
156132
|
-
state = @state,
|
|
156133
|
-
portability = @portability,
|
|
156134
|
-
title = @title,
|
|
156135
|
-
content = @content,
|
|
156136
|
-
factsJson = @factsJson,
|
|
156137
|
-
tagsJson = @tagsJson,
|
|
156138
|
-
applicability = @applicability,
|
|
156139
|
-
updatedAt = @updatedAt,
|
|
156140
|
-
qualifiedAt = @qualifiedAt,
|
|
156141
|
-
approvedAt = @approvedAt,
|
|
156142
|
-
archivedAt = @archivedAt,
|
|
156143
|
-
supersededBy = @supersededBy,
|
|
156144
|
-
lastValidatedAt = @lastValidatedAt,
|
|
156145
|
-
accessCount = @accessCount,
|
|
156146
|
-
lastAccessedAt = @lastAccessedAt
|
|
156147
|
-
WHERE id = @id
|
|
156148
|
-
`).run({
|
|
156149
|
-
...memory,
|
|
156150
|
-
factsJson: JSON.stringify(memory.facts),
|
|
156151
|
-
tagsJson: JSON.stringify(memory.tags),
|
|
156152
|
-
applicability: memory.applicability ?? null,
|
|
156153
|
-
qualifiedAt: memory.qualifiedAt ?? null,
|
|
156154
|
-
approvedAt: memory.approvedAt ?? null,
|
|
156155
|
-
archivedAt: memory.archivedAt ?? null,
|
|
156156
|
-
supersededBy: memory.supersededBy ?? null,
|
|
156157
|
-
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
156158
|
-
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
156159
|
-
});
|
|
156160
|
-
}
|
|
156161
|
-
insertEvidence(input) {
|
|
156162
|
-
const evidence = {
|
|
156163
|
-
id: randomUUID12(),
|
|
156164
|
-
...input,
|
|
156165
|
-
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
156166
|
-
};
|
|
156167
|
-
this.db.prepare(`
|
|
156168
|
-
INSERT INTO long_term_memory_evidence (
|
|
156169
|
-
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
156170
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
156171
|
-
`).run(
|
|
156172
|
-
evidence.id,
|
|
156173
|
-
evidence.memoryId,
|
|
156174
|
-
evidence.kind,
|
|
156175
|
-
evidence.referenceId,
|
|
156176
|
-
evidence.relation,
|
|
156177
|
-
evidence.locator ?? null,
|
|
156178
|
-
evidence.capturedHash ?? null,
|
|
156179
|
-
evidence.createdAt
|
|
156180
|
-
);
|
|
156181
|
-
return evidence;
|
|
156182
|
-
}
|
|
156183
|
-
listEvidence(memoryId) {
|
|
156184
|
-
return this.db.prepare(`
|
|
156185
|
-
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
156186
|
-
`).all(memoryId).map(rowToEvidence2);
|
|
156187
|
-
}
|
|
156188
|
-
recordEvent(input) {
|
|
156189
|
-
const event = {
|
|
156190
|
-
id: randomUUID12(),
|
|
156191
|
-
...input,
|
|
156192
|
-
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
156193
|
-
};
|
|
156194
|
-
this.db.prepare(`
|
|
156195
|
-
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
156196
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
156197
|
-
`).run(
|
|
156198
|
-
event.id,
|
|
156199
|
-
event.memoryId,
|
|
156200
|
-
event.kind,
|
|
156201
|
-
event.fromState ?? null,
|
|
156202
|
-
event.toState ?? null,
|
|
156203
|
-
event.detail ?? null,
|
|
156204
|
-
event.createdAt
|
|
156205
|
-
);
|
|
156206
|
-
return event;
|
|
156207
|
-
}
|
|
156208
|
-
listEvents(memoryId) {
|
|
156209
|
-
return this.db.prepare(`
|
|
156210
|
-
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
156211
|
-
`).all(memoryId).map(rowToEvent2);
|
|
156212
|
-
}
|
|
156213
|
-
markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
156214
|
-
const unique2 = [...new Set(ids.filter(Boolean))];
|
|
156215
|
-
if (unique2.length === 0) return;
|
|
156216
|
-
const statement = this.db.prepare(`
|
|
156217
|
-
UPDATE long_term_memories
|
|
156218
|
-
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
156219
|
-
WHERE id = ?
|
|
156220
|
-
`);
|
|
156221
|
-
this.db.transaction(() => {
|
|
156222
|
-
for (const id of unique2) statement.run(at, id);
|
|
156223
|
-
})();
|
|
156224
|
-
}
|
|
156225
|
-
};
|
|
156226
|
-
}
|
|
156227
|
-
});
|
|
156228
|
-
|
|
156229
|
-
// src/memory/long-term-types.ts
|
|
156230
|
-
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
156231
|
-
var init_long_term_types = __esm({
|
|
156232
|
-
"src/memory/long-term-types.ts"() {
|
|
156233
|
-
"use strict";
|
|
156234
|
-
init_esm_shims();
|
|
156235
|
-
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
156236
|
-
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
156237
|
-
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
156238
|
-
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
156239
|
-
"observation",
|
|
156240
|
-
"claim",
|
|
156241
|
-
"workflow",
|
|
156242
|
-
"session",
|
|
156243
|
-
"checkpoint",
|
|
156244
|
-
"git",
|
|
156245
|
-
"code",
|
|
156246
|
-
"test",
|
|
156247
|
-
"run",
|
|
156248
|
-
"document",
|
|
156249
|
-
"manual",
|
|
156250
|
-
"user"
|
|
156251
|
-
];
|
|
156252
|
-
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
156253
|
-
"supports",
|
|
156254
|
-
"verifies",
|
|
156255
|
-
"derives",
|
|
156256
|
-
"user-confirmed"
|
|
156257
|
-
];
|
|
156258
|
-
}
|
|
156259
|
-
});
|
|
156260
|
-
|
|
156261
|
-
// src/memory/long-term.ts
|
|
156262
|
-
var long_term_exports = {};
|
|
156263
|
-
__export(long_term_exports, {
|
|
156264
|
-
approveLongTermMemory: () => approveLongTermMemory,
|
|
156265
|
-
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
156266
|
-
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
156267
|
-
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
156268
|
-
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
156269
|
-
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
156270
|
-
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
156271
|
-
listLongTermMemories: () => listLongTermMemories,
|
|
156272
|
-
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
156273
|
-
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
156274
|
-
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
156275
|
-
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
156276
|
-
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
156277
|
-
});
|
|
156278
|
-
import { randomUUID as randomUUID13 } from "crypto";
|
|
156279
|
-
function isOneOf(value, values) {
|
|
156280
|
-
return typeof value === "string" && values.includes(value);
|
|
156281
|
-
}
|
|
156282
|
-
function compactText2(value, field, max = MAX_TEXT_LENGTH) {
|
|
156283
|
-
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
156284
|
-
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
156285
|
-
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
156286
|
-
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
156287
|
-
return safe;
|
|
156288
|
-
}
|
|
156289
|
-
function compactList(values, field) {
|
|
156290
|
-
if (!values) return [];
|
|
156291
|
-
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
156292
|
-
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
156293
|
-
}
|
|
156294
|
-
function normalizeEvidence(input) {
|
|
156295
|
-
if (!Array.isArray(input) || input.length === 0) {
|
|
156296
|
-
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
156297
|
-
}
|
|
156298
|
-
return input.map((evidence) => {
|
|
156299
|
-
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
156300
|
-
throw new Error("Long-term memory evidence kind is invalid.");
|
|
156301
|
-
}
|
|
156302
|
-
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
156303
|
-
throw new Error("Long-term memory evidence relation is invalid.");
|
|
156304
|
-
}
|
|
156305
|
-
return {
|
|
156306
|
-
kind: evidence.kind,
|
|
156307
|
-
referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
|
|
156308
|
-
relation: evidence.relation,
|
|
156309
|
-
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
|
|
156310
|
-
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
156311
|
-
};
|
|
156312
|
-
});
|
|
156313
|
-
}
|
|
156314
|
-
function assertScope(scope) {
|
|
156315
|
-
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
156316
|
-
}
|
|
156317
|
-
function assertKind(kind) {
|
|
156318
|
-
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
156319
|
-
}
|
|
156320
|
-
function assertPortability(value) {
|
|
156321
|
-
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
156322
|
-
throw new Error("Long-term memory portability is invalid.");
|
|
156323
|
-
}
|
|
156324
|
-
}
|
|
156325
|
-
function assertPortableEvidence(scope, portability, evidence) {
|
|
156326
|
-
if (portability !== "portable") return;
|
|
156327
|
-
if (scope !== "user") {
|
|
156328
|
-
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
156329
|
-
}
|
|
156330
|
-
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
156331
|
-
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.");
|
|
156332
|
-
}
|
|
156333
|
-
}
|
|
156334
|
-
function requireTeamReader(scope, reader) {
|
|
156335
|
-
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
156336
|
-
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
156337
|
-
}
|
|
156338
|
-
}
|
|
156339
|
-
function matchesScope(memory, reader) {
|
|
156340
|
-
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
156341
|
-
if (memory.scope === "team") {
|
|
156342
|
-
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
156343
|
-
}
|
|
156344
|
-
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
156345
|
-
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
156346
|
-
}
|
|
156347
|
-
function canReadLongTermMemory(memory, reader) {
|
|
156348
|
-
return matchesScope(memory, reader);
|
|
156349
|
-
}
|
|
156350
|
-
function isEligibleLongTermMemory(memory) {
|
|
156351
|
-
return memory.state === "qualified" || memory.state === "approved";
|
|
156352
|
-
}
|
|
156353
|
-
function taskTerms(task) {
|
|
156354
|
-
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
156355
|
-
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
156356
|
-
}
|
|
156357
|
-
function kindFitScore(kind, task) {
|
|
156358
|
-
const normalized = task.toLocaleLowerCase("en-US");
|
|
156359
|
-
const hints = {
|
|
156360
|
-
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
156361
|
-
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
156362
|
-
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
156363
|
-
};
|
|
156364
|
-
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
156365
|
-
}
|
|
156366
|
-
function validationFreshnessScore(memory) {
|
|
156367
|
-
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
156368
|
-
if (!validatedAt) return 0;
|
|
156369
|
-
const time = Date.parse(validatedAt);
|
|
156370
|
-
if (!Number.isFinite(time)) return 0;
|
|
156371
|
-
const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
|
|
156372
|
-
if (ageDays <= 30) return 0.35;
|
|
156373
|
-
if (ageDays <= 90) return 0.15;
|
|
156374
|
-
return 0;
|
|
156375
|
-
}
|
|
156376
|
-
function scoreForTask(memory, task) {
|
|
156377
|
-
const terms = taskTerms(task);
|
|
156378
|
-
if (terms.length === 0) return void 0;
|
|
156379
|
-
const title = memory.title.toLocaleLowerCase("en-US");
|
|
156380
|
-
const content = memory.content.toLocaleLowerCase("en-US");
|
|
156381
|
-
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
156382
|
-
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
156383
|
-
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
156384
|
-
let score = memory.state === "approved" ? 0.5 : 0;
|
|
156385
|
-
const matched = [];
|
|
156386
|
-
for (const term of terms) {
|
|
156387
|
-
let termScore = 0;
|
|
156388
|
-
if (title.includes(term)) termScore += 5;
|
|
156389
|
-
if (tags.includes(term)) termScore += 4;
|
|
156390
|
-
if (applicability.includes(term)) termScore += 3;
|
|
156391
|
-
if (facts.includes(term)) termScore += 2;
|
|
156392
|
-
if (content.includes(term)) termScore += 1;
|
|
156393
|
-
if (termScore > 0) {
|
|
156394
|
-
score += termScore;
|
|
156395
|
-
matched.push(term);
|
|
156396
|
-
}
|
|
156397
|
-
}
|
|
156398
|
-
if (matched.length === 0) return void 0;
|
|
156399
|
-
score += kindFitScore(memory.kind, task);
|
|
156400
|
-
score += validationFreshnessScore(memory);
|
|
156401
|
-
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
156402
|
-
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
156403
|
-
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
156404
|
-
}
|
|
156405
|
-
function semanticText(memory) {
|
|
156406
|
-
return sanitizeCredentials([
|
|
156407
|
-
memory.title,
|
|
156408
|
-
memory.applicability,
|
|
156409
|
-
memory.tags.join(" "),
|
|
156410
|
-
memory.facts.join(" "),
|
|
156411
|
-
memory.content
|
|
156412
|
-
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
156413
|
-
}
|
|
156414
|
-
function cosineSimilarity(left, right) {
|
|
156415
|
-
if (left.length === 0 || left.length !== right.length) return 0;
|
|
156416
|
-
let dot = 0;
|
|
156417
|
-
let leftMagnitude = 0;
|
|
156418
|
-
let rightMagnitude = 0;
|
|
156419
|
-
for (let index = 0; index < left.length; index += 1) {
|
|
156420
|
-
const a = left[index];
|
|
156421
|
-
const b = right[index];
|
|
156422
|
-
if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
|
|
156423
|
-
dot += a * b;
|
|
156424
|
-
leftMagnitude += a * a;
|
|
156425
|
-
rightMagnitude += b * b;
|
|
156426
|
-
}
|
|
156427
|
-
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
156428
|
-
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
156429
|
-
}
|
|
156430
|
-
function semanticSelection(memory, similarity, task) {
|
|
156431
|
-
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
156432
|
-
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
156433
|
-
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
156434
|
-
return {
|
|
156435
|
-
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
156436
|
-
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
156437
|
-
};
|
|
156438
|
-
}
|
|
156439
|
-
async function resolveSemanticProvider(explicit) {
|
|
156440
|
-
if (explicit !== void 0) return explicit;
|
|
156441
|
-
try {
|
|
156442
|
-
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
156443
|
-
return await withTimeout(
|
|
156444
|
-
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
156445
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
156446
|
-
"Long-term semantic provider initialization"
|
|
156447
|
-
);
|
|
156448
|
-
} catch {
|
|
156449
|
-
return null;
|
|
156450
|
-
}
|
|
156451
|
-
}
|
|
156452
|
-
function createStore(dataDir) {
|
|
156453
|
-
const store = new LongTermMemoryStore();
|
|
156454
|
-
return store.init(dataDir).then(() => store);
|
|
156455
|
-
}
|
|
156456
|
-
async function createManualLongTermMemory(input) {
|
|
156457
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
156458
|
-
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
156459
|
-
assertScope(input.scope);
|
|
156460
|
-
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID13(), relation: "supports" }];
|
|
156461
|
-
return createLongTermMemoryWithDataDir({
|
|
156462
|
-
dataDir: input.dataDir,
|
|
156463
|
-
originProjectId: input.projectId,
|
|
156464
|
-
ownerId: owner.id,
|
|
156465
|
-
scope: input.scope,
|
|
156466
|
-
kind: input.kind,
|
|
156467
|
-
portability: input.portability,
|
|
156468
|
-
title: input.title,
|
|
156469
|
-
content: input.content,
|
|
156470
|
-
facts: input.facts,
|
|
156471
|
-
tags: input.tags,
|
|
156472
|
-
applicability: input.applicability,
|
|
156473
|
-
origin: "manual",
|
|
156474
|
-
evidence,
|
|
156475
|
-
reader: input.reader
|
|
156476
|
-
});
|
|
156477
|
-
}
|
|
156478
|
-
async function promoteObservationToLongTermMemory(input) {
|
|
156479
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
156480
|
-
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
156481
|
-
return createLongTermMemoryWithDataDir({
|
|
156482
|
-
dataDir: input.dataDir,
|
|
156483
|
-
originProjectId: input.observation.projectId,
|
|
156484
|
-
ownerId: owner.id,
|
|
156485
|
-
scope: input.scope,
|
|
156486
|
-
kind: input.kind,
|
|
156487
|
-
portability: "project-bound",
|
|
156488
|
-
title: input.observation.title,
|
|
156489
|
-
content: input.observation.narrative,
|
|
156490
|
-
facts: input.observation.facts,
|
|
156491
|
-
tags: input.tags ?? input.observation.concepts,
|
|
156492
|
-
applicability: input.applicability,
|
|
156493
|
-
origin: input.observation.source === "git" ? "git" : "agent",
|
|
156494
|
-
evidence: [{
|
|
156495
|
-
kind: "observation",
|
|
156496
|
-
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
156497
|
-
relation: "derives"
|
|
156498
|
-
}],
|
|
156499
|
-
reader: input.reader
|
|
156500
|
-
});
|
|
156501
|
-
}
|
|
156502
|
-
async function createLongTermMemoryCandidate(input) {
|
|
156503
|
-
return createLongTermMemoryWithDataDir(input);
|
|
156504
|
-
}
|
|
156505
|
-
async function createLongTermMemoryWithDataDir(input) {
|
|
156506
|
-
assertScope(input.scope);
|
|
156507
|
-
assertKind(input.kind);
|
|
156508
|
-
const portability = input.portability ?? "project-bound";
|
|
156509
|
-
assertPortability(portability);
|
|
156510
|
-
requireTeamReader(input.scope, input.reader);
|
|
156511
|
-
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
156512
|
-
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
156513
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
156514
|
-
const memory = {
|
|
156515
|
-
id: randomUUID13(),
|
|
156516
|
-
originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
|
|
156517
|
-
ownerId: compactText2(input.ownerId, "owner id", 160),
|
|
156518
|
-
scope: input.scope,
|
|
156519
|
-
kind: input.kind,
|
|
156520
|
-
state: "candidate",
|
|
156521
|
-
portability,
|
|
156522
|
-
title: compactText2(input.title, "title", 500),
|
|
156523
|
-
content: compactText2(input.content, "content"),
|
|
156524
|
-
facts: compactList(input.facts, "facts"),
|
|
156525
|
-
tags: compactList(input.tags, "tags"),
|
|
156526
|
-
...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
|
|
156527
|
-
origin: input.origin ?? "manual",
|
|
156528
|
-
createdAt: now3,
|
|
156529
|
-
updatedAt: now3,
|
|
156530
|
-
accessCount: 0
|
|
156531
|
-
};
|
|
156532
|
-
const store = await createStore(input.dataDir);
|
|
156533
|
-
const evidence = store.transaction(() => {
|
|
156534
|
-
store.insert(memory);
|
|
156535
|
-
const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
156536
|
-
store.recordEvent({
|
|
156537
|
-
memoryId: memory.id,
|
|
156538
|
-
kind: "created",
|
|
156539
|
-
toState: "candidate",
|
|
156540
|
-
detail: "Created as a source-backed long-term memory candidate.",
|
|
156541
|
-
createdAt: now3
|
|
156542
|
-
});
|
|
156543
|
-
return written;
|
|
156544
|
-
});
|
|
156545
|
-
return { memory, evidence };
|
|
156546
|
-
}
|
|
156547
|
-
function transitionDetail(value) {
|
|
156548
|
-
return compactText2(value, "transition reason", 1e3);
|
|
156549
|
-
}
|
|
156550
|
-
async function transitionMemory(input) {
|
|
156551
|
-
const store = await createStore(input.dataDir);
|
|
156552
|
-
return store.transaction(() => {
|
|
156553
|
-
const current = store.get(input.id);
|
|
156554
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
156555
|
-
if (current.state !== input.expected) {
|
|
156556
|
-
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
156557
|
-
}
|
|
156558
|
-
const evidence = store.listEvidence(current.id);
|
|
156559
|
-
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
156560
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
156561
|
-
const next = {
|
|
156562
|
-
...current,
|
|
156563
|
-
state: input.next,
|
|
156564
|
-
updatedAt: now3,
|
|
156565
|
-
lastValidatedAt: now3,
|
|
156566
|
-
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
156567
|
-
};
|
|
156568
|
-
store.update(next);
|
|
156569
|
-
store.recordEvent({
|
|
156570
|
-
memoryId: next.id,
|
|
156571
|
-
kind: input.event,
|
|
156572
|
-
fromState: current.state,
|
|
156573
|
-
toState: next.state,
|
|
156574
|
-
detail: transitionDetail(input.reason),
|
|
156575
|
-
createdAt: now3
|
|
156576
|
-
});
|
|
156577
|
-
return next;
|
|
156578
|
-
});
|
|
156579
|
-
}
|
|
156580
|
-
async function qualifyLongTermMemory(input) {
|
|
156581
|
-
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
156582
|
-
}
|
|
156583
|
-
async function approveLongTermMemory(input) {
|
|
156584
|
-
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
156585
|
-
}
|
|
156586
|
-
async function archiveLongTermMemory(input) {
|
|
156587
|
-
const store = await createStore(input.dataDir);
|
|
156588
|
-
return store.transaction(() => {
|
|
156589
|
-
const current = store.get(input.id);
|
|
156590
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
156591
|
-
if (current.state === "archived" || current.state === "superseded") {
|
|
156592
|
-
throw new Error("Long-term memory is already inactive.");
|
|
156593
|
-
}
|
|
156594
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
156595
|
-
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
156596
|
-
store.update(next);
|
|
156597
|
-
store.recordEvent({
|
|
156598
|
-
memoryId: next.id,
|
|
156599
|
-
kind: "archived",
|
|
156600
|
-
fromState: current.state,
|
|
156601
|
-
toState: "archived",
|
|
156602
|
-
detail: transitionDetail(input.reason),
|
|
156603
|
-
createdAt: now3
|
|
156604
|
-
});
|
|
156605
|
-
return next;
|
|
156606
|
-
});
|
|
156607
|
-
}
|
|
156608
|
-
async function supersedeLongTermMemory(input) {
|
|
156609
|
-
const store = await createStore(input.dataDir);
|
|
156610
|
-
return store.transaction(() => {
|
|
156611
|
-
const current = store.get(input.id);
|
|
156612
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
156613
|
-
if (current.state === "archived" || current.state === "superseded") {
|
|
156614
|
-
throw new Error("Long-term memory is already inactive.");
|
|
156615
|
-
}
|
|
156616
|
-
if (current.id === input.supersededBy) {
|
|
156617
|
-
throw new Error("Long-term memory cannot supersede itself.");
|
|
156618
|
-
}
|
|
156619
|
-
const replacement = store.get(input.supersededBy);
|
|
156620
|
-
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
156621
|
-
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
156622
|
-
}
|
|
156623
|
-
if (replacement.scope !== current.scope) {
|
|
156624
|
-
throw new Error("Replacement long-term memory must use the same scope.");
|
|
156625
|
-
}
|
|
156626
|
-
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
156627
|
-
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
156628
|
-
}
|
|
156629
|
-
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
156630
|
-
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
156631
|
-
}
|
|
156632
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
156633
|
-
const next = {
|
|
156634
|
-
...current,
|
|
156635
|
-
state: "superseded",
|
|
156636
|
-
supersededBy: replacement.id,
|
|
156637
|
-
updatedAt: now3
|
|
156638
|
-
};
|
|
156639
|
-
store.update(next);
|
|
156640
|
-
store.recordEvent({
|
|
156641
|
-
memoryId: next.id,
|
|
156642
|
-
kind: "superseded",
|
|
156643
|
-
fromState: current.state,
|
|
156644
|
-
toState: "superseded",
|
|
156645
|
-
detail: transitionDetail(input.reason),
|
|
156646
|
-
createdAt: now3
|
|
156647
|
-
});
|
|
156648
|
-
return next;
|
|
156649
|
-
});
|
|
156650
|
-
}
|
|
156651
|
-
async function listLongTermMemories(input) {
|
|
156652
|
-
const store = await createStore(input.dataDir);
|
|
156653
|
-
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) }));
|
|
156654
|
-
}
|
|
156655
|
-
async function getLongTermMemoryDetail(input) {
|
|
156656
|
-
const store = await createStore(input.dataDir);
|
|
156657
|
-
const memory = store.get(input.id);
|
|
156658
|
-
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
156659
|
-
throw new Error("Long-term memory was not found in the active scope.");
|
|
156660
|
-
}
|
|
156661
|
-
store.markAccess([memory.id]);
|
|
156662
|
-
return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
|
|
156663
|
-
}
|
|
156664
|
-
async function selectLongTermMemoriesForTask(input) {
|
|
156665
|
-
const task = input.task?.trim();
|
|
156666
|
-
if (!task) return [];
|
|
156667
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
156668
|
-
const reader = {
|
|
156669
|
-
projectId: input.projectId,
|
|
156670
|
-
...owner ? { ownerId: owner.id } : {},
|
|
156671
|
-
...input.agentId ? { agentId: input.agentId } : {},
|
|
156672
|
-
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
156673
|
-
};
|
|
156674
|
-
const store = await createStore(input.dataDir);
|
|
156675
|
-
const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
156676
|
-
let selected = eligible.map((memory) => {
|
|
156677
|
-
const ranked = scoreForTask(memory, task);
|
|
156678
|
-
return ranked ? {
|
|
156679
|
-
memory,
|
|
156680
|
-
evidence: store.listEvidence(memory.id),
|
|
156681
|
-
score: ranked.score,
|
|
156682
|
-
reason: ranked.reason
|
|
156683
|
-
} : void 0;
|
|
156684
|
-
}).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));
|
|
156685
|
-
if (selected.length === 0 && eligible.length > 0) {
|
|
156686
|
-
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
156687
|
-
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
156688
|
-
if (provider2 && candidates.length > 0) {
|
|
156689
|
-
try {
|
|
156690
|
-
const vectors = await withTimeout(
|
|
156691
|
-
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
156692
|
-
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
156693
|
-
retry: false
|
|
156694
|
-
}),
|
|
156695
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
156696
|
-
"Long-term semantic retrieval"
|
|
156697
|
-
);
|
|
156698
|
-
const query = vectors[0];
|
|
156699
|
-
if (Array.isArray(query)) {
|
|
156700
|
-
selected = candidates.map((memory, index) => {
|
|
156701
|
-
const vector = vectors[index + 1];
|
|
156702
|
-
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
156703
|
-
return ranked ? {
|
|
156704
|
-
memory,
|
|
156705
|
-
evidence: store.listEvidence(memory.id),
|
|
156706
|
-
score: ranked.score,
|
|
156707
|
-
reason: ranked.reason
|
|
156708
|
-
} : void 0;
|
|
156709
|
-
}).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));
|
|
156710
|
-
}
|
|
156711
|
-
} catch {
|
|
156712
|
-
}
|
|
156713
|
-
}
|
|
156714
|
-
}
|
|
156715
|
-
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
156716
|
-
store.markAccess(selected.map((item) => item.memory.id));
|
|
156717
|
-
return selected;
|
|
156718
|
-
}
|
|
156719
|
-
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
156720
|
-
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
156721
|
-
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
156722
|
-
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
156723
|
-
}
|
|
156724
|
-
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
156725
|
-
var init_long_term = __esm({
|
|
156726
|
-
"src/memory/long-term.ts"() {
|
|
156727
|
-
"use strict";
|
|
156728
|
-
init_esm_shims();
|
|
156729
|
-
init_token_budget();
|
|
156730
|
-
init_timeout();
|
|
156731
|
-
init_secret_filter();
|
|
156732
|
-
init_owner();
|
|
156733
|
-
init_long_term_store();
|
|
156734
|
-
init_long_term_types();
|
|
156735
|
-
MAX_TEXT_LENGTH = 12e3;
|
|
156736
|
-
MAX_LIST_ITEMS = 40;
|
|
156737
|
-
MAX_SEMANTIC_CANDIDATES = 48;
|
|
156738
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
156739
|
-
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
156740
|
-
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
156741
|
-
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
156742
|
-
"the",
|
|
156743
|
-
"and",
|
|
156744
|
-
"for",
|
|
156745
|
-
"with",
|
|
156746
|
-
"from",
|
|
156747
|
-
"that",
|
|
156748
|
-
"this",
|
|
156749
|
-
"into",
|
|
156750
|
-
"when",
|
|
156751
|
-
"what",
|
|
156752
|
-
"\u5982\u4F55",
|
|
156753
|
-
"\u4EC0\u4E48",
|
|
156754
|
-
"\u8FD9\u4E2A",
|
|
156755
|
-
"\u90A3\u4E2A",
|
|
156756
|
-
"\u6211\u4EEC",
|
|
156757
|
-
"\u9879\u76EE",
|
|
156758
|
-
"\u8FDB\u884C",
|
|
156759
|
-
"\u9700\u8981",
|
|
156760
|
-
"\u7EE7\u7EED",
|
|
156761
|
-
"\u5904\u7406"
|
|
156762
|
-
]);
|
|
156763
|
-
}
|
|
156764
|
-
});
|
|
156765
|
-
|
|
156766
156912
|
// src/knowledge/workset.ts
|
|
156767
156913
|
function unique(values) {
|
|
156768
156914
|
return [...new Set(values.filter(Boolean))];
|
|
@@ -156970,6 +157116,37 @@ function renderTaskWorksetPrompt(input) {
|
|
|
156970
157116
|
trust: "source-backed"
|
|
156971
157117
|
});
|
|
156972
157118
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
157119
|
+
if (input.alwaysOn) {
|
|
157120
|
+
const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
|
|
157121
|
+
if (hasAlwaysOn) {
|
|
157122
|
+
appendLine(lines, "", maxTokens, omitted, "always-on-heading");
|
|
157123
|
+
appendLine(lines, "You and this workspace", maxTokens, omitted, "always-on-heading");
|
|
157124
|
+
for (const profile of input.alwaysOn.profile.slice(0, 2)) {
|
|
157125
|
+
appendLine(lines, "- " + short(profile, 26), maxTokens, omitted, "always-on-profile", selected, {
|
|
157126
|
+
kind: "memory",
|
|
157127
|
+
id: "profile",
|
|
157128
|
+
reason: "user profile memory",
|
|
157129
|
+
trust: "source-backed"
|
|
157130
|
+
});
|
|
157131
|
+
}
|
|
157132
|
+
if (input.alwaysOn.state) {
|
|
157133
|
+
appendLine(lines, "- Recently: " + short(input.alwaysOn.state, 40), maxTokens, omitted, "always-on-state", selected, {
|
|
157134
|
+
kind: "continuation",
|
|
157135
|
+
id: "workspace-state",
|
|
157136
|
+
reason: "latest session summary for this workspace",
|
|
157137
|
+
trust: "historical"
|
|
157138
|
+
});
|
|
157139
|
+
}
|
|
157140
|
+
for (const durable of input.alwaysOn.durable.slice(0, 2)) {
|
|
157141
|
+
appendLine(lines, "- " + short(durable, 28), maxTokens, omitted, "always-on-durable", selected, {
|
|
157142
|
+
kind: "durable-memory",
|
|
157143
|
+
id: "durable-always",
|
|
157144
|
+
reason: "recent durable memory for this workspace",
|
|
157145
|
+
trust: "source-backed"
|
|
157146
|
+
});
|
|
157147
|
+
}
|
|
157148
|
+
}
|
|
157149
|
+
}
|
|
156973
157150
|
const hasContinuation = Boolean(
|
|
156974
157151
|
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
|
|
156975
157152
|
);
|
|
@@ -157440,6 +157617,7 @@ async function buildTaskWorkset(input) {
|
|
|
157440
157617
|
version: "1.3",
|
|
157441
157618
|
task,
|
|
157442
157619
|
lens: input.lens,
|
|
157620
|
+
...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
|
|
157443
157621
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
157444
157622
|
...continuation ? { continuation } : {},
|
|
157445
157623
|
...input.codeState ? { codeState: short(input.codeState, 28) } : {},
|
|
@@ -158592,6 +158770,33 @@ function activeProjectObservations(observations2, projectId) {
|
|
|
158592
158770
|
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
158593
158771
|
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
158594
158772
|
}
|
|
158773
|
+
async function buildAlwaysOnBlock(input) {
|
|
158774
|
+
const profile = filterReadableObservations(input.eligible, input.reader).filter((observation) => observation.visibility === "personal" && observation.entityName === "user-profile").slice(0, 2).map((observation) => compactContinuationText(observation.title, 22));
|
|
158775
|
+
let state;
|
|
158776
|
+
try {
|
|
158777
|
+
await initSessionStore(input.dataDir);
|
|
158778
|
+
const { getSessionStore: getSessionStore2 } = await Promise.resolve().then(() => (init_session_store(), session_store_exports));
|
|
158779
|
+
const sessions = await getSessionStore2().loadByProject(input.projectId);
|
|
158780
|
+
const latest = sessions.filter((session) => session.endedAt && session.summary?.trim()).sort((a, b) => Date.parse(b.endedAt ?? "") - Date.parse(a.endedAt ?? ""))[0];
|
|
158781
|
+
if (latest?.summary) state = compactContinuationText(latest.summary, 40);
|
|
158782
|
+
} catch {
|
|
158783
|
+
}
|
|
158784
|
+
const durable = [];
|
|
158785
|
+
try {
|
|
158786
|
+
const items = await listLongTermMemories({
|
|
158787
|
+
dataDir: input.dataDir,
|
|
158788
|
+
reader: {
|
|
158789
|
+
projectId: input.projectId,
|
|
158790
|
+
...input.reader.agentId ? { agentId: input.reader.agentId } : {},
|
|
158791
|
+
...input.reader.isTeamMember ? { isTeamMember: true } : {}
|
|
158792
|
+
},
|
|
158793
|
+
limit: 10
|
|
158794
|
+
});
|
|
158795
|
+
durable.push(...items.filter((item) => item.memory.state === "approved").sort((a, b) => (b.memory.updatedAt ?? "").localeCompare(a.memory.updatedAt ?? "")).slice(0, 2).map((item) => item.memory.kind + ": " + compactContinuationText(item.memory.title, 22)));
|
|
158796
|
+
} catch {
|
|
158797
|
+
}
|
|
158798
|
+
return { profile, state, durable };
|
|
158799
|
+
}
|
|
158595
158800
|
function decideRefresh(input) {
|
|
158596
158801
|
if (input.mode === "never") {
|
|
158597
158802
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -158796,12 +159001,19 @@ async function buildAutoProjectContext(input) {
|
|
|
158796
159001
|
};
|
|
158797
159002
|
}
|
|
158798
159003
|
}
|
|
159004
|
+
const alwaysOn = await buildAlwaysOnBlock({
|
|
159005
|
+
dataDir: input.dataDir,
|
|
159006
|
+
projectId: input.project.id,
|
|
159007
|
+
reader: input.reader ?? { projectId: input.project.id },
|
|
159008
|
+
eligible: deliveryEligibleProjectObservations(input.observations, input.project.id)
|
|
159009
|
+
});
|
|
158799
159010
|
const workset = await buildTaskWorkset({
|
|
158800
159011
|
projectId: input.project.id,
|
|
158801
159012
|
dataDir: input.dataDir,
|
|
158802
159013
|
...task ? { task } : {},
|
|
158803
159014
|
lens: lens.id,
|
|
158804
159015
|
startHere,
|
|
159016
|
+
...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
|
|
158805
159017
|
...externalOutline ? { semanticCode: externalOutline } : {},
|
|
158806
159018
|
providerQuality: providerQuality2,
|
|
158807
159019
|
currentFacts: worksetFactLines(currentFacts),
|
|
@@ -158984,7 +159196,16 @@ function formatAutoProjectContextSummary(context) {
|
|
|
158984
159196
|
lensLine(context),
|
|
158985
159197
|
"",
|
|
158986
159198
|
...formatCurrentFactsLines(context.currentFacts),
|
|
158987
|
-
""
|
|
159199
|
+
""
|
|
159200
|
+
].filter(Boolean);
|
|
159201
|
+
if (context.workset.alwaysOn && (context.workset.alwaysOn.profile.length > 0 || context.workset.alwaysOn.state || context.workset.alwaysOn.durable.length > 0)) {
|
|
159202
|
+
lines.push("You and this workspace");
|
|
159203
|
+
for (const profile of context.workset.alwaysOn.profile.slice(0, 2)) lines.push(`- ${profile}`);
|
|
159204
|
+
if (context.workset.alwaysOn.state) lines.push(`- Recently: ${context.workset.alwaysOn.state}`);
|
|
159205
|
+
for (const durable of context.workset.alwaysOn.durable.slice(0, 2)) lines.push(`- ${durable}`);
|
|
159206
|
+
lines.push("");
|
|
159207
|
+
}
|
|
159208
|
+
lines.push(...[
|
|
158988
159209
|
`- Code memory: ${context.overview.code.files} files / ${context.overview.code.symbols} symbols / ${context.overview.code.refs} memory links`,
|
|
158989
159210
|
`- Code provider: ${context.providerQuality.selected} (${context.providerQuality.selectedQuality})`,
|
|
158990
159211
|
`- Languages: ${formatLanguages(context.overview)}`,
|
|
@@ -158993,7 +159214,7 @@ function formatAutoProjectContextSummary(context) {
|
|
|
158993
159214
|
`- Refresh: ${context.refresh.message}`,
|
|
158994
159215
|
"",
|
|
158995
159216
|
"Start here"
|
|
158996
|
-
].filter(Boolean);
|
|
159217
|
+
].filter(Boolean));
|
|
158997
159218
|
if (startHere.length > 0) {
|
|
158998
159219
|
startHere.forEach((path37, index) => lines.push(`${index + 1}. ${path37}`));
|
|
158999
159220
|
} else {
|
|
@@ -159117,6 +159338,8 @@ var init_auto_context = __esm({
|
|
|
159117
159338
|
init_store();
|
|
159118
159339
|
init_admission();
|
|
159119
159340
|
init_session();
|
|
159341
|
+
init_long_term();
|
|
159342
|
+
init_visibility();
|
|
159120
159343
|
init_session_store();
|
|
159121
159344
|
init_task_lens();
|
|
159122
159345
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
@@ -162067,8 +162290,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
162067
162290
|
const { promises: fsP } = await import("fs");
|
|
162068
162291
|
const registryPath = path30.join(baseDir, ".project-aliases.json");
|
|
162069
162292
|
const raw = await fsP.readFile(registryPath, "utf-8");
|
|
162070
|
-
const
|
|
162071
|
-
aliasGroups =
|
|
162293
|
+
const registry2 = JSON.parse(raw);
|
|
162294
|
+
aliasGroups = registry2.groups || [];
|
|
162072
162295
|
} catch {
|
|
162073
162296
|
}
|
|
162074
162297
|
const currentGroup = aliasGroups.find((g) => g.aliases?.includes(effectiveProjectId) || g.canonical === effectiveProjectId);
|
|
@@ -164984,13 +165207,28 @@ alwaysApply: true
|
|
|
164984
165207
|
'- For "why" decisions, use `memorix_store_reasoning`',
|
|
164985
165208
|
'- 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`.',
|
|
164986
165209
|
"- 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.",
|
|
165210
|
+
'- Record the user profile: the user\u2019s role, expertise, preferences, and goals. Save these with `entityName: "user-profile"` and `visibility: "personal"` so they stay private and appear in every brief as the "who you are" context.',
|
|
164987
165211
|
"",
|
|
164988
165212
|
"**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
|
|
164989
165213
|
"",
|
|
165214
|
+
"**Only store what a future session cannot re-derive.** Code structure, file contents, and Git history are live in the checkout \u2014 do not store facts already visible there. A memory earns its place by capturing the why, the context, or a conclusion the checkout alone cannot show.",
|
|
165215
|
+
"",
|
|
165216
|
+
`**Record what worked, not only what failed.** Store validated approaches and explicit user confirmations alongside corrections. Saving only failures drifts behavior away from what the user already accepted; a clear "yes, that's right" is feedback worth keeping too.`,
|
|
165217
|
+
"",
|
|
165218
|
+
"**Recalled memory is a claim about the past.** A memory naming a specific file, function, or flag describes the past at write time \u2014 check the file exists or grep the symbol before recommending it. If the user says to ignore or not use memory, proceed as if memory were empty: do not apply, cite, compare, or mention stored content.",
|
|
165219
|
+
"",
|
|
164990
165220
|
"## When to resolve memory",
|
|
164991
165221
|
"",
|
|
164992
165222
|
"Use `memorix_resolve` when a task is done or a bug is fixed. This keeps future searches focused on active work instead of surfacing completed items.",
|
|
164993
165223
|
"",
|
|
165224
|
+
"## End sessions with a summary",
|
|
165225
|
+
"",
|
|
165226
|
+
"When a session finishes, call `memorix_session_end` with a short structured summary so the next agent can resume. Recommended sections:",
|
|
165227
|
+
"- **Goal** \u2014 what this session was working on",
|
|
165228
|
+
"- **Discoveries** \u2014 findings, gotchas, learnings",
|
|
165229
|
+
"- **Accomplished** \u2014 completed items, plus PENDING items for the next session",
|
|
165230
|
+
"- **Relevant Files** \u2014 paths and what changed",
|
|
165231
|
+
"",
|
|
164994
165232
|
"## Tools quick reference",
|
|
164995
165233
|
"",
|
|
164996
165234
|
"| Tool | Use when |",
|
|
@@ -165541,6 +165779,12 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
165541
165779
|
kind: "consolidation",
|
|
165542
165780
|
dedupeKey: "startup-consolidation"
|
|
165543
165781
|
});
|
|
165782
|
+
lifecycle.enqueueLongTermMaintenance({
|
|
165783
|
+
dataDir: projectDir2,
|
|
165784
|
+
projectId: project.id,
|
|
165785
|
+
source: "startup",
|
|
165786
|
+
queue
|
|
165787
|
+
});
|
|
165544
165788
|
}
|
|
165545
165789
|
try {
|
|
165546
165790
|
const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
@@ -165590,7 +165834,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
165590
165834
|
};
|
|
165591
165835
|
const server = existingServer ?? new McpServer({
|
|
165592
165836
|
name: "memorix",
|
|
165593
|
-
version: true ? "1.
|
|
165837
|
+
version: true ? "1.5.0" : "1.0.1"
|
|
165594
165838
|
});
|
|
165595
165839
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
165596
165840
|
server.registerTool = ((name, ...args) => {
|
|
@@ -166075,7 +166319,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
166075
166319
|
let longTermNote = "";
|
|
166076
166320
|
if (longTerm) {
|
|
166077
166321
|
try {
|
|
166078
|
-
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
166322
|
+
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2, maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
166079
166323
|
const promoted = await promoteObservationToLongTermMemory2({
|
|
166080
166324
|
dataDir: projectDir2,
|
|
166081
166325
|
observation: obs,
|
|
@@ -166089,7 +166333,24 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
166089
166333
|
...reader.isTeamMember ? { isTeamMember: true } : {}
|
|
166090
166334
|
}
|
|
166091
166335
|
});
|
|
166092
|
-
|
|
166336
|
+
const autoQualified = await maybeAutoQualifyLongTermMemory2({
|
|
166337
|
+
dataDir: projectDir2,
|
|
166338
|
+
id: promoted.memory.id,
|
|
166339
|
+
sourceDetail: "explicit"
|
|
166340
|
+
});
|
|
166341
|
+
if (autoQualified) {
|
|
166342
|
+
try {
|
|
166343
|
+
const { enqueueLongTermMaintenance: enqueueLongTermMaintenance2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
166344
|
+
enqueueLongTermMaintenance2({
|
|
166345
|
+
dataDir: projectDir2,
|
|
166346
|
+
projectId: project.id,
|
|
166347
|
+
source: "long-term:" + promoted.memory.id
|
|
166348
|
+
});
|
|
166349
|
+
} catch {
|
|
166350
|
+
}
|
|
166351
|
+
}
|
|
166352
|
+
longTermNote = autoQualified ? `
|
|
166353
|
+
Long-term memory: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}, auto-qualified) and now enters briefs as durable context. Audit with \`memorix memory long-term list\`.` : `
|
|
166093
166354
|
Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
|
|
166094
166355
|
} catch (longTermError) {
|
|
166095
166356
|
longTermNote = `
|
|
@@ -166285,6 +166546,13 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166285
166546
|
const boundary = requireExplicitAutopilotExpansion("memorix_search", purpose);
|
|
166286
166547
|
if (boundary) return boundary;
|
|
166287
166548
|
}
|
|
166549
|
+
let surfacedKey = `project:${project.id}`;
|
|
166550
|
+
try {
|
|
166551
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
166552
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
166553
|
+
if (active) surfacedKey = `session:${active.id}`;
|
|
166554
|
+
} catch {
|
|
166555
|
+
}
|
|
166288
166556
|
return withFreshIndex(async () => {
|
|
166289
166557
|
const safeLimit2 = limit2 != null ? coerceNumber(limit2, 20) : void 0;
|
|
166290
166558
|
const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
|
|
@@ -166302,7 +166570,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166302
166570
|
status: status ?? "active",
|
|
166303
166571
|
source,
|
|
166304
166572
|
quality,
|
|
166305
|
-
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
166573
|
+
reader: getObservationReader(scope === "global" ? "global" : "project"),
|
|
166574
|
+
surfacedIds: getSurfacedIds(surfacedKey)
|
|
166306
166575
|
});
|
|
166307
166576
|
let result;
|
|
166308
166577
|
try {
|
|
@@ -166321,6 +166590,7 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166321
166590
|
}
|
|
166322
166591
|
throw error;
|
|
166323
166592
|
}
|
|
166593
|
+
recordSurfacedIds(surfacedKey, result.entries.map((entry) => entry.id));
|
|
166324
166594
|
const activeBoundary = getActiveAutopilotRetrievalBoundary();
|
|
166325
166595
|
if (scope !== "global" && activeBoundary && !force && result.entries.length > 0 && result.entries.every((entry) => activeBoundary.coveredObservationIds.has(entry.id))) {
|
|
166326
166596
|
const duplicate = blockCoveredAutopilotEvidence("memorix_search", result.entries.map((entry) => entry.id), force);
|
|
@@ -169283,13 +169553,26 @@ ${lines.join("\n")}` }] };
|
|
|
169283
169553
|
if (bytes.length >= 12 && Buffer.from(bytes.subarray(0, 4)).toString("ascii") === "RIFF" && Buffer.from(bytes.subarray(8, 12)).toString("ascii") === "WEBP") return "image/webp";
|
|
169284
169554
|
return void 0;
|
|
169285
169555
|
}
|
|
169556
|
+
const mediaGenerationEnabled = process.env.MEMORIX_MCP_MEDIA_GENERATION === "1";
|
|
169557
|
+
const mediaTranscriptionEnabled = process.env.MEMORIX_MCP_MEDIA_TRANSCRIPTION === "1";
|
|
169558
|
+
const mediaGenerationOrTranscription = mediaGenerationEnabled || mediaTranscriptionEnabled;
|
|
169286
169559
|
server.registerTool(
|
|
169287
169560
|
"memorix_media",
|
|
169288
169561
|
{
|
|
169289
169562
|
title: "Manage Controlled Media",
|
|
169290
169563
|
description: "Use the controlled local media library for an explicit import, attachment, inspection, or MiniMax generation request. Assets stay outside the Git worktree and enter normal memory only when attach is explicitly true. Use the CLI for destructive removal, quota cleanup, and direct generation. MCP generation is disabled by default and requires MEMORIX_MCP_MEDIA_GENERATION=1 after the operator reviews provider billing.",
|
|
169291
169564
|
inputSchema: {
|
|
169292
|
-
action: z626.enum([
|
|
169565
|
+
action: z626.enum([
|
|
169566
|
+
"import",
|
|
169567
|
+
"attach",
|
|
169568
|
+
"list",
|
|
169569
|
+
"show",
|
|
169570
|
+
"derive-pdf",
|
|
169571
|
+
"status",
|
|
169572
|
+
"cancel",
|
|
169573
|
+
...mediaTranscriptionEnabled ? ["derive-audio"] : [],
|
|
169574
|
+
...mediaGenerationEnabled ? ["generate-image", "generate-video"] : []
|
|
169575
|
+
]),
|
|
169293
169576
|
path: z626.string().optional().describe("Explicit local image/audio/video/PDF path for import."),
|
|
169294
169577
|
assetId: z626.string().optional().describe("Controlled MediaAsset ID for attach/show."),
|
|
169295
169578
|
jobId: z626.string().optional().describe("Durable media job ID for status."),
|
|
@@ -169297,20 +169580,26 @@ ${lines.join("\n")}` }] };
|
|
|
169297
169580
|
limit: z626.number().int().min(1).max(100).optional().describe("Maximum assets to list."),
|
|
169298
169581
|
title: z626.string().optional().describe("Observation title when attaching generated/imported output."),
|
|
169299
169582
|
narrative: z626.string().optional().describe("Short retrieval text when attaching an asset."),
|
|
169300
|
-
prompt: z626.string().optional().describe("Explicit MiniMax generation prompt."),
|
|
169301
|
-
image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
|
|
169302
|
-
model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
|
|
169303
|
-
provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
|
|
169304
|
-
language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription."),
|
|
169305
|
-
region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
|
|
169306
|
-
n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
|
|
169307
|
-
ratio: z626.enum(["adaptive", "1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]).optional().describe("Image or video aspect ratio."),
|
|
169308
|
-
width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
|
|
169309
|
-
height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
|
|
169310
|
-
duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds."),
|
|
169311
169583
|
maxPages: z626.number().int().min(1).max(100).optional().describe("Bounded PDF extraction page limit."),
|
|
169312
169584
|
maxChars: z626.number().int().min(1).max(6e4).optional().describe("Bounded PDF extraction character limit."),
|
|
169313
|
-
attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly.")
|
|
169585
|
+
attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly."),
|
|
169586
|
+
...mediaGenerationOrTranscription ? {
|
|
169587
|
+
model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
|
|
169588
|
+
prompt: z626.string().optional().describe("Explicit prompt for generation or transcription.")
|
|
169589
|
+
} : {},
|
|
169590
|
+
...mediaTranscriptionEnabled ? {
|
|
169591
|
+
provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
|
|
169592
|
+
language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription.")
|
|
169593
|
+
} : {},
|
|
169594
|
+
...mediaGenerationEnabled ? {
|
|
169595
|
+
image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
|
|
169596
|
+
region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
|
|
169597
|
+
n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
|
|
169598
|
+
ratio: z626.enum(["adaptive", "1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]).optional().describe("Image or video aspect ratio."),
|
|
169599
|
+
width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
|
|
169600
|
+
height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
|
|
169601
|
+
duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds.")
|
|
169602
|
+
} : {}
|
|
169314
169603
|
}
|
|
169315
169604
|
},
|
|
169316
169605
|
async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt, image, model, provider: provider2, language, region, n, ratio, width, height, duration, maxPages, maxChars, attach }) => {
|
|
@@ -169794,6 +170083,7 @@ var init_server2 = __esm({
|
|
|
169794
170083
|
init_formation_timeout();
|
|
169795
170084
|
init_timeout();
|
|
169796
170085
|
init_secret_filter();
|
|
170086
|
+
init_surfaced_registry();
|
|
169797
170087
|
init_request_context();
|
|
169798
170088
|
FORMATION_TIMEOUT_MS = parseFormationTimeoutMs(process.env.MEMORIX_FORMATION_TIMEOUT_MS);
|
|
169799
170089
|
COMPACT_ON_WRITE_TIMEOUT_MS = 12e3;
|
|
@@ -169923,7 +170213,7 @@ var init_serve_shared = __esm({
|
|
|
169923
170213
|
|
|
169924
170214
|
// src/cli/version.ts
|
|
169925
170215
|
function getCliVersion() {
|
|
169926
|
-
return true ? "1.
|
|
170216
|
+
return true ? "1.5.0" : pkg.version;
|
|
169927
170217
|
}
|
|
169928
170218
|
var init_version3 = __esm({
|
|
169929
170219
|
"src/cli/version.ts"() {
|