memorix 1.9.4 → 1.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -1
- package/README.md +12 -6
- package/README.zh-CN.md +12 -6
- package/dist/cli/index.js +2533 -585
- package/dist/index.d.ts +15 -8
- package/dist/index.js +1201 -284
- package/dist/maintenance-runner.js +387 -216
- package/dist/media-audio-runner.js +300 -129
- package/dist/media-video-runner.js +300 -129
- package/dist/memcode-runtime/CHANGELOG.md +75 -1
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +1882 -998
- package/dist/types.d.ts +1 -1
- package/dist/vector-backfill-runner.js +300 -129
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +47 -3
- package/docs/API_REFERENCE.md +39 -1
- package/docs/CONFIGURATION.md +58 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/DOCKER.md +23 -3
- package/docs/INTEGRATIONS.md +1 -1
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +61 -1
- package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +4 -0
- package/docs/PERFORMANCE.md +46 -2
- package/docs/README.md +2 -1
- package/docs/SETUP.md +56 -1
- package/package.json +3 -2
- package/plugins/antigravity/memorix/plugin.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/capability-map.ts +1 -0
- package/src/cli/command-guide.ts +15 -3
- package/src/cli/commands/agent-integrations.ts +57 -0
- package/src/cli/commands/background.ts +11 -2
- package/src/cli/commands/context.ts +2 -0
- package/src/cli/commands/continuity.ts +155 -0
- package/src/cli/commands/init.ts +5 -2
- package/src/cli/commands/serve-http.ts +435 -84
- package/src/cli/commands/setup.ts +77 -10
- package/src/cli/commands/sync-store.ts +47 -12
- package/src/cli/commands/sync.ts +26 -0
- package/src/cli/index.ts +3 -1
- package/src/cli/tui/ConfigureView.tsx +2 -0
- package/src/codegraph/auto-context.ts +17 -0
- package/src/config/legacy-loader.ts +2 -2
- package/src/config/resolved-config.ts +8 -5
- package/src/config/yaml-loader.ts +20 -10
- package/src/dashboard/server.ts +10 -12
- package/src/embedding/api-provider.ts +4 -2
- package/src/embedding/fastembed-provider.ts +47 -14
- package/src/embedding/transformers-provider.ts +29 -7
- package/src/evaluation/memory-quality-gate.ts +90 -0
- package/src/hooks/official-skills.ts +2 -0
- package/src/integrations/registry.ts +1 -1
- package/src/knowledge/context-assembly.ts +1 -0
- package/src/knowledge/context-receipt.ts +15 -0
- package/src/knowledge/task-continuity.ts +413 -0
- package/src/knowledge/workset.ts +77 -0
- package/src/llm/provider.ts +2 -1
- package/src/memory/graph.ts +10 -8
- package/src/memory/session.ts +1 -1
- package/src/orchestrate/coordinator.ts +60 -0
- package/src/orchestrate/effect-ledger.ts +160 -0
- package/src/orchestrate/pipeline-trace.ts +40 -6
- package/src/runtime/maintenance-jobs.ts +12 -6
- package/src/sdk.ts +24 -0
- package/src/server/mcp-binding-store.ts +14 -3
- package/src/server/modern-mcp-bridge.ts +153 -27
- package/src/server/tool-profile.ts +3 -2
- package/src/server.ts +131 -7
- package/src/store/file-lock.ts +28 -3
- package/src/store/graph-store.ts +178 -46
- package/src/store/persistence.ts +14 -9
- package/src/store/sqlite-db.ts +150 -7
- package/src/store/sqlite-store.ts +32 -27
- package/src/sync/engine.ts +9 -7
- package/src/sync/namespace.ts +23 -0
- package/src/sync/policy.ts +19 -4
- package/src/sync/store-port.ts +43 -13
- package/src/sync/types.ts +2 -0
- package/src/team/team-store.ts +62 -27
- package/src/types.ts +1 -1
- package/src/workspace/mcp-adapters/grok.ts +32 -0
package/dist/index.js
CHANGED
|
@@ -444,11 +444,21 @@ function applySchemaMigrations(db2) {
|
|
|
444
444
|
apply();
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
|
+
function normalizeDataDir(dataDir) {
|
|
448
|
+
return path3.resolve(dataDir);
|
|
449
|
+
}
|
|
450
|
+
function touchDatabase(normalized, db2) {
|
|
451
|
+
_dbCache.delete(normalized);
|
|
452
|
+
_dbCache.set(normalized, db2);
|
|
453
|
+
}
|
|
447
454
|
function getDatabase(dataDir) {
|
|
448
|
-
const normalized =
|
|
455
|
+
const normalized = normalizeDataDir(dataDir);
|
|
449
456
|
assertNotHomeDataDir(normalized);
|
|
450
457
|
const existing = _dbCache.get(normalized);
|
|
451
|
-
if (existing)
|
|
458
|
+
if (existing) {
|
|
459
|
+
touchDatabase(normalized, existing);
|
|
460
|
+
return existing;
|
|
461
|
+
}
|
|
452
462
|
loadBetterSqlite3();
|
|
453
463
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
454
464
|
const dbPath = path3.join(dataDir, "memorix.db");
|
|
@@ -478,6 +488,8 @@ function getDatabase(dataDir) {
|
|
|
478
488
|
db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
|
|
479
489
|
db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
|
|
480
490
|
db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
|
|
491
|
+
db2.exec(CREATE_TASK_CONTINUITY_EVENTS_TABLE);
|
|
492
|
+
db2.exec(CREATE_AGENT_EFFECTS_TABLE);
|
|
481
493
|
try {
|
|
482
494
|
db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
|
|
483
495
|
} catch {
|
|
@@ -516,7 +528,7 @@ function getDatabase(dataDir) {
|
|
|
516
528
|
db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
|
|
517
529
|
db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
|
|
518
530
|
db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
|
|
519
|
-
|
|
531
|
+
touchDatabase(normalized, db2);
|
|
520
532
|
return db2;
|
|
521
533
|
} catch (error) {
|
|
522
534
|
try {
|
|
@@ -534,8 +546,9 @@ function closeAllDatabases() {
|
|
|
534
546
|
}
|
|
535
547
|
_dbCache.delete(key);
|
|
536
548
|
}
|
|
549
|
+
_dbLeases.clear();
|
|
537
550
|
}
|
|
538
|
-
var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_EVIDENCE_CARDS_TABLE, CREATE_EVIDENCE_CARD_EVENTS_TABLE, CREATE_MEMORY_FEEDBACK_STATES_TABLE, CREATE_MEMORY_FEEDBACK_EVENTS_TABLE, CREATE_MCP_BINDINGS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
|
|
551
|
+
var BetterSqlite3, CREATE_OBSERVATIONS_TABLE, CREATE_MINI_SKILLS_TABLE, CREATE_SESSIONS_TABLE, CREATE_META_TABLE, CREATE_TEAM_AGENTS_TABLE, CREATE_TEAM_MESSAGES_TABLE, CREATE_TEAM_TASKS_TABLE, CREATE_TEAM_TASK_DEPS_TABLE, CREATE_TEAM_LOCKS_TABLE, CREATE_TEAM_ROLES_TABLE, CREATE_CHAT_TRANSCRIPT_TABLE, CREATE_GRAPH_ENTITIES_TABLE, CREATE_GRAPH_RELATIONS_TABLE, CREATE_CODE_FILES_TABLE, CREATE_CODE_SYMBOLS_TABLE, CREATE_CODE_EDGES_TABLE, CREATE_OBSERVATION_CODE_REFS_TABLE, CREATE_SCHEMA_MIGRATIONS_TABLE, CREATE_CODE_STATE_SNAPSHOTS_TABLE, CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE, CREATE_KNOWLEDGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_CLAIM_EVIDENCE_TABLE, CREATE_KNOWLEDGE_CLAIM_EVENTS_TABLE, CREATE_KNOWLEDGE_WORKSPACES_TABLE, CREATE_KNOWLEDGE_PAGES_TABLE, CREATE_KNOWLEDGE_PAGE_CLAIMS_TABLE, CREATE_KNOWLEDGE_PAGE_LINKS_TABLE, CREATE_KNOWLEDGE_PROPOSALS_TABLE, CREATE_KNOWLEDGE_WORKFLOWS_TABLE, CREATE_KNOWLEDGE_WORKFLOW_RUNS_TABLE, CREATE_LONG_TERM_MEMORIES_TABLE, CREATE_LONG_TERM_MEMORY_EVIDENCE_TABLE, CREATE_LONG_TERM_MEMORY_EVENTS_TABLE, CREATE_MEMORY_OUTCOME_SIGNALS_TABLE, CREATE_MEDIA_ASSETS_TABLE, CREATE_MEDIA_ASSET_LINKS_TABLE, CREATE_MEDIA_DERIVATIONS_TABLE, CREATE_MEDIA_EMBEDDING_PROFILES_TABLE, CREATE_MEDIA_EMBEDDINGS_TABLE, CREATE_MEDIA_JOBS_TABLE, CREATE_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_EVIDENCE_CARDS_TABLE, CREATE_EVIDENCE_CARD_EVENTS_TABLE, CREATE_MEMORY_FEEDBACK_STATES_TABLE, CREATE_MEMORY_FEEDBACK_EVENTS_TABLE, CREATE_TASK_CONTINUITY_EVENTS_TABLE, CREATE_AGENT_EFFECTS_TABLE, CREATE_MCP_BINDINGS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache, _dbLeases;
|
|
539
552
|
var init_sqlite_db = __esm({
|
|
540
553
|
"src/store/sqlite-db.ts"() {
|
|
541
554
|
"use strict";
|
|
@@ -707,18 +720,21 @@ CREATE TABLE IF NOT EXISTS chat_transcript (
|
|
|
707
720
|
`;
|
|
708
721
|
CREATE_GRAPH_ENTITIES_TABLE = `
|
|
709
722
|
CREATE TABLE IF NOT EXISTS graph_entities (
|
|
710
|
-
|
|
723
|
+
projectId TEXT NOT NULL,
|
|
724
|
+
name TEXT NOT NULL,
|
|
711
725
|
entityType TEXT NOT NULL DEFAULT '',
|
|
712
|
-
observations TEXT NOT NULL DEFAULT '[]'
|
|
726
|
+
observations TEXT NOT NULL DEFAULT '[]',
|
|
727
|
+
PRIMARY KEY (projectId, name)
|
|
713
728
|
);
|
|
714
729
|
`;
|
|
715
730
|
CREATE_GRAPH_RELATIONS_TABLE = `
|
|
716
731
|
CREATE TABLE IF NOT EXISTS graph_relations (
|
|
717
732
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
733
|
+
projectId TEXT NOT NULL,
|
|
718
734
|
from_entity TEXT NOT NULL,
|
|
719
735
|
to_entity TEXT NOT NULL,
|
|
720
736
|
relationType TEXT NOT NULL DEFAULT '',
|
|
721
|
-
UNIQUE(from_entity, to_entity, relationType)
|
|
737
|
+
UNIQUE(projectId, from_entity, to_entity, relationType)
|
|
722
738
|
);
|
|
723
739
|
`;
|
|
724
740
|
CREATE_CODE_FILES_TABLE = `
|
|
@@ -1249,6 +1265,43 @@ CREATE TABLE IF NOT EXISTS memory_feedback_events (
|
|
|
1249
1265
|
delta REAL NOT NULL DEFAULT 0,
|
|
1250
1266
|
observed_at TEXT NOT NULL
|
|
1251
1267
|
);
|
|
1268
|
+
`;
|
|
1269
|
+
CREATE_TASK_CONTINUITY_EVENTS_TABLE = `
|
|
1270
|
+
CREATE TABLE IF NOT EXISTS task_continuity_events (
|
|
1271
|
+
id TEXT PRIMARY KEY,
|
|
1272
|
+
project_id TEXT NOT NULL,
|
|
1273
|
+
task_id TEXT NOT NULL,
|
|
1274
|
+
task TEXT NOT NULL,
|
|
1275
|
+
kind TEXT NOT NULL,
|
|
1276
|
+
content TEXT NOT NULL,
|
|
1277
|
+
item_status TEXT,
|
|
1278
|
+
source_ref TEXT,
|
|
1279
|
+
evidence_json TEXT NOT NULL DEFAULT '[]',
|
|
1280
|
+
actor TEXT,
|
|
1281
|
+
created_at TEXT NOT NULL,
|
|
1282
|
+
event_key TEXT,
|
|
1283
|
+
item_key TEXT,
|
|
1284
|
+
supersedes_id TEXT
|
|
1285
|
+
);
|
|
1286
|
+
`;
|
|
1287
|
+
CREATE_AGENT_EFFECTS_TABLE = `
|
|
1288
|
+
CREATE TABLE IF NOT EXISTS agent_effects (
|
|
1289
|
+
id TEXT PRIMARY KEY,
|
|
1290
|
+
pipeline_id TEXT NOT NULL,
|
|
1291
|
+
task_id TEXT NOT NULL,
|
|
1292
|
+
attempt INTEGER NOT NULL,
|
|
1293
|
+
effect_key TEXT NOT NULL,
|
|
1294
|
+
tool TEXT NOT NULL,
|
|
1295
|
+
risk_tier TEXT NOT NULL,
|
|
1296
|
+
replay_policy TEXT NOT NULL,
|
|
1297
|
+
status TEXT NOT NULL,
|
|
1298
|
+
call_id TEXT,
|
|
1299
|
+
idempotency_key TEXT NOT NULL,
|
|
1300
|
+
input_fingerprint TEXT,
|
|
1301
|
+
detail TEXT,
|
|
1302
|
+
started_at INTEGER NOT NULL,
|
|
1303
|
+
finished_at INTEGER
|
|
1304
|
+
);
|
|
1252
1305
|
`;
|
|
1253
1306
|
CREATE_MCP_BINDINGS_TABLE = `
|
|
1254
1307
|
CREATE TABLE IF NOT EXISTS mcp_bindings (
|
|
@@ -1327,7 +1380,18 @@ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(projec
|
|
|
1327
1380
|
CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
|
|
1328
1381
|
CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
|
|
1329
1382
|
CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
|
|
1383
|
+
CREATE INDEX IF NOT EXISTS idx_task_continuity_project_task ON task_continuity_events(project_id, task_id, created_at ASC);
|
|
1384
|
+
CREATE INDEX IF NOT EXISTS idx_task_continuity_project_recent ON task_continuity_events(project_id, created_at DESC);
|
|
1385
|
+
CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC);
|
|
1386
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
|
|
1387
|
+
ON task_continuity_events(project_id, task_id, event_key)
|
|
1388
|
+
WHERE event_key IS NOT NULL;
|
|
1389
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_effect_identity
|
|
1390
|
+
ON agent_effects(pipeline_id, task_id, attempt, effect_key);
|
|
1391
|
+
CREATE INDEX IF NOT EXISTS idx_agent_effect_pipeline ON agent_effects(pipeline_id, started_at ASC);
|
|
1330
1392
|
CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
|
|
1393
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC);
|
|
1394
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at);
|
|
1331
1395
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
1332
1396
|
ON maintenance_jobs(project_id, kind, dedupe_key)
|
|
1333
1397
|
WHERE status IN ('pending', 'running', 'retry');
|
|
@@ -1498,6 +1562,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
1498
1562
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
|
|
1499
1563
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC)");
|
|
1500
1564
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
|
|
1565
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC)");
|
|
1566
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at)");
|
|
1501
1567
|
}
|
|
1502
1568
|
},
|
|
1503
1569
|
{
|
|
@@ -1508,9 +1574,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
1508
1574
|
addColumnIfMissing(db2, "code_symbols", "source", "source TEXT");
|
|
1509
1575
|
addColumnIfMissing(db2, "code_edges", "source", "source TEXT");
|
|
1510
1576
|
}
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
id: "1.9-continuity-v2",
|
|
1580
|
+
apply: (db2) => {
|
|
1581
|
+
addColumnIfMissing(db2, "task_continuity_events", "event_key", "event_key TEXT");
|
|
1582
|
+
addColumnIfMissing(db2, "task_continuity_events", "item_key", "item_key TEXT");
|
|
1583
|
+
addColumnIfMissing(db2, "task_continuity_events", "supersedes_id", "supersedes_id TEXT");
|
|
1584
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC)");
|
|
1585
|
+
db2.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
|
|
1586
|
+
ON task_continuity_events(project_id, task_id, event_key)
|
|
1587
|
+
WHERE event_key IS NOT NULL`);
|
|
1588
|
+
}
|
|
1511
1589
|
}
|
|
1512
1590
|
];
|
|
1513
1591
|
_dbCache = /* @__PURE__ */ new Map();
|
|
1592
|
+
_dbLeases = /* @__PURE__ */ new Map();
|
|
1514
1593
|
}
|
|
1515
1594
|
});
|
|
1516
1595
|
|
|
@@ -1518,6 +1597,21 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
1518
1597
|
import { promises as fs2 } from "fs";
|
|
1519
1598
|
import { randomUUID } from "crypto";
|
|
1520
1599
|
import path4 from "path";
|
|
1600
|
+
function canReclaimStaleLock(raw) {
|
|
1601
|
+
try {
|
|
1602
|
+
const parsed = JSON.parse(raw);
|
|
1603
|
+
const pid = Number(parsed.pid);
|
|
1604
|
+
if (!Number.isSafeInteger(pid) || pid < 1) return true;
|
|
1605
|
+
try {
|
|
1606
|
+
process.kill(pid, 0);
|
|
1607
|
+
return false;
|
|
1608
|
+
} catch (error) {
|
|
1609
|
+
return error.code !== "EPERM";
|
|
1610
|
+
}
|
|
1611
|
+
} catch {
|
|
1612
|
+
return true;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1521
1615
|
async function acquireLock(lockPath) {
|
|
1522
1616
|
for (let i2 = 0; i2 < MAX_RETRIES; i2++) {
|
|
1523
1617
|
try {
|
|
@@ -1531,9 +1625,12 @@ async function acquireLock(lockPath) {
|
|
|
1531
1625
|
try {
|
|
1532
1626
|
const stat5 = await fs2.stat(lockPath);
|
|
1533
1627
|
if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
|
|
1534
|
-
await fs2.
|
|
1535
|
-
|
|
1536
|
-
|
|
1628
|
+
const raw2 = await fs2.readFile(lockPath, "utf8").catch(() => "");
|
|
1629
|
+
if (canReclaimStaleLock(raw2)) {
|
|
1630
|
+
await fs2.unlink(lockPath).catch(() => {
|
|
1631
|
+
});
|
|
1632
|
+
continue;
|
|
1633
|
+
}
|
|
1537
1634
|
}
|
|
1538
1635
|
} catch {
|
|
1539
1636
|
continue;
|
|
@@ -1544,6 +1641,10 @@ async function acquireLock(lockPath) {
|
|
|
1544
1641
|
}
|
|
1545
1642
|
}
|
|
1546
1643
|
}
|
|
1644
|
+
const raw = await fs2.readFile(lockPath, "utf8").catch(() => "");
|
|
1645
|
+
if (!canReclaimStaleLock(raw)) {
|
|
1646
|
+
throw new Error(`Failed to acquire lock: ${lockPath} (owner is still running)`);
|
|
1647
|
+
}
|
|
1547
1648
|
await fs2.unlink(lockPath).catch(() => {
|
|
1548
1649
|
});
|
|
1549
1650
|
try {
|
|
@@ -1782,6 +1883,11 @@ async function listProjectDirs(baseDir) {
|
|
|
1782
1883
|
}
|
|
1783
1884
|
}
|
|
1784
1885
|
async function migrateSubdirsToFlat(baseDir) {
|
|
1886
|
+
const base = baseDir ?? resolveDefaultDataDir();
|
|
1887
|
+
await fs4.mkdir(base, { recursive: true });
|
|
1888
|
+
return withFileLock(base, () => migrateSubdirsToFlatUnlocked(base));
|
|
1889
|
+
}
|
|
1890
|
+
async function migrateSubdirsToFlatUnlocked(baseDir) {
|
|
1785
1891
|
const base = baseDir ?? resolveDefaultDataDir();
|
|
1786
1892
|
await fs4.mkdir(base, { recursive: true });
|
|
1787
1893
|
let entries;
|
|
@@ -1838,7 +1944,7 @@ async function migrateSubdirsToFlat(baseDir) {
|
|
|
1838
1944
|
for (const { obs } of subdirData) {
|
|
1839
1945
|
for (const o of obs) {
|
|
1840
1946
|
const isDuplicate = allObs.some(
|
|
1841
|
-
(existing) => existing.title === o.title && existing.createdAt === o.createdAt
|
|
1947
|
+
(existing) => existing.title === o.title && existing.createdAt === o.createdAt && existing.projectId === o.projectId
|
|
1842
1948
|
);
|
|
1843
1949
|
if (!isDuplicate) {
|
|
1844
1950
|
allObs.push(o);
|
|
@@ -1871,18 +1977,14 @@ async function migrateSubdirsToFlat(baseDir) {
|
|
|
1871
1977
|
mergedRelations.push(rel);
|
|
1872
1978
|
}
|
|
1873
1979
|
}
|
|
1874
|
-
await
|
|
1875
|
-
await
|
|
1876
|
-
path6.join(base, "counter.json"),
|
|
1877
|
-
JSON.stringify({ nextId: allObs.length + 1 }),
|
|
1878
|
-
"utf-8"
|
|
1879
|
-
);
|
|
1980
|
+
await atomicWriteFile(path6.join(base, "observations.json"), JSON.stringify(allObs, null, 2));
|
|
1981
|
+
await atomicWriteFile(path6.join(base, "counter.json"), JSON.stringify({ nextId: allObs.length + 1 }));
|
|
1880
1982
|
const graphLines = [
|
|
1881
1983
|
...[...entityMap.values()].map((e2) => JSON.stringify({ type: "entity", name: e2.name, entityType: e2.entityType, observations: e2.observations })),
|
|
1882
1984
|
...mergedRelations.map((r2) => JSON.stringify({ type: "relation", from: r2.from, to: r2.to, relationType: r2.relationType }))
|
|
1883
1985
|
];
|
|
1884
1986
|
if (graphLines.length > 0) {
|
|
1885
|
-
await
|
|
1987
|
+
await atomicWriteFile(path6.join(base, "graph.jsonl"), graphLines.join("\n"));
|
|
1886
1988
|
}
|
|
1887
1989
|
let allSessions = [];
|
|
1888
1990
|
try {
|
|
@@ -1900,7 +2002,7 @@ async function migrateSubdirsToFlat(baseDir) {
|
|
|
1900
2002
|
}
|
|
1901
2003
|
}
|
|
1902
2004
|
if (allSessions.length > 0) {
|
|
1903
|
-
await
|
|
2005
|
+
await atomicWriteFile(path6.join(base, "sessions.json"), JSON.stringify(allSessions, null, 2));
|
|
1904
2006
|
}
|
|
1905
2007
|
const backupDir = path6.join(base, ".migrated-subdirs");
|
|
1906
2008
|
await fs4.mkdir(backupDir, { recursive: true });
|
|
@@ -1936,6 +2038,7 @@ var init_persistence = __esm({
|
|
|
1936
2038
|
"src/store/persistence.ts"() {
|
|
1937
2039
|
"use strict";
|
|
1938
2040
|
init_esm_shims();
|
|
2041
|
+
init_file_lock();
|
|
1939
2042
|
init_persistence_json();
|
|
1940
2043
|
}
|
|
1941
2044
|
});
|
|
@@ -1943,6 +2046,7 @@ var init_persistence = __esm({
|
|
|
1943
2046
|
// src/store/graph-store.ts
|
|
1944
2047
|
var graph_store_exports = {};
|
|
1945
2048
|
__export(graph_store_exports, {
|
|
2049
|
+
DEFAULT_GRAPH_PROJECT_ID: () => DEFAULT_GRAPH_PROJECT_ID,
|
|
1946
2050
|
GraphSqliteStore: () => GraphSqliteStore,
|
|
1947
2051
|
getGraphStore: () => getGraphStore,
|
|
1948
2052
|
initGraphStore: () => initGraphStore,
|
|
@@ -1972,37 +2076,106 @@ function entityToRow(entity) {
|
|
|
1972
2076
|
observations: JSON.stringify(entity.observations ?? [])
|
|
1973
2077
|
};
|
|
1974
2078
|
}
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2079
|
+
function ensureScopedGraphSchema(db2) {
|
|
2080
|
+
const entityColumns = db2.prepare("PRAGMA table_info(graph_entities)").all();
|
|
2081
|
+
if (!entityColumns.some((column) => column.name === "projectId")) {
|
|
2082
|
+
const migrate = db2.transaction(() => {
|
|
2083
|
+
db2.exec("ALTER TABLE graph_entities RENAME TO graph_entities_legacy_unscoped");
|
|
2084
|
+
db2.exec(CREATE_SCOPED_GRAPH_ENTITIES);
|
|
2085
|
+
db2.exec(`INSERT INTO graph_entities (projectId, name, entityType, observations)
|
|
2086
|
+
SELECT '${LEGACY_GRAPH_PROJECT_ID}', name, entityType, observations
|
|
2087
|
+
FROM graph_entities_legacy_unscoped`);
|
|
2088
|
+
db2.exec("DROP TABLE graph_entities_legacy_unscoped");
|
|
2089
|
+
});
|
|
2090
|
+
migrate();
|
|
2091
|
+
} else {
|
|
2092
|
+
db2.exec(CREATE_SCOPED_GRAPH_ENTITIES);
|
|
2093
|
+
}
|
|
2094
|
+
const relationColumns = db2.prepare("PRAGMA table_info(graph_relations)").all();
|
|
2095
|
+
if (!relationColumns.some((column) => column.name === "projectId")) {
|
|
2096
|
+
const migrate = db2.transaction(() => {
|
|
2097
|
+
db2.exec("ALTER TABLE graph_relations RENAME TO graph_relations_legacy_unscoped");
|
|
2098
|
+
db2.exec(CREATE_SCOPED_GRAPH_RELATIONS);
|
|
2099
|
+
db2.exec(`INSERT INTO graph_relations (projectId, from_entity, to_entity, relationType)
|
|
2100
|
+
SELECT '${LEGACY_GRAPH_PROJECT_ID}', from_entity, to_entity, relationType
|
|
2101
|
+
FROM graph_relations_legacy_unscoped`);
|
|
2102
|
+
db2.exec("DROP TABLE graph_relations_legacy_unscoped");
|
|
2103
|
+
});
|
|
2104
|
+
migrate();
|
|
2105
|
+
} else {
|
|
2106
|
+
db2.exec(CREATE_SCOPED_GRAPH_RELATIONS);
|
|
2107
|
+
}
|
|
1981
2108
|
}
|
|
1982
|
-
function
|
|
1983
|
-
|
|
1984
|
-
|
|
2109
|
+
function graphStoreKey(dataDir, projectId) {
|
|
2110
|
+
return `${path7.resolve(dataDir)}\0${projectId}`;
|
|
2111
|
+
}
|
|
2112
|
+
async function initGraphStore(dataDir, projectId = DEFAULT_GRAPH_PROJECT_ID) {
|
|
2113
|
+
const key = graphStoreKey(dataDir, projectId);
|
|
2114
|
+
const existing = _graphStores.get(key);
|
|
2115
|
+
if (existing) {
|
|
2116
|
+
_graphStores.delete(key);
|
|
2117
|
+
_graphStores.set(key, existing);
|
|
2118
|
+
_activeGraphKey = key;
|
|
2119
|
+
return existing;
|
|
2120
|
+
}
|
|
2121
|
+
const store = new GraphSqliteStore();
|
|
2122
|
+
await store.init(dataDir, projectId);
|
|
2123
|
+
_graphStores.set(key, store);
|
|
2124
|
+
while (_graphStores.size > MAX_GRAPH_STORE_CACHE) {
|
|
2125
|
+
const oldest = _graphStores.keys().next().value;
|
|
2126
|
+
if (!oldest) break;
|
|
2127
|
+
_graphStores.delete(oldest);
|
|
2128
|
+
}
|
|
2129
|
+
_activeGraphKey = key;
|
|
2130
|
+
return store;
|
|
2131
|
+
}
|
|
2132
|
+
function getGraphStore(dataDir, projectId) {
|
|
2133
|
+
const key = dataDir && projectId ? graphStoreKey(dataDir, projectId) : _activeGraphKey;
|
|
2134
|
+
const store = key ? _graphStores.get(key) : void 0;
|
|
2135
|
+
if (!store) throw new Error("[memorix] GraphStore not initialized \u2014 call initGraphStore() first");
|
|
2136
|
+
return store;
|
|
1985
2137
|
}
|
|
1986
2138
|
function resetGraphStore() {
|
|
1987
|
-
|
|
2139
|
+
for (const store of _graphStores.values()) {
|
|
1988
2140
|
try {
|
|
1989
|
-
|
|
2141
|
+
store.close();
|
|
1990
2142
|
} catch {
|
|
1991
2143
|
}
|
|
1992
2144
|
}
|
|
1993
|
-
|
|
1994
|
-
|
|
2145
|
+
_graphStores.clear();
|
|
2146
|
+
_activeGraphKey = null;
|
|
1995
2147
|
}
|
|
1996
|
-
var GraphSqliteStore,
|
|
2148
|
+
var DEFAULT_GRAPH_PROJECT_ID, LEGACY_GRAPH_PROJECT_ID, MAX_GRAPH_STORE_CACHE, CREATE_SCOPED_GRAPH_ENTITIES, CREATE_SCOPED_GRAPH_RELATIONS, GraphSqliteStore, _graphStores, _activeGraphKey;
|
|
1997
2149
|
var init_graph_store = __esm({
|
|
1998
2150
|
"src/store/graph-store.ts"() {
|
|
1999
2151
|
"use strict";
|
|
2000
2152
|
init_esm_shims();
|
|
2001
2153
|
init_sqlite_db();
|
|
2002
2154
|
init_persistence();
|
|
2155
|
+
DEFAULT_GRAPH_PROJECT_ID = "__default__";
|
|
2156
|
+
LEGACY_GRAPH_PROJECT_ID = "__legacy__";
|
|
2157
|
+
MAX_GRAPH_STORE_CACHE = 32;
|
|
2158
|
+
CREATE_SCOPED_GRAPH_ENTITIES = `
|
|
2159
|
+
CREATE TABLE IF NOT EXISTS graph_entities (
|
|
2160
|
+
projectId TEXT NOT NULL,
|
|
2161
|
+
name TEXT NOT NULL,
|
|
2162
|
+
entityType TEXT NOT NULL DEFAULT '',
|
|
2163
|
+
observations TEXT NOT NULL DEFAULT '[]',
|
|
2164
|
+
PRIMARY KEY (projectId, name)
|
|
2165
|
+
);`;
|
|
2166
|
+
CREATE_SCOPED_GRAPH_RELATIONS = `
|
|
2167
|
+
CREATE TABLE IF NOT EXISTS graph_relations (
|
|
2168
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
2169
|
+
projectId TEXT NOT NULL,
|
|
2170
|
+
from_entity TEXT NOT NULL,
|
|
2171
|
+
to_entity TEXT NOT NULL,
|
|
2172
|
+
relationType TEXT NOT NULL DEFAULT '',
|
|
2173
|
+
UNIQUE(projectId, from_entity, to_entity, relationType)
|
|
2174
|
+
);`;
|
|
2003
2175
|
GraphSqliteStore = class {
|
|
2004
2176
|
db = null;
|
|
2005
2177
|
dataDir = "";
|
|
2178
|
+
projectId = DEFAULT_GRAPH_PROJECT_ID;
|
|
2006
2179
|
stmtInsertEntity = null;
|
|
2007
2180
|
stmtUpdateEntityObs = null;
|
|
2008
2181
|
stmtDeleteEntity = null;
|
|
@@ -2012,45 +2185,89 @@ var init_graph_store = __esm({
|
|
|
2012
2185
|
stmtDeleteRelation = null;
|
|
2013
2186
|
stmtDeleteRelationsByEntity = null;
|
|
2014
2187
|
stmtSelectAllRelations = null;
|
|
2015
|
-
async init(dataDir) {
|
|
2188
|
+
async init(dataDir, projectId = DEFAULT_GRAPH_PROJECT_ID) {
|
|
2016
2189
|
this.dataDir = dataDir;
|
|
2190
|
+
this.projectId = projectId;
|
|
2017
2191
|
this.db = getDatabase(dataDir);
|
|
2192
|
+
ensureScopedGraphSchema(this.db);
|
|
2018
2193
|
this.stmtInsertEntity = this.db.prepare(
|
|
2019
|
-
`INSERT OR REPLACE INTO graph_entities (name, entityType, observations) VALUES (@name, @entityType, @observations)`
|
|
2194
|
+
`INSERT OR REPLACE INTO graph_entities (projectId, name, entityType, observations) VALUES (@projectId, @name, @entityType, @observations)`
|
|
2020
2195
|
);
|
|
2021
2196
|
this.stmtUpdateEntityObs = this.db.prepare(
|
|
2022
|
-
`UPDATE graph_entities SET observations = @observations WHERE name = @name`
|
|
2197
|
+
`UPDATE graph_entities SET observations = @observations WHERE projectId = @projectId AND name = @name`
|
|
2023
2198
|
);
|
|
2024
|
-
this.stmtDeleteEntity = this.db.prepare(`DELETE FROM graph_entities WHERE name = ?`);
|
|
2025
|
-
this.stmtSelectAllEntities = this.db.prepare(`SELECT * FROM graph_entities`);
|
|
2026
|
-
this.stmtSelectEntityByName = this.db.prepare(`SELECT * FROM graph_entities WHERE name = ?`);
|
|
2199
|
+
this.stmtDeleteEntity = this.db.prepare(`DELETE FROM graph_entities WHERE projectId = ? AND name = ?`);
|
|
2200
|
+
this.stmtSelectAllEntities = this.db.prepare(`SELECT * FROM graph_entities WHERE projectId = ? ORDER BY name`);
|
|
2201
|
+
this.stmtSelectEntityByName = this.db.prepare(`SELECT * FROM graph_entities WHERE projectId = ? AND name = ?`);
|
|
2027
2202
|
this.stmtInsertRelation = this.db.prepare(
|
|
2028
|
-
`INSERT OR IGNORE INTO graph_relations (from_entity, to_entity, relationType) VALUES (@from, @to, @relationType)`
|
|
2203
|
+
`INSERT OR IGNORE INTO graph_relations (projectId, from_entity, to_entity, relationType) VALUES (@projectId, @from, @to, @relationType)`
|
|
2029
2204
|
);
|
|
2030
2205
|
this.stmtDeleteRelation = this.db.prepare(
|
|
2031
|
-
`DELETE FROM graph_relations WHERE from_entity = ? AND to_entity = ? AND relationType = ?`
|
|
2206
|
+
`DELETE FROM graph_relations WHERE projectId = ? AND from_entity = ? AND to_entity = ? AND relationType = ?`
|
|
2032
2207
|
);
|
|
2033
2208
|
this.stmtDeleteRelationsByEntity = this.db.prepare(
|
|
2034
|
-
`DELETE FROM graph_relations WHERE from_entity = ? OR to_entity =
|
|
2209
|
+
`DELETE FROM graph_relations WHERE projectId = ? AND (from_entity = ? OR to_entity = ?)`
|
|
2035
2210
|
);
|
|
2036
|
-
this.stmtSelectAllRelations = this.db.prepare(`SELECT * FROM graph_relations`);
|
|
2211
|
+
this.stmtSelectAllRelations = this.db.prepare(`SELECT * FROM graph_relations WHERE projectId = ? ORDER BY id`);
|
|
2037
2212
|
await this.migrateFromJsonlIfNeeded();
|
|
2038
2213
|
}
|
|
2039
2214
|
async migrateFromJsonlIfNeeded() {
|
|
2040
|
-
const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities
|
|
2215
|
+
const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities WHERE projectId = ?`).get(this.projectId);
|
|
2216
|
+
const totalCount = this.db.prepare(`SELECT COUNT(*) AS cnt FROM graph_entities`).get();
|
|
2041
2217
|
if (count2.cnt > 0) return;
|
|
2218
|
+
if (totalCount.cnt > 0) return;
|
|
2042
2219
|
const jsonlPath = path7.join(this.dataDir, "graph.jsonl");
|
|
2043
2220
|
if (!fs5.existsSync(jsonlPath)) return;
|
|
2044
2221
|
try {
|
|
2045
2222
|
const data = await loadGraphJsonl(this.dataDir);
|
|
2046
2223
|
if (data.entities.length === 0 && data.relations.length === 0) return;
|
|
2047
2224
|
console.error(`[memorix] Migrating graph from JSONL to SQLite (${data.entities.length} entities, ${data.relations.length} relations)...`);
|
|
2225
|
+
const observations2 = this.db.prepare("SELECT id, projectId FROM observations").all();
|
|
2226
|
+
const projectByObservationId = new Map(observations2.map((row) => [row.id, row.projectId]));
|
|
2227
|
+
const projectsByEntity = /* @__PURE__ */ new Map();
|
|
2228
|
+
for (const entity of data.entities) {
|
|
2229
|
+
const projects = /* @__PURE__ */ new Set();
|
|
2230
|
+
for (const content of entity.observations ?? []) {
|
|
2231
|
+
const match2 = /^\[#(\d+)\]/.exec(content);
|
|
2232
|
+
const projectId = match2 ? projectByObservationId.get(Number(match2[1])) : void 0;
|
|
2233
|
+
if (projectId) projects.add(projectId);
|
|
2234
|
+
}
|
|
2235
|
+
projectsByEntity.set(entity.name, projects);
|
|
2236
|
+
}
|
|
2237
|
+
let changed = true;
|
|
2238
|
+
while (changed) {
|
|
2239
|
+
changed = false;
|
|
2240
|
+
for (const relation of data.relations) {
|
|
2241
|
+
const from = projectsByEntity.get(relation.from) ?? /* @__PURE__ */ new Set();
|
|
2242
|
+
const to = projectsByEntity.get(relation.to) ?? /* @__PURE__ */ new Set();
|
|
2243
|
+
if (from.size > 0 && to.size === 0) {
|
|
2244
|
+
projectsByEntity.set(relation.to, new Set(from));
|
|
2245
|
+
changed = true;
|
|
2246
|
+
} else if (to.size > 0 && from.size === 0) {
|
|
2247
|
+
projectsByEntity.set(relation.from, new Set(to));
|
|
2248
|
+
changed = true;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
const entityRows = data.entities.flatMap((entity) => {
|
|
2253
|
+
const projects = projectsByEntity.get(entity.name) ?? /* @__PURE__ */ new Set();
|
|
2254
|
+
const owners = projects.size > 0 ? [...projects] : [this.projectId];
|
|
2255
|
+
return owners.map((projectId) => ({ ...entityToRow(entity), projectId }));
|
|
2256
|
+
});
|
|
2257
|
+
const projectSet = new Set(entityRows.map((row) => String(row.projectId)));
|
|
2258
|
+
const relationRows = data.relations.flatMap((relation) => {
|
|
2259
|
+
const from = projectsByEntity.get(relation.from) ?? /* @__PURE__ */ new Set();
|
|
2260
|
+
const to = projectsByEntity.get(relation.to) ?? /* @__PURE__ */ new Set();
|
|
2261
|
+
const shared = [...from].filter((projectId) => to.has(projectId));
|
|
2262
|
+
const owners = shared.length > 0 ? shared : from.size === 0 && to.size === 0 ? [this.projectId] : [];
|
|
2263
|
+
return owners.filter((projectId) => projectSet.has(projectId)).map((projectId) => ({ projectId, from: relation.from, to: relation.to, relationType: relation.relationType }));
|
|
2264
|
+
});
|
|
2048
2265
|
const insertAll = this.db.transaction(() => {
|
|
2049
|
-
for (const entity of
|
|
2050
|
-
this.stmtInsertEntity.run(
|
|
2266
|
+
for (const entity of entityRows) {
|
|
2267
|
+
this.stmtInsertEntity.run(entity);
|
|
2051
2268
|
}
|
|
2052
|
-
for (const
|
|
2053
|
-
this.stmtInsertRelation.run(
|
|
2269
|
+
for (const relation of relationRows) {
|
|
2270
|
+
this.stmtInsertRelation.run(relation);
|
|
2054
2271
|
}
|
|
2055
2272
|
});
|
|
2056
2273
|
insertAll();
|
|
@@ -2060,10 +2277,10 @@ var init_graph_store = __esm({
|
|
|
2060
2277
|
}
|
|
2061
2278
|
}
|
|
2062
2279
|
loadEntities() {
|
|
2063
|
-
return this.stmtSelectAllEntities.all().map(rowToEntity);
|
|
2280
|
+
return this.stmtSelectAllEntities.all(this.projectId).map(rowToEntity);
|
|
2064
2281
|
}
|
|
2065
2282
|
loadRelations() {
|
|
2066
|
-
return this.stmtSelectAllRelations.all().map((row) => ({
|
|
2283
|
+
return this.stmtSelectAllRelations.all(this.projectId).map((row) => ({
|
|
2067
2284
|
from: row.from_entity,
|
|
2068
2285
|
to: row.to_entity,
|
|
2069
2286
|
relationType: row.relationType
|
|
@@ -2072,7 +2289,7 @@ var init_graph_store = __esm({
|
|
|
2072
2289
|
insertEntities(entities) {
|
|
2073
2290
|
const insertAll = this.db.transaction(() => {
|
|
2074
2291
|
for (const entity of entities) {
|
|
2075
|
-
this.stmtInsertEntity.run(entityToRow(entity));
|
|
2292
|
+
this.stmtInsertEntity.run({ ...entityToRow(entity), projectId: this.projectId });
|
|
2076
2293
|
}
|
|
2077
2294
|
});
|
|
2078
2295
|
insertAll();
|
|
@@ -2080,7 +2297,7 @@ var init_graph_store = __esm({
|
|
|
2080
2297
|
insertRelations(relations) {
|
|
2081
2298
|
const insertAll = this.db.transaction(() => {
|
|
2082
2299
|
for (const rel of relations) {
|
|
2083
|
-
this.stmtInsertRelation.run({ from: rel.from, to: rel.to, relationType: rel.relationType });
|
|
2300
|
+
this.stmtInsertRelation.run({ projectId: this.projectId, from: rel.from, to: rel.to, relationType: rel.relationType });
|
|
2084
2301
|
}
|
|
2085
2302
|
});
|
|
2086
2303
|
insertAll();
|
|
@@ -2088,8 +2305,8 @@ var init_graph_store = __esm({
|
|
|
2088
2305
|
deleteEntities(names) {
|
|
2089
2306
|
const deleteAll = this.db.transaction(() => {
|
|
2090
2307
|
for (const name of names) {
|
|
2091
|
-
this.stmtDeleteRelationsByEntity.run(name, name);
|
|
2092
|
-
this.stmtDeleteEntity.run(name);
|
|
2308
|
+
this.stmtDeleteRelationsByEntity.run(this.projectId, name, name);
|
|
2309
|
+
this.stmtDeleteEntity.run(this.projectId, name);
|
|
2093
2310
|
}
|
|
2094
2311
|
});
|
|
2095
2312
|
deleteAll();
|
|
@@ -2097,7 +2314,7 @@ var init_graph_store = __esm({
|
|
|
2097
2314
|
deleteRelations(relations) {
|
|
2098
2315
|
const deleteAll = this.db.transaction(() => {
|
|
2099
2316
|
for (const rel of relations) {
|
|
2100
|
-
this.stmtDeleteRelation.run(rel.from, rel.to, rel.relationType);
|
|
2317
|
+
this.stmtDeleteRelation.run(this.projectId, rel.from, rel.to, rel.relationType);
|
|
2101
2318
|
}
|
|
2102
2319
|
});
|
|
2103
2320
|
deleteAll();
|
|
@@ -2105,13 +2322,13 @@ var init_graph_store = __esm({
|
|
|
2105
2322
|
addObservations(updates) {
|
|
2106
2323
|
const updateAll = this.db.transaction(() => {
|
|
2107
2324
|
for (const u of updates) {
|
|
2108
|
-
const row = this.stmtSelectEntityByName.get(u.entityName);
|
|
2325
|
+
const row = this.stmtSelectEntityByName.get(this.projectId, u.entityName);
|
|
2109
2326
|
if (!row) continue;
|
|
2110
2327
|
const existing = safeJsonParse(row.observations, []);
|
|
2111
2328
|
const newObs = u.contents.filter((c) => !existing.includes(c));
|
|
2112
2329
|
if (newObs.length > 0) {
|
|
2113
2330
|
existing.push(...newObs);
|
|
2114
|
-
this.stmtUpdateEntityObs.run({ name: u.entityName, observations: JSON.stringify(existing) });
|
|
2331
|
+
this.stmtUpdateEntityObs.run({ projectId: this.projectId, name: u.entityName, observations: JSON.stringify(existing) });
|
|
2115
2332
|
}
|
|
2116
2333
|
}
|
|
2117
2334
|
});
|
|
@@ -2120,11 +2337,11 @@ var init_graph_store = __esm({
|
|
|
2120
2337
|
deleteObservations(deletions) {
|
|
2121
2338
|
const deleteAll = this.db.transaction(() => {
|
|
2122
2339
|
for (const d of deletions) {
|
|
2123
|
-
const row = this.stmtSelectEntityByName.get(d.entityName);
|
|
2340
|
+
const row = this.stmtSelectEntityByName.get(this.projectId, d.entityName);
|
|
2124
2341
|
if (!row) continue;
|
|
2125
2342
|
const existing = safeJsonParse(row.observations, []);
|
|
2126
2343
|
const filtered = existing.filter((o) => !d.observations.includes(o));
|
|
2127
|
-
this.stmtUpdateEntityObs.run({ name: d.entityName, observations: JSON.stringify(filtered) });
|
|
2344
|
+
this.stmtUpdateEntityObs.run({ projectId: this.projectId, name: d.entityName, observations: JSON.stringify(filtered) });
|
|
2128
2345
|
}
|
|
2129
2346
|
});
|
|
2130
2347
|
deleteAll();
|
|
@@ -2135,13 +2352,13 @@ var init_graph_store = __esm({
|
|
|
2135
2352
|
*/
|
|
2136
2353
|
replaceAll(entities, relations) {
|
|
2137
2354
|
const replaceAll = this.db.transaction(() => {
|
|
2138
|
-
this.db.prepare("DELETE FROM graph_relations").run();
|
|
2139
|
-
this.db.prepare("DELETE FROM graph_entities").run();
|
|
2355
|
+
this.db.prepare("DELETE FROM graph_relations WHERE projectId = ?").run(this.projectId);
|
|
2356
|
+
this.db.prepare("DELETE FROM graph_entities WHERE projectId = ?").run(this.projectId);
|
|
2140
2357
|
for (const entity of entities) {
|
|
2141
|
-
this.stmtInsertEntity.run(entityToRow(entity));
|
|
2358
|
+
this.stmtInsertEntity.run({ ...entityToRow(entity), projectId: this.projectId });
|
|
2142
2359
|
}
|
|
2143
2360
|
for (const rel of relations) {
|
|
2144
|
-
this.stmtInsertRelation.run({ from: rel.from, to: rel.to, relationType: rel.relationType });
|
|
2361
|
+
this.stmtInsertRelation.run({ projectId: this.projectId, from: rel.from, to: rel.to, relationType: rel.relationType });
|
|
2145
2362
|
}
|
|
2146
2363
|
});
|
|
2147
2364
|
replaceAll();
|
|
@@ -2149,8 +2366,8 @@ var init_graph_store = __esm({
|
|
|
2149
2366
|
close() {
|
|
2150
2367
|
}
|
|
2151
2368
|
};
|
|
2152
|
-
|
|
2153
|
-
|
|
2369
|
+
_graphStores = /* @__PURE__ */ new Map();
|
|
2370
|
+
_activeGraphKey = null;
|
|
2154
2371
|
}
|
|
2155
2372
|
});
|
|
2156
2373
|
|
|
@@ -2165,11 +2382,14 @@ var init_graph = __esm({
|
|
|
2165
2382
|
entities = [];
|
|
2166
2383
|
relations = [];
|
|
2167
2384
|
projectDir;
|
|
2385
|
+
projectId;
|
|
2386
|
+
store = null;
|
|
2168
2387
|
initialized = false;
|
|
2169
2388
|
/** Index: lowercase entity name → Entity for O(1) lookups */
|
|
2170
2389
|
entityIndex = /* @__PURE__ */ new Map();
|
|
2171
|
-
constructor(projectDir2) {
|
|
2390
|
+
constructor(projectDir2, projectId = "__default__") {
|
|
2172
2391
|
this.projectDir = projectDir2;
|
|
2392
|
+
this.projectId = projectId;
|
|
2173
2393
|
}
|
|
2174
2394
|
/** Rebuild the entity name index */
|
|
2175
2395
|
rebuildIndex() {
|
|
@@ -2181,10 +2401,9 @@ var init_graph = __esm({
|
|
|
2181
2401
|
/** Load graph from SQLite on first access */
|
|
2182
2402
|
async init() {
|
|
2183
2403
|
if (this.initialized) return;
|
|
2184
|
-
await initGraphStore(this.projectDir);
|
|
2185
|
-
|
|
2186
|
-
this.
|
|
2187
|
-
this.relations = store.loadRelations();
|
|
2404
|
+
this.store = await initGraphStore(this.projectDir, this.projectId);
|
|
2405
|
+
this.entities = this.store.loadEntities();
|
|
2406
|
+
this.relations = this.store.loadRelations();
|
|
2188
2407
|
this.rebuildIndex();
|
|
2189
2408
|
this.initialized = true;
|
|
2190
2409
|
}
|
|
@@ -2198,8 +2417,8 @@ var init_graph = __esm({
|
|
|
2198
2417
|
}
|
|
2199
2418
|
/** Persist current state to SQLite */
|
|
2200
2419
|
async save() {
|
|
2201
|
-
|
|
2202
|
-
store.replaceAll(this.entities, this.relations);
|
|
2420
|
+
if (!this.store) throw new Error("[memorix] KnowledgeGraphManager not initialized");
|
|
2421
|
+
this.store.replaceAll(this.entities, this.relations);
|
|
2203
2422
|
}
|
|
2204
2423
|
/** Create new entities (skip duplicates by name) */
|
|
2205
2424
|
async createEntities(entities) {
|
|
@@ -3085,18 +3304,23 @@ __export(yaml_loader_exports, {
|
|
|
3085
3304
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
3086
3305
|
import { homedir } from "os";
|
|
3087
3306
|
import { parse as parseYamlDocument } from "yaml";
|
|
3307
|
+
function cacheKey(homeDir, projectRoot) {
|
|
3308
|
+
return `${homeDir}\0${projectRoot ?? ""}`;
|
|
3309
|
+
}
|
|
3088
3310
|
function initProjectRoot(root) {
|
|
3089
3311
|
globalProjectRoot = root;
|
|
3090
|
-
configCache.delete(root);
|
|
3312
|
+
configCache.delete(cacheKey(homedir(), root));
|
|
3091
3313
|
}
|
|
3092
3314
|
function clearProjectRoot() {
|
|
3093
3315
|
globalProjectRoot = null;
|
|
3094
3316
|
}
|
|
3095
|
-
function loadYamlConfig(projectRoot) {
|
|
3317
|
+
function loadYamlConfig(projectRoot, options = {}) {
|
|
3318
|
+
const homeDir = options.homeDir ?? homedir();
|
|
3096
3319
|
const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
|
|
3097
|
-
const
|
|
3320
|
+
const key = cacheKey(homeDir, resolvedRoot);
|
|
3321
|
+
const cached = configCache.get(key);
|
|
3098
3322
|
if (cached) return cached;
|
|
3099
|
-
const userYaml = getGlobalYamlPath(
|
|
3323
|
+
const userYaml = getGlobalYamlPath(homeDir);
|
|
3100
3324
|
const projectYaml = resolvedRoot ? getProjectYamlPath(resolvedRoot) : null;
|
|
3101
3325
|
let userConfig = {};
|
|
3102
3326
|
let projectConfig = {};
|
|
@@ -3128,12 +3352,12 @@ function loadYamlConfig(projectRoot) {
|
|
|
3128
3352
|
server: { ...userConfig.server, ...projectConfig.server },
|
|
3129
3353
|
team: { ...userConfig.team, ...projectConfig.team }
|
|
3130
3354
|
};
|
|
3131
|
-
configCache.set(
|
|
3355
|
+
configCache.set(key, merged);
|
|
3132
3356
|
return merged;
|
|
3133
3357
|
}
|
|
3134
|
-
function resetYamlConfigCache(projectRoot) {
|
|
3358
|
+
function resetYamlConfigCache(projectRoot, homeDir = homedir()) {
|
|
3135
3359
|
if (projectRoot !== void 0) {
|
|
3136
|
-
configCache.delete(projectRoot ?? null);
|
|
3360
|
+
configCache.delete(cacheKey(homeDir, projectRoot ?? null));
|
|
3137
3361
|
} else {
|
|
3138
3362
|
configCache.clear();
|
|
3139
3363
|
}
|
|
@@ -3343,9 +3567,9 @@ var init_detector = __esm({
|
|
|
3343
3567
|
// src/config/legacy-loader.ts
|
|
3344
3568
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
3345
3569
|
import { homedir as homedir2 } from "os";
|
|
3346
|
-
function loadFileConfig() {
|
|
3570
|
+
function loadFileConfig(homeDir = homedir2()) {
|
|
3347
3571
|
if (cachedConfig !== null) return cachedConfig;
|
|
3348
|
-
const configPath = getLegacyConfigJsonPath(
|
|
3572
|
+
const configPath = getLegacyConfigJsonPath(homeDir);
|
|
3349
3573
|
try {
|
|
3350
3574
|
if (existsSync4(configPath)) {
|
|
3351
3575
|
cachedConfig = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
@@ -3375,15 +3599,15 @@ import { homedir as homedir3 } from "os";
|
|
|
3375
3599
|
function loadTomlConfig(options = {}) {
|
|
3376
3600
|
const homeDir = options.homeDir ?? homedir3();
|
|
3377
3601
|
const projectRoot = options.projectRoot ?? null;
|
|
3378
|
-
const
|
|
3379
|
-
const cached = configCache2.get(
|
|
3602
|
+
const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
|
|
3603
|
+
const cached = configCache2.get(cacheKey3);
|
|
3380
3604
|
if (cached) return cached;
|
|
3381
3605
|
const userToml = getGlobalConfigTomlPath(homeDir);
|
|
3382
3606
|
const projectToml = projectRoot ? getProjectConfigTomlPath(projectRoot) : null;
|
|
3383
3607
|
const userConfig = existsSync5(userToml) ? parseTomlConfig(readFileSync5(userToml, "utf8"), userToml) : {};
|
|
3384
3608
|
const projectConfig = projectToml && existsSync5(projectToml) ? parseTomlConfig(readFileSync5(projectToml, "utf8"), projectToml) : {};
|
|
3385
3609
|
const merged = mergeTomlConfig(userConfig, projectConfig);
|
|
3386
|
-
configCache2.set(
|
|
3610
|
+
configCache2.set(cacheKey3, merged);
|
|
3387
3611
|
return merged;
|
|
3388
3612
|
}
|
|
3389
3613
|
function mergeTomlConfig(base, override) {
|
|
@@ -3583,8 +3807,8 @@ function getResolvedConfig(options = {}) {
|
|
|
3583
3807
|
const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
|
|
3584
3808
|
loadDotenv(projectRoot === null ? void 0 : projectRoot ?? void 0, { userHomeDir: homeDir });
|
|
3585
3809
|
const toml = loadTomlConfig({ projectRoot: projectRoot ?? null, homeDir });
|
|
3586
|
-
const yaml = loadYamlConfig(projectRoot ?? null);
|
|
3587
|
-
const legacy = loadFileConfig();
|
|
3810
|
+
const yaml = loadYamlConfig(projectRoot ?? null, { homeDir });
|
|
3811
|
+
const legacy = loadFileConfig(homeDir);
|
|
3588
3812
|
const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
|
|
3589
3813
|
const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
|
|
3590
3814
|
const memoryLlmProvider = first(
|
|
@@ -3606,15 +3830,17 @@ function getResolvedConfig(options = {}) {
|
|
|
3606
3830
|
legacy.llm?.baseUrl
|
|
3607
3831
|
);
|
|
3608
3832
|
const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
|
|
3833
|
+
const isAtlasCloud = memoryLlmProvider === "atlascloud";
|
|
3609
3834
|
const memoryLlmApiKey = first(
|
|
3610
3835
|
process.env.MEMORIX_LLM_API_KEY,
|
|
3611
3836
|
process.env.MEMORIX_API_KEY,
|
|
3612
3837
|
toml.memory?.llm?.api_key,
|
|
3613
3838
|
yaml.llm?.apiKey,
|
|
3614
3839
|
legacy.llm?.apiKey,
|
|
3615
|
-
|
|
3616
|
-
process.env.
|
|
3617
|
-
|
|
3840
|
+
// Never borrow another provider's credentials for the Atlas Cloud preset.
|
|
3841
|
+
isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : process.env.OPENAI_API_KEY,
|
|
3842
|
+
isAtlasCloud ? void 0 : process.env.ANTHROPIC_API_KEY,
|
|
3843
|
+
isAtlasCloud ? void 0 : openRouterMemoryLlmApiKey
|
|
3618
3844
|
);
|
|
3619
3845
|
const resolved = {
|
|
3620
3846
|
agent: {
|
|
@@ -3793,7 +4019,8 @@ function getEnvSourceNames() {
|
|
|
3793
4019
|
"MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
|
|
3794
4020
|
"MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
|
|
3795
4021
|
"MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
|
|
3796
|
-
"OPENROUTER_API_KEY"
|
|
4022
|
+
"OPENROUTER_API_KEY",
|
|
4023
|
+
"ATLASCLOUD_API_KEY"
|
|
3797
4024
|
].filter((name) => process.env[name]);
|
|
3798
4025
|
}
|
|
3799
4026
|
function isOpenRouterUrl(value) {
|
|
@@ -4012,12 +4239,45 @@ function textHash(text3) {
|
|
|
4012
4239
|
async function loadDiskCache() {
|
|
4013
4240
|
try {
|
|
4014
4241
|
const raw = await readFile(CACHE_FILE, "utf-8");
|
|
4242
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_CACHE_FILE_BYTES) {
|
|
4243
|
+
console.error("[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache");
|
|
4244
|
+
return;
|
|
4245
|
+
}
|
|
4015
4246
|
const entries = JSON.parse(raw);
|
|
4016
|
-
|
|
4017
|
-
|
|
4247
|
+
if (!Array.isArray(entries)) return;
|
|
4248
|
+
for (const entry of entries) {
|
|
4249
|
+
if (!Array.isArray(entry) || typeof entry[0] !== "string" || !isVector(entry[1])) continue;
|
|
4250
|
+
cacheSet(entry[0], entry[1], false);
|
|
4251
|
+
}
|
|
4252
|
+
console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
|
|
4018
4253
|
} catch {
|
|
4019
4254
|
}
|
|
4020
4255
|
}
|
|
4256
|
+
function isVector(value) {
|
|
4257
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
4258
|
+
}
|
|
4259
|
+
function maxCachePayloadBytes() {
|
|
4260
|
+
const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
|
|
4261
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
|
|
4262
|
+
}
|
|
4263
|
+
function cacheSet(hash4, value, markDirty = true) {
|
|
4264
|
+
const incomingBytes = value.length * 8;
|
|
4265
|
+
if (incomingBytes > maxCachePayloadBytes()) return;
|
|
4266
|
+
const existing = cache.get(hash4);
|
|
4267
|
+
if (existing) cachePayloadBytes -= existing.length * 8;
|
|
4268
|
+
else {
|
|
4269
|
+
while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
|
|
4270
|
+
const firstKey = cache.keys().next().value;
|
|
4271
|
+
if (firstKey === void 0) break;
|
|
4272
|
+
const old = cache.get(firstKey);
|
|
4273
|
+
cache.delete(firstKey);
|
|
4274
|
+
if (old) cachePayloadBytes -= old.length * 8;
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
cache.set(hash4, value);
|
|
4278
|
+
cachePayloadBytes += incomingBytes;
|
|
4279
|
+
if (markDirty) diskCacheDirty = true;
|
|
4280
|
+
}
|
|
4021
4281
|
async function saveDiskCache() {
|
|
4022
4282
|
if (!diskCacheDirty) return;
|
|
4023
4283
|
try {
|
|
@@ -4028,7 +4288,7 @@ async function saveDiskCache() {
|
|
|
4028
4288
|
} catch {
|
|
4029
4289
|
}
|
|
4030
4290
|
}
|
|
4031
|
-
var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
|
|
4291
|
+
var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, DEFAULT_CACHE_PAYLOAD_BYTES, MAX_CACHE_FILE_BYTES, cachePayloadBytes, diskCacheDirty, FastEmbedProvider;
|
|
4032
4292
|
var init_fastembed_provider = __esm({
|
|
4033
4293
|
"src/embedding/fastembed-provider.ts"() {
|
|
4034
4294
|
"use strict";
|
|
@@ -4038,6 +4298,9 @@ var init_fastembed_provider = __esm({
|
|
|
4038
4298
|
CACHE_FILE = join2(CACHE_DIR, ".embedding-cache.json");
|
|
4039
4299
|
cache = /* @__PURE__ */ new Map();
|
|
4040
4300
|
MAX_CACHE_SIZE = 5e3;
|
|
4301
|
+
DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
|
|
4302
|
+
MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
|
|
4303
|
+
cachePayloadBytes = 0;
|
|
4041
4304
|
diskCacheDirty = false;
|
|
4042
4305
|
FastEmbedProvider = class _FastEmbedProvider {
|
|
4043
4306
|
name = "fastembed-bge-small";
|
|
@@ -4079,7 +4342,7 @@ var init_fastembed_provider = __esm({
|
|
|
4079
4342
|
if (result.length !== this.dimensions) {
|
|
4080
4343
|
throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
|
|
4081
4344
|
}
|
|
4082
|
-
|
|
4345
|
+
cacheSet(hash4, result);
|
|
4083
4346
|
return result;
|
|
4084
4347
|
}
|
|
4085
4348
|
async embedBatch(texts) {
|
|
@@ -4104,7 +4367,7 @@ var init_fastembed_provider = __esm({
|
|
|
4104
4367
|
const originalIdx = uncachedIndices[batchIdx];
|
|
4105
4368
|
const plain = Array.from(vec);
|
|
4106
4369
|
results[originalIdx] = plain;
|
|
4107
|
-
|
|
4370
|
+
cacheSet(textHash(uncachedTexts[batchIdx]), plain);
|
|
4108
4371
|
batchIdx++;
|
|
4109
4372
|
}
|
|
4110
4373
|
}
|
|
@@ -4112,14 +4375,6 @@ var init_fastembed_provider = __esm({
|
|
|
4112
4375
|
}
|
|
4113
4376
|
return results;
|
|
4114
4377
|
}
|
|
4115
|
-
cacheSet(hash4, value) {
|
|
4116
|
-
if (cache.size >= MAX_CACHE_SIZE) {
|
|
4117
|
-
const firstKey = cache.keys().next().value;
|
|
4118
|
-
if (firstKey !== void 0) cache.delete(firstKey);
|
|
4119
|
-
}
|
|
4120
|
-
cache.set(hash4, value);
|
|
4121
|
-
diskCacheDirty = true;
|
|
4122
|
-
}
|
|
4123
4378
|
};
|
|
4124
4379
|
}
|
|
4125
4380
|
});
|
|
@@ -4129,7 +4384,15 @@ var transformers_provider_exports = {};
|
|
|
4129
4384
|
__export(transformers_provider_exports, {
|
|
4130
4385
|
TransformersProvider: () => TransformersProvider
|
|
4131
4386
|
});
|
|
4132
|
-
|
|
4387
|
+
import { createHash as createHash2 } from "crypto";
|
|
4388
|
+
function cacheKey2(text3) {
|
|
4389
|
+
return createHash2("sha256").update(text3).digest("hex");
|
|
4390
|
+
}
|
|
4391
|
+
function maxCachePayloadBytes2() {
|
|
4392
|
+
const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
|
|
4393
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES2;
|
|
4394
|
+
}
|
|
4395
|
+
var cache2, MAX_CACHE_SIZE2, DEFAULT_CACHE_PAYLOAD_BYTES2, cachePayloadBytes2, TransformersProvider;
|
|
4133
4396
|
var init_transformers_provider = __esm({
|
|
4134
4397
|
"src/embedding/transformers-provider.ts"() {
|
|
4135
4398
|
"use strict";
|
|
@@ -4137,6 +4400,8 @@ var init_transformers_provider = __esm({
|
|
|
4137
4400
|
init_provider();
|
|
4138
4401
|
cache2 = /* @__PURE__ */ new Map();
|
|
4139
4402
|
MAX_CACHE_SIZE2 = 5e3;
|
|
4403
|
+
DEFAULT_CACHE_PAYLOAD_BYTES2 = 64 * 1024 * 1024;
|
|
4404
|
+
cachePayloadBytes2 = 0;
|
|
4140
4405
|
TransformersProvider = class _TransformersProvider {
|
|
4141
4406
|
name = "transformers-minilm";
|
|
4142
4407
|
dimensions = 384;
|
|
@@ -4171,7 +4436,7 @@ var init_transformers_provider = __esm({
|
|
|
4171
4436
|
return Promise.all(inputs.map((input) => this.embedInput(input, options)));
|
|
4172
4437
|
}
|
|
4173
4438
|
async embed(text3) {
|
|
4174
|
-
const cached = cache2.get(text3);
|
|
4439
|
+
const cached = cache2.get(cacheKey2(text3));
|
|
4175
4440
|
if (cached) return cached;
|
|
4176
4441
|
const output = await this.extractor(text3, {
|
|
4177
4442
|
pooling: "mean",
|
|
@@ -4181,7 +4446,7 @@ var init_transformers_provider = __esm({
|
|
|
4181
4446
|
if (result.length !== this.dimensions) {
|
|
4182
4447
|
throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
|
|
4183
4448
|
}
|
|
4184
|
-
this.cacheSet(text3, result);
|
|
4449
|
+
this.cacheSet(cacheKey2(text3), result);
|
|
4185
4450
|
return result;
|
|
4186
4451
|
}
|
|
4187
4452
|
async embedBatch(texts) {
|
|
@@ -4189,7 +4454,7 @@ var init_transformers_provider = __esm({
|
|
|
4189
4454
|
const uncachedIndices = [];
|
|
4190
4455
|
const uncachedTexts = [];
|
|
4191
4456
|
for (let i2 = 0; i2 < texts.length; i2++) {
|
|
4192
|
-
const cached = cache2.get(texts[i2]);
|
|
4457
|
+
const cached = cache2.get(cacheKey2(texts[i2]));
|
|
4193
4458
|
if (cached) {
|
|
4194
4459
|
results[i2] = cached;
|
|
4195
4460
|
} else {
|
|
@@ -4207,17 +4472,27 @@ var init_transformers_provider = __esm({
|
|
|
4207
4472
|
const vec = Array.from(allVecs[i2]);
|
|
4208
4473
|
const originalIdx = uncachedIndices[i2];
|
|
4209
4474
|
results[originalIdx] = vec;
|
|
4210
|
-
this.cacheSet(uncachedTexts[i2], vec);
|
|
4475
|
+
this.cacheSet(cacheKey2(uncachedTexts[i2]), vec);
|
|
4211
4476
|
}
|
|
4212
4477
|
}
|
|
4213
4478
|
return results;
|
|
4214
4479
|
}
|
|
4215
4480
|
cacheSet(key, value) {
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4481
|
+
const incomingBytes = value.length * 8;
|
|
4482
|
+
if (incomingBytes > maxCachePayloadBytes2()) return;
|
|
4483
|
+
const existing = cache2.get(key);
|
|
4484
|
+
if (existing) cachePayloadBytes2 -= existing.length * 8;
|
|
4485
|
+
else {
|
|
4486
|
+
while (cache2.size >= MAX_CACHE_SIZE2 || cachePayloadBytes2 + incomingBytes > maxCachePayloadBytes2()) {
|
|
4487
|
+
const firstKey = cache2.keys().next().value;
|
|
4488
|
+
if (firstKey === void 0) break;
|
|
4489
|
+
const old = cache2.get(firstKey);
|
|
4490
|
+
cache2.delete(firstKey);
|
|
4491
|
+
if (old) cachePayloadBytes2 -= old.length * 8;
|
|
4492
|
+
}
|
|
4219
4493
|
}
|
|
4220
4494
|
cache2.set(key, value);
|
|
4495
|
+
cachePayloadBytes2 += incomingBytes;
|
|
4221
4496
|
}
|
|
4222
4497
|
};
|
|
4223
4498
|
}
|
|
@@ -4239,7 +4514,7 @@ __export(api_provider_exports, {
|
|
|
4239
4514
|
saveDiskCacheNow: () => saveDiskCacheNow,
|
|
4240
4515
|
seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
|
|
4241
4516
|
});
|
|
4242
|
-
import { createHash as
|
|
4517
|
+
import { createHash as createHash3 } from "crypto";
|
|
4243
4518
|
import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
|
|
4244
4519
|
import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
|
|
4245
4520
|
import { join as join3 } from "path";
|
|
@@ -4266,7 +4541,7 @@ function cacheStatFile() {
|
|
|
4266
4541
|
function payloadBytes(vector) {
|
|
4267
4542
|
return vector.length * BYTES_PER_DIM;
|
|
4268
4543
|
}
|
|
4269
|
-
function
|
|
4544
|
+
function maxCachePayloadBytes3() {
|
|
4270
4545
|
const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
|
|
4271
4546
|
if (raw) {
|
|
4272
4547
|
const parsed = Number.parseInt(raw, 10);
|
|
@@ -4276,7 +4551,7 @@ function maxCachePayloadBytes() {
|
|
|
4276
4551
|
}
|
|
4277
4552
|
function maxEntriesForDimensions(dimensions) {
|
|
4278
4553
|
const dim = Math.max(1, Math.floor(dimensions));
|
|
4279
|
-
const byBytes = Math.floor(
|
|
4554
|
+
const byBytes = Math.floor(maxCachePayloadBytes3() / (dim * BYTES_PER_DIM));
|
|
4280
4555
|
return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
|
|
4281
4556
|
}
|
|
4282
4557
|
function rememberDimensions(vector) {
|
|
@@ -4289,13 +4564,15 @@ function evictOldest() {
|
|
|
4289
4564
|
if (firstKey === void 0) return;
|
|
4290
4565
|
const old = cache3.get(firstKey);
|
|
4291
4566
|
cache3.delete(firstKey);
|
|
4292
|
-
if (old)
|
|
4567
|
+
if (old) cachePayloadBytes3 -= payloadBytes(old);
|
|
4293
4568
|
}
|
|
4294
4569
|
function evictToFit(incomingBytes, incomingDims) {
|
|
4570
|
+
if (incomingBytes > maxCachePayloadBytes3()) return false;
|
|
4295
4571
|
const limit2 = maxEntriesForDimensions(incomingDims);
|
|
4296
|
-
while (cache3.size > 0 && (cache3.size >= limit2 ||
|
|
4572
|
+
while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes3 + incomingBytes > maxCachePayloadBytes3())) {
|
|
4297
4573
|
evictOldest();
|
|
4298
4574
|
}
|
|
4575
|
+
return true;
|
|
4299
4576
|
}
|
|
4300
4577
|
function isNumberArray(value) {
|
|
4301
4578
|
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
@@ -4322,12 +4599,12 @@ function cacheInsert(hash4, value, dirty) {
|
|
|
4322
4599
|
const incoming = payloadBytes(value);
|
|
4323
4600
|
const existing = cache3.get(hash4);
|
|
4324
4601
|
if (existing) {
|
|
4325
|
-
|
|
4602
|
+
cachePayloadBytes3 -= payloadBytes(existing);
|
|
4326
4603
|
cache3.delete(hash4);
|
|
4327
4604
|
}
|
|
4328
|
-
evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
|
|
4605
|
+
if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
|
|
4329
4606
|
cache3.set(hash4, value);
|
|
4330
|
-
|
|
4607
|
+
cachePayloadBytes3 += incoming;
|
|
4331
4608
|
if (dirty) diskCacheDirty2 = true;
|
|
4332
4609
|
}
|
|
4333
4610
|
async function readCacheStat() {
|
|
@@ -4363,7 +4640,7 @@ function cacheNamespace(config) {
|
|
|
4363
4640
|
].join("|");
|
|
4364
4641
|
}
|
|
4365
4642
|
function textHash2(text3, namespace) {
|
|
4366
|
-
return
|
|
4643
|
+
return createHash3("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
|
|
4367
4644
|
}
|
|
4368
4645
|
function inputIdentity(input, options = {}) {
|
|
4369
4646
|
return JSON.stringify({
|
|
@@ -4705,7 +4982,7 @@ function getApiEmbeddingDiskCacheLoadCountForTests() {
|
|
|
4705
4982
|
}
|
|
4706
4983
|
function resetApiEmbeddingCacheForTests() {
|
|
4707
4984
|
cache3.clear();
|
|
4708
|
-
|
|
4985
|
+
cachePayloadBytes3 = 0;
|
|
4709
4986
|
cacheDimensions = null;
|
|
4710
4987
|
diskCacheEntryCount = 0;
|
|
4711
4988
|
diskCacheLoaded = false;
|
|
@@ -4716,14 +4993,14 @@ function resetApiEmbeddingCacheForTests() {
|
|
|
4716
4993
|
}
|
|
4717
4994
|
function seedApiEmbeddingCacheForTests(entries) {
|
|
4718
4995
|
cache3.clear();
|
|
4719
|
-
|
|
4996
|
+
cachePayloadBytes3 = 0;
|
|
4720
4997
|
cacheDimensions = null;
|
|
4721
4998
|
for (const [key, value] of entries) cacheInsert(key, value, true);
|
|
4722
4999
|
diskCacheLoaded = true;
|
|
4723
5000
|
diskCacheDirty2 = true;
|
|
4724
5001
|
}
|
|
4725
5002
|
function getApiEmbeddingCachePayloadBytesForTests() {
|
|
4726
|
-
return
|
|
5003
|
+
return cachePayloadBytes3;
|
|
4727
5004
|
}
|
|
4728
5005
|
function getApiEmbeddingJsonlLineCountCallsForTests() {
|
|
4729
5006
|
return jsonlLineCountCalls;
|
|
@@ -4897,7 +5174,7 @@ function scheduleDiskSave() {
|
|
|
4897
5174
|
diskSaveTimer = null;
|
|
4898
5175
|
}, DISK_SAVE_DEBOUNCE_MS);
|
|
4899
5176
|
}
|
|
4900
|
-
function
|
|
5177
|
+
function cacheSet2(hash4, value) {
|
|
4901
5178
|
cacheInsert(hash4, value, true);
|
|
4902
5179
|
}
|
|
4903
5180
|
function resolveEnvEmbeddingApiKey() {
|
|
@@ -5017,7 +5294,7 @@ async function waitForRetry(waitMs, signal) {
|
|
|
5017
5294
|
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
5018
5295
|
});
|
|
5019
5296
|
}
|
|
5020
|
-
var cache3, MAX_CACHE_ENTRIES, MAX_CACHE_PAYLOAD_BYTES, BYTES_PER_DIM, DEFAULT_ASSUMED_DIMENSIONS, MAX_MIGRATION_VALUE_BYTES,
|
|
5297
|
+
var cache3, MAX_CACHE_ENTRIES, MAX_CACHE_PAYLOAD_BYTES, BYTES_PER_DIM, DEFAULT_ASSUMED_DIMENSIONS, MAX_MIGRATION_VALUE_BYTES, cachePayloadBytes3, cacheDimensions, diskCacheEntryCount, diskCacheDirty2, diskSaveTimer, diskCacheLoaded, diskCacheLoadPromise, diskCacheLoadCount, jsonlLineCountCalls, MAX_INPUT_CHARS, MAX_CONCURRENCY, DISK_SAVE_DEBOUNCE_MS, DEFAULT_MAX_BATCH_SIZE, DASHSCOPE_MAX_BATCH_SIZE, MAX_RETRIES2, BASE_DELAY_MS, EmbeddingAPIError, APIEmbeddingProvider;
|
|
5021
5298
|
var init_api_provider = __esm({
|
|
5022
5299
|
"src/embedding/api-provider.ts"() {
|
|
5023
5300
|
"use strict";
|
|
@@ -5029,7 +5306,7 @@ var init_api_provider = __esm({
|
|
|
5029
5306
|
BYTES_PER_DIM = 8;
|
|
5030
5307
|
DEFAULT_ASSUMED_DIMENSIONS = 4096;
|
|
5031
5308
|
MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
|
|
5032
|
-
|
|
5309
|
+
cachePayloadBytes3 = 0;
|
|
5033
5310
|
cacheDimensions = null;
|
|
5034
5311
|
diskCacheEntryCount = 0;
|
|
5035
5312
|
diskCacheDirty2 = false;
|
|
@@ -5214,7 +5491,7 @@ var init_api_provider = __esm({
|
|
|
5214
5491
|
} else {
|
|
5215
5492
|
embedding = await apiCall();
|
|
5216
5493
|
}
|
|
5217
|
-
|
|
5494
|
+
cacheSet2(hash4, embedding);
|
|
5218
5495
|
scheduleDiskSave();
|
|
5219
5496
|
return embedding;
|
|
5220
5497
|
}
|
|
@@ -5262,7 +5539,7 @@ var init_api_provider = __esm({
|
|
|
5262
5539
|
for (const item of response.data) {
|
|
5263
5540
|
const originalIdx = chunkIndices[item.index];
|
|
5264
5541
|
results[originalIdx] = item.embedding;
|
|
5265
|
-
|
|
5542
|
+
cacheSet2(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
|
|
5266
5543
|
}
|
|
5267
5544
|
} catch (error) {
|
|
5268
5545
|
const providerLimit = parseBatchLimit(error);
|
|
@@ -5340,7 +5617,7 @@ var init_api_provider = __esm({
|
|
|
5340
5617
|
throw new Error(`Expected ${this.dimensions}d, got ${embedding.length}d; dimension mismatch`);
|
|
5341
5618
|
}
|
|
5342
5619
|
this.trackUsage(response);
|
|
5343
|
-
|
|
5620
|
+
cacheSet2(hash4, embedding);
|
|
5344
5621
|
scheduleDiskSave();
|
|
5345
5622
|
return embedding;
|
|
5346
5623
|
}
|
|
@@ -6723,6 +7000,7 @@ var init_provider2 = __esm({
|
|
|
6723
7000
|
openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
|
|
6724
7001
|
anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
|
|
6725
7002
|
openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
|
|
7003
|
+
atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
|
|
6726
7004
|
custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
|
|
6727
7005
|
};
|
|
6728
7006
|
currentConfig = null;
|
|
@@ -6986,6 +7264,7 @@ var init_sqlite_store = __esm({
|
|
|
6986
7264
|
init_sqlite_db();
|
|
6987
7265
|
init_sqlite_reliability();
|
|
6988
7266
|
init_sqlite_fts();
|
|
7267
|
+
init_file_lock();
|
|
6989
7268
|
SqliteBackend = class {
|
|
6990
7269
|
db = null;
|
|
6991
7270
|
dataDir = "";
|
|
@@ -7149,38 +7428,43 @@ var init_sqlite_store = __esm({
|
|
|
7149
7428
|
}
|
|
7150
7429
|
// ── Migration ────────────────────────────────────────────────────
|
|
7151
7430
|
async migrateFromJsonIfNeeded() {
|
|
7152
|
-
const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM observations`).get();
|
|
7153
|
-
if (count2.cnt > 0) return;
|
|
7154
7431
|
const jsonPath = path10.join(this.dataDir, "observations.json");
|
|
7155
7432
|
if (!fs7.existsSync(jsonPath)) return;
|
|
7156
|
-
|
|
7433
|
+
await withFileLock(this.dataDir, async () => {
|
|
7434
|
+
const count2 = this.db.prepare(`SELECT COUNT(*) AS cnt FROM observations`).get();
|
|
7435
|
+
if (count2.cnt > 0) return;
|
|
7157
7436
|
const raw = fs7.readFileSync(jsonPath, "utf-8");
|
|
7158
|
-
const
|
|
7437
|
+
const parsed = JSON.parse(raw);
|
|
7438
|
+
if (!Array.isArray(parsed)) {
|
|
7439
|
+
throw new Error("[memorix] observations.json migration refused: expected a JSON array");
|
|
7440
|
+
}
|
|
7441
|
+
const observations2 = parsed;
|
|
7159
7442
|
if (observations2.length === 0) return;
|
|
7443
|
+
if (observations2.some((obs) => !obs || !Number.isSafeInteger(obs.id) || obs.id < 1 || typeof obs.projectId !== "string")) {
|
|
7444
|
+
throw new Error("[memorix] observations.json migration refused: invalid observation record");
|
|
7445
|
+
}
|
|
7160
7446
|
console.error(`[memorix] Migrating ${observations2.length} observations from JSON to SQLite...`);
|
|
7161
7447
|
const insertMany = this.db.transaction((obsList) => {
|
|
7162
7448
|
for (const obs of obsList) {
|
|
7163
7449
|
this.stmtInsert.run(obsToRow(obs));
|
|
7164
7450
|
}
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
if (fs7.existsSync(counterPath)) {
|
|
7169
|
-
try {
|
|
7451
|
+
const counterPath = path10.join(this.dataDir, "counter.json");
|
|
7452
|
+
let counterNextId;
|
|
7453
|
+
if (fs7.existsSync(counterPath)) {
|
|
7170
7454
|
const counterData = JSON.parse(fs7.readFileSync(counterPath, "utf-8"));
|
|
7171
|
-
const
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7455
|
+
const candidate = counterData.nextId ?? counterData.next_id;
|
|
7456
|
+
if (candidate !== void 0 && (!Number.isSafeInteger(candidate) || Number(candidate) < 1)) {
|
|
7457
|
+
throw new Error("[memorix] counter.json migration refused: nextId must be a positive integer");
|
|
7458
|
+
}
|
|
7459
|
+
counterNextId = candidate === void 0 ? void 0 : Number(candidate);
|
|
7175
7460
|
}
|
|
7176
|
-
|
|
7177
|
-
this.rawSaveIdCounter(Math.max(
|
|
7178
|
-
|
|
7179
|
-
|
|
7461
|
+
const maxId = Math.max(...obsList.map((obs) => obs.id));
|
|
7462
|
+
this.rawSaveIdCounter(Math.max(counterNextId ?? 1, maxId + 1));
|
|
7463
|
+
this.bumpGeneration();
|
|
7464
|
+
});
|
|
7465
|
+
insertMany(observations2);
|
|
7180
7466
|
console.error(`[memorix] Migration complete. ${observations2.length} observations now in SQLite.`);
|
|
7181
|
-
}
|
|
7182
|
-
console.error(`[memorix] JSON\u2192SQLite migration failed (non-fatal, data preserved in JSON): ${err}`);
|
|
7183
|
-
}
|
|
7467
|
+
});
|
|
7184
7468
|
}
|
|
7185
7469
|
// ── Public read ──────────────────────────────────────────────────
|
|
7186
7470
|
async loadAll() {
|
|
@@ -7480,7 +7764,7 @@ var init_obs_store = __esm({
|
|
|
7480
7764
|
});
|
|
7481
7765
|
|
|
7482
7766
|
// src/search/semantic-index.ts
|
|
7483
|
-
import { createHash as
|
|
7767
|
+
import { createHash as createHash4 } from "crypto";
|
|
7484
7768
|
import { createRequire as createRequire3 } from "module";
|
|
7485
7769
|
import path11 from "path";
|
|
7486
7770
|
function createSemanticIndexProfile(provider2) {
|
|
@@ -7494,7 +7778,7 @@ function stateKey(dataDir, profile) {
|
|
|
7494
7778
|
return `${path11.resolve(dataDir)}::${profile.key}`;
|
|
7495
7779
|
}
|
|
7496
7780
|
function tableName(profile) {
|
|
7497
|
-
const digest2 =
|
|
7781
|
+
const digest2 = createHash4("sha256").update(profile.key).digest("hex").slice(0, 20);
|
|
7498
7782
|
return `${TABLE_PREFIX}${digest2}`;
|
|
7499
7783
|
}
|
|
7500
7784
|
function indexRoot(dataDir) {
|
|
@@ -9537,6 +9821,7 @@ var init_secret_filter = __esm({
|
|
|
9537
9821
|
// src/runtime/maintenance-jobs.ts
|
|
9538
9822
|
var maintenance_jobs_exports = {};
|
|
9539
9823
|
__export(maintenance_jobs_exports, {
|
|
9824
|
+
DEFAULT_FAILED_HISTORY_RETENTION_MS: () => DEFAULT_FAILED_HISTORY_RETENTION_MS,
|
|
9540
9825
|
MAINTENANCE_JOB_KINDS: () => MAINTENANCE_JOB_KINDS,
|
|
9541
9826
|
MaintenanceJobStore: () => MaintenanceJobStore,
|
|
9542
9827
|
MaintenanceJobWorker: () => MaintenanceJobWorker,
|
|
@@ -9586,7 +9871,7 @@ function normalizedKinds(kinds) {
|
|
|
9586
9871
|
function getMaintenanceRetryDelayMs(attempts) {
|
|
9587
9872
|
return Math.min(6e4, 1e3 * 2 ** Math.max(0, attempts - 1));
|
|
9588
9873
|
}
|
|
9589
|
-
var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
|
|
9874
|
+
var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, DEFAULT_FAILED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
|
|
9590
9875
|
var init_maintenance_jobs = __esm({
|
|
9591
9876
|
"src/runtime/maintenance-jobs.ts"() {
|
|
9592
9877
|
"use strict";
|
|
@@ -9613,6 +9898,7 @@ var init_maintenance_jobs = __esm({
|
|
|
9613
9898
|
DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
9614
9899
|
MAX_ERROR_LENGTH = 1e3;
|
|
9615
9900
|
DEFAULT_COMPLETED_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
9901
|
+
DEFAULT_FAILED_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
9616
9902
|
MaintenanceJobStore = class {
|
|
9617
9903
|
db;
|
|
9618
9904
|
constructor(dataDir) {
|
|
@@ -9739,18 +10025,21 @@ var init_maintenance_jobs = __esm({
|
|
|
9739
10025
|
}
|
|
9740
10026
|
/**
|
|
9741
10027
|
* Completed jobs are useful recent operator history, not permanent data.
|
|
9742
|
-
* Failed
|
|
9743
|
-
*
|
|
10028
|
+
* Failed diagnostics are retained longer, but are still bounded so a
|
|
10029
|
+
* permanently failing job kind cannot grow the database forever.
|
|
9744
10030
|
*/
|
|
9745
10031
|
pruneCompletedHistory(options = {}) {
|
|
9746
10032
|
const now3 = options.now ?? Date.now();
|
|
9747
10033
|
const maxAgeMs = Number.isFinite(options.maxAgeMs) ? Math.max(0, Math.floor(options.maxAgeMs)) : DEFAULT_COMPLETED_HISTORY_RETENTION_MS;
|
|
10034
|
+
const failedMaxAgeMs = Number.isFinite(options.failedMaxAgeMs) ? Math.max(0, Math.floor(options.failedMaxAgeMs)) : DEFAULT_FAILED_HISTORY_RETENTION_MS;
|
|
9748
10035
|
const result = this.db.prepare(`
|
|
9749
10036
|
DELETE FROM maintenance_jobs
|
|
9750
|
-
WHERE status = 'completed'
|
|
10037
|
+
WHERE (status = 'completed'
|
|
9751
10038
|
AND completed_at IS NOT NULL
|
|
9752
|
-
AND completed_at < ?
|
|
9753
|
-
|
|
10039
|
+
AND completed_at < ?)
|
|
10040
|
+
OR (status = 'failed'
|
|
10041
|
+
AND updated_at < ?)
|
|
10042
|
+
`).run(now3 - maxAgeMs, now3 - failedMaxAgeMs);
|
|
9754
10043
|
return Number(result.changes ?? 0);
|
|
9755
10044
|
}
|
|
9756
10045
|
claimNext(options) {
|
|
@@ -10104,7 +10393,7 @@ var init_lifecycle = __esm({
|
|
|
10104
10393
|
});
|
|
10105
10394
|
|
|
10106
10395
|
// src/store/evidence-store.ts
|
|
10107
|
-
import { createHash as
|
|
10396
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
10108
10397
|
function safeJson(value, fallback) {
|
|
10109
10398
|
if (typeof value !== "string" || value.length === 0) return fallback;
|
|
10110
10399
|
try {
|
|
@@ -10138,7 +10427,7 @@ function rowToCard(row) {
|
|
|
10138
10427
|
};
|
|
10139
10428
|
}
|
|
10140
10429
|
function observationHash(observation) {
|
|
10141
|
-
return
|
|
10430
|
+
return createHash5("sha256").update(JSON.stringify({
|
|
10142
10431
|
projectId: observation.projectId,
|
|
10143
10432
|
id: observation.id,
|
|
10144
10433
|
title: observation.title,
|
|
@@ -10732,13 +11021,13 @@ __export(workspace_exports, {
|
|
|
10732
11021
|
loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
|
|
10733
11022
|
resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
|
|
10734
11023
|
});
|
|
10735
|
-
import { createHash as
|
|
11024
|
+
import { createHash as createHash6 } from "crypto";
|
|
10736
11025
|
import { execFile } from "child_process";
|
|
10737
11026
|
import { promises as fs9 } from "fs";
|
|
10738
11027
|
import path14 from "path";
|
|
10739
11028
|
import { promisify } from "util";
|
|
10740
11029
|
function hash(value) {
|
|
10741
|
-
return
|
|
11030
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
10742
11031
|
}
|
|
10743
11032
|
function workspaceId(projectId, mode) {
|
|
10744
11033
|
return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
|
|
@@ -11891,14 +12180,14 @@ __export(asset_store_exports, {
|
|
|
11891
12180
|
readMediaAsset: () => readMediaAsset,
|
|
11892
12181
|
removeMediaAsset: () => removeMediaAsset
|
|
11893
12182
|
});
|
|
11894
|
-
import { createHash as
|
|
12183
|
+
import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
|
|
11895
12184
|
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
11896
12185
|
import path15 from "path";
|
|
11897
12186
|
function mediaRoot(dataDir) {
|
|
11898
12187
|
return path15.resolve(dataDir, "media");
|
|
11899
12188
|
}
|
|
11900
12189
|
function projectStorageKey(projectId) {
|
|
11901
|
-
return
|
|
12190
|
+
return createHash7("sha256").update(projectId).digest("hex").slice(0, 16);
|
|
11902
12191
|
}
|
|
11903
12192
|
function validateUnderRoot(root, candidate) {
|
|
11904
12193
|
const resolved = path15.resolve(candidate);
|
|
@@ -11958,7 +12247,7 @@ async function readHeader(filePath, bytes = 32) {
|
|
|
11958
12247
|
}
|
|
11959
12248
|
async function sha256File(filePath) {
|
|
11960
12249
|
const bytes = await readFile3(filePath);
|
|
11961
|
-
return
|
|
12250
|
+
return createHash7("sha256").update(bytes).digest("hex");
|
|
11962
12251
|
}
|
|
11963
12252
|
function destinationFor(root, projectId, sha256, extension3) {
|
|
11964
12253
|
const relative5 = path15.join("assets", projectStorageKey(projectId), sha256.slice(0, 2), `${sha256}${extension3}`);
|
|
@@ -12064,7 +12353,7 @@ async function importMediaBuffer(input) {
|
|
|
12064
12353
|
if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
|
|
12065
12354
|
if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
|
|
12066
12355
|
const detected = detectMedia(input.bytes.subarray(0, 32));
|
|
12067
|
-
const sha256 =
|
|
12356
|
+
const sha256 = createHash7("sha256").update(input.bytes).digest("hex");
|
|
12068
12357
|
return importKnownMedia({
|
|
12069
12358
|
dataDir: input.dataDir,
|
|
12070
12359
|
projectId: input.projectId,
|
|
@@ -12082,7 +12371,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
|
|
|
12082
12371
|
if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
|
|
12083
12372
|
const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
|
|
12084
12373
|
if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
|
|
12085
|
-
const sha256 =
|
|
12374
|
+
const sha256 = createHash7("sha256").update(bytes).digest("hex");
|
|
12086
12375
|
if (sha256 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
|
|
12087
12376
|
return bytes;
|
|
12088
12377
|
}
|
|
@@ -64952,7 +65241,7 @@ var require_gaxios = __commonJS({
|
|
|
64952
65241
|
var retry_js_1 = require_retry3();
|
|
64953
65242
|
var stream_1 = __require("stream");
|
|
64954
65243
|
var interceptor_js_1 = require_interceptor();
|
|
64955
|
-
var
|
|
65244
|
+
var randomUUID20 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
|
|
64956
65245
|
var HTTP_STATUS_NO_CONTENT = 204;
|
|
64957
65246
|
var Gaxios = class {
|
|
64958
65247
|
agentCache = /* @__PURE__ */ new Map();
|
|
@@ -65225,7 +65514,7 @@ var require_gaxios = __commonJS({
|
|
|
65225
65514
|
*/
|
|
65226
65515
|
["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
|
|
65227
65516
|
if (opts.multipart?.length) {
|
|
65228
|
-
const boundary = await
|
|
65517
|
+
const boundary = await randomUUID20();
|
|
65229
65518
|
preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
|
|
65230
65519
|
opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
|
|
65231
65520
|
} else if (shouldDirectlyPassData) {
|
|
@@ -70897,16 +71186,16 @@ var require_jwtaccess = __commonJS({
|
|
|
70897
71186
|
* @returns A string that returns the cached key.
|
|
70898
71187
|
*/
|
|
70899
71188
|
getCachedKey(url, scopes) {
|
|
70900
|
-
let
|
|
71189
|
+
let cacheKey3 = url;
|
|
70901
71190
|
if (scopes && Array.isArray(scopes) && scopes.length) {
|
|
70902
|
-
|
|
71191
|
+
cacheKey3 = url ? `${url}_${scopes.join("_")}` : `${scopes.join("_")}`;
|
|
70903
71192
|
} else if (typeof scopes === "string") {
|
|
70904
|
-
|
|
71193
|
+
cacheKey3 = url ? `${url}_${scopes}` : scopes;
|
|
70905
71194
|
}
|
|
70906
|
-
if (!
|
|
71195
|
+
if (!cacheKey3) {
|
|
70907
71196
|
throw Error("Scopes or url must be provided");
|
|
70908
71197
|
}
|
|
70909
|
-
return
|
|
71198
|
+
return cacheKey3;
|
|
70910
71199
|
}
|
|
70911
71200
|
/**
|
|
70912
71201
|
* Get a non-expired access token, after refreshing if necessary.
|
|
@@ -77452,7 +77741,7 @@ var require_websocket = __commonJS({
|
|
|
77452
77741
|
var http3 = __require("http");
|
|
77453
77742
|
var net = __require("net");
|
|
77454
77743
|
var tls = __require("tls");
|
|
77455
|
-
var { randomBytes: randomBytes2, createHash:
|
|
77744
|
+
var { randomBytes: randomBytes2, createHash: createHash20 } = __require("crypto");
|
|
77456
77745
|
var { Duplex, Readable: Readable2 } = __require("stream");
|
|
77457
77746
|
var { URL: URL2 } = __require("url");
|
|
77458
77747
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -78120,7 +78409,7 @@ var require_websocket = __commonJS({
|
|
|
78120
78409
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
78121
78410
|
return;
|
|
78122
78411
|
}
|
|
78123
|
-
const digest2 =
|
|
78412
|
+
const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
|
|
78124
78413
|
if (res.headers["sec-websocket-accept"] !== digest2) {
|
|
78125
78414
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
78126
78415
|
return;
|
|
@@ -78492,7 +78781,7 @@ var require_websocket_server = __commonJS({
|
|
|
78492
78781
|
var EventEmitter2 = __require("events");
|
|
78493
78782
|
var http3 = __require("http");
|
|
78494
78783
|
var { Duplex } = __require("stream");
|
|
78495
|
-
var { createHash:
|
|
78784
|
+
var { createHash: createHash20 } = __require("crypto");
|
|
78496
78785
|
var extension3 = require_extension();
|
|
78497
78786
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
78498
78787
|
var subprotocol2 = require_subprotocol();
|
|
@@ -78799,7 +79088,7 @@ var require_websocket_server = __commonJS({
|
|
|
78799
79088
|
);
|
|
78800
79089
|
}
|
|
78801
79090
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
78802
|
-
const digest2 =
|
|
79091
|
+
const digest2 = createHash20("sha1").update(key + GUID).digest("base64");
|
|
78803
79092
|
const headers = [
|
|
78804
79093
|
"HTTP/1.1 101 Switching Protocols",
|
|
78805
79094
|
"Upgrade: websocket",
|
|
@@ -142858,9 +143147,9 @@ var init_union_priority_sort = __esm({
|
|
|
142858
143147
|
// node_modules/typebox/build/value/clean/from_union.mjs
|
|
142859
143148
|
function FromUnion9(context, type, value) {
|
|
142860
143149
|
for (const schema of UnionPrioritySort(type.anyOf)) {
|
|
142861
|
-
const
|
|
142862
|
-
if (Check2(context, schema,
|
|
142863
|
-
return
|
|
143150
|
+
const clean2 = FromType19(context, schema, Clone2(value));
|
|
143151
|
+
if (Check2(context, schema, clean2))
|
|
143152
|
+
return clean2;
|
|
142864
143153
|
}
|
|
142865
143154
|
return value;
|
|
142866
143155
|
}
|
|
@@ -147498,9 +147787,9 @@ var init_long_term = __esm({
|
|
|
147498
147787
|
});
|
|
147499
147788
|
|
|
147500
147789
|
// src/codegraph/ids.ts
|
|
147501
|
-
import { createHash as
|
|
147790
|
+
import { createHash as createHash8 } from "crypto";
|
|
147502
147791
|
function digest(input) {
|
|
147503
|
-
return
|
|
147792
|
+
return createHash8("sha256").update(input).digest("hex").slice(0, 16);
|
|
147504
147793
|
}
|
|
147505
147794
|
function normalizeCodePath(path38) {
|
|
147506
147795
|
return path38.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
|
|
@@ -148548,7 +148837,7 @@ var init_store = __esm({
|
|
|
148548
148837
|
|
|
148549
148838
|
// src/codegraph/code-state.ts
|
|
148550
148839
|
import { execFile as execFile2 } from "child_process";
|
|
148551
|
-
import { createHash as
|
|
148840
|
+
import { createHash as createHash9 } from "crypto";
|
|
148552
148841
|
import { promisify as promisify3 } from "util";
|
|
148553
148842
|
async function runGit(rootPath, args) {
|
|
148554
148843
|
try {
|
|
@@ -148577,7 +148866,7 @@ function changedPathCount(status) {
|
|
|
148577
148866
|
return count2;
|
|
148578
148867
|
}
|
|
148579
148868
|
function fingerprint(baseRevision, status) {
|
|
148580
|
-
return
|
|
148869
|
+
return createHash9("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
|
|
148581
148870
|
}
|
|
148582
148871
|
async function collectCodeStateSnapshot(input) {
|
|
148583
148872
|
const [revision, status] = await Promise.all([
|
|
@@ -148671,12 +148960,12 @@ var init_exclude3 = __esm({
|
|
|
148671
148960
|
});
|
|
148672
148961
|
|
|
148673
148962
|
// src/codegraph/semantic-provider.ts
|
|
148674
|
-
import { createHash as
|
|
148963
|
+
import { createHash as createHash10 } from "crypto";
|
|
148675
148964
|
import "fs";
|
|
148676
148965
|
import { resolve } from "path";
|
|
148677
148966
|
import ts from "typescript";
|
|
148678
148967
|
function hashText(value) {
|
|
148679
|
-
return
|
|
148968
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
148680
148969
|
}
|
|
148681
148970
|
function canonicalPath(value) {
|
|
148682
148971
|
const normalized = resolve(value).replace(/\\/g, "/");
|
|
@@ -149021,11 +149310,11 @@ __export(lite_provider_exports, {
|
|
|
149021
149310
|
indexProjectLite: () => indexProjectLite,
|
|
149022
149311
|
refreshProjectLite: () => refreshProjectLite
|
|
149023
149312
|
});
|
|
149024
|
-
import { createHash as
|
|
149313
|
+
import { createHash as createHash11 } from "crypto";
|
|
149025
149314
|
import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
149026
149315
|
import { join as join4, relative as relative2 } from "path";
|
|
149027
149316
|
function hashText2(text3) {
|
|
149028
|
-
return
|
|
149317
|
+
return createHash11("sha256").update(text3).digest("hex");
|
|
149029
149318
|
}
|
|
149030
149319
|
function extension2(path38) {
|
|
149031
149320
|
const index = path38.lastIndexOf(".");
|
|
@@ -149658,7 +149947,7 @@ var claim_store_exports = {};
|
|
|
149658
149947
|
__export(claim_store_exports, {
|
|
149659
149948
|
ClaimStore: () => ClaimStore
|
|
149660
149949
|
});
|
|
149661
|
-
import { createHash as
|
|
149950
|
+
import { createHash as createHash12, randomUUID as randomUUID12 } from "crypto";
|
|
149662
149951
|
function optionalText3(value) {
|
|
149663
149952
|
return typeof value === "string" && value ? value : void 0;
|
|
149664
149953
|
}
|
|
@@ -149711,7 +150000,7 @@ function rowToEvent3(row) {
|
|
|
149711
150000
|
};
|
|
149712
150001
|
}
|
|
149713
150002
|
function evidenceKey(input) {
|
|
149714
|
-
return
|
|
150003
|
+
return createHash12("sha256").update(JSON.stringify([
|
|
149715
150004
|
input.evidenceKind,
|
|
149716
150005
|
input.evidenceId,
|
|
149717
150006
|
input.relation,
|
|
@@ -149909,7 +150198,7 @@ __export(claims_exports, {
|
|
|
149909
150198
|
supersedeClaim: () => supersedeClaim,
|
|
149910
150199
|
writeClaim: () => writeClaim
|
|
149911
150200
|
});
|
|
149912
|
-
import { createHash as
|
|
150201
|
+
import { createHash as createHash13, randomUUID as randomUUID13 } from "crypto";
|
|
149913
150202
|
function compactText2(value, field) {
|
|
149914
150203
|
if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
|
|
149915
150204
|
const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
|
|
@@ -149921,7 +150210,7 @@ function normalizedTerm(value) {
|
|
|
149921
150210
|
return compactText2(value, "identity").toLocaleLowerCase("en-US");
|
|
149922
150211
|
}
|
|
149923
150212
|
function hashIdentity(parts) {
|
|
149924
|
-
return
|
|
150213
|
+
return createHash13("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
|
|
149925
150214
|
}
|
|
149926
150215
|
function buildClaimIdentity(input) {
|
|
149927
150216
|
const subject = normalizedTerm(input.subject);
|
|
@@ -150162,7 +150451,7 @@ function supersedeClaim(store, input) {
|
|
|
150162
150451
|
});
|
|
150163
150452
|
}
|
|
150164
150453
|
function hashObservation(observation) {
|
|
150165
|
-
return
|
|
150454
|
+
return createHash13("sha256").update(JSON.stringify([
|
|
150166
150455
|
observation.title,
|
|
150167
150456
|
observation.narrative,
|
|
150168
150457
|
observation.facts,
|
|
@@ -150406,11 +150695,11 @@ var init_frontmatter = __esm({
|
|
|
150406
150695
|
});
|
|
150407
150696
|
|
|
150408
150697
|
// src/knowledge/markdown.ts
|
|
150409
|
-
import { createHash as
|
|
150698
|
+
import { createHash as createHash14 } from "crypto";
|
|
150410
150699
|
import { promises as fs13 } from "fs";
|
|
150411
150700
|
import path22 from "path";
|
|
150412
150701
|
function hashContent(content) {
|
|
150413
|
-
return
|
|
150702
|
+
return createHash14("sha256").update(content).digest("hex");
|
|
150414
150703
|
}
|
|
150415
150704
|
function stringField(data, key) {
|
|
150416
150705
|
const value = data[key];
|
|
@@ -150536,11 +150825,11 @@ __export(wiki_exports, {
|
|
|
150536
150825
|
lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
|
|
150537
150826
|
readKnowledgePage: () => readKnowledgePage
|
|
150538
150827
|
});
|
|
150539
|
-
import { createHash as
|
|
150828
|
+
import { createHash as createHash15 } from "crypto";
|
|
150540
150829
|
import { promises as fs14 } from "fs";
|
|
150541
150830
|
import path23 from "path";
|
|
150542
150831
|
function hash2(value) {
|
|
150543
|
-
return
|
|
150832
|
+
return createHash15("sha256").update(value).digest("hex");
|
|
150544
150833
|
}
|
|
150545
150834
|
function now() {
|
|
150546
150835
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -151759,11 +152048,11 @@ __export(workflows_exports, {
|
|
|
151759
152048
|
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
151760
152049
|
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
151761
152050
|
});
|
|
151762
|
-
import { createHash as
|
|
152051
|
+
import { createHash as createHash16 } from "crypto";
|
|
151763
152052
|
import { promises as fs15 } from "fs";
|
|
151764
152053
|
import path24 from "path";
|
|
151765
152054
|
function hash3(value) {
|
|
151766
|
-
return
|
|
152055
|
+
return createHash16("sha256").update(value).digest("hex");
|
|
151767
152056
|
}
|
|
151768
152057
|
function now2() {
|
|
151769
152058
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -156124,9 +156413,9 @@ var init_graph_context = __esm({
|
|
|
156124
156413
|
});
|
|
156125
156414
|
|
|
156126
156415
|
// src/rules/utils.ts
|
|
156127
|
-
import { createHash as
|
|
156416
|
+
import { createHash as createHash17 } from "crypto";
|
|
156128
156417
|
function hashContent2(content) {
|
|
156129
|
-
return
|
|
156418
|
+
return createHash17("sha256").update(content.trim()).digest("hex").substring(0, 16);
|
|
156130
156419
|
}
|
|
156131
156420
|
function generateRuleId(source, filePath) {
|
|
156132
156421
|
const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
|
|
@@ -158569,6 +158858,7 @@ var init_tool_profile = __esm({
|
|
|
158569
158858
|
// keep the default micro profile at its nine-tool token budget.
|
|
158570
158859
|
memorix_evidence: ["lite", "team", "full"],
|
|
158571
158860
|
memorix_feedback: ["lite", "team", "full"],
|
|
158861
|
+
memorix_continuity: ["lite", "team", "full"],
|
|
158572
158862
|
// ── lite: extended solo memory surface ────────────────────────────
|
|
158573
158863
|
memorix_graph_context: ["lite", "team", "full"],
|
|
158574
158864
|
memorix_timeline: ["lite", "team", "full"],
|
|
@@ -159802,7 +160092,7 @@ var init_request_context = __esm({
|
|
|
159802
160092
|
function getMcpServerInfo() {
|
|
159803
160093
|
return {
|
|
159804
160094
|
name: "memorix",
|
|
159805
|
-
version: true ? "1.9.
|
|
160095
|
+
version: true ? "1.9.6" : "1.0.1"
|
|
159806
160096
|
};
|
|
159807
160097
|
}
|
|
159808
160098
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
@@ -159891,8 +160181,8 @@ function legacyBehavior(homeDir) {
|
|
|
159891
160181
|
function getBehaviorConfig(options = {}) {
|
|
159892
160182
|
const homeDir = options.homeDir ?? homedir22();
|
|
159893
160183
|
const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
|
|
159894
|
-
const
|
|
159895
|
-
const cached = cache4.get(
|
|
160184
|
+
const cacheKey3 = `${homeDir}\0${projectRoot ?? ""}`;
|
|
160185
|
+
const cached = cache4.get(cacheKey3);
|
|
159896
160186
|
if (cached) return cached;
|
|
159897
160187
|
const legacy = legacyBehavior(homeDir);
|
|
159898
160188
|
let resolved = {};
|
|
@@ -159906,7 +160196,7 @@ function getBehaviorConfig(options = {}) {
|
|
|
159906
160196
|
autoCleanup: typeof resolved.autoCleanup === "boolean" ? resolved.autoCleanup : typeof legacy.autoCleanup === "boolean" ? legacy.autoCleanup : DEFAULTS.autoCleanup,
|
|
159907
160197
|
formationMode: isFormationMode(resolved.formation) ? resolved.formation : isFormationMode(legacy.formationMode) ? legacy.formationMode : DEFAULTS.formationMode
|
|
159908
160198
|
};
|
|
159909
|
-
cache4.set(
|
|
160199
|
+
cache4.set(cacheKey3, config);
|
|
159910
160200
|
return config;
|
|
159911
160201
|
}
|
|
159912
160202
|
function resetBehaviorConfigCache() {
|
|
@@ -160197,7 +160487,7 @@ async function getSessionContext(projectDir2, projectId, limit2 = 3, reader2) {
|
|
|
160197
160487
|
let graphNeighbors = [];
|
|
160198
160488
|
if (!reader2 && activeEntities.length > 0) {
|
|
160199
160489
|
try {
|
|
160200
|
-
const graphMgr = new KnowledgeGraphManager(projectDir2);
|
|
160490
|
+
const graphMgr = new KnowledgeGraphManager(projectDir2, projectId);
|
|
160201
160491
|
await graphMgr.init();
|
|
160202
160492
|
const { relations } = await graphMgr.readGraph();
|
|
160203
160493
|
const activeSet = new Set(activeEntities.map((n) => n.toLowerCase()));
|
|
@@ -160511,6 +160801,7 @@ function freshnessForMemory(status) {
|
|
|
160511
160801
|
return "unknown";
|
|
160512
160802
|
}
|
|
160513
160803
|
function receiptOmissionKind(raw) {
|
|
160804
|
+
if (raw.includes("continuity")) return "continuity";
|
|
160514
160805
|
if (raw.includes("continuation")) return "continuation";
|
|
160515
160806
|
if (raw.includes("task")) return "task";
|
|
160516
160807
|
if (raw.includes("fact")) return "current-fact";
|
|
@@ -160621,6 +160912,77 @@ function renderTaskWorksetPrompt(input) {
|
|
|
160621
160912
|
trust: "source-backed"
|
|
160622
160913
|
});
|
|
160623
160914
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
160915
|
+
if (input.continuity) {
|
|
160916
|
+
const continuity = input.continuity;
|
|
160917
|
+
appendLine(lines, "", maxTokens, omitted, "continuity-heading");
|
|
160918
|
+
appendLine(lines, "Task continuity", maxTokens, omitted, "continuity-heading", selected, {
|
|
160919
|
+
kind: "continuity",
|
|
160920
|
+
id: "continuity:" + continuity.taskId,
|
|
160921
|
+
reason: "explicit task continuity ledger",
|
|
160922
|
+
trust: "source-backed"
|
|
160923
|
+
});
|
|
160924
|
+
appendLine(
|
|
160925
|
+
lines,
|
|
160926
|
+
"- Status: " + continuity.status + " | " + short(continuity.task, 30),
|
|
160927
|
+
maxTokens,
|
|
160928
|
+
omitted,
|
|
160929
|
+
"continuity-task"
|
|
160930
|
+
);
|
|
160931
|
+
appendLine(
|
|
160932
|
+
lines,
|
|
160933
|
+
"- Outcome: " + continuity.outcome.state,
|
|
160934
|
+
maxTokens,
|
|
160935
|
+
omitted,
|
|
160936
|
+
"continuity-outcome",
|
|
160937
|
+
selected,
|
|
160938
|
+
{
|
|
160939
|
+
kind: "continuity",
|
|
160940
|
+
id: "continuity:" + continuity.taskId,
|
|
160941
|
+
reason: "deterministic task outcome projection",
|
|
160942
|
+
trust: "derived"
|
|
160943
|
+
}
|
|
160944
|
+
);
|
|
160945
|
+
for (const requirement of continuity.requirements.slice(0, 3)) {
|
|
160946
|
+
appendLine(lines, "- Requirement: " + short(requirement, 24), maxTokens, omitted, "continuity-requirement", selected, {
|
|
160947
|
+
kind: "continuity",
|
|
160948
|
+
id: "continuity:" + continuity.taskId,
|
|
160949
|
+
reason: "explicit task requirement",
|
|
160950
|
+
trust: "source-backed"
|
|
160951
|
+
});
|
|
160952
|
+
}
|
|
160953
|
+
for (const decision of continuity.decisions.slice(0, 2)) {
|
|
160954
|
+
appendLine(lines, "- Decision: " + short(decision, 24), maxTokens, omitted, "continuity-decision", selected, {
|
|
160955
|
+
kind: "continuity",
|
|
160956
|
+
id: "continuity:" + continuity.taskId,
|
|
160957
|
+
reason: "explicit task decision",
|
|
160958
|
+
trust: "source-backed"
|
|
160959
|
+
});
|
|
160960
|
+
}
|
|
160961
|
+
for (const verification of continuity.verification.slice(-3)) {
|
|
160962
|
+
appendLine(
|
|
160963
|
+
lines,
|
|
160964
|
+
"- Verify [" + verification.status + "]: " + short(verification.content, 22),
|
|
160965
|
+
maxTokens,
|
|
160966
|
+
omitted,
|
|
160967
|
+
"continuity-verification",
|
|
160968
|
+
selected,
|
|
160969
|
+
{
|
|
160970
|
+
kind: "continuity",
|
|
160971
|
+
id: "continuity:" + continuity.taskId,
|
|
160972
|
+
reason: "task verification state",
|
|
160973
|
+
trust: "source-backed"
|
|
160974
|
+
}
|
|
160975
|
+
);
|
|
160976
|
+
}
|
|
160977
|
+
for (const risk of continuity.risks.slice(0, 2)) {
|
|
160978
|
+
appendLine(lines, "- Risk: " + short(risk, 22), maxTokens, omitted, "continuity-risk", selected, {
|
|
160979
|
+
kind: "continuity",
|
|
160980
|
+
id: "continuity:" + continuity.taskId,
|
|
160981
|
+
reason: "open task risk",
|
|
160982
|
+
trust: "source-backed"
|
|
160983
|
+
});
|
|
160984
|
+
}
|
|
160985
|
+
}
|
|
160624
160986
|
if (input.alwaysOn) {
|
|
160625
160987
|
const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
|
|
160626
160988
|
if (hasAlwaysOn) {
|
|
@@ -161144,6 +161506,7 @@ async function buildTaskWorkset(input) {
|
|
|
161144
161506
|
version: "1.3",
|
|
161145
161507
|
task,
|
|
161146
161508
|
lens: input.lens,
|
|
161509
|
+
...input.continuity ? { continuity: input.continuity } : {},
|
|
161147
161510
|
...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
|
|
161148
161511
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
161149
161512
|
...continuation ? { continuation } : {},
|
|
@@ -162528,7 +162891,7 @@ var compaction_checkpoint_store_exports = {};
|
|
|
162528
162891
|
__export(compaction_checkpoint_store_exports, {
|
|
162529
162892
|
CompactionCheckpointStore: () => CompactionCheckpointStore
|
|
162530
162893
|
});
|
|
162531
|
-
import { createHash as
|
|
162894
|
+
import { createHash as createHash18, randomUUID as randomUUID16 } from "crypto";
|
|
162532
162895
|
function optionalText7(value) {
|
|
162533
162896
|
return typeof value === "string" && value ? value : void 0;
|
|
162534
162897
|
}
|
|
@@ -162570,7 +162933,7 @@ function sourceKeyFor(input, summary) {
|
|
|
162570
162933
|
input.tokensBefore ?? "",
|
|
162571
162934
|
input.firstKeptEntryId ?? ""
|
|
162572
162935
|
].join("\0");
|
|
162573
|
-
return `derived:${
|
|
162936
|
+
return `derived:${createHash18("sha256").update(material).digest("hex").slice(0, 32)}`;
|
|
162574
162937
|
}
|
|
162575
162938
|
function rowToCheckpoint(row) {
|
|
162576
162939
|
return {
|
|
@@ -162828,6 +163191,305 @@ var init_compaction_checkpoint_store = __esm({
|
|
|
162828
163191
|
}
|
|
162829
163192
|
});
|
|
162830
163193
|
|
|
163194
|
+
// src/knowledge/task-continuity.ts
|
|
163195
|
+
var task_continuity_exports = {};
|
|
163196
|
+
__export(task_continuity_exports, {
|
|
163197
|
+
TASK_CONTINUITY_EVENT_KINDS: () => TASK_CONTINUITY_EVENT_KINDS,
|
|
163198
|
+
TaskContinuityStore: () => TaskContinuityStore,
|
|
163199
|
+
evaluateTaskContinuity: () => evaluateTaskContinuity
|
|
163200
|
+
});
|
|
163201
|
+
import { randomUUID as randomUUID17 } from "crypto";
|
|
163202
|
+
function clean(value, maxLength = MAX_TEXT_LENGTH2) {
|
|
163203
|
+
return sanitizeCredentials(value).replace(/\s+/g, " ").trim().slice(0, maxLength);
|
|
163204
|
+
}
|
|
163205
|
+
function cleanList(values, maxLength = MAX_TEXT_LENGTH2) {
|
|
163206
|
+
return [...new Set((values ?? []).map((value) => clean(value, maxLength)).filter(Boolean))].slice(0, MAX_LIST_ITEMS2);
|
|
163207
|
+
}
|
|
163208
|
+
function parseRefs(value) {
|
|
163209
|
+
if (typeof value !== "string") return [];
|
|
163210
|
+
try {
|
|
163211
|
+
const parsed = JSON.parse(value);
|
|
163212
|
+
return Array.isArray(parsed) ? cleanList(parsed.filter((item) => typeof item === "string"), 500).slice(0, MAX_EVIDENCE_REFS) : [];
|
|
163213
|
+
} catch {
|
|
163214
|
+
return [];
|
|
163215
|
+
}
|
|
163216
|
+
}
|
|
163217
|
+
function evaluateTaskContinuity(ledger) {
|
|
163218
|
+
const verification = {
|
|
163219
|
+
passed: ledger.verification.filter((item) => item.status === "passed").length,
|
|
163220
|
+
failed: ledger.verification.filter((item) => item.status === "failed").length,
|
|
163221
|
+
pending: ledger.verification.filter((item) => item.status === "pending").length,
|
|
163222
|
+
skipped: ledger.verification.filter((item) => item.status === "skipped").length
|
|
163223
|
+
};
|
|
163224
|
+
const reasons = [];
|
|
163225
|
+
if (verification.failed > 0) reasons.push("verification-failed");
|
|
163226
|
+
if (verification.pending > 0) reasons.push("verification-pending");
|
|
163227
|
+
if (ledger.risks.length > 0) reasons.push("open-risks");
|
|
163228
|
+
if (ledger.status === "blocked") reasons.push("task-blocked");
|
|
163229
|
+
if (ledger.status === "abandoned") reasons.push("task-abandoned");
|
|
163230
|
+
if (ledger.status === "blocked" || ledger.status === "abandoned" || verification.failed > 0) {
|
|
163231
|
+
return {
|
|
163232
|
+
state: "at-risk",
|
|
163233
|
+
score: verification.passed > verification.failed ? 0.55 : 0.2,
|
|
163234
|
+
verification,
|
|
163235
|
+
reasons
|
|
163236
|
+
};
|
|
163237
|
+
}
|
|
163238
|
+
if (ledger.status === "completed") {
|
|
163239
|
+
if (verification.passed > 0 && verification.pending === 0 && verification.skipped === 0) {
|
|
163240
|
+
return {
|
|
163241
|
+
state: ledger.risks.length > 0 ? "validated-with-risks" : "validated",
|
|
163242
|
+
score: ledger.risks.length > 0 ? 0.8 : 1,
|
|
163243
|
+
verification,
|
|
163244
|
+
reasons: reasons.length > 0 ? reasons : ["verification-passed"]
|
|
163245
|
+
};
|
|
163246
|
+
}
|
|
163247
|
+
return {
|
|
163248
|
+
state: "unverified",
|
|
163249
|
+
score: 0.4,
|
|
163250
|
+
verification,
|
|
163251
|
+
reasons: reasons.length > 0 ? reasons : ["no-passing-verification"]
|
|
163252
|
+
};
|
|
163253
|
+
}
|
|
163254
|
+
return {
|
|
163255
|
+
state: "in-progress",
|
|
163256
|
+
score: verification.passed > 0 && verification.pending === 0 ? 0.75 : 0.5,
|
|
163257
|
+
verification,
|
|
163258
|
+
reasons: reasons.length > 0 ? reasons : ["task-open"]
|
|
163259
|
+
};
|
|
163260
|
+
}
|
|
163261
|
+
function rowToEvent4(row) {
|
|
163262
|
+
const status = row.item_status ? String(row.item_status) : void 0;
|
|
163263
|
+
return {
|
|
163264
|
+
id: String(row.id),
|
|
163265
|
+
projectId: String(row.project_id),
|
|
163266
|
+
taskId: String(row.task_id),
|
|
163267
|
+
task: clean(String(row.task), MAX_TASK_LENGTH),
|
|
163268
|
+
kind: row.kind,
|
|
163269
|
+
content: clean(String(row.content)),
|
|
163270
|
+
...status ? { status } : {},
|
|
163271
|
+
...row.source_ref ? { sourceRef: clean(String(row.source_ref), 500) } : {},
|
|
163272
|
+
evidenceRefs: parseRefs(row.evidence_json),
|
|
163273
|
+
...row.actor ? { actor: clean(String(row.actor), 200) } : {},
|
|
163274
|
+
at: String(row.created_at),
|
|
163275
|
+
...row.event_key ? { eventKey: clean(String(row.event_key), 200) } : {},
|
|
163276
|
+
...row.item_key ? { itemKey: clean(String(row.item_key), 120) } : {},
|
|
163277
|
+
...row.supersedes_id ? { supersedesId: clean(String(row.supersedes_id), 120) } : {}
|
|
163278
|
+
};
|
|
163279
|
+
}
|
|
163280
|
+
function aggregate(events) {
|
|
163281
|
+
const first2 = events.find((event) => event.kind === "task") ?? events[0];
|
|
163282
|
+
const latestOutcome = [...events].reverse().find((event) => event.kind === "outcome");
|
|
163283
|
+
const verificationByKey = /* @__PURE__ */ new Map();
|
|
163284
|
+
for (const event of events.filter((event2) => event2.kind === "verification")) {
|
|
163285
|
+
verificationByKey.set(event.itemKey ?? `event:${event.id}`, event);
|
|
163286
|
+
}
|
|
163287
|
+
const ledger = {
|
|
163288
|
+
taskId: first2.taskId,
|
|
163289
|
+
projectId: first2.projectId,
|
|
163290
|
+
task: first2.task,
|
|
163291
|
+
status: latestOutcome?.status ?? "open",
|
|
163292
|
+
requirements: events.filter((event) => event.kind === "requirement").map((event) => event.content),
|
|
163293
|
+
decisions: events.filter((event) => event.kind === "decision").map((event) => event.content),
|
|
163294
|
+
verification: [...verificationByKey.values()].map((event) => ({
|
|
163295
|
+
id: event.itemKey ?? event.id,
|
|
163296
|
+
content: event.content,
|
|
163297
|
+
status: event.status ?? "pending",
|
|
163298
|
+
...event.sourceRef ? { sourceRef: event.sourceRef } : {},
|
|
163299
|
+
evidenceRefs: event.evidenceRefs
|
|
163300
|
+
})),
|
|
163301
|
+
risks: events.filter((event) => event.kind === "risk").map((event) => event.content),
|
|
163302
|
+
outcomes: events.filter((event) => event.kind === "outcome").map((event) => event.content),
|
|
163303
|
+
updatedAt: events[events.length - 1].at,
|
|
163304
|
+
events
|
|
163305
|
+
};
|
|
163306
|
+
return { ...ledger, outcome: evaluateTaskContinuity(ledger) };
|
|
163307
|
+
}
|
|
163308
|
+
var TASK_CONTINUITY_EVENT_KINDS, MAX_TEXT_LENGTH2, MAX_TASK_LENGTH, MAX_LIST_ITEMS2, MAX_EVIDENCE_REFS, MAX_EVENTS_PER_TASK, TaskContinuityStore;
|
|
163309
|
+
var init_task_continuity = __esm({
|
|
163310
|
+
"src/knowledge/task-continuity.ts"() {
|
|
163311
|
+
"use strict";
|
|
163312
|
+
init_esm_shims();
|
|
163313
|
+
init_secret_filter();
|
|
163314
|
+
init_sqlite_db();
|
|
163315
|
+
TASK_CONTINUITY_EVENT_KINDS = [
|
|
163316
|
+
"task",
|
|
163317
|
+
"requirement",
|
|
163318
|
+
"decision",
|
|
163319
|
+
"verification",
|
|
163320
|
+
"risk",
|
|
163321
|
+
"outcome"
|
|
163322
|
+
];
|
|
163323
|
+
MAX_TEXT_LENGTH2 = 2e3;
|
|
163324
|
+
MAX_TASK_LENGTH = 1e3;
|
|
163325
|
+
MAX_LIST_ITEMS2 = 8;
|
|
163326
|
+
MAX_EVIDENCE_REFS = 8;
|
|
163327
|
+
MAX_EVENTS_PER_TASK = 200;
|
|
163328
|
+
TaskContinuityStore = class {
|
|
163329
|
+
db = null;
|
|
163330
|
+
async init(dataDir) {
|
|
163331
|
+
this.db = getDatabase(dataDir);
|
|
163332
|
+
}
|
|
163333
|
+
requireDb() {
|
|
163334
|
+
if (!this.db) throw new Error("TaskContinuityStore is not initialized.");
|
|
163335
|
+
return this.db;
|
|
163336
|
+
}
|
|
163337
|
+
insert(input) {
|
|
163338
|
+
const db2 = this.requireDb();
|
|
163339
|
+
const projectId = clean(input.projectId, 500);
|
|
163340
|
+
const taskId = clean(input.taskId, 120);
|
|
163341
|
+
const eventKey = input.eventKey ? clean(input.eventKey, 200) : void 0;
|
|
163342
|
+
if (eventKey) {
|
|
163343
|
+
const existing = db2.prepare(
|
|
163344
|
+
"SELECT * FROM task_continuity_events WHERE project_id = ? AND task_id = ? AND event_key = ?"
|
|
163345
|
+
).get(projectId, taskId, eventKey);
|
|
163346
|
+
if (existing) return rowToEvent4(existing);
|
|
163347
|
+
}
|
|
163348
|
+
const existingCount = db2.prepare(
|
|
163349
|
+
"SELECT COUNT(*) AS count FROM task_continuity_events WHERE project_id = ? AND task_id = ?"
|
|
163350
|
+
).get(projectId, taskId);
|
|
163351
|
+
if (Number(existingCount?.count ?? 0) >= MAX_EVENTS_PER_TASK) {
|
|
163352
|
+
throw new Error("Task continuity ledger reached its 200-event limit.");
|
|
163353
|
+
}
|
|
163354
|
+
const event = {
|
|
163355
|
+
id: randomUUID17(),
|
|
163356
|
+
projectId,
|
|
163357
|
+
taskId,
|
|
163358
|
+
task: clean(input.task, MAX_TASK_LENGTH),
|
|
163359
|
+
kind: input.kind,
|
|
163360
|
+
content: clean(input.content),
|
|
163361
|
+
...input.status ? { status: input.status } : {},
|
|
163362
|
+
...input.sourceRef ? { sourceRef: clean(input.sourceRef, 500) } : {},
|
|
163363
|
+
evidenceRefs: cleanList(input.evidenceRefs, 500).slice(0, MAX_EVIDENCE_REFS),
|
|
163364
|
+
...input.actor ? { actor: clean(input.actor, 200) } : {},
|
|
163365
|
+
at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
163366
|
+
...eventKey ? { eventKey } : {},
|
|
163367
|
+
...input.itemKey ? { itemKey: clean(input.itemKey, 120) } : {},
|
|
163368
|
+
...input.supersedesId ? { supersedesId: clean(input.supersedesId, 120) } : {}
|
|
163369
|
+
};
|
|
163370
|
+
db2.prepare(`
|
|
163371
|
+
INSERT INTO task_continuity_events (
|
|
163372
|
+
id, project_id, task_id, task, kind, content, item_status,
|
|
163373
|
+
source_ref, evidence_json, actor, created_at, event_key, item_key, supersedes_id
|
|
163374
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
163375
|
+
`).run(
|
|
163376
|
+
event.id,
|
|
163377
|
+
event.projectId,
|
|
163378
|
+
event.taskId,
|
|
163379
|
+
event.task,
|
|
163380
|
+
event.kind,
|
|
163381
|
+
event.content,
|
|
163382
|
+
event.status ?? null,
|
|
163383
|
+
event.sourceRef ?? null,
|
|
163384
|
+
JSON.stringify(event.evidenceRefs),
|
|
163385
|
+
event.actor ?? null,
|
|
163386
|
+
event.at,
|
|
163387
|
+
event.eventKey ?? null,
|
|
163388
|
+
event.itemKey ?? null,
|
|
163389
|
+
event.supersedesId ?? null
|
|
163390
|
+
);
|
|
163391
|
+
return event;
|
|
163392
|
+
}
|
|
163393
|
+
start(input) {
|
|
163394
|
+
const task = clean(input.task, MAX_TASK_LENGTH);
|
|
163395
|
+
if (!task) throw new Error("A non-empty task is required.");
|
|
163396
|
+
const idempotencyKey = input.idempotencyKey ? clean(input.idempotencyKey, 200) : void 0;
|
|
163397
|
+
const taskId = clean(input.taskId ?? idempotencyKey ?? "", 120) || randomUUID17();
|
|
163398
|
+
const requirements = cleanList(input.requirements);
|
|
163399
|
+
const transaction = this.requireDb().transaction(() => {
|
|
163400
|
+
this.insert({
|
|
163401
|
+
projectId: input.projectId,
|
|
163402
|
+
taskId,
|
|
163403
|
+
task,
|
|
163404
|
+
kind: "task",
|
|
163405
|
+
content: task,
|
|
163406
|
+
status: "open",
|
|
163407
|
+
actor: input.actor,
|
|
163408
|
+
...idempotencyKey ? { eventKey: `start:${idempotencyKey}` } : {}
|
|
163409
|
+
});
|
|
163410
|
+
for (const requirement of requirements) {
|
|
163411
|
+
this.insert({
|
|
163412
|
+
projectId: input.projectId,
|
|
163413
|
+
taskId,
|
|
163414
|
+
task,
|
|
163415
|
+
kind: "requirement",
|
|
163416
|
+
content: requirement,
|
|
163417
|
+
actor: input.actor
|
|
163418
|
+
});
|
|
163419
|
+
}
|
|
163420
|
+
});
|
|
163421
|
+
transaction.immediate();
|
|
163422
|
+
return { taskId, ledger: this.get(input.projectId, taskId) };
|
|
163423
|
+
}
|
|
163424
|
+
record(input) {
|
|
163425
|
+
const projectId = clean(input.projectId, 500);
|
|
163426
|
+
const taskId = clean(input.taskId, 120);
|
|
163427
|
+
const existing = this.get(projectId, taskId);
|
|
163428
|
+
if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
|
|
163429
|
+
const transaction = this.requireDb().transaction(() => this.insert({
|
|
163430
|
+
projectId,
|
|
163431
|
+
taskId,
|
|
163432
|
+
task: existing.task,
|
|
163433
|
+
kind: input.kind,
|
|
163434
|
+
content: input.content,
|
|
163435
|
+
...input.kind === "verification" && input.verificationStatus ? { status: input.verificationStatus } : {},
|
|
163436
|
+
sourceRef: input.sourceRef,
|
|
163437
|
+
evidenceRefs: input.evidenceRefs,
|
|
163438
|
+
actor: input.actor,
|
|
163439
|
+
at: input.at,
|
|
163440
|
+
...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {},
|
|
163441
|
+
...input.verificationId ? { itemKey: input.verificationId } : {}
|
|
163442
|
+
}));
|
|
163443
|
+
const event = transaction.immediate();
|
|
163444
|
+
return { event, ledger: this.get(projectId, taskId) };
|
|
163445
|
+
}
|
|
163446
|
+
close(input) {
|
|
163447
|
+
const projectId = clean(input.projectId, 500);
|
|
163448
|
+
const taskId = clean(input.taskId, 120);
|
|
163449
|
+
const existing = this.get(projectId, taskId);
|
|
163450
|
+
if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
|
|
163451
|
+
const transaction = this.requireDb().transaction(() => this.insert({
|
|
163452
|
+
projectId,
|
|
163453
|
+
taskId,
|
|
163454
|
+
task: existing.task,
|
|
163455
|
+
kind: "outcome",
|
|
163456
|
+
content: input.content,
|
|
163457
|
+
status: input.status,
|
|
163458
|
+
sourceRef: input.sourceRef,
|
|
163459
|
+
evidenceRefs: input.evidenceRefs,
|
|
163460
|
+
actor: input.actor,
|
|
163461
|
+
at: input.at,
|
|
163462
|
+
...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {}
|
|
163463
|
+
}));
|
|
163464
|
+
const event = transaction.immediate();
|
|
163465
|
+
return { event, ledger: this.get(projectId, taskId) };
|
|
163466
|
+
}
|
|
163467
|
+
get(projectId, taskId) {
|
|
163468
|
+
const normalizedProjectId = clean(projectId, 500);
|
|
163469
|
+
const normalizedTaskId = clean(taskId, 120);
|
|
163470
|
+
const rows = this.requireDb().prepare(`
|
|
163471
|
+
SELECT * FROM task_continuity_events
|
|
163472
|
+
WHERE project_id = ? AND task_id = ?
|
|
163473
|
+
ORDER BY created_at ASC, rowid ASC
|
|
163474
|
+
`).all(normalizedProjectId, normalizedTaskId);
|
|
163475
|
+
if (rows.length === 0) return void 0;
|
|
163476
|
+
return aggregate(rows.map(rowToEvent4));
|
|
163477
|
+
}
|
|
163478
|
+
list(projectId, limit2 = 20) {
|
|
163479
|
+
const rows = this.requireDb().prepare(`
|
|
163480
|
+
SELECT task_id
|
|
163481
|
+
FROM task_continuity_events
|
|
163482
|
+
WHERE project_id = ?
|
|
163483
|
+
GROUP BY task_id
|
|
163484
|
+
ORDER BY MAX(created_at) DESC
|
|
163485
|
+
LIMIT ?
|
|
163486
|
+
`).all(projectId, Math.min(100, Math.max(1, Math.floor(limit2))));
|
|
163487
|
+
return rows.map((row) => this.get(projectId, String(row.task_id))).filter((ledger) => Boolean(ledger));
|
|
163488
|
+
}
|
|
163489
|
+
};
|
|
163490
|
+
}
|
|
163491
|
+
});
|
|
163492
|
+
|
|
162831
163493
|
// src/codegraph/auto-context.ts
|
|
162832
163494
|
var auto_context_exports = {};
|
|
162833
163495
|
__export(auto_context_exports, {
|
|
@@ -163084,6 +163746,16 @@ async function buildAutoProjectContext(input) {
|
|
|
163084
163746
|
};
|
|
163085
163747
|
}
|
|
163086
163748
|
}
|
|
163749
|
+
let continuity = input.continuity;
|
|
163750
|
+
if (!continuity && input.taskId) {
|
|
163751
|
+
try {
|
|
163752
|
+
const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
|
|
163753
|
+
const continuityStore = new TaskContinuityStore2();
|
|
163754
|
+
await continuityStore.init(input.dataDir);
|
|
163755
|
+
continuity = continuityStore.get(input.project.id, input.taskId);
|
|
163756
|
+
} catch {
|
|
163757
|
+
}
|
|
163758
|
+
}
|
|
163087
163759
|
const alwaysOn = await buildAlwaysOnBlock({
|
|
163088
163760
|
dataDir: input.dataDir,
|
|
163089
163761
|
projectId: input.project.id,
|
|
@@ -163102,6 +163774,7 @@ async function buildAutoProjectContext(input) {
|
|
|
163102
163774
|
providerQuality: providerQuality2,
|
|
163103
163775
|
currentFacts: worksetFactLines(currentFacts),
|
|
163104
163776
|
...continuation ? { continuation } : {},
|
|
163777
|
+
...continuity ? { continuity } : {},
|
|
163105
163778
|
codeState: codeStateLine(overview),
|
|
163106
163779
|
...codeEvolution ? { codeEvolution } : {},
|
|
163107
163780
|
reliableMemory: sourceSets.reliableSources.slice(0, lens.sourceLimit).map((source) => ({
|
|
@@ -163149,6 +163822,7 @@ async function buildAutoProjectContext(input) {
|
|
|
163149
163822
|
refresh,
|
|
163150
163823
|
providerQuality: providerQuality2,
|
|
163151
163824
|
...continuationRequested && workset.continuation ? { continuation: workset.continuation } : {},
|
|
163825
|
+
...workset.continuity ? { continuity: workset.continuity } : {},
|
|
163152
163826
|
workset
|
|
163153
163827
|
};
|
|
163154
163828
|
}
|
|
@@ -163450,7 +164124,14 @@ function buildBoundedContextReceipt(input) {
|
|
|
163450
164124
|
externalState: provider2?.external.state ?? "not-detected",
|
|
163451
164125
|
...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
|
|
163452
164126
|
},
|
|
163453
|
-
...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
|
|
164127
|
+
...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {},
|
|
164128
|
+
...input.workset.continuity ? {
|
|
164129
|
+
continuity: {
|
|
164130
|
+
taskId: input.workset.continuity.taskId,
|
|
164131
|
+
status: input.workset.continuity.status,
|
|
164132
|
+
outcome: input.workset.continuity.outcome
|
|
164133
|
+
}
|
|
164134
|
+
} : {}
|
|
163454
164135
|
};
|
|
163455
164136
|
}
|
|
163456
164137
|
var init_context_receipt = __esm({
|
|
@@ -164082,15 +164763,21 @@ var team_store_exports = {};
|
|
|
164082
164763
|
__export(team_store_exports, {
|
|
164083
164764
|
AGENT_TYPE_ROLE_MAP: () => AGENT_TYPE_ROLE_MAP,
|
|
164084
164765
|
DEFAULT_ROLES: () => DEFAULT_ROLES,
|
|
164766
|
+
DEFAULT_TEAM_QUERY_LIMIT: () => DEFAULT_TEAM_QUERY_LIMIT,
|
|
164767
|
+
MAX_TEAM_QUERY_LIMIT: () => MAX_TEAM_QUERY_LIMIT,
|
|
164085
164768
|
TeamStore: () => TeamStore,
|
|
164086
164769
|
getTeamStore: () => getTeamStore,
|
|
164087
164770
|
initTeamStore: () => initTeamStore,
|
|
164088
164771
|
isTeamStoreInitialized: () => isTeamStoreInitialized,
|
|
164089
164772
|
resetTeamStore: () => resetTeamStore
|
|
164090
164773
|
});
|
|
164091
|
-
import { randomUUID as
|
|
164774
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
164092
164775
|
import path31 from "path";
|
|
164093
164776
|
import fs18 from "fs";
|
|
164777
|
+
function clampTeamQueryLimit(limit2) {
|
|
164778
|
+
if (!Number.isFinite(limit2) || limit2 == null) return DEFAULT_TEAM_QUERY_LIMIT;
|
|
164779
|
+
return Math.min(MAX_TEAM_QUERY_LIMIT, Math.max(1, Math.floor(limit2)));
|
|
164780
|
+
}
|
|
164094
164781
|
function getTeamStore() {
|
|
164095
164782
|
if (!_teamStore) {
|
|
164096
164783
|
throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
|
|
@@ -164114,12 +164801,14 @@ function resetTeamStore() {
|
|
|
164114
164801
|
_teamStore = null;
|
|
164115
164802
|
_teamStoreDataDir = null;
|
|
164116
164803
|
}
|
|
164117
|
-
var DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
|
|
164804
|
+
var DEFAULT_TEAM_QUERY_LIMIT, MAX_TEAM_QUERY_LIMIT, DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
|
|
164118
164805
|
var init_team_store = __esm({
|
|
164119
164806
|
"src/team/team-store.ts"() {
|
|
164120
164807
|
"use strict";
|
|
164121
164808
|
init_esm_shims();
|
|
164122
164809
|
init_sqlite_db();
|
|
164810
|
+
DEFAULT_TEAM_QUERY_LIMIT = 100;
|
|
164811
|
+
MAX_TEAM_QUERY_LIMIT = 500;
|
|
164123
164812
|
DEFAULT_ROLES = [
|
|
164124
164813
|
{ roleId: "planner", label: "Planner", description: "Breaks down requirements, creates tasks, defines dependencies", preferredAgentTypes: ["claude-code", "cursor", "codex"], maxConcurrent: 2 },
|
|
164125
164814
|
{ roleId: "researcher", label: "Researcher", description: "Investigates codebase, gathers context, answers questions", preferredAgentTypes: ["claude-code", "cursor", "gemini-cli"], maxConcurrent: 2 },
|
|
@@ -164377,7 +165066,7 @@ var init_team_store = __esm({
|
|
|
164377
165066
|
* reactivate it (preserving agent_id). Otherwise create a new one.
|
|
164378
165067
|
*/
|
|
164379
165068
|
registerAgent(input) {
|
|
164380
|
-
const instanceId = input.instanceId ||
|
|
165069
|
+
const instanceId = input.instanceId || randomUUID18();
|
|
164381
165070
|
const now3 = Date.now();
|
|
164382
165071
|
const existing = this.stmtAgentFindByInstance.get(
|
|
164383
165072
|
input.projectId,
|
|
@@ -164403,7 +165092,7 @@ var init_team_store = __esm({
|
|
|
164403
165092
|
this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
|
|
164404
165093
|
return row2;
|
|
164405
165094
|
}
|
|
164406
|
-
const agentId =
|
|
165095
|
+
const agentId = randomUUID18();
|
|
164407
165096
|
this.stmtAgentUpsert.run({
|
|
164408
165097
|
agent_id: agentId,
|
|
164409
165098
|
project_id: input.projectId,
|
|
@@ -164429,32 +165118,37 @@ var init_team_store = __esm({
|
|
|
164429
165118
|
return this.stmtAgentFindByInstance.get(projectId, agentType, instanceId);
|
|
164430
165119
|
}
|
|
164431
165120
|
listAgents(projectId, filter) {
|
|
164432
|
-
const
|
|
165121
|
+
const limit2 = clampTeamQueryLimit(filter?.limit);
|
|
164433
165122
|
if (filter?.status) {
|
|
164434
|
-
return
|
|
165123
|
+
return this.db.prepare(
|
|
165124
|
+
`SELECT * FROM team_agents WHERE project_id = ? AND status = ? ORDER BY joined_at DESC LIMIT ?`
|
|
165125
|
+
).all(projectId, filter.status, limit2);
|
|
164435
165126
|
}
|
|
164436
|
-
return
|
|
165127
|
+
return this.db.prepare(
|
|
165128
|
+
`SELECT * FROM team_agents WHERE project_id = ? ORDER BY joined_at DESC LIMIT ?`
|
|
165129
|
+
).all(projectId, limit2);
|
|
164437
165130
|
}
|
|
164438
165131
|
/** List agents across all projects (for global scope) */
|
|
164439
|
-
listAllAgents() {
|
|
165132
|
+
listAllAgents(limit2) {
|
|
164440
165133
|
if (!this.db) return [];
|
|
164441
|
-
return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC").all();
|
|
165134
|
+
return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC LIMIT ?").all(clampTeamQueryLimit(limit2));
|
|
164442
165135
|
}
|
|
164443
165136
|
/** List locks across all projects (for global scope) */
|
|
164444
|
-
listAllLocks() {
|
|
165137
|
+
listAllLocks(limit2) {
|
|
164445
165138
|
if (!this.db) return [];
|
|
164446
165139
|
this.db.prepare("DELETE FROM team_locks WHERE expires_at <= ?").run(Date.now());
|
|
164447
|
-
return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC").all(Date.now());
|
|
165140
|
+
return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC LIMIT ?").all(Date.now(), clampTeamQueryLimit(limit2));
|
|
164448
165141
|
}
|
|
164449
165142
|
/** List tasks across all projects (for global scope) */
|
|
164450
165143
|
listAllTasks(filter) {
|
|
164451
165144
|
if (!this.db) return [];
|
|
165145
|
+
const limit2 = clampTeamQueryLimit(filter?.limit);
|
|
164452
165146
|
if (filter?.available) {
|
|
164453
165147
|
return this.db.prepare(
|
|
164454
|
-
`SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC
|
|
164455
|
-
).all();
|
|
165148
|
+
`SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC LIMIT ?`
|
|
165149
|
+
).all(limit2);
|
|
164456
165150
|
}
|
|
164457
|
-
return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC").all();
|
|
165151
|
+
return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC LIMIT ?").all(limit2);
|
|
164458
165152
|
}
|
|
164459
165153
|
heartbeat(agentId) {
|
|
164460
165154
|
const info = this.stmtAgentUpdateHeartbeat.run(Date.now(), agentId);
|
|
@@ -164489,7 +165183,10 @@ var init_team_store = __esm({
|
|
|
164489
165183
|
return staleIds;
|
|
164490
165184
|
}
|
|
164491
165185
|
getActiveCount(projectId) {
|
|
164492
|
-
|
|
165186
|
+
const row = this.db.prepare(
|
|
165187
|
+
`SELECT COUNT(*) AS count FROM team_agents WHERE project_id = ? AND status = 'active'`
|
|
165188
|
+
).get(projectId);
|
|
165189
|
+
return Number(row?.count ?? 0);
|
|
164493
165190
|
}
|
|
164494
165191
|
// ═══════════════════════════════════════════════════════════════════
|
|
164495
165192
|
// Message Operations
|
|
@@ -164505,7 +165202,7 @@ var init_team_store = __esm({
|
|
|
164505
165202
|
return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
|
|
164506
165203
|
}
|
|
164507
165204
|
}
|
|
164508
|
-
const id =
|
|
165205
|
+
const id = randomUUID18();
|
|
164509
165206
|
const now3 = Date.now();
|
|
164510
165207
|
const row = {
|
|
164511
165208
|
id,
|
|
@@ -164524,8 +165221,14 @@ var init_team_store = __esm({
|
|
|
164524
165221
|
this.stmtMsgInsert.run(row);
|
|
164525
165222
|
return row;
|
|
164526
165223
|
}
|
|
164527
|
-
getInbox(projectId, agentId) {
|
|
164528
|
-
return this.
|
|
165224
|
+
getInbox(projectId, agentId, limit2) {
|
|
165225
|
+
return this.db.prepare(`
|
|
165226
|
+
SELECT * FROM team_messages
|
|
165227
|
+
WHERE project_id = ?
|
|
165228
|
+
AND (recipient_agent_id = ? OR recipient_agent_id IS NULL)
|
|
165229
|
+
ORDER BY created_at ASC
|
|
165230
|
+
LIMIT ?
|
|
165231
|
+
`).all(projectId, agentId, clampTeamQueryLimit(limit2));
|
|
164529
165232
|
}
|
|
164530
165233
|
getUnreadCount(projectId, agentId) {
|
|
164531
165234
|
const row = this.stmtMsgUnreadCount.get(projectId, agentId);
|
|
@@ -164555,7 +165258,7 @@ var init_team_store = __esm({
|
|
|
164555
165258
|
// Task Operations (with atomic claim semantics)
|
|
164556
165259
|
// ═══════════════════════════════════════════════════════════════════
|
|
164557
165260
|
createTask(input) {
|
|
164558
|
-
const taskId =
|
|
165261
|
+
const taskId = randomUUID18();
|
|
164559
165262
|
const now3 = Date.now();
|
|
164560
165263
|
const row = {
|
|
164561
165264
|
task_id: taskId,
|
|
@@ -164682,17 +165385,22 @@ var init_team_store = __esm({
|
|
|
164682
165385
|
return info.changes;
|
|
164683
165386
|
}
|
|
164684
165387
|
listTasks(projectId, filter) {
|
|
164685
|
-
|
|
164686
|
-
|
|
164687
|
-
|
|
164688
|
-
const all = this.stmtTaskListByProject.all(projectId);
|
|
165388
|
+
const clauses = ["project_id = ?"];
|
|
165389
|
+
const values = [projectId];
|
|
165390
|
+
if (filter?.available) clauses.push("status = 'pending'", "assignee_agent_id IS NULL");
|
|
164689
165391
|
if (filter?.status) {
|
|
164690
|
-
|
|
165392
|
+
clauses.push("status = ?");
|
|
165393
|
+
values.push(filter.status);
|
|
164691
165394
|
}
|
|
164692
165395
|
if (filter?.assignee) {
|
|
164693
|
-
|
|
165396
|
+
clauses.push("assignee_agent_id = ?");
|
|
165397
|
+
values.push(filter.assignee);
|
|
164694
165398
|
}
|
|
164695
|
-
|
|
165399
|
+
values.push(clampTeamQueryLimit(filter?.limit));
|
|
165400
|
+
const orderBy = filter?.available ? "created_at ASC, rowid ASC" : "created_at DESC, rowid DESC";
|
|
165401
|
+
return this.db.prepare(
|
|
165402
|
+
`SELECT * FROM team_tasks WHERE ${clauses.join(" AND ")} ORDER BY ${orderBy} LIMIT ?`
|
|
165403
|
+
).all(...values);
|
|
164696
165404
|
}
|
|
164697
165405
|
/**
|
|
164698
165406
|
* List available tasks for a specific agent, sorted by role affinity.
|
|
@@ -164704,7 +165412,7 @@ var init_team_store = __esm({
|
|
|
164704
165412
|
const agent = this.stmtAgentFindById.get(agentId);
|
|
164705
165413
|
if (!agent) return [];
|
|
164706
165414
|
const agentRole = agent.role;
|
|
164707
|
-
const available = this.
|
|
165415
|
+
const available = this.listTasks(projectId, { available: true, limit: MAX_TEAM_QUERY_LIMIT });
|
|
164708
165416
|
const eligible = available.filter((t2) => !t2.required_role || t2.required_role === agentRole);
|
|
164709
165417
|
const score = (t2) => {
|
|
164710
165418
|
if (t2.preferred_role === agentRole) return 0;
|
|
@@ -164758,12 +165466,16 @@ var init_team_store = __esm({
|
|
|
164758
165466
|
const row = this.stmtLockGet.get(file, projectId);
|
|
164759
165467
|
return row ?? null;
|
|
164760
165468
|
}
|
|
164761
|
-
listLocks(projectId, agentId) {
|
|
165469
|
+
listLocks(projectId, agentId, limit2) {
|
|
164762
165470
|
this.stmtLockDeleteExpired.run(projectId, Date.now());
|
|
164763
165471
|
if (agentId) {
|
|
164764
|
-
return this.
|
|
165472
|
+
return this.db.prepare(
|
|
165473
|
+
`SELECT * FROM team_locks WHERE project_id = ? AND locked_by = ? ORDER BY locked_at DESC LIMIT ?`
|
|
165474
|
+
).all(projectId, agentId, clampTeamQueryLimit(limit2));
|
|
164765
165475
|
}
|
|
164766
|
-
return this.
|
|
165476
|
+
return this.db.prepare(
|
|
165477
|
+
`SELECT * FROM team_locks WHERE project_id = ? ORDER BY locked_at DESC LIMIT ?`
|
|
165478
|
+
).all(projectId, clampTeamQueryLimit(limit2));
|
|
164767
165479
|
}
|
|
164768
165480
|
releaseAllLocks(agentId) {
|
|
164769
165481
|
const info = this.stmtLockDeleteByAgent.run(agentId);
|
|
@@ -164827,7 +165539,7 @@ var init_team_store = __esm({
|
|
|
164827
165539
|
for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
|
|
164828
165540
|
for (const msg of msgs) {
|
|
164829
165541
|
this.stmtMsgInsert.run({
|
|
164830
|
-
id: msg.id ??
|
|
165542
|
+
id: msg.id ?? randomUUID18(),
|
|
164831
165543
|
project_id: "migrated",
|
|
164832
165544
|
sender_agent_id: msg.from ?? "unknown",
|
|
164833
165545
|
recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
|
|
@@ -166539,8 +167251,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
166539
167251
|
break;
|
|
166540
167252
|
}
|
|
166541
167253
|
case "/graph": {
|
|
166542
|
-
await initGraphStore(effectiveDataDir);
|
|
166543
|
-
const gStore = getGraphStore();
|
|
167254
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
166544
167255
|
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
166545
167256
|
const graphObs = filterDashboardObservations(
|
|
166546
167257
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
@@ -166584,8 +167295,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
166584
167295
|
break;
|
|
166585
167296
|
}
|
|
166586
167297
|
case "/stats": {
|
|
166587
|
-
await initGraphStore(effectiveDataDir);
|
|
166588
|
-
const graph = { entities:
|
|
167298
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
167299
|
+
const graph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
166589
167300
|
const observations2 = filterDashboardObservations(
|
|
166590
167301
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
166591
167302
|
effectiveProjectId
|
|
@@ -166778,15 +167489,15 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
166778
167489
|
case "/knowledge-graph": {
|
|
166779
167490
|
const { generateKnowledgeGraph: generateKnowledgeGraph2 } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
166780
167491
|
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
166781
|
-
const { initGraphStore: initGraphStore2
|
|
167492
|
+
const { initGraphStore: initGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
166782
167493
|
await initMiniSkillStore2(effectiveDataDir);
|
|
166783
|
-
await initGraphStore2(effectiveDataDir);
|
|
167494
|
+
const gStore = await initGraphStore2(effectiveDataDir, effectiveProjectId);
|
|
166784
167495
|
const allObs = filterDashboardObservations(
|
|
166785
167496
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
166786
167497
|
effectiveProjectId
|
|
166787
167498
|
);
|
|
166788
167499
|
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
166789
|
-
const fullGraph = { entities:
|
|
167500
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
166790
167501
|
const scoped = scopeKnowledgeGraphToProject(fullGraph, allObs);
|
|
166791
167502
|
const graph = generateKnowledgeGraph2({
|
|
166792
167503
|
projectId: effectiveProjectId,
|
|
@@ -166983,8 +167694,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
166983
167694
|
} else {
|
|
166984
167695
|
await obsStore.remove(obsId);
|
|
166985
167696
|
try {
|
|
166986
|
-
await initGraphStore(effectiveDataDir);
|
|
166987
|
-
const gStore = getGraphStore();
|
|
167697
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
166988
167698
|
const prefix = `[#${obsId}] `;
|
|
166989
167699
|
const deletions = [];
|
|
166990
167700
|
for (const entity of gStore.loadEntities()) {
|
|
@@ -166999,8 +167709,8 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
166999
167709
|
break;
|
|
167000
167710
|
}
|
|
167001
167711
|
if (apiPath === "/export") {
|
|
167002
|
-
await initGraphStore(effectiveDataDir);
|
|
167003
|
-
const fullGraph = { entities:
|
|
167712
|
+
const gStore = await initGraphStore(effectiveDataDir, effectiveProjectId);
|
|
167713
|
+
const fullGraph = { entities: gStore.loadEntities(), relations: gStore.loadRelations() };
|
|
167004
167714
|
const observations2 = filterDashboardObservations(
|
|
167005
167715
|
await getObservationStore().loadByProject(effectiveProjectId, { status: "active" }),
|
|
167006
167716
|
effectiveProjectId
|
|
@@ -167787,7 +168497,7 @@ __export(planner_exports, {
|
|
|
167787
168497
|
materializeTaskGraph: () => materializeTaskGraph,
|
|
167788
168498
|
seedAutonomousPipeline: () => seedAutonomousPipeline
|
|
167789
168499
|
});
|
|
167790
|
-
import { randomUUID as
|
|
168500
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
167791
168501
|
function isPlannerTask(metadata) {
|
|
167792
168502
|
if (!metadata) return null;
|
|
167793
168503
|
try {
|
|
@@ -167845,7 +168555,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
|
|
|
167845
168555
|
const maxIterations = config.maxIterations ?? 3;
|
|
167846
168556
|
const taskBudget = config.taskBudget ?? 15;
|
|
167847
168557
|
const structuredPlan = opts?.structuredPlan ?? true;
|
|
167848
|
-
const pipelineId =
|
|
168558
|
+
const pipelineId = randomUUID19();
|
|
167849
168559
|
const meta = {
|
|
167850
168560
|
plannerType: "plan",
|
|
167851
168561
|
pipelineId,
|
|
@@ -168526,6 +169236,7 @@ var init_official_skills = __esm({
|
|
|
168526
169236
|
"| Need the sequence around one memory | `memorix_timeline` | `memorix memory timeline --id <id>` |",
|
|
168527
169237
|
"| Check source and freshness for a memory | `memorix_evidence` | `memorix evidence list|get|events` |",
|
|
168528
169238
|
"| Record whether a memory helped, conflicted, or was corrected | `memorix_feedback` | `memorix feedback record|show|audit` |",
|
|
169239
|
+
"| Track task requirements, decisions, verification, and outcome | `memorix_continuity` | `memorix continuity start|record|show|close` |",
|
|
168529
169240
|
"| Inspect or import controlled media | `memorix_media` | `memorix media import|attach|list|show|status` |",
|
|
168530
169241
|
'| Learned reusable project knowledge | `memorix_store` | `memorix memory store --type <type> --entity <name> --title "<title>" "<text>"` |',
|
|
168531
169242
|
"| Task or bug is complete/outdated | `memorix_resolve` | `memorix memory resolve --ids <ids>` |",
|
|
@@ -168549,6 +169260,7 @@ var init_official_skills = __esm({
|
|
|
168549
169260
|
"3. Use stale or unbound memory only as a warning or search lead.",
|
|
168550
169261
|
"4. Store durable decisions, fixes, and gotchas after the project state changes.",
|
|
168551
169262
|
"5. Resolve completed or obsolete memories so future briefs stay small.",
|
|
169263
|
+
"6. For multi-step work, start a continuity ledger and pass its taskId back to memorix_project_context.",
|
|
168552
169264
|
"",
|
|
168553
169265
|
"## Store Rules",
|
|
168554
169266
|
"",
|
|
@@ -170270,7 +170982,7 @@ __export(server_exports2, {
|
|
|
170270
170982
|
createMemorixServer: () => createMemorixServer,
|
|
170271
170983
|
shouldAwaitProjectRuntime: () => shouldAwaitProjectRuntime
|
|
170272
170984
|
});
|
|
170273
|
-
import { createHash as
|
|
170985
|
+
import { createHash as createHash19 } from "crypto";
|
|
170274
170986
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
170275
170987
|
import { z as z627 } from "zod";
|
|
170276
170988
|
function formatFormationStageDurations(stageDurationsMs) {
|
|
@@ -170354,7 +171066,7 @@ function coerceObjectArray(val) {
|
|
|
170354
171066
|
return [];
|
|
170355
171067
|
}
|
|
170356
171068
|
function createDeterministicInstanceId(projectId, agentType, agentName) {
|
|
170357
|
-
const digest2 =
|
|
171069
|
+
const digest2 = createHash19("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
|
|
170358
171070
|
return `auto-${digest2}`;
|
|
170359
171071
|
}
|
|
170360
171072
|
function shouldAwaitProjectRuntime(toolName) {
|
|
@@ -170445,7 +171157,7 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
170445
171157
|
const store = getObservationStore();
|
|
170446
171158
|
console.error(`[memorix] ObservationStore backend: ${store.getBackendName()}, generation: ${store.getGeneration()}`);
|
|
170447
171159
|
}
|
|
170448
|
-
let graphManager = new KnowledgeGraphManager(projectDir2);
|
|
171160
|
+
let graphManager = new KnowledgeGraphManager(projectDir2, project.id);
|
|
170449
171161
|
if (!deferProjectRuntimeInit) {
|
|
170450
171162
|
await graphManager.init();
|
|
170451
171163
|
await initObservations(projectDir2);
|
|
@@ -170457,7 +171169,7 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
170457
171169
|
await initObservationStore(projectDir2);
|
|
170458
171170
|
await initMiniSkillStore(projectDir2);
|
|
170459
171171
|
await initSessionStore(projectDir2);
|
|
170460
|
-
graphManager = new KnowledgeGraphManager(projectDir2);
|
|
171172
|
+
graphManager = new KnowledgeGraphManager(projectDir2, project.id);
|
|
170461
171173
|
await graphManager.init();
|
|
170462
171174
|
await initObservations(projectDir2);
|
|
170463
171175
|
const indexed = await prepareSearchIndex();
|
|
@@ -170561,6 +171273,22 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
170561
171273
|
}
|
|
170562
171274
|
await projectRuntimeInitPromise;
|
|
170563
171275
|
};
|
|
171276
|
+
const activateProjectRuntime = async () => {
|
|
171277
|
+
if (!projectResolved) return;
|
|
171278
|
+
await initObservationStore(projectDir2);
|
|
171279
|
+
await initMiniSkillStore(projectDir2);
|
|
171280
|
+
await initSessionStore(projectDir2);
|
|
171281
|
+
await initObservations(projectDir2, { skipCorpusLoad: true });
|
|
171282
|
+
try {
|
|
171283
|
+
const { initProjectRoot: initProjectRoot2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
|
|
171284
|
+
const { resetDotenv: resetDotenv2, loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
|
|
171285
|
+
initProjectRoot2(project.rootPath);
|
|
171286
|
+
resetDotenv2();
|
|
171287
|
+
loadDotenv2(project.rootPath);
|
|
171288
|
+
} catch {
|
|
171289
|
+
}
|
|
171290
|
+
initLLM({ projectRoot: project.rootPath });
|
|
171291
|
+
};
|
|
170564
171292
|
let maintenanceWorker = null;
|
|
170565
171293
|
const startProjectMaintenanceWorker = async (autoCleanup = false) => {
|
|
170566
171294
|
maintenanceWorker?.stop();
|
|
@@ -171476,6 +172204,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
171476
172204
|
description: "Build a compact Memory Autopilot brief for the current coding task. Schedules Code Memory refresh when needed, includes Start here files, reliable code-bound memories, stale/suspect cautions, and verification hints. Use this at the start of a new coding turn or after switching tasks.",
|
|
171477
172205
|
inputSchema: {
|
|
171478
172206
|
task: z627.string().optional().describe("Current coding task or question"),
|
|
172207
|
+
taskId: z627.string().max(120).optional().describe("Optional task continuity ledger id to include in the brief"),
|
|
171479
172208
|
refresh: z627.enum(["auto", "always", "never"]).optional().default("auto").describe(
|
|
171480
172209
|
"Code Memory refresh policy. auto refreshes only when missing or stale."
|
|
171481
172210
|
),
|
|
@@ -171500,7 +172229,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
171500
172229
|
limit: z627.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
|
|
171501
172230
|
}
|
|
171502
172231
|
},
|
|
171503
|
-
async ({ task, refresh, format, agent }) => {
|
|
172232
|
+
async ({ task, taskId, refresh, format, agent }) => {
|
|
171504
172233
|
const unresolved = requireResolvedProject("build project context for the current project");
|
|
171505
172234
|
if (unresolved) return unresolved;
|
|
171506
172235
|
const [
|
|
@@ -171530,6 +172259,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
171530
172259
|
dataDir: projectDir2,
|
|
171531
172260
|
observations: observations2,
|
|
171532
172261
|
task,
|
|
172262
|
+
taskId,
|
|
171533
172263
|
agent,
|
|
171534
172264
|
refresh: refresh ?? "auto",
|
|
171535
172265
|
reader: getObservationReader(),
|
|
@@ -173955,6 +174685,94 @@ ${json2}
|
|
|
173955
174685
|
}
|
|
173956
174686
|
}
|
|
173957
174687
|
);
|
|
174688
|
+
server.registerTool(
|
|
174689
|
+
"memorix_continuity",
|
|
174690
|
+
{
|
|
174691
|
+
title: "Task Continuity Ledger",
|
|
174692
|
+
description: "Maintain a bounded, append-only continuity ledger for a coding task. Use start for requirements, record for decisions/verification/risks, show or list to inspect, and close for the final outcome. Pass the returned taskId to memorix_project_context so the current brief includes this ledger.",
|
|
174693
|
+
inputSchema: {
|
|
174694
|
+
action: z627.enum(["start", "record", "show", "list", "close"]).describe("Continuity operation"),
|
|
174695
|
+
taskId: z627.string().max(120).optional().describe("Continuity task id"),
|
|
174696
|
+
task: z627.string().max(1e3).optional().describe("Task description for start"),
|
|
174697
|
+
requirements: z627.array(z627.string().max(2e3)).max(8).optional().describe("Requirements for start"),
|
|
174698
|
+
kind: z627.enum(["requirement", "decision", "verification", "risk"]).optional().describe("Entry kind for record"),
|
|
174699
|
+
content: z627.string().max(2e3).optional().describe("Requirement, decision, verification, risk, or outcome text"),
|
|
174700
|
+
verificationStatus: z627.enum(["pending", "passed", "failed", "skipped"]).optional(),
|
|
174701
|
+
verificationId: z627.string().max(120).optional().describe("Stable verification obligation id; reuse it to move pending to passed/failed"),
|
|
174702
|
+
idempotencyKey: z627.string().max(200).optional().describe("Stable retry key; repeating the same operation returns the original event"),
|
|
174703
|
+
status: z627.enum(["completed", "blocked", "abandoned"]).optional().describe("Outcome status for close"),
|
|
174704
|
+
sourceRef: z627.string().max(500).optional().describe("Evidence or source reference"),
|
|
174705
|
+
evidenceRefs: z627.array(z627.string().max(500)).max(8).optional(),
|
|
174706
|
+
actor: z627.string().max(200).optional(),
|
|
174707
|
+
limit: z627.number().int().positive().max(100).optional().default(20)
|
|
174708
|
+
}
|
|
174709
|
+
},
|
|
174710
|
+
async ({ action, taskId, task, requirements, kind, content, verificationStatus, verificationId, idempotencyKey, status, sourceRef, evidenceRefs: evidenceRefs2, actor, limit: limit2 }) => {
|
|
174711
|
+
const unresolved = requireResolvedProject("manage task continuity for the current project");
|
|
174712
|
+
if (unresolved) return unresolved;
|
|
174713
|
+
const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
|
|
174714
|
+
const continuity = new TaskContinuityStore2();
|
|
174715
|
+
await continuity.init(projectDir2);
|
|
174716
|
+
const effectiveActor = actor ?? currentAgentId;
|
|
174717
|
+
try {
|
|
174718
|
+
if (action === "start") {
|
|
174719
|
+
if (!task?.trim()) return { content: [{ type: "text", text: "task is required for continuity start." }], isError: true };
|
|
174720
|
+
const result2 = continuity.start({
|
|
174721
|
+
projectId: project.id,
|
|
174722
|
+
task,
|
|
174723
|
+
...taskId ? { taskId } : {},
|
|
174724
|
+
requirements,
|
|
174725
|
+
...effectiveActor ? { actor: effectiveActor } : {},
|
|
174726
|
+
...idempotencyKey ? { idempotencyKey } : {}
|
|
174727
|
+
});
|
|
174728
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
|
|
174729
|
+
}
|
|
174730
|
+
if (action === "list") {
|
|
174731
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledgers: continuity.list(project.id, limit2) }, null, 2) }] };
|
|
174732
|
+
}
|
|
174733
|
+
if (!taskId?.trim()) return { content: [{ type: "text", text: "taskId is required for this continuity action." }], isError: true };
|
|
174734
|
+
if (action === "show") {
|
|
174735
|
+
const ledger = continuity.get(project.id, taskId);
|
|
174736
|
+
if (!ledger) return { content: [{ type: "text", text: "Task continuity ledger not found." }], isError: true };
|
|
174737
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledger }, null, 2) }] };
|
|
174738
|
+
}
|
|
174739
|
+
if (action === "record") {
|
|
174740
|
+
if (!kind || !content?.trim()) {
|
|
174741
|
+
return { content: [{ type: "text", text: "record requires kind and content." }], isError: true };
|
|
174742
|
+
}
|
|
174743
|
+
const result2 = continuity.record({
|
|
174744
|
+
projectId: project.id,
|
|
174745
|
+
taskId,
|
|
174746
|
+
kind,
|
|
174747
|
+
content,
|
|
174748
|
+
...verificationStatus ? { verificationStatus } : {},
|
|
174749
|
+
...sourceRef ? { sourceRef } : {},
|
|
174750
|
+
...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
|
|
174751
|
+
...effectiveActor ? { actor: effectiveActor } : {},
|
|
174752
|
+
...verificationId ? { verificationId } : {},
|
|
174753
|
+
...idempotencyKey ? { idempotencyKey } : {}
|
|
174754
|
+
});
|
|
174755
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
|
|
174756
|
+
}
|
|
174757
|
+
if (!status || !content?.trim()) {
|
|
174758
|
+
return { content: [{ type: "text", text: "close requires status and content." }], isError: true };
|
|
174759
|
+
}
|
|
174760
|
+
const result = continuity.close({
|
|
174761
|
+
projectId: project.id,
|
|
174762
|
+
taskId,
|
|
174763
|
+
status,
|
|
174764
|
+
content,
|
|
174765
|
+
...sourceRef ? { sourceRef } : {},
|
|
174766
|
+
...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
|
|
174767
|
+
...effectiveActor ? { actor: effectiveActor } : {},
|
|
174768
|
+
...idempotencyKey ? { idempotencyKey } : {}
|
|
174769
|
+
});
|
|
174770
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result }, null, 2) }] };
|
|
174771
|
+
} catch (error) {
|
|
174772
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true };
|
|
174773
|
+
}
|
|
174774
|
+
}
|
|
174775
|
+
);
|
|
173958
174776
|
if (teamFeaturesEnabled) {
|
|
173959
174777
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
173960
174778
|
initTeamStoreForProject = initTeamStore2;
|
|
@@ -174982,7 +175800,9 @@ fi
|
|
|
174982
175800
|
graphManager,
|
|
174983
175801
|
projectId: project.id,
|
|
174984
175802
|
deferredInit,
|
|
175803
|
+
activateProjectRuntime,
|
|
174985
175804
|
switchProject,
|
|
175805
|
+
getProjectDataDir: () => projectDir2,
|
|
174986
175806
|
isExplicitlyBound: () => projectBinding.isExplicit(),
|
|
174987
175807
|
getRequestContext,
|
|
174988
175808
|
handleTransportClose
|
|
@@ -175333,7 +176153,10 @@ var init_mcp_root_path = __esm({
|
|
|
175333
176153
|
// src/server/modern-mcp-bridge.ts
|
|
175334
176154
|
var modern_mcp_bridge_exports = {};
|
|
175335
176155
|
__export(modern_mcp_bridge_exports, {
|
|
175336
|
-
|
|
176156
|
+
ModernMcpRuntimePool: () => ModernMcpRuntimePool,
|
|
176157
|
+
createModernMcpBridge: () => createModernMcpBridge,
|
|
176158
|
+
createModernMcpRuntime: () => createModernMcpRuntime,
|
|
176159
|
+
withBusinessRuntimeQueue: () => withBusinessRuntimeQueue
|
|
175337
176160
|
});
|
|
175338
176161
|
import {
|
|
175339
176162
|
fromJsonSchema,
|
|
@@ -175341,7 +176164,12 @@ import {
|
|
|
175341
176164
|
} from "@modelcontextprotocol/server";
|
|
175342
176165
|
import { Client as LegacyClient } from "@modelcontextprotocol/sdk/client/index.js";
|
|
175343
176166
|
import { InMemoryTransport as LegacyInMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
175344
|
-
|
|
176167
|
+
function withBusinessRuntimeQueue(work) {
|
|
176168
|
+
const result = businessRuntimeQueue.then(work);
|
|
176169
|
+
businessRuntimeQueue = result.catch(() => void 0);
|
|
176170
|
+
return result;
|
|
176171
|
+
}
|
|
176172
|
+
async function createModernMcpRuntime(options) {
|
|
175345
176173
|
const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
|
|
175346
176174
|
const legacy = await createMemorixServer2(
|
|
175347
176175
|
options.projectRoot,
|
|
@@ -175362,46 +176190,135 @@ async function createModernMcpBridge(options) {
|
|
|
175362
176190
|
await legacy.server.connect(serverTransport);
|
|
175363
176191
|
await legacyClient.connect(clientTransport);
|
|
175364
176192
|
const listed = await legacyClient.listTools();
|
|
175365
|
-
const
|
|
175366
|
-
|
|
175367
|
-
|
|
175368
|
-
|
|
175369
|
-
|
|
175370
|
-
...tool.title ? { title: tool.title } : {},
|
|
175371
|
-
...tool.description ? { description: tool.description } : {},
|
|
175372
|
-
inputSchema: fromJsonSchema(tool.inputSchema)
|
|
175373
|
-
},
|
|
175374
|
-
async (args) => await legacyClient.callTool({
|
|
175375
|
-
name: tool.name,
|
|
175376
|
-
arguments: args
|
|
175377
|
-
})
|
|
175378
|
-
);
|
|
175379
|
-
}
|
|
175380
|
-
const bridge = modern;
|
|
175381
|
-
bridge.memorix = {
|
|
176193
|
+
const callTool = (name, args) => withBusinessRuntimeQueue(async () => {
|
|
176194
|
+
await legacy.activateProjectRuntime();
|
|
176195
|
+
return await legacyClient.callTool({ name, arguments: args });
|
|
176196
|
+
});
|
|
176197
|
+
const lifecycle = {
|
|
175382
176198
|
projectId: legacy.projectId,
|
|
175383
176199
|
deferredInit: legacy.deferredInit,
|
|
176200
|
+
activateProjectRuntime: legacy.activateProjectRuntime,
|
|
175384
176201
|
switchProject: legacy.switchProject,
|
|
176202
|
+
getProjectDataDir: legacy.getProjectDataDir,
|
|
175385
176203
|
isExplicitlyBound: legacy.isExplicitlyBound,
|
|
175386
176204
|
handleTransportClose: legacy.handleTransportClose
|
|
175387
176205
|
};
|
|
175388
176206
|
let closed = false;
|
|
175389
|
-
|
|
175390
|
-
|
|
176207
|
+
return {
|
|
176208
|
+
projectId: legacy.projectId,
|
|
176209
|
+
getProjectDataDir: legacy.getProjectDataDir,
|
|
176210
|
+
createBridge: () => {
|
|
176211
|
+
const requestServer = new ModernMcpServer(getMcpServerInfo());
|
|
176212
|
+
for (const tool of listed.tools) {
|
|
176213
|
+
requestServer.registerTool(
|
|
176214
|
+
tool.name,
|
|
176215
|
+
{
|
|
176216
|
+
...tool.title ? { title: tool.title } : {},
|
|
176217
|
+
...tool.description ? { description: tool.description } : {},
|
|
176218
|
+
inputSchema: fromJsonSchema(tool.inputSchema)
|
|
176219
|
+
},
|
|
176220
|
+
async (args) => await callTool(tool.name, args)
|
|
176221
|
+
);
|
|
176222
|
+
}
|
|
176223
|
+
requestServer.memorix = lifecycle;
|
|
176224
|
+
return requestServer;
|
|
176225
|
+
},
|
|
176226
|
+
close: async () => {
|
|
176227
|
+
if (closed) return;
|
|
176228
|
+
closed = true;
|
|
176229
|
+
await legacyClient.close().catch(() => void 0);
|
|
176230
|
+
await legacy.server.close().catch(() => void 0);
|
|
176231
|
+
legacy.handleTransportClose();
|
|
176232
|
+
}
|
|
176233
|
+
};
|
|
176234
|
+
}
|
|
176235
|
+
async function createModernMcpBridge(options) {
|
|
176236
|
+
const runtime = await createModernMcpRuntime(options);
|
|
176237
|
+
const bridge = runtime.createBridge();
|
|
176238
|
+
const close = bridge.close.bind(bridge);
|
|
176239
|
+
let closed = false;
|
|
176240
|
+
bridge.close = async () => {
|
|
175391
176241
|
if (closed) return;
|
|
175392
176242
|
closed = true;
|
|
175393
|
-
await
|
|
175394
|
-
await
|
|
175395
|
-
await legacy.server.close().catch(() => void 0);
|
|
175396
|
-
legacy.handleTransportClose();
|
|
176243
|
+
await close();
|
|
176244
|
+
await runtime.close();
|
|
175397
176245
|
};
|
|
175398
176246
|
return bridge;
|
|
175399
176247
|
}
|
|
176248
|
+
var businessRuntimeQueue, ModernMcpRuntimePool;
|
|
175400
176249
|
var init_modern_mcp_bridge = __esm({
|
|
175401
176250
|
"src/server/modern-mcp-bridge.ts"() {
|
|
175402
176251
|
"use strict";
|
|
175403
176252
|
init_esm_shims();
|
|
175404
176253
|
init_mcp_discovery();
|
|
176254
|
+
businessRuntimeQueue = Promise.resolve();
|
|
176255
|
+
ModernMcpRuntimePool = class {
|
|
176256
|
+
constructor(maxEntries = 32) {
|
|
176257
|
+
this.maxEntries = maxEntries;
|
|
176258
|
+
}
|
|
176259
|
+
maxEntries;
|
|
176260
|
+
entries = /* @__PURE__ */ new Map();
|
|
176261
|
+
pending = /* @__PURE__ */ new Map();
|
|
176262
|
+
async createBridge(key, create2) {
|
|
176263
|
+
let entry = this.entries.get(key);
|
|
176264
|
+
if (!entry) {
|
|
176265
|
+
let pending = this.pending.get(key);
|
|
176266
|
+
if (!pending) {
|
|
176267
|
+
pending = create2().then((runtime) => {
|
|
176268
|
+
const created = { runtime, refs: 0, lastUsedAt: Date.now() };
|
|
176269
|
+
this.entries.set(key, created);
|
|
176270
|
+
return created;
|
|
176271
|
+
});
|
|
176272
|
+
this.pending.set(key, pending);
|
|
176273
|
+
}
|
|
176274
|
+
try {
|
|
176275
|
+
entry = await pending;
|
|
176276
|
+
} finally {
|
|
176277
|
+
this.pending.delete(key);
|
|
176278
|
+
}
|
|
176279
|
+
}
|
|
176280
|
+
entry.refs++;
|
|
176281
|
+
entry.lastUsedAt = Date.now();
|
|
176282
|
+
const bridge = entry.runtime.createBridge();
|
|
176283
|
+
const close = bridge.close.bind(bridge);
|
|
176284
|
+
let released = false;
|
|
176285
|
+
bridge.close = async () => {
|
|
176286
|
+
try {
|
|
176287
|
+
await close();
|
|
176288
|
+
} finally {
|
|
176289
|
+
if (!released) {
|
|
176290
|
+
released = true;
|
|
176291
|
+
entry.refs = Math.max(0, entry.refs - 1);
|
|
176292
|
+
entry.lastUsedAt = Date.now();
|
|
176293
|
+
}
|
|
176294
|
+
}
|
|
176295
|
+
};
|
|
176296
|
+
return bridge;
|
|
176297
|
+
}
|
|
176298
|
+
async evictIdle() {
|
|
176299
|
+
let closed = 0;
|
|
176300
|
+
const idle = [...this.entries.entries()].filter(([, entry]) => entry.refs === 0).sort(([, left], [, right]) => left.lastUsedAt - right.lastUsedAt);
|
|
176301
|
+
for (const [key, entry] of idle) {
|
|
176302
|
+
if (this.entries.size <= this.maxEntries || entry.refs > 0) continue;
|
|
176303
|
+
this.entries.delete(key);
|
|
176304
|
+
await entry.runtime.close();
|
|
176305
|
+
closed++;
|
|
176306
|
+
}
|
|
176307
|
+
return closed;
|
|
176308
|
+
}
|
|
176309
|
+
async close() {
|
|
176310
|
+
const entries = [...this.entries.values()];
|
|
176311
|
+
this.entries.clear();
|
|
176312
|
+
await Promise.all(entries.map((entry) => entry.runtime.close().catch(() => void 0)));
|
|
176313
|
+
}
|
|
176314
|
+
stats() {
|
|
176315
|
+
return {
|
|
176316
|
+
entries: this.entries.size,
|
|
176317
|
+
activeRefs: [...this.entries.values()].reduce((sum, entry) => sum + entry.refs, 0),
|
|
176318
|
+
pending: this.pending.size
|
|
176319
|
+
};
|
|
176320
|
+
}
|
|
176321
|
+
};
|
|
175405
176322
|
}
|
|
175406
176323
|
});
|
|
175407
176324
|
|
|
@@ -175513,7 +176430,7 @@ var init_serve_shared = __esm({
|
|
|
175513
176430
|
|
|
175514
176431
|
// src/cli/version.ts
|
|
175515
176432
|
function getCliVersion() {
|
|
175516
|
-
return true ? "1.9.
|
|
176433
|
+
return true ? "1.9.6" : pkg.version;
|
|
175517
176434
|
}
|
|
175518
176435
|
var init_version3 = __esm({
|
|
175519
176436
|
"src/cli/version.ts"() {
|