memorix 1.4.5 → 1.4.6
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 +54 -0
- package/CLAUDE.md +7 -3
- package/README.md +5 -2
- package/dist/cli/index.js +1456 -1013
- package/dist/index.js +1265 -988
- 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 +1755 -829
- package/dist/media-audio-runner.js +1760 -835
- package/dist/media-video-runner.js +1755 -830
- package/dist/memcode-runtime/CHANGELOG.md +54 -0
- package/dist/sdk.js +1257 -987
- package/dist/types.d.ts +6 -0
- package/dist/vector-backfill-runner.d.ts +1 -1
- package/dist/vector-backfill-runner.js +1751 -826
- package/docs/1.4.5-AGENT-AUTO-MEMORY-RESEARCH.md +95 -0
- package/docs/API_REFERENCE.md +23 -4
- package/docs/CONFIGURATION.md +17 -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 +0 -5
- 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
|
@@ -3393,7 +3393,6 @@ __export(config_exports, {
|
|
|
3393
3393
|
getLLMProvider: () => getLLMProvider,
|
|
3394
3394
|
getLoadedEnvFiles: () => getLoadedEnvFiles,
|
|
3395
3395
|
getServerConfig: () => getServerConfig,
|
|
3396
|
-
getTeamConfig: () => getTeamConfig,
|
|
3397
3396
|
loadDotenv: () => loadDotenv,
|
|
3398
3397
|
loadFileConfig: () => loadFileConfig,
|
|
3399
3398
|
loadYamlConfig: () => loadYamlConfig,
|
|
@@ -3465,14 +3464,10 @@ function getServerConfig() {
|
|
|
3465
3464
|
port: server.port
|
|
3466
3465
|
};
|
|
3467
3466
|
}
|
|
3468
|
-
function getTeamConfig() {
|
|
3469
|
-
return loadYamlConfig().team ?? {};
|
|
3470
|
-
}
|
|
3471
3467
|
var init_config = __esm({
|
|
3472
3468
|
"src/config.ts"() {
|
|
3473
3469
|
"use strict";
|
|
3474
3470
|
init_esm_shims();
|
|
3475
|
-
init_yaml_loader();
|
|
3476
3471
|
init_resolved_config();
|
|
3477
3472
|
init_dotenv_loader();
|
|
3478
3473
|
init_legacy_loader();
|
|
@@ -5757,9 +5752,9 @@ async function saveRegistry(baseDir) {
|
|
|
5757
5752
|
await fs7.mkdir(path9.dirname(filePath), { recursive: true });
|
|
5758
5753
|
await fs7.writeFile(filePath, JSON.stringify(registryCache, null, 2), "utf-8");
|
|
5759
5754
|
}
|
|
5760
|
-
function findMatchingGroup(
|
|
5755
|
+
function findMatchingGroup(registry2, projectInfo) {
|
|
5761
5756
|
const normalizedRoot = normalizePath(projectInfo.rootPath);
|
|
5762
|
-
for (const group of
|
|
5757
|
+
for (const group of registry2.groups) {
|
|
5763
5758
|
if (group.aliases.includes(projectInfo.id)) return group;
|
|
5764
5759
|
if (group.rootPaths.some((rp) => rp === normalizedRoot)) return group;
|
|
5765
5760
|
if (projectInfo.gitRemote && group.gitRemote && group.gitRemote === projectInfo.gitRemote) {
|
|
@@ -5772,9 +5767,9 @@ function selectCanonical(aliases) {
|
|
|
5772
5767
|
return [...aliases].sort((a, b) => idPriority(b) - idPriority(a))[0];
|
|
5773
5768
|
}
|
|
5774
5769
|
async function registerAlias(projectInfo, baseDir) {
|
|
5775
|
-
const
|
|
5770
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5776
5771
|
const normalizedRoot = normalizePath(projectInfo.rootPath);
|
|
5777
|
-
const existingGroup = findMatchingGroup(
|
|
5772
|
+
const existingGroup = findMatchingGroup(registry2, projectInfo);
|
|
5778
5773
|
if (existingGroup) {
|
|
5779
5774
|
let changed = false;
|
|
5780
5775
|
if (!existingGroup.aliases.includes(projectInfo.id)) {
|
|
@@ -5805,13 +5800,13 @@ async function registerAlias(projectInfo, baseDir) {
|
|
|
5805
5800
|
rootPaths: [normalizedRoot],
|
|
5806
5801
|
...projectInfo.gitRemote ? { gitRemote: projectInfo.gitRemote } : {}
|
|
5807
5802
|
};
|
|
5808
|
-
|
|
5803
|
+
registry2.groups.push(newGroup);
|
|
5809
5804
|
await saveRegistry(baseDir);
|
|
5810
5805
|
return newGroup.canonical;
|
|
5811
5806
|
}
|
|
5812
5807
|
async function resolveAliases(projectId, baseDir) {
|
|
5813
|
-
const
|
|
5814
|
-
for (const group of
|
|
5808
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5809
|
+
for (const group of registry2.groups) {
|
|
5815
5810
|
if (group.aliases.includes(projectId) || group.canonical === projectId) {
|
|
5816
5811
|
return [...group.aliases];
|
|
5817
5812
|
}
|
|
@@ -5819,8 +5814,8 @@ async function resolveAliases(projectId, baseDir) {
|
|
|
5819
5814
|
return [projectId];
|
|
5820
5815
|
}
|
|
5821
5816
|
async function getCanonicalId(projectId, baseDir) {
|
|
5822
|
-
const
|
|
5823
|
-
for (const group of
|
|
5817
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5818
|
+
for (const group of registry2.groups) {
|
|
5824
5819
|
if (group.aliases.includes(projectId) || group.canonical === projectId) {
|
|
5825
5820
|
return group.canonical;
|
|
5826
5821
|
}
|
|
@@ -5828,12 +5823,12 @@ async function getCanonicalId(projectId, baseDir) {
|
|
|
5828
5823
|
return projectId;
|
|
5829
5824
|
}
|
|
5830
5825
|
async function getAllAliasGroups(baseDir) {
|
|
5831
|
-
const
|
|
5832
|
-
return
|
|
5826
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5827
|
+
return registry2.groups;
|
|
5833
5828
|
}
|
|
5834
5829
|
async function autoMergeByBaseName(observedIds, baseDir) {
|
|
5835
5830
|
if (observedIds.length <= 1) return 0;
|
|
5836
|
-
const
|
|
5831
|
+
const registry2 = await loadRegistry(baseDir);
|
|
5837
5832
|
const byBaseName = /* @__PURE__ */ new Map();
|
|
5838
5833
|
for (const id of observedIds) {
|
|
5839
5834
|
const baseName = id.split("/").pop() ?? id;
|
|
@@ -5846,7 +5841,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5846
5841
|
const existingGroups = /* @__PURE__ */ new Set();
|
|
5847
5842
|
const ungroupedIds = [];
|
|
5848
5843
|
for (const id of ids) {
|
|
5849
|
-
const groupIdx =
|
|
5844
|
+
const groupIdx = registry2.groups.findIndex(
|
|
5850
5845
|
(g) => g.aliases.includes(id) || g.canonical === id
|
|
5851
5846
|
);
|
|
5852
5847
|
if (groupIdx >= 0) {
|
|
@@ -5860,7 +5855,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5860
5855
|
const canonical = selectCanonical(allIdsInGroup);
|
|
5861
5856
|
if (existingGroups.size > 0) {
|
|
5862
5857
|
const primaryIdx = [...existingGroups][0];
|
|
5863
|
-
const primaryGroup =
|
|
5858
|
+
const primaryGroup = registry2.groups[primaryIdx];
|
|
5864
5859
|
for (const id of allIdsInGroup) {
|
|
5865
5860
|
if (!primaryGroup.aliases.includes(id)) {
|
|
5866
5861
|
primaryGroup.aliases.push(id);
|
|
@@ -5868,7 +5863,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5868
5863
|
}
|
|
5869
5864
|
const otherIdxs = [...existingGroups].slice(1).sort((a, b) => b - a);
|
|
5870
5865
|
for (const idx of otherIdxs) {
|
|
5871
|
-
const other =
|
|
5866
|
+
const other = registry2.groups[idx];
|
|
5872
5867
|
for (const alias of other.aliases) {
|
|
5873
5868
|
if (!primaryGroup.aliases.includes(alias)) {
|
|
5874
5869
|
primaryGroup.aliases.push(alias);
|
|
@@ -5882,12 +5877,12 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
5882
5877
|
if (other.gitRemote && !primaryGroup.gitRemote) {
|
|
5883
5878
|
primaryGroup.gitRemote = other.gitRemote;
|
|
5884
5879
|
}
|
|
5885
|
-
|
|
5880
|
+
registry2.groups.splice(idx, 1);
|
|
5886
5881
|
}
|
|
5887
5882
|
primaryGroup.canonical = selectCanonical(primaryGroup.aliases);
|
|
5888
5883
|
mergeCount++;
|
|
5889
5884
|
} else {
|
|
5890
|
-
|
|
5885
|
+
registry2.groups.push({
|
|
5891
5886
|
canonical,
|
|
5892
5887
|
aliases: allIdsInGroup,
|
|
5893
5888
|
rootPaths: []
|
|
@@ -6523,7 +6518,7 @@ async function searchObservations(options) {
|
|
|
6523
6518
|
const expandedEmbeddingQuery = quality === "thorough" && tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
|
|
6524
6519
|
mark("queryExpansion");
|
|
6525
6520
|
const intentResult = quality === "fast" || !hasQuery ? null : detectQueryIntent(originalQuery);
|
|
6526
|
-
const requestLimit = projectIds ? (options.limit ?? 20) * 3 : options.limit ?? 20;
|
|
6521
|
+
const requestLimit = projectIds || options.surfacedIds && options.surfacedIds.length > 0 ? (options.limit ?? 20) * 3 : options.limit ?? 20;
|
|
6527
6522
|
const defaultBoost = {
|
|
6528
6523
|
title: 3,
|
|
6529
6524
|
entityName: 2,
|
|
@@ -6755,6 +6750,14 @@ async function searchObservations(options) {
|
|
|
6755
6750
|
});
|
|
6756
6751
|
intermediate.sort((a, b) => b.score - a.score);
|
|
6757
6752
|
}
|
|
6753
|
+
if (options.surfacedIds && options.surfacedIds.length > 0) {
|
|
6754
|
+
const SURFACED_PENALTY = 0.5;
|
|
6755
|
+
const surfaced = new Set(options.surfacedIds);
|
|
6756
|
+
intermediate = intermediate.map(
|
|
6757
|
+
(entry) => surfaced.has(entry.id) ? { ...entry, score: (entry.score ?? 0) * SURFACED_PENALTY } : entry
|
|
6758
|
+
);
|
|
6759
|
+
intermediate.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
|
|
6760
|
+
}
|
|
6758
6761
|
if (options.since) {
|
|
6759
6762
|
const sinceDate = new Date(options.since).getTime();
|
|
6760
6763
|
intermediate = intermediate.filter((e2) => new Date(e2.rawTime).getTime() >= sinceDate);
|
|
@@ -7746,7 +7749,8 @@ var init_maintenance_jobs = __esm({
|
|
|
7746
7749
|
"claim-requalification",
|
|
7747
7750
|
"knowledge-compile",
|
|
7748
7751
|
"knowledge-lint",
|
|
7749
|
-
"workflow-index"
|
|
7752
|
+
"workflow-index",
|
|
7753
|
+
"long-term-maintenance"
|
|
7750
7754
|
];
|
|
7751
7755
|
DEFAULT_MAX_ATTEMPTS = 8;
|
|
7752
7756
|
DEFAULT_LEASE_MS = 3e4;
|
|
@@ -8155,6 +8159,7 @@ __export(lifecycle_exports, {
|
|
|
8155
8159
|
enqueueClaimRequalification: () => enqueueClaimRequalification,
|
|
8156
8160
|
enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
|
|
8157
8161
|
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
|
|
8162
|
+
enqueueLongTermMaintenance: () => enqueueLongTermMaintenance,
|
|
8158
8163
|
enqueueObservationQualification: () => enqueueObservationQualification
|
|
8159
8164
|
});
|
|
8160
8165
|
function queueFor(input) {
|
|
@@ -8198,6 +8203,14 @@ function enqueueObservationQualification(input) {
|
|
|
8198
8203
|
payload: { source: input.source, limit: 100 }
|
|
8199
8204
|
});
|
|
8200
8205
|
}
|
|
8206
|
+
function enqueueLongTermMaintenance(input) {
|
|
8207
|
+
queueFor(input).enqueue({
|
|
8208
|
+
projectId: input.projectId,
|
|
8209
|
+
kind: "long-term-maintenance",
|
|
8210
|
+
dedupeKey: "long-term-maintenance",
|
|
8211
|
+
payload: { source: input.source }
|
|
8212
|
+
});
|
|
8213
|
+
}
|
|
8201
8214
|
function enqueueKnowledgeFollowups(input) {
|
|
8202
8215
|
const queue = queueFor(input);
|
|
8203
8216
|
const payload = {
|
|
@@ -8377,7 +8390,8 @@ var init_isolated_maintenance = __esm({
|
|
|
8377
8390
|
"claim-requalification",
|
|
8378
8391
|
"knowledge-compile",
|
|
8379
8392
|
"knowledge-lint",
|
|
8380
|
-
"workflow-index"
|
|
8393
|
+
"workflow-index",
|
|
8394
|
+
"long-term-maintenance"
|
|
8381
8395
|
];
|
|
8382
8396
|
DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
8383
8397
|
MAX_CHILD_OUTPUT_BYTES = 1e6;
|
|
@@ -144224,6 +144238,889 @@ var init_retention = __esm({
|
|
|
144224
144238
|
}
|
|
144225
144239
|
});
|
|
144226
144240
|
|
|
144241
|
+
// src/memory/owner.ts
|
|
144242
|
+
var owner_exports = {};
|
|
144243
|
+
__export(owner_exports, {
|
|
144244
|
+
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
144245
|
+
});
|
|
144246
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
144247
|
+
import { promises as fs12 } from "fs";
|
|
144248
|
+
import path19 from "path";
|
|
144249
|
+
function ownerPath(dataDir) {
|
|
144250
|
+
const resolved = path19.resolve(dataDir);
|
|
144251
|
+
const home = path19.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path19.dirname(resolved) : resolved;
|
|
144252
|
+
return path19.join(home, "memorix-user.json");
|
|
144253
|
+
}
|
|
144254
|
+
function normalizedOwnerId(value) {
|
|
144255
|
+
const trimmed = value?.trim();
|
|
144256
|
+
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
144257
|
+
}
|
|
144258
|
+
async function readOwnerFile(filePath) {
|
|
144259
|
+
try {
|
|
144260
|
+
const value = JSON.parse(await fs12.readFile(filePath, "utf8"));
|
|
144261
|
+
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
144262
|
+
return id ? { id, source: "local-file" } : void 0;
|
|
144263
|
+
} catch {
|
|
144264
|
+
return void 0;
|
|
144265
|
+
}
|
|
144266
|
+
}
|
|
144267
|
+
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
144268
|
+
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
144269
|
+
if (configured) return { id: configured, source: "environment" };
|
|
144270
|
+
const filePath = ownerPath(dataDir);
|
|
144271
|
+
const existing = await readOwnerFile(filePath);
|
|
144272
|
+
if (existing || !options.create) return existing;
|
|
144273
|
+
await fs12.mkdir(path19.dirname(filePath), { recursive: true });
|
|
144274
|
+
const created = {
|
|
144275
|
+
version: 1,
|
|
144276
|
+
id: "local-" + randomUUID6(),
|
|
144277
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
144278
|
+
};
|
|
144279
|
+
try {
|
|
144280
|
+
await fs12.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
144281
|
+
return { id: created.id, source: "local-file" };
|
|
144282
|
+
} catch (error) {
|
|
144283
|
+
if (error?.code !== "EEXIST") throw error;
|
|
144284
|
+
const concurrent = await readOwnerFile(filePath);
|
|
144285
|
+
if (concurrent) return concurrent;
|
|
144286
|
+
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
144287
|
+
}
|
|
144288
|
+
}
|
|
144289
|
+
var ID_PATTERN;
|
|
144290
|
+
var init_owner = __esm({
|
|
144291
|
+
"src/memory/owner.ts"() {
|
|
144292
|
+
"use strict";
|
|
144293
|
+
init_esm_shims();
|
|
144294
|
+
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
144295
|
+
}
|
|
144296
|
+
});
|
|
144297
|
+
|
|
144298
|
+
// src/memory/long-term-store.ts
|
|
144299
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
144300
|
+
function optionalText2(value) {
|
|
144301
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
144302
|
+
}
|
|
144303
|
+
function arrayValue2(value) {
|
|
144304
|
+
if (typeof value !== "string" || !value) return [];
|
|
144305
|
+
try {
|
|
144306
|
+
const parsed = JSON.parse(value);
|
|
144307
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
144308
|
+
} catch {
|
|
144309
|
+
return [];
|
|
144310
|
+
}
|
|
144311
|
+
}
|
|
144312
|
+
function rowToMemory(row) {
|
|
144313
|
+
return {
|
|
144314
|
+
id: row.id,
|
|
144315
|
+
originProjectId: row.originProjectId,
|
|
144316
|
+
ownerId: row.ownerId,
|
|
144317
|
+
scope: row.scope,
|
|
144318
|
+
kind: row.kind,
|
|
144319
|
+
state: row.state,
|
|
144320
|
+
portability: row.portability,
|
|
144321
|
+
title: row.title,
|
|
144322
|
+
content: row.content,
|
|
144323
|
+
facts: arrayValue2(row.factsJson),
|
|
144324
|
+
tags: arrayValue2(row.tagsJson),
|
|
144325
|
+
...optionalText2(row.applicability) ? { applicability: row.applicability } : {},
|
|
144326
|
+
origin: row.origin,
|
|
144327
|
+
createdAt: row.createdAt,
|
|
144328
|
+
updatedAt: row.updatedAt,
|
|
144329
|
+
...optionalText2(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
144330
|
+
...optionalText2(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
144331
|
+
...optionalText2(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
144332
|
+
...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
144333
|
+
...optionalText2(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
144334
|
+
accessCount: Number(row.accessCount ?? 0),
|
|
144335
|
+
...optionalText2(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
144336
|
+
};
|
|
144337
|
+
}
|
|
144338
|
+
function rowToEvidence(row) {
|
|
144339
|
+
return {
|
|
144340
|
+
id: row.id,
|
|
144341
|
+
memoryId: row.memoryId,
|
|
144342
|
+
kind: row.kind,
|
|
144343
|
+
referenceId: row.referenceId,
|
|
144344
|
+
relation: row.relation,
|
|
144345
|
+
...optionalText2(row.locator) ? { locator: row.locator } : {},
|
|
144346
|
+
...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
144347
|
+
createdAt: row.createdAt
|
|
144348
|
+
};
|
|
144349
|
+
}
|
|
144350
|
+
function rowToEvent(row) {
|
|
144351
|
+
return {
|
|
144352
|
+
id: row.id,
|
|
144353
|
+
memoryId: row.memoryId,
|
|
144354
|
+
kind: row.kind,
|
|
144355
|
+
...optionalText2(row.fromState) ? { fromState: row.fromState } : {},
|
|
144356
|
+
...optionalText2(row.toState) ? { toState: row.toState } : {},
|
|
144357
|
+
...optionalText2(row.detail) ? { detail: row.detail } : {},
|
|
144358
|
+
createdAt: row.createdAt
|
|
144359
|
+
};
|
|
144360
|
+
}
|
|
144361
|
+
var LongTermMemoryStore;
|
|
144362
|
+
var init_long_term_store = __esm({
|
|
144363
|
+
"src/memory/long-term-store.ts"() {
|
|
144364
|
+
"use strict";
|
|
144365
|
+
init_esm_shims();
|
|
144366
|
+
init_sqlite_db();
|
|
144367
|
+
LongTermMemoryStore = class {
|
|
144368
|
+
db = null;
|
|
144369
|
+
async init(dataDir) {
|
|
144370
|
+
this.db = getDatabase(dataDir);
|
|
144371
|
+
}
|
|
144372
|
+
transaction(fn) {
|
|
144373
|
+
return this.db.transaction(fn)();
|
|
144374
|
+
}
|
|
144375
|
+
get(id) {
|
|
144376
|
+
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
144377
|
+
return row ? rowToMemory(row) : void 0;
|
|
144378
|
+
}
|
|
144379
|
+
list(options = {}) {
|
|
144380
|
+
const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
144381
|
+
const clauses = [];
|
|
144382
|
+
const params = [];
|
|
144383
|
+
if (options.ownerId) {
|
|
144384
|
+
clauses.push("ownerId = ?");
|
|
144385
|
+
params.push(options.ownerId);
|
|
144386
|
+
}
|
|
144387
|
+
if (options.originProjectId) {
|
|
144388
|
+
clauses.push("originProjectId = ?");
|
|
144389
|
+
params.push(options.originProjectId);
|
|
144390
|
+
}
|
|
144391
|
+
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
144392
|
+
const rows = this.db.prepare(
|
|
144393
|
+
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
144394
|
+
).all(...params, limit2);
|
|
144395
|
+
return rows.map(rowToMemory);
|
|
144396
|
+
}
|
|
144397
|
+
insert(memory) {
|
|
144398
|
+
this.db.prepare(`
|
|
144399
|
+
INSERT INTO long_term_memories (
|
|
144400
|
+
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
144401
|
+
title, content, factsJson, tagsJson, applicability, origin,
|
|
144402
|
+
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
144403
|
+
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
144404
|
+
) VALUES (
|
|
144405
|
+
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
144406
|
+
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
144407
|
+
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
144408
|
+
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
144409
|
+
)
|
|
144410
|
+
`).run({
|
|
144411
|
+
...memory,
|
|
144412
|
+
factsJson: JSON.stringify(memory.facts),
|
|
144413
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
144414
|
+
applicability: memory.applicability ?? null,
|
|
144415
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
144416
|
+
approvedAt: memory.approvedAt ?? null,
|
|
144417
|
+
archivedAt: memory.archivedAt ?? null,
|
|
144418
|
+
supersededBy: memory.supersededBy ?? null,
|
|
144419
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
144420
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
144421
|
+
});
|
|
144422
|
+
}
|
|
144423
|
+
update(memory) {
|
|
144424
|
+
this.db.prepare(`
|
|
144425
|
+
UPDATE long_term_memories SET
|
|
144426
|
+
scope = @scope,
|
|
144427
|
+
kind = @kind,
|
|
144428
|
+
state = @state,
|
|
144429
|
+
portability = @portability,
|
|
144430
|
+
title = @title,
|
|
144431
|
+
content = @content,
|
|
144432
|
+
factsJson = @factsJson,
|
|
144433
|
+
tagsJson = @tagsJson,
|
|
144434
|
+
applicability = @applicability,
|
|
144435
|
+
updatedAt = @updatedAt,
|
|
144436
|
+
qualifiedAt = @qualifiedAt,
|
|
144437
|
+
approvedAt = @approvedAt,
|
|
144438
|
+
archivedAt = @archivedAt,
|
|
144439
|
+
supersededBy = @supersededBy,
|
|
144440
|
+
lastValidatedAt = @lastValidatedAt,
|
|
144441
|
+
accessCount = @accessCount,
|
|
144442
|
+
lastAccessedAt = @lastAccessedAt
|
|
144443
|
+
WHERE id = @id
|
|
144444
|
+
`).run({
|
|
144445
|
+
...memory,
|
|
144446
|
+
factsJson: JSON.stringify(memory.facts),
|
|
144447
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
144448
|
+
applicability: memory.applicability ?? null,
|
|
144449
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
144450
|
+
approvedAt: memory.approvedAt ?? null,
|
|
144451
|
+
archivedAt: memory.archivedAt ?? null,
|
|
144452
|
+
supersededBy: memory.supersededBy ?? null,
|
|
144453
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
144454
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
144455
|
+
});
|
|
144456
|
+
}
|
|
144457
|
+
insertEvidence(input) {
|
|
144458
|
+
const evidence = {
|
|
144459
|
+
id: randomUUID7(),
|
|
144460
|
+
...input,
|
|
144461
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
144462
|
+
};
|
|
144463
|
+
this.db.prepare(`
|
|
144464
|
+
INSERT INTO long_term_memory_evidence (
|
|
144465
|
+
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
144466
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
144467
|
+
`).run(
|
|
144468
|
+
evidence.id,
|
|
144469
|
+
evidence.memoryId,
|
|
144470
|
+
evidence.kind,
|
|
144471
|
+
evidence.referenceId,
|
|
144472
|
+
evidence.relation,
|
|
144473
|
+
evidence.locator ?? null,
|
|
144474
|
+
evidence.capturedHash ?? null,
|
|
144475
|
+
evidence.createdAt
|
|
144476
|
+
);
|
|
144477
|
+
return evidence;
|
|
144478
|
+
}
|
|
144479
|
+
listEvidence(memoryId) {
|
|
144480
|
+
return this.db.prepare(`
|
|
144481
|
+
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
144482
|
+
`).all(memoryId).map(rowToEvidence);
|
|
144483
|
+
}
|
|
144484
|
+
recordEvent(input) {
|
|
144485
|
+
const event = {
|
|
144486
|
+
id: randomUUID7(),
|
|
144487
|
+
...input,
|
|
144488
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
144489
|
+
};
|
|
144490
|
+
this.db.prepare(`
|
|
144491
|
+
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
144492
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
144493
|
+
`).run(
|
|
144494
|
+
event.id,
|
|
144495
|
+
event.memoryId,
|
|
144496
|
+
event.kind,
|
|
144497
|
+
event.fromState ?? null,
|
|
144498
|
+
event.toState ?? null,
|
|
144499
|
+
event.detail ?? null,
|
|
144500
|
+
event.createdAt
|
|
144501
|
+
);
|
|
144502
|
+
return event;
|
|
144503
|
+
}
|
|
144504
|
+
listEvents(memoryId) {
|
|
144505
|
+
return this.db.prepare(`
|
|
144506
|
+
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
144507
|
+
`).all(memoryId).map(rowToEvent);
|
|
144508
|
+
}
|
|
144509
|
+
markAccess(ids, at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
144510
|
+
const unique2 = [...new Set(ids.filter(Boolean))];
|
|
144511
|
+
if (unique2.length === 0) return;
|
|
144512
|
+
const statement = this.db.prepare(`
|
|
144513
|
+
UPDATE long_term_memories
|
|
144514
|
+
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
144515
|
+
WHERE id = ?
|
|
144516
|
+
`);
|
|
144517
|
+
this.db.transaction(() => {
|
|
144518
|
+
for (const id of unique2) statement.run(at, id);
|
|
144519
|
+
})();
|
|
144520
|
+
}
|
|
144521
|
+
};
|
|
144522
|
+
}
|
|
144523
|
+
});
|
|
144524
|
+
|
|
144525
|
+
// src/memory/long-term-types.ts
|
|
144526
|
+
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
144527
|
+
var init_long_term_types = __esm({
|
|
144528
|
+
"src/memory/long-term-types.ts"() {
|
|
144529
|
+
"use strict";
|
|
144530
|
+
init_esm_shims();
|
|
144531
|
+
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
144532
|
+
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
144533
|
+
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
144534
|
+
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
144535
|
+
"observation",
|
|
144536
|
+
"claim",
|
|
144537
|
+
"workflow",
|
|
144538
|
+
"session",
|
|
144539
|
+
"checkpoint",
|
|
144540
|
+
"git",
|
|
144541
|
+
"code",
|
|
144542
|
+
"test",
|
|
144543
|
+
"run",
|
|
144544
|
+
"document",
|
|
144545
|
+
"manual",
|
|
144546
|
+
"user"
|
|
144547
|
+
];
|
|
144548
|
+
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
144549
|
+
"supports",
|
|
144550
|
+
"verifies",
|
|
144551
|
+
"derives",
|
|
144552
|
+
"user-confirmed"
|
|
144553
|
+
];
|
|
144554
|
+
}
|
|
144555
|
+
});
|
|
144556
|
+
|
|
144557
|
+
// src/memory/long-term.ts
|
|
144558
|
+
var long_term_exports = {};
|
|
144559
|
+
__export(long_term_exports, {
|
|
144560
|
+
approveLongTermMemory: () => approveLongTermMemory,
|
|
144561
|
+
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
144562
|
+
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
144563
|
+
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
144564
|
+
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
144565
|
+
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
144566
|
+
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
144567
|
+
listLongTermMemories: () => listLongTermMemories,
|
|
144568
|
+
maintainLongTermMemories: () => maintainLongTermMemories,
|
|
144569
|
+
maybeAutoQualifyLongTermMemory: () => maybeAutoQualifyLongTermMemory,
|
|
144570
|
+
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
144571
|
+
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
144572
|
+
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
144573
|
+
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
144574
|
+
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
144575
|
+
});
|
|
144576
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
144577
|
+
function isOneOf(value, values) {
|
|
144578
|
+
return typeof value === "string" && values.includes(value);
|
|
144579
|
+
}
|
|
144580
|
+
function compactText(value, field, max = MAX_TEXT_LENGTH) {
|
|
144581
|
+
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
144582
|
+
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
144583
|
+
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
144584
|
+
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
144585
|
+
return safe;
|
|
144586
|
+
}
|
|
144587
|
+
function compactList(values, field) {
|
|
144588
|
+
if (!values) return [];
|
|
144589
|
+
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
144590
|
+
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
144591
|
+
}
|
|
144592
|
+
function normalizeEvidence(input) {
|
|
144593
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
144594
|
+
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
144595
|
+
}
|
|
144596
|
+
return input.map((evidence) => {
|
|
144597
|
+
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
144598
|
+
throw new Error("Long-term memory evidence kind is invalid.");
|
|
144599
|
+
}
|
|
144600
|
+
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
144601
|
+
throw new Error("Long-term memory evidence relation is invalid.");
|
|
144602
|
+
}
|
|
144603
|
+
return {
|
|
144604
|
+
kind: evidence.kind,
|
|
144605
|
+
referenceId: compactText(evidence.referenceId, "evidence reference", 1e3),
|
|
144606
|
+
relation: evidence.relation,
|
|
144607
|
+
...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator", 1e3) } : {},
|
|
144608
|
+
...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
144609
|
+
};
|
|
144610
|
+
});
|
|
144611
|
+
}
|
|
144612
|
+
function assertScope(scope) {
|
|
144613
|
+
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
144614
|
+
}
|
|
144615
|
+
function assertKind(kind) {
|
|
144616
|
+
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
144617
|
+
}
|
|
144618
|
+
function assertPortability(value) {
|
|
144619
|
+
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
144620
|
+
throw new Error("Long-term memory portability is invalid.");
|
|
144621
|
+
}
|
|
144622
|
+
}
|
|
144623
|
+
function assertPortableEvidence(scope, portability, evidence) {
|
|
144624
|
+
if (portability !== "portable") return;
|
|
144625
|
+
if (scope !== "user") {
|
|
144626
|
+
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
144627
|
+
}
|
|
144628
|
+
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
144629
|
+
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.");
|
|
144630
|
+
}
|
|
144631
|
+
}
|
|
144632
|
+
function requireTeamReader(scope, reader) {
|
|
144633
|
+
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
144634
|
+
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
144635
|
+
}
|
|
144636
|
+
}
|
|
144637
|
+
function matchesScope(memory, reader) {
|
|
144638
|
+
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
144639
|
+
if (memory.scope === "team") {
|
|
144640
|
+
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
144641
|
+
}
|
|
144642
|
+
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
144643
|
+
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
144644
|
+
}
|
|
144645
|
+
function canReadLongTermMemory(memory, reader) {
|
|
144646
|
+
return matchesScope(memory, reader);
|
|
144647
|
+
}
|
|
144648
|
+
function isEligibleLongTermMemory(memory) {
|
|
144649
|
+
return memory.state === "qualified" || memory.state === "approved";
|
|
144650
|
+
}
|
|
144651
|
+
function taskTerms(task) {
|
|
144652
|
+
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
144653
|
+
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
144654
|
+
}
|
|
144655
|
+
function kindFitScore(kind, task) {
|
|
144656
|
+
const normalized = task.toLocaleLowerCase("en-US");
|
|
144657
|
+
const hints = {
|
|
144658
|
+
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
144659
|
+
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
144660
|
+
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
144661
|
+
};
|
|
144662
|
+
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
144663
|
+
}
|
|
144664
|
+
function validationFreshnessScore(memory) {
|
|
144665
|
+
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
144666
|
+
if (!validatedAt) return 0;
|
|
144667
|
+
const time = Date.parse(validatedAt);
|
|
144668
|
+
if (!Number.isFinite(time)) return 0;
|
|
144669
|
+
const ageDays = Math.max(0, Date.now() - time) / (24 * 60 * 60 * 1e3);
|
|
144670
|
+
if (ageDays <= 30) return 0.35;
|
|
144671
|
+
if (ageDays <= 90) return 0.15;
|
|
144672
|
+
return 0;
|
|
144673
|
+
}
|
|
144674
|
+
function scoreForTask(memory, task) {
|
|
144675
|
+
const terms = taskTerms(task);
|
|
144676
|
+
if (terms.length === 0) return void 0;
|
|
144677
|
+
const title = memory.title.toLocaleLowerCase("en-US");
|
|
144678
|
+
const content = memory.content.toLocaleLowerCase("en-US");
|
|
144679
|
+
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
144680
|
+
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
144681
|
+
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
144682
|
+
let score = memory.state === "approved" ? 0.5 : 0;
|
|
144683
|
+
const matched = [];
|
|
144684
|
+
for (const term of terms) {
|
|
144685
|
+
let termScore = 0;
|
|
144686
|
+
if (title.includes(term)) termScore += 5;
|
|
144687
|
+
if (tags.includes(term)) termScore += 4;
|
|
144688
|
+
if (applicability.includes(term)) termScore += 3;
|
|
144689
|
+
if (facts.includes(term)) termScore += 2;
|
|
144690
|
+
if (content.includes(term)) termScore += 1;
|
|
144691
|
+
if (termScore > 0) {
|
|
144692
|
+
score += termScore;
|
|
144693
|
+
matched.push(term);
|
|
144694
|
+
}
|
|
144695
|
+
}
|
|
144696
|
+
if (matched.length === 0) return void 0;
|
|
144697
|
+
score += kindFitScore(memory.kind, task);
|
|
144698
|
+
score += validationFreshnessScore(memory);
|
|
144699
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
144700
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
144701
|
+
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
144702
|
+
}
|
|
144703
|
+
function semanticText(memory) {
|
|
144704
|
+
return sanitizeCredentials([
|
|
144705
|
+
memory.title,
|
|
144706
|
+
memory.applicability,
|
|
144707
|
+
memory.tags.join(" "),
|
|
144708
|
+
memory.facts.join(" "),
|
|
144709
|
+
memory.content
|
|
144710
|
+
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
144711
|
+
}
|
|
144712
|
+
function cosineSimilarity(left, right) {
|
|
144713
|
+
if (left.length === 0 || left.length !== right.length) return 0;
|
|
144714
|
+
let dot = 0;
|
|
144715
|
+
let leftMagnitude = 0;
|
|
144716
|
+
let rightMagnitude = 0;
|
|
144717
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
144718
|
+
const a = left[index];
|
|
144719
|
+
const b = right[index];
|
|
144720
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return 0;
|
|
144721
|
+
dot += a * b;
|
|
144722
|
+
leftMagnitude += a * a;
|
|
144723
|
+
rightMagnitude += b * b;
|
|
144724
|
+
}
|
|
144725
|
+
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
144726
|
+
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
144727
|
+
}
|
|
144728
|
+
function semanticSelection(memory, similarity, task) {
|
|
144729
|
+
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
144730
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
144731
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
144732
|
+
return {
|
|
144733
|
+
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
144734
|
+
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
144735
|
+
};
|
|
144736
|
+
}
|
|
144737
|
+
async function resolveSemanticProvider(explicit) {
|
|
144738
|
+
if (explicit !== void 0) return explicit;
|
|
144739
|
+
try {
|
|
144740
|
+
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
144741
|
+
return await withTimeout(
|
|
144742
|
+
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
144743
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
144744
|
+
"Long-term semantic provider initialization"
|
|
144745
|
+
);
|
|
144746
|
+
} catch {
|
|
144747
|
+
return null;
|
|
144748
|
+
}
|
|
144749
|
+
}
|
|
144750
|
+
function createStore(dataDir) {
|
|
144751
|
+
const store = new LongTermMemoryStore();
|
|
144752
|
+
return store.init(dataDir).then(() => store);
|
|
144753
|
+
}
|
|
144754
|
+
async function createManualLongTermMemory(input) {
|
|
144755
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
144756
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
144757
|
+
assertScope(input.scope);
|
|
144758
|
+
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
|
|
144759
|
+
return createLongTermMemoryWithDataDir({
|
|
144760
|
+
dataDir: input.dataDir,
|
|
144761
|
+
originProjectId: input.projectId,
|
|
144762
|
+
ownerId: owner.id,
|
|
144763
|
+
scope: input.scope,
|
|
144764
|
+
kind: input.kind,
|
|
144765
|
+
portability: input.portability,
|
|
144766
|
+
title: input.title,
|
|
144767
|
+
content: input.content,
|
|
144768
|
+
facts: input.facts,
|
|
144769
|
+
tags: input.tags,
|
|
144770
|
+
applicability: input.applicability,
|
|
144771
|
+
origin: "manual",
|
|
144772
|
+
evidence,
|
|
144773
|
+
reader: input.reader
|
|
144774
|
+
});
|
|
144775
|
+
}
|
|
144776
|
+
async function promoteObservationToLongTermMemory(input) {
|
|
144777
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
144778
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
144779
|
+
return createLongTermMemoryWithDataDir({
|
|
144780
|
+
dataDir: input.dataDir,
|
|
144781
|
+
originProjectId: input.observation.projectId,
|
|
144782
|
+
ownerId: owner.id,
|
|
144783
|
+
scope: input.scope,
|
|
144784
|
+
kind: input.kind,
|
|
144785
|
+
portability: "project-bound",
|
|
144786
|
+
title: input.observation.title,
|
|
144787
|
+
content: input.observation.narrative,
|
|
144788
|
+
facts: input.observation.facts,
|
|
144789
|
+
tags: input.tags ?? input.observation.concepts,
|
|
144790
|
+
applicability: input.applicability,
|
|
144791
|
+
origin: input.observation.source === "git" ? "git" : "agent",
|
|
144792
|
+
evidence: [{
|
|
144793
|
+
kind: "observation",
|
|
144794
|
+
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
144795
|
+
relation: "derives"
|
|
144796
|
+
}],
|
|
144797
|
+
reader: input.reader
|
|
144798
|
+
});
|
|
144799
|
+
}
|
|
144800
|
+
async function createLongTermMemoryCandidate(input) {
|
|
144801
|
+
return createLongTermMemoryWithDataDir(input);
|
|
144802
|
+
}
|
|
144803
|
+
async function createLongTermMemoryWithDataDir(input) {
|
|
144804
|
+
assertScope(input.scope);
|
|
144805
|
+
assertKind(input.kind);
|
|
144806
|
+
const portability = input.portability ?? "project-bound";
|
|
144807
|
+
assertPortability(portability);
|
|
144808
|
+
requireTeamReader(input.scope, input.reader);
|
|
144809
|
+
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
144810
|
+
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
144811
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144812
|
+
const memory = {
|
|
144813
|
+
id: randomUUID8(),
|
|
144814
|
+
originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
|
|
144815
|
+
ownerId: compactText(input.ownerId, "owner id", 160),
|
|
144816
|
+
scope: input.scope,
|
|
144817
|
+
kind: input.kind,
|
|
144818
|
+
state: "candidate",
|
|
144819
|
+
portability,
|
|
144820
|
+
title: compactText(input.title, "title", 500),
|
|
144821
|
+
content: compactText(input.content, "content"),
|
|
144822
|
+
facts: compactList(input.facts, "facts"),
|
|
144823
|
+
tags: compactList(input.tags, "tags"),
|
|
144824
|
+
...input.applicability ? { applicability: compactText(input.applicability, "applicability", 1e3) } : {},
|
|
144825
|
+
origin: input.origin ?? "manual",
|
|
144826
|
+
createdAt: now3,
|
|
144827
|
+
updatedAt: now3,
|
|
144828
|
+
accessCount: 0
|
|
144829
|
+
};
|
|
144830
|
+
const store = await createStore(input.dataDir);
|
|
144831
|
+
const evidence = store.transaction(() => {
|
|
144832
|
+
store.insert(memory);
|
|
144833
|
+
const written = evidenceInputs.map((item) => store.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
144834
|
+
store.recordEvent({
|
|
144835
|
+
memoryId: memory.id,
|
|
144836
|
+
kind: "created",
|
|
144837
|
+
toState: "candidate",
|
|
144838
|
+
detail: "Created as a source-backed long-term memory candidate.",
|
|
144839
|
+
createdAt: now3
|
|
144840
|
+
});
|
|
144841
|
+
return written;
|
|
144842
|
+
});
|
|
144843
|
+
return { memory, evidence };
|
|
144844
|
+
}
|
|
144845
|
+
function transitionDetail(value) {
|
|
144846
|
+
return compactText(value, "transition reason", 1e3);
|
|
144847
|
+
}
|
|
144848
|
+
async function transitionMemory(input) {
|
|
144849
|
+
const store = await createStore(input.dataDir);
|
|
144850
|
+
return store.transaction(() => {
|
|
144851
|
+
const current = store.get(input.id);
|
|
144852
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144853
|
+
if (current.state !== input.expected) {
|
|
144854
|
+
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
144855
|
+
}
|
|
144856
|
+
const evidence = store.listEvidence(current.id);
|
|
144857
|
+
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
144858
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144859
|
+
const next = {
|
|
144860
|
+
...current,
|
|
144861
|
+
state: input.next,
|
|
144862
|
+
updatedAt: now3,
|
|
144863
|
+
lastValidatedAt: now3,
|
|
144864
|
+
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
144865
|
+
};
|
|
144866
|
+
store.update(next);
|
|
144867
|
+
store.recordEvent({
|
|
144868
|
+
memoryId: next.id,
|
|
144869
|
+
kind: input.event,
|
|
144870
|
+
fromState: current.state,
|
|
144871
|
+
toState: next.state,
|
|
144872
|
+
detail: transitionDetail(input.reason),
|
|
144873
|
+
createdAt: now3
|
|
144874
|
+
});
|
|
144875
|
+
return next;
|
|
144876
|
+
});
|
|
144877
|
+
}
|
|
144878
|
+
async function qualifyLongTermMemory(input) {
|
|
144879
|
+
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
144880
|
+
}
|
|
144881
|
+
async function approveLongTermMemory(input) {
|
|
144882
|
+
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
144883
|
+
}
|
|
144884
|
+
async function archiveLongTermMemory(input) {
|
|
144885
|
+
const store = await createStore(input.dataDir);
|
|
144886
|
+
return store.transaction(() => {
|
|
144887
|
+
const current = store.get(input.id);
|
|
144888
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144889
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
144890
|
+
throw new Error("Long-term memory is already inactive.");
|
|
144891
|
+
}
|
|
144892
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144893
|
+
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
144894
|
+
store.update(next);
|
|
144895
|
+
store.recordEvent({
|
|
144896
|
+
memoryId: next.id,
|
|
144897
|
+
kind: "archived",
|
|
144898
|
+
fromState: current.state,
|
|
144899
|
+
toState: "archived",
|
|
144900
|
+
detail: transitionDetail(input.reason),
|
|
144901
|
+
createdAt: now3
|
|
144902
|
+
});
|
|
144903
|
+
return next;
|
|
144904
|
+
});
|
|
144905
|
+
}
|
|
144906
|
+
async function supersedeLongTermMemory(input) {
|
|
144907
|
+
const store = await createStore(input.dataDir);
|
|
144908
|
+
return store.transaction(() => {
|
|
144909
|
+
const current = store.get(input.id);
|
|
144910
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
144911
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
144912
|
+
throw new Error("Long-term memory is already inactive.");
|
|
144913
|
+
}
|
|
144914
|
+
if (current.id === input.supersededBy) {
|
|
144915
|
+
throw new Error("Long-term memory cannot supersede itself.");
|
|
144916
|
+
}
|
|
144917
|
+
const replacement = store.get(input.supersededBy);
|
|
144918
|
+
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
144919
|
+
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
144920
|
+
}
|
|
144921
|
+
if (replacement.scope !== current.scope) {
|
|
144922
|
+
throw new Error("Replacement long-term memory must use the same scope.");
|
|
144923
|
+
}
|
|
144924
|
+
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
144925
|
+
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
144926
|
+
}
|
|
144927
|
+
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
144928
|
+
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
144929
|
+
}
|
|
144930
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
144931
|
+
const next = {
|
|
144932
|
+
...current,
|
|
144933
|
+
state: "superseded",
|
|
144934
|
+
supersededBy: replacement.id,
|
|
144935
|
+
updatedAt: now3
|
|
144936
|
+
};
|
|
144937
|
+
store.update(next);
|
|
144938
|
+
store.recordEvent({
|
|
144939
|
+
memoryId: next.id,
|
|
144940
|
+
kind: "superseded",
|
|
144941
|
+
fromState: current.state,
|
|
144942
|
+
toState: "superseded",
|
|
144943
|
+
detail: transitionDetail(input.reason),
|
|
144944
|
+
createdAt: now3
|
|
144945
|
+
});
|
|
144946
|
+
return next;
|
|
144947
|
+
});
|
|
144948
|
+
}
|
|
144949
|
+
async function maybeAutoQualifyLongTermMemory(input) {
|
|
144950
|
+
if (input.source !== "manual" && input.sourceDetail !== "explicit") return null;
|
|
144951
|
+
try {
|
|
144952
|
+
return await qualifyLongTermMemory({
|
|
144953
|
+
dataDir: input.dataDir,
|
|
144954
|
+
id: input.id,
|
|
144955
|
+
reason: "auto-qualified: explicit request carries its own source evidence"
|
|
144956
|
+
});
|
|
144957
|
+
} catch {
|
|
144958
|
+
return null;
|
|
144959
|
+
}
|
|
144960
|
+
}
|
|
144961
|
+
async function maintainLongTermMemories(input) {
|
|
144962
|
+
const now3 = input.now ?? /* @__PURE__ */ new Date();
|
|
144963
|
+
const decayDays = input.decayDays ?? 30;
|
|
144964
|
+
const cutoff = now3.getTime() - decayDays * 24 * 60 * 60 * 1e3;
|
|
144965
|
+
const result = { archived: 0, superseded: 0 };
|
|
144966
|
+
const store = await createStore(input.dataDir);
|
|
144967
|
+
const all = store.list({ originProjectId: input.projectId });
|
|
144968
|
+
for (const memory of all) {
|
|
144969
|
+
if (memory.state !== "qualified" && memory.state !== "approved") continue;
|
|
144970
|
+
const lastActivity = Date.parse(memory.lastAccessedAt ?? memory.updatedAt);
|
|
144971
|
+
if (!Number.isFinite(lastActivity) || lastActivity >= cutoff) continue;
|
|
144972
|
+
try {
|
|
144973
|
+
await archiveLongTermMemory({
|
|
144974
|
+
dataDir: input.dataDir,
|
|
144975
|
+
id: memory.id,
|
|
144976
|
+
reason: "auto-archive: no activity since " + new Date(lastActivity).toISOString().slice(0, 10)
|
|
144977
|
+
});
|
|
144978
|
+
result.archived += 1;
|
|
144979
|
+
} catch {
|
|
144980
|
+
}
|
|
144981
|
+
}
|
|
144982
|
+
const groups = /* @__PURE__ */ new Map();
|
|
144983
|
+
for (const memory of store.list({ originProjectId: input.projectId })) {
|
|
144984
|
+
if (memory.state !== "qualified" && memory.state !== "approved") continue;
|
|
144985
|
+
const key = memory.scope + "\0" + memory.kind + "\0" + memory.title.trim().toLowerCase();
|
|
144986
|
+
const group = groups.get(key) ?? [];
|
|
144987
|
+
group.push(memory);
|
|
144988
|
+
groups.set(key, group);
|
|
144989
|
+
}
|
|
144990
|
+
for (const group of groups.values()) {
|
|
144991
|
+
if (group.length < 2) continue;
|
|
144992
|
+
group.sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
|
|
144993
|
+
const newest = group[0];
|
|
144994
|
+
for (const older of group.slice(1)) {
|
|
144995
|
+
try {
|
|
144996
|
+
await supersedeLongTermMemory({
|
|
144997
|
+
dataDir: input.dataDir,
|
|
144998
|
+
id: older.id,
|
|
144999
|
+
supersededBy: newest.id,
|
|
145000
|
+
reason: "auto-supersede: a newer record shares the same title"
|
|
145001
|
+
});
|
|
145002
|
+
result.superseded += 1;
|
|
145003
|
+
} catch {
|
|
145004
|
+
}
|
|
145005
|
+
}
|
|
145006
|
+
}
|
|
145007
|
+
return result;
|
|
145008
|
+
}
|
|
145009
|
+
async function listLongTermMemories(input) {
|
|
145010
|
+
const store = await createStore(input.dataDir);
|
|
145011
|
+
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) }));
|
|
145012
|
+
}
|
|
145013
|
+
async function getLongTermMemoryDetail(input) {
|
|
145014
|
+
const store = await createStore(input.dataDir);
|
|
145015
|
+
const memory = store.get(input.id);
|
|
145016
|
+
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
145017
|
+
throw new Error("Long-term memory was not found in the active scope.");
|
|
145018
|
+
}
|
|
145019
|
+
store.markAccess([memory.id]);
|
|
145020
|
+
return { memory, evidence: store.listEvidence(memory.id), events: store.listEvents(memory.id) };
|
|
145021
|
+
}
|
|
145022
|
+
async function selectLongTermMemoriesForTask(input) {
|
|
145023
|
+
const task = input.task?.trim();
|
|
145024
|
+
if (!task) return [];
|
|
145025
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
145026
|
+
const reader = {
|
|
145027
|
+
projectId: input.projectId,
|
|
145028
|
+
...owner ? { ownerId: owner.id } : {},
|
|
145029
|
+
...input.agentId ? { agentId: input.agentId } : {},
|
|
145030
|
+
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
145031
|
+
};
|
|
145032
|
+
const store = await createStore(input.dataDir);
|
|
145033
|
+
const eligible = store.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
145034
|
+
let selected = eligible.map((memory) => {
|
|
145035
|
+
const ranked = scoreForTask(memory, task);
|
|
145036
|
+
return ranked ? {
|
|
145037
|
+
memory,
|
|
145038
|
+
evidence: store.listEvidence(memory.id),
|
|
145039
|
+
score: ranked.score,
|
|
145040
|
+
reason: ranked.reason
|
|
145041
|
+
} : void 0;
|
|
145042
|
+
}).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));
|
|
145043
|
+
if (selected.length === 0 && eligible.length > 0) {
|
|
145044
|
+
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
145045
|
+
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
145046
|
+
if (provider2 && candidates.length > 0) {
|
|
145047
|
+
try {
|
|
145048
|
+
const vectors = await withTimeout(
|
|
145049
|
+
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
145050
|
+
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
145051
|
+
retry: false
|
|
145052
|
+
}),
|
|
145053
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
145054
|
+
"Long-term semantic retrieval"
|
|
145055
|
+
);
|
|
145056
|
+
const query = vectors[0];
|
|
145057
|
+
if (Array.isArray(query)) {
|
|
145058
|
+
selected = candidates.map((memory, index) => {
|
|
145059
|
+
const vector = vectors[index + 1];
|
|
145060
|
+
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
145061
|
+
return ranked ? {
|
|
145062
|
+
memory,
|
|
145063
|
+
evidence: store.listEvidence(memory.id),
|
|
145064
|
+
score: ranked.score,
|
|
145065
|
+
reason: ranked.reason
|
|
145066
|
+
} : void 0;
|
|
145067
|
+
}).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));
|
|
145068
|
+
}
|
|
145069
|
+
} catch {
|
|
145070
|
+
}
|
|
145071
|
+
}
|
|
145072
|
+
}
|
|
145073
|
+
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
145074
|
+
store.markAccess(selected.map((item) => item.memory.id));
|
|
145075
|
+
return selected;
|
|
145076
|
+
}
|
|
145077
|
+
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
145078
|
+
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
145079
|
+
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
145080
|
+
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
145081
|
+
}
|
|
145082
|
+
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
145083
|
+
var init_long_term = __esm({
|
|
145084
|
+
"src/memory/long-term.ts"() {
|
|
145085
|
+
"use strict";
|
|
145086
|
+
init_esm_shims();
|
|
145087
|
+
init_token_budget();
|
|
145088
|
+
init_timeout();
|
|
145089
|
+
init_secret_filter();
|
|
145090
|
+
init_owner();
|
|
145091
|
+
init_long_term_store();
|
|
145092
|
+
init_long_term_types();
|
|
145093
|
+
MAX_TEXT_LENGTH = 12e3;
|
|
145094
|
+
MAX_LIST_ITEMS = 40;
|
|
145095
|
+
MAX_SEMANTIC_CANDIDATES = 48;
|
|
145096
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
145097
|
+
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
145098
|
+
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
145099
|
+
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
145100
|
+
"the",
|
|
145101
|
+
"and",
|
|
145102
|
+
"for",
|
|
145103
|
+
"with",
|
|
145104
|
+
"from",
|
|
145105
|
+
"that",
|
|
145106
|
+
"this",
|
|
145107
|
+
"into",
|
|
145108
|
+
"when",
|
|
145109
|
+
"what",
|
|
145110
|
+
"\u5982\u4F55",
|
|
145111
|
+
"\u4EC0\u4E48",
|
|
145112
|
+
"\u8FD9\u4E2A",
|
|
145113
|
+
"\u90A3\u4E2A",
|
|
145114
|
+
"\u6211\u4EEC",
|
|
145115
|
+
"\u9879\u76EE",
|
|
145116
|
+
"\u8FDB\u884C",
|
|
145117
|
+
"\u9700\u8981",
|
|
145118
|
+
"\u7EE7\u7EED",
|
|
145119
|
+
"\u5904\u7406"
|
|
145120
|
+
]);
|
|
145121
|
+
}
|
|
145122
|
+
});
|
|
145123
|
+
|
|
144227
145124
|
// src/codegraph/ids.ts
|
|
144228
145125
|
import { createHash as createHash5 } from "crypto";
|
|
144229
145126
|
function digest(input) {
|
|
@@ -144262,7 +145159,7 @@ var store_exports = {};
|
|
|
144262
145159
|
__export(store_exports, {
|
|
144263
145160
|
CodeGraphStore: () => CodeGraphStore
|
|
144264
145161
|
});
|
|
144265
|
-
import { randomUUID as
|
|
145162
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
144266
145163
|
import { posix as pathPosix } from "path";
|
|
144267
145164
|
function rowToFile(row) {
|
|
144268
145165
|
return {
|
|
@@ -144979,7 +145876,7 @@ var init_store = __esm({
|
|
|
144979
145876
|
* so an interrupted scan cannot be advertised as complete.
|
|
144980
145877
|
*/
|
|
144981
145878
|
recordCodeStateSnapshot(input) {
|
|
144982
|
-
const id =
|
|
145879
|
+
const id = randomUUID9();
|
|
144983
145880
|
let snapshot;
|
|
144984
145881
|
const tx = this.db.transaction(() => {
|
|
144985
145882
|
const previous = this.db.prepare(
|
|
@@ -145757,8 +146654,8 @@ var claim_store_exports = {};
|
|
|
145757
146654
|
__export(claim_store_exports, {
|
|
145758
146655
|
ClaimStore: () => ClaimStore
|
|
145759
146656
|
});
|
|
145760
|
-
import { createHash as createHash8, randomUUID as
|
|
145761
|
-
function
|
|
146657
|
+
import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
|
|
146658
|
+
function optionalText3(value) {
|
|
145762
146659
|
return typeof value === "string" && value ? value : void 0;
|
|
145763
146660
|
}
|
|
145764
146661
|
function rowToClaim(row) {
|
|
@@ -145774,38 +146671,38 @@ function rowToClaim(row) {
|
|
|
145774
146671
|
status: row.status,
|
|
145775
146672
|
confidence: Number(row.confidence),
|
|
145776
146673
|
observedAt: row.observedAt,
|
|
145777
|
-
...
|
|
145778
|
-
...
|
|
145779
|
-
...
|
|
146674
|
+
...optionalText3(row.validFrom) ? { validFrom: row.validFrom } : {},
|
|
146675
|
+
...optionalText3(row.validTo) ? { validTo: row.validTo } : {},
|
|
146676
|
+
...optionalText3(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
145780
146677
|
reviewState: row.reviewState,
|
|
145781
146678
|
origin: row.origin,
|
|
145782
146679
|
createdAt: row.createdAt,
|
|
145783
146680
|
updatedAt: row.updatedAt
|
|
145784
146681
|
};
|
|
145785
146682
|
}
|
|
145786
|
-
function
|
|
146683
|
+
function rowToEvidence2(row) {
|
|
145787
146684
|
return {
|
|
145788
146685
|
id: row.id,
|
|
145789
146686
|
claimId: row.claimId,
|
|
145790
146687
|
evidenceKind: row.evidenceKind,
|
|
145791
146688
|
evidenceId: row.evidenceId,
|
|
145792
146689
|
relation: row.relation,
|
|
145793
|
-
...
|
|
145794
|
-
...
|
|
145795
|
-
...
|
|
146690
|
+
...optionalText3(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
|
|
146691
|
+
...optionalText3(row.locator) ? { locator: row.locator } : {},
|
|
146692
|
+
...optionalText3(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
145796
146693
|
createdAt: row.createdAt
|
|
145797
146694
|
};
|
|
145798
146695
|
}
|
|
145799
|
-
function
|
|
146696
|
+
function rowToEvent2(row) {
|
|
145800
146697
|
return {
|
|
145801
146698
|
id: row.id,
|
|
145802
146699
|
projectId: row.projectId,
|
|
145803
146700
|
claimId: row.claimId,
|
|
145804
146701
|
kind: row.kind,
|
|
145805
|
-
...
|
|
145806
|
-
...
|
|
145807
|
-
...
|
|
145808
|
-
...
|
|
146702
|
+
...optionalText3(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
|
|
146703
|
+
...optionalText3(row.toStatus) ? { toStatus: row.toStatus } : {},
|
|
146704
|
+
...optionalText3(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
|
|
146705
|
+
...optionalText3(row.detail) ? { detail: row.detail } : {},
|
|
145809
146706
|
createdAt: row.createdAt
|
|
145810
146707
|
};
|
|
145811
146708
|
}
|
|
@@ -145941,7 +146838,7 @@ var init_claim_store = __esm({
|
|
|
145941
146838
|
locator, capturedHash, evidenceKey, createdAt
|
|
145942
146839
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
145943
146840
|
`).run(
|
|
145944
|
-
|
|
146841
|
+
randomUUID10(),
|
|
145945
146842
|
input.claimId,
|
|
145946
146843
|
input.evidenceKind,
|
|
145947
146844
|
input.evidenceId,
|
|
@@ -145959,11 +146856,11 @@ var init_claim_store = __esm({
|
|
|
145959
146856
|
SELECT * FROM knowledge_claim_evidence
|
|
145960
146857
|
WHERE claimId = ?
|
|
145961
146858
|
ORDER BY createdAt ASC, id
|
|
145962
|
-
`).all(claimId).map(
|
|
146859
|
+
`).all(claimId).map(rowToEvidence2);
|
|
145963
146860
|
}
|
|
145964
146861
|
recordEvent(input) {
|
|
145965
146862
|
const event = {
|
|
145966
|
-
id:
|
|
146863
|
+
id: randomUUID10(),
|
|
145967
146864
|
...input,
|
|
145968
146865
|
...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
|
|
145969
146866
|
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -145991,7 +146888,7 @@ var init_claim_store = __esm({
|
|
|
145991
146888
|
SELECT * FROM knowledge_claim_events
|
|
145992
146889
|
WHERE claimId = ?
|
|
145993
146890
|
ORDER BY createdAt ASC, id
|
|
145994
|
-
`).all(claimId).map(
|
|
146891
|
+
`).all(claimId).map(rowToEvent2);
|
|
145995
146892
|
}
|
|
145996
146893
|
};
|
|
145997
146894
|
}
|
|
@@ -146008,8 +146905,8 @@ __export(claims_exports, {
|
|
|
146008
146905
|
supersedeClaim: () => supersedeClaim,
|
|
146009
146906
|
writeClaim: () => writeClaim
|
|
146010
146907
|
});
|
|
146011
|
-
import { createHash as createHash9, randomUUID as
|
|
146012
|
-
function
|
|
146908
|
+
import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
|
|
146909
|
+
function compactText2(value, field) {
|
|
146013
146910
|
if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
|
|
146014
146911
|
const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
|
|
146015
146912
|
if (!sanitized) throw new Error("Claim " + field + " is required");
|
|
@@ -146017,7 +146914,7 @@ function compactText(value, field) {
|
|
|
146017
146914
|
return sanitized;
|
|
146018
146915
|
}
|
|
146019
146916
|
function normalizedTerm(value) {
|
|
146020
|
-
return
|
|
146917
|
+
return compactText2(value, "identity").toLocaleLowerCase("en-US");
|
|
146021
146918
|
}
|
|
146022
146919
|
function hashIdentity(parts) {
|
|
146023
146920
|
return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
|
|
@@ -146046,7 +146943,7 @@ function validateEvidence(input) {
|
|
|
146046
146943
|
if (!evidence || typeof evidence !== "object") {
|
|
146047
146944
|
throw new Error("Claim evidence must be an object");
|
|
146048
146945
|
}
|
|
146049
|
-
const evidenceId =
|
|
146946
|
+
const evidenceId = compactText2(evidence.evidenceId, "evidence id");
|
|
146050
146947
|
if (evidenceId.length > MAX_EVIDENCE_ID_LENGTH) {
|
|
146051
146948
|
throw new Error("Claim evidence id is too long");
|
|
146052
146949
|
}
|
|
@@ -146060,9 +146957,9 @@ function validateEvidence(input) {
|
|
|
146060
146957
|
evidenceKind: evidence.evidenceKind,
|
|
146061
146958
|
evidenceId,
|
|
146062
146959
|
relation: evidence.relation,
|
|
146063
|
-
...evidence.snapshotId ? { snapshotId:
|
|
146064
|
-
...evidence.locator ? { locator:
|
|
146065
|
-
...evidence.capturedHash ? { capturedHash:
|
|
146960
|
+
...evidence.snapshotId ? { snapshotId: compactText2(evidence.snapshotId, "evidence snapshot id") } : {},
|
|
146961
|
+
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator") } : {},
|
|
146962
|
+
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash") } : {}
|
|
146066
146963
|
};
|
|
146067
146964
|
});
|
|
146068
146965
|
}
|
|
@@ -146070,10 +146967,10 @@ function normalizeClaimInput(input) {
|
|
|
146070
146967
|
if (!["project", "workspace", "team", "workflow", "task"].includes(input.scope)) {
|
|
146071
146968
|
throw new Error("Claim scope is invalid");
|
|
146072
146969
|
}
|
|
146073
|
-
const projectId =
|
|
146074
|
-
const subject =
|
|
146075
|
-
const predicate =
|
|
146076
|
-
const objectValue =
|
|
146970
|
+
const projectId = compactText2(input.projectId, "project id");
|
|
146971
|
+
const subject = compactText2(input.subject, "subject");
|
|
146972
|
+
const predicate = compactText2(input.predicate, "predicate");
|
|
146973
|
+
const objectValue = compactText2(input.objectValue, "object value");
|
|
146077
146974
|
const identity = buildClaimIdentity({ subject, predicate, objectValue, scope: input.scope });
|
|
146078
146975
|
const origin = input.origin ?? "explicit";
|
|
146079
146976
|
let reviewState = input.reviewState ?? "approved";
|
|
@@ -146094,7 +146991,7 @@ function normalizeClaimInput(input) {
|
|
|
146094
146991
|
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
146095
146992
|
return {
|
|
146096
146993
|
claim: {
|
|
146097
|
-
id:
|
|
146994
|
+
id: randomUUID11(),
|
|
146098
146995
|
projectId,
|
|
146099
146996
|
subject,
|
|
146100
146997
|
predicate,
|
|
@@ -146194,7 +147091,7 @@ function reviewClaim(store, input) {
|
|
|
146194
147091
|
if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
|
|
146195
147092
|
throw new Error("A claim review must approve or reject the claim");
|
|
146196
147093
|
}
|
|
146197
|
-
const detail =
|
|
147094
|
+
const detail = compactText2(input.detail, "review detail");
|
|
146198
147095
|
return store.transaction(() => {
|
|
146199
147096
|
const claim = store.getClaim(input.claimId);
|
|
146200
147097
|
if (!claim) throw new Error("Claim was not found");
|
|
@@ -146506,8 +147403,8 @@ var init_frontmatter = __esm({
|
|
|
146506
147403
|
|
|
146507
147404
|
// src/knowledge/markdown.ts
|
|
146508
147405
|
import { createHash as createHash10 } from "crypto";
|
|
146509
|
-
import { promises as
|
|
146510
|
-
import
|
|
147406
|
+
import { promises as fs13 } from "fs";
|
|
147407
|
+
import path20 from "path";
|
|
146511
147408
|
function hashContent(content) {
|
|
146512
147409
|
return createHash10("sha256").update(content).digest("hex");
|
|
146513
147410
|
}
|
|
@@ -146565,8 +147462,8 @@ function extractInternalMarkdownLinks(body) {
|
|
|
146565
147462
|
return [...links].sort();
|
|
146566
147463
|
}
|
|
146567
147464
|
function resolvePageLink(sourceRelativePath, target) {
|
|
146568
|
-
const sourceDir =
|
|
146569
|
-
const normalized =
|
|
147465
|
+
const sourceDir = path20.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
|
|
147466
|
+
const normalized = path20.posix.normalize(path20.posix.join(sourceDir, target.replace(/\\/g, "/")));
|
|
146570
147467
|
if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
|
|
146571
147468
|
return normalized;
|
|
146572
147469
|
}
|
|
@@ -146588,13 +147485,13 @@ function renderKnowledgePage(frontmatter, body) {
|
|
|
146588
147485
|
});
|
|
146589
147486
|
}
|
|
146590
147487
|
async function readKnowledgePage(absolutePath, workspaceRoot) {
|
|
146591
|
-
const raw = await
|
|
147488
|
+
const raw = await fs13.readFile(absolutePath, "utf8");
|
|
146592
147489
|
try {
|
|
146593
147490
|
const parsed = frontmatter_default(raw);
|
|
146594
147491
|
const frontmatter = validateKnowledgePageFrontmatter(parsed.data);
|
|
146595
147492
|
return {
|
|
146596
147493
|
absolutePath,
|
|
146597
|
-
relativePath: workspaceRoot ?
|
|
147494
|
+
relativePath: workspaceRoot ? path20.relative(workspaceRoot, absolutePath).split(path20.sep).join("/") : path20.basename(absolutePath),
|
|
146598
147495
|
frontmatter,
|
|
146599
147496
|
body: parsed.content.trim(),
|
|
146600
147497
|
contentHash: hashContent(raw),
|
|
@@ -146606,7 +147503,7 @@ async function readKnowledgePage(absolutePath, workspaceRoot) {
|
|
|
146606
147503
|
}
|
|
146607
147504
|
}
|
|
146608
147505
|
async function writeKnowledgePage(absolutePath, content) {
|
|
146609
|
-
await
|
|
147506
|
+
await fs13.mkdir(path20.dirname(absolutePath), { recursive: true });
|
|
146610
147507
|
await atomicWriteFile(absolutePath, content);
|
|
146611
147508
|
return hashContent(content);
|
|
146612
147509
|
}
|
|
@@ -146636,8 +147533,8 @@ __export(wiki_exports, {
|
|
|
146636
147533
|
readKnowledgePage: () => readKnowledgePage
|
|
146637
147534
|
});
|
|
146638
147535
|
import { createHash as createHash11 } from "crypto";
|
|
146639
|
-
import { promises as
|
|
146640
|
-
import
|
|
147536
|
+
import { promises as fs14 } from "fs";
|
|
147537
|
+
import path21 from "path";
|
|
146641
147538
|
function hash2(value) {
|
|
146642
147539
|
return createHash11("sha256").update(value).digest("hex");
|
|
146643
147540
|
}
|
|
@@ -146769,7 +147666,7 @@ function candidatePage(workspace, title, claims, store) {
|
|
|
146769
147666
|
}
|
|
146770
147667
|
async function fileHashIfPresent(filePath) {
|
|
146771
147668
|
try {
|
|
146772
|
-
return pageContentHash(await
|
|
147669
|
+
return pageContentHash(await fs14.readFile(filePath, "utf8"));
|
|
146773
147670
|
} catch (error) {
|
|
146774
147671
|
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
146775
147672
|
if (code === "ENOENT") return void 0;
|
|
@@ -146793,8 +147690,8 @@ async function writeIndex(workspace, store) {
|
|
|
146793
147690
|
lines.push("No pending proposals.");
|
|
146794
147691
|
} else {
|
|
146795
147692
|
for (const proposal of pending) {
|
|
146796
|
-
const relative3 =
|
|
146797
|
-
lines.push("- [" +
|
|
147693
|
+
const relative3 = path21.relative(paths.root, proposal.proposalPath).split(path21.sep).join("/");
|
|
147694
|
+
lines.push("- [" + path21.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
|
|
146798
147695
|
}
|
|
146799
147696
|
}
|
|
146800
147697
|
lines.push("");
|
|
@@ -146802,7 +147699,7 @@ async function writeIndex(workspace, store) {
|
|
|
146802
147699
|
}
|
|
146803
147700
|
async function appendLog(workspace, line) {
|
|
146804
147701
|
const logPath = getKnowledgeWorkspacePaths(workspace).log;
|
|
146805
|
-
await
|
|
147702
|
+
await fs14.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
|
|
146806
147703
|
}
|
|
146807
147704
|
function toPageRecord(workspace, page, status, contentHash, manualContentHash) {
|
|
146808
147705
|
return {
|
|
@@ -146887,9 +147784,9 @@ async function applyKnowledgeProposal(input) {
|
|
|
146887
147784
|
}
|
|
146888
147785
|
if (proposal.status !== "pending") throw new Error("Knowledge proposal is not pending review");
|
|
146889
147786
|
const root = getKnowledgeWorkspacePaths(input.workspace).root;
|
|
146890
|
-
const targetPath =
|
|
146891
|
-
const proposalPath =
|
|
146892
|
-
if (!targetPath.startsWith(root +
|
|
147787
|
+
const targetPath = path21.resolve(proposal.targetPath);
|
|
147788
|
+
const proposalPath = path21.resolve(proposal.proposalPath);
|
|
147789
|
+
if (!targetPath.startsWith(root + path21.sep) || !proposalPath.startsWith(root + path21.sep)) {
|
|
146893
147790
|
throw new Error("Knowledge proposal path escapes its workspace");
|
|
146894
147791
|
}
|
|
146895
147792
|
await withFileLock(root, async () => {
|
|
@@ -146927,12 +147824,12 @@ async function markdownFiles(directory) {
|
|
|
146927
147824
|
const files = [];
|
|
146928
147825
|
let entries;
|
|
146929
147826
|
try {
|
|
146930
|
-
entries = await
|
|
147827
|
+
entries = await fs14.readdir(directory, { withFileTypes: true });
|
|
146931
147828
|
} catch {
|
|
146932
147829
|
return files;
|
|
146933
147830
|
}
|
|
146934
147831
|
for (const entry of entries) {
|
|
146935
|
-
const filePath =
|
|
147832
|
+
const filePath = path21.join(directory, entry.name);
|
|
146936
147833
|
if (entry.isDirectory()) {
|
|
146937
147834
|
files.push(...await markdownFiles(filePath));
|
|
146938
147835
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
@@ -146955,13 +147852,13 @@ async function lintKnowledgeWorkspace(input) {
|
|
|
146955
147852
|
kind: "malformed-frontmatter",
|
|
146956
147853
|
severity: "error",
|
|
146957
147854
|
message: error instanceof Error ? error.message : "Malformed knowledge page.",
|
|
146958
|
-
relativePath:
|
|
147855
|
+
relativePath: path21.relative(paths.root, filePath).split(path21.sep).join("/")
|
|
146959
147856
|
});
|
|
146960
147857
|
}
|
|
146961
147858
|
}
|
|
146962
147859
|
const indexed = /* @__PURE__ */ new Set();
|
|
146963
147860
|
try {
|
|
146964
|
-
const index = await
|
|
147861
|
+
const index = await fs14.readFile(paths.index, "utf8");
|
|
146965
147862
|
for (const link of extractInternalMarkdownLinks(index)) {
|
|
146966
147863
|
const resolved = resolvePageLink("index.md", link);
|
|
146967
147864
|
if (resolved) indexed.add(resolved);
|
|
@@ -147050,7 +147947,7 @@ async function lintKnowledgeWorkspace(input) {
|
|
|
147050
147947
|
kind: "manual-edit-protected",
|
|
147051
147948
|
severity: "warning",
|
|
147052
147949
|
message: "A newer proposal was held because the target page has manual edits.",
|
|
147053
|
-
relativePath:
|
|
147950
|
+
relativePath: path21.relative(paths.root, proposal.targetPath).split(path21.sep).join("/")
|
|
147054
147951
|
});
|
|
147055
147952
|
}
|
|
147056
147953
|
}
|
|
@@ -147183,7 +148080,7 @@ var workflow_store_exports = {};
|
|
|
147183
148080
|
__export(workflow_store_exports, {
|
|
147184
148081
|
WorkflowStore: () => WorkflowStore
|
|
147185
148082
|
});
|
|
147186
|
-
import { randomUUID as
|
|
148083
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
147187
148084
|
function parseJson(value, fallback) {
|
|
147188
148085
|
if (typeof value !== "string" || !value) return fallback;
|
|
147189
148086
|
try {
|
|
@@ -147192,7 +148089,7 @@ function parseJson(value, fallback) {
|
|
|
147192
148089
|
return fallback;
|
|
147193
148090
|
}
|
|
147194
148091
|
}
|
|
147195
|
-
function
|
|
148092
|
+
function optionalText4(value) {
|
|
147196
148093
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
147197
148094
|
}
|
|
147198
148095
|
function rowToWorkflow(row) {
|
|
@@ -147229,14 +148126,14 @@ function rowToRun(row) {
|
|
|
147229
148126
|
workflowId: row.workflowId,
|
|
147230
148127
|
projectId: row.projectId,
|
|
147231
148128
|
task: row.task,
|
|
147232
|
-
...
|
|
148129
|
+
...optionalText4(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
|
|
147233
148130
|
selectedEvidence: parseJson(row.selectedEvidenceJson, []),
|
|
147234
148131
|
phaseState: parseJson(row.phaseStateJson, {}),
|
|
147235
148132
|
outcome: row.outcome,
|
|
147236
148133
|
verificationVerdict: row.verificationVerdict,
|
|
147237
|
-
...
|
|
148134
|
+
...optionalText4(row.failureReason) ? { failureReason: row.failureReason } : {},
|
|
147238
148135
|
startedAt: row.startedAt,
|
|
147239
|
-
...
|
|
148136
|
+
...optionalText4(row.completedAt) ? { completedAt: row.completedAt } : {}
|
|
147240
148137
|
};
|
|
147241
148138
|
}
|
|
147242
148139
|
var WorkflowStore;
|
|
@@ -147283,7 +148180,7 @@ var init_workflow_store = __esm({
|
|
|
147283
148180
|
}
|
|
147284
148181
|
recordRun(input) {
|
|
147285
148182
|
const run = {
|
|
147286
|
-
id:
|
|
148183
|
+
id: randomUUID12(),
|
|
147287
148184
|
workflowId: input.workflowId,
|
|
147288
148185
|
projectId: input.projectId,
|
|
147289
148186
|
task: input.task.trim(),
|
|
@@ -147361,8 +148258,8 @@ var init_outcome_types = __esm({
|
|
|
147361
148258
|
});
|
|
147362
148259
|
|
|
147363
148260
|
// src/knowledge/outcome-store.ts
|
|
147364
|
-
import { randomUUID as
|
|
147365
|
-
function
|
|
148261
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
148262
|
+
function optionalText5(value) {
|
|
147366
148263
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
147367
148264
|
}
|
|
147368
148265
|
function text(value, field, max = 1e3) {
|
|
@@ -147385,8 +148282,8 @@ function rowToSignal(row) {
|
|
|
147385
148282
|
candidateId: row.candidateId,
|
|
147386
148283
|
kind: row.kind,
|
|
147387
148284
|
sourceRef: row.sourceRef,
|
|
147388
|
-
...
|
|
147389
|
-
...
|
|
148285
|
+
...optionalText5(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
|
|
148286
|
+
...optionalText5(row.detail) ? { detail: row.detail } : {},
|
|
147390
148287
|
observedAt: row.observedAt
|
|
147391
148288
|
};
|
|
147392
148289
|
}
|
|
@@ -147405,7 +148302,7 @@ var init_outcome_store = __esm({
|
|
|
147405
148302
|
}
|
|
147406
148303
|
record(input) {
|
|
147407
148304
|
const signal = {
|
|
147408
|
-
id:
|
|
148305
|
+
id: randomUUID13(),
|
|
147409
148306
|
projectId: text(input.projectId, "project id"),
|
|
147410
148307
|
candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
|
|
147411
148308
|
candidateId: text(input.candidateId, "candidate id"),
|
|
@@ -147832,8 +148729,8 @@ __export(workflows_exports, {
|
|
|
147832
148729
|
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
147833
148730
|
});
|
|
147834
148731
|
import { createHash as createHash12 } from "crypto";
|
|
147835
|
-
import { promises as
|
|
147836
|
-
import
|
|
148732
|
+
import { promises as fs15 } from "fs";
|
|
148733
|
+
import path22 from "path";
|
|
147837
148734
|
function hash3(value) {
|
|
147838
148735
|
return createHash12("sha256").update(value).digest("hex");
|
|
147839
148736
|
}
|
|
@@ -147854,7 +148751,7 @@ function requiredText(data, key) {
|
|
|
147854
148751
|
}
|
|
147855
148752
|
return value.trim();
|
|
147856
148753
|
}
|
|
147857
|
-
function
|
|
148754
|
+
function optionalText6(data, key) {
|
|
147858
148755
|
const value = data[key];
|
|
147859
148756
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
147860
148757
|
if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
|
|
@@ -147897,11 +148794,11 @@ function phaseFromValue(value, index) {
|
|
|
147897
148794
|
}
|
|
147898
148795
|
const data = value;
|
|
147899
148796
|
const title = requiredText(data, "title");
|
|
147900
|
-
const phaseId =
|
|
148797
|
+
const phaseId = optionalText6(data, "id") ?? slug(title) + "-" + (index + 1);
|
|
147901
148798
|
return {
|
|
147902
148799
|
id: phaseId,
|
|
147903
148800
|
title,
|
|
147904
|
-
instructions:
|
|
148801
|
+
instructions: optionalText6(data, "instructions") ?? "",
|
|
147905
148802
|
branches: optionalStringArray(data, "branches"),
|
|
147906
148803
|
expectedOutputs: optionalStringArray(data, "expectedOutputs"),
|
|
147907
148804
|
verificationGates: optionalStringArray(data, "verificationGates")
|
|
@@ -147936,7 +148833,7 @@ function phasesFromBody(body) {
|
|
|
147936
148833
|
}
|
|
147937
148834
|
function normalizeSourcePath(sourcePath) {
|
|
147938
148835
|
const normalized = sourcePath.replace(/\\/g, "/");
|
|
147939
|
-
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") ||
|
|
148836
|
+
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path22.posix.normalize(normalized) !== normalized) {
|
|
147940
148837
|
throw new Error("workflow source path must be a safe workflows/*.md path");
|
|
147941
148838
|
}
|
|
147942
148839
|
return normalized;
|
|
@@ -148010,7 +148907,7 @@ function parsedWorkflow(data, body, input) {
|
|
|
148010
148907
|
id: requiredText(data, "id"),
|
|
148011
148908
|
workspaceId: input.workspaceId,
|
|
148012
148909
|
title: requiredText(data, "title"),
|
|
148013
|
-
description:
|
|
148910
|
+
description: optionalText6(data, "description") ?? "",
|
|
148014
148911
|
status: optionalStatus(data),
|
|
148015
148912
|
version: optionalNumber(data, "version", 1),
|
|
148016
148913
|
taskLenses: optionalStringArray(data, "taskLenses"),
|
|
@@ -148029,9 +148926,9 @@ function parsedWorkflow(data, body, input) {
|
|
|
148029
148926
|
sourcePath: normalizeSourcePath(input.sourcePath),
|
|
148030
148927
|
sourceHash: "",
|
|
148031
148928
|
contentHash: input.contentHash,
|
|
148032
|
-
createdAt:
|
|
148033
|
-
updatedAt:
|
|
148034
|
-
...
|
|
148929
|
+
createdAt: optionalText6(data, "createdAt") ?? now2(),
|
|
148930
|
+
updatedAt: optionalText6(data, "updatedAt") ?? now2(),
|
|
148931
|
+
...optionalText6(data, "importedFrom") ? { importedFrom: optionalText6(data, "importedFrom") } : {}
|
|
148035
148932
|
};
|
|
148036
148933
|
if (candidate.phases.length === 0) {
|
|
148037
148934
|
throw new Error("workflow must contain at least one phase or a non-empty body");
|
|
@@ -148085,7 +148982,7 @@ async function writeCanonicalWorkflow(input) {
|
|
|
148085
148982
|
const filePath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
|
|
148086
148983
|
const content = renderWorkflowMarkdown(workflow);
|
|
148087
148984
|
await withFileLock(paths.root, async () => {
|
|
148088
|
-
await
|
|
148985
|
+
await fs15.mkdir(path22.dirname(filePath), { recursive: true });
|
|
148089
148986
|
await atomicWriteFile(filePath, content);
|
|
148090
148987
|
});
|
|
148091
148988
|
const store = await storeFor2(input.workspace);
|
|
@@ -148094,13 +148991,13 @@ async function writeCanonicalWorkflow(input) {
|
|
|
148094
148991
|
async function markdownFiles2(directory) {
|
|
148095
148992
|
let entries;
|
|
148096
148993
|
try {
|
|
148097
|
-
entries = await
|
|
148994
|
+
entries = await fs15.readdir(directory, { withFileTypes: true });
|
|
148098
148995
|
} catch {
|
|
148099
148996
|
return [];
|
|
148100
148997
|
}
|
|
148101
148998
|
const files = [];
|
|
148102
148999
|
for (const entry of entries) {
|
|
148103
|
-
const absolutePath =
|
|
149000
|
+
const absolutePath = path22.join(directory, entry.name);
|
|
148104
149001
|
if (entry.isDirectory()) {
|
|
148105
149002
|
files.push(...await markdownFiles2(absolutePath));
|
|
148106
149003
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
@@ -148115,9 +149012,9 @@ async function syncCanonicalWorkflows(workspace) {
|
|
|
148115
149012
|
const workflows = [];
|
|
148116
149013
|
const errors = [];
|
|
148117
149014
|
for (const absolutePath of await markdownFiles2(paths.workflows)) {
|
|
148118
|
-
const sourcePath =
|
|
149015
|
+
const sourcePath = path22.relative(paths.root, absolutePath).split(path22.sep).join("/");
|
|
148119
149016
|
try {
|
|
148120
|
-
const raw = await
|
|
149017
|
+
const raw = await fs15.readFile(absolutePath, "utf8");
|
|
148121
149018
|
const workflow = parseWorkflowMarkdown(raw, { workspaceId: workspace.id, sourcePath });
|
|
148122
149019
|
workflows.push(store.upsertWorkflow(workflow));
|
|
148123
149020
|
} catch (error) {
|
|
@@ -148229,21 +149126,21 @@ function importedWorkflowSpec(input) {
|
|
|
148229
149126
|
return materializeWorkflow(spec);
|
|
148230
149127
|
}
|
|
148231
149128
|
async function importWindsurfWorkflows(input) {
|
|
148232
|
-
const sourceDirectory =
|
|
149129
|
+
const sourceDirectory = path22.join(path22.resolve(input.projectRoot), ".windsurf", "workflows");
|
|
148233
149130
|
const paths = getKnowledgeWorkspacePaths(input.workspace);
|
|
148234
149131
|
const imported = [];
|
|
148235
149132
|
const skipped = [];
|
|
148236
149133
|
let entries;
|
|
148237
149134
|
try {
|
|
148238
|
-
entries = await
|
|
149135
|
+
entries = await fs15.readdir(sourceDirectory, { withFileTypes: true });
|
|
148239
149136
|
} catch {
|
|
148240
149137
|
return { imported, skipped };
|
|
148241
149138
|
}
|
|
148242
149139
|
for (const entry of entries.filter((item) => item.isFile() && item.name.toLowerCase().endsWith(".md"))) {
|
|
148243
|
-
const originalPath =
|
|
148244
|
-
const sourcePath =
|
|
149140
|
+
const originalPath = path22.join(sourceDirectory, entry.name);
|
|
149141
|
+
const sourcePath = path22.relative(path22.resolve(input.projectRoot), originalPath).split(path22.sep).join("/");
|
|
148245
149142
|
try {
|
|
148246
|
-
const raw = await
|
|
149143
|
+
const raw = await fs15.readFile(originalPath, "utf8");
|
|
148247
149144
|
const workflow = importedWorkflowSpec({
|
|
148248
149145
|
workspace: input.workspace,
|
|
148249
149146
|
sourceName: entry.name,
|
|
@@ -148252,7 +149149,7 @@ async function importWindsurfWorkflows(input) {
|
|
|
148252
149149
|
});
|
|
148253
149150
|
const targetPath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
|
|
148254
149151
|
try {
|
|
148255
|
-
await
|
|
149152
|
+
await fs15.access(targetPath);
|
|
148256
149153
|
skipped.push({ sourcePath, reason: "canonical workflow already exists and was preserved" });
|
|
148257
149154
|
continue;
|
|
148258
149155
|
} catch {
|
|
@@ -148265,12 +149162,12 @@ async function importWindsurfWorkflows(input) {
|
|
|
148265
149162
|
});
|
|
148266
149163
|
}
|
|
148267
149164
|
}
|
|
148268
|
-
await
|
|
149165
|
+
await fs15.mkdir(paths.workflows, { recursive: true });
|
|
148269
149166
|
return { imported, skipped };
|
|
148270
149167
|
}
|
|
148271
149168
|
function isWithin2(root, candidate) {
|
|
148272
|
-
const relative3 =
|
|
148273
|
-
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." +
|
|
149169
|
+
const relative3 = path22.relative(root, candidate);
|
|
149170
|
+
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path22.sep) && !path22.isAbsolute(relative3);
|
|
148274
149171
|
}
|
|
148275
149172
|
function adapterRelativePath(workflow, agent) {
|
|
148276
149173
|
const name = "memorix-workflow-" + slug(workflow.id.replace(/^workflow:/, ""));
|
|
@@ -148356,15 +149253,15 @@ async function previewWorkflowAdapter(input) {
|
|
|
148356
149253
|
reason: "The canonical workflow does not declare compatibility with this agent."
|
|
148357
149254
|
};
|
|
148358
149255
|
}
|
|
148359
|
-
const projectRoot =
|
|
148360
|
-
const targetPath =
|
|
149256
|
+
const projectRoot = path22.resolve(input.projectRoot);
|
|
149257
|
+
const targetPath = path22.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
|
|
148361
149258
|
if (!isWithin2(projectRoot, targetPath)) {
|
|
148362
149259
|
throw new Error("Workflow adapter target escapes the project root");
|
|
148363
149260
|
}
|
|
148364
149261
|
const content = adapterContent(input.workflow, input.agent);
|
|
148365
149262
|
let existing;
|
|
148366
149263
|
try {
|
|
148367
|
-
existing = await
|
|
149264
|
+
existing = await fs15.readFile(targetPath, "utf8");
|
|
148368
149265
|
} catch (error) {
|
|
148369
149266
|
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
|
|
148370
149267
|
if (code !== "ENOENT") throw error;
|
|
@@ -148385,11 +149282,11 @@ async function applyWorkflowAdapter(input) {
|
|
|
148385
149282
|
if (initial.status === "unsupported" || initial.status === "conflict" || initial.status === "unchanged") {
|
|
148386
149283
|
return initial;
|
|
148387
149284
|
}
|
|
148388
|
-
const projectRoot =
|
|
149285
|
+
const projectRoot = path22.resolve(input.projectRoot);
|
|
148389
149286
|
return withFileLock(projectRoot, async () => {
|
|
148390
149287
|
const current = await previewWorkflowAdapter(input);
|
|
148391
149288
|
if (current.status !== "create" && current.status !== "update") return current;
|
|
148392
|
-
await
|
|
149289
|
+
await fs15.mkdir(path22.dirname(current.targetPath), { recursive: true });
|
|
148393
149290
|
await atomicWriteFile(current.targetPath, current.content);
|
|
148394
149291
|
return current;
|
|
148395
149292
|
});
|
|
@@ -148679,6 +149576,11 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
148679
149576
|
}
|
|
148680
149577
|
return { action: "complete" };
|
|
148681
149578
|
}
|
|
149579
|
+
if (job.kind === "long-term-maintenance") {
|
|
149580
|
+
const { maintainLongTermMemories: maintainLongTermMemories2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
149581
|
+
await maintainLongTermMemories2({ dataDir: projectDir2, projectId });
|
|
149582
|
+
return { action: "reschedule", delayMs: 24 * 60 * 60 * 1e3, resetAttempts: true };
|
|
149583
|
+
}
|
|
148682
149584
|
if (job.kind === "consolidation") {
|
|
148683
149585
|
const limit2 = consolidationBatchSize(job.payload);
|
|
148684
149586
|
const result2 = await runAutomaticConsolidation(projectId, projectDir2, {
|
|
@@ -149020,15 +149922,15 @@ __export(vector_backfill_runner_exports, {
|
|
|
149020
149922
|
});
|
|
149021
149923
|
import { spawn as spawn4 } from "child_process";
|
|
149022
149924
|
import { existsSync as existsSync10 } from "fs";
|
|
149023
|
-
import
|
|
149925
|
+
import path23 from "path";
|
|
149024
149926
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
149025
149927
|
function isNonEmptyString(value) {
|
|
149026
149928
|
return typeof value === "string" && value.trim().length > 0;
|
|
149027
149929
|
}
|
|
149028
149930
|
function resolveVectorBackfillRunnerPath(moduleUrl = import.meta.url) {
|
|
149029
|
-
const moduleDir =
|
|
149030
|
-
const distDir =
|
|
149031
|
-
return
|
|
149931
|
+
const moduleDir = path23.dirname(fileURLToPath5(moduleUrl));
|
|
149932
|
+
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;
|
|
149933
|
+
return path23.join(distDir, "vector-backfill-runner.js");
|
|
149032
149934
|
}
|
|
149033
149935
|
function parseVectorBackfillRequest(raw) {
|
|
149034
149936
|
let value;
|
|
@@ -149037,7 +149939,7 @@ function parseVectorBackfillRequest(raw) {
|
|
|
149037
149939
|
} catch {
|
|
149038
149940
|
throw new Error("Vector backfill runner received invalid JSON input");
|
|
149039
149941
|
}
|
|
149040
|
-
if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !
|
|
149942
|
+
if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path23.isAbsolute(value.projectRoot) || !path23.isAbsolute(value.dataDir)) {
|
|
149041
149943
|
throw new Error("Vector backfill runner received an invalid request");
|
|
149042
149944
|
}
|
|
149043
149945
|
return {
|
|
@@ -150119,8 +151021,16 @@ var init_observations = __esm({
|
|
|
150119
151021
|
});
|
|
150120
151022
|
|
|
150121
151023
|
// src/store/session-store.ts
|
|
150122
|
-
|
|
150123
|
-
|
|
151024
|
+
var session_store_exports = {};
|
|
151025
|
+
__export(session_store_exports, {
|
|
151026
|
+
SessionGracefulDegrade: () => SessionGracefulDegrade,
|
|
151027
|
+
SessionSqliteStore: () => SessionSqliteStore,
|
|
151028
|
+
getSessionStore: () => getSessionStore,
|
|
151029
|
+
initSessionStore: () => initSessionStore,
|
|
151030
|
+
resetSessionStore: () => resetSessionStore
|
|
151031
|
+
});
|
|
151032
|
+
import path24 from "path";
|
|
151033
|
+
import fs16 from "fs";
|
|
150124
151034
|
function sessionToRow(session) {
|
|
150125
151035
|
return {
|
|
150126
151036
|
id: session.id,
|
|
@@ -150149,6 +151059,10 @@ function getSessionStore() {
|
|
|
150149
151059
|
}
|
|
150150
151060
|
return _store3;
|
|
150151
151061
|
}
|
|
151062
|
+
function resetSessionStore() {
|
|
151063
|
+
_store3 = null;
|
|
151064
|
+
_storeDataDir3 = null;
|
|
151065
|
+
}
|
|
150152
151066
|
async function initSessionStore(dataDir) {
|
|
150153
151067
|
if (_store3 && _storeDataDir3 === dataDir) return _store3;
|
|
150154
151068
|
_store3 = null;
|
|
@@ -150202,10 +151116,10 @@ var init_session_store = __esm({
|
|
|
150202
151116
|
async migrateFromJsonIfNeeded() {
|
|
150203
151117
|
const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM sessions`).get();
|
|
150204
151118
|
if (count2.cnt > 0) return;
|
|
150205
|
-
const jsonPath =
|
|
150206
|
-
if (!
|
|
151119
|
+
const jsonPath = path24.join(this.dataDir, "sessions.json");
|
|
151120
|
+
if (!fs16.existsSync(jsonPath)) return;
|
|
150207
151121
|
try {
|
|
150208
|
-
const raw =
|
|
151122
|
+
const raw = fs16.readFileSync(jsonPath, "utf-8");
|
|
150209
151123
|
const sessions = JSON.parse(raw);
|
|
150210
151124
|
if (!Array.isArray(sessions) || sessions.length === 0) return;
|
|
150211
151125
|
console.error(`[memorix] Migrating ${sessions.length} sessions from JSON to SQLite...`);
|
|
@@ -152001,827 +152915,6 @@ var init_session = __esm({
|
|
|
152001
152915
|
}
|
|
152002
152916
|
});
|
|
152003
152917
|
|
|
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
152918
|
// src/knowledge/workset.ts
|
|
152826
152919
|
function unique(values) {
|
|
152827
152920
|
return [...new Set(values.filter(Boolean))];
|
|
@@ -153029,6 +153122,37 @@ function renderTaskWorksetPrompt(input) {
|
|
|
153029
153122
|
trust: "source-backed"
|
|
153030
153123
|
});
|
|
153031
153124
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
153125
|
+
if (input.alwaysOn) {
|
|
153126
|
+
const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
|
|
153127
|
+
if (hasAlwaysOn) {
|
|
153128
|
+
appendLine(lines, "", maxTokens, omitted, "always-on-heading");
|
|
153129
|
+
appendLine(lines, "You and this workspace", maxTokens, omitted, "always-on-heading");
|
|
153130
|
+
for (const profile of input.alwaysOn.profile.slice(0, 2)) {
|
|
153131
|
+
appendLine(lines, "- " + short(profile, 26), maxTokens, omitted, "always-on-profile", selected, {
|
|
153132
|
+
kind: "memory",
|
|
153133
|
+
id: "profile",
|
|
153134
|
+
reason: "user profile memory",
|
|
153135
|
+
trust: "source-backed"
|
|
153136
|
+
});
|
|
153137
|
+
}
|
|
153138
|
+
if (input.alwaysOn.state) {
|
|
153139
|
+
appendLine(lines, "- Recently: " + short(input.alwaysOn.state, 40), maxTokens, omitted, "always-on-state", selected, {
|
|
153140
|
+
kind: "continuation",
|
|
153141
|
+
id: "workspace-state",
|
|
153142
|
+
reason: "latest session summary for this workspace",
|
|
153143
|
+
trust: "historical"
|
|
153144
|
+
});
|
|
153145
|
+
}
|
|
153146
|
+
for (const durable of input.alwaysOn.durable.slice(0, 2)) {
|
|
153147
|
+
appendLine(lines, "- " + short(durable, 28), maxTokens, omitted, "always-on-durable", selected, {
|
|
153148
|
+
kind: "durable-memory",
|
|
153149
|
+
id: "durable-always",
|
|
153150
|
+
reason: "recent durable memory for this workspace",
|
|
153151
|
+
trust: "source-backed"
|
|
153152
|
+
});
|
|
153153
|
+
}
|
|
153154
|
+
}
|
|
153155
|
+
}
|
|
153032
153156
|
const hasContinuation = Boolean(
|
|
153033
153157
|
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
|
|
153034
153158
|
);
|
|
@@ -153499,6 +153623,7 @@ async function buildTaskWorkset(input) {
|
|
|
153499
153623
|
version: "1.3",
|
|
153500
153624
|
task,
|
|
153501
153625
|
lens: input.lens,
|
|
153626
|
+
...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
|
|
153502
153627
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
153503
153628
|
...continuation ? { continuation } : {},
|
|
153504
153629
|
...input.codeState ? { codeState: short(input.codeState, 28) } : {},
|
|
@@ -154651,6 +154776,33 @@ function activeProjectObservations(observations2, projectId) {
|
|
|
154651
154776
|
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
154652
154777
|
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
154653
154778
|
}
|
|
154779
|
+
async function buildAlwaysOnBlock(input) {
|
|
154780
|
+
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));
|
|
154781
|
+
let state;
|
|
154782
|
+
try {
|
|
154783
|
+
await initSessionStore(input.dataDir);
|
|
154784
|
+
const { getSessionStore: getSessionStore2 } = await Promise.resolve().then(() => (init_session_store(), session_store_exports));
|
|
154785
|
+
const sessions = await getSessionStore2().loadByProject(input.projectId);
|
|
154786
|
+
const latest = sessions.filter((session) => session.endedAt && session.summary?.trim()).sort((a, b) => Date.parse(b.endedAt ?? "") - Date.parse(a.endedAt ?? ""))[0];
|
|
154787
|
+
if (latest?.summary) state = compactContinuationText(latest.summary, 40);
|
|
154788
|
+
} catch {
|
|
154789
|
+
}
|
|
154790
|
+
const durable = [];
|
|
154791
|
+
try {
|
|
154792
|
+
const items = await listLongTermMemories({
|
|
154793
|
+
dataDir: input.dataDir,
|
|
154794
|
+
reader: {
|
|
154795
|
+
projectId: input.projectId,
|
|
154796
|
+
...input.reader.agentId ? { agentId: input.reader.agentId } : {},
|
|
154797
|
+
...input.reader.isTeamMember ? { isTeamMember: true } : {}
|
|
154798
|
+
},
|
|
154799
|
+
limit: 10
|
|
154800
|
+
});
|
|
154801
|
+
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)));
|
|
154802
|
+
} catch {
|
|
154803
|
+
}
|
|
154804
|
+
return { profile, state, durable };
|
|
154805
|
+
}
|
|
154654
154806
|
function decideRefresh(input) {
|
|
154655
154807
|
if (input.mode === "never") {
|
|
154656
154808
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -154855,12 +155007,19 @@ async function buildAutoProjectContext(input) {
|
|
|
154855
155007
|
};
|
|
154856
155008
|
}
|
|
154857
155009
|
}
|
|
155010
|
+
const alwaysOn = await buildAlwaysOnBlock({
|
|
155011
|
+
dataDir: input.dataDir,
|
|
155012
|
+
projectId: input.project.id,
|
|
155013
|
+
reader: input.reader ?? { projectId: input.project.id },
|
|
155014
|
+
eligible: deliveryEligibleProjectObservations(input.observations, input.project.id)
|
|
155015
|
+
});
|
|
154858
155016
|
const workset = await buildTaskWorkset({
|
|
154859
155017
|
projectId: input.project.id,
|
|
154860
155018
|
dataDir: input.dataDir,
|
|
154861
155019
|
...task ? { task } : {},
|
|
154862
155020
|
lens: lens.id,
|
|
154863
155021
|
startHere,
|
|
155022
|
+
...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
|
|
154864
155023
|
...externalOutline ? { semanticCode: externalOutline } : {},
|
|
154865
155024
|
providerQuality: providerQuality2,
|
|
154866
155025
|
currentFacts: worksetFactLines(currentFacts),
|
|
@@ -155043,7 +155202,16 @@ function formatAutoProjectContextSummary(context) {
|
|
|
155043
155202
|
lensLine(context),
|
|
155044
155203
|
"",
|
|
155045
155204
|
...formatCurrentFactsLines(context.currentFacts),
|
|
155046
|
-
""
|
|
155205
|
+
""
|
|
155206
|
+
].filter(Boolean);
|
|
155207
|
+
if (context.workset.alwaysOn && (context.workset.alwaysOn.profile.length > 0 || context.workset.alwaysOn.state || context.workset.alwaysOn.durable.length > 0)) {
|
|
155208
|
+
lines.push("You and this workspace");
|
|
155209
|
+
for (const profile of context.workset.alwaysOn.profile.slice(0, 2)) lines.push(`- ${profile}`);
|
|
155210
|
+
if (context.workset.alwaysOn.state) lines.push(`- Recently: ${context.workset.alwaysOn.state}`);
|
|
155211
|
+
for (const durable of context.workset.alwaysOn.durable.slice(0, 2)) lines.push(`- ${durable}`);
|
|
155212
|
+
lines.push("");
|
|
155213
|
+
}
|
|
155214
|
+
lines.push(...[
|
|
155047
155215
|
`- Code memory: ${context.overview.code.files} files / ${context.overview.code.symbols} symbols / ${context.overview.code.refs} memory links`,
|
|
155048
155216
|
`- Code provider: ${context.providerQuality.selected} (${context.providerQuality.selectedQuality})`,
|
|
155049
155217
|
`- Languages: ${formatLanguages(context.overview)}`,
|
|
@@ -155052,7 +155220,7 @@ function formatAutoProjectContextSummary(context) {
|
|
|
155052
155220
|
`- Refresh: ${context.refresh.message}`,
|
|
155053
155221
|
"",
|
|
155054
155222
|
"Start here"
|
|
155055
|
-
].filter(Boolean);
|
|
155223
|
+
].filter(Boolean));
|
|
155056
155224
|
if (startHere.length > 0) {
|
|
155057
155225
|
startHere.forEach((path34, index) => lines.push(`${index + 1}. ${path34}`));
|
|
155058
155226
|
} else {
|
|
@@ -155176,6 +155344,8 @@ var init_auto_context = __esm({
|
|
|
155176
155344
|
init_store();
|
|
155177
155345
|
init_admission();
|
|
155178
155346
|
init_session();
|
|
155347
|
+
init_long_term();
|
|
155348
|
+
init_visibility();
|
|
155179
155349
|
init_session_store();
|
|
155180
155350
|
init_task_lens();
|
|
155181
155351
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
@@ -158126,8 +158296,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
158126
158296
|
const { promises: fsP } = await import("fs");
|
|
158127
158297
|
const registryPath = path30.join(baseDir, ".project-aliases.json");
|
|
158128
158298
|
const raw = await fsP.readFile(registryPath, "utf-8");
|
|
158129
|
-
const
|
|
158130
|
-
aliasGroups =
|
|
158299
|
+
const registry2 = JSON.parse(raw);
|
|
158300
|
+
aliasGroups = registry2.groups || [];
|
|
158131
158301
|
} catch {
|
|
158132
158302
|
}
|
|
158133
158303
|
const currentGroup = aliasGroups.find((g) => g.aliases?.includes(effectiveProjectId) || g.canonical === effectiveProjectId);
|
|
@@ -161043,13 +161213,28 @@ alwaysApply: true
|
|
|
161043
161213
|
'- For "why" decisions, use `memorix_store_reasoning`',
|
|
161044
161214
|
'- 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
161215
|
"- 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.",
|
|
161216
|
+
'- 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
161217
|
"",
|
|
161047
161218
|
"**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
|
|
161048
161219
|
"",
|
|
161220
|
+
"**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.",
|
|
161221
|
+
"",
|
|
161222
|
+
`**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.`,
|
|
161223
|
+
"",
|
|
161224
|
+
"**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.",
|
|
161225
|
+
"",
|
|
161049
161226
|
"## When to resolve memory",
|
|
161050
161227
|
"",
|
|
161051
161228
|
"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
161229
|
"",
|
|
161230
|
+
"## End sessions with a summary",
|
|
161231
|
+
"",
|
|
161232
|
+
"When a session finishes, call `memorix_session_end` with a short structured summary so the next agent can resume. Recommended sections:",
|
|
161233
|
+
"- **Goal** \u2014 what this session was working on",
|
|
161234
|
+
"- **Discoveries** \u2014 findings, gotchas, learnings",
|
|
161235
|
+
"- **Accomplished** \u2014 completed items, plus PENDING items for the next session",
|
|
161236
|
+
"- **Relevant Files** \u2014 paths and what changed",
|
|
161237
|
+
"",
|
|
161053
161238
|
"## Tools quick reference",
|
|
161054
161239
|
"",
|
|
161055
161240
|
"| Tool | Use when |",
|
|
@@ -162644,8 +162829,8 @@ init_persistence();
|
|
|
162644
162829
|
|
|
162645
162830
|
// src/rules/syncer.ts
|
|
162646
162831
|
init_esm_shims();
|
|
162647
|
-
import { promises as
|
|
162648
|
-
import
|
|
162832
|
+
import { promises as fs17 } from "fs";
|
|
162833
|
+
import path25 from "path";
|
|
162649
162834
|
|
|
162650
162835
|
// src/rules/adapters/cursor.ts
|
|
162651
162836
|
init_esm_shims();
|
|
@@ -163308,8 +163493,8 @@ var RulesSyncer = class {
|
|
|
163308
163493
|
const found = await this.findFiles(scanPath);
|
|
163309
163494
|
for (const filePath of found) {
|
|
163310
163495
|
try {
|
|
163311
|
-
const content = await
|
|
163312
|
-
const relativePath =
|
|
163496
|
+
const content = await fs17.readFile(filePath, "utf-8");
|
|
163497
|
+
const relativePath = path25.relative(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
163313
163498
|
const parsed = entry.adapter.parse(relativePath, content);
|
|
163314
163499
|
rules.push(...parsed);
|
|
163315
163500
|
} catch {
|
|
@@ -163377,7 +163562,7 @@ var RulesSyncer = class {
|
|
|
163377
163562
|
const entries = [];
|
|
163378
163563
|
for (const adapter of this.adapters.values()) {
|
|
163379
163564
|
const absolutePaths = adapter.filePatterns.map(
|
|
163380
|
-
(p) =>
|
|
163565
|
+
(p) => path25.join(this.projectRoot, p)
|
|
163381
163566
|
);
|
|
163382
163567
|
entries.push({ adapter, paths: absolutePaths });
|
|
163383
163568
|
}
|
|
@@ -163385,13 +163570,13 @@ var RulesSyncer = class {
|
|
|
163385
163570
|
}
|
|
163386
163571
|
/** Find files matching a glob-like path (simple implementation) */
|
|
163387
163572
|
async findFiles(pattern) {
|
|
163388
|
-
const dir =
|
|
163389
|
-
const fileGlob =
|
|
163573
|
+
const dir = path25.dirname(pattern);
|
|
163574
|
+
const fileGlob = path25.basename(pattern);
|
|
163390
163575
|
try {
|
|
163391
|
-
const stat4 = await
|
|
163576
|
+
const stat4 = await fs17.stat(dir);
|
|
163392
163577
|
if (!stat4.isDirectory()) {
|
|
163393
163578
|
try {
|
|
163394
|
-
await
|
|
163579
|
+
await fs17.access(pattern);
|
|
163395
163580
|
return [pattern];
|
|
163396
163581
|
} catch {
|
|
163397
163582
|
return [];
|
|
@@ -163399,7 +163584,7 @@ var RulesSyncer = class {
|
|
|
163399
163584
|
}
|
|
163400
163585
|
} catch {
|
|
163401
163586
|
try {
|
|
163402
|
-
await
|
|
163587
|
+
await fs17.access(pattern);
|
|
163403
163588
|
return [pattern];
|
|
163404
163589
|
} catch {
|
|
163405
163590
|
return [];
|
|
@@ -163407,16 +163592,16 @@ var RulesSyncer = class {
|
|
|
163407
163592
|
}
|
|
163408
163593
|
if (fileGlob.includes("*")) {
|
|
163409
163594
|
try {
|
|
163410
|
-
const files = await
|
|
163595
|
+
const files = await fs17.readdir(dir);
|
|
163411
163596
|
const ext = fileGlob.replace("*", "");
|
|
163412
|
-
return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) =>
|
|
163597
|
+
return files.filter((f3) => ext ? f3.endsWith(ext) : true).map((f3) => path25.join(dir, f3));
|
|
163413
163598
|
} catch {
|
|
163414
163599
|
return [];
|
|
163415
163600
|
}
|
|
163416
163601
|
}
|
|
163417
163602
|
try {
|
|
163418
|
-
await
|
|
163419
|
-
return [
|
|
163603
|
+
await fs17.access(path25.join(dir, fileGlob));
|
|
163604
|
+
return [path25.join(dir, fileGlob)];
|
|
163420
163605
|
} catch {
|
|
163421
163606
|
return [];
|
|
163422
163607
|
}
|
|
@@ -164958,13 +165143,47 @@ function parseFormationTimeoutMs(raw) {
|
|
|
164958
165143
|
init_timeout();
|
|
164959
165144
|
init_secret_filter();
|
|
164960
165145
|
|
|
165146
|
+
// src/search/surfaced-registry.ts
|
|
165147
|
+
init_esm_shims();
|
|
165148
|
+
var MAX_KEYS = 64;
|
|
165149
|
+
var MAX_IDS = 200;
|
|
165150
|
+
var registry = /* @__PURE__ */ new Map();
|
|
165151
|
+
function touch(key) {
|
|
165152
|
+
const ids = registry.get(key);
|
|
165153
|
+
if (!ids) return [];
|
|
165154
|
+
registry.delete(key);
|
|
165155
|
+
registry.set(key, ids);
|
|
165156
|
+
return ids;
|
|
165157
|
+
}
|
|
165158
|
+
function getSurfacedIds(key) {
|
|
165159
|
+
return [...touch(key)];
|
|
165160
|
+
}
|
|
165161
|
+
function recordSurfacedIds(key, ids) {
|
|
165162
|
+
if (ids.length === 0) return;
|
|
165163
|
+
let list = registry.get(key) ?? [];
|
|
165164
|
+
const known = new Set(list);
|
|
165165
|
+
for (const id of ids) {
|
|
165166
|
+
if (known.has(id)) continue;
|
|
165167
|
+
known.add(id);
|
|
165168
|
+
list.push(id);
|
|
165169
|
+
}
|
|
165170
|
+
if (list.length > MAX_IDS) {
|
|
165171
|
+
list = list.slice(list.length - MAX_IDS);
|
|
165172
|
+
}
|
|
165173
|
+
registry.set(key, list);
|
|
165174
|
+
if (registry.size > MAX_KEYS) {
|
|
165175
|
+
const oldestKey = registry.keys().next().value;
|
|
165176
|
+
if (oldestKey !== void 0 && oldestKey !== key) registry.delete(oldestKey);
|
|
165177
|
+
}
|
|
165178
|
+
}
|
|
165179
|
+
|
|
164961
165180
|
// src/server/request-context.ts
|
|
164962
165181
|
init_esm_shims();
|
|
164963
|
-
import
|
|
165182
|
+
import path26 from "path";
|
|
164964
165183
|
function resolveRoot(projectRoot) {
|
|
164965
165184
|
const value = projectRoot.trim();
|
|
164966
165185
|
if (!value) throw new Error("Project binding requires a non-empty project root");
|
|
164967
|
-
return
|
|
165186
|
+
return path26.resolve(value);
|
|
164968
165187
|
}
|
|
164969
165188
|
var ProjectBindingController = class {
|
|
164970
165189
|
current;
|
|
@@ -165355,6 +165574,12 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
165355
165574
|
kind: "consolidation",
|
|
165356
165575
|
dedupeKey: "startup-consolidation"
|
|
165357
165576
|
});
|
|
165577
|
+
lifecycle.enqueueLongTermMaintenance({
|
|
165578
|
+
dataDir: projectDir2,
|
|
165579
|
+
projectId: project.id,
|
|
165580
|
+
source: "startup",
|
|
165581
|
+
queue
|
|
165582
|
+
});
|
|
165358
165583
|
}
|
|
165359
165584
|
try {
|
|
165360
165585
|
const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
@@ -165404,7 +165629,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
165404
165629
|
};
|
|
165405
165630
|
const server = existingServer ?? new McpServer({
|
|
165406
165631
|
name: "memorix",
|
|
165407
|
-
version: true ? "1.4.
|
|
165632
|
+
version: true ? "1.4.6" : "1.0.1"
|
|
165408
165633
|
});
|
|
165409
165634
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
165410
165635
|
server.registerTool = ((name, ...args) => {
|
|
@@ -165889,7 +166114,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
165889
166114
|
let longTermNote = "";
|
|
165890
166115
|
if (longTerm) {
|
|
165891
166116
|
try {
|
|
165892
|
-
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
166117
|
+
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2, maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
165893
166118
|
const promoted = await promoteObservationToLongTermMemory2({
|
|
165894
166119
|
dataDir: projectDir2,
|
|
165895
166120
|
observation: obs,
|
|
@@ -165903,7 +166128,24 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
165903
166128
|
...reader.isTeamMember ? { isTeamMember: true } : {}
|
|
165904
166129
|
}
|
|
165905
166130
|
});
|
|
165906
|
-
|
|
166131
|
+
const autoQualified = await maybeAutoQualifyLongTermMemory2({
|
|
166132
|
+
dataDir: projectDir2,
|
|
166133
|
+
id: promoted.memory.id,
|
|
166134
|
+
sourceDetail: "explicit"
|
|
166135
|
+
});
|
|
166136
|
+
if (autoQualified) {
|
|
166137
|
+
try {
|
|
166138
|
+
const { enqueueLongTermMaintenance: enqueueLongTermMaintenance2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
166139
|
+
enqueueLongTermMaintenance2({
|
|
166140
|
+
dataDir: projectDir2,
|
|
166141
|
+
projectId: project.id,
|
|
166142
|
+
source: "long-term:" + promoted.memory.id
|
|
166143
|
+
});
|
|
166144
|
+
} catch {
|
|
166145
|
+
}
|
|
166146
|
+
}
|
|
166147
|
+
longTermNote = autoQualified ? `
|
|
166148
|
+
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
166149
|
Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
|
|
165908
166150
|
} catch (longTermError) {
|
|
165909
166151
|
longTermNote = `
|
|
@@ -166099,6 +166341,13 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166099
166341
|
const boundary = requireExplicitAutopilotExpansion("memorix_search", purpose);
|
|
166100
166342
|
if (boundary) return boundary;
|
|
166101
166343
|
}
|
|
166344
|
+
let surfacedKey = `project:${project.id}`;
|
|
166345
|
+
try {
|
|
166346
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
166347
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
166348
|
+
if (active) surfacedKey = `session:${active.id}`;
|
|
166349
|
+
} catch {
|
|
166350
|
+
}
|
|
166102
166351
|
return withFreshIndex(async () => {
|
|
166103
166352
|
const safeLimit2 = limit2 != null ? coerceNumber(limit2, 20) : void 0;
|
|
166104
166353
|
const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
|
|
@@ -166116,7 +166365,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166116
166365
|
status: status ?? "active",
|
|
166117
166366
|
source,
|
|
166118
166367
|
quality,
|
|
166119
|
-
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
166368
|
+
reader: getObservationReader(scope === "global" ? "global" : "project"),
|
|
166369
|
+
surfacedIds: getSurfacedIds(surfacedKey)
|
|
166120
166370
|
});
|
|
166121
166371
|
let result;
|
|
166122
166372
|
try {
|
|
@@ -166135,6 +166385,7 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
166135
166385
|
}
|
|
166136
166386
|
throw error;
|
|
166137
166387
|
}
|
|
166388
|
+
recordSurfacedIds(surfacedKey, result.entries.map((entry) => entry.id));
|
|
166138
166389
|
const activeBoundary = getActiveAutopilotRetrievalBoundary();
|
|
166139
166390
|
if (scope !== "global" && activeBoundary && !force && result.entries.length > 0 && result.entries.every((entry) => activeBoundary.coveredObservationIds.has(entry.id))) {
|
|
166140
166391
|
const duplicate = blockCoveredAutopilotEvidence("memorix_search", result.entries.map((entry) => entry.id), force);
|
|
@@ -169097,13 +169348,26 @@ ${lines.join("\n")}` }] };
|
|
|
169097
169348
|
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
169349
|
return void 0;
|
|
169099
169350
|
}
|
|
169351
|
+
const mediaGenerationEnabled = process.env.MEMORIX_MCP_MEDIA_GENERATION === "1";
|
|
169352
|
+
const mediaTranscriptionEnabled = process.env.MEMORIX_MCP_MEDIA_TRANSCRIPTION === "1";
|
|
169353
|
+
const mediaGenerationOrTranscription = mediaGenerationEnabled || mediaTranscriptionEnabled;
|
|
169100
169354
|
server.registerTool(
|
|
169101
169355
|
"memorix_media",
|
|
169102
169356
|
{
|
|
169103
169357
|
title: "Manage Controlled Media",
|
|
169104
169358
|
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
169359
|
inputSchema: {
|
|
169106
|
-
action: z626.enum([
|
|
169360
|
+
action: z626.enum([
|
|
169361
|
+
"import",
|
|
169362
|
+
"attach",
|
|
169363
|
+
"list",
|
|
169364
|
+
"show",
|
|
169365
|
+
"derive-pdf",
|
|
169366
|
+
"status",
|
|
169367
|
+
"cancel",
|
|
169368
|
+
...mediaTranscriptionEnabled ? ["derive-audio"] : [],
|
|
169369
|
+
...mediaGenerationEnabled ? ["generate-image", "generate-video"] : []
|
|
169370
|
+
]),
|
|
169107
169371
|
path: z626.string().optional().describe("Explicit local image/audio/video/PDF path for import."),
|
|
169108
169372
|
assetId: z626.string().optional().describe("Controlled MediaAsset ID for attach/show."),
|
|
169109
169373
|
jobId: z626.string().optional().describe("Durable media job ID for status."),
|
|
@@ -169111,20 +169375,26 @@ ${lines.join("\n")}` }] };
|
|
|
169111
169375
|
limit: z626.number().int().min(1).max(100).optional().describe("Maximum assets to list."),
|
|
169112
169376
|
title: z626.string().optional().describe("Observation title when attaching generated/imported output."),
|
|
169113
169377
|
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
169378
|
maxPages: z626.number().int().min(1).max(100).optional().describe("Bounded PDF extraction page limit."),
|
|
169126
169379
|
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.")
|
|
169380
|
+
attach: z626.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly."),
|
|
169381
|
+
...mediaGenerationOrTranscription ? {
|
|
169382
|
+
model: z626.string().max(160).optional().describe("Provider model for the selected media action."),
|
|
169383
|
+
prompt: z626.string().optional().describe("Explicit prompt for generation or transcription.")
|
|
169384
|
+
} : {},
|
|
169385
|
+
...mediaTranscriptionEnabled ? {
|
|
169386
|
+
provider: z626.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
|
|
169387
|
+
language: z626.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription.")
|
|
169388
|
+
} : {},
|
|
169389
|
+
...mediaGenerationEnabled ? {
|
|
169390
|
+
image: z626.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
|
|
169391
|
+
region: z626.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
|
|
169392
|
+
n: z626.number().int().min(1).max(4).optional().describe("Image output count."),
|
|
169393
|
+
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."),
|
|
169394
|
+
width: z626.number().int().min(1).max(8192).optional().describe("Requested image width."),
|
|
169395
|
+
height: z626.number().int().min(1).max(8192).optional().describe("Requested image height."),
|
|
169396
|
+
duration: z626.union([z626.literal(5), z626.literal(10)]).optional().describe("MiniMax video duration in seconds.")
|
|
169397
|
+
} : {}
|
|
169128
169398
|
}
|
|
169129
169399
|
},
|
|
169130
169400
|
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 }) => {
|