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/sdk.js
CHANGED
|
@@ -3377,6 +3377,7 @@ var init_resolved_config = __esm({
|
|
|
3377
3377
|
// src/config.ts
|
|
3378
3378
|
var config_exports = {};
|
|
3379
3379
|
__export(config_exports, {
|
|
3380
|
+
defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
|
|
3380
3381
|
getAgentLLMApiKey: () => getAgentLLMApiKey,
|
|
3381
3382
|
getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
|
|
3382
3383
|
getAgentLLMModel: () => getAgentLLMModel,
|
|
@@ -3393,7 +3394,6 @@ __export(config_exports, {
|
|
|
3393
3394
|
getLLMProvider: () => getLLMProvider,
|
|
3394
3395
|
getLoadedEnvFiles: () => getLoadedEnvFiles,
|
|
3395
3396
|
getServerConfig: () => getServerConfig,
|
|
3396
|
-
getTeamConfig: () => getTeamConfig,
|
|
3397
3397
|
loadDotenv: () => loadDotenv,
|
|
3398
3398
|
loadFileConfig: () => loadFileConfig,
|
|
3399
3399
|
loadYamlConfig: () => loadYamlConfig,
|
|
@@ -3440,7 +3440,19 @@ function getEmbeddingBaseUrl() {
|
|
|
3440
3440
|
return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
|
|
3441
3441
|
}
|
|
3442
3442
|
function getEmbeddingModel() {
|
|
3443
|
-
|
|
3443
|
+
const lane = getResolvedEmbeddingLane();
|
|
3444
|
+
return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
|
|
3445
|
+
}
|
|
3446
|
+
function defaultEmbeddingModelFor(baseUrl, explicitModel) {
|
|
3447
|
+
if (explicitModel) return explicitModel;
|
|
3448
|
+
try {
|
|
3449
|
+
const url = baseUrl ? new URL(baseUrl) : null;
|
|
3450
|
+
if (url && (url.hostname === "openrouter.ai" || url.hostname.endsWith(".openrouter.ai"))) {
|
|
3451
|
+
return "qwen/qwen3-embedding-8b";
|
|
3452
|
+
}
|
|
3453
|
+
} catch {
|
|
3454
|
+
}
|
|
3455
|
+
return "text-embedding-3-small";
|
|
3444
3456
|
}
|
|
3445
3457
|
function getEmbeddingDimensions() {
|
|
3446
3458
|
return getResolvedEmbeddingLane().dimensions ?? null;
|
|
@@ -3465,14 +3477,10 @@ function getServerConfig() {
|
|
|
3465
3477
|
port: server.port
|
|
3466
3478
|
};
|
|
3467
3479
|
}
|
|
3468
|
-
function getTeamConfig() {
|
|
3469
|
-
return loadYamlConfig().team ?? {};
|
|
3470
|
-
}
|
|
3471
3480
|
var init_config = __esm({
|
|
3472
3481
|
"src/config.ts"() {
|
|
3473
3482
|
"use strict";
|
|
3474
3483
|
init_esm_shims();
|
|
3475
|
-
init_yaml_loader();
|
|
3476
3484
|
init_resolved_config();
|
|
3477
3485
|
init_dotenv_loader();
|
|
3478
3486
|
init_legacy_loader();
|
|
@@ -5757,9 +5765,9 @@ async function saveRegistry(baseDir) {
|
|
|
5757
5765
|
await fs7.mkdir(path9.dirname(filePath), { recursive: true });
|
|
5758
5766
|
await fs7.writeFile(filePath, JSON.stringify(registryCache, null, 2), "utf-8");
|
|
5759
5767
|
}
|
|
5760
|
-
function findMatchingGroup(
|
|
5768
|
+
function findMatchingGroup(registry2, projectInfo) {
|
|
5761
5769
|
const normalizedRoot = normalizePath(projectInfo.rootPath);
|
|
5762
|
-
for (const group of
|
|
5770
|
+
for (const group of registry2.groups) {
|
|
5763
5771
|
if (group.aliases.includes(projectInfo.id)) return group;
|
|
5764
5772
|
if (group.rootPaths.some((rp) => rp === normalizedRoot)) return group;
|
|
5765
5773
|
if (projectInfo.gitRemote && group.gitRemote && group.gitRemote === projectInfo.gitRemote) {
|
|
@@ -5772,9 +5780,9 @@ function selectCanonical(aliases) {
|
|
|
5772
5780
|
return [...aliases].sort((a, b) => idPriority(b) - idPriority(a))[0];
|
|
5773
5781
|
}
|
|
5774
5782
|
async function registerAlias(projectInfo, baseDir) {
|
|
5775
|
-
const
|
|
5783
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5776
5784
|
const normalizedRoot = normalizePath(projectInfo.rootPath);
|
|
5777
|
-
const existingGroup = findMatchingGroup(
|
|
5785
|
+
const existingGroup = findMatchingGroup(registry2, projectInfo);
|
|
5778
5786
|
if (existingGroup) {
|
|
5779
5787
|
let changed = false;
|
|
5780
5788
|
if (!existingGroup.aliases.includes(projectInfo.id)) {
|
|
@@ -5805,13 +5813,13 @@ async function registerAlias(projectInfo, baseDir) {
|
|
|
5805
5813
|
rootPaths: [normalizedRoot],
|
|
5806
5814
|
...projectInfo.gitRemote ? { gitRemote: projectInfo.gitRemote } : {}
|
|
5807
5815
|
};
|
|
5808
|
-
|
|
5816
|
+
registry2.groups.push(newGroup);
|
|
5809
5817
|
await saveRegistry(baseDir);
|
|
5810
5818
|
return newGroup.canonical;
|
|
5811
5819
|
}
|
|
5812
5820
|
async function resolveAliases(projectId, baseDir) {
|
|
5813
|
-
const
|
|
5814
|
-
for (const group of
|
|
5821
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5822
|
+
for (const group of registry2.groups) {
|
|
5815
5823
|
if (group.aliases.includes(projectId) || group.canonical === projectId) {
|
|
5816
5824
|
return [...group.aliases];
|
|
5817
5825
|
}
|
|
@@ -5819,8 +5827,8 @@ async function resolveAliases(projectId, baseDir) {
|
|
|
5819
5827
|
return [projectId];
|
|
5820
5828
|
}
|
|
5821
5829
|
async function getCanonicalId(projectId, baseDir) {
|
|
5822
|
-
const
|
|
5823
|
-
for (const group of
|
|
5830
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5831
|
+
for (const group of registry2.groups) {
|
|
5824
5832
|
if (group.aliases.includes(projectId) || group.canonical === projectId) {
|
|
5825
5833
|
return group.canonical;
|
|
5826
5834
|
}
|
|
@@ -5828,12 +5836,12 @@ async function getCanonicalId(projectId, baseDir) {
|
|
|
5828
5836
|
return projectId;
|
|
5829
5837
|
}
|
|
5830
5838
|
async function getAllAliasGroups(baseDir) {
|
|
5831
|
-
const
|
|
5832
|
-
return
|
|
5839
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5840
|
+
return registry2.groups;
|
|
5833
5841
|
}
|
|
5834
5842
|
async function autoMergeByBaseName(observedIds, baseDir) {
|
|
5835
5843
|
if (observedIds.length <= 1) return 0;
|
|
5836
|
-
const
|
|
5844
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5837
5845
|
const byBaseName = /* @__PURE__ */ new Map();
|
|
5838
5846
|
for (const id of observedIds) {
|
|
5839
5847
|
const baseName = id.split("/").pop() ?? id;
|
|
@@ -5846,7 +5854,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5846
5854
|
const existingGroups = /* @__PURE__ */ new Set();
|
|
5847
5855
|
const ungroupedIds = [];
|
|
5848
5856
|
for (const id of ids) {
|
|
5849
|
-
const groupIdx =
|
|
5857
|
+
const groupIdx = registry2.groups.findIndex(
|
|
5850
5858
|
(g) => g.aliases.includes(id) || g.canonical === id
|
|
5851
5859
|
);
|
|
5852
5860
|
if (groupIdx >= 0) {
|
|
@@ -5860,7 +5868,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5860
5868
|
const canonical = selectCanonical(allIdsInGroup);
|
|
5861
5869
|
if (existingGroups.size > 0) {
|
|
5862
5870
|
const primaryIdx = [...existingGroups][0];
|
|
5863
|
-
const primaryGroup =
|
|
5871
|
+
const primaryGroup = registry2.groups[primaryIdx];
|
|
5864
5872
|
for (const id of allIdsInGroup) {
|
|
5865
5873
|
if (!primaryGroup.aliases.includes(id)) {
|
|
5866
5874
|
primaryGroup.aliases.push(id);
|
|
@@ -5868,7 +5876,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5868
5876
|
}
|
|
5869
5877
|
const otherIdxs = [...existingGroups].slice(1).sort((a, b) => b - a);
|
|
5870
5878
|
for (const idx of otherIdxs) {
|
|
5871
|
-
const other =
|
|
5879
|
+
const other = registry2.groups[idx];
|
|
5872
5880
|
for (const alias of other.aliases) {
|
|
5873
5881
|
if (!primaryGroup.aliases.includes(alias)) {
|
|
5874
5882
|
primaryGroup.aliases.push(alias);
|
|
@@ -5882,12 +5890,12 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5882
5890
|
if (other.gitRemote && !primaryGroup.gitRemote) {
|
|
5883
5891
|
primaryGroup.gitRemote = other.gitRemote;
|
|
5884
5892
|
}
|
|
5885
|
-
|
|
5893
|
+
registry2.groups.splice(idx, 1);
|
|
5886
5894
|
}
|
|
5887
5895
|
primaryGroup.canonical = selectCanonical(primaryGroup.aliases);
|
|
5888
5896
|
mergeCount++;
|
|
5889
5897
|
} else {
|
|
5890
|
-
|
|
5898
|
+
registry2.groups.push({
|
|
5891
5899
|
canonical,
|
|
5892
5900
|
aliases: allIdsInGroup,
|
|
5893
5901
|
rootPaths: []
|
|
@@ -6523,7 +6531,7 @@ async function searchObservations(options) {
|
|
|
6523
6531
|
const expandedEmbeddingQuery = quality === "thorough" && tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
|
|
6524
6532
|
mark("queryExpansion");
|
|
6525
6533
|
const intentResult = quality === "fast" || !hasQuery ? null : detectQueryIntent(originalQuery);
|
|
6526
|
-
const requestLimit = projectIds ? (options.limit ?? 20) * 3 : options.limit ?? 20;
|
|
6534
|
+
const requestLimit = projectIds || options.surfacedIds && options.surfacedIds.length > 0 ? (options.limit ?? 20) * 3 : options.limit ?? 20;
|
|
6527
6535
|
const defaultBoost = {
|
|
6528
6536
|
title: 3,
|
|
6529
6537
|
entityName: 2,
|
|
@@ -6755,6 +6763,14 @@ async function searchObservations(options) {
|
|
|
6755
6763
|
});
|
|
6756
6764
|
intermediate.sort((a, b) => b.score - a.score);
|
|
6757
6765
|
}
|
|
6766
|
+
if (options.surfacedIds && options.surfacedIds.length > 0) {
|
|
6767
|
+
const SURFACED_PENALTY = 0.5;
|
|
6768
|
+
const surfaced = new Set(options.surfacedIds);
|
|
6769
|
+
intermediate = intermediate.map(
|
|
6770
|
+
(entry) => surfaced.has(entry.id) ? { ...entry, score: (entry.score ?? 0) * SURFACED_PENALTY } : entry
|
|
6771
|
+
);
|
|
6772
|
+
intermediate.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
|
6773
|
+
}
|
|
6758
6774
|
if (options.since) {
|
|
6759
6775
|
const sinceDate = new Date(options.since).getTime();
|
|
6760
6776
|
intermediate = intermediate.filter((e2) => new Date(e2.rawTime).getTime() >= sinceDate);
|
|
@@ -7746,7 +7762,8 @@ var init_maintenance_jobs = __esm({
|
|
|
7746
7762
|
"claim-requalification",
|
|
7747
7763
|
"knowledge-compile",
|
|
7748
7764
|
"knowledge-lint",
|
|
7749
|
-
"workflow-index"
|
|
7765
|
+
"workflow-index",
|
|
7766
|
+
"long-term-maintenance"
|
|
7750
7767
|
];
|
|
7751
7768
|
DEFAULT_MAX_ATTEMPTS = 8;
|
|
7752
7769
|
DEFAULT_LEASE_MS = 3e4;
|
|
@@ -8155,6 +8172,7 @@ __export(lifecycle_exports, {
|
|
|
8155
8172
|
enqueueClaimRequalification: () => enqueueClaimRequalification,
|
|
8156
8173
|
enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
|
|
8157
8174
|
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
|
|
8175
|
+
enqueueLongTermMaintenance: () => enqueueLongTermMaintenance,
|
|
8158
8176
|
enqueueObservationQualification: () => enqueueObservationQualification
|
|
8159
8177
|
});
|
|
8160
8178
|
function queueFor(input) {
|
|
@@ -8198,6 +8216,14 @@ function enqueueObservationQualification(input) {
|
|
|
8198
8216
|
payload: { source: input.source, limit: 100 }
|
|
8199
8217
|
});
|
|
8200
8218
|
}
|
|
8219
|
+
function enqueueLongTermMaintenance(input) {
|
|
8220
|
+
queueFor(input).enqueue({
|
|
8221
|
+
projectId: input.projectId,
|
|
8222
|
+
kind: "long-term-maintenance",
|
|
8223
|
+
dedupeKey: "long-term-maintenance",
|
|
8224
|
+
payload: { source: input.source }
|
|
8225
|
+
});
|
|
8226
|
+
}
|
|
8201
8227
|
function enqueueKnowledgeFollowups(input) {
|
|
8202
8228
|
const queue = queueFor(input);
|
|
8203
8229
|
const payload = {
|
|
@@ -8377,7 +8403,8 @@ var init_isolated_maintenance = __esm({
|
|
|
8377
8403
|
"claim-requalification",
|
|
8378
8404
|
"knowledge-compile",
|
|
8379
8405
|
"knowledge-lint",
|
|
8380
|
-
"workflow-index"
|
|
8406
|
+
"workflow-index",
|
|
8407
|
+
"long-term-maintenance"
|
|
8381
8408
|
];
|
|
8382
8409
|
DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
8383
8410
|
MAX_CHILD_OUTPUT_BYTES = 1e6;
|
|
@@ -144224,6 +144251,889 @@ var init_retention = __esm({
|
|
|
144224
144251
|
}
|
|
144225
144252
|
});
|
|
144226
144253
|
|
|
144254
|
+
// src/memory/owner.ts
|
|
144255
|
+
var owner_exports = {};
|
|
144256
|
+
__export(owner_exports, {
|
|
144257
|
+
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
144258
|
+
});
|
|
144259
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
144260
|
+
import { promises as fs12 } from "fs";
|
|
144261
|
+
import path19 from "path";
|
|
144262
|
+
function ownerPath(dataDir) {
|
|
144263
|
+
const resolved = path19.resolve(dataDir);
|
|
144264
|
+
const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
|
|
144265
|
+
return path19.join(home, "memorix-user.json");
|
|
144266
|
+
}
|
|
144267
|
+
function normalizedOwnerId(value) {
|
|
144268
|
+
const trimmed = value?.trim();
|
|
144269
|
+
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
144270
|
+
}
|
|
144271
|
+
async function readOwnerFile(filePath) {
|
|
144272
|
+
try {
|
|
144273
|
+
const value = JSON.parse(await fs12.readFile(filePath, "utf8"));
|
|
144274
|
+
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
144275
|
+
return id ? { id, source: "local-file" } : void 0;
|
|
144276
|
+
} catch {
|
|
144277
|
+
return void 0;
|
|
144278
|
+
}
|
|
144279
|
+
}
|
|
144280
|
+
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
144281
|
+
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
144282
|
+
if (configured) return { id: configured, source: "environment" };
|
|
144283
|
+
const filePath = ownerPath(dataDir);
|
|
144284
|
+
const existing = await readOwnerFile(filePath);
|
|
144285
|
+
if (existing || !options.create) return existing;
|
|
144286
|
+
await fs12.mkdir(path19.dirname(filePath), { recursive: true });
|
|
144287
|
+
const created = {
|
|
144288
|
+
version: 1,
|
|
144289
|
+
id: "local-" + randomUUID6(),
|
|
144290
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
144291
|
+
};
|
|
144292
|
+
try {
|
|
144293
|
+
await fs12.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
144294
|
+
return { id: created.id, source: "local-file" };
|
|
144295
|
+
} catch (error) {
|
|
144296
|
+
if (error?.code !== "EEXIST") throw error;
|
|
144297
|
+
const concurrent = await readOwnerFile(filePath);
|
|
144298
|
+
if (concurrent) return concurrent;
|
|
144299
|
+
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
144300
|
+
}
|
|
144301
|
+
}
|
|
144302
|
+
var ID_PATTERN;
|
|
144303
|
+
var init_owner = __esm({
|
|
144304
|
+
"src/memory/owner.ts"() {
|
|
144305
|
+
"use strict";
|
|
144306
|
+
init_esm_shims();
|
|
144307
|
+
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
144308
|
+
}
|
|
144309
|
+
});
|
|
144310
|
+
|
|
144311
|
+
// src/memory/long-term-store.ts
|
|
144312
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
144313
|
+
function optionalText2(value) {
|
|
144314
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
144315
|
+
}
|
|
144316
|
+
function arrayValue2(value) {
|
|
144317
|
+
if (typeof value !== "string" || !value) return [];
|
|
144318
|
+
try {
|
|
144319
|
+
const parsed = JSON.parse(value);
|
|
144320
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
144321
|
+
} catch {
|
|
144322
|
+
return [];
|
|
144323
|
+
}
|
|
144324
|
+
}
|
|
144325
|
+
function rowToMemory(row) {
|
|
144326
|
+
return {
|
|
144327
|
+
id: row.id,
|
|
144328
|
+
originProjectId: row.originProjectId,
|
|
144329
|
+
ownerId: row.ownerId,
|
|
144330
|
+
scope: row.scope,
|
|
144331
|
+
kind: row.kind,
|
|
144332
|
+
state: row.state,
|
|
144333
|
+
portability: row.portability,
|
|
144334
|
+
title: row.title,
|
|
144335
|
+
content: row.content,
|
|
144336
|
+
facts: arrayValue2(row.factsJson),
|
|
144337
|
+
tags: arrayValue2(row.tagsJson),
|
|
144338
|
+
...optionalText2(row.applicability) ? { applicability: row.applicability } : {},
|
|
144339
|
+
origin: row.origin,
|
|
144340
|
+
createdAt: row.createdAt,
|
|
144341
|
+
updatedAt: row.updatedAt,
|
|
144342
|
+
...optionalText2(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
144343
|
+
...optionalText2(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
144344
|
+
...optionalText2(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
144345
|
+
...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
144346
|
+
...optionalText2(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
144347
|
+
accessCount: Number(row.accessCount ?? 0),
|
|
144348
|
+
...optionalText2(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
144349
|
+
};
|
|
144350
|
+
}
|
|
144351
|
+
function rowToEvidence(row) {
|
|
144352
|
+
return {
|
|
144353
|
+
id: row.id,
|
|
144354
|
+
memoryId: row.memoryId,
|
|
144355
|
+
kind: row.kind,
|
|
144356
|
+
referenceId: row.referenceId,
|
|
144357
|
+
relation: row.relation,
|
|
144358
|
+
...optionalText2(row.locator) ? { locator: row.locator } : {},
|
|
144359
|
+
...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
144360
|
+
createdAt: row.createdAt
|
|
144361
|
+
};
|
|
144362
|
+
}
|
|
144363
|
+
function rowToEvent(row) {
|
|
144364
|
+
return {
|
|
144365
|
+
id: row.id,
|
|
144366
|
+
memoryId: row.memoryId,
|
|
144367
|
+
kind: row.kind,
|
|
144368
|
+
...optionalText2(row.fromState) ? { fromState: row.fromState } : {},
|
|
144369
|
+
...optionalText2(row.toState) ? { toState: row.toState } : {},
|
|
144370
|
+
...optionalText2(row.detail) ? { detail: row.detail } : {},
|
|
144371
|
+
createdAt: row.createdAt
|
|
144372
|
+
};
|
|
144373
|
+
}
|
|
144374
|
+
var LongTermMemoryStore;
|
|
144375
|
+
var init_long_term_store = __esm({
|
|
144376
|
+
"src/memory/long-term-store.ts"() {
|
|
144377
|
+
"use strict";
|
|
144378
|
+
init_esm_shims();
|
|
144379
|
+
init_sqlite_db();
|
|
144380
|
+
LongTermMemoryStore = class {
|
|
144381
|
+
db = null;
|
|
144382
|
+
async init(dataDir) {
|
|
144383
|
+
this.db = getDatabase(dataDir);
|
|
144384
|
+
}
|
|
144385
|
+
transaction(fn) {
|
|
144386
|
+
return this.db.transaction(fn)();
|
|
144387
|
+
}
|
|
144388
|
+
get(id) {
|
|
144389
|
+
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
144390
|
+
return row ? rowToMemory(row) : void 0;
|
|
144391
|
+
}
|
|
144392
|
+
list(options = {}) {
|
|
144393
|
+
const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
144394
|
+
const clauses = [];
|
|
144395
|
+
const params = [];
|
|
144396
|
+
if (options.ownerId) {
|
|
144397
|
+
clauses.push("ownerId = ?");
|
|
144398
|
+
params.push(options.ownerId);
|
|
144399
|
+
}
|
|
144400
|
+
if (options.originProjectId) {
|
|
144401
|
+
clauses.push("originProjectId = ?");
|
|
144402
|
+
params.push(options.originProjectId);
|
|
144403
|
+
}
|
|
144404
|
+
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
144405
|
+
const rows = this.db.prepare(
|
|
144406
|
+
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
144407
|
+
).all(...params, limit2);
|
|
144408
|
+
return rows.map(rowToMemory);
|
|
144409
|
+
}
|
|
144410
|
+
insert(memory) {
|
|
144411
|
+
this.db.prepare(`
|
|
144412
|
+
INSERT INTO long_term_memories (
|
|
144413
|
+
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
144414
|
+
title, content, factsJson, tagsJson, applicability, origin,
|
|
144415
|
+
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
144416
|
+
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
144417
|
+
) VALUES (
|
|
144418
|
+
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
144419
|
+
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
144420
|
+
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
144421
|
+
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
144422
|
+
)
|
|
144423
|
+
`).run({
|
|
144424
|
+
...memory,
|
|
144425
|
+
factsJson: JSON.stringify(memory.facts),
|
|
144426
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
144427
|
+
applicability: memory.applicability ?? null,
|
|
144428
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
144429
|
+
approvedAt: memory.approvedAt ?? null,
|
|
144430
|
+
archivedAt: memory.archivedAt ?? null,
|
|
144431
|
+
supersededBy: memory.supersededBy ?? null,
|
|
144432
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
144433
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
144434
|
+
});
|
|
144435
|
+
}
|
|
144436
|
+
update(memory) {
|
|
144437
|
+
this.db.prepare(`
|
|
144438
|
+
UPDATE long_term_memories SET
|
|
144439
|
+
scope = @scope,
|
|
144440
|
+
kind = @kind,
|
|
144441
|
+
state = @state,
|
|
144442
|
+
portability = @portability,
|
|
144443
|
+
title = @title,
|
|
144444
|
+
content = @content,
|
|
144445
|
+
factsJson = @factsJson,
|
|
144446
|
+
tagsJson = @tagsJson,
|
|
144447
|
+
applicability = @applicability,
|
|
144448
|
+
updatedAt = @updatedAt,
|
|
144449
|
+
qualifiedAt = @qualifiedAt,
|
|
144450
|
+
approvedAt = @approvedAt,
|
|
144451
|
+
archivedAt = @archivedAt,
|
|
144452
|
+
supersededBy = @supersededBy,
|
|
144453
|
+
lastValidatedAt = @lastValidatedAt,
|
|
144454
|
+
accessCount = @accessCount,
|
|
144455
|
+
lastAccessedAt = @lastAccessedAt
|
|
144456
|
+
WHERE id = @id
|
|
144457
|
+
`).run({
|
|
144458
|
+
...memory,
|
|
144459
|
+
factsJson: JSON.stringify(memory.facts),
|
|
144460
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
144461
|
+
applicability: memory.applicability ?? null,
|
|
144462
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
144463
|
+
approvedAt: memory.approvedAt ?? null,
|
|
144464
|
+
archivedAt: memory.archivedAt ?? null,
|
|
144465
|
+
supersededBy: memory.supersededBy ?? null,
|
|
144466
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
144467
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
144468
|
+
});
|
|
144469
|
+
}
|
|
144470
|
+
insertEvidence(input) {
|
|
144471
|
+
const evidence = {
|
|
144472
|
+
id: randomUUID7(),
|
|
144473
|
+
...input,
|
|
144474
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
144475
|
+
};
|
|
144476
|
+
this.db.prepare(`
|
|
144477
|
+
INSERT INTO long_term_memory_evidence (
|
|
144478
|
+
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
144479
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
144480
|
+
`).run(
|
|
144481
|
+
evidence.id,
|
|
144482
|
+
evidence.memoryId,
|
|
144483
|
+
evidence.kind,
|
|
144484
|
+
evidence.referenceId,
|
|
144485
|
+
evidence.relation,
|
|
144486
|
+
evidence.locator ?? null,
|
|
144487
|
+
evidence.capturedHash ?? null,
|
|
144488
|
+
evidence.createdAt
|
|
144489
|
+
);
|
|
144490
|
+
return evidence;
|
|
144491
|
+
}
|
|
144492
|
+
listEvidence(memoryId) {
|
|
144493
|
+
return this.db.prepare(`
|
|
144494
|
+
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
144495
|
+
`).all(memoryId).map(rowToEvidence);
|
|
144496
|
+
}
|
|
144497
|
+
recordEvent(input) {
|
|
144498
|
+
const event = {
|
|
144499
|
+
id: randomUUID7(),
|
|
144500
|
+
...input,
|
|
144501
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
144502
|
+
};
|
|
144503
|
+
this.db.prepare(`
|
|
144504
|
+
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
144505
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
144506
|
+
`).run(
|
|
144507
|
+
event.id,
|
|
144508
|
+
event.memoryId,
|
|
144509
|
+
event.kind,
|
|
144510
|
+
event.fromState ?? null,
|
|
144511
|
+
event.toState ?? null,
|
|
144512
|
+
event.detail ?? null,
|
|
144513
|
+
event.createdAt
|
|
144514
|
+
);
|
|
144515
|
+
return event;
|
|
144516
|
+
}
|
|
144517
|
+
listEvents(memoryId) {
|
|
144518
|
+
return this.db.prepare(`
|
|
144519
|
+
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
144520
|
+
`).all(memoryId).map(rowToEvent);
|
|
144521
|
+
}
|
|
144522
|
+
markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
144523
|
+
const unique2 = [...new Set(ids.filter(Boolean))];
|
|
144524
|
+
if (unique2.length === 0) return;
|
|
144525
|
+
const statement = this.db.prepare(`
|
|
144526
|
+
UPDATE long_term_memories
|
|
144527
|
+
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
144528
|
+
WHERE id = ?
|
|
144529
|
+
`);
|
|
144530
|
+
this.db.transaction(() => {
|
|
144531
|
+
for (const id of unique2) statement.run(at, id);
|
|
144532
|
+
})();
|
|
144533
|
+
}
|
|
144534
|
+
};
|
|
144535
|
+
}
|
|
144536
|
+
});
|
|
144537
|
+
|
|
144538
|
+
// src/memory/long-term-types.ts
|
|
144539
|
+
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
144540
|
+
var init_long_term_types = __esm({
|
|
144541
|
+
"src/memory/long-term-types.ts"() {
|
|
144542
|
+
"use strict";
|
|
144543
|
+
init_esm_shims();
|
|
144544
|
+
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
144545
|
+
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
144546
|
+
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
144547
|
+
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
144548
|
+
"observation",
|
|
144549
|
+
"claim",
|
|
144550
|
+
"workflow",
|
|
144551
|
+
"session",
|
|
144552
|
+
"checkpoint",
|
|
144553
|
+
"git",
|
|
144554
|
+
"code",
|
|
144555
|
+
"test",
|
|
144556
|
+
"run",
|
|
144557
|
+
"document",
|
|
144558
|
+
"manual",
|
|
144559
|
+
"user"
|
|
144560
|
+
];
|
|
144561
|
+
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
144562
|
+
"supports",
|
|
144563
|
+
"verifies",
|
|
144564
|
+
"derives",
|
|
144565
|
+
"user-confirmed"
|
|
144566
|
+
];
|
|
144567
|
+
}
|
|
144568
|
+
});
|
|
144569
|
+
|
|
144570
|
+
// src/memory/long-term.ts
|
|
144571
|
+
var long_term_exports = {};
|
|
144572
|
+
__export(long_term_exports, {
|
|
144573
|
+
approveLongTermMemory: () => approveLongTermMemory,
|
|
144574
|
+
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
144575
|
+
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
144576
|
+
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
144577
|
+
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
144578
|
+
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
144579
|
+
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
144580
|
+
listLongTermMemories: () => listLongTermMemories,
|
|
144581
|
+
maintainLongTermMemories: () => maintainLongTermMemories,
|
|
144582
|
+
maybeAutoQualifyLongTermMemory: () => maybeAutoQualifyLongTermMemory,
|
|
144583
|
+
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
144584
|
+
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
144585
|
+
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
144586
|
+
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
144587
|
+
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
144588
|
+
});
|
|
144589
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
144590
|
+
function isOneOf(value, values) {
|
|
144591
|
+
return typeof value === "string" && values.includes(value);
|
|
144592
|
+
}
|
|
144593
|
+
function compactText(value, field, max = MAX_TEXT_LENGTH) {
|
|
144594
|
+
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
144595
|
+
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
144596
|
+
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
144597
|
+
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
144598
|
+
return safe;
|
|
144599
|
+
}
|
|
144600
|
+
function compactList(values, field) {
|
|
144601
|
+
if (!values) return [];
|
|
144602
|
+
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
144603
|
+
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
144604
|
+
}
|
|
144605
|
+
function normalizeEvidence(input) {
|
|
144606
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
144607
|
+
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
144608
|
+
}
|
|
144609
|
+
return input.map((evidence) => {
|
|
144610
|
+
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
144611
|
+
throw new Error("Long-term memory evidence kind is invalid.");
|
|
144612
|
+
}
|
|
144613
|
+
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
144614
|
+
throw new Error("Long-term memory evidence relation is invalid.");
|
|
144615
|
+
}
|
|
144616
|
+
return {
|
|
144617
|
+
kind: evidence.kind,
|
|
144618
|
+
referenceId: compactText(evidence.referenceId, "evidence reference", 1e3),
|
|
144619
|
+
relation: evidence.relation,
|
|
144620
|
+
...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator", 1e3) } : {},
|
|
144621
|
+
...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
144622
|
+
};
|
|
144623
|
+
});
|
|
144624
|
+
}
|
|
144625
|
+
function assertScope(scope) {
|
|
144626
|
+
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
144627
|
+
}
|
|
144628
|
+
function assertKind(kind) {
|
|
144629
|
+
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
144630
|
+
}
|
|
144631
|
+
function assertPortability(value) {
|
|
144632
|
+
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
144633
|
+
throw new Error("Long-term memory portability is invalid.");
|
|
144634
|
+
}
|
|
144635
|
+
}
|
|
144636
|
+
function assertPortableEvidence(scope, portability, evidence) {
|
|
144637
|
+
if (portability !== "portable") return;
|
|
144638
|
+
if (scope !== "user") {
|
|
144639
|
+
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
144640
|
+
}
|
|
144641
|
+
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
144642
|
+
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.");
|
|
144643
|
+
}
|
|
144644
|
+
}
|
|
144645
|
+
function requireTeamReader(scope, reader) {
|
|
144646
|
+
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
144647
|
+
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
144648
|
+
}
|
|
144649
|
+
}
|
|
144650
|
+
function matchesScope(memory, reader) {
|
|
144651
|
+
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
144652
|
+
if (memory.scope === "team") {
|
|
144653
|
+
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
144654
|
+
}
|
|
144655
|
+
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
144656
|
+
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
144657
|
+
}
|
|
144658
|
+
function canReadLongTermMemory(memory, reader) {
|
|
144659
|
+
return matchesScope(memory, reader);
|
|
144660
|
+
}
|
|
144661
|
+
function isEligibleLongTermMemory(memory) {
|
|
144662
|
+
return memory.state === "qualified" || memory.state === "approved";
|
|
144663
|
+
}
|
|
144664
|
+
function taskTerms(task) {
|
|
144665
|
+
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
144666
|
+
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
144667
|
+
}
|
|
144668
|
+
function kindFitScore(kind, task) {
|
|
144669
|
+
const normalized = task.toLocaleLowerCase("en-US");
|
|
144670
|
+
const hints = {
|
|
144671
|
+
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
144672
|
+
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
144673
|
+
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
144674
|
+
};
|
|
144675
|
+
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
144676
|
+
}
|
|
144677
|
+
function validationFreshnessScore(memory) {
|
|
144678
|
+
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
144679
|
+
if (!validatedAt) return 0;
|
|
144680
|
+
const time = Date.parse(validatedAt);
|
|
144681
|
+
if (!Number.isFinite(time)) return 0;
|
|
144682
|
+
const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
|
|
144683
|
+
if (ageDays <= 30) return 0.35;
|
|
144684
|
+
if (ageDays <= 90) return 0.15;
|
|
144685
|
+
return 0;
|
|
144686
|
+
}
|
|
144687
|
+
function scoreForTask(memory, task) {
|
|
144688
|
+
const terms = taskTerms(task);
|
|
144689
|
+
if (terms.length === 0) return void 0;
|
|
144690
|
+
const title = memory.title.toLocaleLowerCase("en-US");
|
|
144691
|
+
const content = memory.content.toLocaleLowerCase("en-US");
|
|
144692
|
+
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
144693
|
+
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
144694
|
+
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
144695
|
+
let score = memory.state === "approved" ? 0.5 : 0;
|
|
144696
|
+
const matched = [];
|
|
144697
|
+
for (const term of terms) {
|
|
144698
|
+
let termScore = 0;
|
|
144699
|
+
if (title.includes(term)) termScore += 5;
|
|
144700
|
+
if (tags.includes(term)) termScore += 4;
|
|
144701
|
+
if (applicability.includes(term)) termScore += 3;
|
|
144702
|
+
if (facts.includes(term)) termScore += 2;
|
|
144703
|
+
if (content.includes(term)) termScore += 1;
|
|
144704
|
+
if (termScore > 0) {
|
|
144705
|
+
score += termScore;
|
|
144706
|
+
matched.push(term);
|
|
144707
|
+
}
|
|
144708
|
+
}
|
|
144709
|
+
if (matched.length === 0) return void 0;
|
|
144710
|
+
score += kindFitScore(memory.kind, task);
|
|
144711
|
+
score += validationFreshnessScore(memory);
|
|
144712
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
144713
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
144714
|
+
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
144715
|
+
}
|
|
144716
|
+
function semanticText(memory) {
|
|
144717
|
+
return sanitizeCredentials([
|
|
144718
|
+
memory.title,
|
|
144719
|
+
memory.applicability,
|
|
144720
|
+
memory.tags.join(" "),
|
|
144721
|
+
memory.facts.join(" "),
|
|
144722
|
+
memory.content
|
|
144723
|
+
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
144724
|
+
}
|
|
144725
|
+
function cosineSimilarity(left, right) {
|
|
144726
|
+
if (left.length === 0 || left.length !== right.length) return 0;
|
|
144727
|
+
let dot = 0;
|
|
144728
|
+
let leftMagnitude = 0;
|
|
144729
|
+
let rightMagnitude = 0;
|
|
144730
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
144731
|
+
const a = left[index];
|
|
144732
|
+
const b = right[index];
|
|
144733
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
|
|
144734
|
+
dot += a * b;
|
|
144735
|
+
leftMagnitude += a * a;
|
|
144736
|
+
rightMagnitude += b * b;
|
|
144737
|
+
}
|
|
144738
|
+
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
144739
|
+
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
144740
|
+
}
|
|
144741
|
+
function semanticSelection(memory, similarity, task) {
|
|
144742
|
+
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
144743
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
144744
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
144745
|
+
return {
|
|
144746
|
+
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
144747
|
+
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
144748
|
+
};
|
|
144749
|
+
}
|
|
144750
|
+
async function resolveSemanticProvider(explicit) {
|
|
144751
|
+
if (explicit !== void 0) return explicit;
|
|
144752
|
+
try {
|
|
144753
|
+
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
144754
|
+
return await withTimeout(
|
|
144755
|
+
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
144756
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
144757
|
+
"Long-term semantic provider initialization"
|
|
144758
|
+
);
|
|
144759
|
+
} catch {
|
|
144760
|
+
return null;
|
|
144761
|
+
}
|
|
144762
|
+
}
|
|
144763
|
+
function createStore(dataDir) {
|
|
144764
|
+
const store = new LongTermMemoryStore();
|
|
144765
|
+
return store.init(dataDir).then(() => store);
|
|
144766
|
+
}
|
|
144767
|
+
async function createManualLongTermMemory(input) {
|
|
144768
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
144769
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
144770
|
+
assertScope(input.scope);
|
|
144771
|
+
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
|
|
144772
|
+
return createLongTermMemoryWithDataDir({
|
|
144773
|
+
dataDir: input.dataDir,
|
|
144774
|
+
originProjectId: input.projectId,
|
|
144775
|
+
ownerId: owner.id,
|
|
144776
|
+
scope: input.scope,
|
|
144777
|
+
kind: input.kind,
|
|
144778
|
+
portability: input.portability,
|
|
144779
|
+
title: input.title,
|
|
144780
|
+
content: input.content,
|
|
144781
|
+
facts: input.facts,
|
|
144782
|
+
tags: input.tags,
|
|
144783
|
+
applicability: input.applicability,
|
|
144784
|
+
origin: "manual",
|
|
144785
|
+
evidence,
|
|
144786
|
+
reader: input.reader
|
|
144787
|
+
});
|
|
144788
|
+
}
|
|
144789
|
+
async function promoteObservationToLongTermMemory(input) {
|
|
144790
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
144791
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
144792
|
+
return createLongTermMemoryWithDataDir({
|
|
144793
|
+
dataDir: input.dataDir,
|
|
144794
|
+
originProjectId: input.observation.projectId,
|
|
144795
|
+
ownerId: owner.id,
|
|
144796
|
+
scope: input.scope,
|
|
144797
|
+
kind: input.kind,
|
|
144798
|
+
portability: "project-bound",
|
|
144799
|
+
title: input.observation.title,
|
|
144800
|
+
content: input.observation.narrative,
|
|
144801
|
+
facts: input.observation.facts,
|
|
144802
|
+
tags: input.tags ?? input.observation.concepts,
|
|
144803
|
+
applicability: input.applicability,
|
|
144804
|
+
origin: input.observation.source === "git" ? "git" : "agent",
|
|
144805
|
+
evidence: [{
|
|
144806
|
+
kind: "observation",
|
|
144807
|
+
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
144808
|
+
relation: "derives"
|
|
144809
|
+
}],
|
|
144810
|
+
reader: input.reader
|
|
144811
|
+
});
|
|
144812
|
+
}
|
|
144813
|
+
async function createLongTermMemoryCandidate(input) {
|
|
144814
|
+
return createLongTermMemoryWithDataDir(input);
|
|
144815
|
+
}
|
|
144816
|
+
async function createLongTermMemoryWithDataDir(input) {
|
|
144817
|
+
assertScope(input.scope);
|
|
144818
|
+
assertKind(input.kind);
|
|
144819
|
+
const portability = input.portability ?? "project-bound";
|
|
144820
|
+
assertPortability(portability);
|
|
144821
|
+
requireTeamReader(input.scope, input.reader);
|
|
144822
|
+
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
144823
|
+
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
144824
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144825
|
+
const memory = {
|
|
144826
|
+
id: randomUUID8(),
|
|
144827
|
+
originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
|
|
144828
|
+
ownerId: compactText(input.ownerId, "owner id", 160),
|
|
144829
|
+
scope: input.scope,
|
|
144830
|
+
kind: input.kind,
|
|
144831
|
+
state: "candidate",
|
|
144832
|
+
portability,
|
|
144833
|
+
title: compactText(input.title, "title", 500),
|
|
144834
|
+
content: compactText(input.content, "content"),
|
|
144835
|
+
facts: compactList(input.facts, "facts"),
|
|
144836
|
+
tags: compactList(input.tags, "tags"),
|
|
144837
|
+
...input.applicability ? { applicability: compactText(input.applicability, "applicability", 1e3) } : {},
|
|
144838
|
+
origin: input.origin ?? "manual",
|
|
144839
|
+
createdAt: now3,
|
|
144840
|
+
updatedAt: now3,
|
|
144841
|
+
accessCount: 0
|
|
144842
|
+
};
|
|
144843
|
+
const store = await createStore(input.dataDir);
|
|
144844
|
+
const evidence = store.transaction(() => {
|
|
144845
|
+
store.insert(memory);
|
|
144846
|
+
const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
144847
|
+
store.recordEvent({
|
|
144848
|
+
memoryId: memory.id,
|
|
144849
|
+
kind: "created",
|
|
144850
|
+
toState: "candidate",
|
|
144851
|
+
detail: "Created as a source-backed long-term memory candidate.",
|
|
144852
|
+
createdAt: now3
|
|
144853
|
+
});
|
|
144854
|
+
return written;
|
|
144855
|
+
});
|
|
144856
|
+
return { memory, evidence };
|
|
144857
|
+
}
|
|
144858
|
+
function transitionDetail(value) {
|
|
144859
|
+
return compactText(value, "transition reason", 1e3);
|
|
144860
|
+
}
|
|
144861
|
+
async function transitionMemory(input) {
|
|
144862
|
+
const store = await createStore(input.dataDir);
|
|
144863
|
+
return store.transaction(() => {
|
|
144864
|
+
const current = store.get(input.id);
|
|
144865
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144866
|
+
if (current.state !== input.expected) {
|
|
144867
|
+
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
144868
|
+
}
|
|
144869
|
+
const evidence = store.listEvidence(current.id);
|
|
144870
|
+
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
144871
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144872
|
+
const next = {
|
|
144873
|
+
...current,
|
|
144874
|
+
state: input.next,
|
|
144875
|
+
updatedAt: now3,
|
|
144876
|
+
lastValidatedAt: now3,
|
|
144877
|
+
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
144878
|
+
};
|
|
144879
|
+
store.update(next);
|
|
144880
|
+
store.recordEvent({
|
|
144881
|
+
memoryId: next.id,
|
|
144882
|
+
kind: input.event,
|
|
144883
|
+
fromState: current.state,
|
|
144884
|
+
toState: next.state,
|
|
144885
|
+
detail: transitionDetail(input.reason),
|
|
144886
|
+
createdAt: now3
|
|
144887
|
+
});
|
|
144888
|
+
return next;
|
|
144889
|
+
});
|
|
144890
|
+
}
|
|
144891
|
+
async function qualifyLongTermMemory(input) {
|
|
144892
|
+
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
144893
|
+
}
|
|
144894
|
+
async function approveLongTermMemory(input) {
|
|
144895
|
+
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
144896
|
+
}
|
|
144897
|
+
async function archiveLongTermMemory(input) {
|
|
144898
|
+
const store = await createStore(input.dataDir);
|
|
144899
|
+
return store.transaction(() => {
|
|
144900
|
+
const current = store.get(input.id);
|
|
144901
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144902
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
144903
|
+
throw new Error("Long-term memory is already inactive.");
|
|
144904
|
+
}
|
|
144905
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144906
|
+
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
144907
|
+
store.update(next);
|
|
144908
|
+
store.recordEvent({
|
|
144909
|
+
memoryId: next.id,
|
|
144910
|
+
kind: "archived",
|
|
144911
|
+
fromState: current.state,
|
|
144912
|
+
toState: "archived",
|
|
144913
|
+
detail: transitionDetail(input.reason),
|
|
144914
|
+
createdAt: now3
|
|
144915
|
+
});
|
|
144916
|
+
return next;
|
|
144917
|
+
});
|
|
144918
|
+
}
|
|
144919
|
+
async function supersedeLongTermMemory(input) {
|
|
144920
|
+
const store = await createStore(input.dataDir);
|
|
144921
|
+
return store.transaction(() => {
|
|
144922
|
+
const current = store.get(input.id);
|
|
144923
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144924
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
144925
|
+
throw new Error("Long-term memory is already inactive.");
|
|
144926
|
+
}
|
|
144927
|
+
if (current.id === input.supersededBy) {
|
|
144928
|
+
throw new Error("Long-term memory cannot supersede itself.");
|
|
144929
|
+
}
|
|
144930
|
+
const replacement = store.get(input.supersededBy);
|
|
144931
|
+
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
144932
|
+
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
144933
|
+
}
|
|
144934
|
+
if (replacement.scope !== current.scope) {
|
|
144935
|
+
throw new Error("Replacement long-term memory must use the same scope.");
|
|
144936
|
+
}
|
|
144937
|
+
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
144938
|
+
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
144939
|
+
}
|
|
144940
|
+
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
144941
|
+
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
144942
|
+
}
|
|
144943
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144944
|
+
const next = {
|
|
144945
|
+
...current,
|
|
144946
|
+
state: "superseded",
|
|
144947
|
+
supersededBy: replacement.id,
|
|
144948
|
+
updatedAt: now3
|
|
144949
|
+
};
|
|
144950
|
+
store.update(next);
|
|
144951
|
+
store.recordEvent({
|
|
144952
|
+
memoryId: next.id,
|
|
144953
|
+
kind: "superseded",
|
|
144954
|
+
fromState: current.state,
|
|
144955
|
+
toState: "superseded",
|
|
144956
|
+
detail: transitionDetail(input.reason),
|
|
144957
|
+
createdAt: now3
|
|
144958
|
+
});
|
|
144959
|
+
return next;
|
|
144960
|
+
});
|
|
144961
|
+
}
|
|
144962
|
+
async function maybeAutoQualifyLongTermMemory(input) {
|
|
144963
|
+
if (input.source !== "manual" && input.sourceDetail !== "explicit") return null;
|
|
144964
|
+
try {
|
|
144965
|
+
return await qualifyLongTermMemory({
|
|
144966
|
+
dataDir: input.dataDir,
|
|
144967
|
+
id: input.id,
|
|
144968
|
+
reason: "auto-qualified: explicit request carries its own source evidence"
|
|
144969
|
+
});
|
|
144970
|
+
} catch {
|
|
144971
|
+
return null;
|
|
144972
|
+
}
|
|
144973
|
+
}
|
|
144974
|
+
async function maintainLongTermMemories(input) {
|
|
144975
|
+
const now3 = input.now ?? /* @__PURE__ */ new Date();
|
|
144976
|
+
const decayDays = input.decayDays ?? 30;
|
|
144977
|
+
const cutoff = now3.getTime() - decayDays * 24 * 60 * 60 * 1e3;
|
|
144978
|
+
const result = { archived: 0, superseded: 0 };
|
|
144979
|
+
const store = await createStore(input.dataDir);
|
|
144980
|
+
const all = store.list({ originProjectId: input.projectId });
|
|
144981
|
+
for (const memory of all) {
|
|
144982
|
+
if (memory.state !== "qualified" && memory.state !== "approved") continue;
|
|
144983
|
+
const lastActivity = Date.parse(memory.lastAccessedAt ?? memory.updatedAt);
|
|
144984
|
+
if (!Number.isFinite(lastActivity) || lastActivity >= cutoff) continue;
|
|
144985
|
+
try {
|
|
144986
|
+
await archiveLongTermMemory({
|
|
144987
|
+
dataDir: input.dataDir,
|
|
144988
|
+
id: memory.id,
|
|
144989
|
+
reason: "auto-archive: no activity since " + new Date(lastActivity).toISOString().slice(0, 10)
|
|
144990
|
+
});
|
|
144991
|
+
result.archived += 1;
|
|
144992
|
+
} catch {
|
|
144993
|
+
}
|
|
144994
|
+
}
|
|
144995
|
+
const groups = /* @__PURE__ */ new Map();
|
|
144996
|
+
for (const memory of store.list({ originProjectId: input.projectId })) {
|
|
144997
|
+
if (memory.state !== "qualified" && memory.state !== "approved") continue;
|
|
144998
|
+
const key = memory.scope + "\0" + memory.kind + "\0" + memory.title.trim().toLowerCase();
|
|
144999
|
+
const group = groups.get(key) ?? [];
|
|
145000
|
+
group.push(memory);
|
|
145001
|
+
groups.set(key, group);
|
|
145002
|
+
}
|
|
145003
|
+
for (const group of groups.values()) {
|
|
145004
|
+
if (group.length < 2) continue;
|
|
145005
|
+
group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
|
|
145006
|
+
const newest = group[0];
|
|
145007
|
+
for (const older of group.slice(1)) {
|
|
145008
|
+
try {
|
|
145009
|
+
await supersedeLongTermMemory({
|
|
145010
|
+
dataDir: input.dataDir,
|
|
145011
|
+
id: older.id,
|
|
145012
|
+
supersededBy: newest.id,
|
|
145013
|
+
reason: "auto-supersede: a newer record shares the same title"
|
|
145014
|
+
});
|
|
145015
|
+
result.superseded += 1;
|
|
145016
|
+
} catch {
|
|
145017
|
+
}
|
|
145018
|
+
}
|
|
145019
|
+
}
|
|
145020
|
+
return result;
|
|
145021
|
+
}
|
|
145022
|
+
async function listLongTermMemories(input) {
|
|
145023
|
+
const store = await createStore(input.dataDir);
|
|
145024
|
+
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) }));
|
|
145025
|
+
}
|
|
145026
|
+
async function getLongTermMemoryDetail(input) {
|
|
145027
|
+
const store = await createStore(input.dataDir);
|
|
145028
|
+
const memory = store.get(input.id);
|
|
145029
|
+
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
145030
|
+
throw new Error("Long-term memory was not found in the active scope.");
|
|
145031
|
+
}
|
|
145032
|
+
store.markAccess([memory.id]);
|
|
145033
|
+
return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
|
|
145034
|
+
}
|
|
145035
|
+
async function selectLongTermMemoriesForTask(input) {
|
|
145036
|
+
const task = input.task?.trim();
|
|
145037
|
+
if (!task) return [];
|
|
145038
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
145039
|
+
const reader = {
|
|
145040
|
+
projectId: input.projectId,
|
|
145041
|
+
...owner ? { ownerId: owner.id } : {},
|
|
145042
|
+
...input.agentId ? { agentId: input.agentId } : {},
|
|
145043
|
+
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
145044
|
+
};
|
|
145045
|
+
const store = await createStore(input.dataDir);
|
|
145046
|
+
const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
145047
|
+
let selected = eligible.map((memory) => {
|
|
145048
|
+
const ranked = scoreForTask(memory, task);
|
|
145049
|
+
return ranked ? {
|
|
145050
|
+
memory,
|
|
145051
|
+
evidence: store.listEvidence(memory.id),
|
|
145052
|
+
score: ranked.score,
|
|
145053
|
+
reason: ranked.reason
|
|
145054
|
+
} : void 0;
|
|
145055
|
+
}).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));
|
|
145056
|
+
if (selected.length === 0 && eligible.length > 0) {
|
|
145057
|
+
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
145058
|
+
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
145059
|
+
if (provider2 && candidates.length > 0) {
|
|
145060
|
+
try {
|
|
145061
|
+
const vectors = await withTimeout(
|
|
145062
|
+
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
145063
|
+
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
145064
|
+
retry: false
|
|
145065
|
+
}),
|
|
145066
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
145067
|
+
"Long-term semantic retrieval"
|
|
145068
|
+
);
|
|
145069
|
+
const query = vectors[0];
|
|
145070
|
+
if (Array.isArray(query)) {
|
|
145071
|
+
selected = candidates.map((memory, index) => {
|
|
145072
|
+
const vector = vectors[index + 1];
|
|
145073
|
+
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
145074
|
+
return ranked ? {
|
|
145075
|
+
memory,
|
|
145076
|
+
evidence: store.listEvidence(memory.id),
|
|
145077
|
+
score: ranked.score,
|
|
145078
|
+
reason: ranked.reason
|
|
145079
|
+
} : void 0;
|
|
145080
|
+
}).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));
|
|
145081
|
+
}
|
|
145082
|
+
} catch {
|
|
145083
|
+
}
|
|
145084
|
+
}
|
|
145085
|
+
}
|
|
145086
|
+
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
145087
|
+
store.markAccess(selected.map((item) => item.memory.id));
|
|
145088
|
+
return selected;
|
|
145089
|
+
}
|
|
145090
|
+
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
145091
|
+
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
145092
|
+
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
145093
|
+
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
145094
|
+
}
|
|
145095
|
+
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
145096
|
+
var init_long_term = __esm({
|
|
145097
|
+
"src/memory/long-term.ts"() {
|
|
145098
|
+
"use strict";
|
|
145099
|
+
init_esm_shims();
|
|
145100
|
+
init_token_budget();
|
|
145101
|
+
init_timeout();
|
|
145102
|
+
init_secret_filter();
|
|
145103
|
+
init_owner();
|
|
145104
|
+
init_long_term_store();
|
|
145105
|
+
init_long_term_types();
|
|
145106
|
+
MAX_TEXT_LENGTH = 12e3;
|
|
145107
|
+
MAX_LIST_ITEMS = 40;
|
|
145108
|
+
MAX_SEMANTIC_CANDIDATES = 48;
|
|
145109
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
145110
|
+
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
145111
|
+
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
145112
|
+
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
145113
|
+
"the",
|
|
145114
|
+
"and",
|
|
145115
|
+
"for",
|
|
145116
|
+
"with",
|
|
145117
|
+
"from",
|
|
145118
|
+
"that",
|
|
145119
|
+
"this",
|
|
145120
|
+
"into",
|
|
145121
|
+
"when",
|
|
145122
|
+
"what",
|
|
145123
|
+
"\u5982\u4F55",
|
|
145124
|
+
"\u4EC0\u4E48",
|
|
145125
|
+
"\u8FD9\u4E2A",
|
|
145126
|
+
"\u90A3\u4E2A",
|
|
145127
|
+
"\u6211\u4EEC",
|
|
145128
|
+
"\u9879\u76EE",
|
|
145129
|
+
"\u8FDB\u884C",
|
|
145130
|
+
"\u9700\u8981",
|
|
145131
|
+
"\u7EE7\u7EED",
|
|
145132
|
+
"\u5904\u7406"
|
|
145133
|
+
]);
|
|
145134
|
+
}
|
|
145135
|
+
});
|
|
145136
|
+
|
|
144227
145137
|
// src/codegraph/ids.ts
|
|
144228
145138
|
import { createHash as createHash5 } from "crypto";
|
|
144229
145139
|
function digest(input) {
|
|
@@ -144262,7 +145172,7 @@ var store_exports = {};
|
|
|
144262
145172
|
__export(store_exports, {
|
|
144263
145173
|
CodeGraphStore: () => CodeGraphStore
|
|
144264
145174
|
});
|
|
144265
|
-
import { randomUUID as
|
|
145175
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
144266
145176
|
import { posix as pathPosix } from "path";
|
|
144267
145177
|
function rowToFile(row) {
|
|
144268
145178
|
return {
|
|
@@ -144979,7 +145889,7 @@ var init_store = __esm({
|
|
|
144979
145889
|
* so an interrupted scan cannot be advertised as complete.
|
|
144980
145890
|
*/
|
|
144981
145891
|
recordCodeStateSnapshot(input) {
|
|
144982
|
-
const id =
|
|
145892
|
+
const id = randomUUID9();
|
|
144983
145893
|
let snapshot;
|
|
144984
145894
|
const tx = this.db.transaction(() => {
|
|
144985
145895
|
const previous = this.db.prepare(
|
|
@@ -145757,8 +146667,8 @@ var claim_store_exports = {};
|
|
|
145757
146667
|
__export(claim_store_exports, {
|
|
145758
146668
|
ClaimStore: () => ClaimStore
|
|
145759
146669
|
});
|
|
145760
|
-
import { createHash as createHash8, randomUUID as
|
|
145761
|
-
function
|
|
146670
|
+
import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
|
|
146671
|
+
function optionalText3(value) {
|
|
145762
146672
|
return typeof value === "string" && value ? value : void 0;
|
|
145763
146673
|
}
|
|
145764
146674
|
function rowToClaim(row) {
|
|
@@ -145774,38 +146684,38 @@ function rowToClaim(row) {
|
|
|
145774
146684
|
status: row.status,
|
|
145775
146685
|
confidence: Number(row.confidence),
|
|
145776
146686
|
observedAt: row.observedAt,
|
|
145777
|
-
...
|
|
145778
|
-
...
|
|
145779
|
-
...
|
|
146687
|
+
...optionalText3(row.validFrom) ? { validFrom: row.validFrom } : {},
|
|
146688
|
+
...optionalText3(row.validTo) ? { validTo: row.validTo } : {},
|
|
146689
|
+
...optionalText3(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
145780
146690
|
reviewState: row.reviewState,
|
|
145781
146691
|
origin: row.origin,
|
|
145782
146692
|
createdAt: row.createdAt,
|
|
145783
146693
|
updatedAt: row.updatedAt
|
|
145784
146694
|
};
|
|
145785
146695
|
}
|
|
145786
|
-
function
|
|
146696
|
+
function rowToEvidence2(row) {
|
|
145787
146697
|
return {
|
|
145788
146698
|
id: row.id,
|
|
145789
146699
|
claimId: row.claimId,
|
|
145790
146700
|
evidenceKind: row.evidenceKind,
|
|
145791
146701
|
evidenceId: row.evidenceId,
|
|
145792
146702
|
relation: row.relation,
|
|
145793
|
-
...
|
|
145794
|
-
...
|
|
145795
|
-
...
|
|
146703
|
+
...optionalText3(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
|
|
146704
|
+
...optionalText3(row.locator) ? { locator: row.locator } : {},
|
|
146705
|
+
...optionalText3(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
145796
146706
|
createdAt: row.createdAt
|
|
145797
146707
|
};
|
|
145798
146708
|
}
|
|
145799
|
-
function
|
|
146709
|
+
function rowToEvent2(row) {
|
|
145800
146710
|
return {
|
|
145801
146711
|
id: row.id,
|
|
145802
146712
|
projectId: row.projectId,
|
|
145803
146713
|
claimId: row.claimId,
|
|
145804
146714
|
kind: row.kind,
|
|
145805
|
-
...
|
|
145806
|
-
...
|
|
145807
|
-
...
|
|
145808
|
-
...
|
|
146715
|
+
...optionalText3(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
|
|
146716
|
+
...optionalText3(row.toStatus) ? { toStatus: row.toStatus } : {},
|
|
146717
|
+
...optionalText3(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
|
|
146718
|
+
...optionalText3(row.detail) ? { detail: row.detail } : {},
|
|
145809
146719
|
createdAt: row.createdAt
|
|
145810
146720
|
};
|
|
145811
146721
|
}
|
|
@@ -145941,7 +146851,7 @@ var init_claim_store = __esm({
|
|
|
145941
146851
|
locator, capturedHash, evidenceKey, createdAt
|
|
145942
146852
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
145943
146853
|
`).run(
|
|
145944
|
-
|
|
146854
|
+
randomUUID10(),
|
|
145945
146855
|
input.claimId,
|
|
145946
146856
|
input.evidenceKind,
|
|
145947
146857
|
input.evidenceId,
|
|
@@ -145959,11 +146869,11 @@ var init_claim_store = __esm({
|
|
|
145959
146869
|
SELECT * FROM knowledge_claim_evidence
|
|
145960
146870
|
WHERE claimId = ?
|
|
145961
146871
|
ORDER BY createdAt ASC, id
|
|
145962
|
-
`).all(claimId).map(
|
|
146872
|
+
`).all(claimId).map(rowToEvidence2);
|
|
145963
146873
|
}
|
|
145964
146874
|
recordEvent(input) {
|
|
145965
146875
|
const event = {
|
|
145966
|
-
id:
|
|
146876
|
+
id: randomUUID10(),
|
|
145967
146877
|
...input,
|
|
145968
146878
|
...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
|
|
145969
146879
|
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -145991,7 +146901,7 @@ var init_claim_store = __esm({
|
|
|
145991
146901
|
SELECT * FROM knowledge_claim_events
|
|
145992
146902
|
WHERE claimId = ?
|
|
145993
146903
|
ORDER BY createdAt ASC, id
|
|
145994
|
-
`).all(claimId).map(
|
|
146904
|
+
`).all(claimId).map(rowToEvent2);
|
|
145995
146905
|
}
|
|
145996
146906
|
};
|
|
145997
146907
|
}
|
|
@@ -146008,8 +146918,8 @@ __export(claims_exports, {
|
|
|
146008
146918
|
supersedeClaim: () => supersedeClaim,
|
|
146009
146919
|
writeClaim: () => writeClaim
|
|
146010
146920
|
});
|
|
146011
|
-
import { createHash as createHash9, randomUUID as
|
|
146012
|
-
function
|
|
146921
|
+
import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
|
|
146922
|
+
function compactText2(value, field) {
|
|
146013
146923
|
if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
|
|
146014
146924
|
const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
|
|
146015
146925
|
if (!sanitized) throw new Error("Claim " + field + " is required");
|
|
@@ -146017,7 +146927,7 @@ function compactText(value, field) {
|
|
|
146017
146927
|
return sanitized;
|
|
146018
146928
|
}
|
|
146019
146929
|
function normalizedTerm(value) {
|
|
146020
|
-
return
|
|
146930
|
+
return compactText2(value, "identity").toLocaleLowerCase("en-US");
|
|
146021
146931
|
}
|
|
146022
146932
|
function hashIdentity(parts) {
|
|
146023
146933
|
return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
|
|
@@ -146046,7 +146956,7 @@ function validateEvidence(input) {
|
|
|
146046
146956
|
if (!evidence || typeof evidence !== "object") {
|
|
146047
146957
|
throw new Error("Claim evidence must be an object");
|
|
146048
146958
|
}
|
|
146049
|
-
const evidenceId =
|
|
146959
|
+
const evidenceId = compactText2(evidence.evidenceId, "evidence id");
|
|
146050
146960
|
if (evidenceId.length > MAX_EVIDENCE_ID_LENGTH) {
|
|
146051
146961
|
throw new Error("Claim evidence id is too long");
|
|
146052
146962
|
}
|
|
@@ -146060,9 +146970,9 @@ function validateEvidence(input) {
|
|
|
146060
146970
|
evidenceKind: evidence.evidenceKind,
|
|
146061
146971
|
evidenceId,
|
|
146062
146972
|
relation: evidence.relation,
|
|
146063
|
-
...evidence.snapshotId ? { snapshotId:
|
|
146064
|
-
...evidence.locator ? { locator:
|
|
146065
|
-
...evidence.capturedHash ? { capturedHash:
|
|
146973
|
+
...evidence.snapshotId ? { snapshotId: compactText2(evidence.snapshotId, "evidence snapshot id") } : {},
|
|
146974
|
+
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator") } : {},
|
|
146975
|
+
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash") } : {}
|
|
146066
146976
|
};
|
|
146067
146977
|
});
|
|
146068
146978
|
}
|
|
@@ -146070,10 +146980,10 @@ function normalizeClaimInput(input) {
|
|
|
146070
146980
|
if (!["project", "workspace", "team", "workflow", "task"].includes(input.scope)) {
|
|
146071
146981
|
throw new Error("Claim scope is invalid");
|
|
146072
146982
|
}
|
|
146073
|
-
const projectId =
|
|
146074
|
-
const subject =
|
|
146075
|
-
const predicate =
|
|
146076
|
-
const objectValue =
|
|
146983
|
+
const projectId = compactText2(input.projectId, "project id");
|
|
146984
|
+
const subject = compactText2(input.subject, "subject");
|
|
146985
|
+
const predicate = compactText2(input.predicate, "predicate");
|
|
146986
|
+
const objectValue = compactText2(input.objectValue, "object value");
|
|
146077
146987
|
const identity = buildClaimIdentity({ subject, predicate, objectValue, scope: input.scope });
|
|
146078
146988
|
const origin = input.origin ?? "explicit";
|
|
146079
146989
|
let reviewState = input.reviewState ?? "approved";
|
|
@@ -146094,7 +147004,7 @@ function normalizeClaimInput(input) {
|
|
|
146094
147004
|
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
146095
147005
|
return {
|
|
146096
147006
|
claim: {
|
|
146097
|
-
id:
|
|
147007
|
+
id: randomUUID11(),
|
|
146098
147008
|
projectId,
|
|
146099
147009
|
subject,
|
|
146100
147010
|
predicate,
|
|
@@ -146194,7 +147104,7 @@ function reviewClaim(store, input) {
|
|
|
146194
147104
|
if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
|
|
146195
147105
|
throw new Error("A claim review must approve or reject the claim");
|
|
146196
147106
|
}
|
|
146197
|
-
const detail =
|
|
147107
|
+
const detail = compactText2(input.detail, "review detail");
|
|
146198
147108
|
return store.transaction(() => {
|
|
146199
147109
|
const claim = store.getClaim(input.claimId);
|
|
146200
147110
|
if (!claim) throw new Error("Claim was not found");
|
|
@@ -146506,8 +147416,8 @@ var init_frontmatter = __esm({
|
|
|
146506
147416
|
|
|
146507
147417
|
// src/knowledge/markdown.ts
|
|
146508
147418
|
import { createHash as createHash10 } from "crypto";
|
|
146509
|
-
import { promises as
|
|
146510
|
-
import
|
|
147419
|
+
import { promises as fs13 } from "fs";
|
|
147420
|
+
import path20 from "path";
|
|
146511
147421
|
function hashContent(content) {
|
|
146512
147422
|
return createHash10("sha256").update(content).digest("hex");
|
|
146513
147423
|
}
|
|
@@ -146565,8 +147475,8 @@ function extractInternalMarkdownLinks(body) {
|
|
|
146565
147475
|
return [...links].sort();
|
|
146566
147476
|
}
|
|
146567
147477
|
function resolvePageLink(sourceRelativePath, target) {
|
|
146568
|
-
const sourceDir =
|
|
146569
|
-
const normalized =
|
|
147478
|
+
const sourceDir = path20.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
|
|
147479
|
+
const normalized = path20.posix.normalize(path20.posix.join(sourceDir, target.replace(/\\/g, "/")));
|
|
146570
147480
|
if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
|
|
146571
147481
|
return normalized;
|
|
146572
147482
|
}
|
|
@@ -146588,13 +147498,13 @@ function renderKnowledgePage(frontmatter, body) {
|
|
|
146588
147498
|
});
|
|
146589
147499
|
}
|
|
146590
147500
|
async function readKnowledgePage(absolutePath, workspaceRoot) {
|
|
146591
|
-
const raw = await
|
|
147501
|
+
const raw = await fs13.readFile(absolutePath, "utf8");
|
|
146592
147502
|
try {
|
|
146593
147503
|
const parsed = frontmatter_default(raw);
|
|
146594
147504
|
const frontmatter = validateKnowledgePageFrontmatter(parsed.data);
|
|
146595
147505
|
return {
|
|
146596
147506
|
absolutePath,
|
|
146597
|
-
relativePath: workspaceRoot ?
|
|
147507
|
+
relativePath: workspaceRoot ? path20.relative(workspaceRoot, absolutePath).split(path20.sep).join("/") : path20.basename(absolutePath),
|
|
146598
147508
|
frontmatter,
|
|
146599
147509
|
body: parsed.content.trim(),
|
|
146600
147510
|
contentHash: hashContent(raw),
|
|
@@ -146606,7 +147516,7 @@ async function readKnowledgePage(absolutePath, workspaceRoot) {
|
|
|
146606
147516
|
}
|
|
146607
147517
|
}
|
|
146608
147518
|
async function writeKnowledgePage(absolutePath, content) {
|
|
146609
|
-
await
|
|
147519
|
+
await fs13.mkdir(path20.dirname(absolutePath), { recursive: true });
|
|
146610
147520
|
await atomicWriteFile(absolutePath, content);
|
|
146611
147521
|
return hashContent(content);
|
|
146612
147522
|
}
|
|
@@ -146636,8 +147546,8 @@ __export(wiki_exports, {
|
|
|
146636
147546
|
readKnowledgePage: () => readKnowledgePage
|
|
146637
147547
|
});
|
|
146638
147548
|
import { createHash as createHash11 } from "crypto";
|
|
146639
|
-
import { promises as
|
|
146640
|
-
import
|
|
147549
|
+
import { promises as fs14 } from "fs";
|
|
147550
|
+
import path21 from "path";
|
|
146641
147551
|
function hash2(value) {
|
|
146642
147552
|
return createHash11("sha256").update(value).digest("hex");
|
|
146643
147553
|
}
|
|
@@ -146769,7 +147679,7 @@ function candidatePage(workspace, title, claims, store) {
|
|
|
146769
147679
|
}
|
|
146770
147680
|
async function fileHashIfPresent(filePath) {
|
|
146771
147681
|
try {
|
|
146772
|
-
return pageContentHash(await
|
|
147682
|
+
return pageContentHash(await fs14.readFile(filePath, "utf8"));
|
|
146773
147683
|
} catch (error) {
|
|
146774
147684
|
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
146775
147685
|
if (code === "ENOENT") return void 0;
|
|
@@ -146793,8 +147703,8 @@ async function writeIndex(workspace, store) {
|
|
|
146793
147703
|
lines.push("No pending proposals.");
|
|
146794
147704
|
} else {
|
|
146795
147705
|
for (const proposal of pending) {
|
|
146796
|
-
const relative3 =
|
|
146797
|
-
lines.push("- [" +
|
|
147706
|
+
const relative3 = path21.relative(paths.root, proposal.proposalPath).split(path21.sep).join("/");
|
|
147707
|
+
lines.push("- [" + path21.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
|
|
146798
147708
|
}
|
|
146799
147709
|
}
|
|
146800
147710
|
lines.push("");
|
|
@@ -146802,7 +147712,7 @@ async function writeIndex(workspace, store) {
|
|
|
146802
147712
|
}
|
|
146803
147713
|
async function appendLog(workspace, line) {
|
|
146804
147714
|
const logPath = getKnowledgeWorkspacePaths(workspace).log;
|
|
146805
|
-
await
|
|
147715
|
+
await fs14.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
|
|
146806
147716
|
}
|
|
146807
147717
|
function toPageRecord(workspace, page, status, contentHash, manualContentHash) {
|
|
146808
147718
|
return {
|
|
@@ -146887,9 +147797,9 @@ async function applyKnowledgeProposal(input) {
|
|
|
146887
147797
|
}
|
|
146888
147798
|
if (proposal.status !== "pending") throw new Error("Knowledge proposal is not pending review");
|
|
146889
147799
|
const root = getKnowledgeWorkspacePaths(input.workspace).root;
|
|
146890
|
-
const targetPath =
|
|
146891
|
-
const proposalPath =
|
|
146892
|
-
if (!targetPath.startsWith(root +
|
|
147800
|
+
const targetPath = path21.resolve(proposal.targetPath);
|
|
147801
|
+
const proposalPath = path21.resolve(proposal.proposalPath);
|
|
147802
|
+
if (!targetPath.startsWith(root + path21.sep) || !proposalPath.startsWith(root + path21.sep)) {
|
|
146893
147803
|
throw new Error("Knowledge proposal path escapes its workspace");
|
|
146894
147804
|
}
|
|
146895
147805
|
await withFileLock(root, async () => {
|
|
@@ -146927,12 +147837,12 @@ async function markdownFiles(directory) {
|
|
|
146927
147837
|
const files = [];
|
|
146928
147838
|
let entries;
|
|
146929
147839
|
try {
|
|
146930
|
-
entries = await
|
|
147840
|
+
entries = await fs14.readdir(directory, { withFileTypes: true });
|
|
146931
147841
|
} catch {
|
|
146932
147842
|
return files;
|
|
146933
147843
|
}
|
|
146934
147844
|
for (const entry of entries) {
|
|
146935
|
-
const filePath =
|
|
147845
|
+
const filePath = path21.join(directory, entry.name);
|
|
146936
147846
|
if (entry.isDirectory()) {
|
|
146937
147847
|
files.push(...await markdownFiles(filePath));
|
|
146938
147848
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
@@ -146955,13 +147865,13 @@ async function lintKnowledgeWorkspace(input) {
|
|
|
146955
147865
|
kind: "malformed-frontmatter",
|
|
146956
147866
|
severity: "error",
|
|
146957
147867
|
message: error instanceof Error ? error.message : "Malformed knowledge page.",
|
|
146958
|
-
relativePath:
|
|
147868
|
+
relativePath: path21.relative(paths.root, filePath).split(path21.sep).join("/")
|
|
146959
147869
|
});
|
|
146960
147870
|
}
|
|
146961
147871
|
}
|
|
146962
147872
|
const indexed = /* @__PURE__ */ new Set();
|
|
146963
147873
|
try {
|
|
146964
|
-
const index = await
|
|
147874
|
+
const index = await fs14.readFile(paths.index, "utf8");
|
|
146965
147875
|
for (const link of extractInternalMarkdownLinks(index)) {
|
|
146966
147876
|
const resolved = resolvePageLink("index.md", link);
|
|
146967
147877
|
if (resolved) indexed.add(resolved);
|
|
@@ -147050,7 +147960,7 @@ async function lintKnowledgeWorkspace(input) {
|
|
|
147050
147960
|
kind: "manual-edit-protected",
|
|
147051
147961
|
severity: "warning",
|
|
147052
147962
|
message: "A newer proposal was held because the target page has manual edits.",
|
|
147053
|
-
relativePath:
|
|
147963
|
+
relativePath: path21.relative(paths.root, proposal.targetPath).split(path21.sep).join("/")
|
|
147054
147964
|
});
|
|
147055
147965
|
}
|
|
147056
147966
|
}
|
|
@@ -147183,7 +148093,7 @@ var workflow_store_exports = {};
|
|
|
147183
148093
|
__export(workflow_store_exports, {
|
|
147184
148094
|
WorkflowStore: () => WorkflowStore
|
|
147185
148095
|
});
|
|
147186
|
-
import { randomUUID as
|
|
148096
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
147187
148097
|
function parseJson(value, fallback) {
|
|
147188
148098
|
if (typeof value !== "string" || !value) return fallback;
|
|
147189
148099
|
try {
|
|
@@ -147192,7 +148102,7 @@ function parseJson(value, fallback) {
|
|
|
147192
148102
|
return fallback;
|
|
147193
148103
|
}
|
|
147194
148104
|
}
|
|
147195
|
-
function
|
|
148105
|
+
function optionalText4(value) {
|
|
147196
148106
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
147197
148107
|
}
|
|
147198
148108
|
function rowToWorkflow(row) {
|
|
@@ -147229,14 +148139,14 @@ function rowToRun(row) {
|
|
|
147229
148139
|
workflowId: row.workflowId,
|
|
147230
148140
|
projectId: row.projectId,
|
|
147231
148141
|
task: row.task,
|
|
147232
|
-
...
|
|
148142
|
+
...optionalText4(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
|
|
147233
148143
|
selectedEvidence: parseJson(row.selectedEvidenceJson, []),
|
|
147234
148144
|
phaseState: parseJson(row.phaseStateJson, {}),
|
|
147235
148145
|
outcome: row.outcome,
|
|
147236
148146
|
verificationVerdict: row.verificationVerdict,
|
|
147237
|
-
...
|
|
148147
|
+
...optionalText4(row.failureReason) ? { failureReason: row.failureReason } : {},
|
|
147238
148148
|
startedAt: row.startedAt,
|
|
147239
|
-
...
|
|
148149
|
+
...optionalText4(row.completedAt) ? { completedAt: row.completedAt } : {}
|
|
147240
148150
|
};
|
|
147241
148151
|
}
|
|
147242
148152
|
var WorkflowStore;
|
|
@@ -147283,7 +148193,7 @@ var init_workflow_store = __esm({
|
|
|
147283
148193
|
}
|
|
147284
148194
|
recordRun(input) {
|
|
147285
148195
|
const run = {
|
|
147286
|
-
id:
|
|
148196
|
+
id: randomUUID12(),
|
|
147287
148197
|
workflowId: input.workflowId,
|
|
147288
148198
|
projectId: input.projectId,
|
|
147289
148199
|
task: input.task.trim(),
|
|
@@ -147361,8 +148271,8 @@ var init_outcome_types = __esm({
|
|
|
147361
148271
|
});
|
|
147362
148272
|
|
|
147363
148273
|
// src/knowledge/outcome-store.ts
|
|
147364
|
-
import { randomUUID as
|
|
147365
|
-
function
|
|
148274
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
148275
|
+
function optionalText5(value) {
|
|
147366
148276
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
147367
148277
|
}
|
|
147368
148278
|
function text(value, field, max = 1e3) {
|
|
@@ -147385,8 +148295,8 @@ function rowToSignal(row) {
|
|
|
147385
148295
|
candidateId: row.candidateId,
|
|
147386
148296
|
kind: row.kind,
|
|
147387
148297
|
sourceRef: row.sourceRef,
|
|
147388
|
-
...
|
|
147389
|
-
...
|
|
148298
|
+
...optionalText5(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
|
|
148299
|
+
...optionalText5(row.detail) ? { detail: row.detail } : {},
|
|
147390
148300
|
observedAt: row.observedAt
|
|
147391
148301
|
};
|
|
147392
148302
|
}
|
|
@@ -147405,7 +148315,7 @@ var init_outcome_store = __esm({
|
|
|
147405
148315
|
}
|
|
147406
148316
|
record(input) {
|
|
147407
148317
|
const signal = {
|
|
147408
|
-
id:
|
|
148318
|
+
id: randomUUID13(),
|
|
147409
148319
|
projectId: text(input.projectId, "project id"),
|
|
147410
148320
|
candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
|
|
147411
148321
|
candidateId: text(input.candidateId, "candidate id"),
|
|
@@ -147832,8 +148742,8 @@ __export(workflows_exports, {
|
|
|
147832
148742
|
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
147833
148743
|
});
|
|
147834
148744
|
import { createHash as createHash12 } from "crypto";
|
|
147835
|
-
import { promises as
|
|
147836
|
-
import
|
|
148745
|
+
import { promises as fs15 } from "fs";
|
|
148746
|
+
import path22 from "path";
|
|
147837
148747
|
function hash3(value) {
|
|
147838
148748
|
return createHash12("sha256").update(value).digest("hex");
|
|
147839
148749
|
}
|
|
@@ -147854,7 +148764,7 @@ function requiredText(data, key) {
|
|
|
147854
148764
|
}
|
|
147855
148765
|
return value.trim();
|
|
147856
148766
|
}
|
|
147857
|
-
function
|
|
148767
|
+
function optionalText6(data, key) {
|
|
147858
148768
|
const value = data[key];
|
|
147859
148769
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
147860
148770
|
if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
|
|
@@ -147897,11 +148807,11 @@ function phaseFromValue(value, index) {
|
|
|
147897
148807
|
}
|
|
147898
148808
|
const data = value;
|
|
147899
148809
|
const title = requiredText(data, "title");
|
|
147900
|
-
const phaseId =
|
|
148810
|
+
const phaseId = optionalText6(data, "id") ?? slug(title) + "-" + (index + 1);
|
|
147901
148811
|
return {
|
|
147902
148812
|
id: phaseId,
|
|
147903
148813
|
title,
|
|
147904
|
-
instructions:
|
|
148814
|
+
instructions: optionalText6(data, "instructions") ?? "",
|
|
147905
148815
|
branches: optionalStringArray(data, "branches"),
|
|
147906
148816
|
expectedOutputs: optionalStringArray(data, "expectedOutputs"),
|
|
147907
148817
|
verificationGates: optionalStringArray(data, "verificationGates")
|
|
@@ -147936,7 +148846,7 @@ function phasesFromBody(body) {
|
|
|
147936
148846
|
}
|
|
147937
148847
|
function normalizeSourcePath(sourcePath) {
|
|
147938
148848
|
const normalized = sourcePath.replace(/\\/g, "/");
|
|
147939
|
-
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") ||
|
|
148849
|
+
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path22.posix.normalize(normalized) !== normalized) {
|
|
147940
148850
|
throw new Error("workflow source path must be a safe workflows/*.md path");
|
|
147941
148851
|
}
|
|
147942
148852
|
return normalized;
|
|
@@ -148010,7 +148920,7 @@ function parsedWorkflow(data, body, input) {
|
|
|
148010
148920
|
id: requiredText(data, "id"),
|
|
148011
148921
|
workspaceId: input.workspaceId,
|
|
148012
148922
|
title: requiredText(data, "title"),
|
|
148013
|
-
description:
|
|
148923
|
+
description: optionalText6(data, "description") ?? "",
|
|
148014
148924
|
status: optionalStatus(data),
|
|
148015
148925
|
version: optionalNumber(data, "version", 1),
|
|
148016
148926
|
taskLenses: optionalStringArray(data, "taskLenses"),
|
|
@@ -148029,9 +148939,9 @@ function parsedWorkflow(data, body, input) {
|
|
|
148029
148939
|
sourcePath: normalizeSourcePath(input.sourcePath),
|
|
148030
148940
|
sourceHash: "",
|
|
148031
148941
|
contentHash: input.contentHash,
|
|
148032
|
-
createdAt:
|
|
148033
|
-
updatedAt:
|
|
148034
|
-
...
|
|
148942
|
+
createdAt: optionalText6(data, "createdAt") ?? now2(),
|
|
148943
|
+
updatedAt: optionalText6(data, "updatedAt") ?? now2(),
|
|
148944
|
+
...optionalText6(data, "importedFrom") ? { importedFrom: optionalText6(data, "importedFrom") } : {}
|
|
148035
148945
|
};
|
|
148036
148946
|
if (candidate.phases.length === 0) {
|
|
148037
148947
|
throw new Error("workflow must contain at least one phase or a non-empty body");
|
|
@@ -148085,7 +148995,7 @@ async function writeCanonicalWorkflow(input) {
|
|
|
148085
148995
|
const filePath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
|
|
148086
148996
|
const content = renderWorkflowMarkdown(workflow);
|
|
148087
148997
|
await withFileLock(paths.root, async () => {
|
|
148088
|
-
await
|
|
148998
|
+
await fs15.mkdir(path22.dirname(filePath), { recursive: true });
|
|
148089
148999
|
await atomicWriteFile(filePath, content);
|
|
148090
149000
|
});
|
|
148091
149001
|
const store = await storeFor2(input.workspace);
|
|
@@ -148094,13 +149004,13 @@ async function writeCanonicalWorkflow(input) {
|
|
|
148094
149004
|
async function markdownFiles2(directory) {
|
|
148095
149005
|
let entries;
|
|
148096
149006
|
try {
|
|
148097
|
-
entries = await
|
|
149007
|
+
entries = await fs15.readdir(directory, { withFileTypes: true });
|
|
148098
149008
|
} catch {
|
|
148099
149009
|
return [];
|
|
148100
149010
|
}
|
|
148101
149011
|
const files = [];
|
|
148102
149012
|
for (const entry of entries) {
|
|
148103
|
-
const absolutePath =
|
|
149013
|
+
const absolutePath = path22.join(directory, entry.name);
|
|
148104
149014
|
if (entry.isDirectory()) {
|
|
148105
149015
|
files.push(...await markdownFiles2(absolutePath));
|
|
148106
149016
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
@@ -148115,9 +149025,9 @@ async function syncCanonicalWorkflows(workspace) {
|
|
|
148115
149025
|
const workflows = [];
|
|
148116
149026
|
const errors = [];
|
|
148117
149027
|
for (const absolutePath of await markdownFiles2(paths.workflows)) {
|
|
148118
|
-
const sourcePath =
|
|
149028
|
+
const sourcePath = path22.relative(paths.root, absolutePath).split(path22.sep).join("/");
|
|
148119
149029
|
try {
|
|
148120
|
-
const raw = await
|
|
149030
|
+
const raw = await fs15.readFile(absolutePath, "utf8");
|
|
148121
149031
|
const workflow = parseWorkflowMarkdown(raw, { workspaceId: workspace.id, sourcePath });
|
|
148122
149032
|
workflows.push(store.upsertWorkflow(workflow));
|
|
148123
149033
|
} catch (error) {
|
|
@@ -148229,21 +149139,21 @@ function importedWorkflowSpec(input) {
|
|
|
148229
149139
|
return materializeWorkflow(spec);
|
|
148230
149140
|
}
|
|
148231
149141
|
async function importWindsurfWorkflows(input) {
|
|
148232
|
-
const sourceDirectory =
|
|
149142
|
+
const sourceDirectory = path22.join(path22.resolve(input.projectRoot), ".windsurf", "workflows");
|
|
148233
149143
|
const paths = getKnowledgeWorkspacePaths(input.workspace);
|
|
148234
149144
|
const imported = [];
|
|
148235
149145
|
const skipped = [];
|
|
148236
149146
|
let entries;
|
|
148237
149147
|
try {
|
|
148238
|
-
entries = await
|
|
149148
|
+
entries = await fs15.readdir(sourceDirectory, { withFileTypes: true });
|
|
148239
149149
|
} catch {
|
|
148240
149150
|
return { imported, skipped };
|
|
148241
149151
|
}
|
|
148242
149152
|
for (const entry of entries.filter((item) => item.isFile() && item.name.toLowerCase().endsWith(".md"))) {
|
|
148243
|
-
const originalPath =
|
|
148244
|
-
const sourcePath =
|
|
149153
|
+
const originalPath = path22.join(sourceDirectory, entry.name);
|
|
149154
|
+
const sourcePath = path22.relative(path22.resolve(input.projectRoot), originalPath).split(path22.sep).join("/");
|
|
148245
149155
|
try {
|
|
148246
|
-
const raw = await
|
|
149156
|
+
const raw = await fs15.readFile(originalPath, "utf8");
|
|
148247
149157
|
const workflow = importedWorkflowSpec({
|
|
148248
149158
|
workspace: input.workspace,
|
|
148249
149159
|
sourceName: entry.name,
|
|
@@ -148252,7 +149162,7 @@ async function importWindsurfWorkflows(input) {
|
|
|
148252
149162
|
});
|
|
148253
149163
|
const targetPath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
|
|
148254
149164
|
try {
|
|
148255
|
-
await
|
|
149165
|
+
await fs15.access(targetPath);
|
|
148256
149166
|
skipped.push({ sourcePath, reason: "canonical workflow already exists and was preserved" });
|
|
148257
149167
|
continue;
|
|
148258
149168
|
} catch {
|
|
@@ -148265,12 +149175,12 @@ async function importWindsurfWorkflows(input) {
|
|
|
148265
149175
|
});
|
|
148266
149176
|
}
|
|
148267
149177
|
}
|
|
148268
|
-
await
|
|
149178
|
+
await fs15.mkdir(paths.workflows, { recursive: true });
|
|
148269
149179
|
return { imported, skipped };
|
|
148270
149180
|
}
|
|
148271
149181
|
function isWithin2(root, candidate) {
|
|
148272
|
-
const relative3 =
|
|
148273
|
-
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." +
|
|
149182
|
+
const relative3 = path22.relative(root, candidate);
|
|
149183
|
+
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path22.sep) && !path22.isAbsolute(relative3);
|
|
148274
149184
|
}
|
|
148275
149185
|
function adapterRelativePath(workflow, agent) {
|
|
148276
149186
|
const name = "memorix-workflow-" + slug(workflow.id.replace(/^workflow:/, ""));
|
|
@@ -148356,15 +149266,15 @@ async function previewWorkflowAdapter(input) {
|
|
|
148356
149266
|
reason: "The canonical workflow does not declare compatibility with this agent."
|
|
148357
149267
|
};
|
|
148358
149268
|
}
|
|
148359
|
-
const projectRoot =
|
|
148360
|
-
const targetPath =
|
|
149269
|
+
const projectRoot = path22.resolve(input.projectRoot);
|
|
149270
|
+
const targetPath = path22.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
|
|
148361
149271
|
if (!isWithin2(projectRoot, targetPath)) {
|
|
148362
149272
|
throw new Error("Workflow adapter target escapes the project root");
|
|
148363
149273
|
}
|
|
148364
149274
|
const content = adapterContent(input.workflow, input.agent);
|
|
148365
149275
|
let existing;
|
|
148366
149276
|
try {
|
|
148367
|
-
existing = await
|
|
149277
|
+
existing = await fs15.readFile(targetPath, "utf8");
|
|
148368
149278
|
} catch (error) {
|
|
148369
149279
|
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
148370
149280
|
if (code !== "ENOENT") throw error;
|
|
@@ -148385,11 +149295,11 @@ async function applyWorkflowAdapter(input) {
|
|
|
148385
149295
|
if (initial.status === "unsupported" || initial.status === "conflict" || initial.status === "unchanged") {
|
|
148386
149296
|
return initial;
|
|
148387
149297
|
}
|
|
148388
|
-
const projectRoot =
|
|
149298
|
+
const projectRoot = path22.resolve(input.projectRoot);
|
|
148389
149299
|
return withFileLock(projectRoot, async () => {
|
|
148390
149300
|
const current = await previewWorkflowAdapter(input);
|
|
148391
149301
|
if (current.status !== "create" && current.status !== "update") return current;
|
|
148392
|
-
await
|
|
149302
|
+
await fs15.mkdir(path22.dirname(current.targetPath), { recursive: true });
|
|
148393
149303
|
await atomicWriteFile(current.targetPath, current.content);
|
|
148394
149304
|
return current;
|
|
148395
149305
|
});
|
|
@@ -148679,6 +149589,11 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
148679
149589
|
}
|
|
148680
149590
|
return { action: "complete" };
|
|
148681
149591
|
}
|
|
149592
|
+
if (job.kind === "long-term-maintenance") {
|
|
149593
|
+
const { maintainLongTermMemories: maintainLongTermMemories2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
149594
|
+
await maintainLongTermMemories2({ dataDir: projectDir2, projectId });
|
|
149595
|
+
return { action: "reschedule", delayMs: 24 * 60 * 60 * 1e3, resetAttempts: true };
|
|
149596
|
+
}
|
|
148682
149597
|
if (job.kind === "consolidation") {
|
|
148683
149598
|
const limit2 = consolidationBatchSize(job.payload);
|
|
148684
149599
|
const result2 = await runAutomaticConsolidation(projectId, projectDir2, {
|
|
@@ -149020,15 +149935,15 @@ __export(vector_backfill_runner_exports, {
|
|
|
149020
149935
|
});
|
|
149021
149936
|
import { spawn as spawn4 } from "child_process";
|
|
149022
149937
|
import { existsSync as existsSync10 } from "fs";
|
|
149023
|
-
import
|
|
149938
|
+
import path23 from "path";
|
|
149024
149939
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
149025
149940
|
function isNonEmptyString(value) {
|
|
149026
149941
|
return typeof value === "string" && value.trim().length > 0;
|
|
149027
149942
|
}
|
|
149028
149943
|
function resolveVectorBackfillRunnerPath(moduleUrl = import.meta.url) {
|
|
149029
|
-
const moduleDir =
|
|
149030
|
-
const distDir =
|
|
149031
|
-
return
|
|
149944
|
+
const moduleDir = path23.dirname(fileURLToPath5(moduleUrl));
|
|
149945
|
+
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;
|
|
149946
|
+
return path23.join(distDir, "vector-backfill-runner.js");
|
|
149032
149947
|
}
|
|
149033
149948
|
function parseVectorBackfillRequest(raw) {
|
|
149034
149949
|
let value;
|
|
@@ -149037,7 +149952,7 @@ function parseVectorBackfillRequest(raw) {
|
|
|
149037
149952
|
} catch {
|
|
149038
149953
|
throw new Error("Vector backfill runner received invalid JSON input");
|
|
149039
149954
|
}
|
|
149040
|
-
if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !
|
|
149955
|
+
if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path23.isAbsolute(value.projectRoot) || !path23.isAbsolute(value.dataDir)) {
|
|
149041
149956
|
throw new Error("Vector backfill runner received an invalid request");
|
|
149042
149957
|
}
|
|
149043
149958
|
return {
|
|
@@ -150119,8 +151034,16 @@ var init_observations = __esm({
|
|
|
150119
151034
|
});
|
|
150120
151035
|
|
|
150121
151036
|
// src/store/session-store.ts
|
|
150122
|
-
|
|
150123
|
-
|
|
151037
|
+
var session_store_exports = {};
|
|
151038
|
+
__export(session_store_exports, {
|
|
151039
|
+
SessionGracefulDegrade: () => SessionGracefulDegrade,
|
|
151040
|
+
SessionSqliteStore: () => SessionSqliteStore,
|
|
151041
|
+
getSessionStore: () => getSessionStore,
|
|
151042
|
+
initSessionStore: () => initSessionStore,
|
|
151043
|
+
resetSessionStore: () => resetSessionStore
|
|
151044
|
+
});
|
|
151045
|
+
import path24 from "path";
|
|
151046
|
+
import fs16 from "fs";
|
|
150124
151047
|
function sessionToRow(session) {
|
|
150125
151048
|
return {
|
|
150126
151049
|
id: session.id,
|
|
@@ -150149,6 +151072,10 @@ function getSessionStore() {
|
|
|
150149
151072
|
}
|
|
150150
151073
|
return _store3;
|
|
150151
151074
|
}
|
|
151075
|
+
function resetSessionStore() {
|
|
151076
|
+
_store3 = null;
|
|
151077
|
+
_storeDataDir3 = null;
|
|
151078
|
+
}
|
|
150152
151079
|
async function initSessionStore(dataDir) {
|
|
150153
151080
|
if (_store3 && _storeDataDir3 === dataDir) return _store3;
|
|
150154
151081
|
_store3 = null;
|
|
@@ -150202,10 +151129,10 @@ var init_session_store = __esm({
|
|
|
150202
151129
|
async migrateFromJsonIfNeeded() {
|
|
150203
151130
|
const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM sessions`).get();
|
|
150204
151131
|
if (count2.cnt > 0) return;
|
|
150205
|
-
const jsonPath =
|
|
150206
|
-
if (!
|
|
151132
|
+
const jsonPath = path24.join(this.dataDir, "sessions.json");
|
|
151133
|
+
if (!fs16.existsSync(jsonPath)) return;
|
|
150207
151134
|
try {
|
|
150208
|
-
const raw =
|
|
151135
|
+
const raw = fs16.readFileSync(jsonPath, "utf-8");
|
|
150209
151136
|
const sessions = JSON.parse(raw);
|
|
150210
151137
|
if (!Array.isArray(sessions) || sessions.length === 0) return;
|
|
150211
151138
|
console.error(`[memorix] Migrating ${sessions.length} sessions from JSON to SQLite...`);
|
|
@@ -152001,827 +152928,6 @@ var init_session = __esm({
|
|
|
152001
152928
|
}
|
|
152002
152929
|
});
|
|
152003
152930
|
|
|
152004
|
-
// src/memory/owner.ts
|
|
152005
|
-
var owner_exports = {};
|
|
152006
|
-
__export(owner_exports, {
|
|
152007
|
-
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
152008
|
-
});
|
|
152009
|
-
import { randomUUID as randomUUID11 } from "crypto";
|
|
152010
|
-
import { promises as fs17 } from "fs";
|
|
152011
|
-
import path26 from "path";
|
|
152012
|
-
function ownerPath(dataDir) {
|
|
152013
|
-
const resolved = path26.resolve(dataDir);
|
|
152014
|
-
const home = path26.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path26.dirname(resolved) : resolved;
|
|
152015
|
-
return path26.join(home, "memorix-user.json");
|
|
152016
|
-
}
|
|
152017
|
-
function normalizedOwnerId(value) {
|
|
152018
|
-
const trimmed = value?.trim();
|
|
152019
|
-
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
152020
|
-
}
|
|
152021
|
-
async function readOwnerFile(filePath) {
|
|
152022
|
-
try {
|
|
152023
|
-
const value = JSON.parse(await fs17.readFile(filePath, "utf8"));
|
|
152024
|
-
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
152025
|
-
return id ? { id, source: "local-file" } : void 0;
|
|
152026
|
-
} catch {
|
|
152027
|
-
return void 0;
|
|
152028
|
-
}
|
|
152029
|
-
}
|
|
152030
|
-
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
152031
|
-
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
152032
|
-
if (configured) return { id: configured, source: "environment" };
|
|
152033
|
-
const filePath = ownerPath(dataDir);
|
|
152034
|
-
const existing = await readOwnerFile(filePath);
|
|
152035
|
-
if (existing || !options.create) return existing;
|
|
152036
|
-
await fs17.mkdir(path26.dirname(filePath), { recursive: true });
|
|
152037
|
-
const created = {
|
|
152038
|
-
version: 1,
|
|
152039
|
-
id: "local-" + randomUUID11(),
|
|
152040
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
152041
|
-
};
|
|
152042
|
-
try {
|
|
152043
|
-
await fs17.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
152044
|
-
return { id: created.id, source: "local-file" };
|
|
152045
|
-
} catch (error) {
|
|
152046
|
-
if (error?.code !== "EEXIST") throw error;
|
|
152047
|
-
const concurrent = await readOwnerFile(filePath);
|
|
152048
|
-
if (concurrent) return concurrent;
|
|
152049
|
-
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
152050
|
-
}
|
|
152051
|
-
}
|
|
152052
|
-
var ID_PATTERN;
|
|
152053
|
-
var init_owner = __esm({
|
|
152054
|
-
"src/memory/owner.ts"() {
|
|
152055
|
-
"use strict";
|
|
152056
|
-
init_esm_shims();
|
|
152057
|
-
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
152058
|
-
}
|
|
152059
|
-
});
|
|
152060
|
-
|
|
152061
|
-
// src/memory/long-term-store.ts
|
|
152062
|
-
import { randomUUID as randomUUID12 } from "crypto";
|
|
152063
|
-
function optionalText6(value) {
|
|
152064
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
152065
|
-
}
|
|
152066
|
-
function arrayValue2(value) {
|
|
152067
|
-
if (typeof value !== "string" || !value) return [];
|
|
152068
|
-
try {
|
|
152069
|
-
const parsed = JSON.parse(value);
|
|
152070
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
152071
|
-
} catch {
|
|
152072
|
-
return [];
|
|
152073
|
-
}
|
|
152074
|
-
}
|
|
152075
|
-
function rowToMemory(row) {
|
|
152076
|
-
return {
|
|
152077
|
-
id: row.id,
|
|
152078
|
-
originProjectId: row.originProjectId,
|
|
152079
|
-
ownerId: row.ownerId,
|
|
152080
|
-
scope: row.scope,
|
|
152081
|
-
kind: row.kind,
|
|
152082
|
-
state: row.state,
|
|
152083
|
-
portability: row.portability,
|
|
152084
|
-
title: row.title,
|
|
152085
|
-
content: row.content,
|
|
152086
|
-
facts: arrayValue2(row.factsJson),
|
|
152087
|
-
tags: arrayValue2(row.tagsJson),
|
|
152088
|
-
...optionalText6(row.applicability) ? { applicability: row.applicability } : {},
|
|
152089
|
-
origin: row.origin,
|
|
152090
|
-
createdAt: row.createdAt,
|
|
152091
|
-
updatedAt: row.updatedAt,
|
|
152092
|
-
...optionalText6(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
152093
|
-
...optionalText6(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
152094
|
-
...optionalText6(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
152095
|
-
...optionalText6(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
152096
|
-
...optionalText6(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
152097
|
-
accessCount: Number(row.accessCount ?? 0),
|
|
152098
|
-
...optionalText6(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
152099
|
-
};
|
|
152100
|
-
}
|
|
152101
|
-
function rowToEvidence2(row) {
|
|
152102
|
-
return {
|
|
152103
|
-
id: row.id,
|
|
152104
|
-
memoryId: row.memoryId,
|
|
152105
|
-
kind: row.kind,
|
|
152106
|
-
referenceId: row.referenceId,
|
|
152107
|
-
relation: row.relation,
|
|
152108
|
-
...optionalText6(row.locator) ? { locator: row.locator } : {},
|
|
152109
|
-
...optionalText6(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
152110
|
-
createdAt: row.createdAt
|
|
152111
|
-
};
|
|
152112
|
-
}
|
|
152113
|
-
function rowToEvent2(row) {
|
|
152114
|
-
return {
|
|
152115
|
-
id: row.id,
|
|
152116
|
-
memoryId: row.memoryId,
|
|
152117
|
-
kind: row.kind,
|
|
152118
|
-
...optionalText6(row.fromState) ? { fromState: row.fromState } : {},
|
|
152119
|
-
...optionalText6(row.toState) ? { toState: row.toState } : {},
|
|
152120
|
-
...optionalText6(row.detail) ? { detail: row.detail } : {},
|
|
152121
|
-
createdAt: row.createdAt
|
|
152122
|
-
};
|
|
152123
|
-
}
|
|
152124
|
-
var LongTermMemoryStore;
|
|
152125
|
-
var init_long_term_store = __esm({
|
|
152126
|
-
"src/memory/long-term-store.ts"() {
|
|
152127
|
-
"use strict";
|
|
152128
|
-
init_esm_shims();
|
|
152129
|
-
init_sqlite_db();
|
|
152130
|
-
LongTermMemoryStore = class {
|
|
152131
|
-
db = null;
|
|
152132
|
-
async init(dataDir) {
|
|
152133
|
-
this.db = getDatabase(dataDir);
|
|
152134
|
-
}
|
|
152135
|
-
transaction(fn) {
|
|
152136
|
-
return this.db.transaction(fn)();
|
|
152137
|
-
}
|
|
152138
|
-
get(id) {
|
|
152139
|
-
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
152140
|
-
return row ? rowToMemory(row) : void 0;
|
|
152141
|
-
}
|
|
152142
|
-
list(options = {}) {
|
|
152143
|
-
const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
152144
|
-
const clauses = [];
|
|
152145
|
-
const params = [];
|
|
152146
|
-
if (options.ownerId) {
|
|
152147
|
-
clauses.push("ownerId = ?");
|
|
152148
|
-
params.push(options.ownerId);
|
|
152149
|
-
}
|
|
152150
|
-
if (options.originProjectId) {
|
|
152151
|
-
clauses.push("originProjectId = ?");
|
|
152152
|
-
params.push(options.originProjectId);
|
|
152153
|
-
}
|
|
152154
|
-
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
152155
|
-
const rows = this.db.prepare(
|
|
152156
|
-
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
152157
|
-
).all(...params, limit2);
|
|
152158
|
-
return rows.map(rowToMemory);
|
|
152159
|
-
}
|
|
152160
|
-
insert(memory) {
|
|
152161
|
-
this.db.prepare(`
|
|
152162
|
-
INSERT INTO long_term_memories (
|
|
152163
|
-
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
152164
|
-
title, content, factsJson, tagsJson, applicability, origin,
|
|
152165
|
-
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
152166
|
-
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
152167
|
-
) VALUES (
|
|
152168
|
-
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
152169
|
-
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
152170
|
-
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
152171
|
-
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
152172
|
-
)
|
|
152173
|
-
`).run({
|
|
152174
|
-
...memory,
|
|
152175
|
-
factsJson: JSON.stringify(memory.facts),
|
|
152176
|
-
tagsJson: JSON.stringify(memory.tags),
|
|
152177
|
-
applicability: memory.applicability ?? null,
|
|
152178
|
-
qualifiedAt: memory.qualifiedAt ?? null,
|
|
152179
|
-
approvedAt: memory.approvedAt ?? null,
|
|
152180
|
-
archivedAt: memory.archivedAt ?? null,
|
|
152181
|
-
supersededBy: memory.supersededBy ?? null,
|
|
152182
|
-
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
152183
|
-
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
152184
|
-
});
|
|
152185
|
-
}
|
|
152186
|
-
update(memory) {
|
|
152187
|
-
this.db.prepare(`
|
|
152188
|
-
UPDATE long_term_memories SET
|
|
152189
|
-
scope = @scope,
|
|
152190
|
-
kind = @kind,
|
|
152191
|
-
state = @state,
|
|
152192
|
-
portability = @portability,
|
|
152193
|
-
title = @title,
|
|
152194
|
-
content = @content,
|
|
152195
|
-
factsJson = @factsJson,
|
|
152196
|
-
tagsJson = @tagsJson,
|
|
152197
|
-
applicability = @applicability,
|
|
152198
|
-
updatedAt = @updatedAt,
|
|
152199
|
-
qualifiedAt = @qualifiedAt,
|
|
152200
|
-
approvedAt = @approvedAt,
|
|
152201
|
-
archivedAt = @archivedAt,
|
|
152202
|
-
supersededBy = @supersededBy,
|
|
152203
|
-
lastValidatedAt = @lastValidatedAt,
|
|
152204
|
-
accessCount = @accessCount,
|
|
152205
|
-
lastAccessedAt = @lastAccessedAt
|
|
152206
|
-
WHERE id = @id
|
|
152207
|
-
`).run({
|
|
152208
|
-
...memory,
|
|
152209
|
-
factsJson: JSON.stringify(memory.facts),
|
|
152210
|
-
tagsJson: JSON.stringify(memory.tags),
|
|
152211
|
-
applicability: memory.applicability ?? null,
|
|
152212
|
-
qualifiedAt: memory.qualifiedAt ?? null,
|
|
152213
|
-
approvedAt: memory.approvedAt ?? null,
|
|
152214
|
-
archivedAt: memory.archivedAt ?? null,
|
|
152215
|
-
supersededBy: memory.supersededBy ?? null,
|
|
152216
|
-
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
152217
|
-
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
152218
|
-
});
|
|
152219
|
-
}
|
|
152220
|
-
insertEvidence(input) {
|
|
152221
|
-
const evidence = {
|
|
152222
|
-
id: randomUUID12(),
|
|
152223
|
-
...input,
|
|
152224
|
-
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
152225
|
-
};
|
|
152226
|
-
this.db.prepare(`
|
|
152227
|
-
INSERT INTO long_term_memory_evidence (
|
|
152228
|
-
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
152229
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
152230
|
-
`).run(
|
|
152231
|
-
evidence.id,
|
|
152232
|
-
evidence.memoryId,
|
|
152233
|
-
evidence.kind,
|
|
152234
|
-
evidence.referenceId,
|
|
152235
|
-
evidence.relation,
|
|
152236
|
-
evidence.locator ?? null,
|
|
152237
|
-
evidence.capturedHash ?? null,
|
|
152238
|
-
evidence.createdAt
|
|
152239
|
-
);
|
|
152240
|
-
return evidence;
|
|
152241
|
-
}
|
|
152242
|
-
listEvidence(memoryId) {
|
|
152243
|
-
return this.db.prepare(`
|
|
152244
|
-
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
152245
|
-
`).all(memoryId).map(rowToEvidence2);
|
|
152246
|
-
}
|
|
152247
|
-
recordEvent(input) {
|
|
152248
|
-
const event = {
|
|
152249
|
-
id: randomUUID12(),
|
|
152250
|
-
...input,
|
|
152251
|
-
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
152252
|
-
};
|
|
152253
|
-
this.db.prepare(`
|
|
152254
|
-
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
152255
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
152256
|
-
`).run(
|
|
152257
|
-
event.id,
|
|
152258
|
-
event.memoryId,
|
|
152259
|
-
event.kind,
|
|
152260
|
-
event.fromState ?? null,
|
|
152261
|
-
event.toState ?? null,
|
|
152262
|
-
event.detail ?? null,
|
|
152263
|
-
event.createdAt
|
|
152264
|
-
);
|
|
152265
|
-
return event;
|
|
152266
|
-
}
|
|
152267
|
-
listEvents(memoryId) {
|
|
152268
|
-
return this.db.prepare(`
|
|
152269
|
-
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
152270
|
-
`).all(memoryId).map(rowToEvent2);
|
|
152271
|
-
}
|
|
152272
|
-
markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
152273
|
-
const unique2 = [...new Set(ids.filter(Boolean))];
|
|
152274
|
-
if (unique2.length === 0) return;
|
|
152275
|
-
const statement = this.db.prepare(`
|
|
152276
|
-
UPDATE long_term_memories
|
|
152277
|
-
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
152278
|
-
WHERE id = ?
|
|
152279
|
-
`);
|
|
152280
|
-
this.db.transaction(() => {
|
|
152281
|
-
for (const id of unique2) statement.run(at, id);
|
|
152282
|
-
})();
|
|
152283
|
-
}
|
|
152284
|
-
};
|
|
152285
|
-
}
|
|
152286
|
-
});
|
|
152287
|
-
|
|
152288
|
-
// src/memory/long-term-types.ts
|
|
152289
|
-
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
152290
|
-
var init_long_term_types = __esm({
|
|
152291
|
-
"src/memory/long-term-types.ts"() {
|
|
152292
|
-
"use strict";
|
|
152293
|
-
init_esm_shims();
|
|
152294
|
-
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
152295
|
-
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
152296
|
-
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
152297
|
-
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
152298
|
-
"observation",
|
|
152299
|
-
"claim",
|
|
152300
|
-
"workflow",
|
|
152301
|
-
"session",
|
|
152302
|
-
"checkpoint",
|
|
152303
|
-
"git",
|
|
152304
|
-
"code",
|
|
152305
|
-
"test",
|
|
152306
|
-
"run",
|
|
152307
|
-
"document",
|
|
152308
|
-
"manual",
|
|
152309
|
-
"user"
|
|
152310
|
-
];
|
|
152311
|
-
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
152312
|
-
"supports",
|
|
152313
|
-
"verifies",
|
|
152314
|
-
"derives",
|
|
152315
|
-
"user-confirmed"
|
|
152316
|
-
];
|
|
152317
|
-
}
|
|
152318
|
-
});
|
|
152319
|
-
|
|
152320
|
-
// src/memory/long-term.ts
|
|
152321
|
-
var long_term_exports = {};
|
|
152322
|
-
__export(long_term_exports, {
|
|
152323
|
-
approveLongTermMemory: () => approveLongTermMemory,
|
|
152324
|
-
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
152325
|
-
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
152326
|
-
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
152327
|
-
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
152328
|
-
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
152329
|
-
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
152330
|
-
listLongTermMemories: () => listLongTermMemories,
|
|
152331
|
-
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
152332
|
-
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
152333
|
-
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
152334
|
-
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
152335
|
-
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
152336
|
-
});
|
|
152337
|
-
import { randomUUID as randomUUID13 } from "crypto";
|
|
152338
|
-
function isOneOf(value, values) {
|
|
152339
|
-
return typeof value === "string" && values.includes(value);
|
|
152340
|
-
}
|
|
152341
|
-
function compactText2(value, field, max = MAX_TEXT_LENGTH) {
|
|
152342
|
-
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
152343
|
-
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
152344
|
-
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
152345
|
-
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
152346
|
-
return safe;
|
|
152347
|
-
}
|
|
152348
|
-
function compactList(values, field) {
|
|
152349
|
-
if (!values) return [];
|
|
152350
|
-
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
152351
|
-
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
152352
|
-
}
|
|
152353
|
-
function normalizeEvidence(input) {
|
|
152354
|
-
if (!Array.isArray(input) || input.length === 0) {
|
|
152355
|
-
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
152356
|
-
}
|
|
152357
|
-
return input.map((evidence) => {
|
|
152358
|
-
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
152359
|
-
throw new Error("Long-term memory evidence kind is invalid.");
|
|
152360
|
-
}
|
|
152361
|
-
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
152362
|
-
throw new Error("Long-term memory evidence relation is invalid.");
|
|
152363
|
-
}
|
|
152364
|
-
return {
|
|
152365
|
-
kind: evidence.kind,
|
|
152366
|
-
referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
|
|
152367
|
-
relation: evidence.relation,
|
|
152368
|
-
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
|
|
152369
|
-
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
152370
|
-
};
|
|
152371
|
-
});
|
|
152372
|
-
}
|
|
152373
|
-
function assertScope(scope) {
|
|
152374
|
-
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
152375
|
-
}
|
|
152376
|
-
function assertKind(kind) {
|
|
152377
|
-
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
152378
|
-
}
|
|
152379
|
-
function assertPortability(value) {
|
|
152380
|
-
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
152381
|
-
throw new Error("Long-term memory portability is invalid.");
|
|
152382
|
-
}
|
|
152383
|
-
}
|
|
152384
|
-
function assertPortableEvidence(scope, portability, evidence) {
|
|
152385
|
-
if (portability !== "portable") return;
|
|
152386
|
-
if (scope !== "user") {
|
|
152387
|
-
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
152388
|
-
}
|
|
152389
|
-
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
152390
|
-
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.");
|
|
152391
|
-
}
|
|
152392
|
-
}
|
|
152393
|
-
function requireTeamReader(scope, reader) {
|
|
152394
|
-
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
152395
|
-
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
152396
|
-
}
|
|
152397
|
-
}
|
|
152398
|
-
function matchesScope(memory, reader) {
|
|
152399
|
-
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
152400
|
-
if (memory.scope === "team") {
|
|
152401
|
-
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
152402
|
-
}
|
|
152403
|
-
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
152404
|
-
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
152405
|
-
}
|
|
152406
|
-
function canReadLongTermMemory(memory, reader) {
|
|
152407
|
-
return matchesScope(memory, reader);
|
|
152408
|
-
}
|
|
152409
|
-
function isEligibleLongTermMemory(memory) {
|
|
152410
|
-
return memory.state === "qualified" || memory.state === "approved";
|
|
152411
|
-
}
|
|
152412
|
-
function taskTerms(task) {
|
|
152413
|
-
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
152414
|
-
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
152415
|
-
}
|
|
152416
|
-
function kindFitScore(kind, task) {
|
|
152417
|
-
const normalized = task.toLocaleLowerCase("en-US");
|
|
152418
|
-
const hints = {
|
|
152419
|
-
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
152420
|
-
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
152421
|
-
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
152422
|
-
};
|
|
152423
|
-
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
152424
|
-
}
|
|
152425
|
-
function validationFreshnessScore(memory) {
|
|
152426
|
-
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
152427
|
-
if (!validatedAt) return 0;
|
|
152428
|
-
const time = Date.parse(validatedAt);
|
|
152429
|
-
if (!Number.isFinite(time)) return 0;
|
|
152430
|
-
const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
|
|
152431
|
-
if (ageDays <= 30) return 0.35;
|
|
152432
|
-
if (ageDays <= 90) return 0.15;
|
|
152433
|
-
return 0;
|
|
152434
|
-
}
|
|
152435
|
-
function scoreForTask(memory, task) {
|
|
152436
|
-
const terms = taskTerms(task);
|
|
152437
|
-
if (terms.length === 0) return void 0;
|
|
152438
|
-
const title = memory.title.toLocaleLowerCase("en-US");
|
|
152439
|
-
const content = memory.content.toLocaleLowerCase("en-US");
|
|
152440
|
-
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
152441
|
-
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
152442
|
-
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
152443
|
-
let score = memory.state === "approved" ? 0.5 : 0;
|
|
152444
|
-
const matched = [];
|
|
152445
|
-
for (const term of terms) {
|
|
152446
|
-
let termScore = 0;
|
|
152447
|
-
if (title.includes(term)) termScore += 5;
|
|
152448
|
-
if (tags.includes(term)) termScore += 4;
|
|
152449
|
-
if (applicability.includes(term)) termScore += 3;
|
|
152450
|
-
if (facts.includes(term)) termScore += 2;
|
|
152451
|
-
if (content.includes(term)) termScore += 1;
|
|
152452
|
-
if (termScore > 0) {
|
|
152453
|
-
score += termScore;
|
|
152454
|
-
matched.push(term);
|
|
152455
|
-
}
|
|
152456
|
-
}
|
|
152457
|
-
if (matched.length === 0) return void 0;
|
|
152458
|
-
score += kindFitScore(memory.kind, task);
|
|
152459
|
-
score += validationFreshnessScore(memory);
|
|
152460
|
-
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
152461
|
-
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
152462
|
-
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
152463
|
-
}
|
|
152464
|
-
function semanticText(memory) {
|
|
152465
|
-
return sanitizeCredentials([
|
|
152466
|
-
memory.title,
|
|
152467
|
-
memory.applicability,
|
|
152468
|
-
memory.tags.join(" "),
|
|
152469
|
-
memory.facts.join(" "),
|
|
152470
|
-
memory.content
|
|
152471
|
-
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
152472
|
-
}
|
|
152473
|
-
function cosineSimilarity(left, right) {
|
|
152474
|
-
if (left.length === 0 || left.length !== right.length) return 0;
|
|
152475
|
-
let dot = 0;
|
|
152476
|
-
let leftMagnitude = 0;
|
|
152477
|
-
let rightMagnitude = 0;
|
|
152478
|
-
for (let index = 0; index < left.length; index += 1) {
|
|
152479
|
-
const a = left[index];
|
|
152480
|
-
const b = right[index];
|
|
152481
|
-
if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
|
|
152482
|
-
dot += a * b;
|
|
152483
|
-
leftMagnitude += a * a;
|
|
152484
|
-
rightMagnitude += b * b;
|
|
152485
|
-
}
|
|
152486
|
-
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
152487
|
-
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
152488
|
-
}
|
|
152489
|
-
function semanticSelection(memory, similarity, task) {
|
|
152490
|
-
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
152491
|
-
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
152492
|
-
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
152493
|
-
return {
|
|
152494
|
-
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
152495
|
-
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
152496
|
-
};
|
|
152497
|
-
}
|
|
152498
|
-
async function resolveSemanticProvider(explicit) {
|
|
152499
|
-
if (explicit !== void 0) return explicit;
|
|
152500
|
-
try {
|
|
152501
|
-
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
152502
|
-
return await withTimeout(
|
|
152503
|
-
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
152504
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
152505
|
-
"Long-term semantic provider initialization"
|
|
152506
|
-
);
|
|
152507
|
-
} catch {
|
|
152508
|
-
return null;
|
|
152509
|
-
}
|
|
152510
|
-
}
|
|
152511
|
-
function createStore(dataDir) {
|
|
152512
|
-
const store = new LongTermMemoryStore();
|
|
152513
|
-
return store.init(dataDir).then(() => store);
|
|
152514
|
-
}
|
|
152515
|
-
async function createManualLongTermMemory(input) {
|
|
152516
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
152517
|
-
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
152518
|
-
assertScope(input.scope);
|
|
152519
|
-
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID13(), relation: "supports" }];
|
|
152520
|
-
return createLongTermMemoryWithDataDir({
|
|
152521
|
-
dataDir: input.dataDir,
|
|
152522
|
-
originProjectId: input.projectId,
|
|
152523
|
-
ownerId: owner.id,
|
|
152524
|
-
scope: input.scope,
|
|
152525
|
-
kind: input.kind,
|
|
152526
|
-
portability: input.portability,
|
|
152527
|
-
title: input.title,
|
|
152528
|
-
content: input.content,
|
|
152529
|
-
facts: input.facts,
|
|
152530
|
-
tags: input.tags,
|
|
152531
|
-
applicability: input.applicability,
|
|
152532
|
-
origin: "manual",
|
|
152533
|
-
evidence,
|
|
152534
|
-
reader: input.reader
|
|
152535
|
-
});
|
|
152536
|
-
}
|
|
152537
|
-
async function promoteObservationToLongTermMemory(input) {
|
|
152538
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
152539
|
-
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
152540
|
-
return createLongTermMemoryWithDataDir({
|
|
152541
|
-
dataDir: input.dataDir,
|
|
152542
|
-
originProjectId: input.observation.projectId,
|
|
152543
|
-
ownerId: owner.id,
|
|
152544
|
-
scope: input.scope,
|
|
152545
|
-
kind: input.kind,
|
|
152546
|
-
portability: "project-bound",
|
|
152547
|
-
title: input.observation.title,
|
|
152548
|
-
content: input.observation.narrative,
|
|
152549
|
-
facts: input.observation.facts,
|
|
152550
|
-
tags: input.tags ?? input.observation.concepts,
|
|
152551
|
-
applicability: input.applicability,
|
|
152552
|
-
origin: input.observation.source === "git" ? "git" : "agent",
|
|
152553
|
-
evidence: [{
|
|
152554
|
-
kind: "observation",
|
|
152555
|
-
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
152556
|
-
relation: "derives"
|
|
152557
|
-
}],
|
|
152558
|
-
reader: input.reader
|
|
152559
|
-
});
|
|
152560
|
-
}
|
|
152561
|
-
async function createLongTermMemoryCandidate(input) {
|
|
152562
|
-
return createLongTermMemoryWithDataDir(input);
|
|
152563
|
-
}
|
|
152564
|
-
async function createLongTermMemoryWithDataDir(input) {
|
|
152565
|
-
assertScope(input.scope);
|
|
152566
|
-
assertKind(input.kind);
|
|
152567
|
-
const portability = input.portability ?? "project-bound";
|
|
152568
|
-
assertPortability(portability);
|
|
152569
|
-
requireTeamReader(input.scope, input.reader);
|
|
152570
|
-
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
152571
|
-
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
152572
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
152573
|
-
const memory = {
|
|
152574
|
-
id: randomUUID13(),
|
|
152575
|
-
originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
|
|
152576
|
-
ownerId: compactText2(input.ownerId, "owner id", 160),
|
|
152577
|
-
scope: input.scope,
|
|
152578
|
-
kind: input.kind,
|
|
152579
|
-
state: "candidate",
|
|
152580
|
-
portability,
|
|
152581
|
-
title: compactText2(input.title, "title", 500),
|
|
152582
|
-
content: compactText2(input.content, "content"),
|
|
152583
|
-
facts: compactList(input.facts, "facts"),
|
|
152584
|
-
tags: compactList(input.tags, "tags"),
|
|
152585
|
-
...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
|
|
152586
|
-
origin: input.origin ?? "manual",
|
|
152587
|
-
createdAt: now3,
|
|
152588
|
-
updatedAt: now3,
|
|
152589
|
-
accessCount: 0
|
|
152590
|
-
};
|
|
152591
|
-
const store = await createStore(input.dataDir);
|
|
152592
|
-
const evidence = store.transaction(() => {
|
|
152593
|
-
store.insert(memory);
|
|
152594
|
-
const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
152595
|
-
store.recordEvent({
|
|
152596
|
-
memoryId: memory.id,
|
|
152597
|
-
kind: "created",
|
|
152598
|
-
toState: "candidate",
|
|
152599
|
-
detail: "Created as a source-backed long-term memory candidate.",
|
|
152600
|
-
createdAt: now3
|
|
152601
|
-
});
|
|
152602
|
-
return written;
|
|
152603
|
-
});
|
|
152604
|
-
return { memory, evidence };
|
|
152605
|
-
}
|
|
152606
|
-
function transitionDetail(value) {
|
|
152607
|
-
return compactText2(value, "transition reason", 1e3);
|
|
152608
|
-
}
|
|
152609
|
-
async function transitionMemory(input) {
|
|
152610
|
-
const store = await createStore(input.dataDir);
|
|
152611
|
-
return store.transaction(() => {
|
|
152612
|
-
const current = store.get(input.id);
|
|
152613
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
152614
|
-
if (current.state !== input.expected) {
|
|
152615
|
-
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
152616
|
-
}
|
|
152617
|
-
const evidence = store.listEvidence(current.id);
|
|
152618
|
-
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
152619
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
152620
|
-
const next = {
|
|
152621
|
-
...current,
|
|
152622
|
-
state: input.next,
|
|
152623
|
-
updatedAt: now3,
|
|
152624
|
-
lastValidatedAt: now3,
|
|
152625
|
-
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
152626
|
-
};
|
|
152627
|
-
store.update(next);
|
|
152628
|
-
store.recordEvent({
|
|
152629
|
-
memoryId: next.id,
|
|
152630
|
-
kind: input.event,
|
|
152631
|
-
fromState: current.state,
|
|
152632
|
-
toState: next.state,
|
|
152633
|
-
detail: transitionDetail(input.reason),
|
|
152634
|
-
createdAt: now3
|
|
152635
|
-
});
|
|
152636
|
-
return next;
|
|
152637
|
-
});
|
|
152638
|
-
}
|
|
152639
|
-
async function qualifyLongTermMemory(input) {
|
|
152640
|
-
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
152641
|
-
}
|
|
152642
|
-
async function approveLongTermMemory(input) {
|
|
152643
|
-
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
152644
|
-
}
|
|
152645
|
-
async function archiveLongTermMemory(input) {
|
|
152646
|
-
const store = await createStore(input.dataDir);
|
|
152647
|
-
return store.transaction(() => {
|
|
152648
|
-
const current = store.get(input.id);
|
|
152649
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
152650
|
-
if (current.state === "archived" || current.state === "superseded") {
|
|
152651
|
-
throw new Error("Long-term memory is already inactive.");
|
|
152652
|
-
}
|
|
152653
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
152654
|
-
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
152655
|
-
store.update(next);
|
|
152656
|
-
store.recordEvent({
|
|
152657
|
-
memoryId: next.id,
|
|
152658
|
-
kind: "archived",
|
|
152659
|
-
fromState: current.state,
|
|
152660
|
-
toState: "archived",
|
|
152661
|
-
detail: transitionDetail(input.reason),
|
|
152662
|
-
createdAt: now3
|
|
152663
|
-
});
|
|
152664
|
-
return next;
|
|
152665
|
-
});
|
|
152666
|
-
}
|
|
152667
|
-
async function supersedeLongTermMemory(input) {
|
|
152668
|
-
const store = await createStore(input.dataDir);
|
|
152669
|
-
return store.transaction(() => {
|
|
152670
|
-
const current = store.get(input.id);
|
|
152671
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
152672
|
-
if (current.state === "archived" || current.state === "superseded") {
|
|
152673
|
-
throw new Error("Long-term memory is already inactive.");
|
|
152674
|
-
}
|
|
152675
|
-
if (current.id === input.supersededBy) {
|
|
152676
|
-
throw new Error("Long-term memory cannot supersede itself.");
|
|
152677
|
-
}
|
|
152678
|
-
const replacement = store.get(input.supersededBy);
|
|
152679
|
-
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
152680
|
-
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
152681
|
-
}
|
|
152682
|
-
if (replacement.scope !== current.scope) {
|
|
152683
|
-
throw new Error("Replacement long-term memory must use the same scope.");
|
|
152684
|
-
}
|
|
152685
|
-
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
152686
|
-
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
152687
|
-
}
|
|
152688
|
-
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
152689
|
-
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
152690
|
-
}
|
|
152691
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
152692
|
-
const next = {
|
|
152693
|
-
...current,
|
|
152694
|
-
state: "superseded",
|
|
152695
|
-
supersededBy: replacement.id,
|
|
152696
|
-
updatedAt: now3
|
|
152697
|
-
};
|
|
152698
|
-
store.update(next);
|
|
152699
|
-
store.recordEvent({
|
|
152700
|
-
memoryId: next.id,
|
|
152701
|
-
kind: "superseded",
|
|
152702
|
-
fromState: current.state,
|
|
152703
|
-
toState: "superseded",
|
|
152704
|
-
detail: transitionDetail(input.reason),
|
|
152705
|
-
createdAt: now3
|
|
152706
|
-
});
|
|
152707
|
-
return next;
|
|
152708
|
-
});
|
|
152709
|
-
}
|
|
152710
|
-
async function listLongTermMemories(input) {
|
|
152711
|
-
const store = await createStore(input.dataDir);
|
|
152712
|
-
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) }));
|
|
152713
|
-
}
|
|
152714
|
-
async function getLongTermMemoryDetail(input) {
|
|
152715
|
-
const store = await createStore(input.dataDir);
|
|
152716
|
-
const memory = store.get(input.id);
|
|
152717
|
-
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
152718
|
-
throw new Error("Long-term memory was not found in the active scope.");
|
|
152719
|
-
}
|
|
152720
|
-
store.markAccess([memory.id]);
|
|
152721
|
-
return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
|
|
152722
|
-
}
|
|
152723
|
-
async function selectLongTermMemoriesForTask(input) {
|
|
152724
|
-
const task = input.task?.trim();
|
|
152725
|
-
if (!task) return [];
|
|
152726
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
152727
|
-
const reader = {
|
|
152728
|
-
projectId: input.projectId,
|
|
152729
|
-
...owner ? { ownerId: owner.id } : {},
|
|
152730
|
-
...input.agentId ? { agentId: input.agentId } : {},
|
|
152731
|
-
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
152732
|
-
};
|
|
152733
|
-
const store = await createStore(input.dataDir);
|
|
152734
|
-
const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
152735
|
-
let selected = eligible.map((memory) => {
|
|
152736
|
-
const ranked = scoreForTask(memory, task);
|
|
152737
|
-
return ranked ? {
|
|
152738
|
-
memory,
|
|
152739
|
-
evidence: store.listEvidence(memory.id),
|
|
152740
|
-
score: ranked.score,
|
|
152741
|
-
reason: ranked.reason
|
|
152742
|
-
} : void 0;
|
|
152743
|
-
}).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));
|
|
152744
|
-
if (selected.length === 0 && eligible.length > 0) {
|
|
152745
|
-
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
152746
|
-
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
152747
|
-
if (provider2 && candidates.length > 0) {
|
|
152748
|
-
try {
|
|
152749
|
-
const vectors = await withTimeout(
|
|
152750
|
-
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
152751
|
-
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
152752
|
-
retry: false
|
|
152753
|
-
}),
|
|
152754
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
152755
|
-
"Long-term semantic retrieval"
|
|
152756
|
-
);
|
|
152757
|
-
const query = vectors[0];
|
|
152758
|
-
if (Array.isArray(query)) {
|
|
152759
|
-
selected = candidates.map((memory, index) => {
|
|
152760
|
-
const vector = vectors[index + 1];
|
|
152761
|
-
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
152762
|
-
return ranked ? {
|
|
152763
|
-
memory,
|
|
152764
|
-
evidence: store.listEvidence(memory.id),
|
|
152765
|
-
score: ranked.score,
|
|
152766
|
-
reason: ranked.reason
|
|
152767
|
-
} : void 0;
|
|
152768
|
-
}).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));
|
|
152769
|
-
}
|
|
152770
|
-
} catch {
|
|
152771
|
-
}
|
|
152772
|
-
}
|
|
152773
|
-
}
|
|
152774
|
-
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
152775
|
-
store.markAccess(selected.map((item) => item.memory.id));
|
|
152776
|
-
return selected;
|
|
152777
|
-
}
|
|
152778
|
-
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
152779
|
-
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
152780
|
-
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
152781
|
-
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
152782
|
-
}
|
|
152783
|
-
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
152784
|
-
var init_long_term = __esm({
|
|
152785
|
-
"src/memory/long-term.ts"() {
|
|
152786
|
-
"use strict";
|
|
152787
|
-
init_esm_shims();
|
|
152788
|
-
init_token_budget();
|
|
152789
|
-
init_timeout();
|
|
152790
|
-
init_secret_filter();
|
|
152791
|
-
init_owner();
|
|
152792
|
-
init_long_term_store();
|
|
152793
|
-
init_long_term_types();
|
|
152794
|
-
MAX_TEXT_LENGTH = 12e3;
|
|
152795
|
-
MAX_LIST_ITEMS = 40;
|
|
152796
|
-
MAX_SEMANTIC_CANDIDATES = 48;
|
|
152797
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
152798
|
-
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
152799
|
-
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
152800
|
-
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
152801
|
-
"the",
|
|
152802
|
-
"and",
|
|
152803
|
-
"for",
|
|
152804
|
-
"with",
|
|
152805
|
-
"from",
|
|
152806
|
-
"that",
|
|
152807
|
-
"this",
|
|
152808
|
-
"into",
|
|
152809
|
-
"when",
|
|
152810
|
-
"what",
|
|
152811
|
-
"\u5982\u4F55",
|
|
152812
|
-
"\u4EC0\u4E48",
|
|
152813
|
-
"\u8FD9\u4E2A",
|
|
152814
|
-
"\u90A3\u4E2A",
|
|
152815
|
-
"\u6211\u4EEC",
|
|
152816
|
-
"\u9879\u76EE",
|
|
152817
|
-
"\u8FDB\u884C",
|
|
152818
|
-
"\u9700\u8981",
|
|
152819
|
-
"\u7EE7\u7EED",
|
|
152820
|
-
"\u5904\u7406"
|
|
152821
|
-
]);
|
|
152822
|
-
}
|
|
152823
|
-
});
|
|
152824
|
-
|
|
152825
152931
|
// src/knowledge/workset.ts
|
|
152826
152932
|
function unique(values) {
|
|
152827
152933
|
return [...new Set(values.filter(Boolean))];
|
|
@@ -153029,6 +153135,37 @@ function renderTaskWorksetPrompt(input) {
|
|
|
153029
153135
|
trust: "source-backed"
|
|
153030
153136
|
});
|
|
153031
153137
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
153138
|
+
if (input.alwaysOn) {
|
|
153139
|
+
const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
|
|
153140
|
+
if (hasAlwaysOn) {
|
|
153141
|
+
appendLine(lines, "", maxTokens, omitted, "always-on-heading");
|
|
153142
|
+
appendLine(lines, "You and this workspace", maxTokens, omitted, "always-on-heading");
|
|
153143
|
+
for (const profile of input.alwaysOn.profile.slice(0, 2)) {
|
|
153144
|
+
appendLine(lines, "- " + short(profile, 26), maxTokens, omitted, "always-on-profile", selected, {
|
|
153145
|
+
kind: "memory",
|
|
153146
|
+
id: "profile",
|
|
153147
|
+
reason: "user profile memory",
|
|
153148
|
+
trust: "source-backed"
|
|
153149
|
+
});
|
|
153150
|
+
}
|
|
153151
|
+
if (input.alwaysOn.state) {
|
|
153152
|
+
appendLine(lines, "- Recently: " + short(input.alwaysOn.state, 40), maxTokens, omitted, "always-on-state", selected, {
|
|
153153
|
+
kind: "continuation",
|
|
153154
|
+
id: "workspace-state",
|
|
153155
|
+
reason: "latest session summary for this workspace",
|
|
153156
|
+
trust: "historical"
|
|
153157
|
+
});
|
|
153158
|
+
}
|
|
153159
|
+
for (const durable of input.alwaysOn.durable.slice(0, 2)) {
|
|
153160
|
+
appendLine(lines, "- " + short(durable, 28), maxTokens, omitted, "always-on-durable", selected, {
|
|
153161
|
+
kind: "durable-memory",
|
|
153162
|
+
id: "durable-always",
|
|
153163
|
+
reason: "recent durable memory for this workspace",
|
|
153164
|
+
trust: "source-backed"
|
|
153165
|
+
});
|
|
153166
|
+
}
|
|
153167
|
+
}
|
|
153168
|
+
}
|
|
153032
153169
|
const hasContinuation = Boolean(
|
|
153033
153170
|
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
|
|
153034
153171
|
);
|
|
@@ -153499,6 +153636,7 @@ async function buildTaskWorkset(input) {
|
|
|
153499
153636
|
version: "1.3",
|
|
153500
153637
|
task,
|
|
153501
153638
|
lens: input.lens,
|
|
153639
|
+
...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
|
|
153502
153640
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
153503
153641
|
...continuation ? { continuation } : {},
|
|
153504
153642
|
...input.codeState ? { codeState: short(input.codeState, 28) } : {},
|
|
@@ -154651,6 +154789,33 @@ function activeProjectObservations(observations2, projectId) {
|
|
|
154651
154789
|
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
154652
154790
|
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
154653
154791
|
}
|
|
154792
|
+
async function buildAlwaysOnBlock(input) {
|
|
154793
|
+
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));
|
|
154794
|
+
let state;
|
|
154795
|
+
try {
|
|
154796
|
+
await initSessionStore(input.dataDir);
|
|
154797
|
+
const { getSessionStore: getSessionStore2 } = await Promise.resolve().then(() => (init_session_store(), session_store_exports));
|
|
154798
|
+
const sessions = await getSessionStore2().loadByProject(input.projectId);
|
|
154799
|
+
const latest = sessions.filter((session) => session.endedAt && session.summary?.trim()).sort((a, b) => Date.parse(b.endedAt ?? "") - Date.parse(a.endedAt ?? ""))[0];
|
|
154800
|
+
if (latest?.summary) state = compactContinuationText(latest.summary, 40);
|
|
154801
|
+
} catch {
|
|
154802
|
+
}
|
|
154803
|
+
const durable = [];
|
|
154804
|
+
try {
|
|
154805
|
+
const items = await listLongTermMemories({
|
|
154806
|
+
dataDir: input.dataDir,
|
|
154807
|
+
reader: {
|
|
154808
|
+
projectId: input.projectId,
|
|
154809
|
+
...input.reader.agentId ? { agentId: input.reader.agentId } : {},
|
|
154810
|
+
...input.reader.isTeamMember ? { isTeamMember: true } : {}
|
|
154811
|
+
},
|
|
154812
|
+
limit: 10
|
|
154813
|
+
});
|
|
154814
|
+
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)));
|
|
154815
|
+
} catch {
|
|
154816
|
+
}
|
|
154817
|
+
return { profile, state, durable };
|
|
154818
|
+
}
|
|
154654
154819
|
function decideRefresh(input) {
|
|
154655
154820
|
if (input.mode === "never") {
|
|
154656
154821
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -154855,12 +155020,19 @@ async function buildAutoProjectContext(input) {
|
|
|
154855
155020
|
};
|
|
154856
155021
|
}
|
|
154857
155022
|
}
|
|
155023
|
+
const alwaysOn = await buildAlwaysOnBlock({
|
|
155024
|
+
dataDir: input.dataDir,
|
|
155025
|
+
projectId: input.project.id,
|
|
155026
|
+
reader: input.reader ?? { projectId: input.project.id },
|
|
155027
|
+
eligible: deliveryEligibleProjectObservations(input.observations, input.project.id)
|
|
155028
|
+
});
|
|
154858
155029
|
const workset = await buildTaskWorkset({
|
|
154859
155030
|
projectId: input.project.id,
|
|
154860
155031
|
dataDir: input.dataDir,
|
|
154861
155032
|
...task ? { task } : {},
|
|
154862
155033
|
lens: lens.id,
|
|
154863
155034
|
startHere,
|
|
155035
|
+
...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
|
|
154864
155036
|
...externalOutline ? { semanticCode: externalOutline } : {},
|
|
154865
155037
|
providerQuality: providerQuality2,
|
|
154866
155038
|
currentFacts: worksetFactLines(currentFacts),
|
|
@@ -155043,7 +155215,16 @@ function formatAutoProjectContextSummary(context) {
|
|
|
155043
155215
|
lensLine(context),
|
|
155044
155216
|
"",
|
|
155045
155217
|
...formatCurrentFactsLines(context.currentFacts),
|
|
155046
|
-
""
|
|
155218
|
+
""
|
|
155219
|
+
].filter(Boolean);
|
|
155220
|
+
if (context.workset.alwaysOn && (context.workset.alwaysOn.profile.length > 0 || context.workset.alwaysOn.state || context.workset.alwaysOn.durable.length > 0)) {
|
|
155221
|
+
lines.push("You and this workspace");
|
|
155222
|
+
for (const profile of context.workset.alwaysOn.profile.slice(0, 2)) lines.push(`- ${profile}`);
|
|
155223
|
+
if (context.workset.alwaysOn.state) lines.push(`- Recently: ${context.workset.alwaysOn.state}`);
|
|
155224
|
+
for (const durable of context.workset.alwaysOn.durable.slice(0, 2)) lines.push(`- ${durable}`);
|
|
155225
|
+
lines.push("");
|
|
155226
|
+
}
|
|
155227
|
+
lines.push(...[
|
|
155047
155228
|
`- Code memory: ${context.overview.code.files} files / ${context.overview.code.symbols} symbols / ${context.overview.code.refs} memory links`,
|
|
155048
155229
|
`- Code provider: ${context.providerQuality.selected} (${context.providerQuality.selectedQuality})`,
|
|
155049
155230
|
`- Languages: ${formatLanguages(context.overview)}`,
|
|
@@ -155052,7 +155233,7 @@ function formatAutoProjectContextSummary(context) {
|
|
|
155052
155233
|
`- Refresh: ${context.refresh.message}`,
|
|
155053
155234
|
"",
|
|
155054
155235
|
"Start here"
|
|
155055
|
-
].filter(Boolean);
|
|
155236
|
+
].filter(Boolean));
|
|
155056
155237
|
if (startHere.length > 0) {
|
|
155057
155238
|
startHere.forEach((path34, index) => lines.push(`${index + 1}. ${path34}`));
|
|
155058
155239
|
} else {
|
|
@@ -155176,6 +155357,8 @@ var init_auto_context = __esm({
|
|
|
155176
155357
|
init_store();
|
|
155177
155358
|
init_admission();
|
|
155178
155359
|
init_session();
|
|
155360
|
+
init_long_term();
|
|
155361
|
+
init_visibility();
|
|
155179
155362
|
init_session_store();
|
|
155180
155363
|
init_task_lens();
|
|
155181
155364
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
@@ -158126,8 +158309,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
158126
158309
|
const { promises: fsP } = await import("fs");
|
|
158127
158310
|
const registryPath = path30.join(baseDir, ".project-aliases.json");
|
|
158128
158311
|
const raw = await fsP.readFile(registryPath, "utf-8");
|
|
158129
|
-
const
|
|
158130
|
-
aliasGroups =
|
|
158312
|
+
const registry2 = JSON.parse(raw);
|
|
158313
|
+
aliasGroups = registry2.groups || [];
|
|
158131
158314
|
} catch {
|
|
158132
158315
|
}
|
|
158133
158316
|
const currentGroup = aliasGroups.find((g) => g.aliases?.includes(effectiveProjectId) || g.canonical === effectiveProjectId);
|
|
@@ -161043,13 +161226,28 @@ alwaysApply: true
|
|
|
161043
161226
|
'- For "why" decisions, use `memorix_store_reasoning`',
|
|
161044
161227
|
'- 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`.',
|
|
161045
161228
|
"- 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.",
|
|
161229
|
+
'- 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.',
|
|
161046
161230
|
"",
|
|
161047
161231
|
"**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
|
|
161048
161232
|
"",
|
|
161233
|
+
"**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.",
|
|
161234
|
+
"",
|
|
161235
|
+
`**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.`,
|
|
161236
|
+
"",
|
|
161237
|
+
"**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.",
|
|
161238
|
+
"",
|
|
161049
161239
|
"## When to resolve memory",
|
|
161050
161240
|
"",
|
|
161051
161241
|
"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.",
|
|
161052
161242
|
"",
|
|
161243
|
+
"## End sessions with a summary",
|
|
161244
|
+
"",
|
|
161245
|
+
"When a session finishes, call `memorix_session_end` with a short structured summary so the next agent can resume. Recommended sections:",
|
|
161246
|
+
"- **Goal** \u2014 what this session was working on",
|
|
161247
|
+
"- **Discoveries** \u2014 findings, gotchas, learnings",
|
|
161248
|
+
"- **Accomplished** \u2014 completed items, plus PENDING items for the next session",
|
|
161249
|
+
"- **Relevant Files** \u2014 paths and what changed",
|
|
161250
|
+
"",
|
|
161053
161251
|
"## Tools quick reference",
|
|
161054
161252
|
"",
|
|
161055
161253
|
"| Tool | Use when |",
|
|
@@ -162644,8 +162842,8 @@ init_persistence();
|
|
|
162644
162842
|
|
|
162645
162843
|
// src/rules/syncer.ts
|
|
162646
162844
|
init_esm_shims();
|
|
162647
|
-
import { promises as
|
|
162648
|
-
import
|
|
162845
|
+
import { promises as fs17 } from "fs";
|
|
162846
|
+
import path25 from "path";
|
|
162649
162847
|
|
|
162650
162848
|
// src/rules/adapters/cursor.ts
|
|
162651
162849
|
init_esm_shims();
|
|
@@ -163308,8 +163506,8 @@ var RulesSyncer = class {
|
|
|
163308
163506
|
const found = await this.findFiles(scanPath);
|
|
163309
163507
|
for (const filePath of found) {
|
|
163310
163508
|
try {
|
|
163311
|
-
const content = await
|
|
163312
|
-
const relativePath =
|
|
163509
|
+
const content = await fs17.readFile(filePath, "utf-8");
|
|
163510
|
+
const relativePath = path25.relative(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
163313
163511
|
const parsed = entry.adapter.parse(relativePath, content);
|
|
163314
163512
|
rules.push(...parsed);
|
|
163315
163513
|
} catch {
|
|
@@ -163377,7 +163575,7 @@ var RulesSyncer = class {
|
|
|
163377
163575
|
const entries = [];
|
|
163378
163576
|
for (const adapter of this.adapters.values()) {
|
|
163379
163577
|
const absolutePaths = adapter.filePatterns.map(
|
|
163380
|
-
(p) =>
|
|
163578
|
+
(p) => path25.join(this.projectRoot, p)
|
|
163381
163579
|
);
|
|
163382
163580
|
entries.push({ adapter, paths: absolutePaths });
|
|
163383
163581
|
}
|
|
@@ -163385,13 +163583,13 @@ var RulesSyncer = class {
|
|
|
163385
163583
|
}
|
|
163386
163584
|
/** Find files matching a glob-like path (simple implementation) */
|
|
163387
163585
|
async findFiles(pattern) {
|
|
163388
|
-
const dir =
|
|
163389
|
-
const fileGlob =
|
|
163586
|
+
const dir = path25.dirname(pattern);
|
|
163587
|
+
const fileGlob = path25.basename(pattern);
|
|
163390
163588
|
try {
|
|
163391
|
-
const stat4 = await
|
|
163589
|
+
const stat4 = await fs17.stat(dir);
|
|
163392
163590
|
if (!stat4.isDirectory()) {
|
|
163393
163591
|
try {
|
|
163394
|
-
await
|
|
163592
|
+
await fs17.access(pattern);
|
|
163395
163593
|
return [pattern];
|
|
163396
163594
|
} catch {
|
|
163397
163595
|
return [];
|
|
@@ -163399,7 +163597,7 @@ var RulesSyncer = class {
|
|
|
163399
163597
|
}
|
|
163400
163598
|
} catch {
|
|
163401
163599
|
try {
|
|
163402
|
-
await
|
|
163600
|
+
await fs17.access(pattern);
|
|
163403
163601
|
return [pattern];
|
|
163404
163602
|
} catch {
|
|
163405
163603
|
return [];
|
|
@@ -163407,16 +163605,16 @@ var RulesSyncer = class {
|
|
|
163407
163605
|
}
|
|
163408
163606
|
if (fileGlob.includes("*")) {
|
|
163409
163607
|
try {
|
|
163410
|
-
const files = await
|
|
163608
|
+
const files = await fs17.readdir(dir);
|
|
163411
163609
|
const ext = fileGlob.replace("*", "");
|
|
163412
|
-
return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) =>
|
|
163610
|
+
return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) => path25.join(dir, f3));
|
|
163413
163611
|
} catch {
|
|
163414
163612
|
return [];
|
|
163415
163613
|
}
|
|
163416
163614
|
}
|
|
163417
163615
|
try {
|
|
163418
|
-
await
|
|
163419
|
-
return [
|
|
163616
|
+
await fs17.access(path25.join(dir, fileGlob));
|
|
163617
|
+
return [path25.join(dir, fileGlob)];
|
|
163420
163618
|
} catch {
|
|
163421
163619
|
return [];
|
|
163422
163620
|
}
|
|
@@ -164958,13 +165156,47 @@ function parseFormationTimeoutMs(raw) {
|
|
|
164958
165156
|
init_timeout();
|
|
164959
165157
|
init_secret_filter();
|
|
164960
165158
|
|
|
165159
|
+
// src/search/surfaced-registry.ts
|
|
165160
|
+
init_esm_shims();
|
|
165161
|
+
var MAX_KEYS = 64;
|
|
165162
|
+
var MAX_IDS = 200;
|
|
165163
|
+
var registry = /* @__PURE__ */ new Map();
|
|
165164
|
+
function touch(key) {
|
|
165165
|
+
const ids = registry.get(key);
|
|
165166
|
+
if (!ids) return [];
|
|
165167
|
+
registry.delete(key);
|
|
165168
|
+
registry.set(key, ids);
|
|
165169
|
+
return ids;
|
|
165170
|
+
}
|
|
165171
|
+
function getSurfacedIds(key) {
|
|
165172
|
+
return [...touch(key)];
|
|
165173
|
+
}
|
|
165174
|
+
function recordSurfacedIds(key, ids) {
|
|
165175
|
+
if (ids.length === 0) return;
|
|
165176
|
+
let list = registry.get(key) ?? [];
|
|
165177
|
+
const known = new Set(list);
|
|
165178
|
+
for (const id of ids) {
|
|
165179
|
+
if (known.has(id)) continue;
|
|
165180
|
+
known.add(id);
|
|
165181
|
+
list.push(id);
|
|
165182
|
+
}
|
|
165183
|
+
if (list.length > MAX_IDS) {
|
|
165184
|
+
list = list.slice(list.length - MAX_IDS);
|
|
165185
|
+
}
|
|
165186
|
+
registry.set(key, list);
|
|
165187
|
+
if (registry.size > MAX_KEYS) {
|
|
165188
|
+
const oldestKey = registry.keys().next().value;
|
|
165189
|
+
if (oldestKey !== void 0 && oldestKey !== key) registry.delete(oldestKey);
|
|
165190
|
+
}
|
|
165191
|
+
}
|
|
165192
|
+
|
|
164961
165193
|
// src/server/request-context.ts
|
|
164962
165194
|
init_esm_shims();
|
|
164963
|
-
import
|
|
165195
|
+
import path26 from "path";
|
|
164964
165196
|
function resolveRoot(projectRoot) {
|
|
164965
165197
|
const value = projectRoot.trim();
|
|
164966
165198
|
if (!value) throw new Error("Project binding requires a non-empty project root");
|
|
164967
|
-
return
|
|
165199
|
+
return path26.resolve(value);
|
|
164968
165200
|
}
|
|
164969
165201
|
var ProjectBindingController = class {
|
|
164970
165202
|
current;
|
|
@@ -165355,6 +165587,12 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
165355
165587
|
kind: "consolidation",
|
|
165356
165588
|
dedupeKey: "startup-consolidation"
|
|
165357
165589
|
});
|
|
165590
|
+
lifecycle.enqueueLongTermMaintenance({
|
|
165591
|
+
dataDir: projectDir2,
|
|
165592
|
+
projectId: project.id,
|
|
165593
|
+
source: "startup",
|
|
165594
|
+
queue
|
|
165595
|
+
});
|
|
165358
165596
|
}
|
|
165359
165597
|
try {
|
|
165360
165598
|
const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
@@ -165404,7 +165642,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
165404
165642
|
};
|
|
165405
165643
|
const server = existingServer ?? new McpServer({
|
|
165406
165644
|
name: "memorix",
|
|
165407
|
-
version: true ? "1.
|
|
165645
|
+
version: true ? "1.5.0" : "1.0.1"
|
|
165408
165646
|
});
|
|
165409
165647
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
165410
165648
|
server.registerTool = ((name, ...args) => {
|
|
@@ -165889,7 +166127,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
165889
166127
|
let longTermNote = "";
|
|
165890
166128
|
if (longTerm) {
|
|
165891
166129
|
try {
|
|
165892
|
-
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
166130
|
+
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2, maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
165893
166131
|
const promoted = await promoteObservationToLongTermMemory2({
|
|
165894
166132
|
dataDir: projectDir2,
|
|
165895
166133
|
observation: obs,
|
|
@@ -165903,7 +166141,24 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
165903
166141
|
...reader.isTeamMember ? { isTeamMember: true } : {}
|
|
165904
166142
|
}
|
|
165905
166143
|
});
|
|
165906
|
-
|
|
166144
|
+
const autoQualified = await maybeAutoQualifyLongTermMemory2({
|
|
166145
|
+
dataDir: projectDir2,
|
|
166146
|
+
id: promoted.memory.id,
|
|
166147
|
+
sourceDetail: "explicit"
|
|
166148
|
+
});
|
|
166149
|
+
if (autoQualified) {
|
|
166150
|
+
try {
|
|
166151
|
+
const { enqueueLongTermMaintenance: enqueueLongTermMaintenance2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
166152
|
+
enqueueLongTermMaintenance2({
|
|
166153
|
+
dataDir: projectDir2,
|
|
166154
|
+
projectId: project.id,
|
|
166155
|
+
source: "long-term:" + promoted.memory.id
|
|
166156
|
+
});
|
|
166157
|
+
} catch {
|
|
166158
|
+
}
|
|
166159
|
+
}
|
|
166160
|
+
longTermNote = autoQualified ? `
|
|
166161
|
+
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\`.` : `
|
|
165907
166162
|
Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
|
|
165908
166163
|
} catch (longTermError) {
|
|
165909
166164
|
longTermNote = `
|
|
@@ -166099,6 +166354,13 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166099
166354
|
const boundary = requireExplicitAutopilotExpansion("memorix_search", purpose);
|
|
166100
166355
|
if (boundary) return boundary;
|
|
166101
166356
|
}
|
|
166357
|
+
let surfacedKey = `project:${project.id}`;
|
|
166358
|
+
try {
|
|
166359
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
166360
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
166361
|
+
if (active) surfacedKey = `session:${active.id}`;
|
|
166362
|
+
} catch {
|
|
166363
|
+
}
|
|
166102
166364
|
return withFreshIndex(async () => {
|
|
166103
166365
|
const safeLimit2 = limit2 != null ? coerceNumber(limit2, 20) : void 0;
|
|
166104
166366
|
const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
|
|
@@ -166116,7 +166378,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166116
166378
|
status: status ?? "active",
|
|
166117
166379
|
source,
|
|
166118
166380
|
quality,
|
|
166119
|
-
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
166381
|
+
reader: getObservationReader(scope === "global" ? "global" : "project"),
|
|
166382
|
+
surfacedIds: getSurfacedIds(surfacedKey)
|
|
166120
166383
|
});
|
|
166121
166384
|
let result;
|
|
166122
166385
|
try {
|
|
@@ -166135,6 +166398,7 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166135
166398
|
}
|
|
166136
166399
|
throw error;
|
|
166137
166400
|
}
|
|
166401
|
+
recordSurfacedIds(surfacedKey, result.entries.map((entry) => entry.id));
|
|
166138
166402
|
const activeBoundary = getActiveAutopilotRetrievalBoundary();
|
|
166139
166403
|
if (scope !== "global" && activeBoundary && !force && result.entries.length > 0 && result.entries.every((entry) => activeBoundary.coveredObservationIds.has(entry.id))) {
|
|
166140
166404
|
const duplicate = blockCoveredAutopilotEvidence("memorix_search", result.entries.map((entry) => entry.id), force);
|
|
@@ -169097,13 +169361,26 @@ ${lines.join("\n")}` }] };
|
|
|
169097
169361
|
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";
|
|
169098
169362
|
return void 0;
|
|
169099
169363
|
}
|
|
169364
|
+
const mediaGenerationEnabled = process.env.MEMORIX_MCP_MEDIA_GENERATION === "1";
|
|
169365
|
+
const mediaTranscriptionEnabled = process.env.MEMORIX_MCP_MEDIA_TRANSCRIPTION === "1";
|
|
169366
|
+
const mediaGenerationOrTranscription = mediaGenerationEnabled || mediaTranscriptionEnabled;
|
|
169100
169367
|
server.registerTool(
|
|
169101
169368
|
"memorix_media",
|
|
169102
169369
|
{
|
|
169103
169370
|
title: "Manage Controlled Media",
|
|
169104
169371
|
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.",
|
|
169105
169372
|
inputSchema: {
|
|
169106
|
-
action: z626.enum([
|
|
169373
|
+
action: z626.enum([
|
|
169374
|
+
"import",
|
|
169375
|
+
"attach",
|
|
169376
|
+
"list",
|
|
169377
|
+
"show",
|
|
169378
|
+
"derive-pdf",
|
|
169379
|
+
"status",
|
|
169380
|
+
"cancel",
|
|
169381
|
+
...mediaTranscriptionEnabled ? ["derive-audio"] : [],
|
|
169382
|
+
...mediaGenerationEnabled ? ["generate-image", "generate-video"] : []
|
|
169383
|
+
]),
|
|
169107
169384
|
path: z626.string().optional().describe("Explicit local image/audio/video/PDF path for import."),
|
|
169108
169385
|
assetId: z626.string().optional().describe("Controlled MediaAsset ID for attach/show."),
|
|
169109
169386
|
jobId: z626.string().optional().describe("Durable media job ID for status."),
|
|
@@ -169111,20 +169388,26 @@ ${lines.join("\n")}` }] };
|
|
|
169111
169388
|
limit: z626.number().int().min(1).max(100).optional().describe("Maximum assets to list."),
|
|
169112
169389
|
title: z626.string().optional().describe("Observation title when attaching generated/imported output."),
|
|
169113
169390
|
narrative: z626.string().optional().describe("Short retrieval text when attaching an asset."),
|
|
169114
|
-
prompt: z626.string().optional().describe("Explicit MiniMax generation prompt."),
|
|
169115
|
-
image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
|
|
169116
|
-
model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
|
|
169117
|
-
provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
|
|
169118
|
-
language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription."),
|
|
169119
|
-
region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
|
|
169120
|
-
n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
|
|
169121
|
-
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."),
|
|
169122
|
-
width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
|
|
169123
|
-
height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
|
|
169124
|
-
duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds."),
|
|
169125
169391
|
maxPages: z626.number().int().min(1).max(100).optional().describe("Bounded PDF extraction page limit."),
|
|
169126
169392
|
maxChars: z626.number().int().min(1).max(6e4).optional().describe("Bounded PDF extraction character limit."),
|
|
169127
|
-
attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly.")
|
|
169393
|
+
attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly."),
|
|
169394
|
+
...mediaGenerationOrTranscription ? {
|
|
169395
|
+
model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
|
|
169396
|
+
prompt: z626.string().optional().describe("Explicit prompt for generation or transcription.")
|
|
169397
|
+
} : {},
|
|
169398
|
+
...mediaTranscriptionEnabled ? {
|
|
169399
|
+
provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
|
|
169400
|
+
language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription.")
|
|
169401
|
+
} : {},
|
|
169402
|
+
...mediaGenerationEnabled ? {
|
|
169403
|
+
image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
|
|
169404
|
+
region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
|
|
169405
|
+
n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
|
|
169406
|
+
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."),
|
|
169407
|
+
width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
|
|
169408
|
+
height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
|
|
169409
|
+
duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds.")
|
|
169410
|
+
} : {}
|
|
169128
169411
|
}
|
|
169129
169412
|
},
|
|
169130
169413
|
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 }) => {
|