memorix 1.4.5 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/CLAUDE.md +7 -3
- package/README.md +5 -2
- package/dist/cli/index.js +1470 -1014
- package/dist/index.js +1279 -989
- package/dist/{maintenance-jobs-B1-W0ssz.d.ts → maintenance-jobs-DVRF-46r.d.ts} +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +1769 -830
- package/dist/media-audio-runner.js +1774 -836
- package/dist/media-video-runner.js +1769 -831
- package/dist/memcode-runtime/CHANGELOG.md +78 -0
- package/dist/sdk.js +1271 -988
- package/dist/types.d.ts +6 -0
- package/dist/vector-backfill-runner.d.ts +1 -1
- package/dist/vector-backfill-runner.js +1765 -827
- package/docs/1.4.5-AGENT-AUTO-MEMORY-RESEARCH.md +95 -0
- package/docs/API_REFERENCE.md +23 -4
- package/docs/CONFIGURATION.md +29 -3
- package/docs/SETUP.md +5 -3
- package/package.json +1 -1
- package/plugins/antigravity/memorix/mcp_config.json +3 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/memorix/.mcp.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.mcp.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.mcp.json +1 -1
- package/plugins/copilot/memorix/.mcp.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +4 -2
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/openclaw/memorix/.mcp.json +3 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/commands/dashboard.ts +14 -3
- package/src/cli/commands/ingest-commit.ts +47 -3
- package/src/cli/commands/memory.ts +17 -4
- package/src/cli/commands/purge.ts +120 -0
- package/src/cli/commands/serve-http.ts +39 -5
- package/src/cli/commands/setup.ts +3 -1
- package/src/cli/index.ts +3 -804
- package/src/codegraph/auto-context.ts +82 -1
- package/src/config/yaml-loader.ts +3 -8
- package/src/config.ts +23 -7
- package/src/evaluation/session-replay.ts +98 -0
- package/src/git/extractor.ts +5 -3
- package/src/hooks/installers/index.ts +15 -0
- package/src/knowledge/workset.ts +58 -0
- package/src/memory/long-term.ts +97 -2
- package/src/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +19 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/project-maintenance.ts +8 -0
- package/src/search/surfaced-registry.ts +62 -0
- package/src/server.ts +74 -14
- package/src/store/orama-store.ts +17 -1
- package/src/types.ts +6 -0
- package/src/cli/commands/audit-list.ts +0 -89
- package/src/cli/commands/purge-all-memory.ts +0 -85
- package/src/cli/commands/purge-project-memory.ts +0 -83
- package/src/cli/commands/uninstall-project-artifacts.ts +0 -86
- package/src/cli/tui/Sidebar.tsx +0 -179
- package/src/cli/tui/interactive-commands.ts +0 -261
- package/src/cli/tui/markdown-render.tsx +0 -371
- package/src/cli/tui/use-mouse.ts +0 -157
- package/src/cli/tui/useNavigation.ts +0 -56
- package/src/orchestrate/context-compact.ts +0 -188
- package/src/team/file-locks.ts +0 -173
- package/src/team/index.ts +0 -23
- package/src/team/messages.ts +0 -203
- package/src/team/persistence.ts +0 -96
- package/src/team/registry.ts +0 -158
- package/src/team/tasks.ts +0 -211
package/dist/cli/index.js
CHANGED
|
@@ -3221,7 +3221,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
|
|
|
3221
3221
|
|
|
3222
3222
|
// src/cli/version.ts
|
|
3223
3223
|
function getCliVersion() {
|
|
3224
|
-
return true ? "1.
|
|
3224
|
+
return true ? "1.5.0" : pkg.version;
|
|
3225
3225
|
}
|
|
3226
3226
|
var init_version = __esm({
|
|
3227
3227
|
"src/cli/version.ts"() {
|
|
@@ -21713,6 +21713,7 @@ var init_resolved_config = __esm({
|
|
|
21713
21713
|
// src/config.ts
|
|
21714
21714
|
var config_exports = {};
|
|
21715
21715
|
__export(config_exports, {
|
|
21716
|
+
defaultEmbeddingModelFor: () => defaultEmbeddingModelFor,
|
|
21716
21717
|
getAgentLLMApiKey: () => getAgentLLMApiKey,
|
|
21717
21718
|
getAgentLLMBaseUrl: () => getAgentLLMBaseUrl,
|
|
21718
21719
|
getAgentLLMModel: () => getAgentLLMModel,
|
|
@@ -21729,7 +21730,6 @@ __export(config_exports, {
|
|
|
21729
21730
|
getLLMProvider: () => getLLMProvider,
|
|
21730
21731
|
getLoadedEnvFiles: () => getLoadedEnvFiles,
|
|
21731
21732
|
getServerConfig: () => getServerConfig,
|
|
21732
|
-
getTeamConfig: () => getTeamConfig,
|
|
21733
21733
|
loadDotenv: () => loadDotenv,
|
|
21734
21734
|
loadFileConfig: () => loadFileConfig,
|
|
21735
21735
|
loadYamlConfig: () => loadYamlConfig,
|
|
@@ -21776,7 +21776,19 @@ function getEmbeddingBaseUrl() {
|
|
|
21776
21776
|
return getResolvedEmbeddingLane().baseUrl || "https://api.openai.com/v1";
|
|
21777
21777
|
}
|
|
21778
21778
|
function getEmbeddingModel() {
|
|
21779
|
-
|
|
21779
|
+
const lane = getResolvedEmbeddingLane();
|
|
21780
|
+
return defaultEmbeddingModelFor(lane.baseUrl, lane.model);
|
|
21781
|
+
}
|
|
21782
|
+
function defaultEmbeddingModelFor(baseUrl, explicitModel) {
|
|
21783
|
+
if (explicitModel) return explicitModel;
|
|
21784
|
+
try {
|
|
21785
|
+
const url2 = baseUrl ? new URL(baseUrl) : null;
|
|
21786
|
+
if (url2 && (url2.hostname === "openrouter.ai" || url2.hostname.endsWith(".openrouter.ai"))) {
|
|
21787
|
+
return "qwen/qwen3-embedding-8b";
|
|
21788
|
+
}
|
|
21789
|
+
} catch {
|
|
21790
|
+
}
|
|
21791
|
+
return "text-embedding-3-small";
|
|
21780
21792
|
}
|
|
21781
21793
|
function getEmbeddingDimensions() {
|
|
21782
21794
|
return getResolvedEmbeddingLane().dimensions ?? null;
|
|
@@ -21801,14 +21813,10 @@ function getServerConfig() {
|
|
|
21801
21813
|
port: server.port
|
|
21802
21814
|
};
|
|
21803
21815
|
}
|
|
21804
|
-
function getTeamConfig() {
|
|
21805
|
-
return loadYamlConfig().team ?? {};
|
|
21806
|
-
}
|
|
21807
21816
|
var init_config = __esm({
|
|
21808
21817
|
"src/config.ts"() {
|
|
21809
21818
|
"use strict";
|
|
21810
21819
|
init_esm_shims();
|
|
21811
|
-
init_yaml_loader();
|
|
21812
21820
|
init_resolved_config();
|
|
21813
21821
|
init_dotenv_loader();
|
|
21814
21822
|
init_legacy_loader();
|
|
@@ -24093,9 +24101,9 @@ async function saveRegistry(baseDir) {
|
|
|
24093
24101
|
await fs6.mkdir(path8.dirname(filePath), { recursive: true });
|
|
24094
24102
|
await fs6.writeFile(filePath, JSON.stringify(registryCache, null, 2), "utf-8");
|
|
24095
24103
|
}
|
|
24096
|
-
function findMatchingGroup(
|
|
24104
|
+
function findMatchingGroup(registry3, projectInfo) {
|
|
24097
24105
|
const normalizedRoot = normalizePath(projectInfo.rootPath);
|
|
24098
|
-
for (const group of
|
|
24106
|
+
for (const group of registry3.groups) {
|
|
24099
24107
|
if (group.aliases.includes(projectInfo.id)) return group;
|
|
24100
24108
|
if (group.rootPaths.some((rp) => rp === normalizedRoot)) return group;
|
|
24101
24109
|
if (projectInfo.gitRemote && group.gitRemote && group.gitRemote === projectInfo.gitRemote) {
|
|
@@ -24108,9 +24116,9 @@ function selectCanonical(aliases) {
|
|
|
24108
24116
|
return [...aliases].sort((a4, b4) => idPriority(b4) - idPriority(a4))[0];
|
|
24109
24117
|
}
|
|
24110
24118
|
async function registerAlias(projectInfo, baseDir) {
|
|
24111
|
-
const
|
|
24119
|
+
const registry3 = await loadRegistry(baseDir);
|
|
24112
24120
|
const normalizedRoot = normalizePath(projectInfo.rootPath);
|
|
24113
|
-
const existingGroup = findMatchingGroup(
|
|
24121
|
+
const existingGroup = findMatchingGroup(registry3, projectInfo);
|
|
24114
24122
|
if (existingGroup) {
|
|
24115
24123
|
let changed = false;
|
|
24116
24124
|
if (!existingGroup.aliases.includes(projectInfo.id)) {
|
|
@@ -24141,13 +24149,13 @@ async function registerAlias(projectInfo, baseDir) {
|
|
|
24141
24149
|
rootPaths: [normalizedRoot],
|
|
24142
24150
|
...projectInfo.gitRemote ? { gitRemote: projectInfo.gitRemote } : {}
|
|
24143
24151
|
};
|
|
24144
|
-
|
|
24152
|
+
registry3.groups.push(newGroup);
|
|
24145
24153
|
await saveRegistry(baseDir);
|
|
24146
24154
|
return newGroup.canonical;
|
|
24147
24155
|
}
|
|
24148
24156
|
async function resolveAliases(projectId, baseDir) {
|
|
24149
|
-
const
|
|
24150
|
-
for (const group of
|
|
24157
|
+
const registry3 = await loadRegistry(baseDir);
|
|
24158
|
+
for (const group of registry3.groups) {
|
|
24151
24159
|
if (group.aliases.includes(projectId) || group.canonical === projectId) {
|
|
24152
24160
|
return [...group.aliases];
|
|
24153
24161
|
}
|
|
@@ -24155,8 +24163,8 @@ async function resolveAliases(projectId, baseDir) {
|
|
|
24155
24163
|
return [projectId];
|
|
24156
24164
|
}
|
|
24157
24165
|
async function getCanonicalId(projectId, baseDir) {
|
|
24158
|
-
const
|
|
24159
|
-
for (const group of
|
|
24166
|
+
const registry3 = await loadRegistry(baseDir);
|
|
24167
|
+
for (const group of registry3.groups) {
|
|
24160
24168
|
if (group.aliases.includes(projectId) || group.canonical === projectId) {
|
|
24161
24169
|
return group.canonical;
|
|
24162
24170
|
}
|
|
@@ -24164,12 +24172,12 @@ async function getCanonicalId(projectId, baseDir) {
|
|
|
24164
24172
|
return projectId;
|
|
24165
24173
|
}
|
|
24166
24174
|
async function getAllAliasGroups(baseDir) {
|
|
24167
|
-
const
|
|
24168
|
-
return
|
|
24175
|
+
const registry3 = await loadRegistry(baseDir);
|
|
24176
|
+
return registry3.groups;
|
|
24169
24177
|
}
|
|
24170
24178
|
async function autoMergeByBaseName(observedIds, baseDir) {
|
|
24171
24179
|
if (observedIds.length <= 1) return 0;
|
|
24172
|
-
const
|
|
24180
|
+
const registry3 = await loadRegistry(baseDir);
|
|
24173
24181
|
const byBaseName = /* @__PURE__ */ new Map();
|
|
24174
24182
|
for (const id of observedIds) {
|
|
24175
24183
|
const baseName = id.split("/").pop() ?? id;
|
|
@@ -24182,7 +24190,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
24182
24190
|
const existingGroups = /* @__PURE__ */ new Set();
|
|
24183
24191
|
const ungroupedIds = [];
|
|
24184
24192
|
for (const id of ids) {
|
|
24185
|
-
const groupIdx =
|
|
24193
|
+
const groupIdx = registry3.groups.findIndex(
|
|
24186
24194
|
(g4) => g4.aliases.includes(id) || g4.canonical === id
|
|
24187
24195
|
);
|
|
24188
24196
|
if (groupIdx >= 0) {
|
|
@@ -24196,7 +24204,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
24196
24204
|
const canonical = selectCanonical(allIdsInGroup);
|
|
24197
24205
|
if (existingGroups.size > 0) {
|
|
24198
24206
|
const primaryIdx = [...existingGroups][0];
|
|
24199
|
-
const primaryGroup =
|
|
24207
|
+
const primaryGroup = registry3.groups[primaryIdx];
|
|
24200
24208
|
for (const id of allIdsInGroup) {
|
|
24201
24209
|
if (!primaryGroup.aliases.includes(id)) {
|
|
24202
24210
|
primaryGroup.aliases.push(id);
|
|
@@ -24204,7 +24212,7 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
24204
24212
|
}
|
|
24205
24213
|
const otherIdxs = [...existingGroups].slice(1).sort((a4, b4) => b4 - a4);
|
|
24206
24214
|
for (const idx of otherIdxs) {
|
|
24207
|
-
const other =
|
|
24215
|
+
const other = registry3.groups[idx];
|
|
24208
24216
|
for (const alias of other.aliases) {
|
|
24209
24217
|
if (!primaryGroup.aliases.includes(alias)) {
|
|
24210
24218
|
primaryGroup.aliases.push(alias);
|
|
@@ -24218,12 +24226,12 @@ async function autoMergeByBaseName(observedIds, baseDir) {
|
|
|
24218
24226
|
if (other.gitRemote && !primaryGroup.gitRemote) {
|
|
24219
24227
|
primaryGroup.gitRemote = other.gitRemote;
|
|
24220
24228
|
}
|
|
24221
|
-
|
|
24229
|
+
registry3.groups.splice(idx, 1);
|
|
24222
24230
|
}
|
|
24223
24231
|
primaryGroup.canonical = selectCanonical(primaryGroup.aliases);
|
|
24224
24232
|
mergeCount++;
|
|
24225
24233
|
} else {
|
|
24226
|
-
|
|
24234
|
+
registry3.groups.push({
|
|
24227
24235
|
canonical,
|
|
24228
24236
|
aliases: allIdsInGroup,
|
|
24229
24237
|
rootPaths: []
|
|
@@ -25105,7 +25113,8 @@ var init_maintenance_jobs = __esm({
|
|
|
25105
25113
|
"claim-requalification",
|
|
25106
25114
|
"knowledge-compile",
|
|
25107
25115
|
"knowledge-lint",
|
|
25108
|
-
"workflow-index"
|
|
25116
|
+
"workflow-index",
|
|
25117
|
+
"long-term-maintenance"
|
|
25109
25118
|
];
|
|
25110
25119
|
DEFAULT_MAX_ATTEMPTS = 8;
|
|
25111
25120
|
DEFAULT_LEASE_MS = 3e4;
|
|
@@ -25514,6 +25523,7 @@ __export(lifecycle_exports, {
|
|
|
25514
25523
|
enqueueClaimRequalification: () => enqueueClaimRequalification,
|
|
25515
25524
|
enqueueCodegraphRefresh: () => enqueueCodegraphRefresh,
|
|
25516
25525
|
enqueueKnowledgeFollowups: () => enqueueKnowledgeFollowups,
|
|
25526
|
+
enqueueLongTermMaintenance: () => enqueueLongTermMaintenance,
|
|
25517
25527
|
enqueueObservationQualification: () => enqueueObservationQualification
|
|
25518
25528
|
});
|
|
25519
25529
|
function queueFor(input) {
|
|
@@ -25557,6 +25567,14 @@ function enqueueObservationQualification(input) {
|
|
|
25557
25567
|
payload: { source: input.source, limit: 100 }
|
|
25558
25568
|
});
|
|
25559
25569
|
}
|
|
25570
|
+
function enqueueLongTermMaintenance(input) {
|
|
25571
|
+
queueFor(input).enqueue({
|
|
25572
|
+
projectId: input.projectId,
|
|
25573
|
+
kind: "long-term-maintenance",
|
|
25574
|
+
dedupeKey: "long-term-maintenance",
|
|
25575
|
+
payload: { source: input.source }
|
|
25576
|
+
});
|
|
25577
|
+
}
|
|
25560
25578
|
function enqueueKnowledgeFollowups(input) {
|
|
25561
25579
|
const queue2 = queueFor(input);
|
|
25562
25580
|
const payload = {
|
|
@@ -25736,7 +25754,8 @@ var init_isolated_maintenance = __esm({
|
|
|
25736
25754
|
"claim-requalification",
|
|
25737
25755
|
"knowledge-compile",
|
|
25738
25756
|
"knowledge-lint",
|
|
25739
|
-
"workflow-index"
|
|
25757
|
+
"workflow-index",
|
|
25758
|
+
"long-term-maintenance"
|
|
25740
25759
|
];
|
|
25741
25760
|
DEFAULT_TIMEOUT_MS = 5 * 6e4;
|
|
25742
25761
|
MAX_CHILD_OUTPUT_BYTES = 1e6;
|
|
@@ -177446,6 +177465,893 @@ var init_retention = __esm({
|
|
|
177446
177465
|
}
|
|
177447
177466
|
});
|
|
177448
177467
|
|
|
177468
|
+
// src/memory/owner.ts
|
|
177469
|
+
var owner_exports = {};
|
|
177470
|
+
__export(owner_exports, {
|
|
177471
|
+
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
177472
|
+
});
|
|
177473
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
177474
|
+
import { promises as fs11 } from "fs";
|
|
177475
|
+
import path18 from "path";
|
|
177476
|
+
function ownerPath(dataDir) {
|
|
177477
|
+
const resolved = path18.resolve(dataDir);
|
|
177478
|
+
const home = path18.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path18.dirname(resolved) : resolved;
|
|
177479
|
+
return path18.join(home, "memorix-user.json");
|
|
177480
|
+
}
|
|
177481
|
+
function normalizedOwnerId(value) {
|
|
177482
|
+
const trimmed = value?.trim();
|
|
177483
|
+
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
177484
|
+
}
|
|
177485
|
+
async function readOwnerFile(filePath) {
|
|
177486
|
+
try {
|
|
177487
|
+
const value = JSON.parse(await fs11.readFile(filePath, "utf8"));
|
|
177488
|
+
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
177489
|
+
return id ? { id, source: "local-file" } : void 0;
|
|
177490
|
+
} catch {
|
|
177491
|
+
return void 0;
|
|
177492
|
+
}
|
|
177493
|
+
}
|
|
177494
|
+
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
177495
|
+
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
177496
|
+
if (configured) return { id: configured, source: "environment" };
|
|
177497
|
+
const filePath = ownerPath(dataDir);
|
|
177498
|
+
const existing = await readOwnerFile(filePath);
|
|
177499
|
+
if (existing || !options.create) return existing;
|
|
177500
|
+
await fs11.mkdir(path18.dirname(filePath), { recursive: true });
|
|
177501
|
+
const created = {
|
|
177502
|
+
version: 1,
|
|
177503
|
+
id: "local-" + randomUUID6(),
|
|
177504
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
177505
|
+
};
|
|
177506
|
+
try {
|
|
177507
|
+
await fs11.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
177508
|
+
return { id: created.id, source: "local-file" };
|
|
177509
|
+
} catch (error40) {
|
|
177510
|
+
if (error40?.code !== "EEXIST") throw error40;
|
|
177511
|
+
const concurrent = await readOwnerFile(filePath);
|
|
177512
|
+
if (concurrent) return concurrent;
|
|
177513
|
+
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
177514
|
+
}
|
|
177515
|
+
}
|
|
177516
|
+
var ID_PATTERN;
|
|
177517
|
+
var init_owner = __esm({
|
|
177518
|
+
"src/memory/owner.ts"() {
|
|
177519
|
+
"use strict";
|
|
177520
|
+
init_esm_shims();
|
|
177521
|
+
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
177522
|
+
}
|
|
177523
|
+
});
|
|
177524
|
+
|
|
177525
|
+
// src/memory/long-term-store.ts
|
|
177526
|
+
var long_term_store_exports = {};
|
|
177527
|
+
__export(long_term_store_exports, {
|
|
177528
|
+
LongTermMemoryStore: () => LongTermMemoryStore
|
|
177529
|
+
});
|
|
177530
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
177531
|
+
function optionalText2(value) {
|
|
177532
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
177533
|
+
}
|
|
177534
|
+
function arrayValue2(value) {
|
|
177535
|
+
if (typeof value !== "string" || !value) return [];
|
|
177536
|
+
try {
|
|
177537
|
+
const parsed = JSON.parse(value);
|
|
177538
|
+
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
177539
|
+
} catch {
|
|
177540
|
+
return [];
|
|
177541
|
+
}
|
|
177542
|
+
}
|
|
177543
|
+
function rowToMemory(row) {
|
|
177544
|
+
return {
|
|
177545
|
+
id: row.id,
|
|
177546
|
+
originProjectId: row.originProjectId,
|
|
177547
|
+
ownerId: row.ownerId,
|
|
177548
|
+
scope: row.scope,
|
|
177549
|
+
kind: row.kind,
|
|
177550
|
+
state: row.state,
|
|
177551
|
+
portability: row.portability,
|
|
177552
|
+
title: row.title,
|
|
177553
|
+
content: row.content,
|
|
177554
|
+
facts: arrayValue2(row.factsJson),
|
|
177555
|
+
tags: arrayValue2(row.tagsJson),
|
|
177556
|
+
...optionalText2(row.applicability) ? { applicability: row.applicability } : {},
|
|
177557
|
+
origin: row.origin,
|
|
177558
|
+
createdAt: row.createdAt,
|
|
177559
|
+
updatedAt: row.updatedAt,
|
|
177560
|
+
...optionalText2(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
177561
|
+
...optionalText2(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
177562
|
+
...optionalText2(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
177563
|
+
...optionalText2(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
177564
|
+
...optionalText2(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
177565
|
+
accessCount: Number(row.accessCount ?? 0),
|
|
177566
|
+
...optionalText2(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
177567
|
+
};
|
|
177568
|
+
}
|
|
177569
|
+
function rowToEvidence(row) {
|
|
177570
|
+
return {
|
|
177571
|
+
id: row.id,
|
|
177572
|
+
memoryId: row.memoryId,
|
|
177573
|
+
kind: row.kind,
|
|
177574
|
+
referenceId: row.referenceId,
|
|
177575
|
+
relation: row.relation,
|
|
177576
|
+
...optionalText2(row.locator) ? { locator: row.locator } : {},
|
|
177577
|
+
...optionalText2(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
177578
|
+
createdAt: row.createdAt
|
|
177579
|
+
};
|
|
177580
|
+
}
|
|
177581
|
+
function rowToEvent(row) {
|
|
177582
|
+
return {
|
|
177583
|
+
id: row.id,
|
|
177584
|
+
memoryId: row.memoryId,
|
|
177585
|
+
kind: row.kind,
|
|
177586
|
+
...optionalText2(row.fromState) ? { fromState: row.fromState } : {},
|
|
177587
|
+
...optionalText2(row.toState) ? { toState: row.toState } : {},
|
|
177588
|
+
...optionalText2(row.detail) ? { detail: row.detail } : {},
|
|
177589
|
+
createdAt: row.createdAt
|
|
177590
|
+
};
|
|
177591
|
+
}
|
|
177592
|
+
var LongTermMemoryStore;
|
|
177593
|
+
var init_long_term_store = __esm({
|
|
177594
|
+
"src/memory/long-term-store.ts"() {
|
|
177595
|
+
"use strict";
|
|
177596
|
+
init_esm_shims();
|
|
177597
|
+
init_sqlite_db();
|
|
177598
|
+
LongTermMemoryStore = class {
|
|
177599
|
+
db = null;
|
|
177600
|
+
async init(dataDir) {
|
|
177601
|
+
this.db = getDatabase(dataDir);
|
|
177602
|
+
}
|
|
177603
|
+
transaction(fn2) {
|
|
177604
|
+
return this.db.transaction(fn2)();
|
|
177605
|
+
}
|
|
177606
|
+
get(id) {
|
|
177607
|
+
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
177608
|
+
return row ? rowToMemory(row) : void 0;
|
|
177609
|
+
}
|
|
177610
|
+
list(options = {}) {
|
|
177611
|
+
const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
177612
|
+
const clauses = [];
|
|
177613
|
+
const params = [];
|
|
177614
|
+
if (options.ownerId) {
|
|
177615
|
+
clauses.push("ownerId = ?");
|
|
177616
|
+
params.push(options.ownerId);
|
|
177617
|
+
}
|
|
177618
|
+
if (options.originProjectId) {
|
|
177619
|
+
clauses.push("originProjectId = ?");
|
|
177620
|
+
params.push(options.originProjectId);
|
|
177621
|
+
}
|
|
177622
|
+
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
177623
|
+
const rows = this.db.prepare(
|
|
177624
|
+
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
177625
|
+
).all(...params, limit2);
|
|
177626
|
+
return rows.map(rowToMemory);
|
|
177627
|
+
}
|
|
177628
|
+
insert(memory) {
|
|
177629
|
+
this.db.prepare(`
|
|
177630
|
+
INSERT INTO long_term_memories (
|
|
177631
|
+
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
177632
|
+
title, content, factsJson, tagsJson, applicability, origin,
|
|
177633
|
+
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
177634
|
+
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
177635
|
+
) VALUES (
|
|
177636
|
+
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
177637
|
+
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
177638
|
+
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
177639
|
+
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
177640
|
+
)
|
|
177641
|
+
`).run({
|
|
177642
|
+
...memory,
|
|
177643
|
+
factsJson: JSON.stringify(memory.facts),
|
|
177644
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
177645
|
+
applicability: memory.applicability ?? null,
|
|
177646
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
177647
|
+
approvedAt: memory.approvedAt ?? null,
|
|
177648
|
+
archivedAt: memory.archivedAt ?? null,
|
|
177649
|
+
supersededBy: memory.supersededBy ?? null,
|
|
177650
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
177651
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
177652
|
+
});
|
|
177653
|
+
}
|
|
177654
|
+
update(memory) {
|
|
177655
|
+
this.db.prepare(`
|
|
177656
|
+
UPDATE long_term_memories SET
|
|
177657
|
+
scope = @scope,
|
|
177658
|
+
kind = @kind,
|
|
177659
|
+
state = @state,
|
|
177660
|
+
portability = @portability,
|
|
177661
|
+
title = @title,
|
|
177662
|
+
content = @content,
|
|
177663
|
+
factsJson = @factsJson,
|
|
177664
|
+
tagsJson = @tagsJson,
|
|
177665
|
+
applicability = @applicability,
|
|
177666
|
+
updatedAt = @updatedAt,
|
|
177667
|
+
qualifiedAt = @qualifiedAt,
|
|
177668
|
+
approvedAt = @approvedAt,
|
|
177669
|
+
archivedAt = @archivedAt,
|
|
177670
|
+
supersededBy = @supersededBy,
|
|
177671
|
+
lastValidatedAt = @lastValidatedAt,
|
|
177672
|
+
accessCount = @accessCount,
|
|
177673
|
+
lastAccessedAt = @lastAccessedAt
|
|
177674
|
+
WHERE id = @id
|
|
177675
|
+
`).run({
|
|
177676
|
+
...memory,
|
|
177677
|
+
factsJson: JSON.stringify(memory.facts),
|
|
177678
|
+
tagsJson: JSON.stringify(memory.tags),
|
|
177679
|
+
applicability: memory.applicability ?? null,
|
|
177680
|
+
qualifiedAt: memory.qualifiedAt ?? null,
|
|
177681
|
+
approvedAt: memory.approvedAt ?? null,
|
|
177682
|
+
archivedAt: memory.archivedAt ?? null,
|
|
177683
|
+
supersededBy: memory.supersededBy ?? null,
|
|
177684
|
+
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
177685
|
+
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
177686
|
+
});
|
|
177687
|
+
}
|
|
177688
|
+
insertEvidence(input) {
|
|
177689
|
+
const evidence = {
|
|
177690
|
+
id: randomUUID7(),
|
|
177691
|
+
...input,
|
|
177692
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
177693
|
+
};
|
|
177694
|
+
this.db.prepare(`
|
|
177695
|
+
INSERT INTO long_term_memory_evidence (
|
|
177696
|
+
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
177697
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
177698
|
+
`).run(
|
|
177699
|
+
evidence.id,
|
|
177700
|
+
evidence.memoryId,
|
|
177701
|
+
evidence.kind,
|
|
177702
|
+
evidence.referenceId,
|
|
177703
|
+
evidence.relation,
|
|
177704
|
+
evidence.locator ?? null,
|
|
177705
|
+
evidence.capturedHash ?? null,
|
|
177706
|
+
evidence.createdAt
|
|
177707
|
+
);
|
|
177708
|
+
return evidence;
|
|
177709
|
+
}
|
|
177710
|
+
listEvidence(memoryId) {
|
|
177711
|
+
return this.db.prepare(`
|
|
177712
|
+
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
177713
|
+
`).all(memoryId).map(rowToEvidence);
|
|
177714
|
+
}
|
|
177715
|
+
recordEvent(input) {
|
|
177716
|
+
const event = {
|
|
177717
|
+
id: randomUUID7(),
|
|
177718
|
+
...input,
|
|
177719
|
+
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
177720
|
+
};
|
|
177721
|
+
this.db.prepare(`
|
|
177722
|
+
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
177723
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
177724
|
+
`).run(
|
|
177725
|
+
event.id,
|
|
177726
|
+
event.memoryId,
|
|
177727
|
+
event.kind,
|
|
177728
|
+
event.fromState ?? null,
|
|
177729
|
+
event.toState ?? null,
|
|
177730
|
+
event.detail ?? null,
|
|
177731
|
+
event.createdAt
|
|
177732
|
+
);
|
|
177733
|
+
return event;
|
|
177734
|
+
}
|
|
177735
|
+
listEvents(memoryId) {
|
|
177736
|
+
return this.db.prepare(`
|
|
177737
|
+
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
177738
|
+
`).all(memoryId).map(rowToEvent);
|
|
177739
|
+
}
|
|
177740
|
+
markAccess(ids, at2 = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
177741
|
+
const unique3 = [...new Set(ids.filter(Boolean))];
|
|
177742
|
+
if (unique3.length === 0) return;
|
|
177743
|
+
const statement = this.db.prepare(`
|
|
177744
|
+
UPDATE long_term_memories
|
|
177745
|
+
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
177746
|
+
WHERE id = ?
|
|
177747
|
+
`);
|
|
177748
|
+
this.db.transaction(() => {
|
|
177749
|
+
for (const id of unique3) statement.run(at2, id);
|
|
177750
|
+
})();
|
|
177751
|
+
}
|
|
177752
|
+
};
|
|
177753
|
+
}
|
|
177754
|
+
});
|
|
177755
|
+
|
|
177756
|
+
// src/memory/long-term-types.ts
|
|
177757
|
+
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
177758
|
+
var init_long_term_types = __esm({
|
|
177759
|
+
"src/memory/long-term-types.ts"() {
|
|
177760
|
+
"use strict";
|
|
177761
|
+
init_esm_shims();
|
|
177762
|
+
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
177763
|
+
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
177764
|
+
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
177765
|
+
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
177766
|
+
"observation",
|
|
177767
|
+
"claim",
|
|
177768
|
+
"workflow",
|
|
177769
|
+
"session",
|
|
177770
|
+
"checkpoint",
|
|
177771
|
+
"git",
|
|
177772
|
+
"code",
|
|
177773
|
+
"test",
|
|
177774
|
+
"run",
|
|
177775
|
+
"document",
|
|
177776
|
+
"manual",
|
|
177777
|
+
"user"
|
|
177778
|
+
];
|
|
177779
|
+
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
177780
|
+
"supports",
|
|
177781
|
+
"verifies",
|
|
177782
|
+
"derives",
|
|
177783
|
+
"user-confirmed"
|
|
177784
|
+
];
|
|
177785
|
+
}
|
|
177786
|
+
});
|
|
177787
|
+
|
|
177788
|
+
// src/memory/long-term.ts
|
|
177789
|
+
var long_term_exports = {};
|
|
177790
|
+
__export(long_term_exports, {
|
|
177791
|
+
approveLongTermMemory: () => approveLongTermMemory,
|
|
177792
|
+
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
177793
|
+
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
177794
|
+
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
177795
|
+
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
177796
|
+
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
177797
|
+
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
177798
|
+
listLongTermMemories: () => listLongTermMemories,
|
|
177799
|
+
maintainLongTermMemories: () => maintainLongTermMemories,
|
|
177800
|
+
maybeAutoQualifyLongTermMemory: () => maybeAutoQualifyLongTermMemory,
|
|
177801
|
+
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
177802
|
+
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
177803
|
+
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
177804
|
+
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
177805
|
+
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
177806
|
+
});
|
|
177807
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
177808
|
+
function isOneOf(value, values) {
|
|
177809
|
+
return typeof value === "string" && values.includes(value);
|
|
177810
|
+
}
|
|
177811
|
+
function compactText(value, field, max = MAX_TEXT_LENGTH) {
|
|
177812
|
+
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
177813
|
+
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
177814
|
+
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
177815
|
+
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
177816
|
+
return safe;
|
|
177817
|
+
}
|
|
177818
|
+
function compactList(values, field) {
|
|
177819
|
+
if (!values) return [];
|
|
177820
|
+
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
177821
|
+
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
177822
|
+
}
|
|
177823
|
+
function normalizeEvidence(input) {
|
|
177824
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
177825
|
+
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
177826
|
+
}
|
|
177827
|
+
return input.map((evidence) => {
|
|
177828
|
+
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
177829
|
+
throw new Error("Long-term memory evidence kind is invalid.");
|
|
177830
|
+
}
|
|
177831
|
+
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
177832
|
+
throw new Error("Long-term memory evidence relation is invalid.");
|
|
177833
|
+
}
|
|
177834
|
+
return {
|
|
177835
|
+
kind: evidence.kind,
|
|
177836
|
+
referenceId: compactText(evidence.referenceId, "evidence reference", 1e3),
|
|
177837
|
+
relation: evidence.relation,
|
|
177838
|
+
...evidence.locator ? { locator: compactText(evidence.locator, "evidence locator", 1e3) } : {},
|
|
177839
|
+
...evidence.capturedHash ? { capturedHash: compactText(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
177840
|
+
};
|
|
177841
|
+
});
|
|
177842
|
+
}
|
|
177843
|
+
function assertScope(scope) {
|
|
177844
|
+
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
177845
|
+
}
|
|
177846
|
+
function assertKind(kind) {
|
|
177847
|
+
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
177848
|
+
}
|
|
177849
|
+
function assertPortability(value) {
|
|
177850
|
+
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
177851
|
+
throw new Error("Long-term memory portability is invalid.");
|
|
177852
|
+
}
|
|
177853
|
+
}
|
|
177854
|
+
function assertPortableEvidence(scope, portability, evidence) {
|
|
177855
|
+
if (portability !== "portable") return;
|
|
177856
|
+
if (scope !== "user") {
|
|
177857
|
+
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
177858
|
+
}
|
|
177859
|
+
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
177860
|
+
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.");
|
|
177861
|
+
}
|
|
177862
|
+
}
|
|
177863
|
+
function requireTeamReader(scope, reader) {
|
|
177864
|
+
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
177865
|
+
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
177866
|
+
}
|
|
177867
|
+
}
|
|
177868
|
+
function matchesScope(memory, reader) {
|
|
177869
|
+
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
177870
|
+
if (memory.scope === "team") {
|
|
177871
|
+
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
177872
|
+
}
|
|
177873
|
+
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
177874
|
+
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
177875
|
+
}
|
|
177876
|
+
function canReadLongTermMemory(memory, reader) {
|
|
177877
|
+
return matchesScope(memory, reader);
|
|
177878
|
+
}
|
|
177879
|
+
function isEligibleLongTermMemory(memory) {
|
|
177880
|
+
return memory.state === "qualified" || memory.state === "approved";
|
|
177881
|
+
}
|
|
177882
|
+
function taskTerms(task) {
|
|
177883
|
+
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
177884
|
+
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
177885
|
+
}
|
|
177886
|
+
function kindFitScore(kind, task) {
|
|
177887
|
+
const normalized = task.toLocaleLowerCase("en-US");
|
|
177888
|
+
const hints = {
|
|
177889
|
+
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
177890
|
+
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
177891
|
+
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
177892
|
+
};
|
|
177893
|
+
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
177894
|
+
}
|
|
177895
|
+
function validationFreshnessScore(memory) {
|
|
177896
|
+
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
177897
|
+
if (!validatedAt) return 0;
|
|
177898
|
+
const time3 = Date.parse(validatedAt);
|
|
177899
|
+
if (!Number.isFinite(time3)) return 0;
|
|
177900
|
+
const ageDays = Math.max(0, Date.now() - time3) / (24 * 60 * 60 * 1e3);
|
|
177901
|
+
if (ageDays <= 30) return 0.35;
|
|
177902
|
+
if (ageDays <= 90) return 0.15;
|
|
177903
|
+
return 0;
|
|
177904
|
+
}
|
|
177905
|
+
function scoreForTask(memory, task) {
|
|
177906
|
+
const terms = taskTerms(task);
|
|
177907
|
+
if (terms.length === 0) return void 0;
|
|
177908
|
+
const title = memory.title.toLocaleLowerCase("en-US");
|
|
177909
|
+
const content = memory.content.toLocaleLowerCase("en-US");
|
|
177910
|
+
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
177911
|
+
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
177912
|
+
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
177913
|
+
let score = memory.state === "approved" ? 0.5 : 0;
|
|
177914
|
+
const matched = [];
|
|
177915
|
+
for (const term of terms) {
|
|
177916
|
+
let termScore = 0;
|
|
177917
|
+
if (title.includes(term)) termScore += 5;
|
|
177918
|
+
if (tags.includes(term)) termScore += 4;
|
|
177919
|
+
if (applicability.includes(term)) termScore += 3;
|
|
177920
|
+
if (facts.includes(term)) termScore += 2;
|
|
177921
|
+
if (content.includes(term)) termScore += 1;
|
|
177922
|
+
if (termScore > 0) {
|
|
177923
|
+
score += termScore;
|
|
177924
|
+
matched.push(term);
|
|
177925
|
+
}
|
|
177926
|
+
}
|
|
177927
|
+
if (matched.length === 0) return void 0;
|
|
177928
|
+
score += kindFitScore(memory.kind, task);
|
|
177929
|
+
score += validationFreshnessScore(memory);
|
|
177930
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
177931
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
177932
|
+
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
177933
|
+
}
|
|
177934
|
+
function semanticText(memory) {
|
|
177935
|
+
return sanitizeCredentials([
|
|
177936
|
+
memory.title,
|
|
177937
|
+
memory.applicability,
|
|
177938
|
+
memory.tags.join(" "),
|
|
177939
|
+
memory.facts.join(" "),
|
|
177940
|
+
memory.content
|
|
177941
|
+
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
177942
|
+
}
|
|
177943
|
+
function cosineSimilarity(left, right) {
|
|
177944
|
+
if (left.length === 0 || left.length !== right.length) return 0;
|
|
177945
|
+
let dot = 0;
|
|
177946
|
+
let leftMagnitude = 0;
|
|
177947
|
+
let rightMagnitude = 0;
|
|
177948
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
177949
|
+
const a4 = left[index];
|
|
177950
|
+
const b4 = right[index];
|
|
177951
|
+
if (!Number.isFinite(a4) || !Number.isFinite(b4)) return 0;
|
|
177952
|
+
dot += a4 * b4;
|
|
177953
|
+
leftMagnitude += a4 * a4;
|
|
177954
|
+
rightMagnitude += b4 * b4;
|
|
177955
|
+
}
|
|
177956
|
+
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
177957
|
+
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
177958
|
+
}
|
|
177959
|
+
function semanticSelection(memory, similarity, task) {
|
|
177960
|
+
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
177961
|
+
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
177962
|
+
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
177963
|
+
return {
|
|
177964
|
+
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
177965
|
+
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
177966
|
+
};
|
|
177967
|
+
}
|
|
177968
|
+
async function resolveSemanticProvider(explicit) {
|
|
177969
|
+
if (explicit !== void 0) return explicit;
|
|
177970
|
+
try {
|
|
177971
|
+
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
177972
|
+
return await withTimeout(
|
|
177973
|
+
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
177974
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
177975
|
+
"Long-term semantic provider initialization"
|
|
177976
|
+
);
|
|
177977
|
+
} catch {
|
|
177978
|
+
return null;
|
|
177979
|
+
}
|
|
177980
|
+
}
|
|
177981
|
+
function createStore(dataDir) {
|
|
177982
|
+
const store2 = new LongTermMemoryStore();
|
|
177983
|
+
return store2.init(dataDir).then(() => store2);
|
|
177984
|
+
}
|
|
177985
|
+
async function createManualLongTermMemory(input) {
|
|
177986
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
177987
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
177988
|
+
assertScope(input.scope);
|
|
177989
|
+
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID8(), relation: "supports" }];
|
|
177990
|
+
return createLongTermMemoryWithDataDir({
|
|
177991
|
+
dataDir: input.dataDir,
|
|
177992
|
+
originProjectId: input.projectId,
|
|
177993
|
+
ownerId: owner.id,
|
|
177994
|
+
scope: input.scope,
|
|
177995
|
+
kind: input.kind,
|
|
177996
|
+
portability: input.portability,
|
|
177997
|
+
title: input.title,
|
|
177998
|
+
content: input.content,
|
|
177999
|
+
facts: input.facts,
|
|
178000
|
+
tags: input.tags,
|
|
178001
|
+
applicability: input.applicability,
|
|
178002
|
+
origin: "manual",
|
|
178003
|
+
evidence,
|
|
178004
|
+
reader: input.reader
|
|
178005
|
+
});
|
|
178006
|
+
}
|
|
178007
|
+
async function promoteObservationToLongTermMemory(input) {
|
|
178008
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
178009
|
+
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
178010
|
+
return createLongTermMemoryWithDataDir({
|
|
178011
|
+
dataDir: input.dataDir,
|
|
178012
|
+
originProjectId: input.observation.projectId,
|
|
178013
|
+
ownerId: owner.id,
|
|
178014
|
+
scope: input.scope,
|
|
178015
|
+
kind: input.kind,
|
|
178016
|
+
portability: "project-bound",
|
|
178017
|
+
title: input.observation.title,
|
|
178018
|
+
content: input.observation.narrative,
|
|
178019
|
+
facts: input.observation.facts,
|
|
178020
|
+
tags: input.tags ?? input.observation.concepts,
|
|
178021
|
+
applicability: input.applicability,
|
|
178022
|
+
origin: input.observation.source === "git" ? "git" : "agent",
|
|
178023
|
+
evidence: [{
|
|
178024
|
+
kind: "observation",
|
|
178025
|
+
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
178026
|
+
relation: "derives"
|
|
178027
|
+
}],
|
|
178028
|
+
reader: input.reader
|
|
178029
|
+
});
|
|
178030
|
+
}
|
|
178031
|
+
async function createLongTermMemoryCandidate(input) {
|
|
178032
|
+
return createLongTermMemoryWithDataDir(input);
|
|
178033
|
+
}
|
|
178034
|
+
async function createLongTermMemoryWithDataDir(input) {
|
|
178035
|
+
assertScope(input.scope);
|
|
178036
|
+
assertKind(input.kind);
|
|
178037
|
+
const portability = input.portability ?? "project-bound";
|
|
178038
|
+
assertPortability(portability);
|
|
178039
|
+
requireTeamReader(input.scope, input.reader);
|
|
178040
|
+
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
178041
|
+
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
178042
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
178043
|
+
const memory = {
|
|
178044
|
+
id: randomUUID8(),
|
|
178045
|
+
originProjectId: compactText(input.originProjectId, "origin project id", 1e3),
|
|
178046
|
+
ownerId: compactText(input.ownerId, "owner id", 160),
|
|
178047
|
+
scope: input.scope,
|
|
178048
|
+
kind: input.kind,
|
|
178049
|
+
state: "candidate",
|
|
178050
|
+
portability,
|
|
178051
|
+
title: compactText(input.title, "title", 500),
|
|
178052
|
+
content: compactText(input.content, "content"),
|
|
178053
|
+
facts: compactList(input.facts, "facts"),
|
|
178054
|
+
tags: compactList(input.tags, "tags"),
|
|
178055
|
+
...input.applicability ? { applicability: compactText(input.applicability, "applicability", 1e3) } : {},
|
|
178056
|
+
origin: input.origin ?? "manual",
|
|
178057
|
+
createdAt: now3,
|
|
178058
|
+
updatedAt: now3,
|
|
178059
|
+
accessCount: 0
|
|
178060
|
+
};
|
|
178061
|
+
const store2 = await createStore(input.dataDir);
|
|
178062
|
+
const evidence = store2.transaction(() => {
|
|
178063
|
+
store2.insert(memory);
|
|
178064
|
+
const written = evidenceInputs.map((item) => store2.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
178065
|
+
store2.recordEvent({
|
|
178066
|
+
memoryId: memory.id,
|
|
178067
|
+
kind: "created",
|
|
178068
|
+
toState: "candidate",
|
|
178069
|
+
detail: "Created as a source-backed long-term memory candidate.",
|
|
178070
|
+
createdAt: now3
|
|
178071
|
+
});
|
|
178072
|
+
return written;
|
|
178073
|
+
});
|
|
178074
|
+
return { memory, evidence };
|
|
178075
|
+
}
|
|
178076
|
+
function transitionDetail(value) {
|
|
178077
|
+
return compactText(value, "transition reason", 1e3);
|
|
178078
|
+
}
|
|
178079
|
+
async function transitionMemory(input) {
|
|
178080
|
+
const store2 = await createStore(input.dataDir);
|
|
178081
|
+
return store2.transaction(() => {
|
|
178082
|
+
const current = store2.get(input.id);
|
|
178083
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
178084
|
+
if (current.state !== input.expected) {
|
|
178085
|
+
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
178086
|
+
}
|
|
178087
|
+
const evidence = store2.listEvidence(current.id);
|
|
178088
|
+
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
178089
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
178090
|
+
const next = {
|
|
178091
|
+
...current,
|
|
178092
|
+
state: input.next,
|
|
178093
|
+
updatedAt: now3,
|
|
178094
|
+
lastValidatedAt: now3,
|
|
178095
|
+
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
178096
|
+
};
|
|
178097
|
+
store2.update(next);
|
|
178098
|
+
store2.recordEvent({
|
|
178099
|
+
memoryId: next.id,
|
|
178100
|
+
kind: input.event,
|
|
178101
|
+
fromState: current.state,
|
|
178102
|
+
toState: next.state,
|
|
178103
|
+
detail: transitionDetail(input.reason),
|
|
178104
|
+
createdAt: now3
|
|
178105
|
+
});
|
|
178106
|
+
return next;
|
|
178107
|
+
});
|
|
178108
|
+
}
|
|
178109
|
+
async function qualifyLongTermMemory(input) {
|
|
178110
|
+
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
178111
|
+
}
|
|
178112
|
+
async function approveLongTermMemory(input) {
|
|
178113
|
+
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
178114
|
+
}
|
|
178115
|
+
async function archiveLongTermMemory(input) {
|
|
178116
|
+
const store2 = await createStore(input.dataDir);
|
|
178117
|
+
return store2.transaction(() => {
|
|
178118
|
+
const current = store2.get(input.id);
|
|
178119
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
178120
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
178121
|
+
throw new Error("Long-term memory is already inactive.");
|
|
178122
|
+
}
|
|
178123
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
178124
|
+
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
178125
|
+
store2.update(next);
|
|
178126
|
+
store2.recordEvent({
|
|
178127
|
+
memoryId: next.id,
|
|
178128
|
+
kind: "archived",
|
|
178129
|
+
fromState: current.state,
|
|
178130
|
+
toState: "archived",
|
|
178131
|
+
detail: transitionDetail(input.reason),
|
|
178132
|
+
createdAt: now3
|
|
178133
|
+
});
|
|
178134
|
+
return next;
|
|
178135
|
+
});
|
|
178136
|
+
}
|
|
178137
|
+
async function supersedeLongTermMemory(input) {
|
|
178138
|
+
const store2 = await createStore(input.dataDir);
|
|
178139
|
+
return store2.transaction(() => {
|
|
178140
|
+
const current = store2.get(input.id);
|
|
178141
|
+
if (!current) throw new Error("Long-term memory was not found.");
|
|
178142
|
+
if (current.state === "archived" || current.state === "superseded") {
|
|
178143
|
+
throw new Error("Long-term memory is already inactive.");
|
|
178144
|
+
}
|
|
178145
|
+
if (current.id === input.supersededBy) {
|
|
178146
|
+
throw new Error("Long-term memory cannot supersede itself.");
|
|
178147
|
+
}
|
|
178148
|
+
const replacement = store2.get(input.supersededBy);
|
|
178149
|
+
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
178150
|
+
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
178151
|
+
}
|
|
178152
|
+
if (replacement.scope !== current.scope) {
|
|
178153
|
+
throw new Error("Replacement long-term memory must use the same scope.");
|
|
178154
|
+
}
|
|
178155
|
+
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
178156
|
+
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
178157
|
+
}
|
|
178158
|
+
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
178159
|
+
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
178160
|
+
}
|
|
178161
|
+
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
178162
|
+
const next = {
|
|
178163
|
+
...current,
|
|
178164
|
+
state: "superseded",
|
|
178165
|
+
supersededBy: replacement.id,
|
|
178166
|
+
updatedAt: now3
|
|
178167
|
+
};
|
|
178168
|
+
store2.update(next);
|
|
178169
|
+
store2.recordEvent({
|
|
178170
|
+
memoryId: next.id,
|
|
178171
|
+
kind: "superseded",
|
|
178172
|
+
fromState: current.state,
|
|
178173
|
+
toState: "superseded",
|
|
178174
|
+
detail: transitionDetail(input.reason),
|
|
178175
|
+
createdAt: now3
|
|
178176
|
+
});
|
|
178177
|
+
return next;
|
|
178178
|
+
});
|
|
178179
|
+
}
|
|
178180
|
+
async function maybeAutoQualifyLongTermMemory(input) {
|
|
178181
|
+
if (input.source !== "manual" && input.sourceDetail !== "explicit") return null;
|
|
178182
|
+
try {
|
|
178183
|
+
return await qualifyLongTermMemory({
|
|
178184
|
+
dataDir: input.dataDir,
|
|
178185
|
+
id: input.id,
|
|
178186
|
+
reason: "auto-qualified: explicit request carries its own source evidence"
|
|
178187
|
+
});
|
|
178188
|
+
} catch {
|
|
178189
|
+
return null;
|
|
178190
|
+
}
|
|
178191
|
+
}
|
|
178192
|
+
async function maintainLongTermMemories(input) {
|
|
178193
|
+
const now3 = input.now ?? /* @__PURE__ */ new Date();
|
|
178194
|
+
const decayDays = input.decayDays ?? 30;
|
|
178195
|
+
const cutoff = now3.getTime() - decayDays * 24 * 60 * 60 * 1e3;
|
|
178196
|
+
const result = { archived: 0, superseded: 0 };
|
|
178197
|
+
const store2 = await createStore(input.dataDir);
|
|
178198
|
+
const all = store2.list({ originProjectId: input.projectId });
|
|
178199
|
+
for (const memory of all) {
|
|
178200
|
+
if (memory.state !== "qualified" && memory.state !== "approved") continue;
|
|
178201
|
+
const lastActivity = Date.parse(memory.lastAccessedAt ?? memory.updatedAt);
|
|
178202
|
+
if (!Number.isFinite(lastActivity) || lastActivity >= cutoff) continue;
|
|
178203
|
+
try {
|
|
178204
|
+
await archiveLongTermMemory({
|
|
178205
|
+
dataDir: input.dataDir,
|
|
178206
|
+
id: memory.id,
|
|
178207
|
+
reason: "auto-archive: no activity since " + new Date(lastActivity).toISOString().slice(0, 10)
|
|
178208
|
+
});
|
|
178209
|
+
result.archived += 1;
|
|
178210
|
+
} catch {
|
|
178211
|
+
}
|
|
178212
|
+
}
|
|
178213
|
+
const groups = /* @__PURE__ */ new Map();
|
|
178214
|
+
for (const memory of store2.list({ originProjectId: input.projectId })) {
|
|
178215
|
+
if (memory.state !== "qualified" && memory.state !== "approved") continue;
|
|
178216
|
+
const key = memory.scope + "\0" + memory.kind + "\0" + memory.title.trim().toLowerCase();
|
|
178217
|
+
const group = groups.get(key) ?? [];
|
|
178218
|
+
group.push(memory);
|
|
178219
|
+
groups.set(key, group);
|
|
178220
|
+
}
|
|
178221
|
+
for (const group of groups.values()) {
|
|
178222
|
+
if (group.length < 2) continue;
|
|
178223
|
+
group.sort((a4, b4) => Date.parse(b4.createdAt) - Date.parse(a4.createdAt));
|
|
178224
|
+
const newest = group[0];
|
|
178225
|
+
for (const older of group.slice(1)) {
|
|
178226
|
+
try {
|
|
178227
|
+
await supersedeLongTermMemory({
|
|
178228
|
+
dataDir: input.dataDir,
|
|
178229
|
+
id: older.id,
|
|
178230
|
+
supersededBy: newest.id,
|
|
178231
|
+
reason: "auto-supersede: a newer record shares the same title"
|
|
178232
|
+
});
|
|
178233
|
+
result.superseded += 1;
|
|
178234
|
+
} catch {
|
|
178235
|
+
}
|
|
178236
|
+
}
|
|
178237
|
+
}
|
|
178238
|
+
return result;
|
|
178239
|
+
}
|
|
178240
|
+
async function listLongTermMemories(input) {
|
|
178241
|
+
const store2 = await createStore(input.dataDir);
|
|
178242
|
+
return store2.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: store2.listEvidence(memory.id) }));
|
|
178243
|
+
}
|
|
178244
|
+
async function getLongTermMemoryDetail(input) {
|
|
178245
|
+
const store2 = await createStore(input.dataDir);
|
|
178246
|
+
const memory = store2.get(input.id);
|
|
178247
|
+
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
178248
|
+
throw new Error("Long-term memory was not found in the active scope.");
|
|
178249
|
+
}
|
|
178250
|
+
store2.markAccess([memory.id]);
|
|
178251
|
+
return { memory, evidence: store2.listEvidence(memory.id), events: store2.listEvents(memory.id) };
|
|
178252
|
+
}
|
|
178253
|
+
async function selectLongTermMemoriesForTask(input) {
|
|
178254
|
+
const task = input.task?.trim();
|
|
178255
|
+
if (!task) return [];
|
|
178256
|
+
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
178257
|
+
const reader = {
|
|
178258
|
+
projectId: input.projectId,
|
|
178259
|
+
...owner ? { ownerId: owner.id } : {},
|
|
178260
|
+
...input.agentId ? { agentId: input.agentId } : {},
|
|
178261
|
+
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
178262
|
+
};
|
|
178263
|
+
const store2 = await createStore(input.dataDir);
|
|
178264
|
+
const eligible = store2.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
178265
|
+
let selected = eligible.map((memory) => {
|
|
178266
|
+
const ranked = scoreForTask(memory, task);
|
|
178267
|
+
return ranked ? {
|
|
178268
|
+
memory,
|
|
178269
|
+
evidence: store2.listEvidence(memory.id),
|
|
178270
|
+
score: ranked.score,
|
|
178271
|
+
reason: ranked.reason
|
|
178272
|
+
} : void 0;
|
|
178273
|
+
}).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));
|
|
178274
|
+
if (selected.length === 0 && eligible.length > 0) {
|
|
178275
|
+
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
178276
|
+
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
178277
|
+
if (provider2 && candidates.length > 0) {
|
|
178278
|
+
try {
|
|
178279
|
+
const vectors = await withTimeout(
|
|
178280
|
+
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
178281
|
+
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
178282
|
+
retry: false
|
|
178283
|
+
}),
|
|
178284
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
178285
|
+
"Long-term semantic retrieval"
|
|
178286
|
+
);
|
|
178287
|
+
const query = vectors[0];
|
|
178288
|
+
if (Array.isArray(query)) {
|
|
178289
|
+
selected = candidates.map((memory, index) => {
|
|
178290
|
+
const vector = vectors[index + 1];
|
|
178291
|
+
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
178292
|
+
return ranked ? {
|
|
178293
|
+
memory,
|
|
178294
|
+
evidence: store2.listEvidence(memory.id),
|
|
178295
|
+
score: ranked.score,
|
|
178296
|
+
reason: ranked.reason
|
|
178297
|
+
} : void 0;
|
|
178298
|
+
}).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));
|
|
178299
|
+
}
|
|
178300
|
+
} catch {
|
|
178301
|
+
}
|
|
178302
|
+
}
|
|
178303
|
+
}
|
|
178304
|
+
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
178305
|
+
store2.markAccess(selected.map((item) => item.memory.id));
|
|
178306
|
+
return selected;
|
|
178307
|
+
}
|
|
178308
|
+
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
178309
|
+
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
178310
|
+
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
178311
|
+
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
178312
|
+
}
|
|
178313
|
+
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
178314
|
+
var init_long_term = __esm({
|
|
178315
|
+
"src/memory/long-term.ts"() {
|
|
178316
|
+
"use strict";
|
|
178317
|
+
init_esm_shims();
|
|
178318
|
+
init_token_budget();
|
|
178319
|
+
init_timeout();
|
|
178320
|
+
init_secret_filter();
|
|
178321
|
+
init_owner();
|
|
178322
|
+
init_long_term_store();
|
|
178323
|
+
init_long_term_types();
|
|
178324
|
+
MAX_TEXT_LENGTH = 12e3;
|
|
178325
|
+
MAX_LIST_ITEMS = 40;
|
|
178326
|
+
MAX_SEMANTIC_CANDIDATES = 48;
|
|
178327
|
+
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
178328
|
+
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
178329
|
+
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
178330
|
+
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
178331
|
+
"the",
|
|
178332
|
+
"and",
|
|
178333
|
+
"for",
|
|
178334
|
+
"with",
|
|
178335
|
+
"from",
|
|
178336
|
+
"that",
|
|
178337
|
+
"this",
|
|
178338
|
+
"into",
|
|
178339
|
+
"when",
|
|
178340
|
+
"what",
|
|
178341
|
+
"\u5982\u4F55",
|
|
178342
|
+
"\u4EC0\u4E48",
|
|
178343
|
+
"\u8FD9\u4E2A",
|
|
178344
|
+
"\u90A3\u4E2A",
|
|
178345
|
+
"\u6211\u4EEC",
|
|
178346
|
+
"\u9879\u76EE",
|
|
178347
|
+
"\u8FDB\u884C",
|
|
178348
|
+
"\u9700\u8981",
|
|
178349
|
+
"\u7EE7\u7EED",
|
|
178350
|
+
"\u5904\u7406"
|
|
178351
|
+
]);
|
|
178352
|
+
}
|
|
178353
|
+
});
|
|
178354
|
+
|
|
177449
178355
|
// src/codegraph/ids.ts
|
|
177450
178356
|
import { createHash as createHash5 } from "crypto";
|
|
177451
178357
|
function digest(input) {
|
|
@@ -177484,7 +178390,7 @@ var store_exports = {};
|
|
|
177484
178390
|
__export(store_exports, {
|
|
177485
178391
|
CodeGraphStore: () => CodeGraphStore
|
|
177486
178392
|
});
|
|
177487
|
-
import { randomUUID as
|
|
178393
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
177488
178394
|
import { posix as pathPosix } from "path";
|
|
177489
178395
|
function rowToFile(row) {
|
|
177490
178396
|
return {
|
|
@@ -178201,7 +179107,7 @@ var init_store = __esm({
|
|
|
178201
179107
|
* so an interrupted scan cannot be advertised as complete.
|
|
178202
179108
|
*/
|
|
178203
179109
|
recordCodeStateSnapshot(input) {
|
|
178204
|
-
const id =
|
|
179110
|
+
const id = randomUUID9();
|
|
178205
179111
|
let snapshot;
|
|
178206
179112
|
const tx = this.db.transaction(() => {
|
|
178207
179113
|
const previous = this.db.prepare(
|
|
@@ -178979,8 +179885,8 @@ var claim_store_exports = {};
|
|
|
178979
179885
|
__export(claim_store_exports, {
|
|
178980
179886
|
ClaimStore: () => ClaimStore
|
|
178981
179887
|
});
|
|
178982
|
-
import { createHash as createHash8, randomUUID as
|
|
178983
|
-
function
|
|
179888
|
+
import { createHash as createHash8, randomUUID as randomUUID10 } from "crypto";
|
|
179889
|
+
function optionalText3(value) {
|
|
178984
179890
|
return typeof value === "string" && value ? value : void 0;
|
|
178985
179891
|
}
|
|
178986
179892
|
function rowToClaim(row) {
|
|
@@ -178996,38 +179902,38 @@ function rowToClaim(row) {
|
|
|
178996
179902
|
status: row.status,
|
|
178997
179903
|
confidence: Number(row.confidence),
|
|
178998
179904
|
observedAt: row.observedAt,
|
|
178999
|
-
...
|
|
179000
|
-
...
|
|
179001
|
-
...
|
|
179905
|
+
...optionalText3(row.validFrom) ? { validFrom: row.validFrom } : {},
|
|
179906
|
+
...optionalText3(row.validTo) ? { validTo: row.validTo } : {},
|
|
179907
|
+
...optionalText3(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
179002
179908
|
reviewState: row.reviewState,
|
|
179003
179909
|
origin: row.origin,
|
|
179004
179910
|
createdAt: row.createdAt,
|
|
179005
179911
|
updatedAt: row.updatedAt
|
|
179006
179912
|
};
|
|
179007
179913
|
}
|
|
179008
|
-
function
|
|
179914
|
+
function rowToEvidence2(row) {
|
|
179009
179915
|
return {
|
|
179010
179916
|
id: row.id,
|
|
179011
179917
|
claimId: row.claimId,
|
|
179012
179918
|
evidenceKind: row.evidenceKind,
|
|
179013
179919
|
evidenceId: row.evidenceId,
|
|
179014
179920
|
relation: row.relation,
|
|
179015
|
-
...
|
|
179016
|
-
...
|
|
179017
|
-
...
|
|
179921
|
+
...optionalText3(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
|
|
179922
|
+
...optionalText3(row.locator) ? { locator: row.locator } : {},
|
|
179923
|
+
...optionalText3(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
179018
179924
|
createdAt: row.createdAt
|
|
179019
179925
|
};
|
|
179020
179926
|
}
|
|
179021
|
-
function
|
|
179927
|
+
function rowToEvent2(row) {
|
|
179022
179928
|
return {
|
|
179023
179929
|
id: row.id,
|
|
179024
179930
|
projectId: row.projectId,
|
|
179025
179931
|
claimId: row.claimId,
|
|
179026
179932
|
kind: row.kind,
|
|
179027
|
-
...
|
|
179028
|
-
...
|
|
179029
|
-
...
|
|
179030
|
-
...
|
|
179933
|
+
...optionalText3(row.fromStatus) ? { fromStatus: row.fromStatus } : {},
|
|
179934
|
+
...optionalText3(row.toStatus) ? { toStatus: row.toStatus } : {},
|
|
179935
|
+
...optionalText3(row.relatedClaimId) ? { relatedClaimId: row.relatedClaimId } : {},
|
|
179936
|
+
...optionalText3(row.detail) ? { detail: row.detail } : {},
|
|
179031
179937
|
createdAt: row.createdAt
|
|
179032
179938
|
};
|
|
179033
179939
|
}
|
|
@@ -179163,7 +180069,7 @@ var init_claim_store = __esm({
|
|
|
179163
180069
|
locator, capturedHash, evidenceKey, createdAt
|
|
179164
180070
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
179165
180071
|
`).run(
|
|
179166
|
-
|
|
180072
|
+
randomUUID10(),
|
|
179167
180073
|
input.claimId,
|
|
179168
180074
|
input.evidenceKind,
|
|
179169
180075
|
input.evidenceId,
|
|
@@ -179181,11 +180087,11 @@ var init_claim_store = __esm({
|
|
|
179181
180087
|
SELECT * FROM knowledge_claim_evidence
|
|
179182
180088
|
WHERE claimId = ?
|
|
179183
180089
|
ORDER BY createdAt ASC, id
|
|
179184
|
-
`).all(claimId).map(
|
|
180090
|
+
`).all(claimId).map(rowToEvidence2);
|
|
179185
180091
|
}
|
|
179186
180092
|
recordEvent(input) {
|
|
179187
180093
|
const event = {
|
|
179188
|
-
id:
|
|
180094
|
+
id: randomUUID10(),
|
|
179189
180095
|
...input,
|
|
179190
180096
|
...input.detail ? { detail: sanitizeCredentials(input.detail) } : {},
|
|
179191
180097
|
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -179213,7 +180119,7 @@ var init_claim_store = __esm({
|
|
|
179213
180119
|
SELECT * FROM knowledge_claim_events
|
|
179214
180120
|
WHERE claimId = ?
|
|
179215
180121
|
ORDER BY createdAt ASC, id
|
|
179216
|
-
`).all(claimId).map(
|
|
180122
|
+
`).all(claimId).map(rowToEvent2);
|
|
179217
180123
|
}
|
|
179218
180124
|
};
|
|
179219
180125
|
}
|
|
@@ -179230,8 +180136,8 @@ __export(claims_exports, {
|
|
|
179230
180136
|
supersedeClaim: () => supersedeClaim,
|
|
179231
180137
|
writeClaim: () => writeClaim
|
|
179232
180138
|
});
|
|
179233
|
-
import { createHash as createHash9, randomUUID as
|
|
179234
|
-
function
|
|
180139
|
+
import { createHash as createHash9, randomUUID as randomUUID11 } from "crypto";
|
|
180140
|
+
function compactText2(value, field) {
|
|
179235
180141
|
if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
|
|
179236
180142
|
const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
|
|
179237
180143
|
if (!sanitized) throw new Error("Claim " + field + " is required");
|
|
@@ -179239,7 +180145,7 @@ function compactText(value, field) {
|
|
|
179239
180145
|
return sanitized;
|
|
179240
180146
|
}
|
|
179241
180147
|
function normalizedTerm(value) {
|
|
179242
|
-
return
|
|
180148
|
+
return compactText2(value, "identity").toLocaleLowerCase("en-US");
|
|
179243
180149
|
}
|
|
179244
180150
|
function hashIdentity(parts) {
|
|
179245
180151
|
return createHash9("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
|
|
@@ -179268,7 +180174,7 @@ function validateEvidence(input) {
|
|
|
179268
180174
|
if (!evidence || typeof evidence !== "object") {
|
|
179269
180175
|
throw new Error("Claim evidence must be an object");
|
|
179270
180176
|
}
|
|
179271
|
-
const evidenceId =
|
|
180177
|
+
const evidenceId = compactText2(evidence.evidenceId, "evidence id");
|
|
179272
180178
|
if (evidenceId.length > MAX_EVIDENCE_ID_LENGTH) {
|
|
179273
180179
|
throw new Error("Claim evidence id is too long");
|
|
179274
180180
|
}
|
|
@@ -179282,9 +180188,9 @@ function validateEvidence(input) {
|
|
|
179282
180188
|
evidenceKind: evidence.evidenceKind,
|
|
179283
180189
|
evidenceId,
|
|
179284
180190
|
relation: evidence.relation,
|
|
179285
|
-
...evidence.snapshotId ? { snapshotId:
|
|
179286
|
-
...evidence.locator ? { locator:
|
|
179287
|
-
...evidence.capturedHash ? { capturedHash:
|
|
180191
|
+
...evidence.snapshotId ? { snapshotId: compactText2(evidence.snapshotId, "evidence snapshot id") } : {},
|
|
180192
|
+
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator") } : {},
|
|
180193
|
+
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash") } : {}
|
|
179288
180194
|
};
|
|
179289
180195
|
});
|
|
179290
180196
|
}
|
|
@@ -179292,10 +180198,10 @@ function normalizeClaimInput(input) {
|
|
|
179292
180198
|
if (!["project", "workspace", "team", "workflow", "task"].includes(input.scope)) {
|
|
179293
180199
|
throw new Error("Claim scope is invalid");
|
|
179294
180200
|
}
|
|
179295
|
-
const projectId =
|
|
179296
|
-
const subject =
|
|
179297
|
-
const predicate =
|
|
179298
|
-
const objectValue =
|
|
180201
|
+
const projectId = compactText2(input.projectId, "project id");
|
|
180202
|
+
const subject = compactText2(input.subject, "subject");
|
|
180203
|
+
const predicate = compactText2(input.predicate, "predicate");
|
|
180204
|
+
const objectValue = compactText2(input.objectValue, "object value");
|
|
179299
180205
|
const identity = buildClaimIdentity({ subject, predicate, objectValue, scope: input.scope });
|
|
179300
180206
|
const origin = input.origin ?? "explicit";
|
|
179301
180207
|
let reviewState = input.reviewState ?? "approved";
|
|
@@ -179316,7 +180222,7 @@ function normalizeClaimInput(input) {
|
|
|
179316
180222
|
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
179317
180223
|
return {
|
|
179318
180224
|
claim: {
|
|
179319
|
-
id:
|
|
180225
|
+
id: randomUUID11(),
|
|
179320
180226
|
projectId,
|
|
179321
180227
|
subject,
|
|
179322
180228
|
predicate,
|
|
@@ -179416,7 +180322,7 @@ function reviewClaim(store2, input) {
|
|
|
179416
180322
|
if (input.reviewState !== "approved" && input.reviewState !== "rejected") {
|
|
179417
180323
|
throw new Error("A claim review must approve or reject the claim");
|
|
179418
180324
|
}
|
|
179419
|
-
const detail =
|
|
180325
|
+
const detail = compactText2(input.detail, "review detail");
|
|
179420
180326
|
return store2.transaction(() => {
|
|
179421
180327
|
const claim = store2.getClaim(input.claimId);
|
|
179422
180328
|
if (!claim) throw new Error("Claim was not found");
|
|
@@ -179728,8 +180634,8 @@ var init_frontmatter = __esm({
|
|
|
179728
180634
|
|
|
179729
180635
|
// src/knowledge/markdown.ts
|
|
179730
180636
|
import { createHash as createHash10 } from "crypto";
|
|
179731
|
-
import { promises as
|
|
179732
|
-
import
|
|
180637
|
+
import { promises as fs12 } from "fs";
|
|
180638
|
+
import path19 from "path";
|
|
179733
180639
|
function hashContent(content) {
|
|
179734
180640
|
return createHash10("sha256").update(content).digest("hex");
|
|
179735
180641
|
}
|
|
@@ -179787,8 +180693,8 @@ function extractInternalMarkdownLinks(body) {
|
|
|
179787
180693
|
return [...links].sort();
|
|
179788
180694
|
}
|
|
179789
180695
|
function resolvePageLink(sourceRelativePath, target) {
|
|
179790
|
-
const sourceDir =
|
|
179791
|
-
const normalized =
|
|
180696
|
+
const sourceDir = path19.posix.dirname(sourceRelativePath.replace(/\\/g, "/"));
|
|
180697
|
+
const normalized = path19.posix.normalize(path19.posix.join(sourceDir, target.replace(/\\/g, "/")));
|
|
179792
180698
|
if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
|
|
179793
180699
|
return normalized;
|
|
179794
180700
|
}
|
|
@@ -179810,13 +180716,13 @@ function renderKnowledgePage(frontmatter, body) {
|
|
|
179810
180716
|
});
|
|
179811
180717
|
}
|
|
179812
180718
|
async function readKnowledgePage(absolutePath, workspaceRoot) {
|
|
179813
|
-
const raw = await
|
|
180719
|
+
const raw = await fs12.readFile(absolutePath, "utf8");
|
|
179814
180720
|
try {
|
|
179815
180721
|
const parsed = frontmatter_default(raw);
|
|
179816
180722
|
const frontmatter = validateKnowledgePageFrontmatter(parsed.data);
|
|
179817
180723
|
return {
|
|
179818
180724
|
absolutePath,
|
|
179819
|
-
relativePath: workspaceRoot ?
|
|
180725
|
+
relativePath: workspaceRoot ? path19.relative(workspaceRoot, absolutePath).split(path19.sep).join("/") : path19.basename(absolutePath),
|
|
179820
180726
|
frontmatter,
|
|
179821
180727
|
body: parsed.content.trim(),
|
|
179822
180728
|
contentHash: hashContent(raw),
|
|
@@ -179828,7 +180734,7 @@ async function readKnowledgePage(absolutePath, workspaceRoot) {
|
|
|
179828
180734
|
}
|
|
179829
180735
|
}
|
|
179830
180736
|
async function writeKnowledgePage(absolutePath, content) {
|
|
179831
|
-
await
|
|
180737
|
+
await fs12.mkdir(path19.dirname(absolutePath), { recursive: true });
|
|
179832
180738
|
await atomicWriteFile(absolutePath, content);
|
|
179833
180739
|
return hashContent(content);
|
|
179834
180740
|
}
|
|
@@ -179858,8 +180764,8 @@ __export(wiki_exports, {
|
|
|
179858
180764
|
readKnowledgePage: () => readKnowledgePage
|
|
179859
180765
|
});
|
|
179860
180766
|
import { createHash as createHash11 } from "crypto";
|
|
179861
|
-
import { promises as
|
|
179862
|
-
import
|
|
180767
|
+
import { promises as fs13 } from "fs";
|
|
180768
|
+
import path20 from "path";
|
|
179863
180769
|
function hash2(value) {
|
|
179864
180770
|
return createHash11("sha256").update(value).digest("hex");
|
|
179865
180771
|
}
|
|
@@ -179991,7 +180897,7 @@ function candidatePage(workspace, title, claims, store2) {
|
|
|
179991
180897
|
}
|
|
179992
180898
|
async function fileHashIfPresent(filePath) {
|
|
179993
180899
|
try {
|
|
179994
|
-
return pageContentHash(await
|
|
180900
|
+
return pageContentHash(await fs13.readFile(filePath, "utf8"));
|
|
179995
180901
|
} catch (error40) {
|
|
179996
180902
|
const code = error40 && typeof error40 === "object" && "code" in error40 ? error40.code : void 0;
|
|
179997
180903
|
if (code === "ENOENT") return void 0;
|
|
@@ -180015,8 +180921,8 @@ async function writeIndex(workspace, store2) {
|
|
|
180015
180921
|
lines.push("No pending proposals.");
|
|
180016
180922
|
} else {
|
|
180017
180923
|
for (const proposal of pending) {
|
|
180018
|
-
const relative3 =
|
|
180019
|
-
lines.push("- [" +
|
|
180924
|
+
const relative3 = path20.relative(paths.root, proposal.proposalPath).split(path20.sep).join("/");
|
|
180925
|
+
lines.push("- [" + path20.basename(proposal.proposalPath, ".md") + "](" + relative3 + ")");
|
|
180020
180926
|
}
|
|
180021
180927
|
}
|
|
180022
180928
|
lines.push("");
|
|
@@ -180024,7 +180930,7 @@ async function writeIndex(workspace, store2) {
|
|
|
180024
180930
|
}
|
|
180025
180931
|
async function appendLog(workspace, line) {
|
|
180026
180932
|
const logPath = getKnowledgeWorkspacePaths(workspace).log;
|
|
180027
|
-
await
|
|
180933
|
+
await fs13.appendFile(logPath, "- " + now() + " " + line + "\n", "utf8");
|
|
180028
180934
|
}
|
|
180029
180935
|
function toPageRecord(workspace, page, status, contentHash, manualContentHash) {
|
|
180030
180936
|
return {
|
|
@@ -180109,9 +181015,9 @@ async function applyKnowledgeProposal(input) {
|
|
|
180109
181015
|
}
|
|
180110
181016
|
if (proposal.status !== "pending") throw new Error("Knowledge proposal is not pending review");
|
|
180111
181017
|
const root = getKnowledgeWorkspacePaths(input.workspace).root;
|
|
180112
|
-
const targetPath =
|
|
180113
|
-
const proposalPath =
|
|
180114
|
-
if (!targetPath.startsWith(root +
|
|
181018
|
+
const targetPath = path20.resolve(proposal.targetPath);
|
|
181019
|
+
const proposalPath = path20.resolve(proposal.proposalPath);
|
|
181020
|
+
if (!targetPath.startsWith(root + path20.sep) || !proposalPath.startsWith(root + path20.sep)) {
|
|
180115
181021
|
throw new Error("Knowledge proposal path escapes its workspace");
|
|
180116
181022
|
}
|
|
180117
181023
|
await withFileLock(root, async () => {
|
|
@@ -180149,12 +181055,12 @@ async function markdownFiles(directory) {
|
|
|
180149
181055
|
const files = [];
|
|
180150
181056
|
let entries;
|
|
180151
181057
|
try {
|
|
180152
|
-
entries = await
|
|
181058
|
+
entries = await fs13.readdir(directory, { withFileTypes: true });
|
|
180153
181059
|
} catch {
|
|
180154
181060
|
return files;
|
|
180155
181061
|
}
|
|
180156
181062
|
for (const entry of entries) {
|
|
180157
|
-
const filePath =
|
|
181063
|
+
const filePath = path20.join(directory, entry.name);
|
|
180158
181064
|
if (entry.isDirectory()) {
|
|
180159
181065
|
files.push(...await markdownFiles(filePath));
|
|
180160
181066
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
@@ -180177,13 +181083,13 @@ async function lintKnowledgeWorkspace(input) {
|
|
|
180177
181083
|
kind: "malformed-frontmatter",
|
|
180178
181084
|
severity: "error",
|
|
180179
181085
|
message: error40 instanceof Error ? error40.message : "Malformed knowledge page.",
|
|
180180
|
-
relativePath:
|
|
181086
|
+
relativePath: path20.relative(paths.root, filePath).split(path20.sep).join("/")
|
|
180181
181087
|
});
|
|
180182
181088
|
}
|
|
180183
181089
|
}
|
|
180184
181090
|
const indexed = /* @__PURE__ */ new Set();
|
|
180185
181091
|
try {
|
|
180186
|
-
const index = await
|
|
181092
|
+
const index = await fs13.readFile(paths.index, "utf8");
|
|
180187
181093
|
for (const link of extractInternalMarkdownLinks(index)) {
|
|
180188
181094
|
const resolved = resolvePageLink("index.md", link);
|
|
180189
181095
|
if (resolved) indexed.add(resolved);
|
|
@@ -180272,7 +181178,7 @@ async function lintKnowledgeWorkspace(input) {
|
|
|
180272
181178
|
kind: "manual-edit-protected",
|
|
180273
181179
|
severity: "warning",
|
|
180274
181180
|
message: "A newer proposal was held because the target page has manual edits.",
|
|
180275
|
-
relativePath:
|
|
181181
|
+
relativePath: path20.relative(paths.root, proposal.targetPath).split(path20.sep).join("/")
|
|
180276
181182
|
});
|
|
180277
181183
|
}
|
|
180278
181184
|
}
|
|
@@ -180405,7 +181311,7 @@ var workflow_store_exports = {};
|
|
|
180405
181311
|
__export(workflow_store_exports, {
|
|
180406
181312
|
WorkflowStore: () => WorkflowStore
|
|
180407
181313
|
});
|
|
180408
|
-
import { randomUUID as
|
|
181314
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
180409
181315
|
function parseJson(value, fallback) {
|
|
180410
181316
|
if (typeof value !== "string" || !value) return fallback;
|
|
180411
181317
|
try {
|
|
@@ -180414,7 +181320,7 @@ function parseJson(value, fallback) {
|
|
|
180414
181320
|
return fallback;
|
|
180415
181321
|
}
|
|
180416
181322
|
}
|
|
180417
|
-
function
|
|
181323
|
+
function optionalText4(value) {
|
|
180418
181324
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
180419
181325
|
}
|
|
180420
181326
|
function rowToWorkflow(row) {
|
|
@@ -180451,14 +181357,14 @@ function rowToRun(row) {
|
|
|
180451
181357
|
workflowId: row.workflowId,
|
|
180452
181358
|
projectId: row.projectId,
|
|
180453
181359
|
task: row.task,
|
|
180454
|
-
...
|
|
181360
|
+
...optionalText4(row.startingSnapshotId) ? { startingSnapshotId: row.startingSnapshotId } : {},
|
|
180455
181361
|
selectedEvidence: parseJson(row.selectedEvidenceJson, []),
|
|
180456
181362
|
phaseState: parseJson(row.phaseStateJson, {}),
|
|
180457
181363
|
outcome: row.outcome,
|
|
180458
181364
|
verificationVerdict: row.verificationVerdict,
|
|
180459
|
-
...
|
|
181365
|
+
...optionalText4(row.failureReason) ? { failureReason: row.failureReason } : {},
|
|
180460
181366
|
startedAt: row.startedAt,
|
|
180461
|
-
...
|
|
181367
|
+
...optionalText4(row.completedAt) ? { completedAt: row.completedAt } : {}
|
|
180462
181368
|
};
|
|
180463
181369
|
}
|
|
180464
181370
|
var WorkflowStore;
|
|
@@ -180505,7 +181411,7 @@ var init_workflow_store = __esm({
|
|
|
180505
181411
|
}
|
|
180506
181412
|
recordRun(input) {
|
|
180507
181413
|
const run = {
|
|
180508
|
-
id:
|
|
181414
|
+
id: randomUUID12(),
|
|
180509
181415
|
workflowId: input.workflowId,
|
|
180510
181416
|
projectId: input.projectId,
|
|
180511
181417
|
task: input.task.trim(),
|
|
@@ -180583,8 +181489,8 @@ var init_outcome_types = __esm({
|
|
|
180583
181489
|
});
|
|
180584
181490
|
|
|
180585
181491
|
// src/knowledge/outcome-store.ts
|
|
180586
|
-
import { randomUUID as
|
|
180587
|
-
function
|
|
181492
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
181493
|
+
function optionalText5(value) {
|
|
180588
181494
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
180589
181495
|
}
|
|
180590
181496
|
function text(value, field, max = 1e3) {
|
|
@@ -180607,8 +181513,8 @@ function rowToSignal(row) {
|
|
|
180607
181513
|
candidateId: row.candidateId,
|
|
180608
181514
|
kind: row.kind,
|
|
180609
181515
|
sourceRef: row.sourceRef,
|
|
180610
|
-
...
|
|
180611
|
-
...
|
|
181516
|
+
...optionalText5(row.snapshotId) ? { snapshotId: row.snapshotId } : {},
|
|
181517
|
+
...optionalText5(row.detail) ? { detail: row.detail } : {},
|
|
180612
181518
|
observedAt: row.observedAt
|
|
180613
181519
|
};
|
|
180614
181520
|
}
|
|
@@ -180627,7 +181533,7 @@ var init_outcome_store = __esm({
|
|
|
180627
181533
|
}
|
|
180628
181534
|
record(input) {
|
|
180629
181535
|
const signal = {
|
|
180630
|
-
id:
|
|
181536
|
+
id: randomUUID13(),
|
|
180631
181537
|
projectId: text(input.projectId, "project id"),
|
|
180632
181538
|
candidateKind: oneOf(input.candidateKind, MEMORY_OUTCOME_CANDIDATE_KINDS, "candidate kind"),
|
|
180633
181539
|
candidateId: text(input.candidateId, "candidate id"),
|
|
@@ -181054,8 +181960,8 @@ __export(workflows_exports, {
|
|
|
181054
181960
|
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
181055
181961
|
});
|
|
181056
181962
|
import { createHash as createHash12 } from "crypto";
|
|
181057
|
-
import { promises as
|
|
181058
|
-
import
|
|
181963
|
+
import { promises as fs14 } from "fs";
|
|
181964
|
+
import path21 from "path";
|
|
181059
181965
|
function hash3(value) {
|
|
181060
181966
|
return createHash12("sha256").update(value).digest("hex");
|
|
181061
181967
|
}
|
|
@@ -181076,7 +181982,7 @@ function requiredText(data, key) {
|
|
|
181076
181982
|
}
|
|
181077
181983
|
return value.trim();
|
|
181078
181984
|
}
|
|
181079
|
-
function
|
|
181985
|
+
function optionalText6(data, key) {
|
|
181080
181986
|
const value = data[key];
|
|
181081
181987
|
if (value === void 0 || value === null || value === "") return void 0;
|
|
181082
181988
|
if (typeof value !== "string") throw new Error("workflow frontmatter field " + key + " must be text");
|
|
@@ -181119,11 +182025,11 @@ function phaseFromValue(value, index) {
|
|
|
181119
182025
|
}
|
|
181120
182026
|
const data = value;
|
|
181121
182027
|
const title = requiredText(data, "title");
|
|
181122
|
-
const phaseId =
|
|
182028
|
+
const phaseId = optionalText6(data, "id") ?? slug(title) + "-" + (index + 1);
|
|
181123
182029
|
return {
|
|
181124
182030
|
id: phaseId,
|
|
181125
182031
|
title,
|
|
181126
|
-
instructions:
|
|
182032
|
+
instructions: optionalText6(data, "instructions") ?? "",
|
|
181127
182033
|
branches: optionalStringArray(data, "branches"),
|
|
181128
182034
|
expectedOutputs: optionalStringArray(data, "expectedOutputs"),
|
|
181129
182035
|
verificationGates: optionalStringArray(data, "verificationGates")
|
|
@@ -181158,7 +182064,7 @@ function phasesFromBody(body) {
|
|
|
181158
182064
|
}
|
|
181159
182065
|
function normalizeSourcePath(sourcePath) {
|
|
181160
182066
|
const normalized = sourcePath.replace(/\\/g, "/");
|
|
181161
|
-
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") ||
|
|
182067
|
+
if (!normalized.startsWith("workflows/") || !normalized.toLowerCase().endsWith(".md") || normalized.includes("../") || path21.posix.normalize(normalized) !== normalized) {
|
|
181162
182068
|
throw new Error("workflow source path must be a safe workflows/*.md path");
|
|
181163
182069
|
}
|
|
181164
182070
|
return normalized;
|
|
@@ -181232,7 +182138,7 @@ function parsedWorkflow(data, body, input) {
|
|
|
181232
182138
|
id: requiredText(data, "id"),
|
|
181233
182139
|
workspaceId: input.workspaceId,
|
|
181234
182140
|
title: requiredText(data, "title"),
|
|
181235
|
-
description:
|
|
182141
|
+
description: optionalText6(data, "description") ?? "",
|
|
181236
182142
|
status: optionalStatus(data),
|
|
181237
182143
|
version: optionalNumber(data, "version", 1),
|
|
181238
182144
|
taskLenses: optionalStringArray(data, "taskLenses"),
|
|
@@ -181251,9 +182157,9 @@ function parsedWorkflow(data, body, input) {
|
|
|
181251
182157
|
sourcePath: normalizeSourcePath(input.sourcePath),
|
|
181252
182158
|
sourceHash: "",
|
|
181253
182159
|
contentHash: input.contentHash,
|
|
181254
|
-
createdAt:
|
|
181255
|
-
updatedAt:
|
|
181256
|
-
...
|
|
182160
|
+
createdAt: optionalText6(data, "createdAt") ?? now2(),
|
|
182161
|
+
updatedAt: optionalText6(data, "updatedAt") ?? now2(),
|
|
182162
|
+
...optionalText6(data, "importedFrom") ? { importedFrom: optionalText6(data, "importedFrom") } : {}
|
|
181257
182163
|
};
|
|
181258
182164
|
if (candidate2.phases.length === 0) {
|
|
181259
182165
|
throw new Error("workflow must contain at least one phase or a non-empty body");
|
|
@@ -181307,7 +182213,7 @@ async function writeCanonicalWorkflow(input) {
|
|
|
181307
182213
|
const filePath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
|
|
181308
182214
|
const content = renderWorkflowMarkdown(workflow);
|
|
181309
182215
|
await withFileLock(paths.root, async () => {
|
|
181310
|
-
await
|
|
182216
|
+
await fs14.mkdir(path21.dirname(filePath), { recursive: true });
|
|
181311
182217
|
await atomicWriteFile(filePath, content);
|
|
181312
182218
|
});
|
|
181313
182219
|
const store2 = await storeFor2(input.workspace);
|
|
@@ -181316,13 +182222,13 @@ async function writeCanonicalWorkflow(input) {
|
|
|
181316
182222
|
async function markdownFiles2(directory) {
|
|
181317
182223
|
let entries;
|
|
181318
182224
|
try {
|
|
181319
|
-
entries = await
|
|
182225
|
+
entries = await fs14.readdir(directory, { withFileTypes: true });
|
|
181320
182226
|
} catch {
|
|
181321
182227
|
return [];
|
|
181322
182228
|
}
|
|
181323
182229
|
const files = [];
|
|
181324
182230
|
for (const entry of entries) {
|
|
181325
|
-
const absolutePath =
|
|
182231
|
+
const absolutePath = path21.join(directory, entry.name);
|
|
181326
182232
|
if (entry.isDirectory()) {
|
|
181327
182233
|
files.push(...await markdownFiles2(absolutePath));
|
|
181328
182234
|
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
@@ -181337,9 +182243,9 @@ async function syncCanonicalWorkflows(workspace) {
|
|
|
181337
182243
|
const workflows = [];
|
|
181338
182244
|
const errors2 = [];
|
|
181339
182245
|
for (const absolutePath of await markdownFiles2(paths.workflows)) {
|
|
181340
|
-
const sourcePath =
|
|
182246
|
+
const sourcePath = path21.relative(paths.root, absolutePath).split(path21.sep).join("/");
|
|
181341
182247
|
try {
|
|
181342
|
-
const raw = await
|
|
182248
|
+
const raw = await fs14.readFile(absolutePath, "utf8");
|
|
181343
182249
|
const workflow = parseWorkflowMarkdown(raw, { workspaceId: workspace.id, sourcePath });
|
|
181344
182250
|
workflows.push(store2.upsertWorkflow(workflow));
|
|
181345
182251
|
} catch (error40) {
|
|
@@ -181451,21 +182357,21 @@ function importedWorkflowSpec(input) {
|
|
|
181451
182357
|
return materializeWorkflow(spec);
|
|
181452
182358
|
}
|
|
181453
182359
|
async function importWindsurfWorkflows(input) {
|
|
181454
|
-
const sourceDirectory =
|
|
182360
|
+
const sourceDirectory = path21.join(path21.resolve(input.projectRoot), ".windsurf", "workflows");
|
|
181455
182361
|
const paths = getKnowledgeWorkspacePaths(input.workspace);
|
|
181456
182362
|
const imported = [];
|
|
181457
182363
|
const skipped = [];
|
|
181458
182364
|
let entries;
|
|
181459
182365
|
try {
|
|
181460
|
-
entries = await
|
|
182366
|
+
entries = await fs14.readdir(sourceDirectory, { withFileTypes: true });
|
|
181461
182367
|
} catch {
|
|
181462
182368
|
return { imported, skipped };
|
|
181463
182369
|
}
|
|
181464
182370
|
for (const entry of entries.filter((item) => item.isFile() && item.name.toLowerCase().endsWith(".md"))) {
|
|
181465
|
-
const originalPath =
|
|
181466
|
-
const sourcePath =
|
|
182371
|
+
const originalPath = path21.join(sourceDirectory, entry.name);
|
|
182372
|
+
const sourcePath = path21.relative(path21.resolve(input.projectRoot), originalPath).split(path21.sep).join("/");
|
|
181467
182373
|
try {
|
|
181468
|
-
const raw = await
|
|
182374
|
+
const raw = await fs14.readFile(originalPath, "utf8");
|
|
181469
182375
|
const workflow = importedWorkflowSpec({
|
|
181470
182376
|
workspace: input.workspace,
|
|
181471
182377
|
sourceName: entry.name,
|
|
@@ -181474,7 +182380,7 @@ async function importWindsurfWorkflows(input) {
|
|
|
181474
182380
|
});
|
|
181475
182381
|
const targetPath = resolveKnowledgeWorkspaceFile(input.workspace, workflow.sourcePath);
|
|
181476
182382
|
try {
|
|
181477
|
-
await
|
|
182383
|
+
await fs14.access(targetPath);
|
|
181478
182384
|
skipped.push({ sourcePath, reason: "canonical workflow already exists and was preserved" });
|
|
181479
182385
|
continue;
|
|
181480
182386
|
} catch {
|
|
@@ -181487,12 +182393,12 @@ async function importWindsurfWorkflows(input) {
|
|
|
181487
182393
|
});
|
|
181488
182394
|
}
|
|
181489
182395
|
}
|
|
181490
|
-
await
|
|
182396
|
+
await fs14.mkdir(paths.workflows, { recursive: true });
|
|
181491
182397
|
return { imported, skipped };
|
|
181492
182398
|
}
|
|
181493
182399
|
function isWithin2(root, candidate2) {
|
|
181494
|
-
const relative3 =
|
|
181495
|
-
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." +
|
|
182400
|
+
const relative3 = path21.relative(root, candidate2);
|
|
182401
|
+
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(".." + path21.sep) && !path21.isAbsolute(relative3);
|
|
181496
182402
|
}
|
|
181497
182403
|
function adapterRelativePath(workflow, agent) {
|
|
181498
182404
|
const name = "memorix-workflow-" + slug(workflow.id.replace(/^workflow:/, ""));
|
|
@@ -181578,15 +182484,15 @@ async function previewWorkflowAdapter(input) {
|
|
|
181578
182484
|
reason: "The canonical workflow does not declare compatibility with this agent."
|
|
181579
182485
|
};
|
|
181580
182486
|
}
|
|
181581
|
-
const projectRoot =
|
|
181582
|
-
const targetPath =
|
|
182487
|
+
const projectRoot = path21.resolve(input.projectRoot);
|
|
182488
|
+
const targetPath = path21.resolve(projectRoot, adapterRelativePath(input.workflow, input.agent));
|
|
181583
182489
|
if (!isWithin2(projectRoot, targetPath)) {
|
|
181584
182490
|
throw new Error("Workflow adapter target escapes the project root");
|
|
181585
182491
|
}
|
|
181586
182492
|
const content = adapterContent(input.workflow, input.agent);
|
|
181587
182493
|
let existing;
|
|
181588
182494
|
try {
|
|
181589
|
-
existing = await
|
|
182495
|
+
existing = await fs14.readFile(targetPath, "utf8");
|
|
181590
182496
|
} catch (error40) {
|
|
181591
182497
|
const code = error40 && typeof error40 === "object" && "code" in error40 ? error40.code : void 0;
|
|
181592
182498
|
if (code !== "ENOENT") throw error40;
|
|
@@ -181607,11 +182513,11 @@ async function applyWorkflowAdapter(input) {
|
|
|
181607
182513
|
if (initial.status === "unsupported" || initial.status === "conflict" || initial.status === "unchanged") {
|
|
181608
182514
|
return initial;
|
|
181609
182515
|
}
|
|
181610
|
-
const projectRoot =
|
|
182516
|
+
const projectRoot = path21.resolve(input.projectRoot);
|
|
181611
182517
|
return withFileLock(projectRoot, async () => {
|
|
181612
182518
|
const current = await previewWorkflowAdapter(input);
|
|
181613
182519
|
if (current.status !== "create" && current.status !== "update") return current;
|
|
181614
|
-
await
|
|
182520
|
+
await fs14.mkdir(path21.dirname(current.targetPath), { recursive: true });
|
|
181615
182521
|
await atomicWriteFile(current.targetPath, current.content);
|
|
181616
182522
|
return current;
|
|
181617
182523
|
});
|
|
@@ -181901,6 +182807,11 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
181901
182807
|
}
|
|
181902
182808
|
return { action: "complete" };
|
|
181903
182809
|
}
|
|
182810
|
+
if (job.kind === "long-term-maintenance") {
|
|
182811
|
+
const { maintainLongTermMemories: maintainLongTermMemories2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
182812
|
+
await maintainLongTermMemories2({ dataDir: projectDir2, projectId });
|
|
182813
|
+
return { action: "reschedule", delayMs: 24 * 60 * 60 * 1e3, resetAttempts: true };
|
|
182814
|
+
}
|
|
181904
182815
|
if (job.kind === "consolidation") {
|
|
181905
182816
|
const limit2 = consolidationBatchSize(job.payload);
|
|
181906
182817
|
const result2 = await runAutomaticConsolidation(projectId, projectDir2, {
|
|
@@ -182242,15 +183153,15 @@ __export(vector_backfill_runner_exports, {
|
|
|
182242
183153
|
});
|
|
182243
183154
|
import { spawn as spawn4 } from "child_process";
|
|
182244
183155
|
import { existsSync as existsSync12 } from "fs";
|
|
182245
|
-
import
|
|
183156
|
+
import path22 from "path";
|
|
182246
183157
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
182247
183158
|
function isNonEmptyString(value) {
|
|
182248
183159
|
return typeof value === "string" && value.trim().length > 0;
|
|
182249
183160
|
}
|
|
182250
183161
|
function resolveVectorBackfillRunnerPath(moduleUrl = import.meta.url) {
|
|
182251
|
-
const moduleDir =
|
|
182252
|
-
const distDir =
|
|
182253
|
-
return
|
|
183162
|
+
const moduleDir = path22.dirname(fileURLToPath6(moduleUrl));
|
|
183163
|
+
const distDir = path22.basename(moduleDir) === "cli" ? path22.dirname(moduleDir) : path22.basename(moduleDir) === "runtime" && path22.basename(path22.dirname(moduleDir)) === "src" ? path22.join(path22.dirname(path22.dirname(moduleDir)), "dist") : moduleDir;
|
|
183164
|
+
return path22.join(distDir, "vector-backfill-runner.js");
|
|
182254
183165
|
}
|
|
182255
183166
|
function parseVectorBackfillRequest(raw) {
|
|
182256
183167
|
let value;
|
|
@@ -182259,7 +183170,7 @@ function parseVectorBackfillRequest(raw) {
|
|
|
182259
183170
|
} catch {
|
|
182260
183171
|
throw new Error("Vector backfill runner received invalid JSON input");
|
|
182261
183172
|
}
|
|
182262
|
-
if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !
|
|
183173
|
+
if (!value || typeof value !== "object" || !isNonEmptyString(value.projectId) || !isNonEmptyString(value.projectRoot) || !isNonEmptyString(value.dataDir) || !path22.isAbsolute(value.projectRoot) || !path22.isAbsolute(value.dataDir)) {
|
|
182263
183174
|
throw new Error("Vector backfill runner received an invalid request");
|
|
182264
183175
|
}
|
|
182265
183176
|
return {
|
|
@@ -183783,7 +184694,7 @@ async function searchObservations(options) {
|
|
|
183783
184694
|
const expandedEmbeddingQuery = quality === "thorough" && tier === "heavy" ? await maybeExpandSearchQuery(options.query) : options.query;
|
|
183784
184695
|
mark("queryExpansion");
|
|
183785
184696
|
const intentResult = quality === "fast" || !hasQuery ? null : detectQueryIntent(originalQuery);
|
|
183786
|
-
const requestLimit = projectIds ? (options.limit ?? 20) * 3 : options.limit ?? 20;
|
|
184697
|
+
const requestLimit = projectIds || options.surfacedIds && options.surfacedIds.length > 0 ? (options.limit ?? 20) * 3 : options.limit ?? 20;
|
|
183787
184698
|
const defaultBoost = {
|
|
183788
184699
|
title: 3,
|
|
183789
184700
|
entityName: 2,
|
|
@@ -184015,6 +184926,14 @@ async function searchObservations(options) {
|
|
|
184015
184926
|
});
|
|
184016
184927
|
intermediate.sort((a4, b4) => b4.score - a4.score);
|
|
184017
184928
|
}
|
|
184929
|
+
if (options.surfacedIds && options.surfacedIds.length > 0) {
|
|
184930
|
+
const SURFACED_PENALTY = 0.5;
|
|
184931
|
+
const surfaced = new Set(options.surfacedIds);
|
|
184932
|
+
intermediate = intermediate.map(
|
|
184933
|
+
(entry) => surfaced.has(entry.id) ? { ...entry, score: (entry.score ?? 0) * SURFACED_PENALTY } : entry
|
|
184934
|
+
);
|
|
184935
|
+
intermediate.sort((a4, b4) => (b4.score ?? 0) - (a4.score ?? 0));
|
|
184936
|
+
}
|
|
184018
184937
|
if (options.since) {
|
|
184019
184938
|
const sinceDate = new Date(options.since).getTime();
|
|
184020
184939
|
intermediate = intermediate.filter((e5) => new Date(e5.rawTime).getTime() >= sinceDate);
|
|
@@ -185021,9 +185940,9 @@ __export(team_store_exports, {
|
|
|
185021
185940
|
isTeamStoreInitialized: () => isTeamStoreInitialized,
|
|
185022
185941
|
resetTeamStore: () => resetTeamStore
|
|
185023
185942
|
});
|
|
185024
|
-
import { randomUUID as
|
|
185025
|
-
import
|
|
185026
|
-
import
|
|
185943
|
+
import { randomUUID as randomUUID14 } from "crypto";
|
|
185944
|
+
import path23 from "path";
|
|
185945
|
+
import fs15 from "fs";
|
|
185027
185946
|
function getTeamStore() {
|
|
185028
185947
|
if (!_teamStore) {
|
|
185029
185948
|
throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
|
|
@@ -185310,7 +186229,7 @@ var init_team_store = __esm({
|
|
|
185310
186229
|
* reactivate it (preserving agent_id). Otherwise create a new one.
|
|
185311
186230
|
*/
|
|
185312
186231
|
registerAgent(input) {
|
|
185313
|
-
const instanceId = input.instanceId ||
|
|
186232
|
+
const instanceId = input.instanceId || randomUUID14();
|
|
185314
186233
|
const now3 = Date.now();
|
|
185315
186234
|
const existing = this.stmtAgentFindByInstance.get(
|
|
185316
186235
|
input.projectId,
|
|
@@ -185336,7 +186255,7 @@ var init_team_store = __esm({
|
|
|
185336
186255
|
this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
|
|
185337
186256
|
return row2;
|
|
185338
186257
|
}
|
|
185339
|
-
const agentId =
|
|
186258
|
+
const agentId = randomUUID14();
|
|
185340
186259
|
this.stmtAgentUpsert.run({
|
|
185341
186260
|
agent_id: agentId,
|
|
185342
186261
|
project_id: input.projectId,
|
|
@@ -185438,7 +186357,7 @@ var init_team_store = __esm({
|
|
|
185438
186357
|
return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
|
|
185439
186358
|
}
|
|
185440
186359
|
}
|
|
185441
|
-
const id =
|
|
186360
|
+
const id = randomUUID14();
|
|
185442
186361
|
const now3 = Date.now();
|
|
185443
186362
|
const row = {
|
|
185444
186363
|
id,
|
|
@@ -185488,7 +186407,7 @@ var init_team_store = __esm({
|
|
|
185488
186407
|
// Task Operations (with atomic claim semantics)
|
|
185489
186408
|
// ═══════════════════════════════════════════════════════════════════
|
|
185490
186409
|
createTask(input) {
|
|
185491
|
-
const taskId =
|
|
186410
|
+
const taskId = randomUUID14();
|
|
185492
186411
|
const now3 = Date.now();
|
|
185493
186412
|
const row = {
|
|
185494
186413
|
task_id: taskId,
|
|
@@ -185713,19 +186632,19 @@ var init_team_store = __esm({
|
|
|
185713
186632
|
const agentCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM team_agents`).get();
|
|
185714
186633
|
if (agentCount.cnt > 0) return;
|
|
185715
186634
|
const candidates = [
|
|
185716
|
-
|
|
185717
|
-
|
|
186635
|
+
path23.join(this.dataDir, "team-state.json"),
|
|
186636
|
+
path23.join(this.dataDir, "..", "team-state.json")
|
|
185718
186637
|
];
|
|
185719
186638
|
let jsonPath = null;
|
|
185720
186639
|
for (const p3 of candidates) {
|
|
185721
|
-
if (
|
|
186640
|
+
if (fs15.existsSync(p3)) {
|
|
185722
186641
|
jsonPath = p3;
|
|
185723
186642
|
break;
|
|
185724
186643
|
}
|
|
185725
186644
|
}
|
|
185726
186645
|
if (!jsonPath) return;
|
|
185727
186646
|
try {
|
|
185728
|
-
const raw =
|
|
186647
|
+
const raw = fs15.readFileSync(jsonPath, "utf-8");
|
|
185729
186648
|
const snap = JSON.parse(raw);
|
|
185730
186649
|
if (snap.version !== 1) return;
|
|
185731
186650
|
console.error(`[memorix] Migrating team-state.json to SQLite...`);
|
|
@@ -185760,7 +186679,7 @@ var init_team_store = __esm({
|
|
|
185760
186679
|
for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
|
|
185761
186680
|
for (const msg of msgs) {
|
|
185762
186681
|
this.stmtMsgInsert.run({
|
|
185763
|
-
id: msg.id ??
|
|
186682
|
+
id: msg.id ?? randomUUID14(),
|
|
185764
186683
|
project_id: "migrated",
|
|
185765
186684
|
sender_agent_id: msg.from ?? "unknown",
|
|
185766
186685
|
recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
|
|
@@ -185819,7 +186738,7 @@ var init_team_store = __esm({
|
|
|
185819
186738
|
`[memorix] Team migration complete: ${agentsMigrated} agents, ${messagesMigrated} messages, ${tasksMigrated} tasks, ${locksMigrated} locks.`
|
|
185820
186739
|
);
|
|
185821
186740
|
try {
|
|
185822
|
-
|
|
186741
|
+
fs15.renameSync(jsonPath, jsonPath + ".migrated");
|
|
185823
186742
|
} catch {
|
|
185824
186743
|
}
|
|
185825
186744
|
} catch (err) {
|
|
@@ -185854,7 +186773,7 @@ var init_team_store = __esm({
|
|
|
185854
186773
|
* Uses projectId derived from the dataDir path.
|
|
185855
186774
|
*/
|
|
185856
186775
|
seedDefaultRoles(dataDir) {
|
|
185857
|
-
const projectId =
|
|
186776
|
+
const projectId = path23.basename(path23.resolve(dataDir, ".."));
|
|
185858
186777
|
const count3 = this.stmtRoleCountByProject.get(projectId).cnt;
|
|
185859
186778
|
if (count3 > 0) return;
|
|
185860
186779
|
const now3 = Date.now();
|
|
@@ -185948,10 +186867,10 @@ var init_team_store = __esm({
|
|
|
185948
186867
|
});
|
|
185949
186868
|
|
|
185950
186869
|
// src/cli/identity.ts
|
|
185951
|
-
import { promises as
|
|
185952
|
-
import
|
|
186870
|
+
import { promises as fs16 } from "fs";
|
|
186871
|
+
import path24 from "path";
|
|
185953
186872
|
function identityPath(dataDir) {
|
|
185954
|
-
return
|
|
186873
|
+
return path24.join(dataDir, "cli-identity.json");
|
|
185955
186874
|
}
|
|
185956
186875
|
function isCliIdentity(value) {
|
|
185957
186876
|
if (!value || typeof value !== "object") return false;
|
|
@@ -185960,18 +186879,18 @@ function isCliIdentity(value) {
|
|
|
185960
186879
|
}
|
|
185961
186880
|
async function loadCliIdentity(dataDir, projectId) {
|
|
185962
186881
|
try {
|
|
185963
|
-
const parsed = JSON.parse(await
|
|
186882
|
+
const parsed = JSON.parse(await fs16.readFile(identityPath(dataDir), "utf8"));
|
|
185964
186883
|
return isCliIdentity(parsed) && parsed.projectId === projectId ? parsed : null;
|
|
185965
186884
|
} catch {
|
|
185966
186885
|
return null;
|
|
185967
186886
|
}
|
|
185968
186887
|
}
|
|
185969
186888
|
async function saveCliIdentity(dataDir, identity) {
|
|
185970
|
-
await
|
|
186889
|
+
await fs16.writeFile(identityPath(dataDir), `${JSON.stringify(identity, null, 2)}
|
|
185971
186890
|
`, "utf8");
|
|
185972
186891
|
}
|
|
185973
186892
|
async function clearCliIdentity(dataDir) {
|
|
185974
|
-
await
|
|
186893
|
+
await fs16.rm(identityPath(dataDir), { force: true });
|
|
185975
186894
|
}
|
|
185976
186895
|
async function resolveCliIdentity(options) {
|
|
185977
186896
|
const stored = await loadCliIdentity(options.dataDir, options.project.id);
|
|
@@ -187321,8 +188240,8 @@ __export(session_store_exports, {
|
|
|
187321
188240
|
initSessionStore: () => initSessionStore,
|
|
187322
188241
|
resetSessionStore: () => resetSessionStore
|
|
187323
188242
|
});
|
|
187324
|
-
import
|
|
187325
|
-
import
|
|
188243
|
+
import path25 from "path";
|
|
188244
|
+
import fs17 from "fs";
|
|
187326
188245
|
function sessionToRow(session) {
|
|
187327
188246
|
return {
|
|
187328
188247
|
id: session.id,
|
|
@@ -187408,10 +188327,10 @@ var init_session_store = __esm({
|
|
|
187408
188327
|
async migrateFromJsonIfNeeded() {
|
|
187409
188328
|
const count3 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM sessions`).get();
|
|
187410
188329
|
if (count3.cnt > 0) return;
|
|
187411
|
-
const jsonPath =
|
|
187412
|
-
if (!
|
|
188330
|
+
const jsonPath = path25.join(this.dataDir, "sessions.json");
|
|
188331
|
+
if (!fs17.existsSync(jsonPath)) return;
|
|
187413
188332
|
try {
|
|
187414
|
-
const raw =
|
|
188333
|
+
const raw = fs17.readFileSync(jsonPath, "utf-8");
|
|
187415
188334
|
const sessions = JSON.parse(raw);
|
|
187416
188335
|
if (!Array.isArray(sessions) || sessions.length === 0) return;
|
|
187417
188336
|
console.error(`[memorix] Migrating ${sessions.length} sessions from JSON to SQLite...`);
|
|
@@ -187753,827 +188672,6 @@ var init_operator_shared = __esm({
|
|
|
187753
188672
|
}
|
|
187754
188673
|
});
|
|
187755
188674
|
|
|
187756
|
-
// src/memory/owner.ts
|
|
187757
|
-
var owner_exports = {};
|
|
187758
|
-
__export(owner_exports, {
|
|
187759
|
-
resolveLocalMemoryOwner: () => resolveLocalMemoryOwner
|
|
187760
|
-
});
|
|
187761
|
-
import { randomUUID as randomUUID12 } from "crypto";
|
|
187762
|
-
import { promises as fs17 } from "fs";
|
|
187763
|
-
import path25 from "path";
|
|
187764
|
-
function ownerPath(dataDir) {
|
|
187765
|
-
const resolved = path25.resolve(dataDir);
|
|
187766
|
-
const home = path25.basename(resolved).toLocaleLowerCase("en-US") === "data" ? path25.dirname(resolved) : resolved;
|
|
187767
|
-
return path25.join(home, "memorix-user.json");
|
|
187768
|
-
}
|
|
187769
|
-
function normalizedOwnerId(value) {
|
|
187770
|
-
const trimmed = value?.trim();
|
|
187771
|
-
return trimmed && ID_PATTERN.test(trimmed) ? trimmed : void 0;
|
|
187772
|
-
}
|
|
187773
|
-
async function readOwnerFile(filePath) {
|
|
187774
|
-
try {
|
|
187775
|
-
const value = JSON.parse(await fs17.readFile(filePath, "utf8"));
|
|
187776
|
-
const id = typeof value.id === "string" ? normalizedOwnerId(value.id) : void 0;
|
|
187777
|
-
return id ? { id, source: "local-file" } : void 0;
|
|
187778
|
-
} catch {
|
|
187779
|
-
return void 0;
|
|
187780
|
-
}
|
|
187781
|
-
}
|
|
187782
|
-
async function resolveLocalMemoryOwner(dataDir, options = {}) {
|
|
187783
|
-
const configured = normalizedOwnerId(process.env.MEMORIX_USER_ID);
|
|
187784
|
-
if (configured) return { id: configured, source: "environment" };
|
|
187785
|
-
const filePath = ownerPath(dataDir);
|
|
187786
|
-
const existing = await readOwnerFile(filePath);
|
|
187787
|
-
if (existing || !options.create) return existing;
|
|
187788
|
-
await fs17.mkdir(path25.dirname(filePath), { recursive: true });
|
|
187789
|
-
const created = {
|
|
187790
|
-
version: 1,
|
|
187791
|
-
id: "local-" + randomUUID12(),
|
|
187792
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
187793
|
-
};
|
|
187794
|
-
try {
|
|
187795
|
-
await fs17.writeFile(filePath, JSON.stringify(created, null, 2) + "\n", { encoding: "utf8", flag: "wx" });
|
|
187796
|
-
return { id: created.id, source: "local-file" };
|
|
187797
|
-
} catch (error40) {
|
|
187798
|
-
if (error40?.code !== "EEXIST") throw error40;
|
|
187799
|
-
const concurrent = await readOwnerFile(filePath);
|
|
187800
|
-
if (concurrent) return concurrent;
|
|
187801
|
-
throw new Error("Memorix could not establish a valid local memory owner identity.");
|
|
187802
|
-
}
|
|
187803
|
-
}
|
|
187804
|
-
var ID_PATTERN;
|
|
187805
|
-
var init_owner = __esm({
|
|
187806
|
-
"src/memory/owner.ts"() {
|
|
187807
|
-
"use strict";
|
|
187808
|
-
init_esm_shims();
|
|
187809
|
-
ID_PATTERN = /^[A-Za-z0-9:_-]{3,160}$/;
|
|
187810
|
-
}
|
|
187811
|
-
});
|
|
187812
|
-
|
|
187813
|
-
// src/memory/long-term-store.ts
|
|
187814
|
-
import { randomUUID as randomUUID13 } from "crypto";
|
|
187815
|
-
function optionalText6(value) {
|
|
187816
|
-
return typeof value === "string" && value.trim() ? value : void 0;
|
|
187817
|
-
}
|
|
187818
|
-
function arrayValue2(value) {
|
|
187819
|
-
if (typeof value !== "string" || !value) return [];
|
|
187820
|
-
try {
|
|
187821
|
-
const parsed = JSON.parse(value);
|
|
187822
|
-
return Array.isArray(parsed) ? parsed.filter((item) => typeof item === "string") : [];
|
|
187823
|
-
} catch {
|
|
187824
|
-
return [];
|
|
187825
|
-
}
|
|
187826
|
-
}
|
|
187827
|
-
function rowToMemory(row) {
|
|
187828
|
-
return {
|
|
187829
|
-
id: row.id,
|
|
187830
|
-
originProjectId: row.originProjectId,
|
|
187831
|
-
ownerId: row.ownerId,
|
|
187832
|
-
scope: row.scope,
|
|
187833
|
-
kind: row.kind,
|
|
187834
|
-
state: row.state,
|
|
187835
|
-
portability: row.portability,
|
|
187836
|
-
title: row.title,
|
|
187837
|
-
content: row.content,
|
|
187838
|
-
facts: arrayValue2(row.factsJson),
|
|
187839
|
-
tags: arrayValue2(row.tagsJson),
|
|
187840
|
-
...optionalText6(row.applicability) ? { applicability: row.applicability } : {},
|
|
187841
|
-
origin: row.origin,
|
|
187842
|
-
createdAt: row.createdAt,
|
|
187843
|
-
updatedAt: row.updatedAt,
|
|
187844
|
-
...optionalText6(row.qualifiedAt) ? { qualifiedAt: row.qualifiedAt } : {},
|
|
187845
|
-
...optionalText6(row.approvedAt) ? { approvedAt: row.approvedAt } : {},
|
|
187846
|
-
...optionalText6(row.archivedAt) ? { archivedAt: row.archivedAt } : {},
|
|
187847
|
-
...optionalText6(row.supersededBy) ? { supersededBy: row.supersededBy } : {},
|
|
187848
|
-
...optionalText6(row.lastValidatedAt) ? { lastValidatedAt: row.lastValidatedAt } : {},
|
|
187849
|
-
accessCount: Number(row.accessCount ?? 0),
|
|
187850
|
-
...optionalText6(row.lastAccessedAt) ? { lastAccessedAt: row.lastAccessedAt } : {}
|
|
187851
|
-
};
|
|
187852
|
-
}
|
|
187853
|
-
function rowToEvidence2(row) {
|
|
187854
|
-
return {
|
|
187855
|
-
id: row.id,
|
|
187856
|
-
memoryId: row.memoryId,
|
|
187857
|
-
kind: row.kind,
|
|
187858
|
-
referenceId: row.referenceId,
|
|
187859
|
-
relation: row.relation,
|
|
187860
|
-
...optionalText6(row.locator) ? { locator: row.locator } : {},
|
|
187861
|
-
...optionalText6(row.capturedHash) ? { capturedHash: row.capturedHash } : {},
|
|
187862
|
-
createdAt: row.createdAt
|
|
187863
|
-
};
|
|
187864
|
-
}
|
|
187865
|
-
function rowToEvent2(row) {
|
|
187866
|
-
return {
|
|
187867
|
-
id: row.id,
|
|
187868
|
-
memoryId: row.memoryId,
|
|
187869
|
-
kind: row.kind,
|
|
187870
|
-
...optionalText6(row.fromState) ? { fromState: row.fromState } : {},
|
|
187871
|
-
...optionalText6(row.toState) ? { toState: row.toState } : {},
|
|
187872
|
-
...optionalText6(row.detail) ? { detail: row.detail } : {},
|
|
187873
|
-
createdAt: row.createdAt
|
|
187874
|
-
};
|
|
187875
|
-
}
|
|
187876
|
-
var LongTermMemoryStore;
|
|
187877
|
-
var init_long_term_store = __esm({
|
|
187878
|
-
"src/memory/long-term-store.ts"() {
|
|
187879
|
-
"use strict";
|
|
187880
|
-
init_esm_shims();
|
|
187881
|
-
init_sqlite_db();
|
|
187882
|
-
LongTermMemoryStore = class {
|
|
187883
|
-
db = null;
|
|
187884
|
-
async init(dataDir) {
|
|
187885
|
-
this.db = getDatabase(dataDir);
|
|
187886
|
-
}
|
|
187887
|
-
transaction(fn2) {
|
|
187888
|
-
return this.db.transaction(fn2)();
|
|
187889
|
-
}
|
|
187890
|
-
get(id) {
|
|
187891
|
-
const row = this.db.prepare("SELECT * FROM long_term_memories WHERE id = ?").get(id);
|
|
187892
|
-
return row ? rowToMemory(row) : void 0;
|
|
187893
|
-
}
|
|
187894
|
-
list(options = {}) {
|
|
187895
|
-
const limit2 = Math.max(1, Math.min(1e3, Math.floor(options.limit ?? 500)));
|
|
187896
|
-
const clauses = [];
|
|
187897
|
-
const params = [];
|
|
187898
|
-
if (options.ownerId) {
|
|
187899
|
-
clauses.push("ownerId = ?");
|
|
187900
|
-
params.push(options.ownerId);
|
|
187901
|
-
}
|
|
187902
|
-
if (options.originProjectId) {
|
|
187903
|
-
clauses.push("originProjectId = ?");
|
|
187904
|
-
params.push(options.originProjectId);
|
|
187905
|
-
}
|
|
187906
|
-
const where = clauses.length ? " WHERE " + clauses.join(" AND ") : "";
|
|
187907
|
-
const rows = this.db.prepare(
|
|
187908
|
-
"SELECT * FROM long_term_memories" + where + " ORDER BY updatedAt DESC, id LIMIT ?"
|
|
187909
|
-
).all(...params, limit2);
|
|
187910
|
-
return rows.map(rowToMemory);
|
|
187911
|
-
}
|
|
187912
|
-
insert(memory) {
|
|
187913
|
-
this.db.prepare(`
|
|
187914
|
-
INSERT INTO long_term_memories (
|
|
187915
|
-
id, originProjectId, ownerId, scope, kind, state, portability,
|
|
187916
|
-
title, content, factsJson, tagsJson, applicability, origin,
|
|
187917
|
-
createdAt, updatedAt, qualifiedAt, approvedAt, archivedAt,
|
|
187918
|
-
supersededBy, lastValidatedAt, accessCount, lastAccessedAt
|
|
187919
|
-
) VALUES (
|
|
187920
|
-
@id, @originProjectId, @ownerId, @scope, @kind, @state, @portability,
|
|
187921
|
-
@title, @content, @factsJson, @tagsJson, @applicability, @origin,
|
|
187922
|
-
@createdAt, @updatedAt, @qualifiedAt, @approvedAt, @archivedAt,
|
|
187923
|
-
@supersededBy, @lastValidatedAt, @accessCount, @lastAccessedAt
|
|
187924
|
-
)
|
|
187925
|
-
`).run({
|
|
187926
|
-
...memory,
|
|
187927
|
-
factsJson: JSON.stringify(memory.facts),
|
|
187928
|
-
tagsJson: JSON.stringify(memory.tags),
|
|
187929
|
-
applicability: memory.applicability ?? null,
|
|
187930
|
-
qualifiedAt: memory.qualifiedAt ?? null,
|
|
187931
|
-
approvedAt: memory.approvedAt ?? null,
|
|
187932
|
-
archivedAt: memory.archivedAt ?? null,
|
|
187933
|
-
supersededBy: memory.supersededBy ?? null,
|
|
187934
|
-
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
187935
|
-
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
187936
|
-
});
|
|
187937
|
-
}
|
|
187938
|
-
update(memory) {
|
|
187939
|
-
this.db.prepare(`
|
|
187940
|
-
UPDATE long_term_memories SET
|
|
187941
|
-
scope = @scope,
|
|
187942
|
-
kind = @kind,
|
|
187943
|
-
state = @state,
|
|
187944
|
-
portability = @portability,
|
|
187945
|
-
title = @title,
|
|
187946
|
-
content = @content,
|
|
187947
|
-
factsJson = @factsJson,
|
|
187948
|
-
tagsJson = @tagsJson,
|
|
187949
|
-
applicability = @applicability,
|
|
187950
|
-
updatedAt = @updatedAt,
|
|
187951
|
-
qualifiedAt = @qualifiedAt,
|
|
187952
|
-
approvedAt = @approvedAt,
|
|
187953
|
-
archivedAt = @archivedAt,
|
|
187954
|
-
supersededBy = @supersededBy,
|
|
187955
|
-
lastValidatedAt = @lastValidatedAt,
|
|
187956
|
-
accessCount = @accessCount,
|
|
187957
|
-
lastAccessedAt = @lastAccessedAt
|
|
187958
|
-
WHERE id = @id
|
|
187959
|
-
`).run({
|
|
187960
|
-
...memory,
|
|
187961
|
-
factsJson: JSON.stringify(memory.facts),
|
|
187962
|
-
tagsJson: JSON.stringify(memory.tags),
|
|
187963
|
-
applicability: memory.applicability ?? null,
|
|
187964
|
-
qualifiedAt: memory.qualifiedAt ?? null,
|
|
187965
|
-
approvedAt: memory.approvedAt ?? null,
|
|
187966
|
-
archivedAt: memory.archivedAt ?? null,
|
|
187967
|
-
supersededBy: memory.supersededBy ?? null,
|
|
187968
|
-
lastValidatedAt: memory.lastValidatedAt ?? null,
|
|
187969
|
-
lastAccessedAt: memory.lastAccessedAt ?? null
|
|
187970
|
-
});
|
|
187971
|
-
}
|
|
187972
|
-
insertEvidence(input) {
|
|
187973
|
-
const evidence = {
|
|
187974
|
-
id: randomUUID13(),
|
|
187975
|
-
...input,
|
|
187976
|
-
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
187977
|
-
};
|
|
187978
|
-
this.db.prepare(`
|
|
187979
|
-
INSERT INTO long_term_memory_evidence (
|
|
187980
|
-
id, memoryId, kind, referenceId, relation, locator, capturedHash, createdAt
|
|
187981
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
187982
|
-
`).run(
|
|
187983
|
-
evidence.id,
|
|
187984
|
-
evidence.memoryId,
|
|
187985
|
-
evidence.kind,
|
|
187986
|
-
evidence.referenceId,
|
|
187987
|
-
evidence.relation,
|
|
187988
|
-
evidence.locator ?? null,
|
|
187989
|
-
evidence.capturedHash ?? null,
|
|
187990
|
-
evidence.createdAt
|
|
187991
|
-
);
|
|
187992
|
-
return evidence;
|
|
187993
|
-
}
|
|
187994
|
-
listEvidence(memoryId) {
|
|
187995
|
-
return this.db.prepare(`
|
|
187996
|
-
SELECT * FROM long_term_memory_evidence WHERE memoryId = ? ORDER BY createdAt ASC, id
|
|
187997
|
-
`).all(memoryId).map(rowToEvidence2);
|
|
187998
|
-
}
|
|
187999
|
-
recordEvent(input) {
|
|
188000
|
-
const event = {
|
|
188001
|
-
id: randomUUID13(),
|
|
188002
|
-
...input,
|
|
188003
|
-
createdAt: input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
188004
|
-
};
|
|
188005
|
-
this.db.prepare(`
|
|
188006
|
-
INSERT INTO long_term_memory_events (id, memoryId, kind, fromState, toState, detail, createdAt)
|
|
188007
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
188008
|
-
`).run(
|
|
188009
|
-
event.id,
|
|
188010
|
-
event.memoryId,
|
|
188011
|
-
event.kind,
|
|
188012
|
-
event.fromState ?? null,
|
|
188013
|
-
event.toState ?? null,
|
|
188014
|
-
event.detail ?? null,
|
|
188015
|
-
event.createdAt
|
|
188016
|
-
);
|
|
188017
|
-
return event;
|
|
188018
|
-
}
|
|
188019
|
-
listEvents(memoryId) {
|
|
188020
|
-
return this.db.prepare(`
|
|
188021
|
-
SELECT * FROM long_term_memory_events WHERE memoryId = ? ORDER BY createdAt ASC, rowid ASC
|
|
188022
|
-
`).all(memoryId).map(rowToEvent2);
|
|
188023
|
-
}
|
|
188024
|
-
markAccess(ids, at2 = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
188025
|
-
const unique3 = [...new Set(ids.filter(Boolean))];
|
|
188026
|
-
if (unique3.length === 0) return;
|
|
188027
|
-
const statement = this.db.prepare(`
|
|
188028
|
-
UPDATE long_term_memories
|
|
188029
|
-
SET accessCount = accessCount + 1, lastAccessedAt = ?
|
|
188030
|
-
WHERE id = ?
|
|
188031
|
-
`);
|
|
188032
|
-
this.db.transaction(() => {
|
|
188033
|
-
for (const id of unique3) statement.run(at2, id);
|
|
188034
|
-
})();
|
|
188035
|
-
}
|
|
188036
|
-
};
|
|
188037
|
-
}
|
|
188038
|
-
});
|
|
188039
|
-
|
|
188040
|
-
// src/memory/long-term-types.ts
|
|
188041
|
-
var LONG_TERM_MEMORY_KINDS, LONG_TERM_MEMORY_SCOPES, LONG_TERM_MEMORY_PORTABILITIES, LONG_TERM_MEMORY_EVIDENCE_KINDS, LONG_TERM_MEMORY_EVIDENCE_RELATIONS;
|
|
188042
|
-
var init_long_term_types = __esm({
|
|
188043
|
-
"src/memory/long-term-types.ts"() {
|
|
188044
|
-
"use strict";
|
|
188045
|
-
init_esm_shims();
|
|
188046
|
-
LONG_TERM_MEMORY_KINDS = ["episodic", "semantic", "procedural"];
|
|
188047
|
-
LONG_TERM_MEMORY_SCOPES = ["project", "user", "team"];
|
|
188048
|
-
LONG_TERM_MEMORY_PORTABILITIES = ["project-bound", "portable"];
|
|
188049
|
-
LONG_TERM_MEMORY_EVIDENCE_KINDS = [
|
|
188050
|
-
"observation",
|
|
188051
|
-
"claim",
|
|
188052
|
-
"workflow",
|
|
188053
|
-
"session",
|
|
188054
|
-
"checkpoint",
|
|
188055
|
-
"git",
|
|
188056
|
-
"code",
|
|
188057
|
-
"test",
|
|
188058
|
-
"run",
|
|
188059
|
-
"document",
|
|
188060
|
-
"manual",
|
|
188061
|
-
"user"
|
|
188062
|
-
];
|
|
188063
|
-
LONG_TERM_MEMORY_EVIDENCE_RELATIONS = [
|
|
188064
|
-
"supports",
|
|
188065
|
-
"verifies",
|
|
188066
|
-
"derives",
|
|
188067
|
-
"user-confirmed"
|
|
188068
|
-
];
|
|
188069
|
-
}
|
|
188070
|
-
});
|
|
188071
|
-
|
|
188072
|
-
// src/memory/long-term.ts
|
|
188073
|
-
var long_term_exports = {};
|
|
188074
|
-
__export(long_term_exports, {
|
|
188075
|
-
approveLongTermMemory: () => approveLongTermMemory,
|
|
188076
|
-
archiveLongTermMemory: () => archiveLongTermMemory,
|
|
188077
|
-
canReadLongTermMemory: () => canReadLongTermMemory,
|
|
188078
|
-
createLongTermMemoryCandidate: () => createLongTermMemoryCandidate,
|
|
188079
|
-
createManualLongTermMemory: () => createManualLongTermMemory,
|
|
188080
|
-
getLongTermMemoryDetail: () => getLongTermMemoryDetail,
|
|
188081
|
-
isEligibleLongTermMemory: () => isEligibleLongTermMemory,
|
|
188082
|
-
listLongTermMemories: () => listLongTermMemories,
|
|
188083
|
-
promoteObservationToLongTermMemory: () => promoteObservationToLongTermMemory,
|
|
188084
|
-
qualifyLongTermMemory: () => qualifyLongTermMemory,
|
|
188085
|
-
renderLongTermMemorySummary: () => renderLongTermMemorySummary,
|
|
188086
|
-
selectLongTermMemoriesForTask: () => selectLongTermMemoriesForTask,
|
|
188087
|
-
supersedeLongTermMemory: () => supersedeLongTermMemory
|
|
188088
|
-
});
|
|
188089
|
-
import { randomUUID as randomUUID14 } from "crypto";
|
|
188090
|
-
function isOneOf(value, values) {
|
|
188091
|
-
return typeof value === "string" && values.includes(value);
|
|
188092
|
-
}
|
|
188093
|
-
function compactText2(value, field, max = MAX_TEXT_LENGTH) {
|
|
188094
|
-
if (typeof value !== "string") throw new Error("Long-term memory " + field + " must be text.");
|
|
188095
|
-
const safe = sanitizeCredentials(value).replace(/\s+/g, " ").trim();
|
|
188096
|
-
if (!safe) throw new Error("Long-term memory " + field + " is required.");
|
|
188097
|
-
if (safe.length > max) throw new Error("Long-term memory " + field + " is too long.");
|
|
188098
|
-
return safe;
|
|
188099
|
-
}
|
|
188100
|
-
function compactList(values, field) {
|
|
188101
|
-
if (!values) return [];
|
|
188102
|
-
if (!Array.isArray(values)) throw new Error("Long-term memory " + field + " must be an array.");
|
|
188103
|
-
return [...new Set(values.filter((value) => typeof value === "string").map((value) => compactText2(value, field, 500)))].slice(0, MAX_LIST_ITEMS);
|
|
188104
|
-
}
|
|
188105
|
-
function normalizeEvidence(input) {
|
|
188106
|
-
if (!Array.isArray(input) || input.length === 0) {
|
|
188107
|
-
throw new Error("A long-term memory requires at least one source evidence reference.");
|
|
188108
|
-
}
|
|
188109
|
-
return input.map((evidence) => {
|
|
188110
|
-
if (!evidence || !isOneOf(evidence.kind, LONG_TERM_MEMORY_EVIDENCE_KINDS)) {
|
|
188111
|
-
throw new Error("Long-term memory evidence kind is invalid.");
|
|
188112
|
-
}
|
|
188113
|
-
if (!isOneOf(evidence.relation, LONG_TERM_MEMORY_EVIDENCE_RELATIONS)) {
|
|
188114
|
-
throw new Error("Long-term memory evidence relation is invalid.");
|
|
188115
|
-
}
|
|
188116
|
-
return {
|
|
188117
|
-
kind: evidence.kind,
|
|
188118
|
-
referenceId: compactText2(evidence.referenceId, "evidence reference", 1e3),
|
|
188119
|
-
relation: evidence.relation,
|
|
188120
|
-
...evidence.locator ? { locator: compactText2(evidence.locator, "evidence locator", 1e3) } : {},
|
|
188121
|
-
...evidence.capturedHash ? { capturedHash: compactText2(evidence.capturedHash, "evidence hash", 1e3) } : {}
|
|
188122
|
-
};
|
|
188123
|
-
});
|
|
188124
|
-
}
|
|
188125
|
-
function assertScope(scope) {
|
|
188126
|
-
if (!isOneOf(scope, LONG_TERM_MEMORY_SCOPES)) throw new Error("Long-term memory scope is invalid.");
|
|
188127
|
-
}
|
|
188128
|
-
function assertKind(kind) {
|
|
188129
|
-
if (!isOneOf(kind, LONG_TERM_MEMORY_KINDS)) throw new Error("Long-term memory kind is invalid.");
|
|
188130
|
-
}
|
|
188131
|
-
function assertPortability(value) {
|
|
188132
|
-
if (!isOneOf(value, LONG_TERM_MEMORY_PORTABILITIES)) {
|
|
188133
|
-
throw new Error("Long-term memory portability is invalid.");
|
|
188134
|
-
}
|
|
188135
|
-
}
|
|
188136
|
-
function assertPortableEvidence(scope, portability, evidence) {
|
|
188137
|
-
if (portability !== "portable") return;
|
|
188138
|
-
if (scope !== "user") {
|
|
188139
|
-
throw new Error("Only user-scoped long-term memories may be portable across projects.");
|
|
188140
|
-
}
|
|
188141
|
-
if (evidence.some((item) => !PORTABLE_EVIDENCE_KINDS.has(item.kind))) {
|
|
188142
|
-
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.");
|
|
188143
|
-
}
|
|
188144
|
-
}
|
|
188145
|
-
function requireTeamReader(scope, reader) {
|
|
188146
|
-
if (scope === "team" && reader?.isTeamMember !== true) {
|
|
188147
|
-
throw new Error("Team long-term memory requires an explicit active team identity for this project.");
|
|
188148
|
-
}
|
|
188149
|
-
}
|
|
188150
|
-
function matchesScope(memory, reader) {
|
|
188151
|
-
if (memory.scope === "project") return memory.originProjectId === reader.projectId;
|
|
188152
|
-
if (memory.scope === "team") {
|
|
188153
|
-
return memory.originProjectId === reader.projectId && reader.isTeamMember === true;
|
|
188154
|
-
}
|
|
188155
|
-
if (!reader.ownerId || memory.ownerId !== reader.ownerId) return false;
|
|
188156
|
-
return memory.portability === "portable" || memory.originProjectId === reader.projectId;
|
|
188157
|
-
}
|
|
188158
|
-
function canReadLongTermMemory(memory, reader) {
|
|
188159
|
-
return matchesScope(memory, reader);
|
|
188160
|
-
}
|
|
188161
|
-
function isEligibleLongTermMemory(memory) {
|
|
188162
|
-
return memory.state === "qualified" || memory.state === "approved";
|
|
188163
|
-
}
|
|
188164
|
-
function taskTerms(task) {
|
|
188165
|
-
const matches = task.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}_./:-]+/gu) ?? [];
|
|
188166
|
-
return [...new Set(matches.filter((term) => term.length > 1 && !STOP_TERMS.has(term)))].slice(0, 16);
|
|
188167
|
-
}
|
|
188168
|
-
function kindFitScore(kind, task) {
|
|
188169
|
-
const normalized = task.toLocaleLowerCase("en-US");
|
|
188170
|
-
const hints = {
|
|
188171
|
-
episodic: ["resume", "continue", "handoff", "previous", "session", "\u63A5\u624B", "\u7EE7\u7EED", "\u4EA4\u63A5", "\u4E0A\u6B21", "\u4F1A\u8BDD"],
|
|
188172
|
-
semantic: ["why", "what", "architecture", "design", "decision", "reason", "\u4E3A\u4EC0\u4E48", "\u662F\u4EC0\u4E48", "\u67B6\u6784", "\u8BBE\u8BA1", "\u51B3\u7B56", "\u539F\u56E0"],
|
|
188173
|
-
procedural: ["how", "steps", "procedure", "run", "release", "publish", "verify", "implement", "\u6D41\u7A0B", "\u6B65\u9AA4", "\u53D1\u5E03", "\u9A8C\u8BC1", "\u4FEE\u590D", "\u5B9E\u73B0"]
|
|
188174
|
-
};
|
|
188175
|
-
return hints[kind].some((hint) => normalized.includes(hint)) ? 1 : 0;
|
|
188176
|
-
}
|
|
188177
|
-
function validationFreshnessScore(memory) {
|
|
188178
|
-
const validatedAt = memory.lastValidatedAt ?? memory.approvedAt ?? memory.qualifiedAt;
|
|
188179
|
-
if (!validatedAt) return 0;
|
|
188180
|
-
const time3 = Date.parse(validatedAt);
|
|
188181
|
-
if (!Number.isFinite(time3)) return 0;
|
|
188182
|
-
const ageDays = Math.max(0, Date.now() - time3) / (24 * 60 * 60 * 1e3);
|
|
188183
|
-
if (ageDays <= 30) return 0.35;
|
|
188184
|
-
if (ageDays <= 90) return 0.15;
|
|
188185
|
-
return 0;
|
|
188186
|
-
}
|
|
188187
|
-
function scoreForTask(memory, task) {
|
|
188188
|
-
const terms = taskTerms(task);
|
|
188189
|
-
if (terms.length === 0) return void 0;
|
|
188190
|
-
const title = memory.title.toLocaleLowerCase("en-US");
|
|
188191
|
-
const content = memory.content.toLocaleLowerCase("en-US");
|
|
188192
|
-
const facts = memory.facts.join(" ").toLocaleLowerCase("en-US");
|
|
188193
|
-
const tags = memory.tags.join(" ").toLocaleLowerCase("en-US");
|
|
188194
|
-
const applicability = (memory.applicability ?? "").toLocaleLowerCase("en-US");
|
|
188195
|
-
let score = memory.state === "approved" ? 0.5 : 0;
|
|
188196
|
-
const matched = [];
|
|
188197
|
-
for (const term of terms) {
|
|
188198
|
-
let termScore = 0;
|
|
188199
|
-
if (title.includes(term)) termScore += 5;
|
|
188200
|
-
if (tags.includes(term)) termScore += 4;
|
|
188201
|
-
if (applicability.includes(term)) termScore += 3;
|
|
188202
|
-
if (facts.includes(term)) termScore += 2;
|
|
188203
|
-
if (content.includes(term)) termScore += 1;
|
|
188204
|
-
if (termScore > 0) {
|
|
188205
|
-
score += termScore;
|
|
188206
|
-
matched.push(term);
|
|
188207
|
-
}
|
|
188208
|
-
}
|
|
188209
|
-
if (matched.length === 0) return void 0;
|
|
188210
|
-
score += kindFitScore(memory.kind, task);
|
|
188211
|
-
score += validationFreshnessScore(memory);
|
|
188212
|
-
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
188213
|
-
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
188214
|
-
return { score, reason: kindLabel + " matches " + matched.slice(0, 3).join(", ") + freshness };
|
|
188215
|
-
}
|
|
188216
|
-
function semanticText(memory) {
|
|
188217
|
-
return sanitizeCredentials([
|
|
188218
|
-
memory.title,
|
|
188219
|
-
memory.applicability,
|
|
188220
|
-
memory.tags.join(" "),
|
|
188221
|
-
memory.facts.join(" "),
|
|
188222
|
-
memory.content
|
|
188223
|
-
].filter((value) => Boolean(value)).join("\n")).replace(/\s+/g, " ").trim().slice(0, MAX_TEXT_LENGTH);
|
|
188224
|
-
}
|
|
188225
|
-
function cosineSimilarity(left, right) {
|
|
188226
|
-
if (left.length === 0 || left.length !== right.length) return 0;
|
|
188227
|
-
let dot = 0;
|
|
188228
|
-
let leftMagnitude = 0;
|
|
188229
|
-
let rightMagnitude = 0;
|
|
188230
|
-
for (let index = 0; index < left.length; index += 1) {
|
|
188231
|
-
const a4 = left[index];
|
|
188232
|
-
const b4 = right[index];
|
|
188233
|
-
if (!Number.isFinite(a4) || !Number.isFinite(b4)) return 0;
|
|
188234
|
-
dot += a4 * b4;
|
|
188235
|
-
leftMagnitude += a4 * a4;
|
|
188236
|
-
rightMagnitude += b4 * b4;
|
|
188237
|
-
}
|
|
188238
|
-
if (leftMagnitude === 0 || rightMagnitude === 0) return 0;
|
|
188239
|
-
return dot / Math.sqrt(leftMagnitude * rightMagnitude);
|
|
188240
|
-
}
|
|
188241
|
-
function semanticSelection(memory, similarity, task) {
|
|
188242
|
-
if (similarity < MIN_SEMANTIC_SIMILARITY) return void 0;
|
|
188243
|
-
const kindLabel = memory.kind === "procedural" ? "procedure" : memory.kind === "semantic" ? "fact" : "episode";
|
|
188244
|
-
const freshness = validationFreshnessScore(memory) > 0 ? "; recently validated" : "";
|
|
188245
|
-
return {
|
|
188246
|
-
score: similarity * 10 + (memory.state === "approved" ? 0.5 : 0) + kindFitScore(memory.kind, task) + validationFreshnessScore(memory),
|
|
188247
|
-
reason: kindLabel + " semantic match " + similarity.toFixed(2) + freshness
|
|
188248
|
-
};
|
|
188249
|
-
}
|
|
188250
|
-
async function resolveSemanticProvider(explicit) {
|
|
188251
|
-
if (explicit !== void 0) return explicit;
|
|
188252
|
-
try {
|
|
188253
|
-
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
188254
|
-
return await withTimeout(
|
|
188255
|
-
getEmbeddingProvider2({ requestTimeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS, retry: false }),
|
|
188256
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
188257
|
-
"Long-term semantic provider initialization"
|
|
188258
|
-
);
|
|
188259
|
-
} catch {
|
|
188260
|
-
return null;
|
|
188261
|
-
}
|
|
188262
|
-
}
|
|
188263
|
-
function createStore(dataDir) {
|
|
188264
|
-
const store2 = new LongTermMemoryStore();
|
|
188265
|
-
return store2.init(dataDir).then(() => store2);
|
|
188266
|
-
}
|
|
188267
|
-
async function createManualLongTermMemory(input) {
|
|
188268
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
188269
|
-
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
188270
|
-
assertScope(input.scope);
|
|
188271
|
-
const evidence = input.scope === "user" ? [{ kind: "user", referenceId: owner.id, relation: "user-confirmed" }] : [{ kind: "manual", referenceId: "manual:" + randomUUID14(), relation: "supports" }];
|
|
188272
|
-
return createLongTermMemoryWithDataDir({
|
|
188273
|
-
dataDir: input.dataDir,
|
|
188274
|
-
originProjectId: input.projectId,
|
|
188275
|
-
ownerId: owner.id,
|
|
188276
|
-
scope: input.scope,
|
|
188277
|
-
kind: input.kind,
|
|
188278
|
-
portability: input.portability,
|
|
188279
|
-
title: input.title,
|
|
188280
|
-
content: input.content,
|
|
188281
|
-
facts: input.facts,
|
|
188282
|
-
tags: input.tags,
|
|
188283
|
-
applicability: input.applicability,
|
|
188284
|
-
origin: "manual",
|
|
188285
|
-
evidence,
|
|
188286
|
-
reader: input.reader
|
|
188287
|
-
});
|
|
188288
|
-
}
|
|
188289
|
-
async function promoteObservationToLongTermMemory(input) {
|
|
188290
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: true });
|
|
188291
|
-
if (!owner) throw new Error("Memorix could not resolve a local memory owner.");
|
|
188292
|
-
return createLongTermMemoryWithDataDir({
|
|
188293
|
-
dataDir: input.dataDir,
|
|
188294
|
-
originProjectId: input.observation.projectId,
|
|
188295
|
-
ownerId: owner.id,
|
|
188296
|
-
scope: input.scope,
|
|
188297
|
-
kind: input.kind,
|
|
188298
|
-
portability: "project-bound",
|
|
188299
|
-
title: input.observation.title,
|
|
188300
|
-
content: input.observation.narrative,
|
|
188301
|
-
facts: input.observation.facts,
|
|
188302
|
-
tags: input.tags ?? input.observation.concepts,
|
|
188303
|
-
applicability: input.applicability,
|
|
188304
|
-
origin: input.observation.source === "git" ? "git" : "agent",
|
|
188305
|
-
evidence: [{
|
|
188306
|
-
kind: "observation",
|
|
188307
|
-
referenceId: "obs:" + input.observation.projectId + ":" + input.observation.id,
|
|
188308
|
-
relation: "derives"
|
|
188309
|
-
}],
|
|
188310
|
-
reader: input.reader
|
|
188311
|
-
});
|
|
188312
|
-
}
|
|
188313
|
-
async function createLongTermMemoryCandidate(input) {
|
|
188314
|
-
return createLongTermMemoryWithDataDir(input);
|
|
188315
|
-
}
|
|
188316
|
-
async function createLongTermMemoryWithDataDir(input) {
|
|
188317
|
-
assertScope(input.scope);
|
|
188318
|
-
assertKind(input.kind);
|
|
188319
|
-
const portability = input.portability ?? "project-bound";
|
|
188320
|
-
assertPortability(portability);
|
|
188321
|
-
requireTeamReader(input.scope, input.reader);
|
|
188322
|
-
const evidenceInputs = normalizeEvidence(input.evidence);
|
|
188323
|
-
assertPortableEvidence(input.scope, portability, evidenceInputs);
|
|
188324
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
188325
|
-
const memory = {
|
|
188326
|
-
id: randomUUID14(),
|
|
188327
|
-
originProjectId: compactText2(input.originProjectId, "origin project id", 1e3),
|
|
188328
|
-
ownerId: compactText2(input.ownerId, "owner id", 160),
|
|
188329
|
-
scope: input.scope,
|
|
188330
|
-
kind: input.kind,
|
|
188331
|
-
state: "candidate",
|
|
188332
|
-
portability,
|
|
188333
|
-
title: compactText2(input.title, "title", 500),
|
|
188334
|
-
content: compactText2(input.content, "content"),
|
|
188335
|
-
facts: compactList(input.facts, "facts"),
|
|
188336
|
-
tags: compactList(input.tags, "tags"),
|
|
188337
|
-
...input.applicability ? { applicability: compactText2(input.applicability, "applicability", 1e3) } : {},
|
|
188338
|
-
origin: input.origin ?? "manual",
|
|
188339
|
-
createdAt: now3,
|
|
188340
|
-
updatedAt: now3,
|
|
188341
|
-
accessCount: 0
|
|
188342
|
-
};
|
|
188343
|
-
const store2 = await createStore(input.dataDir);
|
|
188344
|
-
const evidence = store2.transaction(() => {
|
|
188345
|
-
store2.insert(memory);
|
|
188346
|
-
const written = evidenceInputs.map((item) => store2.insertEvidence({ memoryId: memory.id, ...item, createdAt: now3 }));
|
|
188347
|
-
store2.recordEvent({
|
|
188348
|
-
memoryId: memory.id,
|
|
188349
|
-
kind: "created",
|
|
188350
|
-
toState: "candidate",
|
|
188351
|
-
detail: "Created as a source-backed long-term memory candidate.",
|
|
188352
|
-
createdAt: now3
|
|
188353
|
-
});
|
|
188354
|
-
return written;
|
|
188355
|
-
});
|
|
188356
|
-
return { memory, evidence };
|
|
188357
|
-
}
|
|
188358
|
-
function transitionDetail(value) {
|
|
188359
|
-
return compactText2(value, "transition reason", 1e3);
|
|
188360
|
-
}
|
|
188361
|
-
async function transitionMemory(input) {
|
|
188362
|
-
const store2 = await createStore(input.dataDir);
|
|
188363
|
-
return store2.transaction(() => {
|
|
188364
|
-
const current = store2.get(input.id);
|
|
188365
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
188366
|
-
if (current.state !== input.expected) {
|
|
188367
|
-
throw new Error("Long-term memory must be " + input.expected + " before it can be " + input.next + ".");
|
|
188368
|
-
}
|
|
188369
|
-
const evidence = store2.listEvidence(current.id);
|
|
188370
|
-
if (evidence.length === 0) throw new Error("Long-term memory cannot advance without source evidence.");
|
|
188371
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
188372
|
-
const next = {
|
|
188373
|
-
...current,
|
|
188374
|
-
state: input.next,
|
|
188375
|
-
updatedAt: now3,
|
|
188376
|
-
lastValidatedAt: now3,
|
|
188377
|
-
...input.next === "qualified" ? { qualifiedAt: now3 } : { approvedAt: now3 }
|
|
188378
|
-
};
|
|
188379
|
-
store2.update(next);
|
|
188380
|
-
store2.recordEvent({
|
|
188381
|
-
memoryId: next.id,
|
|
188382
|
-
kind: input.event,
|
|
188383
|
-
fromState: current.state,
|
|
188384
|
-
toState: next.state,
|
|
188385
|
-
detail: transitionDetail(input.reason),
|
|
188386
|
-
createdAt: now3
|
|
188387
|
-
});
|
|
188388
|
-
return next;
|
|
188389
|
-
});
|
|
188390
|
-
}
|
|
188391
|
-
async function qualifyLongTermMemory(input) {
|
|
188392
|
-
return transitionMemory({ ...input, expected: "candidate", next: "qualified", event: "qualified" });
|
|
188393
|
-
}
|
|
188394
|
-
async function approveLongTermMemory(input) {
|
|
188395
|
-
return transitionMemory({ ...input, expected: "qualified", next: "approved", event: "approved" });
|
|
188396
|
-
}
|
|
188397
|
-
async function archiveLongTermMemory(input) {
|
|
188398
|
-
const store2 = await createStore(input.dataDir);
|
|
188399
|
-
return store2.transaction(() => {
|
|
188400
|
-
const current = store2.get(input.id);
|
|
188401
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
188402
|
-
if (current.state === "archived" || current.state === "superseded") {
|
|
188403
|
-
throw new Error("Long-term memory is already inactive.");
|
|
188404
|
-
}
|
|
188405
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
188406
|
-
const next = { ...current, state: "archived", archivedAt: now3, updatedAt: now3 };
|
|
188407
|
-
store2.update(next);
|
|
188408
|
-
store2.recordEvent({
|
|
188409
|
-
memoryId: next.id,
|
|
188410
|
-
kind: "archived",
|
|
188411
|
-
fromState: current.state,
|
|
188412
|
-
toState: "archived",
|
|
188413
|
-
detail: transitionDetail(input.reason),
|
|
188414
|
-
createdAt: now3
|
|
188415
|
-
});
|
|
188416
|
-
return next;
|
|
188417
|
-
});
|
|
188418
|
-
}
|
|
188419
|
-
async function supersedeLongTermMemory(input) {
|
|
188420
|
-
const store2 = await createStore(input.dataDir);
|
|
188421
|
-
return store2.transaction(() => {
|
|
188422
|
-
const current = store2.get(input.id);
|
|
188423
|
-
if (!current) throw new Error("Long-term memory was not found.");
|
|
188424
|
-
if (current.state === "archived" || current.state === "superseded") {
|
|
188425
|
-
throw new Error("Long-term memory is already inactive.");
|
|
188426
|
-
}
|
|
188427
|
-
if (current.id === input.supersededBy) {
|
|
188428
|
-
throw new Error("Long-term memory cannot supersede itself.");
|
|
188429
|
-
}
|
|
188430
|
-
const replacement = store2.get(input.supersededBy);
|
|
188431
|
-
if (!replacement || !isEligibleLongTermMemory(replacement)) {
|
|
188432
|
-
throw new Error("Replacement long-term memory must be qualified or approved.");
|
|
188433
|
-
}
|
|
188434
|
-
if (replacement.scope !== current.scope) {
|
|
188435
|
-
throw new Error("Replacement long-term memory must use the same scope.");
|
|
188436
|
-
}
|
|
188437
|
-
if (current.scope === "user" && replacement.ownerId !== current.ownerId) {
|
|
188438
|
-
throw new Error("User long-term memory may only be superseded by the same local owner.");
|
|
188439
|
-
}
|
|
188440
|
-
if (current.scope !== "user" && replacement.originProjectId !== current.originProjectId) {
|
|
188441
|
-
throw new Error("Project and team long-term memory may only be superseded within the same project.");
|
|
188442
|
-
}
|
|
188443
|
-
const now3 = (/* @__PURE__ */ new Date()).toISOString();
|
|
188444
|
-
const next = {
|
|
188445
|
-
...current,
|
|
188446
|
-
state: "superseded",
|
|
188447
|
-
supersededBy: replacement.id,
|
|
188448
|
-
updatedAt: now3
|
|
188449
|
-
};
|
|
188450
|
-
store2.update(next);
|
|
188451
|
-
store2.recordEvent({
|
|
188452
|
-
memoryId: next.id,
|
|
188453
|
-
kind: "superseded",
|
|
188454
|
-
fromState: current.state,
|
|
188455
|
-
toState: "superseded",
|
|
188456
|
-
detail: transitionDetail(input.reason),
|
|
188457
|
-
createdAt: now3
|
|
188458
|
-
});
|
|
188459
|
-
return next;
|
|
188460
|
-
});
|
|
188461
|
-
}
|
|
188462
|
-
async function listLongTermMemories(input) {
|
|
188463
|
-
const store2 = await createStore(input.dataDir);
|
|
188464
|
-
return store2.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: store2.listEvidence(memory.id) }));
|
|
188465
|
-
}
|
|
188466
|
-
async function getLongTermMemoryDetail(input) {
|
|
188467
|
-
const store2 = await createStore(input.dataDir);
|
|
188468
|
-
const memory = store2.get(input.id);
|
|
188469
|
-
if (!memory || !canReadLongTermMemory(memory, input.reader)) {
|
|
188470
|
-
throw new Error("Long-term memory was not found in the active scope.");
|
|
188471
|
-
}
|
|
188472
|
-
store2.markAccess([memory.id]);
|
|
188473
|
-
return { memory, evidence: store2.listEvidence(memory.id), events: store2.listEvents(memory.id) };
|
|
188474
|
-
}
|
|
188475
|
-
async function selectLongTermMemoriesForTask(input) {
|
|
188476
|
-
const task = input.task?.trim();
|
|
188477
|
-
if (!task) return [];
|
|
188478
|
-
const owner = await resolveLocalMemoryOwner(input.dataDir, { create: false });
|
|
188479
|
-
const reader = {
|
|
188480
|
-
projectId: input.projectId,
|
|
188481
|
-
...owner ? { ownerId: owner.id } : {},
|
|
188482
|
-
...input.agentId ? { agentId: input.agentId } : {},
|
|
188483
|
-
...input.isTeamMember ? { isTeamMember: true } : {}
|
|
188484
|
-
};
|
|
188485
|
-
const store2 = await createStore(input.dataDir);
|
|
188486
|
-
const eligible = store2.list({ limit: 1e3 }).filter((memory) => isEligibleLongTermMemory(memory) && canReadLongTermMemory(memory, reader));
|
|
188487
|
-
let selected = eligible.map((memory) => {
|
|
188488
|
-
const ranked = scoreForTask(memory, task);
|
|
188489
|
-
return ranked ? {
|
|
188490
|
-
memory,
|
|
188491
|
-
evidence: store2.listEvidence(memory.id),
|
|
188492
|
-
score: ranked.score,
|
|
188493
|
-
reason: ranked.reason
|
|
188494
|
-
} : void 0;
|
|
188495
|
-
}).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));
|
|
188496
|
-
if (selected.length === 0 && eligible.length > 0) {
|
|
188497
|
-
const provider2 = await resolveSemanticProvider(input.embeddingProvider);
|
|
188498
|
-
const candidates = eligible.slice(0, MAX_SEMANTIC_CANDIDATES);
|
|
188499
|
-
if (provider2 && candidates.length > 0) {
|
|
188500
|
-
try {
|
|
188501
|
-
const vectors = await withTimeout(
|
|
188502
|
-
provider2.embedBatch([task, ...candidates.map(semanticText)], {
|
|
188503
|
-
timeoutMs: SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
188504
|
-
retry: false
|
|
188505
|
-
}),
|
|
188506
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS,
|
|
188507
|
-
"Long-term semantic retrieval"
|
|
188508
|
-
);
|
|
188509
|
-
const query = vectors[0];
|
|
188510
|
-
if (Array.isArray(query)) {
|
|
188511
|
-
selected = candidates.map((memory, index) => {
|
|
188512
|
-
const vector = vectors[index + 1];
|
|
188513
|
-
const ranked = Array.isArray(vector) ? semanticSelection(memory, cosineSimilarity(query, vector), task) : void 0;
|
|
188514
|
-
return ranked ? {
|
|
188515
|
-
memory,
|
|
188516
|
-
evidence: store2.listEvidence(memory.id),
|
|
188517
|
-
score: ranked.score,
|
|
188518
|
-
reason: ranked.reason
|
|
188519
|
-
} : void 0;
|
|
188520
|
-
}).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));
|
|
188521
|
-
}
|
|
188522
|
-
} catch {
|
|
188523
|
-
}
|
|
188524
|
-
}
|
|
188525
|
-
}
|
|
188526
|
-
selected = selected.slice(0, Math.max(1, Math.min(input.limit ?? 3, 5)));
|
|
188527
|
-
store2.markAccess(selected.map((item) => item.memory.id));
|
|
188528
|
-
return selected;
|
|
188529
|
-
}
|
|
188530
|
-
function renderLongTermMemorySummary(memory, maxTokens = 22) {
|
|
188531
|
-
const source = [memory.title, memory.applicability, memory.content].filter(Boolean).join(": ");
|
|
188532
|
-
const safe = sanitizeCredentials(source).replace(/\s+/g, " ").trim();
|
|
188533
|
-
return countTextTokens(safe) <= maxTokens ? safe : truncateToTokenBudget(safe, maxTokens);
|
|
188534
|
-
}
|
|
188535
|
-
var MAX_TEXT_LENGTH, MAX_LIST_ITEMS, MAX_SEMANTIC_CANDIDATES, SEMANTIC_RETRIEVAL_TIMEOUT_MS, MIN_SEMANTIC_SIMILARITY, PORTABLE_EVIDENCE_KINDS, STOP_TERMS;
|
|
188536
|
-
var init_long_term = __esm({
|
|
188537
|
-
"src/memory/long-term.ts"() {
|
|
188538
|
-
"use strict";
|
|
188539
|
-
init_esm_shims();
|
|
188540
|
-
init_token_budget();
|
|
188541
|
-
init_timeout();
|
|
188542
|
-
init_secret_filter();
|
|
188543
|
-
init_owner();
|
|
188544
|
-
init_long_term_store();
|
|
188545
|
-
init_long_term_types();
|
|
188546
|
-
MAX_TEXT_LENGTH = 12e3;
|
|
188547
|
-
MAX_LIST_ITEMS = 40;
|
|
188548
|
-
MAX_SEMANTIC_CANDIDATES = 48;
|
|
188549
|
-
SEMANTIC_RETRIEVAL_TIMEOUT_MS = 1800;
|
|
188550
|
-
MIN_SEMANTIC_SIMILARITY = 0.58;
|
|
188551
|
-
PORTABLE_EVIDENCE_KINDS = /* @__PURE__ */ new Set(["manual", "user"]);
|
|
188552
|
-
STOP_TERMS = /* @__PURE__ */ new Set([
|
|
188553
|
-
"the",
|
|
188554
|
-
"and",
|
|
188555
|
-
"for",
|
|
188556
|
-
"with",
|
|
188557
|
-
"from",
|
|
188558
|
-
"that",
|
|
188559
|
-
"this",
|
|
188560
|
-
"into",
|
|
188561
|
-
"when",
|
|
188562
|
-
"what",
|
|
188563
|
-
"\u5982\u4F55",
|
|
188564
|
-
"\u4EC0\u4E48",
|
|
188565
|
-
"\u8FD9\u4E2A",
|
|
188566
|
-
"\u90A3\u4E2A",
|
|
188567
|
-
"\u6211\u4EEC",
|
|
188568
|
-
"\u9879\u76EE",
|
|
188569
|
-
"\u8FDB\u884C",
|
|
188570
|
-
"\u9700\u8981",
|
|
188571
|
-
"\u7EE7\u7EED",
|
|
188572
|
-
"\u5904\u7406"
|
|
188573
|
-
]);
|
|
188574
|
-
}
|
|
188575
|
-
});
|
|
188576
|
-
|
|
188577
188675
|
// src/cli/commands/memory.ts
|
|
188578
188676
|
var memory_exports2 = {};
|
|
188579
188677
|
__export(memory_exports2, {
|
|
@@ -189052,6 +189150,7 @@ var init_memory3 = __esm({
|
|
|
189052
189150
|
createManualLongTermMemory: createManualLongTermMemory2,
|
|
189053
189151
|
getLongTermMemoryDetail: getLongTermMemoryDetail2,
|
|
189054
189152
|
listLongTermMemories: listLongTermMemories2,
|
|
189153
|
+
maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2,
|
|
189055
189154
|
promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2,
|
|
189056
189155
|
qualifyLongTermMemory: qualifyLongTermMemory2,
|
|
189057
189156
|
supersedeLongTermMemory: supersedeLongTermMemory2
|
|
@@ -189122,9 +189221,14 @@ var init_memory3 = __esm({
|
|
|
189122
189221
|
portability: portability(),
|
|
189123
189222
|
reader: longTermReader
|
|
189124
189223
|
});
|
|
189224
|
+
const qualified = await maybeAutoQualifyLongTermMemory2({
|
|
189225
|
+
dataDir,
|
|
189226
|
+
id: result.memory.id,
|
|
189227
|
+
source: "manual"
|
|
189228
|
+
});
|
|
189125
189229
|
emitResult(
|
|
189126
|
-
{ project, ...result },
|
|
189127
|
-
`Created long-term memory
|
|
189230
|
+
{ project, ...result, memory: qualified ?? result.memory },
|
|
189231
|
+
`Created long-term memory ${result.memory.id}.`,
|
|
189128
189232
|
asJson
|
|
189129
189233
|
);
|
|
189130
189234
|
return;
|
|
@@ -189150,9 +189254,14 @@ var init_memory3 = __esm({
|
|
|
189150
189254
|
applicability: asStringArg(args.applicability),
|
|
189151
189255
|
reader: longTermReader
|
|
189152
189256
|
});
|
|
189257
|
+
const qualified = await maybeAutoQualifyLongTermMemory2({
|
|
189258
|
+
dataDir,
|
|
189259
|
+
id: result.memory.id,
|
|
189260
|
+
sourceDetail: "explicit"
|
|
189261
|
+
});
|
|
189153
189262
|
emitResult(
|
|
189154
|
-
{ project, sourceObservationId: observation.id, ...result },
|
|
189155
|
-
`Promoted observation #${observation.id} to long-term memory
|
|
189263
|
+
{ project, sourceObservationId: observation.id, ...result, memory: qualified ?? result.memory },
|
|
189264
|
+
`Promoted observation #${observation.id} to long-term memory ${result.memory.id}.`,
|
|
189156
189265
|
asJson
|
|
189157
189266
|
);
|
|
189158
189267
|
return;
|
|
@@ -192200,13 +192309,28 @@ alwaysApply: true
|
|
|
192200
192309
|
'- For "why" decisions, use `memorix_store_reasoning`',
|
|
192201
192310
|
'- 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`.',
|
|
192202
192311
|
"- 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.",
|
|
192312
|
+
'- 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.',
|
|
192203
192313
|
"",
|
|
192204
192314
|
"**Don't store:** greetings, simple file reads, trivial commands (ls, pwd, git status).",
|
|
192205
192315
|
"",
|
|
192316
|
+
"**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.",
|
|
192317
|
+
"",
|
|
192318
|
+
`**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.`,
|
|
192319
|
+
"",
|
|
192320
|
+
"**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.",
|
|
192321
|
+
"",
|
|
192206
192322
|
"## When to resolve memory",
|
|
192207
192323
|
"",
|
|
192208
192324
|
"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.",
|
|
192209
192325
|
"",
|
|
192326
|
+
"## End sessions with a summary",
|
|
192327
|
+
"",
|
|
192328
|
+
"When a session finishes, call `memorix_session_end` with a short structured summary so the next agent can resume. Recommended sections:",
|
|
192329
|
+
"- **Goal** \u2014 what this session was working on",
|
|
192330
|
+
"- **Discoveries** \u2014 findings, gotchas, learnings",
|
|
192331
|
+
"- **Accomplished** \u2014 completed items, plus PENDING items for the next session",
|
|
192332
|
+
"- **Relevant Files** \u2014 paths and what changed",
|
|
192333
|
+
"",
|
|
192210
192334
|
"## Tools quick reference",
|
|
192211
192335
|
"",
|
|
192212
192336
|
"| Tool | Use when |",
|
|
@@ -192590,7 +192714,9 @@ function buildMemorixServer(mcp) {
|
|
|
192590
192714
|
return {
|
|
192591
192715
|
name: "memorix",
|
|
192592
192716
|
command: "memorix",
|
|
192593
|
-
|
|
192717
|
+
// The installed surface must expose every tool the generated guidance
|
|
192718
|
+
// teaches. lite covers the full taught set; micro would hide half of it.
|
|
192719
|
+
args: ["serve", "--mode", "lite"]
|
|
192594
192720
|
};
|
|
192595
192721
|
}
|
|
192596
192722
|
function mergeJsonMcpConfig(existingContent, generatedContent) {
|
|
@@ -194313,6 +194439,37 @@ function renderTaskWorksetPrompt(input) {
|
|
|
194313
194439
|
trust: "source-backed"
|
|
194314
194440
|
});
|
|
194315
194441
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
194442
|
+
if (input.alwaysOn) {
|
|
194443
|
+
const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
|
|
194444
|
+
if (hasAlwaysOn) {
|
|
194445
|
+
appendLine(lines, "", maxTokens, omitted, "always-on-heading");
|
|
194446
|
+
appendLine(lines, "You and this workspace", maxTokens, omitted, "always-on-heading");
|
|
194447
|
+
for (const profile of input.alwaysOn.profile.slice(0, 2)) {
|
|
194448
|
+
appendLine(lines, "- " + short(profile, 26), maxTokens, omitted, "always-on-profile", selected, {
|
|
194449
|
+
kind: "memory",
|
|
194450
|
+
id: "profile",
|
|
194451
|
+
reason: "user profile memory",
|
|
194452
|
+
trust: "source-backed"
|
|
194453
|
+
});
|
|
194454
|
+
}
|
|
194455
|
+
if (input.alwaysOn.state) {
|
|
194456
|
+
appendLine(lines, "- Recently: " + short(input.alwaysOn.state, 40), maxTokens, omitted, "always-on-state", selected, {
|
|
194457
|
+
kind: "continuation",
|
|
194458
|
+
id: "workspace-state",
|
|
194459
|
+
reason: "latest session summary for this workspace",
|
|
194460
|
+
trust: "historical"
|
|
194461
|
+
});
|
|
194462
|
+
}
|
|
194463
|
+
for (const durable of input.alwaysOn.durable.slice(0, 2)) {
|
|
194464
|
+
appendLine(lines, "- " + short(durable, 28), maxTokens, omitted, "always-on-durable", selected, {
|
|
194465
|
+
kind: "durable-memory",
|
|
194466
|
+
id: "durable-always",
|
|
194467
|
+
reason: "recent durable memory for this workspace",
|
|
194468
|
+
trust: "source-backed"
|
|
194469
|
+
});
|
|
194470
|
+
}
|
|
194471
|
+
}
|
|
194472
|
+
}
|
|
194316
194473
|
const hasContinuation = Boolean(
|
|
194317
194474
|
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
|
|
194318
194475
|
);
|
|
@@ -194783,6 +194940,7 @@ async function buildTaskWorkset(input) {
|
|
|
194783
194940
|
version: "1.3",
|
|
194784
194941
|
task,
|
|
194785
194942
|
lens: input.lens,
|
|
194943
|
+
...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
|
|
194786
194944
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
194787
194945
|
...continuation ? { continuation } : {},
|
|
194788
194946
|
...input.codeState ? { codeState: short(input.codeState, 28) } : {},
|
|
@@ -196832,6 +196990,33 @@ function activeProjectObservations(observations2, projectId) {
|
|
|
196832
196990
|
function deliveryEligibleProjectObservations(observations2, projectId) {
|
|
196833
196991
|
return activeProjectObservations(observations2, projectId).filter((observation) => isEligibleForAutomaticDelivery(observation));
|
|
196834
196992
|
}
|
|
196993
|
+
async function buildAlwaysOnBlock(input) {
|
|
196994
|
+
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));
|
|
196995
|
+
let state;
|
|
196996
|
+
try {
|
|
196997
|
+
await initSessionStore(input.dataDir);
|
|
196998
|
+
const { getSessionStore: getSessionStore2 } = await Promise.resolve().then(() => (init_session_store(), session_store_exports));
|
|
196999
|
+
const sessions = await getSessionStore2().loadByProject(input.projectId);
|
|
197000
|
+
const latest = sessions.filter((session) => session.endedAt && session.summary?.trim()).sort((a4, b4) => Date.parse(b4.endedAt ?? "") - Date.parse(a4.endedAt ?? ""))[0];
|
|
197001
|
+
if (latest?.summary) state = compactContinuationText(latest.summary, 40);
|
|
197002
|
+
} catch {
|
|
197003
|
+
}
|
|
197004
|
+
const durable = [];
|
|
197005
|
+
try {
|
|
197006
|
+
const items = await listLongTermMemories({
|
|
197007
|
+
dataDir: input.dataDir,
|
|
197008
|
+
reader: {
|
|
197009
|
+
projectId: input.projectId,
|
|
197010
|
+
...input.reader.agentId ? { agentId: input.reader.agentId } : {},
|
|
197011
|
+
...input.reader.isTeamMember ? { isTeamMember: true } : {}
|
|
197012
|
+
},
|
|
197013
|
+
limit: 10
|
|
197014
|
+
});
|
|
197015
|
+
durable.push(...items.filter((item) => item.memory.state === "approved").sort((a4, b4) => (b4.memory.updatedAt ?? "").localeCompare(a4.memory.updatedAt ?? "")).slice(0, 2).map((item) => item.memory.kind + ": " + compactContinuationText(item.memory.title, 22)));
|
|
197016
|
+
} catch {
|
|
197017
|
+
}
|
|
197018
|
+
return { profile, state, durable };
|
|
197019
|
+
}
|
|
196835
197020
|
function decideRefresh(input) {
|
|
196836
197021
|
if (input.mode === "never") {
|
|
196837
197022
|
return { performed: false, reason: "disabled", message: "Automatic project scan disabled." };
|
|
@@ -197036,12 +197221,19 @@ async function buildAutoProjectContext(input) {
|
|
|
197036
197221
|
};
|
|
197037
197222
|
}
|
|
197038
197223
|
}
|
|
197224
|
+
const alwaysOn = await buildAlwaysOnBlock({
|
|
197225
|
+
dataDir: input.dataDir,
|
|
197226
|
+
projectId: input.project.id,
|
|
197227
|
+
reader: input.reader ?? { projectId: input.project.id },
|
|
197228
|
+
eligible: deliveryEligibleProjectObservations(input.observations, input.project.id)
|
|
197229
|
+
});
|
|
197039
197230
|
const workset = await buildTaskWorkset({
|
|
197040
197231
|
projectId: input.project.id,
|
|
197041
197232
|
dataDir: input.dataDir,
|
|
197042
197233
|
...task ? { task } : {},
|
|
197043
197234
|
lens: lens.id,
|
|
197044
197235
|
startHere,
|
|
197236
|
+
...alwaysOn.profile.length > 0 || alwaysOn.state || alwaysOn.durable.length > 0 ? { alwaysOn } : {},
|
|
197045
197237
|
...externalOutline ? { semanticCode: externalOutline } : {},
|
|
197046
197238
|
providerQuality: providerQuality2,
|
|
197047
197239
|
currentFacts: worksetFactLines(currentFacts),
|
|
@@ -197224,7 +197416,16 @@ function formatAutoProjectContextSummary(context) {
|
|
|
197224
197416
|
lensLine(context),
|
|
197225
197417
|
"",
|
|
197226
197418
|
...formatCurrentFactsLines(context.currentFacts),
|
|
197227
|
-
""
|
|
197419
|
+
""
|
|
197420
|
+
].filter(Boolean);
|
|
197421
|
+
if (context.workset.alwaysOn && (context.workset.alwaysOn.profile.length > 0 || context.workset.alwaysOn.state || context.workset.alwaysOn.durable.length > 0)) {
|
|
197422
|
+
lines.push("You and this workspace");
|
|
197423
|
+
for (const profile of context.workset.alwaysOn.profile.slice(0, 2)) lines.push(`- ${profile}`);
|
|
197424
|
+
if (context.workset.alwaysOn.state) lines.push(`- Recently: ${context.workset.alwaysOn.state}`);
|
|
197425
|
+
for (const durable of context.workset.alwaysOn.durable.slice(0, 2)) lines.push(`- ${durable}`);
|
|
197426
|
+
lines.push("");
|
|
197427
|
+
}
|
|
197428
|
+
lines.push(...[
|
|
197228
197429
|
`- Code memory: ${context.overview.code.files} files / ${context.overview.code.symbols} symbols / ${context.overview.code.refs} memory links`,
|
|
197229
197430
|
`- Code provider: ${context.providerQuality.selected} (${context.providerQuality.selectedQuality})`,
|
|
197230
197431
|
`- Languages: ${formatLanguages(context.overview)}`,
|
|
@@ -197233,7 +197434,7 @@ function formatAutoProjectContextSummary(context) {
|
|
|
197233
197434
|
`- Refresh: ${context.refresh.message}`,
|
|
197234
197435
|
"",
|
|
197235
197436
|
"Start here"
|
|
197236
|
-
].filter(Boolean);
|
|
197437
|
+
].filter(Boolean));
|
|
197237
197438
|
if (startHere.length > 0) {
|
|
197238
197439
|
startHere.forEach((path47, index) => lines.push(`${index + 1}. ${path47}`));
|
|
197239
197440
|
} else {
|
|
@@ -197357,6 +197558,8 @@ var init_auto_context = __esm({
|
|
|
197357
197558
|
init_store();
|
|
197358
197559
|
init_admission();
|
|
197359
197560
|
init_session();
|
|
197561
|
+
init_long_term();
|
|
197562
|
+
init_visibility();
|
|
197360
197563
|
init_session_store();
|
|
197361
197564
|
init_task_lens();
|
|
197362
197565
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
@@ -216457,6 +216660,46 @@ var init_formation_timeout = __esm({
|
|
|
216457
216660
|
}
|
|
216458
216661
|
});
|
|
216459
216662
|
|
|
216663
|
+
// src/search/surfaced-registry.ts
|
|
216664
|
+
function touch(key) {
|
|
216665
|
+
const ids = registry2.get(key);
|
|
216666
|
+
if (!ids) return [];
|
|
216667
|
+
registry2.delete(key);
|
|
216668
|
+
registry2.set(key, ids);
|
|
216669
|
+
return ids;
|
|
216670
|
+
}
|
|
216671
|
+
function getSurfacedIds(key) {
|
|
216672
|
+
return [...touch(key)];
|
|
216673
|
+
}
|
|
216674
|
+
function recordSurfacedIds(key, ids) {
|
|
216675
|
+
if (ids.length === 0) return;
|
|
216676
|
+
let list = registry2.get(key) ?? [];
|
|
216677
|
+
const known = new Set(list);
|
|
216678
|
+
for (const id of ids) {
|
|
216679
|
+
if (known.has(id)) continue;
|
|
216680
|
+
known.add(id);
|
|
216681
|
+
list.push(id);
|
|
216682
|
+
}
|
|
216683
|
+
if (list.length > MAX_IDS) {
|
|
216684
|
+
list = list.slice(list.length - MAX_IDS);
|
|
216685
|
+
}
|
|
216686
|
+
registry2.set(key, list);
|
|
216687
|
+
if (registry2.size > MAX_KEYS) {
|
|
216688
|
+
const oldestKey = registry2.keys().next().value;
|
|
216689
|
+
if (oldestKey !== void 0 && oldestKey !== key) registry2.delete(oldestKey);
|
|
216690
|
+
}
|
|
216691
|
+
}
|
|
216692
|
+
var MAX_KEYS, MAX_IDS, registry2;
|
|
216693
|
+
var init_surfaced_registry = __esm({
|
|
216694
|
+
"src/search/surfaced-registry.ts"() {
|
|
216695
|
+
"use strict";
|
|
216696
|
+
init_esm_shims();
|
|
216697
|
+
MAX_KEYS = 64;
|
|
216698
|
+
MAX_IDS = 200;
|
|
216699
|
+
registry2 = /* @__PURE__ */ new Map();
|
|
216700
|
+
}
|
|
216701
|
+
});
|
|
216702
|
+
|
|
216460
216703
|
// src/server/request-context.ts
|
|
216461
216704
|
var request_context_exports = {};
|
|
216462
216705
|
__export(request_context_exports, {
|
|
@@ -217660,8 +217903,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
217660
217903
|
const { promises: fsP } = await import("fs");
|
|
217661
217904
|
const registryPath = path39.join(baseDir, ".project-aliases.json");
|
|
217662
217905
|
const raw = await fsP.readFile(registryPath, "utf-8");
|
|
217663
|
-
const
|
|
217664
|
-
aliasGroups =
|
|
217906
|
+
const registry3 = JSON.parse(raw);
|
|
217907
|
+
aliasGroups = registry3.groups || [];
|
|
217665
217908
|
} catch {
|
|
217666
217909
|
}
|
|
217667
217910
|
const currentGroup = aliasGroups.find((g4) => g4.aliases?.includes(effectiveProjectId) || g4.canonical === effectiveProjectId);
|
|
@@ -258410,6 +258653,12 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
258410
258653
|
kind: "consolidation",
|
|
258411
258654
|
dedupeKey: "startup-consolidation"
|
|
258412
258655
|
});
|
|
258656
|
+
lifecycle.enqueueLongTermMaintenance({
|
|
258657
|
+
dataDir: projectDir2,
|
|
258658
|
+
projectId: project.id,
|
|
258659
|
+
source: "startup",
|
|
258660
|
+
queue: queue2
|
|
258661
|
+
});
|
|
258413
258662
|
}
|
|
258414
258663
|
try {
|
|
258415
258664
|
const { CodeGraphStore: CodeGraphStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
|
|
@@ -258459,7 +258708,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
258459
258708
|
};
|
|
258460
258709
|
const server = existingServer ?? new McpServer({
|
|
258461
258710
|
name: "memorix",
|
|
258462
|
-
version: true ? "1.
|
|
258711
|
+
version: true ? "1.5.0" : "1.0.1"
|
|
258463
258712
|
});
|
|
258464
258713
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
258465
258714
|
server.registerTool = ((name, ...args) => {
|
|
@@ -258944,7 +259193,7 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
258944
259193
|
let longTermNote = "";
|
|
258945
259194
|
if (longTerm) {
|
|
258946
259195
|
try {
|
|
258947
|
-
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
259196
|
+
const { promoteObservationToLongTermMemory: promoteObservationToLongTermMemory2, maybeAutoQualifyLongTermMemory: maybeAutoQualifyLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
258948
259197
|
const promoted = await promoteObservationToLongTermMemory2({
|
|
258949
259198
|
dataDir: projectDir2,
|
|
258950
259199
|
observation: obs,
|
|
@@ -258958,7 +259207,24 @@ Mode: ${decision.usedLLM ? "LLM" : "heuristic"}`
|
|
|
258958
259207
|
...reader.isTeamMember ? { isTeamMember: true } : {}
|
|
258959
259208
|
}
|
|
258960
259209
|
});
|
|
258961
|
-
|
|
259210
|
+
const autoQualified = await maybeAutoQualifyLongTermMemory2({
|
|
259211
|
+
dataDir: projectDir2,
|
|
259212
|
+
id: promoted.memory.id,
|
|
259213
|
+
sourceDetail: "explicit"
|
|
259214
|
+
});
|
|
259215
|
+
if (autoQualified) {
|
|
259216
|
+
try {
|
|
259217
|
+
const { enqueueLongTermMaintenance: enqueueLongTermMaintenance2 } = await Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports));
|
|
259218
|
+
enqueueLongTermMaintenance2({
|
|
259219
|
+
dataDir: projectDir2,
|
|
259220
|
+
projectId: project.id,
|
|
259221
|
+
source: "long-term:" + promoted.memory.id
|
|
259222
|
+
});
|
|
259223
|
+
} catch {
|
|
259224
|
+
}
|
|
259225
|
+
}
|
|
259226
|
+
longTermNote = autoQualified ? `
|
|
259227
|
+
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\`.` : `
|
|
258962
259228
|
Long-term candidate: ${promoted.memory.id} (${promoted.memory.kind}/${promoted.memory.scope}). Review it with the CLI before it is delivered automatically.`;
|
|
258963
259229
|
} catch (longTermError) {
|
|
258964
259230
|
longTermNote = `
|
|
@@ -259154,6 +259420,13 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
259154
259420
|
const boundary = requireExplicitAutopilotExpansion("memorix_search", purpose);
|
|
259155
259421
|
if (boundary) return boundary;
|
|
259156
259422
|
}
|
|
259423
|
+
let surfacedKey = `project:${project.id}`;
|
|
259424
|
+
try {
|
|
259425
|
+
const { getActiveSession: getActiveSession2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
259426
|
+
const active = await getActiveSession2(projectDir2, project.id);
|
|
259427
|
+
if (active) surfacedKey = `session:${active.id}`;
|
|
259428
|
+
} catch {
|
|
259429
|
+
}
|
|
259157
259430
|
return withFreshIndex(async () => {
|
|
259158
259431
|
const safeLimit2 = limit2 != null ? coerceNumber(limit2, 20) : void 0;
|
|
259159
259432
|
const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
|
|
@@ -259171,7 +259444,8 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
259171
259444
|
status: status ?? "active",
|
|
259172
259445
|
source,
|
|
259173
259446
|
quality,
|
|
259174
|
-
reader: getObservationReader(scope === "global" ? "global" : "project")
|
|
259447
|
+
reader: getObservationReader(scope === "global" ? "global" : "project"),
|
|
259448
|
+
surfacedIds: getSurfacedIds(surfacedKey)
|
|
259175
259449
|
});
|
|
259176
259450
|
let result;
|
|
259177
259451
|
try {
|
|
@@ -259190,6 +259464,7 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
259190
259464
|
}
|
|
259191
259465
|
throw error40;
|
|
259192
259466
|
}
|
|
259467
|
+
recordSurfacedIds(surfacedKey, result.entries.map((entry) => entry.id));
|
|
259193
259468
|
const activeBoundary = getActiveAutopilotRetrievalBoundary();
|
|
259194
259469
|
if (scope !== "global" && activeBoundary && !force && result.entries.length > 0 && result.entries.every((entry) => activeBoundary.coveredObservationIds.has(entry.id))) {
|
|
259195
259470
|
const duplicate = blockCoveredAutopilotEvidence("memorix_search", result.entries.map((entry) => entry.id), force);
|
|
@@ -262152,13 +262427,26 @@ ${lines.join("\n")}` }] };
|
|
|
262152
262427
|
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";
|
|
262153
262428
|
return void 0;
|
|
262154
262429
|
}
|
|
262430
|
+
const mediaGenerationEnabled = process.env.MEMORIX_MCP_MEDIA_GENERATION === "1";
|
|
262431
|
+
const mediaTranscriptionEnabled = process.env.MEMORIX_MCP_MEDIA_TRANSCRIPTION === "1";
|
|
262432
|
+
const mediaGenerationOrTranscription = mediaGenerationEnabled || mediaTranscriptionEnabled;
|
|
262155
262433
|
server.registerTool(
|
|
262156
262434
|
"memorix_media",
|
|
262157
262435
|
{
|
|
262158
262436
|
title: "Manage Controlled Media",
|
|
262159
262437
|
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.",
|
|
262160
262438
|
inputSchema: {
|
|
262161
|
-
action: external_exports2.enum([
|
|
262439
|
+
action: external_exports2.enum([
|
|
262440
|
+
"import",
|
|
262441
|
+
"attach",
|
|
262442
|
+
"list",
|
|
262443
|
+
"show",
|
|
262444
|
+
"derive-pdf",
|
|
262445
|
+
"status",
|
|
262446
|
+
"cancel",
|
|
262447
|
+
...mediaTranscriptionEnabled ? ["derive-audio"] : [],
|
|
262448
|
+
...mediaGenerationEnabled ? ["generate-image", "generate-video"] : []
|
|
262449
|
+
]),
|
|
262162
262450
|
path: external_exports2.string().optional().describe("Explicit local image/audio/video/PDF path for import."),
|
|
262163
262451
|
assetId: external_exports2.string().optional().describe("Controlled MediaAsset ID for attach/show."),
|
|
262164
262452
|
jobId: external_exports2.string().optional().describe("Durable media job ID for status."),
|
|
@@ -262166,20 +262454,26 @@ ${lines.join("\n")}` }] };
|
|
|
262166
262454
|
limit: external_exports2.number().int().min(1).max(100).optional().describe("Maximum assets to list."),
|
|
262167
262455
|
title: external_exports2.string().optional().describe("Observation title when attaching generated/imported output."),
|
|
262168
262456
|
narrative: external_exports2.string().optional().describe("Short retrieval text when attaching an asset."),
|
|
262169
|
-
prompt: external_exports2.string().optional().describe("Explicit MiniMax generation prompt."),
|
|
262170
|
-
image: external_exports2.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
|
|
262171
|
-
model: external_exports2.string().max(160).optional().describe("Provider model for the selected media action."),
|
|
262172
|
-
provider: external_exports2.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
|
|
262173
|
-
language: external_exports2.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription."),
|
|
262174
|
-
region: external_exports2.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
|
|
262175
|
-
n: external_exports2.number().int().min(1).max(4).optional().describe("Image output count."),
|
|
262176
|
-
ratio: external_exports2.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."),
|
|
262177
|
-
width: external_exports2.number().int().min(1).max(8192).optional().describe("Requested image width."),
|
|
262178
|
-
height: external_exports2.number().int().min(1).max(8192).optional().describe("Requested image height."),
|
|
262179
|
-
duration: external_exports2.union([external_exports2.literal(5), external_exports2.literal(10)]).optional().describe("MiniMax video duration in seconds."),
|
|
262180
262457
|
maxPages: external_exports2.number().int().min(1).max(100).optional().describe("Bounded PDF extraction page limit."),
|
|
262181
262458
|
maxChars: external_exports2.number().int().min(1).max(6e4).optional().describe("Bounded PDF extraction character limit."),
|
|
262182
|
-
attach: external_exports2.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly.")
|
|
262459
|
+
attach: external_exports2.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly."),
|
|
262460
|
+
...mediaGenerationOrTranscription ? {
|
|
262461
|
+
model: external_exports2.string().max(160).optional().describe("Provider model for the selected media action."),
|
|
262462
|
+
prompt: external_exports2.string().optional().describe("Explicit prompt for generation or transcription.")
|
|
262463
|
+
} : {},
|
|
262464
|
+
...mediaTranscriptionEnabled ? {
|
|
262465
|
+
provider: external_exports2.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
|
|
262466
|
+
language: external_exports2.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription.")
|
|
262467
|
+
} : {},
|
|
262468
|
+
...mediaGenerationEnabled ? {
|
|
262469
|
+
image: external_exports2.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
|
|
262470
|
+
region: external_exports2.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
|
|
262471
|
+
n: external_exports2.number().int().min(1).max(4).optional().describe("Image output count."),
|
|
262472
|
+
ratio: external_exports2.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."),
|
|
262473
|
+
width: external_exports2.number().int().min(1).max(8192).optional().describe("Requested image width."),
|
|
262474
|
+
height: external_exports2.number().int().min(1).max(8192).optional().describe("Requested image height."),
|
|
262475
|
+
duration: external_exports2.union([external_exports2.literal(5), external_exports2.literal(10)]).optional().describe("MiniMax video duration in seconds.")
|
|
262476
|
+
} : {}
|
|
262183
262477
|
}
|
|
262184
262478
|
},
|
|
262185
262479
|
async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt: prompt2, image, model, provider: provider2, language, region, n: n3, ratio, width, height, duration: duration3, maxPages, maxChars, attach }) => {
|
|
@@ -262665,6 +262959,7 @@ var init_server4 = __esm({
|
|
|
262665
262959
|
init_formation_timeout();
|
|
262666
262960
|
init_timeout();
|
|
262667
262961
|
init_secret_filter();
|
|
262962
|
+
init_surfaced_registry();
|
|
262668
262963
|
init_request_context();
|
|
262669
262964
|
FORMATION_TIMEOUT_MS = parseFormationTimeoutMs(process.env.MEMORIX_FORMATION_TIMEOUT_MS);
|
|
262670
262965
|
COMPACT_ON_WRITE_TIMEOUT_MS = 12e3;
|
|
@@ -265243,7 +265538,8 @@ var init_control_plane_maintenance = __esm({
|
|
|
265243
265538
|
"claim-requalification",
|
|
265244
265539
|
"knowledge-compile",
|
|
265245
265540
|
"knowledge-lint",
|
|
265246
|
-
"workflow-index"
|
|
265541
|
+
"workflow-index",
|
|
265542
|
+
"long-term-maintenance"
|
|
265247
265543
|
];
|
|
265248
265544
|
TARGET_WAIT_RETRY_MS = 3e4;
|
|
265249
265545
|
}
|
|
@@ -265255,7 +265551,8 @@ __export(serve_http_exports, {
|
|
|
265255
265551
|
DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
|
|
265256
265552
|
_testing: () => _testing2,
|
|
265257
265553
|
default: () => serve_http_default,
|
|
265258
|
-
parseSessionTimeoutMs: () => parseSessionTimeoutMs
|
|
265554
|
+
parseSessionTimeoutMs: () => parseSessionTimeoutMs,
|
|
265555
|
+
resolveServeHttpDefaults: () => resolveServeHttpDefaults
|
|
265259
265556
|
});
|
|
265260
265557
|
function parseSessionTimeoutMs(raw) {
|
|
265261
265558
|
const value = raw?.trim();
|
|
@@ -265264,6 +265561,15 @@ function parseSessionTimeoutMs(raw) {
|
|
|
265264
265561
|
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_SESSION_TIMEOUT_MS;
|
|
265265
265562
|
return Math.floor(parsed);
|
|
265266
265563
|
}
|
|
265564
|
+
function resolveServeHttpDefaults(argsPort, configured) {
|
|
265565
|
+
const configuredPort = typeof configured.port === "number" && Number.isFinite(configured.port) ? configured.port : void 0;
|
|
265566
|
+
const port = parseTcpPortOrReport(argsPort, configuredPort ?? 3211);
|
|
265567
|
+
if (port === void 0) {
|
|
265568
|
+
return { port: 3211 };
|
|
265569
|
+
}
|
|
265570
|
+
const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
|
|
265571
|
+
return { port, transportNotice };
|
|
265572
|
+
}
|
|
265267
265573
|
var DEFAULT_SESSION_TIMEOUT_MS, serve_http_default, _testing2;
|
|
265268
265574
|
var init_serve_http = __esm({
|
|
265269
265575
|
"src/cli/commands/serve-http.ts"() {
|
|
@@ -265314,11 +265620,15 @@ var init_serve_http = __esm({
|
|
|
265314
265620
|
const { findGitInSubdirs: findGitInSubdirs2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
265315
265621
|
const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync11 } = await import("fs");
|
|
265316
265622
|
const { homedir: homedir36 } = await import("os");
|
|
265317
|
-
|
|
265318
|
-
|
|
265319
|
-
|
|
265320
|
-
|
|
265623
|
+
let configuredDefaults = {};
|
|
265624
|
+
try {
|
|
265625
|
+
const { getServerConfig: getServerConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
265626
|
+
configuredDefaults = getServerConfig2();
|
|
265627
|
+
} catch {
|
|
265321
265628
|
}
|
|
265629
|
+
const resolved = resolveServeHttpDefaults(args.port, configuredDefaults);
|
|
265630
|
+
if (resolved.transportNotice) console.error(resolved.transportNotice);
|
|
265631
|
+
const port = resolved.port;
|
|
265322
265632
|
const host = args.host || "127.0.0.1";
|
|
265323
265633
|
const toolProfile = resolveToolProfile({ explicit: args.mode, envValue: process.env.MEMORIX_MODE, fallback: "team" });
|
|
265324
265634
|
let safeCwd;
|
|
@@ -266190,8 +266500,8 @@ var init_serve_http = __esm({
|
|
|
266190
266500
|
canonicalId = await aliasModule.getCanonicalId(idProjectId);
|
|
266191
266501
|
const registryPath = pathModule.default.join(baseDir, ".project-aliases.json");
|
|
266192
266502
|
const raw = await fsPromises.readFile(registryPath, "utf-8");
|
|
266193
|
-
const
|
|
266194
|
-
aliasGroups =
|
|
266503
|
+
const registry3 = JSON.parse(raw);
|
|
266504
|
+
aliasGroups = registry3.groups || [];
|
|
266195
266505
|
} catch {
|
|
266196
266506
|
}
|
|
266197
266507
|
const currentGroup = aliasGroups.find((g4) => g4.aliases?.includes(idProjectId) || g4.canonical === idProjectId);
|
|
@@ -269262,7 +269572,8 @@ function normalizeCommitCount(count3) {
|
|
|
269262
269572
|
}
|
|
269263
269573
|
return count3;
|
|
269264
269574
|
}
|
|
269265
|
-
function getCommitInfo(cwd, ref = "HEAD") {
|
|
269575
|
+
function getCommitInfo(cwd, ref = "HEAD", maxDiffChars = 500) {
|
|
269576
|
+
const cap = Number.isFinite(maxDiffChars) ? Math.max(0, Math.floor(maxDiffChars)) : 500;
|
|
269266
269577
|
const FORMAT = "%H%n%h%n%aN%n%aI%n%s%n%b";
|
|
269267
269578
|
const revision = normalizeCommitRef(ref);
|
|
269268
269579
|
const raw = runGit3(cwd, ["log", "-1", `--format=${FORMAT}`, "--end-of-options", revision]).trim();
|
|
@@ -269290,7 +269601,7 @@ function getCommitInfo(cwd, ref = "HEAD") {
|
|
|
269290
269601
|
let diffSummary = "";
|
|
269291
269602
|
try {
|
|
269292
269603
|
const diff = runGit3(cwd, ["diff-tree", "-p", "--no-commit-id", hash4], 1024 * 100);
|
|
269293
|
-
diffSummary = diff.substring(0,
|
|
269604
|
+
diffSummary = diff.substring(0, cap);
|
|
269294
269605
|
} catch {
|
|
269295
269606
|
}
|
|
269296
269607
|
return {
|
|
@@ -269541,8 +269852,23 @@ var init_noise_filter = __esm({
|
|
|
269541
269852
|
// src/cli/commands/ingest-commit.ts
|
|
269542
269853
|
var ingest_commit_exports = {};
|
|
269543
269854
|
__export(ingest_commit_exports, {
|
|
269855
|
+
applyGitIngestPolicy: () => applyGitIngestPolicy,
|
|
269544
269856
|
default: () => ingest_commit_default
|
|
269545
269857
|
});
|
|
269858
|
+
function applyGitIngestPolicy(commit, policy) {
|
|
269859
|
+
if (policy.ingestOnCommit === false) {
|
|
269860
|
+
return {
|
|
269861
|
+
skip: true,
|
|
269862
|
+
reason: "ingest_on_commit is disabled",
|
|
269863
|
+
diffSummary: commit.diffSummary
|
|
269864
|
+
};
|
|
269865
|
+
}
|
|
269866
|
+
const cap = policy.maxDiffSize;
|
|
269867
|
+
return {
|
|
269868
|
+
skip: false,
|
|
269869
|
+
diffSummary: typeof cap === "number" && Number.isFinite(cap) ? commit.diffSummary.slice(0, Math.max(0, Math.floor(cap))) : commit.diffSummary
|
|
269870
|
+
};
|
|
269871
|
+
}
|
|
269546
269872
|
var ingest_commit_default;
|
|
269547
269873
|
var init_ingest_commit = __esm({
|
|
269548
269874
|
"src/cli/commands/ingest-commit.ts"() {
|
|
@@ -269585,11 +269911,21 @@ var init_ingest_commit = __esm({
|
|
|
269585
269911
|
const force = !!args.force;
|
|
269586
269912
|
if (!auto) we(`Ingest commit: ${ref}`);
|
|
269587
269913
|
try {
|
|
269588
|
-
const { getCommitInfo: getCommitInfo2, ingestCommit: ingestCommit2 } = await Promise.resolve().then(() => (init_extractor(), extractor_exports));
|
|
269589
|
-
const commit = getCommitInfo2(cwd, ref);
|
|
269590
|
-
const { shouldFilterCommit: shouldFilterCommit2 } = await Promise.resolve().then(() => (init_noise_filter(), noise_filter_exports));
|
|
269591
269914
|
const { getGitConfig: getGitConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
269592
269915
|
const gitCfg = getGitConfig2();
|
|
269916
|
+
const { getCommitInfo: getCommitInfo2, ingestCommit: ingestCommit2 } = await Promise.resolve().then(() => (init_extractor(), extractor_exports));
|
|
269917
|
+
const commit = getCommitInfo2(cwd, ref, gitCfg.maxDiffSize ?? 500);
|
|
269918
|
+
const policy = applyGitIngestPolicy(commit, {
|
|
269919
|
+
ingestOnCommit: gitCfg.ingestOnCommit,
|
|
269920
|
+
maxDiffSize: gitCfg.maxDiffSize
|
|
269921
|
+
});
|
|
269922
|
+
if (policy.skip && auto) {
|
|
269923
|
+
console.error(`[memorix] Skipped ${commit.shortHash}: ${policy.reason}`);
|
|
269924
|
+
process.exit(0);
|
|
269925
|
+
return;
|
|
269926
|
+
}
|
|
269927
|
+
commit.diffSummary = policy.diffSummary;
|
|
269928
|
+
const { shouldFilterCommit: shouldFilterCommit2 } = await Promise.resolve().then(() => (init_noise_filter(), noise_filter_exports));
|
|
269593
269929
|
const filterResult = shouldFilterCommit2(commit, {
|
|
269594
269930
|
skipMergeCommits: gitCfg.skipMergeCommits,
|
|
269595
269931
|
excludePatterns: gitCfg.excludePatterns,
|
|
@@ -272948,21 +273284,28 @@ var init_dashboard = __esm({
|
|
|
272948
273284
|
args: {
|
|
272949
273285
|
port: {
|
|
272950
273286
|
type: "string",
|
|
272951
|
-
description: "Port to run the dashboard on (default: 3210)",
|
|
272952
|
-
|
|
273287
|
+
description: "Port to run the dashboard on (default: [server].dashboardPort or 3210)",
|
|
273288
|
+
required: false
|
|
272953
273289
|
}
|
|
272954
273290
|
},
|
|
272955
273291
|
run: async ({ args }) => {
|
|
272956
273292
|
const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
272957
273293
|
const { getProjectDataDir: getProjectDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
|
|
272958
273294
|
const { startDashboard: startDashboard2 } = await Promise.resolve().then(() => (init_server3(), server_exports));
|
|
273295
|
+
let configuredDashboardPort;
|
|
273296
|
+
try {
|
|
273297
|
+
const { getServerConfig: getServerConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
273298
|
+
const serverConfig = getServerConfig2();
|
|
273299
|
+
configuredDashboardPort = typeof serverConfig.dashboardPort === "number" ? serverConfig.dashboardPort : void 0;
|
|
273300
|
+
} catch {
|
|
273301
|
+
}
|
|
272959
273302
|
const project = detectProject2();
|
|
272960
273303
|
if (!project) {
|
|
272961
273304
|
console.error("Memorix requires a git repo to establish project identity. Run `git init` in this workspace first.");
|
|
272962
273305
|
process.exit(1);
|
|
272963
273306
|
}
|
|
272964
273307
|
const dataDir = await getProjectDataDir2(project.id);
|
|
272965
|
-
const port = parseTcpPortOrReport(args.port, 3210);
|
|
273308
|
+
const port = parseTcpPortOrReport(args.port, configuredDashboardPort ?? 3210);
|
|
272966
273309
|
if (port === void 0) {
|
|
272967
273310
|
process.exitCode = 1;
|
|
272968
273311
|
return;
|
|
@@ -273219,6 +273562,116 @@ Project: ${projectName} (${projectId})
|
|
|
273219
273562
|
}
|
|
273220
273563
|
});
|
|
273221
273564
|
|
|
273565
|
+
// src/cli/commands/purge.ts
|
|
273566
|
+
var purge_exports = {};
|
|
273567
|
+
__export(purge_exports, {
|
|
273568
|
+
default: () => purge_default
|
|
273569
|
+
});
|
|
273570
|
+
var purge_default;
|
|
273571
|
+
var init_purge = __esm({
|
|
273572
|
+
"src/cli/commands/purge.ts"() {
|
|
273573
|
+
"use strict";
|
|
273574
|
+
init_esm_shims();
|
|
273575
|
+
init_dist2();
|
|
273576
|
+
init_detector();
|
|
273577
|
+
init_persistence();
|
|
273578
|
+
purge_default = defineCommand({
|
|
273579
|
+
meta: {
|
|
273580
|
+
name: "purge",
|
|
273581
|
+
description: "Retire memories from retrieval (current project by default, --all for every project)"
|
|
273582
|
+
},
|
|
273583
|
+
args: {
|
|
273584
|
+
all: {
|
|
273585
|
+
type: "boolean",
|
|
273586
|
+
description: "Purge every project instead of the current one",
|
|
273587
|
+
default: false
|
|
273588
|
+
},
|
|
273589
|
+
yes: {
|
|
273590
|
+
type: "boolean",
|
|
273591
|
+
description: "Skip the interactive confirmation",
|
|
273592
|
+
default: false
|
|
273593
|
+
},
|
|
273594
|
+
json: {
|
|
273595
|
+
type: "boolean",
|
|
273596
|
+
description: "Emit a machine-readable result",
|
|
273597
|
+
default: false
|
|
273598
|
+
}
|
|
273599
|
+
},
|
|
273600
|
+
async run({ args }) {
|
|
273601
|
+
const purgeAll = Boolean(args.all);
|
|
273602
|
+
const yes = Boolean(args.yes);
|
|
273603
|
+
const asJson = Boolean(args.json);
|
|
273604
|
+
if (!yes && !process.stdin.isTTY) {
|
|
273605
|
+
console.error("[ERROR] Non-interactive environment. Add --yes to confirm.");
|
|
273606
|
+
console.error("[ERROR] Refusing to purge memories without confirmation.");
|
|
273607
|
+
process.exitCode = 1;
|
|
273608
|
+
return;
|
|
273609
|
+
}
|
|
273610
|
+
if (!yes) {
|
|
273611
|
+
const readline = await import("readline");
|
|
273612
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
273613
|
+
const question = purgeAll ? "Retire ALL memories from every project? (y/N) " : "Retire all memories for this project? (y/N) ";
|
|
273614
|
+
const answer = await new Promise((resolve6) => rl.question(question, resolve6));
|
|
273615
|
+
rl.close();
|
|
273616
|
+
if (answer.trim().toLowerCase() !== "y") {
|
|
273617
|
+
console.log("Cancelled.");
|
|
273618
|
+
return;
|
|
273619
|
+
}
|
|
273620
|
+
}
|
|
273621
|
+
let projectId = null;
|
|
273622
|
+
if (!purgeAll) {
|
|
273623
|
+
const project = detectProject();
|
|
273624
|
+
if (!project) {
|
|
273625
|
+
console.error("[ERROR] No .git found \u2014 run from a project directory or pass --all.");
|
|
273626
|
+
process.exitCode = 1;
|
|
273627
|
+
return;
|
|
273628
|
+
}
|
|
273629
|
+
projectId = project.id;
|
|
273630
|
+
}
|
|
273631
|
+
const dataDir = await getProjectDataDir(projectId ?? "");
|
|
273632
|
+
const { initObservationStore: initObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
273633
|
+
const { getAllObservations: getAllObservations2, resolveObservations: resolveObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
273634
|
+
const { withFreshIndex: withFreshIndex2 } = await Promise.resolve().then(() => (init_freshness(), freshness_exports));
|
|
273635
|
+
await initObservationStore2(dataDir);
|
|
273636
|
+
const observations2 = await withFreshIndex2(() => getAllObservations2());
|
|
273637
|
+
const target = purgeAll ? observations2 : observations2.filter((o4) => o4.projectId === projectId);
|
|
273638
|
+
const ids = target.map((o4) => o4.id).filter((id) => typeof id === "number");
|
|
273639
|
+
const archived = ids.length > 0 ? (await resolveObservations2(ids, "archived")).resolved.length : 0;
|
|
273640
|
+
const { LongTermMemoryStore: LongTermMemoryStore2 } = await Promise.resolve().then(() => (init_long_term_store(), long_term_store_exports));
|
|
273641
|
+
const { archiveLongTermMemory: archiveLongTermMemory2 } = await Promise.resolve().then(() => (init_long_term(), long_term_exports));
|
|
273642
|
+
const longTermStore = new LongTermMemoryStore2();
|
|
273643
|
+
await longTermStore.init(dataDir);
|
|
273644
|
+
let longTermArchived = 0;
|
|
273645
|
+
for (const memory of longTermStore.list()) {
|
|
273646
|
+
if (memory.state === "archived" || memory.state === "superseded") continue;
|
|
273647
|
+
if (!purgeAll && memory.originProjectId !== projectId) continue;
|
|
273648
|
+
try {
|
|
273649
|
+
await archiveLongTermMemory2({
|
|
273650
|
+
dataDir,
|
|
273651
|
+
id: memory.id,
|
|
273652
|
+
reason: purgeAll ? "purge --all" : "purge (project) " + (projectId ?? "")
|
|
273653
|
+
});
|
|
273654
|
+
longTermArchived += 1;
|
|
273655
|
+
} catch {
|
|
273656
|
+
}
|
|
273657
|
+
}
|
|
273658
|
+
const result = {
|
|
273659
|
+
scope: purgeAll ? "all" : projectId,
|
|
273660
|
+
observationsArchived: archived,
|
|
273661
|
+
longTermArchived
|
|
273662
|
+
};
|
|
273663
|
+
if (asJson) {
|
|
273664
|
+
console.log(JSON.stringify(result));
|
|
273665
|
+
} else {
|
|
273666
|
+
console.log(
|
|
273667
|
+
`[OK] Purged ${purgeAll ? "all projects" : result.scope}: archived ${archived} observations and ${longTermArchived} long-term memories.`
|
|
273668
|
+
);
|
|
273669
|
+
}
|
|
273670
|
+
}
|
|
273671
|
+
});
|
|
273672
|
+
}
|
|
273673
|
+
});
|
|
273674
|
+
|
|
273222
273675
|
// src/cli/commands/uninstall.ts
|
|
273223
273676
|
var uninstall_exports = {};
|
|
273224
273677
|
__export(uninstall_exports, {
|
|
@@ -282119,6 +282572,7 @@ var main2 = defineCommand({
|
|
|
282119
282572
|
repair: () => Promise.resolve().then(() => (init_repair3(), repair_exports)).then((m6) => m6.default),
|
|
282120
282573
|
dashboard: () => Promise.resolve().then(() => (init_dashboard(), dashboard_exports)).then((m6) => m6.default),
|
|
282121
282574
|
cleanup: () => Promise.resolve().then(() => (init_cleanup(), cleanup_exports)).then((m6) => m6.default),
|
|
282575
|
+
purge: () => Promise.resolve().then(() => (init_purge(), purge_exports)).then((m6) => m6.default),
|
|
282122
282576
|
uninstall: () => Promise.resolve().then(() => (init_uninstall(), uninstall_exports)).then((m6) => m6.default),
|
|
282123
282577
|
orchestrate: () => Promise.resolve().then(() => (init_orchestrate(), orchestrate_exports)).then((m6) => m6.default),
|
|
282124
282578
|
workbench: () => Promise.resolve(defineCommand({
|
|
@@ -282194,6 +282648,7 @@ var main2 = defineCommand({
|
|
|
282194
282648
|
"repair",
|
|
282195
282649
|
"dashboard",
|
|
282196
282650
|
"cleanup",
|
|
282651
|
+
"purge",
|
|
282197
282652
|
"uninstall",
|
|
282198
282653
|
"orchestrate"
|
|
282199
282654
|
];
|
|
@@ -282257,6 +282712,7 @@ var main2 = defineCommand({
|
|
|
282257
282712
|
console.error(" dashboard Open standalone dashboard (read-mostly)");
|
|
282258
282713
|
console.error(" hooks Open legacy hook installer menu");
|
|
282259
282714
|
console.error(" cleanup Remove old memories");
|
|
282715
|
+
console.error(" purge Retire all memories (project by default, --all for everything)");
|
|
282260
282716
|
console.error(" sync Rules/workspace sync plus interactive wizard");
|
|
282261
282717
|
console.error(" ingest Ingest commit, log, or image knowledge");
|
|
282262
282718
|
console.error("\nRun `memorix` in an interactive terminal for memcode TUI.");
|