memorix 1.9.5 → 1.9.7
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 +53 -0
- package/README.md +6 -1
- package/README.zh-CN.md +6 -1
- package/dist/cli/index.js +1743 -348
- package/dist/index.d.ts +12 -7
- package/dist/index.js +1048 -266
- package/dist/maintenance-runner.js +339 -187
- package/dist/media-audio-runner.js +339 -187
- package/dist/media-video-runner.js +339 -187
- package/dist/memcode-runtime/CHANGELOG.md +53 -0
- package/dist/sdk.js +967 -241
- package/dist/vector-backfill-runner.js +339 -187
- package/docs/API_REFERENCE.md +36 -0
- package/docs/CONFIGURATION.md +43 -0
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/KNOWN_ISSUES_AND_ROADMAP.md +49 -14
- package/docs/PERFORMANCE.md +46 -2
- package/docs/README.md +2 -1
- package/docs/SETUP.md +1 -1
- package/package.json +2 -1
- 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 +13 -1
- 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 +242 -42
- 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/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/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/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/server/modern-mcp-bridge.ts +153 -27
- package/src/server/tool-profile.ts +3 -2
- package/src/server.ts +129 -5
- package/src/store/orama-store.ts +16 -1
- package/src/store/sqlite-db.ts +144 -4
- package/src/store/sqlite-fts.ts +38 -16
- package/src/team/team-store.ts +62 -27
package/dist/cli/index.js
CHANGED
|
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
|
|
|
3232
3232
|
|
|
3233
3233
|
// src/cli/version.ts
|
|
3234
3234
|
function getCliVersion() {
|
|
3235
|
-
return true ? "1.9.
|
|
3235
|
+
return true ? "1.9.7" : pkg.version;
|
|
3236
3236
|
}
|
|
3237
3237
|
var init_version = __esm({
|
|
3238
3238
|
"src/cli/version.ts"() {
|
|
@@ -9349,6 +9349,74 @@ var init_launch_root = __esm({
|
|
|
9349
9349
|
}
|
|
9350
9350
|
});
|
|
9351
9351
|
|
|
9352
|
+
// src/store/sqlite-reliability.ts
|
|
9353
|
+
function isTransientSqliteError(error40) {
|
|
9354
|
+
const code = error40?.code;
|
|
9355
|
+
if (typeof code === "string") {
|
|
9356
|
+
return /^SQLITE_BUSY(?:_|$)/i.test(code);
|
|
9357
|
+
}
|
|
9358
|
+
return error40 instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error40.message);
|
|
9359
|
+
}
|
|
9360
|
+
function isSqliteUnavailableError(error40) {
|
|
9361
|
+
return error40 instanceof SqliteUnavailableError || error40 instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error40.message);
|
|
9362
|
+
}
|
|
9363
|
+
function degradedReadError(storeName) {
|
|
9364
|
+
return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
|
|
9365
|
+
}
|
|
9366
|
+
function degradedWriteError(storeName) {
|
|
9367
|
+
return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
|
|
9368
|
+
}
|
|
9369
|
+
function formatSqliteFailure(error40) {
|
|
9370
|
+
return error40 instanceof Error && error40.message ? error40.message : String(error40);
|
|
9371
|
+
}
|
|
9372
|
+
function delay(ms2) {
|
|
9373
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms2));
|
|
9374
|
+
}
|
|
9375
|
+
async function retrySqliteBusy(operation, label) {
|
|
9376
|
+
for (let attempt = 0; ; attempt++) {
|
|
9377
|
+
try {
|
|
9378
|
+
return await operation();
|
|
9379
|
+
} catch (error40) {
|
|
9380
|
+
if (!isTransientSqliteError(error40) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error40;
|
|
9381
|
+
const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
|
|
9382
|
+
console.error(
|
|
9383
|
+
`[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
|
|
9384
|
+
);
|
|
9385
|
+
await delay(waitMs);
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9388
|
+
}
|
|
9389
|
+
async function initializeSqliteStore(factory, dataDir, label) {
|
|
9390
|
+
try {
|
|
9391
|
+
loadBetterSqlite3();
|
|
9392
|
+
} catch (error40) {
|
|
9393
|
+
throw new SqliteUnavailableError(error40);
|
|
9394
|
+
}
|
|
9395
|
+
return retrySqliteBusy(async () => {
|
|
9396
|
+
const store2 = factory();
|
|
9397
|
+
await store2.init(dataDir);
|
|
9398
|
+
return store2;
|
|
9399
|
+
}, label);
|
|
9400
|
+
}
|
|
9401
|
+
var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
|
|
9402
|
+
var init_sqlite_reliability = __esm({
|
|
9403
|
+
"src/store/sqlite-reliability.ts"() {
|
|
9404
|
+
"use strict";
|
|
9405
|
+
init_esm_shims();
|
|
9406
|
+
init_sqlite_db();
|
|
9407
|
+
SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
|
|
9408
|
+
SqliteUnavailableError = class extends Error {
|
|
9409
|
+
code = "MEMORIX_SQLITE_UNAVAILABLE";
|
|
9410
|
+
constructor(cause) {
|
|
9411
|
+
super(
|
|
9412
|
+
cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
|
|
9413
|
+
);
|
|
9414
|
+
this.name = "SqliteUnavailableError";
|
|
9415
|
+
}
|
|
9416
|
+
};
|
|
9417
|
+
}
|
|
9418
|
+
});
|
|
9419
|
+
|
|
9352
9420
|
// src/store/sqlite-fts.ts
|
|
9353
9421
|
function normalizeFtsToken(token) {
|
|
9354
9422
|
return token.replace(/"/g, '""');
|
|
@@ -9362,22 +9430,27 @@ function buildFtsQuery(query) {
|
|
|
9362
9430
|
function initializeObservationLexicalIndex(db2) {
|
|
9363
9431
|
const known = ftsAvailability.get(db2);
|
|
9364
9432
|
if (known !== void 0) return known;
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
db2.
|
|
9433
|
+
let lastError;
|
|
9434
|
+
for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
|
|
9435
|
+
try {
|
|
9436
|
+
db2.exec(CREATE_OBSERVATION_FTS);
|
|
9437
|
+
const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
|
|
9438
|
+
const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
|
|
9439
|
+
if (observationCount !== indexedCount) {
|
|
9440
|
+
db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
|
|
9441
|
+
}
|
|
9442
|
+
ftsAvailability.set(db2, true);
|
|
9443
|
+
return true;
|
|
9444
|
+
} catch (error40) {
|
|
9445
|
+
lastError = error40;
|
|
9446
|
+
if (!isTransientSqliteError(error40) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
|
|
9371
9447
|
}
|
|
9372
|
-
ftsAvailability.set(db2, true);
|
|
9373
|
-
return true;
|
|
9374
|
-
} catch (error40) {
|
|
9375
|
-
ftsAvailability.set(db2, false);
|
|
9376
|
-
console.warn(
|
|
9377
|
-
`[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error40 instanceof Error ? error40.message : String(error40)}`
|
|
9378
|
-
);
|
|
9379
|
-
return false;
|
|
9380
9448
|
}
|
|
9449
|
+
ftsAvailability.set(db2, false);
|
|
9450
|
+
console.warn(
|
|
9451
|
+
`[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
|
|
9452
|
+
);
|
|
9453
|
+
return false;
|
|
9381
9454
|
}
|
|
9382
9455
|
function isObservationLexicalIndexEnabled(db2) {
|
|
9383
9456
|
return ftsAvailability.get(db2) === true;
|
|
@@ -9431,11 +9504,12 @@ function searchObservationLexically(db2, options) {
|
|
|
9431
9504
|
score: Math.max(0, Number(row.lexical_score ?? 0))
|
|
9432
9505
|
}));
|
|
9433
9506
|
}
|
|
9434
|
-
var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
|
|
9507
|
+
var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
|
|
9435
9508
|
var init_sqlite_fts = __esm({
|
|
9436
9509
|
"src/store/sqlite-fts.ts"() {
|
|
9437
9510
|
"use strict";
|
|
9438
9511
|
init_esm_shims();
|
|
9512
|
+
init_sqlite_reliability();
|
|
9439
9513
|
ftsAvailability = /* @__PURE__ */ new WeakMap();
|
|
9440
9514
|
FTS_TABLE = "observations_fts";
|
|
9441
9515
|
CREATE_OBSERVATION_FTS = `
|
|
@@ -9469,15 +9543,19 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
|
|
|
9469
9543
|
VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
|
|
9470
9544
|
END;
|
|
9471
9545
|
`;
|
|
9546
|
+
FTS_INIT_MAX_ATTEMPTS = 3;
|
|
9472
9547
|
}
|
|
9473
9548
|
});
|
|
9474
9549
|
|
|
9475
9550
|
// src/store/sqlite-db.ts
|
|
9476
9551
|
var sqlite_db_exports = {};
|
|
9477
9552
|
__export(sqlite_db_exports, {
|
|
9553
|
+
acquireDatabase: () => acquireDatabase,
|
|
9478
9554
|
closeAllDatabases: () => closeAllDatabases,
|
|
9479
9555
|
closeDatabase: () => closeDatabase,
|
|
9556
|
+
evictIdleDatabases: () => evictIdleDatabases,
|
|
9480
9557
|
getDatabase: () => getDatabase,
|
|
9558
|
+
getDatabaseStats: () => getDatabaseStats,
|
|
9481
9559
|
isSqliteAvailable: () => isSqliteAvailable,
|
|
9482
9560
|
loadBetterSqlite3: () => loadBetterSqlite3
|
|
9483
9561
|
});
|
|
@@ -9515,11 +9593,21 @@ function applySchemaMigrations(db2) {
|
|
|
9515
9593
|
apply();
|
|
9516
9594
|
}
|
|
9517
9595
|
}
|
|
9596
|
+
function normalizeDataDir(dataDir) {
|
|
9597
|
+
return path3.resolve(dataDir);
|
|
9598
|
+
}
|
|
9599
|
+
function touchDatabase(normalized, db2) {
|
|
9600
|
+
_dbCache.delete(normalized);
|
|
9601
|
+
_dbCache.set(normalized, db2);
|
|
9602
|
+
}
|
|
9518
9603
|
function getDatabase(dataDir) {
|
|
9519
|
-
const normalized =
|
|
9604
|
+
const normalized = normalizeDataDir(dataDir);
|
|
9520
9605
|
assertNotHomeDataDir(normalized);
|
|
9521
9606
|
const existing = _dbCache.get(normalized);
|
|
9522
|
-
if (existing)
|
|
9607
|
+
if (existing) {
|
|
9608
|
+
touchDatabase(normalized, existing);
|
|
9609
|
+
return existing;
|
|
9610
|
+
}
|
|
9523
9611
|
loadBetterSqlite3();
|
|
9524
9612
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
9525
9613
|
const dbPath = path3.join(dataDir, "memorix.db");
|
|
@@ -9549,6 +9637,8 @@ function getDatabase(dataDir) {
|
|
|
9549
9637
|
db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
|
|
9550
9638
|
db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
|
|
9551
9639
|
db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
|
|
9640
|
+
db2.exec(CREATE_TASK_CONTINUITY_EVENTS_TABLE);
|
|
9641
|
+
db2.exec(CREATE_AGENT_EFFECTS_TABLE);
|
|
9552
9642
|
try {
|
|
9553
9643
|
db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
|
|
9554
9644
|
} catch {
|
|
@@ -9587,7 +9677,7 @@ function getDatabase(dataDir) {
|
|
|
9587
9677
|
db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
|
|
9588
9678
|
db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
|
|
9589
9679
|
db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
|
|
9590
|
-
|
|
9680
|
+
touchDatabase(normalized, db2);
|
|
9591
9681
|
return db2;
|
|
9592
9682
|
} catch (error40) {
|
|
9593
9683
|
try {
|
|
@@ -9597,8 +9687,46 @@ function getDatabase(dataDir) {
|
|
|
9597
9687
|
throw error40;
|
|
9598
9688
|
}
|
|
9599
9689
|
}
|
|
9690
|
+
function acquireDatabase(dataDir) {
|
|
9691
|
+
const normalized = normalizeDataDir(dataDir);
|
|
9692
|
+
const db2 = getDatabase(normalized);
|
|
9693
|
+
_dbLeases.set(normalized, (_dbLeases.get(normalized) ?? 0) + 1);
|
|
9694
|
+
let released = false;
|
|
9695
|
+
return {
|
|
9696
|
+
dataDir: normalized,
|
|
9697
|
+
db: db2,
|
|
9698
|
+
release: () => {
|
|
9699
|
+
if (released) return;
|
|
9700
|
+
released = true;
|
|
9701
|
+
const count3 = (_dbLeases.get(normalized) ?? 0) - 1;
|
|
9702
|
+
if (count3 > 0) _dbLeases.set(normalized, count3);
|
|
9703
|
+
else _dbLeases.delete(normalized);
|
|
9704
|
+
}
|
|
9705
|
+
};
|
|
9706
|
+
}
|
|
9707
|
+
function getDatabaseStats() {
|
|
9708
|
+
let leased = 0;
|
|
9709
|
+
for (const count3 of _dbLeases.values()) leased += count3;
|
|
9710
|
+
return { cached: _dbCache.size, leased, leasedDataDirs: _dbLeases.size };
|
|
9711
|
+
}
|
|
9712
|
+
function evictIdleDatabases(maxCached) {
|
|
9713
|
+
const target = Math.max(0, Math.floor(maxCached));
|
|
9714
|
+
let evicted = 0;
|
|
9715
|
+
for (const [normalized, db2] of [..._dbCache]) {
|
|
9716
|
+
if (_dbCache.size <= target) break;
|
|
9717
|
+
if ((_dbLeases.get(normalized) ?? 0) > 0) continue;
|
|
9718
|
+
try {
|
|
9719
|
+
db2.close();
|
|
9720
|
+
} catch {
|
|
9721
|
+
}
|
|
9722
|
+
_dbCache.delete(normalized);
|
|
9723
|
+
evicted++;
|
|
9724
|
+
}
|
|
9725
|
+
return evicted;
|
|
9726
|
+
}
|
|
9600
9727
|
function closeDatabase(dataDir) {
|
|
9601
|
-
const normalized =
|
|
9728
|
+
const normalized = normalizeDataDir(dataDir);
|
|
9729
|
+
if ((_dbLeases.get(normalized) ?? 0) > 0) return;
|
|
9602
9730
|
const db2 = _dbCache.get(normalized);
|
|
9603
9731
|
if (db2) {
|
|
9604
9732
|
try {
|
|
@@ -9616,6 +9744,7 @@ function closeAllDatabases() {
|
|
|
9616
9744
|
}
|
|
9617
9745
|
_dbCache.delete(key);
|
|
9618
9746
|
}
|
|
9747
|
+
_dbLeases.clear();
|
|
9619
9748
|
}
|
|
9620
9749
|
function isSqliteAvailable() {
|
|
9621
9750
|
try {
|
|
@@ -9625,7 +9754,7 @@ function isSqliteAvailable() {
|
|
|
9625
9754
|
return false;
|
|
9626
9755
|
}
|
|
9627
9756
|
}
|
|
9628
|
-
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;
|
|
9757
|
+
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;
|
|
9629
9758
|
var init_sqlite_db = __esm({
|
|
9630
9759
|
"src/store/sqlite-db.ts"() {
|
|
9631
9760
|
"use strict";
|
|
@@ -10342,6 +10471,43 @@ CREATE TABLE IF NOT EXISTS memory_feedback_events (
|
|
|
10342
10471
|
delta REAL NOT NULL DEFAULT 0,
|
|
10343
10472
|
observed_at TEXT NOT NULL
|
|
10344
10473
|
);
|
|
10474
|
+
`;
|
|
10475
|
+
CREATE_TASK_CONTINUITY_EVENTS_TABLE = `
|
|
10476
|
+
CREATE TABLE IF NOT EXISTS task_continuity_events (
|
|
10477
|
+
id TEXT PRIMARY KEY,
|
|
10478
|
+
project_id TEXT NOT NULL,
|
|
10479
|
+
task_id TEXT NOT NULL,
|
|
10480
|
+
task TEXT NOT NULL,
|
|
10481
|
+
kind TEXT NOT NULL,
|
|
10482
|
+
content TEXT NOT NULL,
|
|
10483
|
+
item_status TEXT,
|
|
10484
|
+
source_ref TEXT,
|
|
10485
|
+
evidence_json TEXT NOT NULL DEFAULT '[]',
|
|
10486
|
+
actor TEXT,
|
|
10487
|
+
created_at TEXT NOT NULL,
|
|
10488
|
+
event_key TEXT,
|
|
10489
|
+
item_key TEXT,
|
|
10490
|
+
supersedes_id TEXT
|
|
10491
|
+
);
|
|
10492
|
+
`;
|
|
10493
|
+
CREATE_AGENT_EFFECTS_TABLE = `
|
|
10494
|
+
CREATE TABLE IF NOT EXISTS agent_effects (
|
|
10495
|
+
id TEXT PRIMARY KEY,
|
|
10496
|
+
pipeline_id TEXT NOT NULL,
|
|
10497
|
+
task_id TEXT NOT NULL,
|
|
10498
|
+
attempt INTEGER NOT NULL,
|
|
10499
|
+
effect_key TEXT NOT NULL,
|
|
10500
|
+
tool TEXT NOT NULL,
|
|
10501
|
+
risk_tier TEXT NOT NULL,
|
|
10502
|
+
replay_policy TEXT NOT NULL,
|
|
10503
|
+
status TEXT NOT NULL,
|
|
10504
|
+
call_id TEXT,
|
|
10505
|
+
idempotency_key TEXT NOT NULL,
|
|
10506
|
+
input_fingerprint TEXT,
|
|
10507
|
+
detail TEXT,
|
|
10508
|
+
started_at INTEGER NOT NULL,
|
|
10509
|
+
finished_at INTEGER
|
|
10510
|
+
);
|
|
10345
10511
|
`;
|
|
10346
10512
|
CREATE_MCP_BINDINGS_TABLE = `
|
|
10347
10513
|
CREATE TABLE IF NOT EXISTS mcp_bindings (
|
|
@@ -10420,7 +10586,18 @@ CREATE INDEX IF NOT EXISTS idx_evidence_cards_candidate ON evidence_cards(projec
|
|
|
10420
10586
|
CREATE INDEX IF NOT EXISTS idx_evidence_card_events_card ON evidence_card_events(card_id, created_at);
|
|
10421
10587
|
CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC);
|
|
10422
10588
|
CREATE INDEX IF NOT EXISTS idx_memory_feedback_events_candidate ON memory_feedback_events(project_id, candidate_kind, candidate_id, observed_at DESC);
|
|
10589
|
+
CREATE INDEX IF NOT EXISTS idx_task_continuity_project_task ON task_continuity_events(project_id, task_id, created_at ASC);
|
|
10590
|
+
CREATE INDEX IF NOT EXISTS idx_task_continuity_project_recent ON task_continuity_events(project_id, created_at DESC);
|
|
10591
|
+
CREATE INDEX IF NOT EXISTS idx_task_continuity_item ON task_continuity_events(project_id, task_id, kind, item_key, created_at DESC);
|
|
10592
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
|
|
10593
|
+
ON task_continuity_events(project_id, task_id, event_key)
|
|
10594
|
+
WHERE event_key IS NOT NULL;
|
|
10595
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_effect_identity
|
|
10596
|
+
ON agent_effects(pipeline_id, task_id, attempt, effect_key);
|
|
10597
|
+
CREATE INDEX IF NOT EXISTS idx_agent_effect_pipeline ON agent_effects(pipeline_id, started_at ASC);
|
|
10423
10598
|
CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC);
|
|
10599
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC);
|
|
10600
|
+
CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at);
|
|
10424
10601
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
10425
10602
|
ON maintenance_jobs(project_id, kind, dedupe_key)
|
|
10426
10603
|
WHERE status IN ('pending', 'running', 'retry');
|
|
@@ -10591,6 +10768,8 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
10591
10768
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_memory_feedback_states_project ON memory_feedback_states(project_id, candidate_kind, updated_at DESC)");
|
|
10592
10769
|
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)");
|
|
10593
10770
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_project ON mcp_bindings(project_id, last_used_at DESC)");
|
|
10771
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_root ON mcp_bindings(project_root, last_used_at DESC)");
|
|
10772
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_mcp_bindings_expiry ON mcp_bindings(expires_at)");
|
|
10594
10773
|
}
|
|
10595
10774
|
},
|
|
10596
10775
|
{
|
|
@@ -10601,77 +10780,22 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
10601
10780
|
addColumnIfMissing(db2, "code_symbols", "source", "source TEXT");
|
|
10602
10781
|
addColumnIfMissing(db2, "code_edges", "source", "source TEXT");
|
|
10603
10782
|
}
|
|
10783
|
+
},
|
|
10784
|
+
{
|
|
10785
|
+
id: "1.9-continuity-v2",
|
|
10786
|
+
apply: (db2) => {
|
|
10787
|
+
addColumnIfMissing(db2, "task_continuity_events", "event_key", "event_key TEXT");
|
|
10788
|
+
addColumnIfMissing(db2, "task_continuity_events", "item_key", "item_key TEXT");
|
|
10789
|
+
addColumnIfMissing(db2, "task_continuity_events", "supersedes_id", "supersedes_id TEXT");
|
|
10790
|
+
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)");
|
|
10791
|
+
db2.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_task_continuity_event_key
|
|
10792
|
+
ON task_continuity_events(project_id, task_id, event_key)
|
|
10793
|
+
WHERE event_key IS NOT NULL`);
|
|
10794
|
+
}
|
|
10604
10795
|
}
|
|
10605
10796
|
];
|
|
10606
10797
|
_dbCache = /* @__PURE__ */ new Map();
|
|
10607
|
-
|
|
10608
|
-
});
|
|
10609
|
-
|
|
10610
|
-
// src/store/sqlite-reliability.ts
|
|
10611
|
-
function isTransientSqliteError(error40) {
|
|
10612
|
-
const code = error40?.code;
|
|
10613
|
-
if (typeof code === "string") {
|
|
10614
|
-
return /^SQLITE_BUSY(?:_|$)/i.test(code);
|
|
10615
|
-
}
|
|
10616
|
-
return error40 instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error40.message);
|
|
10617
|
-
}
|
|
10618
|
-
function isSqliteUnavailableError(error40) {
|
|
10619
|
-
return error40 instanceof SqliteUnavailableError || error40 instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error40.message);
|
|
10620
|
-
}
|
|
10621
|
-
function degradedReadError(storeName) {
|
|
10622
|
-
return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
|
|
10623
|
-
}
|
|
10624
|
-
function degradedWriteError(storeName) {
|
|
10625
|
-
return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
|
|
10626
|
-
}
|
|
10627
|
-
function formatSqliteFailure(error40) {
|
|
10628
|
-
return error40 instanceof Error && error40.message ? error40.message : String(error40);
|
|
10629
|
-
}
|
|
10630
|
-
function delay(ms2) {
|
|
10631
|
-
return new Promise((resolve9) => setTimeout(resolve9, ms2));
|
|
10632
|
-
}
|
|
10633
|
-
async function retrySqliteBusy(operation, label) {
|
|
10634
|
-
for (let attempt = 0; ; attempt++) {
|
|
10635
|
-
try {
|
|
10636
|
-
return await operation();
|
|
10637
|
-
} catch (error40) {
|
|
10638
|
-
if (!isTransientSqliteError(error40) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error40;
|
|
10639
|
-
const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
|
|
10640
|
-
console.error(
|
|
10641
|
-
`[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
|
|
10642
|
-
);
|
|
10643
|
-
await delay(waitMs);
|
|
10644
|
-
}
|
|
10645
|
-
}
|
|
10646
|
-
}
|
|
10647
|
-
async function initializeSqliteStore(factory, dataDir, label) {
|
|
10648
|
-
try {
|
|
10649
|
-
loadBetterSqlite3();
|
|
10650
|
-
} catch (error40) {
|
|
10651
|
-
throw new SqliteUnavailableError(error40);
|
|
10652
|
-
}
|
|
10653
|
-
return retrySqliteBusy(async () => {
|
|
10654
|
-
const store2 = factory();
|
|
10655
|
-
await store2.init(dataDir);
|
|
10656
|
-
return store2;
|
|
10657
|
-
}, label);
|
|
10658
|
-
}
|
|
10659
|
-
var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
|
|
10660
|
-
var init_sqlite_reliability = __esm({
|
|
10661
|
-
"src/store/sqlite-reliability.ts"() {
|
|
10662
|
-
"use strict";
|
|
10663
|
-
init_esm_shims();
|
|
10664
|
-
init_sqlite_db();
|
|
10665
|
-
SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
|
|
10666
|
-
SqliteUnavailableError = class extends Error {
|
|
10667
|
-
code = "MEMORIX_SQLITE_UNAVAILABLE";
|
|
10668
|
-
constructor(cause) {
|
|
10669
|
-
super(
|
|
10670
|
-
cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
|
|
10671
|
-
);
|
|
10672
|
-
this.name = "SqliteUnavailableError";
|
|
10673
|
-
}
|
|
10674
|
-
};
|
|
10798
|
+
_dbLeases = /* @__PURE__ */ new Map();
|
|
10675
10799
|
}
|
|
10676
10800
|
});
|
|
10677
10801
|
|
|
@@ -21094,18 +21218,23 @@ __export(yaml_loader_exports, {
|
|
|
21094
21218
|
});
|
|
21095
21219
|
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
21096
21220
|
import { homedir } from "os";
|
|
21221
|
+
function cacheKey(homeDir, projectRoot) {
|
|
21222
|
+
return `${homeDir}\0${projectRoot ?? ""}`;
|
|
21223
|
+
}
|
|
21097
21224
|
function initProjectRoot(root) {
|
|
21098
21225
|
globalProjectRoot = root;
|
|
21099
|
-
configCache.delete(root);
|
|
21226
|
+
configCache.delete(cacheKey(homedir(), root));
|
|
21100
21227
|
}
|
|
21101
21228
|
function clearProjectRoot() {
|
|
21102
21229
|
globalProjectRoot = null;
|
|
21103
21230
|
}
|
|
21104
|
-
function loadYamlConfig(projectRoot) {
|
|
21231
|
+
function loadYamlConfig(projectRoot, options = {}) {
|
|
21232
|
+
const homeDir = options.homeDir ?? homedir();
|
|
21105
21233
|
const resolvedRoot = projectRoot === null ? null : projectRoot ?? globalProjectRoot ?? null;
|
|
21106
|
-
const
|
|
21234
|
+
const key = cacheKey(homeDir, resolvedRoot);
|
|
21235
|
+
const cached2 = configCache.get(key);
|
|
21107
21236
|
if (cached2) return cached2;
|
|
21108
|
-
const userYaml = getGlobalYamlPath(
|
|
21237
|
+
const userYaml = getGlobalYamlPath(homeDir);
|
|
21109
21238
|
const projectYaml = resolvedRoot ? getProjectYamlPath(resolvedRoot) : null;
|
|
21110
21239
|
let userConfig = {};
|
|
21111
21240
|
let projectConfig = {};
|
|
@@ -21137,12 +21266,12 @@ function loadYamlConfig(projectRoot) {
|
|
|
21137
21266
|
server: { ...userConfig.server, ...projectConfig.server },
|
|
21138
21267
|
team: { ...userConfig.team, ...projectConfig.team }
|
|
21139
21268
|
};
|
|
21140
|
-
configCache.set(
|
|
21269
|
+
configCache.set(key, merged);
|
|
21141
21270
|
return merged;
|
|
21142
21271
|
}
|
|
21143
|
-
function resetYamlConfigCache(projectRoot) {
|
|
21272
|
+
function resetYamlConfigCache(projectRoot, homeDir = homedir()) {
|
|
21144
21273
|
if (projectRoot !== void 0) {
|
|
21145
|
-
configCache.delete(projectRoot ?? null);
|
|
21274
|
+
configCache.delete(cacheKey(homeDir, projectRoot ?? null));
|
|
21146
21275
|
} else {
|
|
21147
21276
|
configCache.clear();
|
|
21148
21277
|
}
|
|
@@ -21353,9 +21482,9 @@ var init_detector = __esm({
|
|
|
21353
21482
|
// src/config/legacy-loader.ts
|
|
21354
21483
|
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
21355
21484
|
import { homedir as homedir2 } from "os";
|
|
21356
|
-
function loadFileConfig() {
|
|
21485
|
+
function loadFileConfig(homeDir = homedir2()) {
|
|
21357
21486
|
if (cachedConfig !== null) return cachedConfig;
|
|
21358
|
-
const configPath = getLegacyConfigJsonPath(
|
|
21487
|
+
const configPath = getLegacyConfigJsonPath(homeDir);
|
|
21359
21488
|
try {
|
|
21360
21489
|
if (existsSync6(configPath)) {
|
|
21361
21490
|
cachedConfig = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
@@ -21385,15 +21514,15 @@ import { homedir as homedir3 } from "os";
|
|
|
21385
21514
|
function loadTomlConfig(options = {}) {
|
|
21386
21515
|
const homeDir = options.homeDir ?? homedir3();
|
|
21387
21516
|
const projectRoot = options.projectRoot ?? null;
|
|
21388
|
-
const
|
|
21389
|
-
const cached2 = configCache2.get(
|
|
21517
|
+
const cacheKey4 = `${homeDir}\0${projectRoot ?? ""}`;
|
|
21518
|
+
const cached2 = configCache2.get(cacheKey4);
|
|
21390
21519
|
if (cached2) return cached2;
|
|
21391
21520
|
const userToml = getGlobalConfigTomlPath(homeDir);
|
|
21392
21521
|
const projectToml = projectRoot ? getProjectConfigTomlPath(projectRoot) : null;
|
|
21393
21522
|
const userConfig = existsSync7(userToml) ? parseTomlConfig(readFileSync6(userToml, "utf8"), userToml) : {};
|
|
21394
21523
|
const projectConfig = projectToml && existsSync7(projectToml) ? parseTomlConfig(readFileSync6(projectToml, "utf8"), projectToml) : {};
|
|
21395
21524
|
const merged = mergeTomlConfig(userConfig, projectConfig);
|
|
21396
|
-
configCache2.set(
|
|
21525
|
+
configCache2.set(cacheKey4, merged);
|
|
21397
21526
|
return merged;
|
|
21398
21527
|
}
|
|
21399
21528
|
function mergeTomlConfig(base, override) {
|
|
@@ -21989,8 +22118,8 @@ function getResolvedConfig(options = {}) {
|
|
|
21989
22118
|
const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
|
|
21990
22119
|
loadDotenv(projectRoot === null ? void 0 : projectRoot ?? void 0, { userHomeDir: homeDir });
|
|
21991
22120
|
const toml = loadTomlConfig({ projectRoot: projectRoot ?? null, homeDir });
|
|
21992
|
-
const yaml = loadYamlConfig(projectRoot ?? null);
|
|
21993
|
-
const legacy = loadFileConfig();
|
|
22121
|
+
const yaml = loadYamlConfig(projectRoot ?? null, { homeDir });
|
|
22122
|
+
const legacy = loadFileConfig(homeDir);
|
|
21994
22123
|
const embeddingBaseUrl = first(process.env.MEMORIX_EMBEDDING_BASE_URL, toml.embedding?.base_url, yaml.embedding?.baseUrl, legacy.embeddingApi?.baseUrl);
|
|
21995
22124
|
const openRouterEmbeddingApiKey = isOpenRouterUrl(embeddingBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
|
|
21996
22125
|
const memoryLlmProvider = first(
|
|
@@ -22012,15 +22141,17 @@ function getResolvedConfig(options = {}) {
|
|
|
22012
22141
|
legacy.llm?.baseUrl
|
|
22013
22142
|
);
|
|
22014
22143
|
const openRouterMemoryLlmApiKey = isOpenRouterMemoryLane(memoryLlmProvider, memoryLlmBaseUrl) ? process.env.OPENROUTER_API_KEY : void 0;
|
|
22144
|
+
const isAtlasCloud = memoryLlmProvider === "atlascloud";
|
|
22015
22145
|
const memoryLlmApiKey = first(
|
|
22016
22146
|
process.env.MEMORIX_LLM_API_KEY,
|
|
22017
22147
|
process.env.MEMORIX_API_KEY,
|
|
22018
22148
|
toml.memory?.llm?.api_key,
|
|
22019
22149
|
yaml.llm?.apiKey,
|
|
22020
22150
|
legacy.llm?.apiKey,
|
|
22021
|
-
|
|
22022
|
-
process.env.
|
|
22023
|
-
|
|
22151
|
+
// Never borrow another provider's credentials for the Atlas Cloud preset.
|
|
22152
|
+
isAtlasCloud ? process.env.ATLASCLOUD_API_KEY : process.env.OPENAI_API_KEY,
|
|
22153
|
+
isAtlasCloud ? void 0 : process.env.ANTHROPIC_API_KEY,
|
|
22154
|
+
isAtlasCloud ? void 0 : openRouterMemoryLlmApiKey
|
|
22024
22155
|
);
|
|
22025
22156
|
const resolved = {
|
|
22026
22157
|
agent: {
|
|
@@ -22199,7 +22330,8 @@ function getEnvSourceNames() {
|
|
|
22199
22330
|
"MEMORIX_CODEGRAPH_EXTERNAL_CONTEXT",
|
|
22200
22331
|
"MEMORIX_CODEGRAPH_EXTERNAL_COMMAND",
|
|
22201
22332
|
"MEMORIX_CODEGRAPH_EXTERNAL_TIMEOUT_MS",
|
|
22202
|
-
"OPENROUTER_API_KEY"
|
|
22333
|
+
"OPENROUTER_API_KEY",
|
|
22334
|
+
"ATLASCLOUD_API_KEY"
|
|
22203
22335
|
].filter((name) => process.env[name]);
|
|
22204
22336
|
}
|
|
22205
22337
|
function isOpenRouterUrl(value) {
|
|
@@ -22418,12 +22550,45 @@ function textHash(text3) {
|
|
|
22418
22550
|
async function loadDiskCache() {
|
|
22419
22551
|
try {
|
|
22420
22552
|
const raw = await readFile(CACHE_FILE, "utf-8");
|
|
22553
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_CACHE_FILE_BYTES) {
|
|
22554
|
+
console.error("[memorix] FastEmbed cache exceeds the disk load limit; starting with an empty cache");
|
|
22555
|
+
return;
|
|
22556
|
+
}
|
|
22421
22557
|
const entries = JSON.parse(raw);
|
|
22422
|
-
|
|
22423
|
-
|
|
22558
|
+
if (!Array.isArray(entries)) return;
|
|
22559
|
+
for (const entry of entries) {
|
|
22560
|
+
if (!Array.isArray(entry) || typeof entry[0] !== "string" || !isVector(entry[1])) continue;
|
|
22561
|
+
cacheSet(entry[0], entry[1], false);
|
|
22562
|
+
}
|
|
22563
|
+
console.error(`[memorix] Loaded ${cache.size} cached embeddings from disk`);
|
|
22424
22564
|
} catch {
|
|
22425
22565
|
}
|
|
22426
22566
|
}
|
|
22567
|
+
function isVector(value) {
|
|
22568
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
22569
|
+
}
|
|
22570
|
+
function maxCachePayloadBytes() {
|
|
22571
|
+
const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
|
|
22572
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES;
|
|
22573
|
+
}
|
|
22574
|
+
function cacheSet(hash4, value, markDirty = true) {
|
|
22575
|
+
const incomingBytes = value.length * 8;
|
|
22576
|
+
if (incomingBytes > maxCachePayloadBytes()) return;
|
|
22577
|
+
const existing = cache.get(hash4);
|
|
22578
|
+
if (existing) cachePayloadBytes -= existing.length * 8;
|
|
22579
|
+
else {
|
|
22580
|
+
while (cache.size >= MAX_CACHE_SIZE || cachePayloadBytes + incomingBytes > maxCachePayloadBytes()) {
|
|
22581
|
+
const firstKey = cache.keys().next().value;
|
|
22582
|
+
if (firstKey === void 0) break;
|
|
22583
|
+
const old = cache.get(firstKey);
|
|
22584
|
+
cache.delete(firstKey);
|
|
22585
|
+
if (old) cachePayloadBytes -= old.length * 8;
|
|
22586
|
+
}
|
|
22587
|
+
}
|
|
22588
|
+
cache.set(hash4, value);
|
|
22589
|
+
cachePayloadBytes += incomingBytes;
|
|
22590
|
+
if (markDirty) diskCacheDirty = true;
|
|
22591
|
+
}
|
|
22427
22592
|
async function saveDiskCache() {
|
|
22428
22593
|
if (!diskCacheDirty) return;
|
|
22429
22594
|
try {
|
|
@@ -22434,7 +22599,7 @@ async function saveDiskCache() {
|
|
|
22434
22599
|
} catch {
|
|
22435
22600
|
}
|
|
22436
22601
|
}
|
|
22437
|
-
var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, diskCacheDirty, FastEmbedProvider;
|
|
22602
|
+
var CACHE_DIR, CACHE_FILE, cache, MAX_CACHE_SIZE, DEFAULT_CACHE_PAYLOAD_BYTES, MAX_CACHE_FILE_BYTES, cachePayloadBytes, diskCacheDirty, FastEmbedProvider;
|
|
22438
22603
|
var init_fastembed_provider = __esm({
|
|
22439
22604
|
"src/embedding/fastembed-provider.ts"() {
|
|
22440
22605
|
"use strict";
|
|
@@ -22444,6 +22609,9 @@ var init_fastembed_provider = __esm({
|
|
|
22444
22609
|
CACHE_FILE = join3(CACHE_DIR, ".embedding-cache.json");
|
|
22445
22610
|
cache = /* @__PURE__ */ new Map();
|
|
22446
22611
|
MAX_CACHE_SIZE = 5e3;
|
|
22612
|
+
DEFAULT_CACHE_PAYLOAD_BYTES = 64 * 1024 * 1024;
|
|
22613
|
+
MAX_CACHE_FILE_BYTES = 128 * 1024 * 1024;
|
|
22614
|
+
cachePayloadBytes = 0;
|
|
22447
22615
|
diskCacheDirty = false;
|
|
22448
22616
|
FastEmbedProvider = class _FastEmbedProvider {
|
|
22449
22617
|
name = "fastembed-bge-small";
|
|
@@ -22485,7 +22653,7 @@ var init_fastembed_provider = __esm({
|
|
|
22485
22653
|
if (result.length !== this.dimensions) {
|
|
22486
22654
|
throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
|
|
22487
22655
|
}
|
|
22488
|
-
|
|
22656
|
+
cacheSet(hash4, result);
|
|
22489
22657
|
return result;
|
|
22490
22658
|
}
|
|
22491
22659
|
async embedBatch(texts) {
|
|
@@ -22510,7 +22678,7 @@ var init_fastembed_provider = __esm({
|
|
|
22510
22678
|
const originalIdx = uncachedIndices[batchIdx];
|
|
22511
22679
|
const plain = Array.from(vec2);
|
|
22512
22680
|
results[originalIdx] = plain;
|
|
22513
|
-
|
|
22681
|
+
cacheSet(textHash(uncachedTexts[batchIdx]), plain);
|
|
22514
22682
|
batchIdx++;
|
|
22515
22683
|
}
|
|
22516
22684
|
}
|
|
@@ -22518,14 +22686,6 @@ var init_fastembed_provider = __esm({
|
|
|
22518
22686
|
}
|
|
22519
22687
|
return results;
|
|
22520
22688
|
}
|
|
22521
|
-
cacheSet(hash4, value) {
|
|
22522
|
-
if (cache.size >= MAX_CACHE_SIZE) {
|
|
22523
|
-
const firstKey = cache.keys().next().value;
|
|
22524
|
-
if (firstKey !== void 0) cache.delete(firstKey);
|
|
22525
|
-
}
|
|
22526
|
-
cache.set(hash4, value);
|
|
22527
|
-
diskCacheDirty = true;
|
|
22528
|
-
}
|
|
22529
22689
|
};
|
|
22530
22690
|
}
|
|
22531
22691
|
});
|
|
@@ -22535,7 +22695,15 @@ var transformers_provider_exports = {};
|
|
|
22535
22695
|
__export(transformers_provider_exports, {
|
|
22536
22696
|
TransformersProvider: () => TransformersProvider
|
|
22537
22697
|
});
|
|
22538
|
-
|
|
22698
|
+
import { createHash as createHash2 } from "crypto";
|
|
22699
|
+
function cacheKey2(text3) {
|
|
22700
|
+
return createHash2("sha256").update(text3).digest("hex");
|
|
22701
|
+
}
|
|
22702
|
+
function maxCachePayloadBytes2() {
|
|
22703
|
+
const raw = Number.parseInt(process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES ?? "", 10);
|
|
22704
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CACHE_PAYLOAD_BYTES2;
|
|
22705
|
+
}
|
|
22706
|
+
var cache2, MAX_CACHE_SIZE2, DEFAULT_CACHE_PAYLOAD_BYTES2, cachePayloadBytes2, TransformersProvider;
|
|
22539
22707
|
var init_transformers_provider = __esm({
|
|
22540
22708
|
"src/embedding/transformers-provider.ts"() {
|
|
22541
22709
|
"use strict";
|
|
@@ -22543,6 +22711,8 @@ var init_transformers_provider = __esm({
|
|
|
22543
22711
|
init_provider();
|
|
22544
22712
|
cache2 = /* @__PURE__ */ new Map();
|
|
22545
22713
|
MAX_CACHE_SIZE2 = 5e3;
|
|
22714
|
+
DEFAULT_CACHE_PAYLOAD_BYTES2 = 64 * 1024 * 1024;
|
|
22715
|
+
cachePayloadBytes2 = 0;
|
|
22546
22716
|
TransformersProvider = class _TransformersProvider {
|
|
22547
22717
|
name = "transformers-minilm";
|
|
22548
22718
|
dimensions = 384;
|
|
@@ -22577,7 +22747,7 @@ var init_transformers_provider = __esm({
|
|
|
22577
22747
|
return Promise.all(inputs.map((input) => this.embedInput(input, options)));
|
|
22578
22748
|
}
|
|
22579
22749
|
async embed(text3) {
|
|
22580
|
-
const cached2 = cache2.get(text3);
|
|
22750
|
+
const cached2 = cache2.get(cacheKey2(text3));
|
|
22581
22751
|
if (cached2) return cached2;
|
|
22582
22752
|
const output = await this.extractor(text3, {
|
|
22583
22753
|
pooling: "mean",
|
|
@@ -22587,7 +22757,7 @@ var init_transformers_provider = __esm({
|
|
|
22587
22757
|
if (result.length !== this.dimensions) {
|
|
22588
22758
|
throw new Error(`Expected ${this.dimensions}d embedding, got ${result.length}d`);
|
|
22589
22759
|
}
|
|
22590
|
-
this.cacheSet(text3, result);
|
|
22760
|
+
this.cacheSet(cacheKey2(text3), result);
|
|
22591
22761
|
return result;
|
|
22592
22762
|
}
|
|
22593
22763
|
async embedBatch(texts) {
|
|
@@ -22595,7 +22765,7 @@ var init_transformers_provider = __esm({
|
|
|
22595
22765
|
const uncachedIndices = [];
|
|
22596
22766
|
const uncachedTexts = [];
|
|
22597
22767
|
for (let i4 = 0; i4 < texts.length; i4++) {
|
|
22598
|
-
const cached2 = cache2.get(texts[i4]);
|
|
22768
|
+
const cached2 = cache2.get(cacheKey2(texts[i4]));
|
|
22599
22769
|
if (cached2) {
|
|
22600
22770
|
results[i4] = cached2;
|
|
22601
22771
|
} else {
|
|
@@ -22613,17 +22783,27 @@ var init_transformers_provider = __esm({
|
|
|
22613
22783
|
const vec2 = Array.from(allVecs[i4]);
|
|
22614
22784
|
const originalIdx = uncachedIndices[i4];
|
|
22615
22785
|
results[originalIdx] = vec2;
|
|
22616
|
-
this.cacheSet(uncachedTexts[i4], vec2);
|
|
22786
|
+
this.cacheSet(cacheKey2(uncachedTexts[i4]), vec2);
|
|
22617
22787
|
}
|
|
22618
22788
|
}
|
|
22619
22789
|
return results;
|
|
22620
22790
|
}
|
|
22621
22791
|
cacheSet(key, value) {
|
|
22622
|
-
|
|
22623
|
-
|
|
22624
|
-
|
|
22792
|
+
const incomingBytes = value.length * 8;
|
|
22793
|
+
if (incomingBytes > maxCachePayloadBytes2()) return;
|
|
22794
|
+
const existing = cache2.get(key);
|
|
22795
|
+
if (existing) cachePayloadBytes2 -= existing.length * 8;
|
|
22796
|
+
else {
|
|
22797
|
+
while (cache2.size >= MAX_CACHE_SIZE2 || cachePayloadBytes2 + incomingBytes > maxCachePayloadBytes2()) {
|
|
22798
|
+
const firstKey = cache2.keys().next().value;
|
|
22799
|
+
if (firstKey === void 0) break;
|
|
22800
|
+
const old = cache2.get(firstKey);
|
|
22801
|
+
cache2.delete(firstKey);
|
|
22802
|
+
if (old) cachePayloadBytes2 -= old.length * 8;
|
|
22803
|
+
}
|
|
22625
22804
|
}
|
|
22626
22805
|
cache2.set(key, value);
|
|
22806
|
+
cachePayloadBytes2 += incomingBytes;
|
|
22627
22807
|
}
|
|
22628
22808
|
};
|
|
22629
22809
|
}
|
|
@@ -22645,7 +22825,7 @@ __export(api_provider_exports, {
|
|
|
22645
22825
|
saveDiskCacheNow: () => saveDiskCacheNow,
|
|
22646
22826
|
seedApiEmbeddingCacheForTests: () => seedApiEmbeddingCacheForTests
|
|
22647
22827
|
});
|
|
22648
|
-
import { createHash as
|
|
22828
|
+
import { createHash as createHash3 } from "crypto";
|
|
22649
22829
|
import { createReadStream, createWriteStream, constants as fsConstants } from "fs";
|
|
22650
22830
|
import { access, readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, rename, stat, unlink } from "fs/promises";
|
|
22651
22831
|
import { join as join4 } from "path";
|
|
@@ -22672,7 +22852,7 @@ function cacheStatFile() {
|
|
|
22672
22852
|
function payloadBytes(vector) {
|
|
22673
22853
|
return vector.length * BYTES_PER_DIM;
|
|
22674
22854
|
}
|
|
22675
|
-
function
|
|
22855
|
+
function maxCachePayloadBytes3() {
|
|
22676
22856
|
const raw = process.env.MEMORIX_EMBEDDING_CACHE_MAX_BYTES;
|
|
22677
22857
|
if (raw) {
|
|
22678
22858
|
const parsed = Number.parseInt(raw, 10);
|
|
@@ -22682,7 +22862,7 @@ function maxCachePayloadBytes() {
|
|
|
22682
22862
|
}
|
|
22683
22863
|
function maxEntriesForDimensions(dimensions) {
|
|
22684
22864
|
const dim = Math.max(1, Math.floor(dimensions));
|
|
22685
|
-
const byBytes = Math.floor(
|
|
22865
|
+
const byBytes = Math.floor(maxCachePayloadBytes3() / (dim * BYTES_PER_DIM));
|
|
22686
22866
|
return Math.max(1, Math.min(MAX_CACHE_ENTRIES, byBytes));
|
|
22687
22867
|
}
|
|
22688
22868
|
function rememberDimensions(vector) {
|
|
@@ -22695,13 +22875,15 @@ function evictOldest() {
|
|
|
22695
22875
|
if (firstKey === void 0) return;
|
|
22696
22876
|
const old = cache3.get(firstKey);
|
|
22697
22877
|
cache3.delete(firstKey);
|
|
22698
|
-
if (old)
|
|
22878
|
+
if (old) cachePayloadBytes3 -= payloadBytes(old);
|
|
22699
22879
|
}
|
|
22700
22880
|
function evictToFit(incomingBytes, incomingDims) {
|
|
22881
|
+
if (incomingBytes > maxCachePayloadBytes3()) return false;
|
|
22701
22882
|
const limit2 = maxEntriesForDimensions(incomingDims);
|
|
22702
|
-
while (cache3.size > 0 && (cache3.size >= limit2 ||
|
|
22883
|
+
while (cache3.size > 0 && (cache3.size >= limit2 || cachePayloadBytes3 + incomingBytes > maxCachePayloadBytes3())) {
|
|
22703
22884
|
evictOldest();
|
|
22704
22885
|
}
|
|
22886
|
+
return true;
|
|
22705
22887
|
}
|
|
22706
22888
|
function isNumberArray(value) {
|
|
22707
22889
|
return Array.isArray(value) && value.every((entry) => typeof entry === "number");
|
|
@@ -22728,12 +22910,12 @@ function cacheInsert(hash4, value, dirty) {
|
|
|
22728
22910
|
const incoming = payloadBytes(value);
|
|
22729
22911
|
const existing = cache3.get(hash4);
|
|
22730
22912
|
if (existing) {
|
|
22731
|
-
|
|
22913
|
+
cachePayloadBytes3 -= payloadBytes(existing);
|
|
22732
22914
|
cache3.delete(hash4);
|
|
22733
22915
|
}
|
|
22734
|
-
evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS);
|
|
22916
|
+
if (!evictToFit(incoming, value.length || cacheDimensions || DEFAULT_ASSUMED_DIMENSIONS)) return;
|
|
22735
22917
|
cache3.set(hash4, value);
|
|
22736
|
-
|
|
22918
|
+
cachePayloadBytes3 += incoming;
|
|
22737
22919
|
if (dirty) diskCacheDirty2 = true;
|
|
22738
22920
|
}
|
|
22739
22921
|
async function readCacheStat() {
|
|
@@ -22769,7 +22951,7 @@ function cacheNamespace(config2) {
|
|
|
22769
22951
|
].join("|");
|
|
22770
22952
|
}
|
|
22771
22953
|
function textHash2(text3, namespace) {
|
|
22772
|
-
return
|
|
22954
|
+
return createHash3("sha256").update(`${namespace}\0${text3}`).digest("hex").slice(0, 16);
|
|
22773
22955
|
}
|
|
22774
22956
|
function inputIdentity(input, options = {}) {
|
|
22775
22957
|
return JSON.stringify({
|
|
@@ -23111,7 +23293,7 @@ function getApiEmbeddingDiskCacheLoadCountForTests() {
|
|
|
23111
23293
|
}
|
|
23112
23294
|
function resetApiEmbeddingCacheForTests() {
|
|
23113
23295
|
cache3.clear();
|
|
23114
|
-
|
|
23296
|
+
cachePayloadBytes3 = 0;
|
|
23115
23297
|
cacheDimensions = null;
|
|
23116
23298
|
diskCacheEntryCount = 0;
|
|
23117
23299
|
diskCacheLoaded = false;
|
|
@@ -23122,14 +23304,14 @@ function resetApiEmbeddingCacheForTests() {
|
|
|
23122
23304
|
}
|
|
23123
23305
|
function seedApiEmbeddingCacheForTests(entries) {
|
|
23124
23306
|
cache3.clear();
|
|
23125
|
-
|
|
23307
|
+
cachePayloadBytes3 = 0;
|
|
23126
23308
|
cacheDimensions = null;
|
|
23127
23309
|
for (const [key, value] of entries) cacheInsert(key, value, true);
|
|
23128
23310
|
diskCacheLoaded = true;
|
|
23129
23311
|
diskCacheDirty2 = true;
|
|
23130
23312
|
}
|
|
23131
23313
|
function getApiEmbeddingCachePayloadBytesForTests() {
|
|
23132
|
-
return
|
|
23314
|
+
return cachePayloadBytes3;
|
|
23133
23315
|
}
|
|
23134
23316
|
function getApiEmbeddingJsonlLineCountCallsForTests() {
|
|
23135
23317
|
return jsonlLineCountCalls;
|
|
@@ -23303,7 +23485,7 @@ function scheduleDiskSave() {
|
|
|
23303
23485
|
diskSaveTimer = null;
|
|
23304
23486
|
}, DISK_SAVE_DEBOUNCE_MS);
|
|
23305
23487
|
}
|
|
23306
|
-
function
|
|
23488
|
+
function cacheSet2(hash4, value) {
|
|
23307
23489
|
cacheInsert(hash4, value, true);
|
|
23308
23490
|
}
|
|
23309
23491
|
function resolveEnvEmbeddingApiKey() {
|
|
@@ -23423,7 +23605,7 @@ async function waitForRetry(waitMs, signal) {
|
|
|
23423
23605
|
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
23424
23606
|
});
|
|
23425
23607
|
}
|
|
23426
|
-
var cache3, MAX_CACHE_ENTRIES, MAX_CACHE_PAYLOAD_BYTES, BYTES_PER_DIM, DEFAULT_ASSUMED_DIMENSIONS, MAX_MIGRATION_VALUE_BYTES,
|
|
23608
|
+
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;
|
|
23427
23609
|
var init_api_provider = __esm({
|
|
23428
23610
|
"src/embedding/api-provider.ts"() {
|
|
23429
23611
|
"use strict";
|
|
@@ -23435,7 +23617,7 @@ var init_api_provider = __esm({
|
|
|
23435
23617
|
BYTES_PER_DIM = 8;
|
|
23436
23618
|
DEFAULT_ASSUMED_DIMENSIONS = 4096;
|
|
23437
23619
|
MAX_MIGRATION_VALUE_BYTES = 16 * 1024 * 1024;
|
|
23438
|
-
|
|
23620
|
+
cachePayloadBytes3 = 0;
|
|
23439
23621
|
cacheDimensions = null;
|
|
23440
23622
|
diskCacheEntryCount = 0;
|
|
23441
23623
|
diskCacheDirty2 = false;
|
|
@@ -23620,7 +23802,7 @@ var init_api_provider = __esm({
|
|
|
23620
23802
|
} else {
|
|
23621
23803
|
embedding = await apiCall();
|
|
23622
23804
|
}
|
|
23623
|
-
|
|
23805
|
+
cacheSet2(hash4, embedding);
|
|
23624
23806
|
scheduleDiskSave();
|
|
23625
23807
|
return embedding;
|
|
23626
23808
|
}
|
|
@@ -23668,7 +23850,7 @@ var init_api_provider = __esm({
|
|
|
23668
23850
|
for (const item of response.data) {
|
|
23669
23851
|
const originalIdx = chunkIndices[item.index];
|
|
23670
23852
|
results[originalIdx] = item.embedding;
|
|
23671
|
-
|
|
23853
|
+
cacheSet2(textHash2(normalizedTexts[originalIdx], this.cacheKeyNamespace), item.embedding);
|
|
23672
23854
|
}
|
|
23673
23855
|
} catch (error40) {
|
|
23674
23856
|
const providerLimit = parseBatchLimit(error40);
|
|
@@ -23746,7 +23928,7 @@ var init_api_provider = __esm({
|
|
|
23746
23928
|
throw new Error(`Expected ${this.dimensions}d, got ${embedding.length}d; dimension mismatch`);
|
|
23747
23929
|
}
|
|
23748
23930
|
this.trackUsage(response);
|
|
23749
|
-
|
|
23931
|
+
cacheSet2(hash4, embedding);
|
|
23750
23932
|
scheduleDiskSave();
|
|
23751
23933
|
return embedding;
|
|
23752
23934
|
}
|
|
@@ -25129,6 +25311,7 @@ var init_provider2 = __esm({
|
|
|
25129
25311
|
openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4.1-nano" },
|
|
25130
25312
|
anthropic: { baseUrl: "https://api.anthropic.com/v1", model: "claude-3-5-haiku-latest" },
|
|
25131
25313
|
openrouter: { baseUrl: "https://openrouter.ai/api/v1", model: "openai/gpt-4.1-nano" },
|
|
25314
|
+
atlascloud: { baseUrl: "https://api.atlascloud.ai/v1", model: "deepseek-ai/deepseek-v3.2" },
|
|
25132
25315
|
custom: { baseUrl: "http://localhost:11434/v1", model: "llama3" }
|
|
25133
25316
|
};
|
|
25134
25317
|
currentConfig = null;
|
|
@@ -25892,7 +26075,7 @@ var init_obs_store = __esm({
|
|
|
25892
26075
|
});
|
|
25893
26076
|
|
|
25894
26077
|
// src/search/semantic-index.ts
|
|
25895
|
-
import { createHash as
|
|
26078
|
+
import { createHash as createHash4 } from "crypto";
|
|
25896
26079
|
import { createRequire as createRequire3 } from "module";
|
|
25897
26080
|
import path10 from "path";
|
|
25898
26081
|
function createSemanticIndexProfile(provider2) {
|
|
@@ -25906,7 +26089,7 @@ function stateKey(dataDir, profile) {
|
|
|
25906
26089
|
return `${path10.resolve(dataDir)}::${profile.key}`;
|
|
25907
26090
|
}
|
|
25908
26091
|
function tableName(profile) {
|
|
25909
|
-
const digest2 =
|
|
26092
|
+
const digest2 = createHash4("sha256").update(profile.key).digest("hex").slice(0, 20);
|
|
25910
26093
|
return `${TABLE_PREFIX}${digest2}`;
|
|
25911
26094
|
}
|
|
25912
26095
|
function indexRoot(dataDir) {
|
|
@@ -26205,6 +26388,7 @@ var init_secret_filter = __esm({
|
|
|
26205
26388
|
// src/runtime/maintenance-jobs.ts
|
|
26206
26389
|
var maintenance_jobs_exports = {};
|
|
26207
26390
|
__export(maintenance_jobs_exports, {
|
|
26391
|
+
DEFAULT_FAILED_HISTORY_RETENTION_MS: () => DEFAULT_FAILED_HISTORY_RETENTION_MS,
|
|
26208
26392
|
MAINTENANCE_JOB_KINDS: () => MAINTENANCE_JOB_KINDS,
|
|
26209
26393
|
MaintenanceJobStore: () => MaintenanceJobStore,
|
|
26210
26394
|
MaintenanceJobWorker: () => MaintenanceJobWorker,
|
|
@@ -26254,7 +26438,7 @@ function normalizedKinds(kinds) {
|
|
|
26254
26438
|
function getMaintenanceRetryDelayMs(attempts) {
|
|
26255
26439
|
return Math.min(6e4, 1e3 * 2 ** Math.max(0, attempts - 1));
|
|
26256
26440
|
}
|
|
26257
|
-
var MAINTENANCE_JOB_KINDS, DEFAULT_MAX_ATTEMPTS, DEFAULT_LEASE_MS, DEFAULT_POLL_INTERVAL_MS, MAX_ERROR_LENGTH, DEFAULT_COMPLETED_HISTORY_RETENTION_MS, MaintenanceJobStore, MaintenanceJobWorker;
|
|
26441
|
+
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;
|
|
26258
26442
|
var init_maintenance_jobs = __esm({
|
|
26259
26443
|
"src/runtime/maintenance-jobs.ts"() {
|
|
26260
26444
|
"use strict";
|
|
@@ -26281,6 +26465,7 @@ var init_maintenance_jobs = __esm({
|
|
|
26281
26465
|
DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
26282
26466
|
MAX_ERROR_LENGTH = 1e3;
|
|
26283
26467
|
DEFAULT_COMPLETED_HISTORY_RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
26468
|
+
DEFAULT_FAILED_HISTORY_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
26284
26469
|
MaintenanceJobStore = class {
|
|
26285
26470
|
db;
|
|
26286
26471
|
constructor(dataDir) {
|
|
@@ -26407,18 +26592,21 @@ var init_maintenance_jobs = __esm({
|
|
|
26407
26592
|
}
|
|
26408
26593
|
/**
|
|
26409
26594
|
* Completed jobs are useful recent operator history, not permanent data.
|
|
26410
|
-
* Failed
|
|
26411
|
-
*
|
|
26595
|
+
* Failed diagnostics are retained longer, but are still bounded so a
|
|
26596
|
+
* permanently failing job kind cannot grow the database forever.
|
|
26412
26597
|
*/
|
|
26413
26598
|
pruneCompletedHistory(options = {}) {
|
|
26414
26599
|
const now3 = options.now ?? Date.now();
|
|
26415
26600
|
const maxAgeMs = Number.isFinite(options.maxAgeMs) ? Math.max(0, Math.floor(options.maxAgeMs)) : DEFAULT_COMPLETED_HISTORY_RETENTION_MS;
|
|
26601
|
+
const failedMaxAgeMs = Number.isFinite(options.failedMaxAgeMs) ? Math.max(0, Math.floor(options.failedMaxAgeMs)) : DEFAULT_FAILED_HISTORY_RETENTION_MS;
|
|
26416
26602
|
const result = this.db.prepare(`
|
|
26417
26603
|
DELETE FROM maintenance_jobs
|
|
26418
|
-
WHERE status = 'completed'
|
|
26604
|
+
WHERE (status = 'completed'
|
|
26419
26605
|
AND completed_at IS NOT NULL
|
|
26420
|
-
AND completed_at < ?
|
|
26421
|
-
|
|
26606
|
+
AND completed_at < ?)
|
|
26607
|
+
OR (status = 'failed'
|
|
26608
|
+
AND updated_at < ?)
|
|
26609
|
+
`).run(now3 - maxAgeMs, now3 - failedMaxAgeMs);
|
|
26422
26610
|
return Number(result.changes ?? 0);
|
|
26423
26611
|
}
|
|
26424
26612
|
claimNext(options) {
|
|
@@ -26777,7 +26965,7 @@ __export(evidence_store_exports, {
|
|
|
26777
26965
|
EvidenceCardStore: () => EvidenceCardStore,
|
|
26778
26966
|
buildEvidenceCardId: () => buildEvidenceCardId
|
|
26779
26967
|
});
|
|
26780
|
-
import { createHash as
|
|
26968
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
26781
26969
|
function safeJson(value, fallback) {
|
|
26782
26970
|
if (typeof value !== "string" || value.length === 0) return fallback;
|
|
26783
26971
|
try {
|
|
@@ -26811,7 +26999,7 @@ function rowToCard(row) {
|
|
|
26811
26999
|
};
|
|
26812
27000
|
}
|
|
26813
27001
|
function observationHash(observation) {
|
|
26814
|
-
return
|
|
27002
|
+
return createHash5("sha256").update(JSON.stringify({
|
|
26815
27003
|
projectId: observation.projectId,
|
|
26816
27004
|
id: observation.id,
|
|
26817
27005
|
title: observation.title,
|
|
@@ -27408,13 +27596,13 @@ __export(workspace_exports, {
|
|
|
27408
27596
|
loadKnowledgeWorkspace: () => loadKnowledgeWorkspace,
|
|
27409
27597
|
resolveKnowledgeWorkspaceFile: () => resolveKnowledgeWorkspaceFile
|
|
27410
27598
|
});
|
|
27411
|
-
import { createHash as
|
|
27599
|
+
import { createHash as createHash6 } from "crypto";
|
|
27412
27600
|
import { execFile } from "child_process";
|
|
27413
27601
|
import { promises as fs7 } from "fs";
|
|
27414
27602
|
import path12 from "path";
|
|
27415
27603
|
import { promisify } from "util";
|
|
27416
27604
|
function hash(value) {
|
|
27417
|
-
return
|
|
27605
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
27418
27606
|
}
|
|
27419
27607
|
function workspaceId(projectId, mode) {
|
|
27420
27608
|
return "workspace:" + hash(projectId + ":" + mode).slice(0, 24);
|
|
@@ -28567,14 +28755,14 @@ __export(asset_store_exports, {
|
|
|
28567
28755
|
readMediaAsset: () => readMediaAsset,
|
|
28568
28756
|
removeMediaAsset: () => removeMediaAsset
|
|
28569
28757
|
});
|
|
28570
|
-
import { createHash as
|
|
28758
|
+
import { createHash as createHash7, randomUUID as randomUUID6 } from "crypto";
|
|
28571
28759
|
import { copyFile, lstat, mkdir as mkdir3, open, readFile as readFile3, readdir, rename as rename2, rm, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
28572
28760
|
import path13 from "path";
|
|
28573
28761
|
function mediaRoot(dataDir) {
|
|
28574
28762
|
return path13.resolve(dataDir, "media");
|
|
28575
28763
|
}
|
|
28576
28764
|
function projectStorageKey(projectId) {
|
|
28577
|
-
return
|
|
28765
|
+
return createHash7("sha256").update(projectId).digest("hex").slice(0, 16);
|
|
28578
28766
|
}
|
|
28579
28767
|
function validateUnderRoot(root, candidate2) {
|
|
28580
28768
|
const resolved = path13.resolve(candidate2);
|
|
@@ -28634,7 +28822,7 @@ async function readHeader(filePath, bytes = 32) {
|
|
|
28634
28822
|
}
|
|
28635
28823
|
async function sha256File(filePath) {
|
|
28636
28824
|
const bytes = await readFile3(filePath);
|
|
28637
|
-
return
|
|
28825
|
+
return createHash7("sha256").update(bytes).digest("hex");
|
|
28638
28826
|
}
|
|
28639
28827
|
function destinationFor(root, projectId, sha2562, extension3) {
|
|
28640
28828
|
const relative5 = path13.join("assets", projectStorageKey(projectId), sha2562.slice(0, 2), `${sha2562}${extension3}`);
|
|
@@ -28740,7 +28928,7 @@ async function importMediaBuffer(input) {
|
|
|
28740
28928
|
if (input.bytes.length === 0) throw new Error("Media import rejects empty payloads");
|
|
28741
28929
|
if (input.bytes.length > maxBytes) throw new Error(`Media payload is too large (${input.bytes.length} bytes; limit ${maxBytes})`);
|
|
28742
28930
|
const detected = detectMedia(input.bytes.subarray(0, 32));
|
|
28743
|
-
const sha2562 =
|
|
28931
|
+
const sha2562 = createHash7("sha256").update(input.bytes).digest("hex");
|
|
28744
28932
|
return importKnownMedia({
|
|
28745
28933
|
dataDir: input.dataDir,
|
|
28746
28934
|
projectId: input.projectId,
|
|
@@ -28758,7 +28946,7 @@ async function readMediaAsset(dataDir, asset, maxBytes = DEFAULT_MAX_MEDIA_BYTES
|
|
|
28758
28946
|
if (asset.byteSize > maxBytes) throw new Error(`Media asset exceeds read limit (${asset.byteSize} bytes; limit ${maxBytes})`);
|
|
28759
28947
|
const bytes = await readFile3(getMediaAssetPath(dataDir, asset));
|
|
28760
28948
|
if (bytes.length !== asset.byteSize) throw new Error(`Media asset size no longer matches metadata: ${asset.id}`);
|
|
28761
|
-
const sha2562 =
|
|
28949
|
+
const sha2562 = createHash7("sha256").update(bytes).digest("hex");
|
|
28762
28950
|
if (sha2562 !== asset.sha256) throw new Error(`Media asset hash no longer matches metadata: ${asset.id}`);
|
|
28763
28951
|
return bytes;
|
|
28764
28952
|
}
|
|
@@ -81628,7 +81816,7 @@ var require_gaxios = __commonJS({
|
|
|
81628
81816
|
var retry_js_1 = require_retry3();
|
|
81629
81817
|
var stream_1 = __require("stream");
|
|
81630
81818
|
var interceptor_js_1 = require_interceptor();
|
|
81631
|
-
var
|
|
81819
|
+
var randomUUID23 = async () => globalThis.crypto?.randomUUID() || (await import("crypto")).randomUUID();
|
|
81632
81820
|
var HTTP_STATUS_NO_CONTENT = 204;
|
|
81633
81821
|
var Gaxios = class {
|
|
81634
81822
|
agentCache = /* @__PURE__ */ new Map();
|
|
@@ -81901,7 +82089,7 @@ var require_gaxios = __commonJS({
|
|
|
81901
82089
|
*/
|
|
81902
82090
|
["Blob", "File", "FormData"].includes(opts.data?.constructor?.name || "");
|
|
81903
82091
|
if (opts.multipart?.length) {
|
|
81904
|
-
const boundary = await
|
|
82092
|
+
const boundary = await randomUUID23();
|
|
81905
82093
|
preparedHeaders.set("content-type", `multipart/related; boundary=${boundary}`);
|
|
81906
82094
|
opts.body = stream_1.Readable.from(this.getMultipartRequest(opts.multipart, boundary));
|
|
81907
82095
|
} else if (shouldDirectlyPassData) {
|
|
@@ -87573,16 +87761,16 @@ var require_jwtaccess = __commonJS({
|
|
|
87573
87761
|
* @returns A string that returns the cached key.
|
|
87574
87762
|
*/
|
|
87575
87763
|
getCachedKey(url2, scopes) {
|
|
87576
|
-
let
|
|
87764
|
+
let cacheKey4 = url2;
|
|
87577
87765
|
if (scopes && Array.isArray(scopes) && scopes.length) {
|
|
87578
|
-
|
|
87766
|
+
cacheKey4 = url2 ? `${url2}_${scopes.join("_")}` : `${scopes.join("_")}`;
|
|
87579
87767
|
} else if (typeof scopes === "string") {
|
|
87580
|
-
|
|
87768
|
+
cacheKey4 = url2 ? `${url2}_${scopes}` : scopes;
|
|
87581
87769
|
}
|
|
87582
|
-
if (!
|
|
87770
|
+
if (!cacheKey4) {
|
|
87583
87771
|
throw Error("Scopes or url must be provided");
|
|
87584
87772
|
}
|
|
87585
|
-
return
|
|
87773
|
+
return cacheKey4;
|
|
87586
87774
|
}
|
|
87587
87775
|
/**
|
|
87588
87776
|
* Get a non-expired access token, after refreshing if necessary.
|
|
@@ -94128,7 +94316,7 @@ var require_websocket = __commonJS({
|
|
|
94128
94316
|
var http3 = __require("http");
|
|
94129
94317
|
var net = __require("net");
|
|
94130
94318
|
var tls = __require("tls");
|
|
94131
|
-
var { randomBytes: randomBytes3, createHash:
|
|
94319
|
+
var { randomBytes: randomBytes3, createHash: createHash29 } = __require("crypto");
|
|
94132
94320
|
var { Duplex, Readable: Readable3 } = __require("stream");
|
|
94133
94321
|
var { URL: URL2 } = __require("url");
|
|
94134
94322
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -94796,7 +94984,7 @@ var require_websocket = __commonJS({
|
|
|
94796
94984
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
94797
94985
|
return;
|
|
94798
94986
|
}
|
|
94799
|
-
const digest2 =
|
|
94987
|
+
const digest2 = createHash29("sha1").update(key + GUID).digest("base64");
|
|
94800
94988
|
if (res.headers["sec-websocket-accept"] !== digest2) {
|
|
94801
94989
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
94802
94990
|
return;
|
|
@@ -95168,7 +95356,7 @@ var require_websocket_server = __commonJS({
|
|
|
95168
95356
|
var EventEmitter2 = __require("events");
|
|
95169
95357
|
var http3 = __require("http");
|
|
95170
95358
|
var { Duplex } = __require("stream");
|
|
95171
|
-
var { createHash:
|
|
95359
|
+
var { createHash: createHash29 } = __require("crypto");
|
|
95172
95360
|
var extension3 = require_extension();
|
|
95173
95361
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
95174
95362
|
var subprotocol2 = require_subprotocol();
|
|
@@ -95475,7 +95663,7 @@ var require_websocket_server = __commonJS({
|
|
|
95475
95663
|
);
|
|
95476
95664
|
}
|
|
95477
95665
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
95478
|
-
const digest2 =
|
|
95666
|
+
const digest2 = createHash29("sha1").update(key + GUID).digest("base64");
|
|
95479
95667
|
const headers = [
|
|
95480
95668
|
"HTTP/1.1 101 Switching Protocols",
|
|
95481
95669
|
"Upgrade: websocket",
|
|
@@ -175397,9 +175585,9 @@ var init_union_priority_sort = __esm({
|
|
|
175397
175585
|
// node_modules/typebox/build/value/clean/from_union.mjs
|
|
175398
175586
|
function FromUnion9(context, type, value) {
|
|
175399
175587
|
for (const schema of UnionPrioritySort(type.anyOf)) {
|
|
175400
|
-
const
|
|
175401
|
-
if (Check2(context, schema,
|
|
175402
|
-
return
|
|
175588
|
+
const clean2 = FromType19(context, schema, Clone2(value));
|
|
175589
|
+
if (Check2(context, schema, clean2))
|
|
175590
|
+
return clean2;
|
|
175403
175591
|
}
|
|
175404
175592
|
return value;
|
|
175405
175593
|
}
|
|
@@ -180041,9 +180229,9 @@ var init_long_term = __esm({
|
|
|
180041
180229
|
});
|
|
180042
180230
|
|
|
180043
180231
|
// src/codegraph/ids.ts
|
|
180044
|
-
import { createHash as
|
|
180232
|
+
import { createHash as createHash8 } from "crypto";
|
|
180045
180233
|
function digest(input) {
|
|
180046
|
-
return
|
|
180234
|
+
return createHash8("sha256").update(input).digest("hex").slice(0, 16);
|
|
180047
180235
|
}
|
|
180048
180236
|
function normalizeCodePath(path50) {
|
|
180049
180237
|
return path50.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/+/g, "/");
|
|
@@ -181091,7 +181279,7 @@ var init_store = __esm({
|
|
|
181091
181279
|
|
|
181092
181280
|
// src/codegraph/code-state.ts
|
|
181093
181281
|
import { execFile as execFile2 } from "child_process";
|
|
181094
|
-
import { createHash as
|
|
181282
|
+
import { createHash as createHash9 } from "crypto";
|
|
181095
181283
|
import { promisify as promisify3 } from "util";
|
|
181096
181284
|
async function runGit(rootPath, args) {
|
|
181097
181285
|
try {
|
|
@@ -181120,7 +181308,7 @@ function changedPathCount(status) {
|
|
|
181120
181308
|
return count3;
|
|
181121
181309
|
}
|
|
181122
181310
|
function fingerprint(baseRevision, status) {
|
|
181123
|
-
return
|
|
181311
|
+
return createHash9("sha256").update(baseRevision ?? "").update("\0").update(status).digest("hex");
|
|
181124
181312
|
}
|
|
181125
181313
|
async function collectCodeStateSnapshot(input) {
|
|
181126
181314
|
const [revision, status] = await Promise.all([
|
|
@@ -181214,12 +181402,12 @@ var init_exclude3 = __esm({
|
|
|
181214
181402
|
});
|
|
181215
181403
|
|
|
181216
181404
|
// src/codegraph/semantic-provider.ts
|
|
181217
|
-
import { createHash as
|
|
181405
|
+
import { createHash as createHash10 } from "crypto";
|
|
181218
181406
|
import "fs";
|
|
181219
181407
|
import { resolve as resolve3 } from "path";
|
|
181220
181408
|
import ts from "typescript";
|
|
181221
181409
|
function hashText(value) {
|
|
181222
|
-
return
|
|
181410
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
181223
181411
|
}
|
|
181224
181412
|
function canonicalPath(value) {
|
|
181225
181413
|
const normalized = resolve3(value).replace(/\\/g, "/");
|
|
@@ -181564,11 +181752,11 @@ __export(lite_provider_exports, {
|
|
|
181564
181752
|
indexProjectLite: () => indexProjectLite,
|
|
181565
181753
|
refreshProjectLite: () => refreshProjectLite
|
|
181566
181754
|
});
|
|
181567
|
-
import { createHash as
|
|
181755
|
+
import { createHash as createHash11 } from "crypto";
|
|
181568
181756
|
import { existsSync as existsSync14, readdirSync as readdirSync2, readFileSync as readFileSync9, statSync as statSync4 } from "fs";
|
|
181569
181757
|
import { join as join5, relative as relative2 } from "path";
|
|
181570
181758
|
function hashText2(text3) {
|
|
181571
|
-
return
|
|
181759
|
+
return createHash11("sha256").update(text3).digest("hex");
|
|
181572
181760
|
}
|
|
181573
181761
|
function extension2(path50) {
|
|
181574
181762
|
const index = path50.lastIndexOf(".");
|
|
@@ -182201,7 +182389,7 @@ var claim_store_exports = {};
|
|
|
182201
182389
|
__export(claim_store_exports, {
|
|
182202
182390
|
ClaimStore: () => ClaimStore
|
|
182203
182391
|
});
|
|
182204
|
-
import { createHash as
|
|
182392
|
+
import { createHash as createHash12, randomUUID as randomUUID12 } from "crypto";
|
|
182205
182393
|
function optionalText3(value) {
|
|
182206
182394
|
return typeof value === "string" && value ? value : void 0;
|
|
182207
182395
|
}
|
|
@@ -182254,7 +182442,7 @@ function rowToEvent3(row) {
|
|
|
182254
182442
|
};
|
|
182255
182443
|
}
|
|
182256
182444
|
function evidenceKey(input) {
|
|
182257
|
-
return
|
|
182445
|
+
return createHash12("sha256").update(JSON.stringify([
|
|
182258
182446
|
input.evidenceKind,
|
|
182259
182447
|
input.evidenceId,
|
|
182260
182448
|
input.relation,
|
|
@@ -182452,7 +182640,7 @@ __export(claims_exports, {
|
|
|
182452
182640
|
supersedeClaim: () => supersedeClaim,
|
|
182453
182641
|
writeClaim: () => writeClaim
|
|
182454
182642
|
});
|
|
182455
|
-
import { createHash as
|
|
182643
|
+
import { createHash as createHash13, randomUUID as randomUUID13 } from "crypto";
|
|
182456
182644
|
function compactText2(value, field) {
|
|
182457
182645
|
if (typeof value !== "string") throw new Error("Claim " + field + " must be text");
|
|
182458
182646
|
const sanitized = sanitizeCredentials(value).trim().replace(/\s+/g, " ");
|
|
@@ -182464,7 +182652,7 @@ function normalizedTerm(value) {
|
|
|
182464
182652
|
return compactText2(value, "identity").toLocaleLowerCase("en-US");
|
|
182465
182653
|
}
|
|
182466
182654
|
function hashIdentity(parts) {
|
|
182467
|
-
return
|
|
182655
|
+
return createHash13("sha256").update(parts.join(String.fromCharCode(31))).digest("hex");
|
|
182468
182656
|
}
|
|
182469
182657
|
function buildClaimIdentity(input) {
|
|
182470
182658
|
const subject = normalizedTerm(input.subject);
|
|
@@ -182705,7 +182893,7 @@ function supersedeClaim(store2, input) {
|
|
|
182705
182893
|
});
|
|
182706
182894
|
}
|
|
182707
182895
|
function hashObservation(observation) {
|
|
182708
|
-
return
|
|
182896
|
+
return createHash13("sha256").update(JSON.stringify([
|
|
182709
182897
|
observation.title,
|
|
182710
182898
|
observation.narrative,
|
|
182711
182899
|
observation.facts,
|
|
@@ -182949,11 +183137,11 @@ var init_frontmatter = __esm({
|
|
|
182949
183137
|
});
|
|
182950
183138
|
|
|
182951
183139
|
// src/knowledge/markdown.ts
|
|
182952
|
-
import { createHash as
|
|
183140
|
+
import { createHash as createHash14 } from "crypto";
|
|
182953
183141
|
import { promises as fs11 } from "fs";
|
|
182954
183142
|
import path20 from "path";
|
|
182955
183143
|
function hashContent(content) {
|
|
182956
|
-
return
|
|
183144
|
+
return createHash14("sha256").update(content).digest("hex");
|
|
182957
183145
|
}
|
|
182958
183146
|
function stringField(data, key) {
|
|
182959
183147
|
const value = data[key];
|
|
@@ -183079,11 +183267,11 @@ __export(wiki_exports, {
|
|
|
183079
183267
|
lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
|
|
183080
183268
|
readKnowledgePage: () => readKnowledgePage
|
|
183081
183269
|
});
|
|
183082
|
-
import { createHash as
|
|
183270
|
+
import { createHash as createHash15 } from "crypto";
|
|
183083
183271
|
import { promises as fs12 } from "fs";
|
|
183084
183272
|
import path21 from "path";
|
|
183085
183273
|
function hash2(value) {
|
|
183086
|
-
return
|
|
183274
|
+
return createHash15("sha256").update(value).digest("hex");
|
|
183087
183275
|
}
|
|
183088
183276
|
function now() {
|
|
183089
183277
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -184302,11 +184490,11 @@ __export(workflows_exports, {
|
|
|
184302
184490
|
syncCanonicalWorkflows: () => syncCanonicalWorkflows,
|
|
184303
184491
|
writeCanonicalWorkflow: () => writeCanonicalWorkflow
|
|
184304
184492
|
});
|
|
184305
|
-
import { createHash as
|
|
184493
|
+
import { createHash as createHash16 } from "crypto";
|
|
184306
184494
|
import { promises as fs13 } from "fs";
|
|
184307
184495
|
import path22 from "path";
|
|
184308
184496
|
function hash3(value) {
|
|
184309
|
-
return
|
|
184497
|
+
return createHash16("sha256").update(value).digest("hex");
|
|
184310
184498
|
}
|
|
184311
184499
|
function now2() {
|
|
184312
184500
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -188043,6 +188231,7 @@ async function searchObservations(options) {
|
|
|
188043
188231
|
};
|
|
188044
188232
|
let queryVector = null;
|
|
188045
188233
|
let embeddingProviderForSearch = null;
|
|
188234
|
+
let embeddingUnavailable = false;
|
|
188046
188235
|
if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
|
|
188047
188236
|
try {
|
|
188048
188237
|
const provider2 = await getEmbeddingProvider();
|
|
@@ -188050,6 +188239,7 @@ async function searchObservations(options) {
|
|
|
188050
188239
|
embeddingProviderForSearch = provider2;
|
|
188051
188240
|
const activeVectorDimensions = getVectorDimensions();
|
|
188052
188241
|
if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
|
|
188242
|
+
embeddingUnavailable = true;
|
|
188053
188243
|
rememberSearchMode(
|
|
188054
188244
|
modeKey,
|
|
188055
188245
|
`fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
|
|
@@ -188089,8 +188279,11 @@ async function searchObservations(options) {
|
|
|
188089
188279
|
hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
|
|
188090
188280
|
};
|
|
188091
188281
|
}
|
|
188282
|
+
} else {
|
|
188283
|
+
embeddingUnavailable = true;
|
|
188092
188284
|
}
|
|
188093
188285
|
} catch (error40) {
|
|
188286
|
+
embeddingUnavailable = true;
|
|
188094
188287
|
rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
|
|
188095
188288
|
console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
|
|
188096
188289
|
}
|
|
@@ -188098,7 +188291,7 @@ async function searchObservations(options) {
|
|
|
188098
188291
|
mark("preSearch");
|
|
188099
188292
|
let results;
|
|
188100
188293
|
const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
|
|
188101
|
-
const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
|
|
188294
|
+
const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
|
|
188102
188295
|
let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
|
|
188103
188296
|
const durableStore = getOptionalObservationStore();
|
|
188104
188297
|
if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count2(database) === 0) {
|
|
@@ -189337,6 +189530,8 @@ var team_store_exports = {};
|
|
|
189337
189530
|
__export(team_store_exports, {
|
|
189338
189531
|
AGENT_TYPE_ROLE_MAP: () => AGENT_TYPE_ROLE_MAP,
|
|
189339
189532
|
DEFAULT_ROLES: () => DEFAULT_ROLES,
|
|
189533
|
+
DEFAULT_TEAM_QUERY_LIMIT: () => DEFAULT_TEAM_QUERY_LIMIT,
|
|
189534
|
+
MAX_TEAM_QUERY_LIMIT: () => MAX_TEAM_QUERY_LIMIT,
|
|
189340
189535
|
TeamStore: () => TeamStore,
|
|
189341
189536
|
getTeamStore: () => getTeamStore,
|
|
189342
189537
|
initTeamStore: () => initTeamStore,
|
|
@@ -189346,6 +189541,10 @@ __export(team_store_exports, {
|
|
|
189346
189541
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
189347
189542
|
import path25 from "path";
|
|
189348
189543
|
import fs15 from "fs";
|
|
189544
|
+
function clampTeamQueryLimit(limit2) {
|
|
189545
|
+
if (!Number.isFinite(limit2) || limit2 == null) return DEFAULT_TEAM_QUERY_LIMIT;
|
|
189546
|
+
return Math.min(MAX_TEAM_QUERY_LIMIT, Math.max(1, Math.floor(limit2)));
|
|
189547
|
+
}
|
|
189349
189548
|
function getTeamStore() {
|
|
189350
189549
|
if (!_teamStore) {
|
|
189351
189550
|
throw new Error("[memorix] TeamStore not initialized \u2014 call initTeamStore() first");
|
|
@@ -189369,12 +189568,14 @@ function resetTeamStore() {
|
|
|
189369
189568
|
_teamStore = null;
|
|
189370
189569
|
_teamStoreDataDir = null;
|
|
189371
189570
|
}
|
|
189372
|
-
var DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
|
|
189571
|
+
var DEFAULT_TEAM_QUERY_LIMIT, MAX_TEAM_QUERY_LIMIT, DEFAULT_ROLES, AGENT_TYPE_ROLE_MAP, TeamStore, _teamStore, _teamStoreDataDir;
|
|
189373
189572
|
var init_team_store = __esm({
|
|
189374
189573
|
"src/team/team-store.ts"() {
|
|
189375
189574
|
"use strict";
|
|
189376
189575
|
init_esm_shims();
|
|
189377
189576
|
init_sqlite_db();
|
|
189577
|
+
DEFAULT_TEAM_QUERY_LIMIT = 100;
|
|
189578
|
+
MAX_TEAM_QUERY_LIMIT = 500;
|
|
189378
189579
|
DEFAULT_ROLES = [
|
|
189379
189580
|
{ roleId: "planner", label: "Planner", description: "Breaks down requirements, creates tasks, defines dependencies", preferredAgentTypes: ["claude-code", "cursor", "codex"], maxConcurrent: 2 },
|
|
189380
189581
|
{ roleId: "researcher", label: "Researcher", description: "Investigates codebase, gathers context, answers questions", preferredAgentTypes: ["claude-code", "cursor", "gemini-cli"], maxConcurrent: 2 },
|
|
@@ -189684,32 +189885,37 @@ var init_team_store = __esm({
|
|
|
189684
189885
|
return this.stmtAgentFindByInstance.get(projectId, agentType, instanceId);
|
|
189685
189886
|
}
|
|
189686
189887
|
listAgents(projectId, filter) {
|
|
189687
|
-
const
|
|
189888
|
+
const limit2 = clampTeamQueryLimit(filter?.limit);
|
|
189688
189889
|
if (filter?.status) {
|
|
189689
|
-
return
|
|
189890
|
+
return this.db.prepare(
|
|
189891
|
+
`SELECT * FROM team_agents WHERE project_id = ? AND status = ? ORDER BY joined_at DESC LIMIT ?`
|
|
189892
|
+
).all(projectId, filter.status, limit2);
|
|
189690
189893
|
}
|
|
189691
|
-
return
|
|
189894
|
+
return this.db.prepare(
|
|
189895
|
+
`SELECT * FROM team_agents WHERE project_id = ? ORDER BY joined_at DESC LIMIT ?`
|
|
189896
|
+
).all(projectId, limit2);
|
|
189692
189897
|
}
|
|
189693
189898
|
/** List agents across all projects (for global scope) */
|
|
189694
|
-
listAllAgents() {
|
|
189899
|
+
listAllAgents(limit2) {
|
|
189695
189900
|
if (!this.db) return [];
|
|
189696
|
-
return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC").all();
|
|
189901
|
+
return this.db.prepare("SELECT * FROM team_agents ORDER BY last_heartbeat DESC LIMIT ?").all(clampTeamQueryLimit(limit2));
|
|
189697
189902
|
}
|
|
189698
189903
|
/** List locks across all projects (for global scope) */
|
|
189699
|
-
listAllLocks() {
|
|
189904
|
+
listAllLocks(limit2) {
|
|
189700
189905
|
if (!this.db) return [];
|
|
189701
189906
|
this.db.prepare("DELETE FROM team_locks WHERE expires_at <= ?").run(Date.now());
|
|
189702
|
-
return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC").all(Date.now());
|
|
189907
|
+
return this.db.prepare("SELECT * FROM team_locks WHERE expires_at > ? ORDER BY locked_at DESC LIMIT ?").all(Date.now(), clampTeamQueryLimit(limit2));
|
|
189703
189908
|
}
|
|
189704
189909
|
/** List tasks across all projects (for global scope) */
|
|
189705
189910
|
listAllTasks(filter) {
|
|
189706
189911
|
if (!this.db) return [];
|
|
189912
|
+
const limit2 = clampTeamQueryLimit(filter?.limit);
|
|
189707
189913
|
if (filter?.available) {
|
|
189708
189914
|
return this.db.prepare(
|
|
189709
|
-
`SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC
|
|
189710
|
-
).all();
|
|
189915
|
+
`SELECT t.* FROM team_tasks t WHERE t.status = 'pending' AND t.assignee_agent_id IS NULL ORDER BY t.created_at DESC LIMIT ?`
|
|
189916
|
+
).all(limit2);
|
|
189711
189917
|
}
|
|
189712
|
-
return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC").all();
|
|
189918
|
+
return this.db.prepare("SELECT * FROM team_tasks ORDER BY created_at DESC LIMIT ?").all(limit2);
|
|
189713
189919
|
}
|
|
189714
189920
|
heartbeat(agentId) {
|
|
189715
189921
|
const info2 = this.stmtAgentUpdateHeartbeat.run(Date.now(), agentId);
|
|
@@ -189744,7 +189950,10 @@ var init_team_store = __esm({
|
|
|
189744
189950
|
return staleIds;
|
|
189745
189951
|
}
|
|
189746
189952
|
getActiveCount(projectId) {
|
|
189747
|
-
|
|
189953
|
+
const row = this.db.prepare(
|
|
189954
|
+
`SELECT COUNT(*) AS count FROM team_agents WHERE project_id = ? AND status = 'active'`
|
|
189955
|
+
).get(projectId);
|
|
189956
|
+
return Number(row?.count ?? 0);
|
|
189748
189957
|
}
|
|
189749
189958
|
// ═══════════════════════════════════════════════════════════════════
|
|
189750
189959
|
// Message Operations
|
|
@@ -189779,8 +189988,14 @@ var init_team_store = __esm({
|
|
|
189779
189988
|
this.stmtMsgInsert.run(row);
|
|
189780
189989
|
return row;
|
|
189781
189990
|
}
|
|
189782
|
-
getInbox(projectId, agentId) {
|
|
189783
|
-
return this.
|
|
189991
|
+
getInbox(projectId, agentId, limit2) {
|
|
189992
|
+
return this.db.prepare(`
|
|
189993
|
+
SELECT * FROM team_messages
|
|
189994
|
+
WHERE project_id = ?
|
|
189995
|
+
AND (recipient_agent_id = ? OR recipient_agent_id IS NULL)
|
|
189996
|
+
ORDER BY created_at ASC
|
|
189997
|
+
LIMIT ?
|
|
189998
|
+
`).all(projectId, agentId, clampTeamQueryLimit(limit2));
|
|
189784
189999
|
}
|
|
189785
190000
|
getUnreadCount(projectId, agentId) {
|
|
189786
190001
|
const row = this.stmtMsgUnreadCount.get(projectId, agentId);
|
|
@@ -189937,17 +190152,22 @@ var init_team_store = __esm({
|
|
|
189937
190152
|
return info2.changes;
|
|
189938
190153
|
}
|
|
189939
190154
|
listTasks(projectId, filter) {
|
|
189940
|
-
|
|
189941
|
-
|
|
189942
|
-
|
|
189943
|
-
const all = this.stmtTaskListByProject.all(projectId);
|
|
190155
|
+
const clauses = ["project_id = ?"];
|
|
190156
|
+
const values = [projectId];
|
|
190157
|
+
if (filter?.available) clauses.push("status = 'pending'", "assignee_agent_id IS NULL");
|
|
189944
190158
|
if (filter?.status) {
|
|
189945
|
-
|
|
190159
|
+
clauses.push("status = ?");
|
|
190160
|
+
values.push(filter.status);
|
|
189946
190161
|
}
|
|
189947
190162
|
if (filter?.assignee) {
|
|
189948
|
-
|
|
190163
|
+
clauses.push("assignee_agent_id = ?");
|
|
190164
|
+
values.push(filter.assignee);
|
|
189949
190165
|
}
|
|
189950
|
-
|
|
190166
|
+
values.push(clampTeamQueryLimit(filter?.limit));
|
|
190167
|
+
const orderBy = filter?.available ? "created_at ASC, rowid ASC" : "created_at DESC, rowid DESC";
|
|
190168
|
+
return this.db.prepare(
|
|
190169
|
+
`SELECT * FROM team_tasks WHERE ${clauses.join(" AND ")} ORDER BY ${orderBy} LIMIT ?`
|
|
190170
|
+
).all(...values);
|
|
189951
190171
|
}
|
|
189952
190172
|
/**
|
|
189953
190173
|
* List available tasks for a specific agent, sorted by role affinity.
|
|
@@ -189959,7 +190179,7 @@ var init_team_store = __esm({
|
|
|
189959
190179
|
const agent = this.stmtAgentFindById.get(agentId);
|
|
189960
190180
|
if (!agent) return [];
|
|
189961
190181
|
const agentRole = agent.role;
|
|
189962
|
-
const available = this.
|
|
190182
|
+
const available = this.listTasks(projectId, { available: true, limit: MAX_TEAM_QUERY_LIMIT });
|
|
189963
190183
|
const eligible = available.filter((t4) => !t4.required_role || t4.required_role === agentRole);
|
|
189964
190184
|
const score = (t4) => {
|
|
189965
190185
|
if (t4.preferred_role === agentRole) return 0;
|
|
@@ -190013,12 +190233,16 @@ var init_team_store = __esm({
|
|
|
190013
190233
|
const row = this.stmtLockGet.get(file2, projectId);
|
|
190014
190234
|
return row ?? null;
|
|
190015
190235
|
}
|
|
190016
|
-
listLocks(projectId, agentId) {
|
|
190236
|
+
listLocks(projectId, agentId, limit2) {
|
|
190017
190237
|
this.stmtLockDeleteExpired.run(projectId, Date.now());
|
|
190018
190238
|
if (agentId) {
|
|
190019
|
-
return this.
|
|
190239
|
+
return this.db.prepare(
|
|
190240
|
+
`SELECT * FROM team_locks WHERE project_id = ? AND locked_by = ? ORDER BY locked_at DESC LIMIT ?`
|
|
190241
|
+
).all(projectId, agentId, clampTeamQueryLimit(limit2));
|
|
190020
190242
|
}
|
|
190021
|
-
return this.
|
|
190243
|
+
return this.db.prepare(
|
|
190244
|
+
`SELECT * FROM team_locks WHERE project_id = ? ORDER BY locked_at DESC LIMIT ?`
|
|
190245
|
+
).all(projectId, clampTeamQueryLimit(limit2));
|
|
190022
190246
|
}
|
|
190023
190247
|
releaseAllLocks(agentId) {
|
|
190024
190248
|
const info2 = this.stmtLockDeleteByAgent.run(agentId);
|
|
@@ -192869,6 +193093,8 @@ function buildInitTomlConfig(options) {
|
|
|
192869
193093
|
lines.push('model = "claude-3-haiku-20240307"');
|
|
192870
193094
|
} else if (options.llmProvider === "openrouter") {
|
|
192871
193095
|
lines.push('# model = "openai/gpt-4o-mini"');
|
|
193096
|
+
} else if (options.llmProvider === "atlascloud") {
|
|
193097
|
+
lines.push('model = "deepseek-ai/deepseek-v3.2"');
|
|
192872
193098
|
}
|
|
192873
193099
|
if (options.llmProvider === "custom") {
|
|
192874
193100
|
lines.push("# OpenAI-compatible endpoint, for example DashScope, DeepSeek, Ollama, or an internal gateway.");
|
|
@@ -193001,6 +193227,7 @@ var init_init = __esm({
|
|
|
193001
193227
|
{ value: "openai", label: "OpenAI", hint: "gpt-4o-mini" },
|
|
193002
193228
|
{ value: "anthropic", label: "Anthropic", hint: "claude-3-haiku" },
|
|
193003
193229
|
{ value: "openrouter", label: "OpenRouter", hint: "multi-provider" },
|
|
193230
|
+
{ value: "atlascloud", label: "Atlas Cloud", hint: "DeepSeek V3.2" },
|
|
193004
193231
|
{ value: "custom", label: "Custom", hint: "OpenAI-compatible endpoint" }
|
|
193005
193232
|
]
|
|
193006
193233
|
});
|
|
@@ -193054,7 +193281,7 @@ var init_init = __esm({
|
|
|
193054
193281
|
];
|
|
193055
193282
|
if (llmProvider !== "none") {
|
|
193056
193283
|
envLines.push("# LLM API key");
|
|
193057
|
-
if (llmProvider === "openai" || llmProvider === "custom") {
|
|
193284
|
+
if (llmProvider === "openai" || llmProvider === "custom" || llmProvider === "atlascloud") {
|
|
193058
193285
|
envLines.push("MEMORIX_LLM_API_KEY=sk-your-key-here");
|
|
193059
193286
|
} else if (llmProvider === "anthropic") {
|
|
193060
193287
|
envLines.push("MEMORIX_LLM_API_KEY=sk-ant-your-key-here");
|
|
@@ -194595,6 +194822,7 @@ var init_official_skills = __esm({
|
|
|
194595
194822
|
"| Need the sequence around one memory | `memorix_timeline` | `memorix memory timeline --id <id>` |",
|
|
194596
194823
|
"| Check source and freshness for a memory | `memorix_evidence` | `memorix evidence list|get|events` |",
|
|
194597
194824
|
"| Record whether a memory helped, conflicted, or was corrected | `memorix_feedback` | `memorix feedback record|show|audit` |",
|
|
194825
|
+
"| Track task requirements, decisions, verification, and outcome | `memorix_continuity` | `memorix continuity start|record|show|close` |",
|
|
194598
194826
|
"| Inspect or import controlled media | `memorix_media` | `memorix media import|attach|list|show|status` |",
|
|
194599
194827
|
'| Learned reusable project knowledge | `memorix_store` | `memorix memory store --type <type> --entity <name> --title "<title>" "<text>"` |',
|
|
194600
194828
|
"| Task or bug is complete/outdated | `memorix_resolve` | `memorix memory resolve --ids <ids>` |",
|
|
@@ -194618,6 +194846,7 @@ var init_official_skills = __esm({
|
|
|
194618
194846
|
"3. Use stale or unbound memory only as a warning or search lead.",
|
|
194619
194847
|
"4. Store durable decisions, fixes, and gotchas after the project state changes.",
|
|
194620
194848
|
"5. Resolve completed or obsolete memories so future briefs stay small.",
|
|
194849
|
+
"6. For multi-step work, start a continuity ledger and pass its taskId back to memorix_project_context.",
|
|
194621
194850
|
"",
|
|
194622
194851
|
"## Store Rules",
|
|
194623
194852
|
"",
|
|
@@ -198185,6 +198414,7 @@ function freshnessForMemory(status) {
|
|
|
198185
198414
|
return "unknown";
|
|
198186
198415
|
}
|
|
198187
198416
|
function receiptOmissionKind(raw) {
|
|
198417
|
+
if (raw.includes("continuity")) return "continuity";
|
|
198188
198418
|
if (raw.includes("continuation")) return "continuation";
|
|
198189
198419
|
if (raw.includes("task")) return "task";
|
|
198190
198420
|
if (raw.includes("fact")) return "current-fact";
|
|
@@ -198295,6 +198525,77 @@ function renderTaskWorksetPrompt(input) {
|
|
|
198295
198525
|
trust: "source-backed"
|
|
198296
198526
|
});
|
|
198297
198527
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
198528
|
+
if (input.continuity) {
|
|
198529
|
+
const continuity = input.continuity;
|
|
198530
|
+
appendLine(lines, "", maxTokens, omitted, "continuity-heading");
|
|
198531
|
+
appendLine(lines, "Task continuity", maxTokens, omitted, "continuity-heading", selected, {
|
|
198532
|
+
kind: "continuity",
|
|
198533
|
+
id: "continuity:" + continuity.taskId,
|
|
198534
|
+
reason: "explicit task continuity ledger",
|
|
198535
|
+
trust: "source-backed"
|
|
198536
|
+
});
|
|
198537
|
+
appendLine(
|
|
198538
|
+
lines,
|
|
198539
|
+
"- Status: " + continuity.status + " | " + short(continuity.task, 30),
|
|
198540
|
+
maxTokens,
|
|
198541
|
+
omitted,
|
|
198542
|
+
"continuity-task"
|
|
198543
|
+
);
|
|
198544
|
+
appendLine(
|
|
198545
|
+
lines,
|
|
198546
|
+
"- Outcome: " + continuity.outcome.state,
|
|
198547
|
+
maxTokens,
|
|
198548
|
+
omitted,
|
|
198549
|
+
"continuity-outcome",
|
|
198550
|
+
selected,
|
|
198551
|
+
{
|
|
198552
|
+
kind: "continuity",
|
|
198553
|
+
id: "continuity:" + continuity.taskId,
|
|
198554
|
+
reason: "deterministic task outcome projection",
|
|
198555
|
+
trust: "derived"
|
|
198556
|
+
}
|
|
198557
|
+
);
|
|
198558
|
+
for (const requirement of continuity.requirements.slice(0, 3)) {
|
|
198559
|
+
appendLine(lines, "- Requirement: " + short(requirement, 24), maxTokens, omitted, "continuity-requirement", selected, {
|
|
198560
|
+
kind: "continuity",
|
|
198561
|
+
id: "continuity:" + continuity.taskId,
|
|
198562
|
+
reason: "explicit task requirement",
|
|
198563
|
+
trust: "source-backed"
|
|
198564
|
+
});
|
|
198565
|
+
}
|
|
198566
|
+
for (const decision of continuity.decisions.slice(0, 2)) {
|
|
198567
|
+
appendLine(lines, "- Decision: " + short(decision, 24), maxTokens, omitted, "continuity-decision", selected, {
|
|
198568
|
+
kind: "continuity",
|
|
198569
|
+
id: "continuity:" + continuity.taskId,
|
|
198570
|
+
reason: "explicit task decision",
|
|
198571
|
+
trust: "source-backed"
|
|
198572
|
+
});
|
|
198573
|
+
}
|
|
198574
|
+
for (const verification of continuity.verification.slice(-3)) {
|
|
198575
|
+
appendLine(
|
|
198576
|
+
lines,
|
|
198577
|
+
"- Verify [" + verification.status + "]: " + short(verification.content, 22),
|
|
198578
|
+
maxTokens,
|
|
198579
|
+
omitted,
|
|
198580
|
+
"continuity-verification",
|
|
198581
|
+
selected,
|
|
198582
|
+
{
|
|
198583
|
+
kind: "continuity",
|
|
198584
|
+
id: "continuity:" + continuity.taskId,
|
|
198585
|
+
reason: "task verification state",
|
|
198586
|
+
trust: "source-backed"
|
|
198587
|
+
}
|
|
198588
|
+
);
|
|
198589
|
+
}
|
|
198590
|
+
for (const risk of continuity.risks.slice(0, 2)) {
|
|
198591
|
+
appendLine(lines, "- Risk: " + short(risk, 22), maxTokens, omitted, "continuity-risk", selected, {
|
|
198592
|
+
kind: "continuity",
|
|
198593
|
+
id: "continuity:" + continuity.taskId,
|
|
198594
|
+
reason: "open task risk",
|
|
198595
|
+
trust: "source-backed"
|
|
198596
|
+
});
|
|
198597
|
+
}
|
|
198598
|
+
}
|
|
198298
198599
|
if (input.alwaysOn) {
|
|
198299
198600
|
const hasAlwaysOn = input.alwaysOn.profile.length > 0 || Boolean(input.alwaysOn.state) || input.alwaysOn.durable.length > 0;
|
|
198300
198601
|
if (hasAlwaysOn) {
|
|
@@ -198818,6 +199119,7 @@ async function buildTaskWorkset(input) {
|
|
|
198818
199119
|
version: "1.3",
|
|
198819
199120
|
task,
|
|
198820
199121
|
lens: input.lens,
|
|
199122
|
+
...input.continuity ? { continuity: input.continuity } : {},
|
|
198821
199123
|
...input.alwaysOn && (input.alwaysOn.profile.length > 0 || input.alwaysOn.state || input.alwaysOn.durable.length > 0) ? { alwaysOn: input.alwaysOn } : {},
|
|
198822
199124
|
currentFacts: input.currentFacts?.map((fact) => fact.startsWith("Historical note:") ? short(fact, 48) : short(fact, 28)).slice(0, 4) ?? [],
|
|
198823
199125
|
...continuation ? { continuation } : {},
|
|
@@ -201226,7 +201528,7 @@ var compaction_checkpoint_store_exports = {};
|
|
|
201226
201528
|
__export(compaction_checkpoint_store_exports, {
|
|
201227
201529
|
CompactionCheckpointStore: () => CompactionCheckpointStore
|
|
201228
201530
|
});
|
|
201229
|
-
import { createHash as
|
|
201531
|
+
import { createHash as createHash17, randomUUID as randomUUID17 } from "crypto";
|
|
201230
201532
|
function optionalText7(value) {
|
|
201231
201533
|
return typeof value === "string" && value ? value : void 0;
|
|
201232
201534
|
}
|
|
@@ -201268,7 +201570,7 @@ function sourceKeyFor(input, summary) {
|
|
|
201268
201570
|
input.tokensBefore ?? "",
|
|
201269
201571
|
input.firstKeptEntryId ?? ""
|
|
201270
201572
|
].join("\0");
|
|
201271
|
-
return `derived:${
|
|
201573
|
+
return `derived:${createHash17("sha256").update(material).digest("hex").slice(0, 32)}`;
|
|
201272
201574
|
}
|
|
201273
201575
|
function rowToCheckpoint(row) {
|
|
201274
201576
|
return {
|
|
@@ -201526,6 +201828,305 @@ var init_compaction_checkpoint_store = __esm({
|
|
|
201526
201828
|
}
|
|
201527
201829
|
});
|
|
201528
201830
|
|
|
201831
|
+
// src/knowledge/task-continuity.ts
|
|
201832
|
+
var task_continuity_exports = {};
|
|
201833
|
+
__export(task_continuity_exports, {
|
|
201834
|
+
TASK_CONTINUITY_EVENT_KINDS: () => TASK_CONTINUITY_EVENT_KINDS,
|
|
201835
|
+
TaskContinuityStore: () => TaskContinuityStore,
|
|
201836
|
+
evaluateTaskContinuity: () => evaluateTaskContinuity
|
|
201837
|
+
});
|
|
201838
|
+
import { randomUUID as randomUUID18 } from "crypto";
|
|
201839
|
+
function clean(value, maxLength = MAX_TEXT_LENGTH2) {
|
|
201840
|
+
return sanitizeCredentials(value).replace(/\s+/g, " ").trim().slice(0, maxLength);
|
|
201841
|
+
}
|
|
201842
|
+
function cleanList(values, maxLength = MAX_TEXT_LENGTH2) {
|
|
201843
|
+
return [...new Set((values ?? []).map((value) => clean(value, maxLength)).filter(Boolean))].slice(0, MAX_LIST_ITEMS2);
|
|
201844
|
+
}
|
|
201845
|
+
function parseRefs(value) {
|
|
201846
|
+
if (typeof value !== "string") return [];
|
|
201847
|
+
try {
|
|
201848
|
+
const parsed = JSON.parse(value);
|
|
201849
|
+
return Array.isArray(parsed) ? cleanList(parsed.filter((item) => typeof item === "string"), 500).slice(0, MAX_EVIDENCE_REFS) : [];
|
|
201850
|
+
} catch {
|
|
201851
|
+
return [];
|
|
201852
|
+
}
|
|
201853
|
+
}
|
|
201854
|
+
function evaluateTaskContinuity(ledger) {
|
|
201855
|
+
const verification = {
|
|
201856
|
+
passed: ledger.verification.filter((item) => item.status === "passed").length,
|
|
201857
|
+
failed: ledger.verification.filter((item) => item.status === "failed").length,
|
|
201858
|
+
pending: ledger.verification.filter((item) => item.status === "pending").length,
|
|
201859
|
+
skipped: ledger.verification.filter((item) => item.status === "skipped").length
|
|
201860
|
+
};
|
|
201861
|
+
const reasons = [];
|
|
201862
|
+
if (verification.failed > 0) reasons.push("verification-failed");
|
|
201863
|
+
if (verification.pending > 0) reasons.push("verification-pending");
|
|
201864
|
+
if (ledger.risks.length > 0) reasons.push("open-risks");
|
|
201865
|
+
if (ledger.status === "blocked") reasons.push("task-blocked");
|
|
201866
|
+
if (ledger.status === "abandoned") reasons.push("task-abandoned");
|
|
201867
|
+
if (ledger.status === "blocked" || ledger.status === "abandoned" || verification.failed > 0) {
|
|
201868
|
+
return {
|
|
201869
|
+
state: "at-risk",
|
|
201870
|
+
score: verification.passed > verification.failed ? 0.55 : 0.2,
|
|
201871
|
+
verification,
|
|
201872
|
+
reasons
|
|
201873
|
+
};
|
|
201874
|
+
}
|
|
201875
|
+
if (ledger.status === "completed") {
|
|
201876
|
+
if (verification.passed > 0 && verification.pending === 0 && verification.skipped === 0) {
|
|
201877
|
+
return {
|
|
201878
|
+
state: ledger.risks.length > 0 ? "validated-with-risks" : "validated",
|
|
201879
|
+
score: ledger.risks.length > 0 ? 0.8 : 1,
|
|
201880
|
+
verification,
|
|
201881
|
+
reasons: reasons.length > 0 ? reasons : ["verification-passed"]
|
|
201882
|
+
};
|
|
201883
|
+
}
|
|
201884
|
+
return {
|
|
201885
|
+
state: "unverified",
|
|
201886
|
+
score: 0.4,
|
|
201887
|
+
verification,
|
|
201888
|
+
reasons: reasons.length > 0 ? reasons : ["no-passing-verification"]
|
|
201889
|
+
};
|
|
201890
|
+
}
|
|
201891
|
+
return {
|
|
201892
|
+
state: "in-progress",
|
|
201893
|
+
score: verification.passed > 0 && verification.pending === 0 ? 0.75 : 0.5,
|
|
201894
|
+
verification,
|
|
201895
|
+
reasons: reasons.length > 0 ? reasons : ["task-open"]
|
|
201896
|
+
};
|
|
201897
|
+
}
|
|
201898
|
+
function rowToEvent4(row) {
|
|
201899
|
+
const status = row.item_status ? String(row.item_status) : void 0;
|
|
201900
|
+
return {
|
|
201901
|
+
id: String(row.id),
|
|
201902
|
+
projectId: String(row.project_id),
|
|
201903
|
+
taskId: String(row.task_id),
|
|
201904
|
+
task: clean(String(row.task), MAX_TASK_LENGTH),
|
|
201905
|
+
kind: row.kind,
|
|
201906
|
+
content: clean(String(row.content)),
|
|
201907
|
+
...status ? { status } : {},
|
|
201908
|
+
...row.source_ref ? { sourceRef: clean(String(row.source_ref), 500) } : {},
|
|
201909
|
+
evidenceRefs: parseRefs(row.evidence_json),
|
|
201910
|
+
...row.actor ? { actor: clean(String(row.actor), 200) } : {},
|
|
201911
|
+
at: String(row.created_at),
|
|
201912
|
+
...row.event_key ? { eventKey: clean(String(row.event_key), 200) } : {},
|
|
201913
|
+
...row.item_key ? { itemKey: clean(String(row.item_key), 120) } : {},
|
|
201914
|
+
...row.supersedes_id ? { supersedesId: clean(String(row.supersedes_id), 120) } : {}
|
|
201915
|
+
};
|
|
201916
|
+
}
|
|
201917
|
+
function aggregate(events) {
|
|
201918
|
+
const first2 = events.find((event) => event.kind === "task") ?? events[0];
|
|
201919
|
+
const latestOutcome = [...events].reverse().find((event) => event.kind === "outcome");
|
|
201920
|
+
const verificationByKey = /* @__PURE__ */ new Map();
|
|
201921
|
+
for (const event of events.filter((event2) => event2.kind === "verification")) {
|
|
201922
|
+
verificationByKey.set(event.itemKey ?? `event:${event.id}`, event);
|
|
201923
|
+
}
|
|
201924
|
+
const ledger = {
|
|
201925
|
+
taskId: first2.taskId,
|
|
201926
|
+
projectId: first2.projectId,
|
|
201927
|
+
task: first2.task,
|
|
201928
|
+
status: latestOutcome?.status ?? "open",
|
|
201929
|
+
requirements: events.filter((event) => event.kind === "requirement").map((event) => event.content),
|
|
201930
|
+
decisions: events.filter((event) => event.kind === "decision").map((event) => event.content),
|
|
201931
|
+
verification: [...verificationByKey.values()].map((event) => ({
|
|
201932
|
+
id: event.itemKey ?? event.id,
|
|
201933
|
+
content: event.content,
|
|
201934
|
+
status: event.status ?? "pending",
|
|
201935
|
+
...event.sourceRef ? { sourceRef: event.sourceRef } : {},
|
|
201936
|
+
evidenceRefs: event.evidenceRefs
|
|
201937
|
+
})),
|
|
201938
|
+
risks: events.filter((event) => event.kind === "risk").map((event) => event.content),
|
|
201939
|
+
outcomes: events.filter((event) => event.kind === "outcome").map((event) => event.content),
|
|
201940
|
+
updatedAt: events[events.length - 1].at,
|
|
201941
|
+
events
|
|
201942
|
+
};
|
|
201943
|
+
return { ...ledger, outcome: evaluateTaskContinuity(ledger) };
|
|
201944
|
+
}
|
|
201945
|
+
var TASK_CONTINUITY_EVENT_KINDS, MAX_TEXT_LENGTH2, MAX_TASK_LENGTH, MAX_LIST_ITEMS2, MAX_EVIDENCE_REFS, MAX_EVENTS_PER_TASK, TaskContinuityStore;
|
|
201946
|
+
var init_task_continuity = __esm({
|
|
201947
|
+
"src/knowledge/task-continuity.ts"() {
|
|
201948
|
+
"use strict";
|
|
201949
|
+
init_esm_shims();
|
|
201950
|
+
init_secret_filter();
|
|
201951
|
+
init_sqlite_db();
|
|
201952
|
+
TASK_CONTINUITY_EVENT_KINDS = [
|
|
201953
|
+
"task",
|
|
201954
|
+
"requirement",
|
|
201955
|
+
"decision",
|
|
201956
|
+
"verification",
|
|
201957
|
+
"risk",
|
|
201958
|
+
"outcome"
|
|
201959
|
+
];
|
|
201960
|
+
MAX_TEXT_LENGTH2 = 2e3;
|
|
201961
|
+
MAX_TASK_LENGTH = 1e3;
|
|
201962
|
+
MAX_LIST_ITEMS2 = 8;
|
|
201963
|
+
MAX_EVIDENCE_REFS = 8;
|
|
201964
|
+
MAX_EVENTS_PER_TASK = 200;
|
|
201965
|
+
TaskContinuityStore = class {
|
|
201966
|
+
db = null;
|
|
201967
|
+
async init(dataDir) {
|
|
201968
|
+
this.db = getDatabase(dataDir);
|
|
201969
|
+
}
|
|
201970
|
+
requireDb() {
|
|
201971
|
+
if (!this.db) throw new Error("TaskContinuityStore is not initialized.");
|
|
201972
|
+
return this.db;
|
|
201973
|
+
}
|
|
201974
|
+
insert(input) {
|
|
201975
|
+
const db2 = this.requireDb();
|
|
201976
|
+
const projectId = clean(input.projectId, 500);
|
|
201977
|
+
const taskId = clean(input.taskId, 120);
|
|
201978
|
+
const eventKey = input.eventKey ? clean(input.eventKey, 200) : void 0;
|
|
201979
|
+
if (eventKey) {
|
|
201980
|
+
const existing = db2.prepare(
|
|
201981
|
+
"SELECT * FROM task_continuity_events WHERE project_id = ? AND task_id = ? AND event_key = ?"
|
|
201982
|
+
).get(projectId, taskId, eventKey);
|
|
201983
|
+
if (existing) return rowToEvent4(existing);
|
|
201984
|
+
}
|
|
201985
|
+
const existingCount = db2.prepare(
|
|
201986
|
+
"SELECT COUNT(*) AS count FROM task_continuity_events WHERE project_id = ? AND task_id = ?"
|
|
201987
|
+
).get(projectId, taskId);
|
|
201988
|
+
if (Number(existingCount?.count ?? 0) >= MAX_EVENTS_PER_TASK) {
|
|
201989
|
+
throw new Error("Task continuity ledger reached its 200-event limit.");
|
|
201990
|
+
}
|
|
201991
|
+
const event = {
|
|
201992
|
+
id: randomUUID18(),
|
|
201993
|
+
projectId,
|
|
201994
|
+
taskId,
|
|
201995
|
+
task: clean(input.task, MAX_TASK_LENGTH),
|
|
201996
|
+
kind: input.kind,
|
|
201997
|
+
content: clean(input.content),
|
|
201998
|
+
...input.status ? { status: input.status } : {},
|
|
201999
|
+
...input.sourceRef ? { sourceRef: clean(input.sourceRef, 500) } : {},
|
|
202000
|
+
evidenceRefs: cleanList(input.evidenceRefs, 500).slice(0, MAX_EVIDENCE_REFS),
|
|
202001
|
+
...input.actor ? { actor: clean(input.actor, 200) } : {},
|
|
202002
|
+
at: input.at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
202003
|
+
...eventKey ? { eventKey } : {},
|
|
202004
|
+
...input.itemKey ? { itemKey: clean(input.itemKey, 120) } : {},
|
|
202005
|
+
...input.supersedesId ? { supersedesId: clean(input.supersedesId, 120) } : {}
|
|
202006
|
+
};
|
|
202007
|
+
db2.prepare(`
|
|
202008
|
+
INSERT INTO task_continuity_events (
|
|
202009
|
+
id, project_id, task_id, task, kind, content, item_status,
|
|
202010
|
+
source_ref, evidence_json, actor, created_at, event_key, item_key, supersedes_id
|
|
202011
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
202012
|
+
`).run(
|
|
202013
|
+
event.id,
|
|
202014
|
+
event.projectId,
|
|
202015
|
+
event.taskId,
|
|
202016
|
+
event.task,
|
|
202017
|
+
event.kind,
|
|
202018
|
+
event.content,
|
|
202019
|
+
event.status ?? null,
|
|
202020
|
+
event.sourceRef ?? null,
|
|
202021
|
+
JSON.stringify(event.evidenceRefs),
|
|
202022
|
+
event.actor ?? null,
|
|
202023
|
+
event.at,
|
|
202024
|
+
event.eventKey ?? null,
|
|
202025
|
+
event.itemKey ?? null,
|
|
202026
|
+
event.supersedesId ?? null
|
|
202027
|
+
);
|
|
202028
|
+
return event;
|
|
202029
|
+
}
|
|
202030
|
+
start(input) {
|
|
202031
|
+
const task = clean(input.task, MAX_TASK_LENGTH);
|
|
202032
|
+
if (!task) throw new Error("A non-empty task is required.");
|
|
202033
|
+
const idempotencyKey = input.idempotencyKey ? clean(input.idempotencyKey, 200) : void 0;
|
|
202034
|
+
const taskId = clean(input.taskId ?? idempotencyKey ?? "", 120) || randomUUID18();
|
|
202035
|
+
const requirements = cleanList(input.requirements);
|
|
202036
|
+
const transaction = this.requireDb().transaction(() => {
|
|
202037
|
+
this.insert({
|
|
202038
|
+
projectId: input.projectId,
|
|
202039
|
+
taskId,
|
|
202040
|
+
task,
|
|
202041
|
+
kind: "task",
|
|
202042
|
+
content: task,
|
|
202043
|
+
status: "open",
|
|
202044
|
+
actor: input.actor,
|
|
202045
|
+
...idempotencyKey ? { eventKey: `start:${idempotencyKey}` } : {}
|
|
202046
|
+
});
|
|
202047
|
+
for (const requirement of requirements) {
|
|
202048
|
+
this.insert({
|
|
202049
|
+
projectId: input.projectId,
|
|
202050
|
+
taskId,
|
|
202051
|
+
task,
|
|
202052
|
+
kind: "requirement",
|
|
202053
|
+
content: requirement,
|
|
202054
|
+
actor: input.actor
|
|
202055
|
+
});
|
|
202056
|
+
}
|
|
202057
|
+
});
|
|
202058
|
+
transaction.immediate();
|
|
202059
|
+
return { taskId, ledger: this.get(input.projectId, taskId) };
|
|
202060
|
+
}
|
|
202061
|
+
record(input) {
|
|
202062
|
+
const projectId = clean(input.projectId, 500);
|
|
202063
|
+
const taskId = clean(input.taskId, 120);
|
|
202064
|
+
const existing = this.get(projectId, taskId);
|
|
202065
|
+
if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
|
|
202066
|
+
const transaction = this.requireDb().transaction(() => this.insert({
|
|
202067
|
+
projectId,
|
|
202068
|
+
taskId,
|
|
202069
|
+
task: existing.task,
|
|
202070
|
+
kind: input.kind,
|
|
202071
|
+
content: input.content,
|
|
202072
|
+
...input.kind === "verification" && input.verificationStatus ? { status: input.verificationStatus } : {},
|
|
202073
|
+
sourceRef: input.sourceRef,
|
|
202074
|
+
evidenceRefs: input.evidenceRefs,
|
|
202075
|
+
actor: input.actor,
|
|
202076
|
+
at: input.at,
|
|
202077
|
+
...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {},
|
|
202078
|
+
...input.verificationId ? { itemKey: input.verificationId } : {}
|
|
202079
|
+
}));
|
|
202080
|
+
const event = transaction.immediate();
|
|
202081
|
+
return { event, ledger: this.get(projectId, taskId) };
|
|
202082
|
+
}
|
|
202083
|
+
close(input) {
|
|
202084
|
+
const projectId = clean(input.projectId, 500);
|
|
202085
|
+
const taskId = clean(input.taskId, 120);
|
|
202086
|
+
const existing = this.get(projectId, taskId);
|
|
202087
|
+
if (!existing) throw new Error("Task continuity ledger not found: " + taskId);
|
|
202088
|
+
const transaction = this.requireDb().transaction(() => this.insert({
|
|
202089
|
+
projectId,
|
|
202090
|
+
taskId,
|
|
202091
|
+
task: existing.task,
|
|
202092
|
+
kind: "outcome",
|
|
202093
|
+
content: input.content,
|
|
202094
|
+
status: input.status,
|
|
202095
|
+
sourceRef: input.sourceRef,
|
|
202096
|
+
evidenceRefs: input.evidenceRefs,
|
|
202097
|
+
actor: input.actor,
|
|
202098
|
+
at: input.at,
|
|
202099
|
+
...input.idempotencyKey ? { eventKey: input.idempotencyKey } : {}
|
|
202100
|
+
}));
|
|
202101
|
+
const event = transaction.immediate();
|
|
202102
|
+
return { event, ledger: this.get(projectId, taskId) };
|
|
202103
|
+
}
|
|
202104
|
+
get(projectId, taskId) {
|
|
202105
|
+
const normalizedProjectId = clean(projectId, 500);
|
|
202106
|
+
const normalizedTaskId = clean(taskId, 120);
|
|
202107
|
+
const rows = this.requireDb().prepare(`
|
|
202108
|
+
SELECT * FROM task_continuity_events
|
|
202109
|
+
WHERE project_id = ? AND task_id = ?
|
|
202110
|
+
ORDER BY created_at ASC, rowid ASC
|
|
202111
|
+
`).all(normalizedProjectId, normalizedTaskId);
|
|
202112
|
+
if (rows.length === 0) return void 0;
|
|
202113
|
+
return aggregate(rows.map(rowToEvent4));
|
|
202114
|
+
}
|
|
202115
|
+
list(projectId, limit2 = 20) {
|
|
202116
|
+
const rows = this.requireDb().prepare(`
|
|
202117
|
+
SELECT task_id
|
|
202118
|
+
FROM task_continuity_events
|
|
202119
|
+
WHERE project_id = ?
|
|
202120
|
+
GROUP BY task_id
|
|
202121
|
+
ORDER BY MAX(created_at) DESC
|
|
202122
|
+
LIMIT ?
|
|
202123
|
+
`).all(projectId, Math.min(100, Math.max(1, Math.floor(limit2))));
|
|
202124
|
+
return rows.map((row) => this.get(projectId, String(row.task_id))).filter((ledger) => Boolean(ledger));
|
|
202125
|
+
}
|
|
202126
|
+
};
|
|
202127
|
+
}
|
|
202128
|
+
});
|
|
202129
|
+
|
|
201529
202130
|
// src/codegraph/auto-context.ts
|
|
201530
202131
|
var auto_context_exports = {};
|
|
201531
202132
|
__export(auto_context_exports, {
|
|
@@ -201782,6 +202383,16 @@ async function buildAutoProjectContext(input) {
|
|
|
201782
202383
|
};
|
|
201783
202384
|
}
|
|
201784
202385
|
}
|
|
202386
|
+
let continuity = input.continuity;
|
|
202387
|
+
if (!continuity && input.taskId) {
|
|
202388
|
+
try {
|
|
202389
|
+
const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
|
|
202390
|
+
const continuityStore = new TaskContinuityStore2();
|
|
202391
|
+
await continuityStore.init(input.dataDir);
|
|
202392
|
+
continuity = continuityStore.get(input.project.id, input.taskId);
|
|
202393
|
+
} catch {
|
|
202394
|
+
}
|
|
202395
|
+
}
|
|
201785
202396
|
const alwaysOn = await buildAlwaysOnBlock({
|
|
201786
202397
|
dataDir: input.dataDir,
|
|
201787
202398
|
projectId: input.project.id,
|
|
@@ -201800,6 +202411,7 @@ async function buildAutoProjectContext(input) {
|
|
|
201800
202411
|
providerQuality: providerQuality2,
|
|
201801
202412
|
currentFacts: worksetFactLines(currentFacts),
|
|
201802
202413
|
...continuation ? { continuation } : {},
|
|
202414
|
+
...continuity ? { continuity } : {},
|
|
201803
202415
|
codeState: codeStateLine(overview),
|
|
201804
202416
|
...codeEvolution ? { codeEvolution } : {},
|
|
201805
202417
|
reliableMemory: sourceSets.reliableSources.slice(0, lens.sourceLimit).map((source) => ({
|
|
@@ -201847,6 +202459,7 @@ async function buildAutoProjectContext(input) {
|
|
|
201847
202459
|
refresh,
|
|
201848
202460
|
providerQuality: providerQuality2,
|
|
201849
202461
|
...continuationRequested && workset.continuation ? { continuation: workset.continuation } : {},
|
|
202462
|
+
...workset.continuity ? { continuity: workset.continuity } : {},
|
|
201850
202463
|
workset
|
|
201851
202464
|
};
|
|
201852
202465
|
}
|
|
@@ -202148,7 +202761,14 @@ function buildBoundedContextReceipt(input) {
|
|
|
202148
202761
|
externalState: provider2?.external.state ?? "not-detected",
|
|
202149
202762
|
...input.workset.provenance.snapshotId ? { snapshotId: input.workset.provenance.snapshotId } : {}
|
|
202150
202763
|
},
|
|
202151
|
-
...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {}
|
|
202764
|
+
...input.workset.agentLoadout ? { loadout: input.workset.agentLoadout } : {},
|
|
202765
|
+
...input.workset.continuity ? {
|
|
202766
|
+
continuity: {
|
|
202767
|
+
taskId: input.workset.continuity.taskId,
|
|
202768
|
+
status: input.workset.continuity.status,
|
|
202769
|
+
outcome: input.workset.continuity.outcome
|
|
202770
|
+
}
|
|
202771
|
+
} : {}
|
|
202152
202772
|
};
|
|
202153
202773
|
}
|
|
202154
202774
|
var init_context_receipt = __esm({
|
|
@@ -202159,10 +202779,10 @@ var init_context_receipt = __esm({
|
|
|
202159
202779
|
});
|
|
202160
202780
|
|
|
202161
202781
|
// src/cli/fallback-budget.ts
|
|
202162
|
-
import { createHash as
|
|
202782
|
+
import { createHash as createHash18 } from "crypto";
|
|
202163
202783
|
function fallbackRequestKey(projectId, task) {
|
|
202164
202784
|
const normalized = (task ?? "").trim().toLowerCase().replace(/\s+/g, " ");
|
|
202165
|
-
return
|
|
202785
|
+
return createHash18("sha256").update(`${projectId}
|
|
202166
202786
|
${normalized}`).digest("hex").slice(0, 32);
|
|
202167
202787
|
}
|
|
202168
202788
|
var DEFAULT_FALLBACK_TTL_MS, CliFallbackBudget;
|
|
@@ -202247,6 +202867,7 @@ var init_context2 = __esm({
|
|
|
202247
202867
|
},
|
|
202248
202868
|
args: {
|
|
202249
202869
|
task: { type: "string", description: "Current task for context shaping" },
|
|
202870
|
+
taskId: { type: "string", description: "Optional task continuity ledger id to include" },
|
|
202250
202871
|
input: {
|
|
202251
202872
|
type: "positional",
|
|
202252
202873
|
description: "Current task for context shaping (ergonomic positional form)",
|
|
@@ -202278,6 +202899,7 @@ var init_context2 = __esm({
|
|
|
202278
202899
|
dataDir,
|
|
202279
202900
|
observations: filterReadableObservations(getAllObservations(), reader2),
|
|
202280
202901
|
task,
|
|
202902
|
+
taskId: args.taskId?.trim() || void 0,
|
|
202281
202903
|
agent: coerceAgentTarget(args.agent),
|
|
202282
202904
|
refresh: coerceRefreshMode(args.refresh),
|
|
202283
202905
|
reader: reader2,
|
|
@@ -203780,6 +204402,158 @@ var init_feedback2 = __esm({
|
|
|
203780
204402
|
}
|
|
203781
204403
|
});
|
|
203782
204404
|
|
|
204405
|
+
// src/cli/commands/continuity.ts
|
|
204406
|
+
var continuity_exports = {};
|
|
204407
|
+
__export(continuity_exports, {
|
|
204408
|
+
default: () => continuity_default
|
|
204409
|
+
});
|
|
204410
|
+
var recordKinds, continuity_default;
|
|
204411
|
+
var init_continuity = __esm({
|
|
204412
|
+
"src/cli/commands/continuity.ts"() {
|
|
204413
|
+
"use strict";
|
|
204414
|
+
init_esm_shims();
|
|
204415
|
+
init_dist2();
|
|
204416
|
+
init_task_continuity();
|
|
204417
|
+
init_operator_shared();
|
|
204418
|
+
recordKinds = [
|
|
204419
|
+
"requirement",
|
|
204420
|
+
"decision",
|
|
204421
|
+
"verification",
|
|
204422
|
+
"risk"
|
|
204423
|
+
];
|
|
204424
|
+
continuity_default = defineCommand({
|
|
204425
|
+
meta: {
|
|
204426
|
+
name: "continuity",
|
|
204427
|
+
description: "Track task requirements, decisions, verification, risks, and outcomes"
|
|
204428
|
+
},
|
|
204429
|
+
args: {
|
|
204430
|
+
taskId: { type: "string", description: "Continuity task id" },
|
|
204431
|
+
task: { type: "string", description: "Task description for start" },
|
|
204432
|
+
requirements: { type: "string", description: "Comma-separated requirements for start" },
|
|
204433
|
+
kind: { type: "string", description: "requirement, decision, verification, or risk" },
|
|
204434
|
+
content: { type: "string", description: "Entry or outcome text" },
|
|
204435
|
+
verificationStatus: { type: "string", description: "pending, passed, failed, or skipped" },
|
|
204436
|
+
verificationId: { type: "string", description: "Stable verification obligation id for status transitions" },
|
|
204437
|
+
idempotencyKey: { type: "string", description: "Stable retry key for idempotent writes" },
|
|
204438
|
+
status: { type: "string", description: "completed, blocked, or abandoned for close" },
|
|
204439
|
+
source: { type: "string", description: "Source or evidence reference" },
|
|
204440
|
+
evidence: { type: "string", description: "Comma-separated evidence references" },
|
|
204441
|
+
actor: { type: "string", description: "Actor id or label" },
|
|
204442
|
+
limit: { type: "string", description: "Maximum ledgers for list" },
|
|
204443
|
+
json: { type: "boolean", description: "Emit machine-readable JSON output" }
|
|
204444
|
+
},
|
|
204445
|
+
run: async ({ args }) => {
|
|
204446
|
+
const action = args._?.[0] || "";
|
|
204447
|
+
const asJson = !!args.json;
|
|
204448
|
+
try {
|
|
204449
|
+
const { project, dataDir, identity } = await getCliProjectContext({ searchIndex: false });
|
|
204450
|
+
const store2 = new TaskContinuityStore();
|
|
204451
|
+
await store2.init(dataDir);
|
|
204452
|
+
const actor = String(args.actor || identity?.agentId || "").trim() || void 0;
|
|
204453
|
+
if (action === "start") {
|
|
204454
|
+
if (!args.task) {
|
|
204455
|
+
emitError('task is required for "memorix continuity start"', asJson);
|
|
204456
|
+
return;
|
|
204457
|
+
}
|
|
204458
|
+
const result = store2.start({
|
|
204459
|
+
projectId: project.id,
|
|
204460
|
+
task: String(args.task),
|
|
204461
|
+
...args.taskId ? { taskId: String(args.taskId) } : {},
|
|
204462
|
+
requirements: parseCsvList(args.requirements),
|
|
204463
|
+
...actor ? { actor } : {},
|
|
204464
|
+
...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
|
|
204465
|
+
});
|
|
204466
|
+
emitResult({ project, ...result }, "Continuity started: " + result.taskId, asJson);
|
|
204467
|
+
return;
|
|
204468
|
+
}
|
|
204469
|
+
if (action === "list") {
|
|
204470
|
+
const ledgers = store2.list(project.id, Number(args.limit || 20));
|
|
204471
|
+
emitResult(
|
|
204472
|
+
{ project, ledgers },
|
|
204473
|
+
ledgers.length ? ledgers.map((item) => item.status + ": " + item.taskId + " " + item.task).join("\n") : "No continuity ledgers found.",
|
|
204474
|
+
asJson
|
|
204475
|
+
);
|
|
204476
|
+
return;
|
|
204477
|
+
}
|
|
204478
|
+
const taskId = String(args.taskId || "").trim();
|
|
204479
|
+
if (!taskId) {
|
|
204480
|
+
emitError("taskId is required for this continuity action.", asJson);
|
|
204481
|
+
return;
|
|
204482
|
+
}
|
|
204483
|
+
if (action === "show") {
|
|
204484
|
+
const ledger = store2.get(project.id, taskId);
|
|
204485
|
+
if (!ledger) {
|
|
204486
|
+
emitError("Task continuity ledger not found.", asJson);
|
|
204487
|
+
return;
|
|
204488
|
+
}
|
|
204489
|
+
emitResult({ project, ledger }, JSON.stringify(ledger, null, 2), asJson);
|
|
204490
|
+
return;
|
|
204491
|
+
}
|
|
204492
|
+
if (action === "record") {
|
|
204493
|
+
const kind = String(args.kind || "").trim();
|
|
204494
|
+
const content = String(args.content || "").trim();
|
|
204495
|
+
if (!recordKinds.includes(kind) || !content) {
|
|
204496
|
+
emitError("record requires --kind (requirement, decision, verification, risk) and --content.", asJson);
|
|
204497
|
+
return;
|
|
204498
|
+
}
|
|
204499
|
+
const verificationStatus = args.verificationStatus ? String(args.verificationStatus).trim() : void 0;
|
|
204500
|
+
if (verificationStatus && !["pending", "passed", "failed", "skipped"].includes(verificationStatus)) {
|
|
204501
|
+
emitError("verificationStatus must be pending, passed, failed, or skipped.", asJson);
|
|
204502
|
+
return;
|
|
204503
|
+
}
|
|
204504
|
+
const result = store2.record({
|
|
204505
|
+
projectId: project.id,
|
|
204506
|
+
taskId,
|
|
204507
|
+
kind,
|
|
204508
|
+
content,
|
|
204509
|
+
...verificationStatus ? { verificationStatus } : {},
|
|
204510
|
+
...args.source ? { sourceRef: String(args.source) } : {},
|
|
204511
|
+
evidenceRefs: parseCsvList(args.evidence),
|
|
204512
|
+
...actor ? { actor } : {},
|
|
204513
|
+
...args.verificationId ? { verificationId: String(args.verificationId) } : {},
|
|
204514
|
+
...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
|
|
204515
|
+
});
|
|
204516
|
+
emitResult({ project, ...result }, "Continuity recorded: " + kind, asJson);
|
|
204517
|
+
return;
|
|
204518
|
+
}
|
|
204519
|
+
if (action === "close") {
|
|
204520
|
+
const status = String(args.status || "").trim();
|
|
204521
|
+
const content = String(args.content || "").trim();
|
|
204522
|
+
if (!["completed", "blocked", "abandoned"].includes(status) || !content) {
|
|
204523
|
+
emitError("close requires --status (completed, blocked, abandoned) and --content.", asJson);
|
|
204524
|
+
return;
|
|
204525
|
+
}
|
|
204526
|
+
const result = store2.close({
|
|
204527
|
+
projectId: project.id,
|
|
204528
|
+
taskId,
|
|
204529
|
+
status,
|
|
204530
|
+
content,
|
|
204531
|
+
...args.source ? { sourceRef: String(args.source) } : {},
|
|
204532
|
+
evidenceRefs: parseCsvList(args.evidence),
|
|
204533
|
+
...actor ? { actor } : {},
|
|
204534
|
+
...args.idempotencyKey ? { idempotencyKey: String(args.idempotencyKey) } : {}
|
|
204535
|
+
});
|
|
204536
|
+
emitResult({ project, ...result }, "Continuity closed: " + status, asJson);
|
|
204537
|
+
return;
|
|
204538
|
+
}
|
|
204539
|
+
console.log("Memorix Task Continuity");
|
|
204540
|
+
console.log("");
|
|
204541
|
+
console.log("Usage:");
|
|
204542
|
+
console.log(' memorix continuity start --task "..." [--requirements "a,b"]');
|
|
204543
|
+
console.log(' memorix continuity record --taskId <id> --kind decision --content "..."');
|
|
204544
|
+
console.log(' memorix continuity record --taskId <id> --kind verification --verificationStatus passed --content "..."');
|
|
204545
|
+
console.log(" Use --verificationId <id> to transition an existing verification obligation.");
|
|
204546
|
+
console.log(" memorix continuity show --taskId <id>");
|
|
204547
|
+
console.log(" memorix continuity list [--limit 20]");
|
|
204548
|
+
console.log(' memorix continuity close --taskId <id> --status completed --content "..."');
|
|
204549
|
+
} catch (error40) {
|
|
204550
|
+
emitError(error40 instanceof Error ? error40.message : String(error40), asJson);
|
|
204551
|
+
}
|
|
204552
|
+
}
|
|
204553
|
+
});
|
|
204554
|
+
}
|
|
204555
|
+
});
|
|
204556
|
+
|
|
203783
204557
|
// src/cli/commands/reasoning.ts
|
|
203784
204558
|
var reasoning_exports = {};
|
|
203785
204559
|
__export(reasoning_exports, {
|
|
@@ -206980,7 +207754,7 @@ __export(planner_exports, {
|
|
|
206980
207754
|
materializeTaskGraph: () => materializeTaskGraph,
|
|
206981
207755
|
seedAutonomousPipeline: () => seedAutonomousPipeline
|
|
206982
207756
|
});
|
|
206983
|
-
import { randomUUID as
|
|
207757
|
+
import { randomUUID as randomUUID19 } from "crypto";
|
|
206984
207758
|
function isPlannerTask(metadata) {
|
|
206985
207759
|
if (!metadata) return null;
|
|
206986
207760
|
try {
|
|
@@ -207038,7 +207812,7 @@ function seedAutonomousPipeline(teamStore, projectId, config2, opts) {
|
|
|
207038
207812
|
const maxIterations = config2.maxIterations ?? 3;
|
|
207039
207813
|
const taskBudget = config2.taskBudget ?? 15;
|
|
207040
207814
|
const structuredPlan = opts?.structuredPlan ?? true;
|
|
207041
|
-
const pipelineId =
|
|
207815
|
+
const pipelineId = randomUUID19();
|
|
207042
207816
|
const meta = {
|
|
207043
207817
|
plannerType: "plan",
|
|
207044
207818
|
pipelineId,
|
|
@@ -207957,11 +208731,11 @@ __export(receipt_service_exports, {
|
|
|
207957
208731
|
buildHandoffReceipt: () => buildHandoffReceipt,
|
|
207958
208732
|
formatHandoffReceipt: () => formatHandoffReceipt
|
|
207959
208733
|
});
|
|
207960
|
-
import { createHash as
|
|
208734
|
+
import { createHash as createHash19 } from "crypto";
|
|
207961
208735
|
import { existsSync as existsSync23, readFileSync as readFileSync15 } from "fs";
|
|
207962
208736
|
import path37 from "path";
|
|
207963
208737
|
function sha256(value) {
|
|
207964
|
-
return `sha256:${
|
|
208738
|
+
return `sha256:${createHash19("sha256").update(value).digest("hex")}`;
|
|
207965
208739
|
}
|
|
207966
208740
|
function hashId(projectId, id) {
|
|
207967
208741
|
return sha256(`${projectId}:obs:${id}`);
|
|
@@ -208194,6 +208968,7 @@ var init_tool_profile = __esm({
|
|
|
208194
208968
|
// keep the default micro profile at its nine-tool token budget.
|
|
208195
208969
|
memorix_evidence: ["lite", "team", "full"],
|
|
208196
208970
|
memorix_feedback: ["lite", "team", "full"],
|
|
208971
|
+
memorix_continuity: ["lite", "team", "full"],
|
|
208197
208972
|
// ── lite: extended solo memory surface ────────────────────────────
|
|
208198
208973
|
memorix_graph_context: ["lite", "team", "full"],
|
|
208199
208974
|
memorix_timeline: ["lite", "team", "full"],
|
|
@@ -208244,7 +209019,7 @@ var init_tool_profile = __esm({
|
|
|
208244
209019
|
function getMcpServerInfo() {
|
|
208245
209020
|
return {
|
|
208246
209021
|
name: "memorix",
|
|
208247
|
-
version: true ? "1.9.
|
|
209022
|
+
version: true ? "1.9.7" : "1.0.1"
|
|
208248
209023
|
};
|
|
208249
209024
|
}
|
|
208250
209025
|
function createMcpDiscoverResult(capabilities, serverInfo) {
|
|
@@ -215850,8 +216625,8 @@ var require_core = __commonJS({
|
|
|
215850
216625
|
return this;
|
|
215851
216626
|
}
|
|
215852
216627
|
case "object": {
|
|
215853
|
-
const
|
|
215854
|
-
this._cache.delete(
|
|
216628
|
+
const cacheKey4 = schemaKeyRef;
|
|
216629
|
+
this._cache.delete(cacheKey4);
|
|
215855
216630
|
let id = schemaKeyRef[this.opts.schemaId];
|
|
215856
216631
|
if (id) {
|
|
215857
216632
|
id = (0, resolve_1.normalizeId)(id);
|
|
@@ -221039,9 +221814,9 @@ var init_graph_scope = __esm({
|
|
|
221039
221814
|
});
|
|
221040
221815
|
|
|
221041
221816
|
// src/rules/utils.ts
|
|
221042
|
-
import { createHash as
|
|
221817
|
+
import { createHash as createHash20 } from "crypto";
|
|
221043
221818
|
function hashContent2(content) {
|
|
221044
|
-
return
|
|
221819
|
+
return createHash20("sha256").update(content.trim()).digest("hex").substring(0, 16);
|
|
221045
221820
|
}
|
|
221046
221821
|
function generateRuleId(source, filePath) {
|
|
221047
221822
|
const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
|
|
@@ -222630,8 +223405,8 @@ function legacyBehavior(homeDir) {
|
|
|
222630
223405
|
function getBehaviorConfig(options = {}) {
|
|
222631
223406
|
const homeDir = options.homeDir ?? homedir34();
|
|
222632
223407
|
const projectRoot = options.projectRoot === void 0 ? detectProject()?.rootPath ?? null : options.projectRoot;
|
|
222633
|
-
const
|
|
222634
|
-
const cached2 = cache4.get(
|
|
223408
|
+
const cacheKey4 = `${homeDir}\0${projectRoot ?? ""}`;
|
|
223409
|
+
const cached2 = cache4.get(cacheKey4);
|
|
222635
223410
|
if (cached2) return cached2;
|
|
222636
223411
|
const legacy = legacyBehavior(homeDir);
|
|
222637
223412
|
let resolved = {};
|
|
@@ -222645,7 +223420,7 @@ function getBehaviorConfig(options = {}) {
|
|
|
222645
223420
|
autoCleanup: typeof resolved.autoCleanup === "boolean" ? resolved.autoCleanup : typeof legacy.autoCleanup === "boolean" ? legacy.autoCleanup : DEFAULTS.autoCleanup,
|
|
222646
223421
|
formationMode: isFormationMode(resolved.formation) ? resolved.formation : isFormationMode(legacy.formationMode) ? legacy.formationMode : DEFAULTS.formationMode
|
|
222647
223422
|
};
|
|
222648
|
-
cache4.set(
|
|
223423
|
+
cache4.set(cacheKey4, config2);
|
|
222649
223424
|
return config2;
|
|
222650
223425
|
}
|
|
222651
223426
|
function resetBehaviorConfigCache() {
|
|
@@ -264676,7 +265451,7 @@ __export(server_exports2, {
|
|
|
264676
265451
|
createMemorixServer: () => createMemorixServer,
|
|
264677
265452
|
shouldAwaitProjectRuntime: () => shouldAwaitProjectRuntime
|
|
264678
265453
|
});
|
|
264679
|
-
import { createHash as
|
|
265454
|
+
import { createHash as createHash21 } from "crypto";
|
|
264680
265455
|
function formatFormationStageDurations(stageDurationsMs) {
|
|
264681
265456
|
const orderedStages = ["extract", "resolve", "evaluate"];
|
|
264682
265457
|
const parts = orderedStages.filter((stage) => stageDurationsMs[stage] !== void 0).map((stage) => `${stage}=${stageDurationsMs[stage]}ms`);
|
|
@@ -264758,7 +265533,7 @@ function coerceObjectArray(val) {
|
|
|
264758
265533
|
return [];
|
|
264759
265534
|
}
|
|
264760
265535
|
function createDeterministicInstanceId(projectId, agentType, agentName) {
|
|
264761
|
-
const digest2 =
|
|
265536
|
+
const digest2 = createHash21("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
|
|
264762
265537
|
return `auto-${digest2}`;
|
|
264763
265538
|
}
|
|
264764
265539
|
function shouldAwaitProjectRuntime(toolName) {
|
|
@@ -264965,6 +265740,22 @@ async function createMemorixServer(cwd, existingServer, sharedTeam, options = {}
|
|
|
264965
265740
|
}
|
|
264966
265741
|
await projectRuntimeInitPromise;
|
|
264967
265742
|
};
|
|
265743
|
+
const activateProjectRuntime = async () => {
|
|
265744
|
+
if (!projectResolved) return;
|
|
265745
|
+
await initObservationStore(projectDir2);
|
|
265746
|
+
await initMiniSkillStore(projectDir2);
|
|
265747
|
+
await initSessionStore(projectDir2);
|
|
265748
|
+
await initObservations(projectDir2, { skipCorpusLoad: true });
|
|
265749
|
+
try {
|
|
265750
|
+
const { initProjectRoot: initProjectRoot2 } = await Promise.resolve().then(() => (init_yaml_loader(), yaml_loader_exports));
|
|
265751
|
+
const { resetDotenv: resetDotenv2, loadDotenv: loadDotenv2 } = await Promise.resolve().then(() => (init_dotenv_loader(), dotenv_loader_exports));
|
|
265752
|
+
initProjectRoot2(project.rootPath);
|
|
265753
|
+
resetDotenv2();
|
|
265754
|
+
loadDotenv2(project.rootPath);
|
|
265755
|
+
} catch {
|
|
265756
|
+
}
|
|
265757
|
+
initLLM({ projectRoot: project.rootPath });
|
|
265758
|
+
};
|
|
264968
265759
|
let maintenanceWorker = null;
|
|
264969
265760
|
const startProjectMaintenanceWorker = async (autoCleanup = false) => {
|
|
264970
265761
|
maintenanceWorker?.stop();
|
|
@@ -265880,6 +266671,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
265880
266671
|
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.",
|
|
265881
266672
|
inputSchema: {
|
|
265882
266673
|
task: external_exports2.string().optional().describe("Current coding task or question"),
|
|
266674
|
+
taskId: external_exports2.string().max(120).optional().describe("Optional task continuity ledger id to include in the brief"),
|
|
265883
266675
|
refresh: external_exports2.enum(["auto", "always", "never"]).optional().default("auto").describe(
|
|
265884
266676
|
"Code Memory refresh policy. auto refreshes only when missing or stale."
|
|
265885
266677
|
),
|
|
@@ -265904,7 +266696,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
265904
266696
|
limit: external_exports2.number().optional().describe("Reserved for future source limits; current prompt stays compact by default.")
|
|
265905
266697
|
}
|
|
265906
266698
|
},
|
|
265907
|
-
async ({ task, refresh, format, agent }) => {
|
|
266699
|
+
async ({ task, taskId, refresh, format, agent }) => {
|
|
265908
266700
|
const unresolved = requireResolvedProject("build project context for the current project");
|
|
265909
266701
|
if (unresolved) return unresolved;
|
|
265910
266702
|
const [
|
|
@@ -265934,6 +266726,7 @@ _Search mode: ${getLastSearchMode2(project.id)}_`;
|
|
|
265934
266726
|
dataDir: projectDir2,
|
|
265935
266727
|
observations: observations2,
|
|
265936
266728
|
task,
|
|
266729
|
+
taskId,
|
|
265937
266730
|
agent,
|
|
265938
266731
|
refresh: refresh ?? "auto",
|
|
265939
266732
|
reader: getObservationReader(),
|
|
@@ -268359,6 +269152,94 @@ ${json4}
|
|
|
268359
269152
|
}
|
|
268360
269153
|
}
|
|
268361
269154
|
);
|
|
269155
|
+
server.registerTool(
|
|
269156
|
+
"memorix_continuity",
|
|
269157
|
+
{
|
|
269158
|
+
title: "Task Continuity Ledger",
|
|
269159
|
+
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.",
|
|
269160
|
+
inputSchema: {
|
|
269161
|
+
action: external_exports2.enum(["start", "record", "show", "list", "close"]).describe("Continuity operation"),
|
|
269162
|
+
taskId: external_exports2.string().max(120).optional().describe("Continuity task id"),
|
|
269163
|
+
task: external_exports2.string().max(1e3).optional().describe("Task description for start"),
|
|
269164
|
+
requirements: external_exports2.array(external_exports2.string().max(2e3)).max(8).optional().describe("Requirements for start"),
|
|
269165
|
+
kind: external_exports2.enum(["requirement", "decision", "verification", "risk"]).optional().describe("Entry kind for record"),
|
|
269166
|
+
content: external_exports2.string().max(2e3).optional().describe("Requirement, decision, verification, risk, or outcome text"),
|
|
269167
|
+
verificationStatus: external_exports2.enum(["pending", "passed", "failed", "skipped"]).optional(),
|
|
269168
|
+
verificationId: external_exports2.string().max(120).optional().describe("Stable verification obligation id; reuse it to move pending to passed/failed"),
|
|
269169
|
+
idempotencyKey: external_exports2.string().max(200).optional().describe("Stable retry key; repeating the same operation returns the original event"),
|
|
269170
|
+
status: external_exports2.enum(["completed", "blocked", "abandoned"]).optional().describe("Outcome status for close"),
|
|
269171
|
+
sourceRef: external_exports2.string().max(500).optional().describe("Evidence or source reference"),
|
|
269172
|
+
evidenceRefs: external_exports2.array(external_exports2.string().max(500)).max(8).optional(),
|
|
269173
|
+
actor: external_exports2.string().max(200).optional(),
|
|
269174
|
+
limit: external_exports2.number().int().positive().max(100).optional().default(20)
|
|
269175
|
+
}
|
|
269176
|
+
},
|
|
269177
|
+
async ({ action, taskId, task, requirements, kind, content, verificationStatus, verificationId, idempotencyKey, status, sourceRef, evidenceRefs: evidenceRefs2, actor, limit: limit2 }) => {
|
|
269178
|
+
const unresolved = requireResolvedProject("manage task continuity for the current project");
|
|
269179
|
+
if (unresolved) return unresolved;
|
|
269180
|
+
const { TaskContinuityStore: TaskContinuityStore2 } = await Promise.resolve().then(() => (init_task_continuity(), task_continuity_exports));
|
|
269181
|
+
const continuity = new TaskContinuityStore2();
|
|
269182
|
+
await continuity.init(projectDir2);
|
|
269183
|
+
const effectiveActor = actor ?? currentAgentId;
|
|
269184
|
+
try {
|
|
269185
|
+
if (action === "start") {
|
|
269186
|
+
if (!task?.trim()) return { content: [{ type: "text", text: "task is required for continuity start." }], isError: true };
|
|
269187
|
+
const result2 = continuity.start({
|
|
269188
|
+
projectId: project.id,
|
|
269189
|
+
task,
|
|
269190
|
+
...taskId ? { taskId } : {},
|
|
269191
|
+
requirements,
|
|
269192
|
+
...effectiveActor ? { actor: effectiveActor } : {},
|
|
269193
|
+
...idempotencyKey ? { idempotencyKey } : {}
|
|
269194
|
+
});
|
|
269195
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
|
|
269196
|
+
}
|
|
269197
|
+
if (action === "list") {
|
|
269198
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledgers: continuity.list(project.id, limit2) }, null, 2) }] };
|
|
269199
|
+
}
|
|
269200
|
+
if (!taskId?.trim()) return { content: [{ type: "text", text: "taskId is required for this continuity action." }], isError: true };
|
|
269201
|
+
if (action === "show") {
|
|
269202
|
+
const ledger = continuity.get(project.id, taskId);
|
|
269203
|
+
if (!ledger) return { content: [{ type: "text", text: "Task continuity ledger not found." }], isError: true };
|
|
269204
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ledger }, null, 2) }] };
|
|
269205
|
+
}
|
|
269206
|
+
if (action === "record") {
|
|
269207
|
+
if (!kind || !content?.trim()) {
|
|
269208
|
+
return { content: [{ type: "text", text: "record requires kind and content." }], isError: true };
|
|
269209
|
+
}
|
|
269210
|
+
const result2 = continuity.record({
|
|
269211
|
+
projectId: project.id,
|
|
269212
|
+
taskId,
|
|
269213
|
+
kind,
|
|
269214
|
+
content,
|
|
269215
|
+
...verificationStatus ? { verificationStatus } : {},
|
|
269216
|
+
...sourceRef ? { sourceRef } : {},
|
|
269217
|
+
...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
|
|
269218
|
+
...effectiveActor ? { actor: effectiveActor } : {},
|
|
269219
|
+
...verificationId ? { verificationId } : {},
|
|
269220
|
+
...idempotencyKey ? { idempotencyKey } : {}
|
|
269221
|
+
});
|
|
269222
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result2 }, null, 2) }] };
|
|
269223
|
+
}
|
|
269224
|
+
if (!status || !content?.trim()) {
|
|
269225
|
+
return { content: [{ type: "text", text: "close requires status and content." }], isError: true };
|
|
269226
|
+
}
|
|
269227
|
+
const result = continuity.close({
|
|
269228
|
+
projectId: project.id,
|
|
269229
|
+
taskId,
|
|
269230
|
+
status,
|
|
269231
|
+
content,
|
|
269232
|
+
...sourceRef ? { sourceRef } : {},
|
|
269233
|
+
...evidenceRefs2 ? { evidenceRefs: evidenceRefs2 } : {},
|
|
269234
|
+
...effectiveActor ? { actor: effectiveActor } : {},
|
|
269235
|
+
...idempotencyKey ? { idempotencyKey } : {}
|
|
269236
|
+
});
|
|
269237
|
+
return { content: [{ type: "text", text: JSON.stringify({ projectId: project.id, ...result }, null, 2) }] };
|
|
269238
|
+
} catch (error40) {
|
|
269239
|
+
return { content: [{ type: "text", text: error40 instanceof Error ? error40.message : String(error40) }], isError: true };
|
|
269240
|
+
}
|
|
269241
|
+
}
|
|
269242
|
+
);
|
|
268362
269243
|
if (teamFeaturesEnabled) {
|
|
268363
269244
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
268364
269245
|
initTeamStoreForProject = initTeamStore2;
|
|
@@ -269386,7 +270267,9 @@ fi
|
|
|
269386
270267
|
graphManager,
|
|
269387
270268
|
projectId: project.id,
|
|
269388
270269
|
deferredInit,
|
|
270270
|
+
activateProjectRuntime,
|
|
269389
270271
|
switchProject,
|
|
270272
|
+
getProjectDataDir: () => projectDir2,
|
|
269390
270273
|
isExplicitlyBound: () => projectBinding.isExplicit(),
|
|
269391
270274
|
getRequestContext,
|
|
269392
270275
|
handleTransportClose
|
|
@@ -269462,13 +270345,21 @@ var init_server4 = __esm({
|
|
|
269462
270345
|
// src/server/modern-mcp-bridge.ts
|
|
269463
270346
|
var modern_mcp_bridge_exports = {};
|
|
269464
270347
|
__export(modern_mcp_bridge_exports, {
|
|
269465
|
-
|
|
270348
|
+
ModernMcpRuntimePool: () => ModernMcpRuntimePool,
|
|
270349
|
+
createModernMcpBridge: () => createModernMcpBridge,
|
|
270350
|
+
createModernMcpRuntime: () => createModernMcpRuntime,
|
|
270351
|
+
withBusinessRuntimeQueue: () => withBusinessRuntimeQueue
|
|
269466
270352
|
});
|
|
269467
270353
|
import {
|
|
269468
270354
|
fromJsonSchema,
|
|
269469
270355
|
McpServer as ModernMcpServer
|
|
269470
270356
|
} from "@modelcontextprotocol/server";
|
|
269471
|
-
|
|
270357
|
+
function withBusinessRuntimeQueue(work) {
|
|
270358
|
+
const result = businessRuntimeQueue.then(work);
|
|
270359
|
+
businessRuntimeQueue = result.catch(() => void 0);
|
|
270360
|
+
return result;
|
|
270361
|
+
}
|
|
270362
|
+
async function createModernMcpRuntime(options) {
|
|
269472
270363
|
const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
|
|
269473
270364
|
const legacy = await createMemorixServer2(
|
|
269474
270365
|
options.projectRoot,
|
|
@@ -269489,41 +270380,62 @@ async function createModernMcpBridge(options) {
|
|
|
269489
270380
|
await legacy.server.connect(serverTransport);
|
|
269490
270381
|
await legacyClient.connect(clientTransport);
|
|
269491
270382
|
const listed = await legacyClient.listTools();
|
|
269492
|
-
const
|
|
269493
|
-
|
|
269494
|
-
|
|
269495
|
-
|
|
269496
|
-
|
|
269497
|
-
...tool.title ? { title: tool.title } : {},
|
|
269498
|
-
...tool.description ? { description: tool.description } : {},
|
|
269499
|
-
inputSchema: fromJsonSchema(tool.inputSchema)
|
|
269500
|
-
},
|
|
269501
|
-
async (args) => await legacyClient.callTool({
|
|
269502
|
-
name: tool.name,
|
|
269503
|
-
arguments: args
|
|
269504
|
-
})
|
|
269505
|
-
);
|
|
269506
|
-
}
|
|
269507
|
-
const bridge = modern;
|
|
269508
|
-
bridge.memorix = {
|
|
270383
|
+
const callTool = (name, args) => withBusinessRuntimeQueue(async () => {
|
|
270384
|
+
await legacy.activateProjectRuntime();
|
|
270385
|
+
return await legacyClient.callTool({ name, arguments: args });
|
|
270386
|
+
});
|
|
270387
|
+
const lifecycle = {
|
|
269509
270388
|
projectId: legacy.projectId,
|
|
269510
270389
|
deferredInit: legacy.deferredInit,
|
|
270390
|
+
activateProjectRuntime: legacy.activateProjectRuntime,
|
|
269511
270391
|
switchProject: legacy.switchProject,
|
|
270392
|
+
getProjectDataDir: legacy.getProjectDataDir,
|
|
269512
270393
|
isExplicitlyBound: legacy.isExplicitlyBound,
|
|
269513
270394
|
handleTransportClose: legacy.handleTransportClose
|
|
269514
270395
|
};
|
|
269515
270396
|
let closed = false;
|
|
269516
|
-
|
|
269517
|
-
|
|
270397
|
+
return {
|
|
270398
|
+
projectId: legacy.projectId,
|
|
270399
|
+
getProjectDataDir: legacy.getProjectDataDir,
|
|
270400
|
+
createBridge: () => {
|
|
270401
|
+
const requestServer = new ModernMcpServer(getMcpServerInfo());
|
|
270402
|
+
for (const tool of listed.tools) {
|
|
270403
|
+
requestServer.registerTool(
|
|
270404
|
+
tool.name,
|
|
270405
|
+
{
|
|
270406
|
+
...tool.title ? { title: tool.title } : {},
|
|
270407
|
+
...tool.description ? { description: tool.description } : {},
|
|
270408
|
+
inputSchema: fromJsonSchema(tool.inputSchema)
|
|
270409
|
+
},
|
|
270410
|
+
async (args) => await callTool(tool.name, args)
|
|
270411
|
+
);
|
|
270412
|
+
}
|
|
270413
|
+
requestServer.memorix = lifecycle;
|
|
270414
|
+
return requestServer;
|
|
270415
|
+
},
|
|
270416
|
+
close: async () => {
|
|
270417
|
+
if (closed) return;
|
|
270418
|
+
closed = true;
|
|
270419
|
+
await legacyClient.close().catch(() => void 0);
|
|
270420
|
+
await legacy.server.close().catch(() => void 0);
|
|
270421
|
+
legacy.handleTransportClose();
|
|
270422
|
+
}
|
|
270423
|
+
};
|
|
270424
|
+
}
|
|
270425
|
+
async function createModernMcpBridge(options) {
|
|
270426
|
+
const runtime = await createModernMcpRuntime(options);
|
|
270427
|
+
const bridge = runtime.createBridge();
|
|
270428
|
+
const close = bridge.close.bind(bridge);
|
|
270429
|
+
let closed = false;
|
|
270430
|
+
bridge.close = async () => {
|
|
269518
270431
|
if (closed) return;
|
|
269519
270432
|
closed = true;
|
|
269520
|
-
await
|
|
269521
|
-
await
|
|
269522
|
-
await legacy.server.close().catch(() => void 0);
|
|
269523
|
-
legacy.handleTransportClose();
|
|
270433
|
+
await close();
|
|
270434
|
+
await runtime.close();
|
|
269524
270435
|
};
|
|
269525
270436
|
return bridge;
|
|
269526
270437
|
}
|
|
270438
|
+
var businessRuntimeQueue, ModernMcpRuntimePool;
|
|
269527
270439
|
var init_modern_mcp_bridge = __esm({
|
|
269528
270440
|
"src/server/modern-mcp-bridge.ts"() {
|
|
269529
270441
|
"use strict";
|
|
@@ -269531,6 +270443,74 @@ var init_modern_mcp_bridge = __esm({
|
|
|
269531
270443
|
init_client4();
|
|
269532
270444
|
init_inMemory();
|
|
269533
270445
|
init_mcp_discovery();
|
|
270446
|
+
businessRuntimeQueue = Promise.resolve();
|
|
270447
|
+
ModernMcpRuntimePool = class {
|
|
270448
|
+
constructor(maxEntries = 32) {
|
|
270449
|
+
this.maxEntries = maxEntries;
|
|
270450
|
+
}
|
|
270451
|
+
maxEntries;
|
|
270452
|
+
entries = /* @__PURE__ */ new Map();
|
|
270453
|
+
pending = /* @__PURE__ */ new Map();
|
|
270454
|
+
async createBridge(key, create6) {
|
|
270455
|
+
let entry = this.entries.get(key);
|
|
270456
|
+
if (!entry) {
|
|
270457
|
+
let pending = this.pending.get(key);
|
|
270458
|
+
if (!pending) {
|
|
270459
|
+
pending = create6().then((runtime) => {
|
|
270460
|
+
const created = { runtime, refs: 0, lastUsedAt: Date.now() };
|
|
270461
|
+
this.entries.set(key, created);
|
|
270462
|
+
return created;
|
|
270463
|
+
});
|
|
270464
|
+
this.pending.set(key, pending);
|
|
270465
|
+
}
|
|
270466
|
+
try {
|
|
270467
|
+
entry = await pending;
|
|
270468
|
+
} finally {
|
|
270469
|
+
this.pending.delete(key);
|
|
270470
|
+
}
|
|
270471
|
+
}
|
|
270472
|
+
entry.refs++;
|
|
270473
|
+
entry.lastUsedAt = Date.now();
|
|
270474
|
+
const bridge = entry.runtime.createBridge();
|
|
270475
|
+
const close = bridge.close.bind(bridge);
|
|
270476
|
+
let released = false;
|
|
270477
|
+
bridge.close = async () => {
|
|
270478
|
+
try {
|
|
270479
|
+
await close();
|
|
270480
|
+
} finally {
|
|
270481
|
+
if (!released) {
|
|
270482
|
+
released = true;
|
|
270483
|
+
entry.refs = Math.max(0, entry.refs - 1);
|
|
270484
|
+
entry.lastUsedAt = Date.now();
|
|
270485
|
+
}
|
|
270486
|
+
}
|
|
270487
|
+
};
|
|
270488
|
+
return bridge;
|
|
270489
|
+
}
|
|
270490
|
+
async evictIdle() {
|
|
270491
|
+
let closed = 0;
|
|
270492
|
+
const idle = [...this.entries.entries()].filter(([, entry]) => entry.refs === 0).sort(([, left], [, right]) => left.lastUsedAt - right.lastUsedAt);
|
|
270493
|
+
for (const [key, entry] of idle) {
|
|
270494
|
+
if (this.entries.size <= this.maxEntries || entry.refs > 0) continue;
|
|
270495
|
+
this.entries.delete(key);
|
|
270496
|
+
await entry.runtime.close();
|
|
270497
|
+
closed++;
|
|
270498
|
+
}
|
|
270499
|
+
return closed;
|
|
270500
|
+
}
|
|
270501
|
+
async close() {
|
|
270502
|
+
const entries = [...this.entries.values()];
|
|
270503
|
+
this.entries.clear();
|
|
270504
|
+
await Promise.all(entries.map((entry) => entry.runtime.close().catch(() => void 0)));
|
|
270505
|
+
}
|
|
270506
|
+
stats() {
|
|
270507
|
+
return {
|
|
270508
|
+
entries: this.entries.size,
|
|
270509
|
+
activeRefs: [...this.entries.values()].reduce((sum, entry) => sum + entry.refs, 0),
|
|
270510
|
+
pending: this.pending.size
|
|
270511
|
+
};
|
|
270512
|
+
}
|
|
270513
|
+
};
|
|
269534
270514
|
}
|
|
269535
270515
|
});
|
|
269536
270516
|
|
|
@@ -270159,7 +271139,7 @@ function writeFromReadableStream(stream2, writable) {
|
|
|
270159
271139
|
else if (writable.destroyed) return;
|
|
270160
271140
|
return writeFromReadableStreamDefaultReader(stream2.getReader(), writable);
|
|
270161
271141
|
}
|
|
270162
|
-
var RequestError, reValidRequestUrl, reDotSegment, reValidHost, buildUrl, toRequestError, GlobalRequest, Request$1, newHeadersFromIncoming, wrapBodyStream, byteExactEncodings, isByteExactEncoding, bodyBufferedBeforeDisconnectKey, bodyBufferedLengthBeforeDisconnectKey, toBufferChunk, isRecoverableDisconnectedIncoming, recordBodyBufferedBeforeDisconnect, readBodyBufferedBeforeDisconnect, enqueueBufferedBody, newRequestFromIncoming, getRequestCache, requestCache, incomingKey, urlKey, methodKey, headersKey, abortControllerKey, getAbortController, abortRequest, bodyBufferKey, bodyReadPromiseKey, bodyConsumedDirectlyKey, bodyLockReaderKey, abortReasonKey, newBodyUnusableError, rejectBodyUnusable, textDecoder2, consumeBodyDirectOnce, toArrayBuffer, contentType, methodTokenRegExp, normalizeIncomingMethod, validateDirectReadMethod, readBodyWithFastPath, readRawBodyIfAvailable, normalizeAbortError, readBodyDirect, requestPrototype, newRequest, defaultContentType, responseCache, getResponseCache,
|
|
271142
|
+
var RequestError, reValidRequestUrl, reDotSegment, reValidHost, buildUrl, toRequestError, GlobalRequest, Request$1, newHeadersFromIncoming, wrapBodyStream, byteExactEncodings, isByteExactEncoding, bodyBufferedBeforeDisconnectKey, bodyBufferedLengthBeforeDisconnectKey, toBufferChunk, isRecoverableDisconnectedIncoming, recordBodyBufferedBeforeDisconnect, readBodyBufferedBeforeDisconnect, enqueueBufferedBody, newRequestFromIncoming, getRequestCache, requestCache, incomingKey, urlKey, methodKey, headersKey, abortControllerKey, getAbortController, abortRequest, bodyBufferKey, bodyReadPromiseKey, bodyConsumedDirectlyKey, bodyLockReaderKey, abortReasonKey, newBodyUnusableError, rejectBodyUnusable, textDecoder2, consumeBodyDirectOnce, toArrayBuffer, contentType, methodTokenRegExp, normalizeIncomingMethod, validateDirectReadMethod, readBodyWithFastPath, readRawBodyIfAvailable, normalizeAbortError, readBodyDirect, requestPrototype, newRequest, defaultContentType, responseCache, getResponseCache, cacheKey3, GlobalResponse, Response$1, validRedirectUrl, parseRedirectUrl, validRedirectStatuses, buildOutgoingHttpHeaders, outgoingEnded, incomingDraining, DRAIN_TIMEOUT_MS, MAX_DRAIN_BYTES, drainIncoming, makeCloseHandler, isImmediateCacheableResponse, handleRequestError, handleFetchError, handleResponseError, flushHeaders, responseViaCache, isPromise, responseViaResponseObject, getRequestListener, CloseEvent, ErrorEvent, generateConnectionSymbol, CONNECTION_SYMBOL_KEY, WAIT_FOR_WEBSOCKET_SYMBOL, upgradeWebSocket;
|
|
270163
271143
|
var init_dist8 = __esm({
|
|
270164
271144
|
"node_modules/@hono/node-server/dist/index.mjs"() {
|
|
270165
271145
|
"use strict";
|
|
@@ -270630,15 +271610,15 @@ var init_dist8 = __esm({
|
|
|
270630
271610
|
defaultContentType = "text/plain; charset=UTF-8";
|
|
270631
271611
|
responseCache = /* @__PURE__ */ Symbol("responseCache");
|
|
270632
271612
|
getResponseCache = /* @__PURE__ */ Symbol("getResponseCache");
|
|
270633
|
-
|
|
271613
|
+
cacheKey3 = /* @__PURE__ */ Symbol("cache");
|
|
270634
271614
|
GlobalResponse = global.Response;
|
|
270635
271615
|
Response$1 = class Response$12 {
|
|
270636
271616
|
#body;
|
|
270637
271617
|
#init;
|
|
270638
271618
|
[getResponseCache]() {
|
|
270639
|
-
const cache5 = this[
|
|
271619
|
+
const cache5 = this[cacheKey3];
|
|
270640
271620
|
const liveHeaders = cache5 && cache5[2] instanceof Headers ? cache5[2] : void 0;
|
|
270641
|
-
delete this[
|
|
271621
|
+
delete this[cacheKey3];
|
|
270642
271622
|
return this[responseCache] ||= new GlobalResponse(this.#body, liveHeaders ? {
|
|
270643
271623
|
status: this.#init?.status,
|
|
270644
271624
|
statusText: this.#init?.statusText,
|
|
@@ -270658,14 +271638,14 @@ var init_dist8 = __esm({
|
|
|
270658
271638
|
this.#init = init2 instanceof Response$12 ? init2.#init : init2;
|
|
270659
271639
|
headers = new Headers(init2.headers);
|
|
270660
271640
|
} else this.#init = init2;
|
|
270661
|
-
if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[
|
|
271641
|
+
if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey3] = [
|
|
270662
271642
|
init2?.status || 200,
|
|
270663
271643
|
body ?? null,
|
|
270664
271644
|
headers || init2?.headers
|
|
270665
271645
|
];
|
|
270666
271646
|
}
|
|
270667
271647
|
get headers() {
|
|
270668
|
-
const cache5 = this[
|
|
271648
|
+
const cache5 = this[cacheKey3];
|
|
270669
271649
|
if (cache5) {
|
|
270670
271650
|
if (!(cache5[2] instanceof Headers)) cache5[2] = new Headers(cache5[2] || (cache5[1] === null ? void 0 : { "content-type": defaultContentType }));
|
|
270671
271651
|
return cache5[2];
|
|
@@ -270673,7 +271653,7 @@ var init_dist8 = __esm({
|
|
|
270673
271653
|
return this[getResponseCache]().headers;
|
|
270674
271654
|
}
|
|
270675
271655
|
get status() {
|
|
270676
|
-
return this[
|
|
271656
|
+
return this[cacheKey3]?.[0] ?? this[getResponseCache]().status;
|
|
270677
271657
|
}
|
|
270678
271658
|
get ok() {
|
|
270679
271659
|
const status = this.status;
|
|
@@ -270826,8 +271806,8 @@ var init_dist8 = __esm({
|
|
|
270826
271806
|
});
|
|
270827
271807
|
};
|
|
270828
271808
|
isImmediateCacheableResponse = (res) => {
|
|
270829
|
-
if (!(
|
|
270830
|
-
const body = res[
|
|
271809
|
+
if (!(cacheKey3 in res)) return false;
|
|
271810
|
+
const body = res[cacheKey3][1];
|
|
270831
271811
|
return body === null || typeof body === "string" || body instanceof Uint8Array;
|
|
270832
271812
|
};
|
|
270833
271813
|
handleRequestError = () => new Response(null, { status: 400 });
|
|
@@ -270846,7 +271826,7 @@ var init_dist8 = __esm({
|
|
|
270846
271826
|
if ("flushHeaders" in outgoing && outgoing.writable) outgoing.flushHeaders();
|
|
270847
271827
|
};
|
|
270848
271828
|
responseViaCache = async (res, outgoing) => {
|
|
270849
|
-
let [status, body, header] = res[
|
|
271829
|
+
let [status, body, header] = res[cacheKey3];
|
|
270850
271830
|
if (!header) {
|
|
270851
271831
|
if (body === null) {
|
|
270852
271832
|
outgoing.writeHead(status);
|
|
@@ -270914,7 +271894,7 @@ var init_dist8 = __esm({
|
|
|
270914
271894
|
res = errRes;
|
|
270915
271895
|
}
|
|
270916
271896
|
else res = await res.catch(handleFetchError);
|
|
270917
|
-
if (
|
|
271897
|
+
if (cacheKey3 in res) return responseViaCache(res, outgoing);
|
|
270918
271898
|
const resHeaderRecord = buildOutgoingHttpHeaders(res.headers, res.body === null ? void 0 : defaultContentType);
|
|
270919
271899
|
if (res.body) {
|
|
270920
271900
|
const reader2 = res.body.getReader();
|
|
@@ -272151,7 +273131,7 @@ __export(mcp_binding_store_exports, {
|
|
|
272151
273131
|
DEFAULT_MCP_BINDING_TTL_MS: () => DEFAULT_MCP_BINDING_TTL_MS,
|
|
272152
273132
|
McpBindingStore: () => McpBindingStore
|
|
272153
273133
|
});
|
|
272154
|
-
import { randomUUID as
|
|
273134
|
+
import { randomUUID as randomUUID20 } from "crypto";
|
|
272155
273135
|
function rowToBinding(row) {
|
|
272156
273136
|
return {
|
|
272157
273137
|
handleId: String(row.handle_id),
|
|
@@ -272183,7 +273163,7 @@ var init_mcp_binding_store = __esm({
|
|
|
272183
273163
|
const now3 = /* @__PURE__ */ new Date();
|
|
272184
273164
|
const ttlMs = input.ttlMs ?? DEFAULT_MCP_BINDING_TTL_MS;
|
|
272185
273165
|
const binding = {
|
|
272186
|
-
handleId: `mxh_${
|
|
273166
|
+
handleId: `mxh_${randomUUID20().replace(/-/g, "")}`,
|
|
272187
273167
|
projectId: input.projectId,
|
|
272188
273168
|
projectRoot: input.projectRoot,
|
|
272189
273169
|
dataDir: input.dataDir,
|
|
@@ -272310,10 +273290,14 @@ var init_control_plane_maintenance = __esm({
|
|
|
272310
273290
|
var serve_http_exports = {};
|
|
272311
273291
|
__export(serve_http_exports, {
|
|
272312
273292
|
DEFAULT_HTTP_BODY_LIMIT_BYTES: () => DEFAULT_HTTP_BODY_LIMIT_BYTES,
|
|
273293
|
+
DEFAULT_MAX_HTTP_SESSIONS: () => DEFAULT_MAX_HTTP_SESSIONS,
|
|
272313
273294
|
DEFAULT_SESSION_TIMEOUT_MS: () => DEFAULT_SESSION_TIMEOUT_MS,
|
|
272314
273295
|
EXPIRED_SESSION_TTL_MS: () => EXPIRED_SESSION_TTL_MS,
|
|
272315
273296
|
HttpPayloadTooLargeError: () => HttpPayloadTooLargeError,
|
|
272316
273297
|
MAX_HTTP_BODY_LIMIT_BYTES: () => MAX_HTTP_BODY_LIMIT_BYTES,
|
|
273298
|
+
MAX_HTTP_SESSIONS_LIMIT: () => MAX_HTTP_SESSIONS_LIMIT,
|
|
273299
|
+
MAX_PROJECT_CACHE_ENTRIES: () => MAX_PROJECT_CACHE_ENTRIES,
|
|
273300
|
+
MAX_STORE_CACHE_ENTRIES: () => MAX_STORE_CACHE_ENTRIES,
|
|
272317
273301
|
MCP_SDK_COMPAT_PROTOCOL_VERSION: () => MCP_SDK_COMPAT_PROTOCOL_VERSION,
|
|
272318
273302
|
MIN_HTTP_BODY_LIMIT_BYTES: () => MIN_HTTP_BODY_LIMIT_BYTES,
|
|
272319
273303
|
_testing: () => _testing2,
|
|
@@ -272322,11 +273306,13 @@ __export(serve_http_exports, {
|
|
|
272322
273306
|
isServerDiscoverRequest: () => isServerDiscoverRequest,
|
|
272323
273307
|
parseHttpBodyLimit: () => parseHttpBodyLimit,
|
|
272324
273308
|
parseJsonBody: () => parseJsonBody,
|
|
273309
|
+
parseMaxHttpSessions: () => parseMaxHttpSessions,
|
|
272325
273310
|
parseSessionTimeoutMs: () => parseSessionTimeoutMs,
|
|
272326
273311
|
resolveServeHttpDefaults: () => resolveServeHttpDefaults,
|
|
272327
273312
|
validateHttpBindSecurity: () => validateHttpBindSecurity
|
|
272328
273313
|
});
|
|
272329
273314
|
import { getHeapStatistics } from "v8";
|
|
273315
|
+
import { monitorEventLoopDelay } from "perf_hooks";
|
|
272330
273316
|
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
272331
273317
|
function isServerDiscoverRequest(body) {
|
|
272332
273318
|
return Boolean(body && typeof body === "object" && !Array.isArray(body) && body.method === "server/discover");
|
|
@@ -272347,6 +273333,13 @@ function parseHttpBodyLimit(raw) {
|
|
|
272347
273333
|
}
|
|
272348
273334
|
return Math.min(parsed, MAX_HTTP_BODY_LIMIT_BYTES);
|
|
272349
273335
|
}
|
|
273336
|
+
function parseMaxHttpSessions(raw) {
|
|
273337
|
+
const value = raw?.trim();
|
|
273338
|
+
if (!value) return DEFAULT_MAX_HTTP_SESSIONS;
|
|
273339
|
+
const parsed = Number(value);
|
|
273340
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) return DEFAULT_MAX_HTTP_SESSIONS;
|
|
273341
|
+
return Math.min(parsed, MAX_HTTP_SESSIONS_LIMIT);
|
|
273342
|
+
}
|
|
272350
273343
|
function isLoopbackHost(host) {
|
|
272351
273344
|
const normalized = host.trim().toLowerCase();
|
|
272352
273345
|
return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]";
|
|
@@ -272419,7 +273412,7 @@ function resolveServeHttpDefaults(argsPort, configured) {
|
|
|
272419
273412
|
const transportNotice = configured.transport === "stdio" ? '[memorix] config server.transport = "stdio"; serve-http continues on HTTP (the explicit command wins).' : void 0;
|
|
272420
273413
|
return { port, transportNotice };
|
|
272421
273414
|
}
|
|
272422
|
-
var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, DEFAULT_HTTP_BODY_LIMIT_BYTES, MAX_HTTP_BODY_LIMIT_BYTES, MIN_HTTP_BODY_LIMIT_BYTES, MCP_SDK_COMPAT_PROTOCOL_VERSION, HttpPayloadTooLargeError, serve_http_default, _testing2;
|
|
273415
|
+
var DEFAULT_SESSION_TIMEOUT_MS, EXPIRED_SESSION_TTL_MS, DEFAULT_HTTP_BODY_LIMIT_BYTES, MAX_HTTP_BODY_LIMIT_BYTES, MIN_HTTP_BODY_LIMIT_BYTES, MAX_PROJECT_CACHE_ENTRIES, MAX_STORE_CACHE_ENTRIES, DEFAULT_MAX_HTTP_SESSIONS, MAX_HTTP_SESSIONS_LIMIT, MCP_SDK_COMPAT_PROTOCOL_VERSION, HttpPayloadTooLargeError, serve_http_default, _testing2;
|
|
272423
273416
|
var init_serve_http = __esm({
|
|
272424
273417
|
"src/cli/commands/serve-http.ts"() {
|
|
272425
273418
|
"use strict";
|
|
@@ -272438,6 +273431,10 @@ var init_serve_http = __esm({
|
|
|
272438
273431
|
DEFAULT_HTTP_BODY_LIMIT_BYTES = 10 * 1024 * 1024;
|
|
272439
273432
|
MAX_HTTP_BODY_LIMIT_BYTES = 64 * 1024 * 1024;
|
|
272440
273433
|
MIN_HTTP_BODY_LIMIT_BYTES = 64 * 1024;
|
|
273434
|
+
MAX_PROJECT_CACHE_ENTRIES = 256;
|
|
273435
|
+
MAX_STORE_CACHE_ENTRIES = 32;
|
|
273436
|
+
DEFAULT_MAX_HTTP_SESSIONS = 64;
|
|
273437
|
+
MAX_HTTP_SESSIONS_LIMIT = 512;
|
|
272441
273438
|
MCP_SDK_COMPAT_PROTOCOL_VERSION = "2025-11-25";
|
|
272442
273439
|
HttpPayloadTooLargeError = class extends Error {
|
|
272443
273440
|
statusCode = 413;
|
|
@@ -272475,7 +273472,7 @@ var init_serve_http = __esm({
|
|
|
272475
273472
|
},
|
|
272476
273473
|
run: async ({ args }) => {
|
|
272477
273474
|
const { createServer: createServer3 } = await import("http");
|
|
272478
|
-
const { randomUUID:
|
|
273475
|
+
const { randomUUID: randomUUID23 } = await import("crypto");
|
|
272479
273476
|
const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp(), streamableHttp_exports));
|
|
272480
273477
|
const { isInitializeRequest: isInitializeRequest2 } = await Promise.resolve().then(() => (init_types8(), types_exports3));
|
|
272481
273478
|
const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
|
|
@@ -272485,7 +273482,8 @@ var init_serve_http = __esm({
|
|
|
272485
273482
|
const { homedir: homedir38 } = await import("os");
|
|
272486
273483
|
const { McpBindingStore: McpBindingStore2 } = await Promise.resolve().then(() => (init_mcp_binding_store(), mcp_binding_store_exports));
|
|
272487
273484
|
const { MemoryFeedbackStore: MemoryFeedbackStore2 } = await Promise.resolve().then(() => (init_feedback(), feedback_exports));
|
|
272488
|
-
const {
|
|
273485
|
+
const { createModernMcpRuntime: createModernMcpRuntime2, ModernMcpRuntimePool: ModernMcpRuntimePool2, withBusinessRuntimeQueue: withBusinessRuntimeQueue2 } = await Promise.resolve().then(() => (init_modern_mcp_bridge(), modern_mcp_bridge_exports));
|
|
273486
|
+
const { acquireDatabase: acquireDatabase2, evictIdleDatabases: evictIdleDatabases2, getDatabaseStats: getDatabaseStats2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
|
|
272489
273487
|
const { createMcpHandler } = await import("@modelcontextprotocol/server");
|
|
272490
273488
|
const { toNodeHandler } = await import("@modelcontextprotocol/node");
|
|
272491
273489
|
let configuredDefaults = {};
|
|
@@ -272537,42 +273535,108 @@ var init_serve_http = __esm({
|
|
|
272537
273535
|
console.error(`[memorix] Project root: ${projectRoot}`);
|
|
272538
273536
|
const sessionTimeoutMs = parseSessionTimeoutMs(process.env.MEMORIX_SESSION_TIMEOUT_MS);
|
|
272539
273537
|
const httpBodyLimitBytes = parseHttpBodyLimit(process.env.MEMORIX_HTTP_MAX_BODY_BYTES);
|
|
273538
|
+
const maxHttpSessions = parseMaxHttpSessions(process.env.MEMORIX_MAX_HTTP_SESSIONS);
|
|
273539
|
+
const eventLoopDelay = monitorEventLoopDelay({ resolution: 20 });
|
|
273540
|
+
eventLoopDelay.enable();
|
|
272540
273541
|
console.error(
|
|
272541
273542
|
sessionTimeoutMs === 0 ? "[memorix] HTTP session idle timeout: disabled" : `[memorix] HTTP session idle timeout: ${Math.round(sessionTimeoutMs / 6e4)}min (${sessionTimeoutMs}ms)`
|
|
272542
273543
|
);
|
|
273544
|
+
console.error(`[memorix] HTTP session limit: ${maxHttpSessions}`);
|
|
272543
273545
|
const teamStoreCache = /* @__PURE__ */ new Map();
|
|
273546
|
+
const teamStoreLeases = /* @__PURE__ */ new Map();
|
|
272544
273547
|
const { initTeamStore: initTeamStore2 } = await Promise.resolve().then(() => (init_team_store(), team_store_exports));
|
|
273548
|
+
function cacheSet3(cache5, key, value, limit2, onEvict) {
|
|
273549
|
+
cache5.delete(key);
|
|
273550
|
+
cache5.set(key, value);
|
|
273551
|
+
while (cache5.size > limit2) {
|
|
273552
|
+
const oldest = cache5.keys().next().value;
|
|
273553
|
+
if (oldest === void 0) break;
|
|
273554
|
+
const oldValue = cache5.get(oldest);
|
|
273555
|
+
cache5.delete(oldest);
|
|
273556
|
+
if (oldValue !== void 0) onEvict?.(oldest, oldValue);
|
|
273557
|
+
}
|
|
273558
|
+
}
|
|
272545
273559
|
async function getTeamStore2(dataDir) {
|
|
272546
273560
|
const existing = teamStoreCache.get(dataDir);
|
|
272547
|
-
if (existing)
|
|
272548
|
-
|
|
272549
|
-
|
|
272550
|
-
|
|
273561
|
+
if (existing) {
|
|
273562
|
+
cacheSet3(teamStoreCache, dataDir, existing, MAX_STORE_CACHE_ENTRIES);
|
|
273563
|
+
return existing;
|
|
273564
|
+
}
|
|
273565
|
+
const lease = acquireDatabase2(dataDir);
|
|
273566
|
+
try {
|
|
273567
|
+
const store2 = await initTeamStore2(dataDir);
|
|
273568
|
+
teamStoreLeases.set(dataDir, lease);
|
|
273569
|
+
cacheSet3(teamStoreCache, dataDir, store2, MAX_STORE_CACHE_ENTRIES, (evictedDataDir) => {
|
|
273570
|
+
teamStoreLeases.get(evictedDataDir)?.release();
|
|
273571
|
+
teamStoreLeases.delete(evictedDataDir);
|
|
273572
|
+
evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
|
|
273573
|
+
});
|
|
273574
|
+
return store2;
|
|
273575
|
+
} catch (error40) {
|
|
273576
|
+
lease.release();
|
|
273577
|
+
throw error40;
|
|
273578
|
+
}
|
|
272551
273579
|
}
|
|
273580
|
+
const controlPlaneBaseDir = persistMod.getBaseDataDir();
|
|
273581
|
+
const controlPlaneBaseLease = acquireDatabase2(controlPlaneBaseDir);
|
|
272552
273582
|
const statelessBindingStore = new McpBindingStore2();
|
|
272553
|
-
await statelessBindingStore.init(
|
|
273583
|
+
await statelessBindingStore.init(controlPlaneBaseDir);
|
|
273584
|
+
const modernRuntimePool = new ModernMcpRuntimePool2(MAX_STORE_CACHE_ENTRIES);
|
|
273585
|
+
async function resolveModernDataDir(root, persisted) {
|
|
273586
|
+
if (persisted?.dataDir) return persisted.dataDir;
|
|
273587
|
+
const detected = detectorMod.detectProject(root);
|
|
273588
|
+
return detected ? persistMod.getProjectDataDir(detected.id) : controlPlaneBaseDir;
|
|
273589
|
+
}
|
|
272554
273590
|
const modernMcpHandler = createMcpHandler(async ({ requestInfo }) => {
|
|
272555
273591
|
const handleId = requestInfo?.headers.get("mcp-project-handle") ?? void 0;
|
|
272556
273592
|
const persisted = handleId ? statelessBindingStore.touch(handleId) : void 0;
|
|
272557
273593
|
const requestedRoot = requestInfo?.headers.get("mcp-project-root") ?? void 0;
|
|
272558
273594
|
const root = persisted?.projectRoot ?? requestedRoot ?? projectRoot;
|
|
272559
|
-
|
|
272560
|
-
|
|
272561
|
-
|
|
272562
|
-
|
|
272563
|
-
|
|
272564
|
-
|
|
272565
|
-
|
|
272566
|
-
|
|
272567
|
-
|
|
273595
|
+
const dataDir = await resolveModernDataDir(root, persisted);
|
|
273596
|
+
const runtimeKey = JSON.stringify([dataDir, root]);
|
|
273597
|
+
return modernRuntimePool.createBridge(runtimeKey, () => withBusinessRuntimeQueue2(async () => {
|
|
273598
|
+
const lease = acquireDatabase2(dataDir);
|
|
273599
|
+
try {
|
|
273600
|
+
const runtime = await createModernMcpRuntime2({
|
|
273601
|
+
projectRoot: root,
|
|
273602
|
+
allowUntrackedFallback: false,
|
|
273603
|
+
deferProjectInitUntilBound: true,
|
|
273604
|
+
deferProjectRuntimeInit: true,
|
|
273605
|
+
dashboardMode: "control-plane",
|
|
273606
|
+
dashboardPort: port,
|
|
273607
|
+
toolProfile
|
|
273608
|
+
});
|
|
273609
|
+
const close = runtime.close.bind(runtime);
|
|
273610
|
+
return {
|
|
273611
|
+
...runtime,
|
|
273612
|
+
close: async () => {
|
|
273613
|
+
try {
|
|
273614
|
+
await close();
|
|
273615
|
+
} finally {
|
|
273616
|
+
lease.release();
|
|
273617
|
+
}
|
|
273618
|
+
}
|
|
273619
|
+
};
|
|
273620
|
+
} catch (error40) {
|
|
273621
|
+
lease.release();
|
|
273622
|
+
throw error40;
|
|
273623
|
+
}
|
|
273624
|
+
}));
|
|
272568
273625
|
}, {
|
|
272569
273626
|
// Legacy requests are routed to the existing sessionful implementation
|
|
272570
273627
|
// below; this handler is strict modern-only by construction.
|
|
272571
273628
|
legacy: "reject",
|
|
272572
|
-
responseMode: "auto"
|
|
273629
|
+
responseMode: "auto",
|
|
273630
|
+
onerror: (error40) => {
|
|
273631
|
+
const message = error40 instanceof Error ? error40.message : String(error40);
|
|
273632
|
+
const stack = error40 instanceof Error ? error40.stack : void 0;
|
|
273633
|
+
console.error(`[memorix] modern MCP request failed: ${message}${stack ? `
|
|
273634
|
+
${stack}` : ""}`);
|
|
273635
|
+
}
|
|
272573
273636
|
});
|
|
272574
273637
|
const modernNodeHandler = toNodeHandler(modernMcpHandler);
|
|
272575
273638
|
const sessions = /* @__PURE__ */ new Map();
|
|
273639
|
+
let pendingSessionInitializations = 0;
|
|
272576
273640
|
const sessionLastActivity = /* @__PURE__ */ new Map();
|
|
272577
273641
|
const expiredSessions = /* @__PURE__ */ new Map();
|
|
272578
273642
|
let suppressedProbeCount = 0;
|
|
@@ -272747,7 +273811,11 @@ var init_serve_http = __esm({
|
|
|
272747
273811
|
if (sessionId && sessions.has(sessionId)) {
|
|
272748
273812
|
sessionLastActivity.set(sessionId, Date.now());
|
|
272749
273813
|
patchMcpResponse(res);
|
|
272750
|
-
|
|
273814
|
+
const session = sessions.get(sessionId);
|
|
273815
|
+
await withBusinessRuntimeQueue2(async () => {
|
|
273816
|
+
await session.activateProjectRuntime();
|
|
273817
|
+
await session.transport.handleRequest(req, res, body);
|
|
273818
|
+
});
|
|
272751
273819
|
return;
|
|
272752
273820
|
}
|
|
272753
273821
|
if (sessionId) {
|
|
@@ -272755,11 +273823,32 @@ var init_serve_http = __esm({
|
|
|
272755
273823
|
return;
|
|
272756
273824
|
}
|
|
272757
273825
|
if (!sessionId && isInitializeRequest2(body)) {
|
|
273826
|
+
if (sessions.size + pendingSessionInitializations >= maxHttpSessions) {
|
|
273827
|
+
res.writeHead(429, {
|
|
273828
|
+
"Content-Type": "application/json",
|
|
273829
|
+
"Retry-After": "5"
|
|
273830
|
+
});
|
|
273831
|
+
res.end(JSON.stringify({
|
|
273832
|
+
jsonrpc: "2.0",
|
|
273833
|
+
error: { code: -32029, message: `HTTP session limit reached (${maxHttpSessions}). Retry after an existing session closes.` },
|
|
273834
|
+
id: body.id ?? null
|
|
273835
|
+
}));
|
|
273836
|
+
return;
|
|
273837
|
+
}
|
|
273838
|
+
pendingSessionInitializations++;
|
|
273839
|
+
let pendingReservationReleased = false;
|
|
273840
|
+
const releasePendingReservation = () => {
|
|
273841
|
+
if (pendingReservationReleased) return;
|
|
273842
|
+
pendingReservationReleased = true;
|
|
273843
|
+
pendingSessionInitializations = Math.max(0, pendingSessionInitializations - 1);
|
|
273844
|
+
};
|
|
272758
273845
|
let createdState = null;
|
|
273846
|
+
const sessionLeases = [acquireDatabase2(defaultDataDir)];
|
|
272759
273847
|
const transport = new StreamableHTTPServerTransport2({
|
|
272760
|
-
sessionIdGenerator: () =>
|
|
273848
|
+
sessionIdGenerator: () => randomUUID23(),
|
|
272761
273849
|
enableJsonResponse: true,
|
|
272762
273850
|
onsessioninitialized: (sid) => {
|
|
273851
|
+
releasePendingReservation();
|
|
272763
273852
|
if (createdState) sessions.set(sid, createdState);
|
|
272764
273853
|
sessionLastActivity.set(sid, Date.now());
|
|
272765
273854
|
const timer = setTimeout(() => {
|
|
@@ -272770,6 +273859,7 @@ var init_serve_http = __esm({
|
|
|
272770
273859
|
}
|
|
272771
273860
|
});
|
|
272772
273861
|
transport.onclose = () => {
|
|
273862
|
+
releasePendingReservation();
|
|
272773
273863
|
const sid = transport.sessionId;
|
|
272774
273864
|
if (sid) {
|
|
272775
273865
|
const pending = pendingLogTimers.get(sid);
|
|
@@ -272785,26 +273875,49 @@ var init_serve_http = __esm({
|
|
|
272785
273875
|
sessionLastActivity.delete(sid);
|
|
272786
273876
|
}
|
|
272787
273877
|
handleTransportClose();
|
|
273878
|
+
for (const lease of sessionLeases) lease.release();
|
|
273879
|
+
evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
|
|
272788
273880
|
};
|
|
272789
273881
|
const binding = createProjectBindingController2(projectRoot);
|
|
272790
|
-
|
|
272791
|
-
|
|
272792
|
-
|
|
272793
|
-
|
|
272794
|
-
|
|
272795
|
-
|
|
272796
|
-
|
|
272797
|
-
|
|
272798
|
-
|
|
272799
|
-
|
|
272800
|
-
|
|
272801
|
-
|
|
272802
|
-
|
|
272803
|
-
|
|
272804
|
-
|
|
273882
|
+
let server;
|
|
273883
|
+
let activateProjectRuntime;
|
|
273884
|
+
let handleTransportClose;
|
|
273885
|
+
let getProjectDataDirForSession;
|
|
273886
|
+
let switchProject;
|
|
273887
|
+
try {
|
|
273888
|
+
({ server, activateProjectRuntime, switchProject, handleTransportClose, getProjectDataDir: getProjectDataDirForSession } = await createMemorixServer2(
|
|
273889
|
+
projectRoot,
|
|
273890
|
+
void 0,
|
|
273891
|
+
void 0,
|
|
273892
|
+
{
|
|
273893
|
+
allowUntrackedFallback: false,
|
|
273894
|
+
deferProjectInitUntilBound: true,
|
|
273895
|
+
// Same as stdio: initialize/tools/list must not hydrate 40k+
|
|
273896
|
+
// observations on the HTTP thread. /health and the LaunchAgent
|
|
273897
|
+
// watchdog die if createMemorixServer blocks here.
|
|
273898
|
+
deferProjectRuntimeInit: true,
|
|
273899
|
+
dashboardMode: "control-plane",
|
|
273900
|
+
dashboardPort: port,
|
|
273901
|
+
toolProfile,
|
|
273902
|
+
projectBinding: binding
|
|
273903
|
+
}
|
|
273904
|
+
));
|
|
273905
|
+
} catch (error40) {
|
|
273906
|
+
releasePendingReservation();
|
|
273907
|
+
for (const lease of sessionLeases) lease.release();
|
|
273908
|
+
throw error40;
|
|
273909
|
+
}
|
|
273910
|
+
const switchProjectWithLease = async (newCwd, source) => {
|
|
273911
|
+
const before = getProjectDataDirForSession();
|
|
273912
|
+
const changed = await switchProject(newCwd, source);
|
|
273913
|
+
const after = getProjectDataDirForSession();
|
|
273914
|
+
if (changed && after !== before) {
|
|
273915
|
+
sessionLeases[0].release();
|
|
273916
|
+
sessionLeases[0] = acquireDatabase2(after);
|
|
272805
273917
|
}
|
|
272806
|
-
|
|
272807
|
-
|
|
273918
|
+
return changed;
|
|
273919
|
+
};
|
|
273920
|
+
createdState = { transport, server, activateProjectRuntime, switchProject: switchProjectWithLease, binding, leases: sessionLeases };
|
|
272808
273921
|
await server.connect(transport);
|
|
272809
273922
|
const tryRootsSwitch = async () => {
|
|
272810
273923
|
try {
|
|
@@ -272843,7 +273956,14 @@ var init_serve_http = __esm({
|
|
|
272843
273956
|
} catch {
|
|
272844
273957
|
}
|
|
272845
273958
|
patchMcpResponse(res);
|
|
272846
|
-
|
|
273959
|
+
try {
|
|
273960
|
+
await withBusinessRuntimeQueue2(async () => {
|
|
273961
|
+
await activateProjectRuntime();
|
|
273962
|
+
await transport.handleRequest(req, res, body);
|
|
273963
|
+
});
|
|
273964
|
+
} finally {
|
|
273965
|
+
if (!transport.sessionId) releasePendingReservation();
|
|
273966
|
+
}
|
|
272847
273967
|
return;
|
|
272848
273968
|
}
|
|
272849
273969
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
@@ -272877,7 +273997,7 @@ var init_serve_http = __esm({
|
|
|
272877
273997
|
sendUnknownSession(res, sessionId);
|
|
272878
273998
|
return;
|
|
272879
273999
|
}
|
|
272880
|
-
await sessions.get(sessionId).transport.handleRequest(req, res);
|
|
274000
|
+
await withBusinessRuntimeQueue2(() => sessions.get(sessionId).transport.handleRequest(req, res));
|
|
272881
274001
|
}
|
|
272882
274002
|
const { detectProject: detectProject2 } = await Promise.resolve().then(() => (init_detector(), detector_exports));
|
|
272883
274003
|
const { getProjectDataDir: getProjectDataDir2, getBaseDataDir: getBaseDataDir2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
|
|
@@ -272898,8 +274018,9 @@ var init_serve_http = __esm({
|
|
|
272898
274018
|
});
|
|
272899
274019
|
controlPlaneMaintenanceWorker.start();
|
|
272900
274020
|
const projectDataDirCache = /* @__PURE__ */ new Map();
|
|
272901
|
-
projectDataDirCache
|
|
274021
|
+
cacheSet3(projectDataDirCache, defaultProject.id, defaultDataDir, MAX_PROJECT_CACHE_ENTRIES);
|
|
272902
274022
|
const dashboardObservationStores = /* @__PURE__ */ new Map();
|
|
274023
|
+
const dashboardStoreLeases = /* @__PURE__ */ new Map();
|
|
272903
274024
|
async function resolveRequestProject(url2) {
|
|
272904
274025
|
const requestedId = url2.searchParams.get("project");
|
|
272905
274026
|
if (!requestedId || requestedId === defaultProject.id) {
|
|
@@ -272908,18 +274029,36 @@ var init_serve_http = __esm({
|
|
|
272908
274029
|
let dataDir = projectDataDirCache.get(requestedId);
|
|
272909
274030
|
if (!dataDir) {
|
|
272910
274031
|
dataDir = await getProjectDataDir2(requestedId);
|
|
272911
|
-
projectDataDirCache
|
|
274032
|
+
cacheSet3(projectDataDirCache, requestedId, dataDir, MAX_PROJECT_CACHE_ENTRIES);
|
|
272912
274033
|
}
|
|
272913
274034
|
const name = requestedId.split("/").pop() || requestedId;
|
|
272914
274035
|
return { projectId: requestedId, projectName: name, dataDir };
|
|
272915
274036
|
}
|
|
272916
274037
|
async function getDashboardObservationStore(dataDir) {
|
|
272917
274038
|
const cached2 = dashboardObservationStores.get(dataDir);
|
|
272918
|
-
if (cached2)
|
|
274039
|
+
if (cached2) {
|
|
274040
|
+
cacheSet3(dashboardObservationStores, dataDir, cached2, MAX_STORE_CACHE_ENTRIES);
|
|
274041
|
+
return cached2;
|
|
274042
|
+
}
|
|
272919
274043
|
const { createObservationStore: createObservationStore2 } = await Promise.resolve().then(() => (init_obs_store(), obs_store_exports));
|
|
272920
|
-
const
|
|
272921
|
-
|
|
272922
|
-
|
|
274044
|
+
const lease = acquireDatabase2(dataDir);
|
|
274045
|
+
try {
|
|
274046
|
+
const store2 = await createObservationStore2(dataDir);
|
|
274047
|
+
dashboardStoreLeases.set(dataDir, lease);
|
|
274048
|
+
cacheSet3(dashboardObservationStores, dataDir, store2, MAX_STORE_CACHE_ENTRIES, (evictedDataDir, evictedStore) => {
|
|
274049
|
+
try {
|
|
274050
|
+
evictedStore.close();
|
|
274051
|
+
} catch {
|
|
274052
|
+
}
|
|
274053
|
+
dashboardStoreLeases.get(evictedDataDir)?.release();
|
|
274054
|
+
dashboardStoreLeases.delete(evictedDataDir);
|
|
274055
|
+
evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
|
|
274056
|
+
});
|
|
274057
|
+
return store2;
|
|
274058
|
+
} catch (error40) {
|
|
274059
|
+
lease.release();
|
|
274060
|
+
throw error40;
|
|
274061
|
+
}
|
|
272923
274062
|
}
|
|
272924
274063
|
async function loadDashboardObservations(dataDir) {
|
|
272925
274064
|
const store2 = await getDashboardObservationStore(dataDir);
|
|
@@ -273749,6 +274888,18 @@ var init_serve_http = __esm({
|
|
|
273749
274888
|
arrayBuffers: memory.arrayBuffers,
|
|
273750
274889
|
heapLimit: heap.heap_size_limit
|
|
273751
274890
|
},
|
|
274891
|
+
sqlite: getDatabaseStats2(),
|
|
274892
|
+
sessions: {
|
|
274893
|
+
active: sessions.size,
|
|
274894
|
+
pending: pendingSessionInitializations,
|
|
274895
|
+
limit: maxHttpSessions
|
|
274896
|
+
},
|
|
274897
|
+
modernRuntime: modernRuntimePool.stats(),
|
|
274898
|
+
eventLoop: {
|
|
274899
|
+
meanMs: Number.isFinite(eventLoopDelay.mean) ? eventLoopDelay.mean / 1e6 : null,
|
|
274900
|
+
p95Ms: eventLoopDelay.percentile(95) / 1e6,
|
|
274901
|
+
maxMs: eventLoopDelay.max / 1e6
|
|
274902
|
+
},
|
|
273752
274903
|
embedding: getEmbeddingRuntimeHealth2()
|
|
273753
274904
|
}));
|
|
273754
274905
|
return;
|
|
@@ -273865,6 +275016,13 @@ var init_serve_http = __esm({
|
|
|
273865
275016
|
}
|
|
273866
275017
|
}
|
|
273867
275018
|
dashboardObservationStores.clear();
|
|
275019
|
+
for (const lease of dashboardStoreLeases.values()) lease.release();
|
|
275020
|
+
dashboardStoreLeases.clear();
|
|
275021
|
+
for (const lease of teamStoreLeases.values()) lease.release();
|
|
275022
|
+
teamStoreLeases.clear();
|
|
275023
|
+
controlPlaneBaseLease.release();
|
|
275024
|
+
await modernRuntimePool.close();
|
|
275025
|
+
eventLoopDelay.disable();
|
|
273868
275026
|
const { closeAllDatabases: closeAllDatabases2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
|
|
273869
275027
|
closeAllDatabases2();
|
|
273870
275028
|
throw error40;
|
|
@@ -273875,6 +275033,8 @@ var init_serve_http = __esm({
|
|
|
273875
275033
|
const now3 = Date.now();
|
|
273876
275034
|
forgetExpiredSessions(now3);
|
|
273877
275035
|
statelessBindingStore.cleanupExpired(now3);
|
|
275036
|
+
evictIdleDatabases2(MAX_STORE_CACHE_ENTRIES);
|
|
275037
|
+
void modernRuntimePool.evictIdle();
|
|
273878
275038
|
if (SESSION_TIMEOUT_MS === 0) return;
|
|
273879
275039
|
for (const [sid, state] of sessions) {
|
|
273880
275040
|
const lastActive = sessionLastActivity.get(sid) ?? 0;
|
|
@@ -273918,6 +275078,10 @@ var init_serve_http = __esm({
|
|
|
273918
275078
|
}
|
|
273919
275079
|
}
|
|
273920
275080
|
dashboardObservationStores.clear();
|
|
275081
|
+
for (const lease of dashboardStoreLeases.values()) lease.release();
|
|
275082
|
+
dashboardStoreLeases.clear();
|
|
275083
|
+
for (const lease of teamStoreLeases.values()) lease.release();
|
|
275084
|
+
teamStoreLeases.clear();
|
|
273921
275085
|
for (const [sid, state] of sessions) {
|
|
273922
275086
|
try {
|
|
273923
275087
|
await state.transport.close();
|
|
@@ -273926,8 +275090,10 @@ var init_serve_http = __esm({
|
|
|
273926
275090
|
console.error(`[memorix] Error closing session ${sid}:`, err);
|
|
273927
275091
|
}
|
|
273928
275092
|
}
|
|
275093
|
+
await modernRuntimePool.close();
|
|
273929
275094
|
clearInterval(gcInterval);
|
|
273930
275095
|
clearInterval(heartbeatInterval);
|
|
275096
|
+
eventLoopDelay.disable();
|
|
273931
275097
|
await new Promise((resolve9) => {
|
|
273932
275098
|
if (!httpServer.listening) {
|
|
273933
275099
|
resolve9();
|
|
@@ -273936,6 +275102,7 @@ var init_serve_http = __esm({
|
|
|
273936
275102
|
httpServer.close(() => resolve9());
|
|
273937
275103
|
});
|
|
273938
275104
|
const { closeAllDatabases: closeAllDatabases2 } = await Promise.resolve().then(() => (init_sqlite_db(), sqlite_db_exports));
|
|
275105
|
+
controlPlaneBaseLease.release();
|
|
273939
275106
|
closeAllDatabases2();
|
|
273940
275107
|
process.exit(0);
|
|
273941
275108
|
};
|
|
@@ -273979,6 +275146,7 @@ var init_serve_http = __esm({
|
|
|
273979
275146
|
MAX_HTTP_BODY_LIMIT_BYTES,
|
|
273980
275147
|
MIN_HTTP_BODY_LIMIT_BYTES,
|
|
273981
275148
|
parseSessionTimeoutMs,
|
|
275149
|
+
parseMaxHttpSessions,
|
|
273982
275150
|
parseHttpBodyLimit,
|
|
273983
275151
|
isLoopbackHost,
|
|
273984
275152
|
validateHttpBindSecurity,
|
|
@@ -274328,7 +275496,7 @@ var init_merge = __esm({
|
|
|
274328
275496
|
});
|
|
274329
275497
|
|
|
274330
275498
|
// src/sync/journal.ts
|
|
274331
|
-
import { createHash as
|
|
275499
|
+
import { createHash as createHash22 } from "crypto";
|
|
274332
275500
|
function nowIso() {
|
|
274333
275501
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
274334
275502
|
}
|
|
@@ -274340,7 +275508,7 @@ function deriveSyncKey(obs, originDevice) {
|
|
|
274340
275508
|
function contentHash(obs) {
|
|
274341
275509
|
const { id: _id, writeGeneration: _wg, ...rest } = obs;
|
|
274342
275510
|
const stable = stableJson(rest);
|
|
274343
|
-
return
|
|
275511
|
+
return createHash22("sha256").update(stable).digest("hex");
|
|
274344
275512
|
}
|
|
274345
275513
|
function stableJson(value) {
|
|
274346
275514
|
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
@@ -274463,9 +275631,9 @@ var init_policy2 = __esm({
|
|
|
274463
275631
|
});
|
|
274464
275632
|
|
|
274465
275633
|
// src/sync/namespace.ts
|
|
274466
|
-
import { createHash as
|
|
275634
|
+
import { createHash as createHash23 } from "crypto";
|
|
274467
275635
|
function syncNamespace(projectId) {
|
|
274468
|
-
return `project-${
|
|
275636
|
+
return `project-${createHash23("sha256").update(projectId).digest("hex").slice(0, 32)}`;
|
|
274469
275637
|
}
|
|
274470
275638
|
function userSyncNamespace(env3 = process.env) {
|
|
274471
275639
|
const override = env3.MEMORIX_SYNC_USER_NAMESPACE?.trim();
|
|
@@ -274491,7 +275659,7 @@ var init_namespace = __esm({
|
|
|
274491
275659
|
});
|
|
274492
275660
|
|
|
274493
275661
|
// src/sync/engine.ts
|
|
274494
|
-
import { createHash as
|
|
275662
|
+
import { createHash as createHash24 } from "crypto";
|
|
274495
275663
|
async function runSync(store2, remote, opts) {
|
|
274496
275664
|
const report = {
|
|
274497
275665
|
remote: remote.kind,
|
|
@@ -274695,7 +275863,7 @@ async function recordConcurrentConflict(store2, report, local, incoming, localRo
|
|
|
274695
275863
|
const incomingWins = compareVersion(incoming.version, { revision: local.revision, writer: local.writer }) > 0;
|
|
274696
275864
|
const loser = incomingWins ? { revision: local.revision, writer: local.writer } : incoming.version;
|
|
274697
275865
|
const loserRow = incomingWins ? localRow : incoming.row;
|
|
274698
|
-
const id =
|
|
275866
|
+
const id = createHash24("sha256").update(`${store2.projectId()}|${incoming.syncKey}|${local.revision}|${local.writer}|${incoming.version.revision}|${incoming.version.writer}`).digest("hex");
|
|
274699
275867
|
await store2.recordConflict({
|
|
274700
275868
|
id,
|
|
274701
275869
|
projectId: store2.projectId(),
|
|
@@ -274734,7 +275902,7 @@ var init_engine6 = __esm({
|
|
|
274734
275902
|
|
|
274735
275903
|
// src/sync/store-port.ts
|
|
274736
275904
|
import os7 from "os";
|
|
274737
|
-
import { randomUUID as
|
|
275905
|
+
import { randomUUID as randomUUID21 } from "crypto";
|
|
274738
275906
|
function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
|
|
274739
275907
|
const db2 = getDatabase(dataDir);
|
|
274740
275908
|
const scope = options.scope ?? "project";
|
|
@@ -274784,7 +275952,7 @@ function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
|
|
|
274784
275952
|
function ensureDevice() {
|
|
274785
275953
|
const row = getMeta.get(DEVICE_KEY);
|
|
274786
275954
|
if (row?.value) return row.value;
|
|
274787
|
-
const id = `dev_${
|
|
275955
|
+
const id = `dev_${randomUUID21().replaceAll("-", "")}`;
|
|
274788
275956
|
setMeta.run(DEVICE_KEY, id);
|
|
274789
275957
|
setMeta.run(FINGERPRINT_KEY, deviceFingerprint);
|
|
274790
275958
|
return id;
|
|
@@ -274814,7 +275982,7 @@ function createSqliteSyncStore(dataDir, store2, projectId, options = {}) {
|
|
|
274814
275982
|
assertCanPublish,
|
|
274815
275983
|
rotateDevice: () => {
|
|
274816
275984
|
const previous = ensureDevice();
|
|
274817
|
-
const id = `dev_${
|
|
275985
|
+
const id = `dev_${randomUUID21().replaceAll("-", "")}`;
|
|
274818
275986
|
const pending = pendingForDevice.all(previous);
|
|
274819
275987
|
const rotate = db2.transaction(() => {
|
|
274820
275988
|
setMeta.run(DEVICE_KEY, id);
|
|
@@ -277591,10 +278759,10 @@ __export(handler_exports, {
|
|
|
277591
278759
|
resetCooldowns: () => resetCooldowns,
|
|
277592
278760
|
runHook: () => runHook
|
|
277593
278761
|
});
|
|
277594
|
-
import { createHash as
|
|
278762
|
+
import { createHash as createHash25 } from "crypto";
|
|
277595
278763
|
function deriveHookActorId(input) {
|
|
277596
278764
|
const material = `${input.agent ?? "unknown"}\0${input.sessionId ?? "unknown"}`;
|
|
277597
|
-
return `hook:${
|
|
278765
|
+
return `hook:${createHash25("sha256").update(material).digest("hex").slice(0, 24)}`;
|
|
277598
278766
|
}
|
|
277599
278767
|
function classifyTool(input) {
|
|
277600
278768
|
if (input.event === "post_edit") return "file_modify";
|
|
@@ -279556,10 +280724,10 @@ var init_ingest = __esm({
|
|
|
279556
280724
|
});
|
|
279557
280725
|
|
|
279558
280726
|
// src/media/embedding.ts
|
|
279559
|
-
import { createHash as
|
|
280727
|
+
import { createHash as createHash26 } from "crypto";
|
|
279560
280728
|
function profileKey(provider2, asset) {
|
|
279561
280729
|
const identity = `${provider2.name}\0${provider2.dimensions}\0${asset.kind}`;
|
|
279562
|
-
return `media:${
|
|
280730
|
+
return `media:${createHash26("sha256").update(identity).digest("hex").slice(0, 20)}`;
|
|
279563
280731
|
}
|
|
279564
280732
|
function supportsAsset(provider2, asset) {
|
|
279565
280733
|
return !!provider2.embedInput && (provider2.supportedModalities?.includes(asset.kind) ?? false);
|
|
@@ -284751,13 +285919,21 @@ var init_capability_router = __esm({
|
|
|
284751
285919
|
// src/orchestrate/pipeline-trace.ts
|
|
284752
285920
|
function initTraceTable(db2) {
|
|
284753
285921
|
db2.exec(CREATE_TABLE_SQL);
|
|
285922
|
+
for (const column of ["attempt INTEGER", "risk_tier TEXT", "effect_key TEXT", "memory_refs TEXT"]) {
|
|
285923
|
+
try {
|
|
285924
|
+
db2.exec(`ALTER TABLE pipeline_traces ADD COLUMN ${column}`);
|
|
285925
|
+
} catch {
|
|
285926
|
+
}
|
|
285927
|
+
}
|
|
284754
285928
|
db2.exec(CREATE_INDEX_SQL);
|
|
284755
285929
|
}
|
|
284756
285930
|
function writeTrace(db2, event) {
|
|
284757
285931
|
if (!_insertStmt) {
|
|
284758
285932
|
_insertStmt = db2.prepare(`
|
|
284759
|
-
INSERT INTO pipeline_traces (
|
|
284760
|
-
|
|
285933
|
+
INSERT INTO pipeline_traces (
|
|
285934
|
+
pipeline_id, timestamp, type, task_id, agent, detail, duration_ms,
|
|
285935
|
+
attempt, risk_tier, effect_key, memory_refs
|
|
285936
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
284761
285937
|
`);
|
|
284762
285938
|
}
|
|
284763
285939
|
_insertStmt.run(
|
|
@@ -284766,8 +285942,12 @@ function writeTrace(db2, event) {
|
|
|
284766
285942
|
event.type,
|
|
284767
285943
|
event.taskId ?? null,
|
|
284768
285944
|
event.agent ?? null,
|
|
284769
|
-
event.detail,
|
|
284770
|
-
event.durationMs ?? null
|
|
285945
|
+
sanitizeCredentials(event.detail).slice(0, 2e3),
|
|
285946
|
+
event.durationMs ?? null,
|
|
285947
|
+
event.attempt ?? null,
|
|
285948
|
+
event.riskTier ?? null,
|
|
285949
|
+
event.effectKey ?? null,
|
|
285950
|
+
event.memoryRefs ? JSON.stringify(event.memoryRefs.slice(0, 16)) : null
|
|
284771
285951
|
);
|
|
284772
285952
|
}
|
|
284773
285953
|
function pruneOldTraces(db2, keepPipelines = 20) {
|
|
@@ -284796,6 +285976,7 @@ var init_pipeline_trace = __esm({
|
|
|
284796
285976
|
"src/orchestrate/pipeline-trace.ts"() {
|
|
284797
285977
|
"use strict";
|
|
284798
285978
|
init_esm_shims();
|
|
285979
|
+
init_secret_filter();
|
|
284799
285980
|
CREATE_TABLE_SQL = `
|
|
284800
285981
|
CREATE TABLE IF NOT EXISTS pipeline_traces (
|
|
284801
285982
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -284805,7 +285986,11 @@ var init_pipeline_trace = __esm({
|
|
|
284805
285986
|
task_id TEXT,
|
|
284806
285987
|
agent TEXT,
|
|
284807
285988
|
detail TEXT NOT NULL,
|
|
284808
|
-
duration_ms INTEGER
|
|
285989
|
+
duration_ms INTEGER,
|
|
285990
|
+
attempt INTEGER,
|
|
285991
|
+
risk_tier TEXT,
|
|
285992
|
+
effect_key TEXT,
|
|
285993
|
+
memory_refs TEXT
|
|
284809
285994
|
)
|
|
284810
285995
|
`;
|
|
284811
285996
|
CREATE_INDEX_SQL = `
|
|
@@ -285568,8 +286753,140 @@ var init_permission = __esm({
|
|
|
285568
286753
|
}
|
|
285569
286754
|
});
|
|
285570
286755
|
|
|
286756
|
+
// src/orchestrate/effect-ledger.ts
|
|
286757
|
+
import { createHash as createHash27, randomUUID as randomUUID22 } from "crypto";
|
|
286758
|
+
function fingerprintEffectInput(input) {
|
|
286759
|
+
if (input === void 0) return void 0;
|
|
286760
|
+
try {
|
|
286761
|
+
const safe = sanitizeCredentials(JSON.stringify(input));
|
|
286762
|
+
return createHash27("sha256").update(safe).digest("hex").slice(0, 24);
|
|
286763
|
+
} catch {
|
|
286764
|
+
return void 0;
|
|
286765
|
+
}
|
|
286766
|
+
}
|
|
286767
|
+
function rowToEffect(row) {
|
|
286768
|
+
return {
|
|
286769
|
+
id: String(row.id),
|
|
286770
|
+
pipelineId: String(row.pipeline_id),
|
|
286771
|
+
taskId: String(row.task_id),
|
|
286772
|
+
attempt: Number(row.attempt),
|
|
286773
|
+
effectKey: String(row.effect_key),
|
|
286774
|
+
tool: String(row.tool),
|
|
286775
|
+
riskTier: row.risk_tier,
|
|
286776
|
+
replayPolicy: row.replay_policy,
|
|
286777
|
+
status: row.status,
|
|
286778
|
+
...row.call_id ? { callId: String(row.call_id) } : {},
|
|
286779
|
+
idempotencyKey: String(row.idempotency_key),
|
|
286780
|
+
...row.input_fingerprint ? { inputFingerprint: String(row.input_fingerprint) } : {},
|
|
286781
|
+
...row.detail ? { detail: String(row.detail) } : {},
|
|
286782
|
+
startedAt: Number(row.started_at),
|
|
286783
|
+
...row.finished_at ? { finishedAt: Number(row.finished_at) } : {}
|
|
286784
|
+
};
|
|
286785
|
+
}
|
|
286786
|
+
var AgentEffectLedger;
|
|
286787
|
+
var init_effect_ledger = __esm({
|
|
286788
|
+
"src/orchestrate/effect-ledger.ts"() {
|
|
286789
|
+
"use strict";
|
|
286790
|
+
init_esm_shims();
|
|
286791
|
+
init_secret_filter();
|
|
286792
|
+
init_permission();
|
|
286793
|
+
AgentEffectLedger = class {
|
|
286794
|
+
constructor(db2) {
|
|
286795
|
+
this.db = db2;
|
|
286796
|
+
}
|
|
286797
|
+
db;
|
|
286798
|
+
observe(input) {
|
|
286799
|
+
const existing = this.db.prepare(`
|
|
286800
|
+
SELECT * FROM agent_effects
|
|
286801
|
+
WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
|
|
286802
|
+
`).get(input.pipelineId, input.taskId, input.attempt, input.effectKey);
|
|
286803
|
+
if (existing) return rowToEffect(existing);
|
|
286804
|
+
const riskTier = classifyTool2(input.tool);
|
|
286805
|
+
const replayPolicy = riskTier === "safe" ? "safe" : "requires-idempotency-key";
|
|
286806
|
+
const idempotencyKey = `${input.pipelineId}:${input.taskId}:${input.attempt}:${input.effectKey}`;
|
|
286807
|
+
const now3 = Date.now();
|
|
286808
|
+
const record2 = {
|
|
286809
|
+
id: randomUUID22(),
|
|
286810
|
+
pipelineId: input.pipelineId,
|
|
286811
|
+
taskId: input.taskId,
|
|
286812
|
+
attempt: input.attempt,
|
|
286813
|
+
effectKey: input.effectKey,
|
|
286814
|
+
tool: input.tool.slice(0, 200),
|
|
286815
|
+
riskTier,
|
|
286816
|
+
replayPolicy,
|
|
286817
|
+
status: "observed",
|
|
286818
|
+
...input.callId ? { callId: input.callId.slice(0, 200) } : {},
|
|
286819
|
+
idempotencyKey,
|
|
286820
|
+
...fingerprintEffectInput(input.input) ? { inputFingerprint: fingerprintEffectInput(input.input) } : {},
|
|
286821
|
+
startedAt: now3
|
|
286822
|
+
};
|
|
286823
|
+
this.db.prepare(`
|
|
286824
|
+
INSERT OR IGNORE INTO agent_effects (
|
|
286825
|
+
id, pipeline_id, task_id, attempt, effect_key, tool, risk_tier,
|
|
286826
|
+
replay_policy, status, call_id, idempotency_key, input_fingerprint,
|
|
286827
|
+
detail, started_at, finished_at
|
|
286828
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
286829
|
+
`).run(
|
|
286830
|
+
record2.id,
|
|
286831
|
+
record2.pipelineId,
|
|
286832
|
+
record2.taskId,
|
|
286833
|
+
record2.attempt,
|
|
286834
|
+
record2.effectKey,
|
|
286835
|
+
record2.tool,
|
|
286836
|
+
record2.riskTier,
|
|
286837
|
+
record2.replayPolicy,
|
|
286838
|
+
record2.status,
|
|
286839
|
+
record2.callId ?? null,
|
|
286840
|
+
record2.idempotencyKey,
|
|
286841
|
+
record2.inputFingerprint ?? null,
|
|
286842
|
+
null,
|
|
286843
|
+
record2.startedAt,
|
|
286844
|
+
null
|
|
286845
|
+
);
|
|
286846
|
+
return rowToEffect(this.db.prepare(`
|
|
286847
|
+
SELECT * FROM agent_effects
|
|
286848
|
+
WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
|
|
286849
|
+
`).get(input.pipelineId, input.taskId, input.attempt, input.effectKey));
|
|
286850
|
+
}
|
|
286851
|
+
settle(input) {
|
|
286852
|
+
const detail = input.detail ? sanitizeCredentials(input.detail).slice(0, 500) : void 0;
|
|
286853
|
+
this.db.prepare(`
|
|
286854
|
+
UPDATE agent_effects
|
|
286855
|
+
SET status = ?, detail = ?, finished_at = ?
|
|
286856
|
+
WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
|
|
286857
|
+
`).run(
|
|
286858
|
+
input.status,
|
|
286859
|
+
detail ?? null,
|
|
286860
|
+
Date.now(),
|
|
286861
|
+
input.pipelineId,
|
|
286862
|
+
input.taskId,
|
|
286863
|
+
input.attempt,
|
|
286864
|
+
input.effectKey
|
|
286865
|
+
);
|
|
286866
|
+
const row = this.db.prepare(`
|
|
286867
|
+
SELECT * FROM agent_effects
|
|
286868
|
+
WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND effect_key = ?
|
|
286869
|
+
`).get(input.pipelineId, input.taskId, input.attempt, input.effectKey);
|
|
286870
|
+
return row ? rowToEffect(row) : void 0;
|
|
286871
|
+
}
|
|
286872
|
+
markAttemptUnknown(pipelineId, taskId, attempt) {
|
|
286873
|
+
const result = this.db.prepare(`
|
|
286874
|
+
UPDATE agent_effects
|
|
286875
|
+
SET status = 'unknown', detail = COALESCE(detail, 'No matching tool result observed'), finished_at = ?
|
|
286876
|
+
WHERE pipeline_id = ? AND task_id = ? AND attempt = ? AND status = 'observed'
|
|
286877
|
+
`).run(Date.now(), pipelineId, taskId, attempt);
|
|
286878
|
+
return Number(result.changes ?? 0);
|
|
286879
|
+
}
|
|
286880
|
+
list(pipelineId, taskId) {
|
|
286881
|
+
const rows = taskId ? this.db.prepare("SELECT * FROM agent_effects WHERE pipeline_id = ? AND task_id = ? ORDER BY started_at ASC, rowid ASC").all(pipelineId, taskId) : this.db.prepare("SELECT * FROM agent_effects WHERE pipeline_id = ? ORDER BY started_at ASC, rowid ASC").all(pipelineId);
|
|
286882
|
+
return rows.map(rowToEffect);
|
|
286883
|
+
}
|
|
286884
|
+
};
|
|
286885
|
+
}
|
|
286886
|
+
});
|
|
286887
|
+
|
|
285571
286888
|
// src/orchestrate/memorix-bridge.ts
|
|
285572
|
-
import { createHash as
|
|
286889
|
+
import { createHash as createHash28 } from "crypto";
|
|
285573
286890
|
async function getStoreObservation() {
|
|
285574
286891
|
if (!_storeObservation) {
|
|
285575
286892
|
const mod = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
@@ -285585,7 +286902,7 @@ async function getSearchObservations() {
|
|
|
285585
286902
|
return _searchObservations;
|
|
285586
286903
|
}
|
|
285587
286904
|
function automaticActorId(...parts) {
|
|
285588
|
-
return `orchestrator:${
|
|
286905
|
+
return `orchestrator:${createHash28("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
|
|
285589
286906
|
}
|
|
285590
286907
|
function isEligibleAutomaticLesson(entry) {
|
|
285591
286908
|
return entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral";
|
|
@@ -285732,7 +287049,7 @@ function sanitizeErrorPattern(text3) {
|
|
|
285732
287049
|
}
|
|
285733
287050
|
function hashErrorPattern(errorOutput) {
|
|
285734
287051
|
const sanitized = sanitizeErrorPattern(errorOutput).slice(0, 500);
|
|
285735
|
-
return
|
|
287052
|
+
return createHash28("sha256").update(sanitized).digest("hex").slice(0, 12);
|
|
285736
287053
|
}
|
|
285737
287054
|
function sleep6(ms2) {
|
|
285738
287055
|
return new Promise((resolve9) => setTimeout(() => resolve9([]), ms2));
|
|
@@ -285843,10 +287160,12 @@ async function runCoordinationLoop(config2) {
|
|
|
285843
287160
|
const dispatchedAdapterNames = /* @__PURE__ */ new Set();
|
|
285844
287161
|
const routingDecisions = [];
|
|
285845
287162
|
let traceDb = null;
|
|
287163
|
+
let effectLedger = null;
|
|
285846
287164
|
try {
|
|
285847
287165
|
traceDb = teamStore.getDb();
|
|
285848
287166
|
resetTraceCache();
|
|
285849
287167
|
initTraceTable(traceDb);
|
|
287168
|
+
if (pipelineId) effectLedger = new AgentEffectLedger(traceDb);
|
|
285850
287169
|
} catch {
|
|
285851
287170
|
}
|
|
285852
287171
|
if (useWorktrees) {
|
|
@@ -286147,6 +287466,32 @@ ${formatCostSummary(costSummary)}`);
|
|
|
286147
287466
|
for await (const msg of agentProcess.messages) {
|
|
286148
287467
|
if (msg.type === "tool_use") {
|
|
286149
287468
|
dispatch.toolCount++;
|
|
287469
|
+
const effectKey = msg.callId ?? `seq:${dispatch.toolCount}`;
|
|
287470
|
+
const effect = effectLedger?.observe({
|
|
287471
|
+
pipelineId,
|
|
287472
|
+
taskId: dispatch.taskId,
|
|
287473
|
+
attempt: dispatch.attempt,
|
|
287474
|
+
effectKey,
|
|
287475
|
+
tool: msg.tool ?? "unknown",
|
|
287476
|
+
...msg.callId ? { callId: msg.callId } : {},
|
|
287477
|
+
...msg.input ? { input: msg.input } : {}
|
|
287478
|
+
});
|
|
287479
|
+
if (effect && traceDb && pipelineId) {
|
|
287480
|
+
try {
|
|
287481
|
+
writeTrace(traceDb, {
|
|
287482
|
+
pipelineId,
|
|
287483
|
+
timestamp: Date.now(),
|
|
287484
|
+
type: "effect",
|
|
287485
|
+
taskId: dispatch.taskId,
|
|
287486
|
+
agent: dispatch.adapterName,
|
|
287487
|
+
detail: `observed tool=${effect.tool} risk=${effect.riskTier} replay=${effect.replayPolicy} effect=${effect.effectKey}`,
|
|
287488
|
+
attempt: dispatch.attempt,
|
|
287489
|
+
riskTier: effect.riskTier,
|
|
287490
|
+
effectKey: effect.effectKey
|
|
287491
|
+
});
|
|
287492
|
+
} catch {
|
|
287493
|
+
}
|
|
287494
|
+
}
|
|
286150
287495
|
if (msg.tool) tracker.record(msg.tool);
|
|
286151
287496
|
emit("agent:tool_use", `[${adapter.name}] tool #${dispatch.toolCount}: ${msg.tool ?? "unknown"}`, {
|
|
286152
287497
|
taskId: task.task_id,
|
|
@@ -286166,6 +287511,16 @@ ${formatCostSummary(costSummary)}`);
|
|
|
286166
287511
|
}
|
|
286167
287512
|
}
|
|
286168
287513
|
}
|
|
287514
|
+
if (msg.type === "tool_result" && msg.callId && effectLedger && pipelineId) {
|
|
287515
|
+
effectLedger.settle({
|
|
287516
|
+
pipelineId,
|
|
287517
|
+
taskId: dispatch.taskId,
|
|
287518
|
+
attempt: dispatch.attempt,
|
|
287519
|
+
effectKey: msg.callId,
|
|
287520
|
+
status: "succeeded",
|
|
287521
|
+
detail: "tool result observed"
|
|
287522
|
+
});
|
|
287523
|
+
}
|
|
286169
287524
|
if (msg.type === "text" && msg.content) {
|
|
286170
287525
|
dispatch.accumulatedText += msg.content;
|
|
286171
287526
|
}
|
|
@@ -286248,6 +287603,9 @@ ${formatCostSummary(costSummary)}`);
|
|
|
286248
287603
|
if (result.sessionId) {
|
|
286249
287604
|
taskSessionIds.set(dispatch.taskId, result.sessionId);
|
|
286250
287605
|
}
|
|
287606
|
+
if (effectLedger && pipelineId) {
|
|
287607
|
+
effectLedger.markAttemptUnknown(pipelineId, dispatch.taskId, dispatch.attempt);
|
|
287608
|
+
}
|
|
286251
287609
|
const taskState = teamStore.getTask(dispatch.taskId);
|
|
286252
287610
|
const taskDesc = taskState?.description ?? dispatch.taskId;
|
|
286253
287611
|
if (!result.killed && result.exitCode === 0) {
|
|
@@ -286311,6 +287669,25 @@ ${formatCostSummary(costSummary)}`);
|
|
|
286311
287669
|
for await (const msg of fixProcess.messages) {
|
|
286312
287670
|
if (msg.type === "tool_use") {
|
|
286313
287671
|
fixDispatch.toolCount++;
|
|
287672
|
+
effectLedger?.observe({
|
|
287673
|
+
pipelineId,
|
|
287674
|
+
taskId: fixDispatch.taskId,
|
|
287675
|
+
attempt: fixDispatch.attempt,
|
|
287676
|
+
effectKey: msg.callId ?? `seq:${fixDispatch.toolCount}`,
|
|
287677
|
+
tool: msg.tool ?? "unknown",
|
|
287678
|
+
...msg.callId ? { callId: msg.callId } : {},
|
|
287679
|
+
...msg.input ? { input: msg.input } : {}
|
|
287680
|
+
});
|
|
287681
|
+
}
|
|
287682
|
+
if (msg.type === "tool_result" && msg.callId && effectLedger && pipelineId) {
|
|
287683
|
+
effectLedger.settle({
|
|
287684
|
+
pipelineId,
|
|
287685
|
+
taskId: fixDispatch.taskId,
|
|
287686
|
+
attempt: fixDispatch.attempt,
|
|
287687
|
+
effectKey: msg.callId,
|
|
287688
|
+
status: "succeeded",
|
|
287689
|
+
detail: "tool result observed"
|
|
287690
|
+
});
|
|
286314
287691
|
}
|
|
286315
287692
|
if (msg.type === "text" && msg.content) {
|
|
286316
287693
|
fixDispatch.accumulatedText += msg.content;
|
|
@@ -286740,6 +288117,7 @@ var init_coordinator = __esm({
|
|
|
286740
288117
|
init_cost_tracker();
|
|
286741
288118
|
init_evidence2();
|
|
286742
288119
|
init_permission();
|
|
288120
|
+
init_effect_ledger();
|
|
286743
288121
|
init_memorix_bridge();
|
|
286744
288122
|
}
|
|
286745
288123
|
});
|
|
@@ -288149,6 +289527,7 @@ function ConfigureView({ onBack }) {
|
|
|
288149
289527
|
{ key: "openai", label: "OpenAI", hint: "gpt-4o-mini" },
|
|
288150
289528
|
{ key: "anthropic", label: "Anthropic", hint: "claude-3-haiku" },
|
|
288151
289529
|
{ key: "openrouter", label: "OpenRouter", hint: "multi-provider" },
|
|
289530
|
+
{ key: "atlascloud", label: "Atlas Cloud", hint: "DeepSeek V3.2" },
|
|
288152
289531
|
{ key: "custom", label: "Custom endpoint", hint: "OpenAI-compatible" },
|
|
288153
289532
|
{ key: "disable", label: "Disable LLM", hint: "free heuristic mode", color: COLORS.warning },
|
|
288154
289533
|
{ key: "back", label: "Back", hint: "", color: COLORS.muted }
|
|
@@ -288441,6 +289820,7 @@ function ConfigureView({ onBack }) {
|
|
|
288441
289820
|
function getDefaultModel(provider2) {
|
|
288442
289821
|
if (provider2 === "anthropic") return "claude-3-haiku-20240307";
|
|
288443
289822
|
if (provider2 === "openrouter") return "openai/gpt-4o-mini";
|
|
289823
|
+
if (provider2 === "atlascloud") return "deepseek-ai/deepseek-v3.2";
|
|
288444
289824
|
return "gpt-4o-mini";
|
|
288445
289825
|
}
|
|
288446
289826
|
var init_ConfigureView = __esm({
|
|
@@ -291646,10 +293026,22 @@ var GUIDES = {
|
|
|
291646
293026
|
context: {
|
|
291647
293027
|
summary: "Build the bounded task Workset used to resume or begin real work.",
|
|
291648
293028
|
usage: [
|
|
291649
|
-
'memorix context "continue the release fix" [--refresh auto|always|never]',
|
|
293029
|
+
'memorix context "continue the release fix" [--task-id <continuity-id>] [--refresh auto|always|never]',
|
|
291650
293030
|
'memorix resume "continue the release fix" [--refresh auto|always|never]'
|
|
291651
293031
|
]
|
|
291652
293032
|
},
|
|
293033
|
+
continuity: {
|
|
293034
|
+
summary: "Track task requirements, decisions, verification, risks, and outcomes.",
|
|
293035
|
+
usage: [
|
|
293036
|
+
'memorix continuity start --task "Harden the release path" [--requirements "a,b"]',
|
|
293037
|
+
'memorix continuity record --task-id <id> --kind decision --content "..."',
|
|
293038
|
+
'memorix continuity record --task-id <id> --kind verification --verification-status passed --content "..."',
|
|
293039
|
+
"memorix continuity show --task-id <id>",
|
|
293040
|
+
'memorix continuity close --task-id <id> --status completed --content "..."',
|
|
293041
|
+
"memorix continuity list"
|
|
293042
|
+
],
|
|
293043
|
+
notes: ["Pass the returned task id to memorix context --task-id so the bounded brief includes the ledger."]
|
|
293044
|
+
},
|
|
291653
293045
|
explain: {
|
|
291654
293046
|
summary: "Show why a task context contains its current facts and memory evidence.",
|
|
291655
293047
|
usage: ["memorix explain [--refresh auto|always|never]"]
|
|
@@ -292058,6 +293450,7 @@ var main2 = defineCommand({
|
|
|
292058
293450
|
knowledge: () => Promise.resolve().then(() => (init_knowledge(), knowledge_exports)).then((m6) => m6.default),
|
|
292059
293451
|
evidence: () => Promise.resolve().then(() => (init_evidence(), evidence_exports)).then((m6) => m6.default),
|
|
292060
293452
|
feedback: () => Promise.resolve().then(() => (init_feedback2(), feedback_exports2)).then((m6) => m6.default),
|
|
293453
|
+
continuity: () => Promise.resolve().then(() => (init_continuity(), continuity_exports)).then((m6) => m6.default),
|
|
292061
293454
|
reasoning: () => Promise.resolve().then(() => (init_reasoning(), reasoning_exports)).then((m6) => m6.default),
|
|
292062
293455
|
retention: () => Promise.resolve().then(() => (init_retention2(), retention_exports2)).then((m6) => m6.default),
|
|
292063
293456
|
formation: () => Promise.resolve().then(() => (init_formation2(), formation_exports2)).then((m6) => m6.default),
|
|
@@ -292158,6 +293551,7 @@ var main2 = defineCommand({
|
|
|
292158
293551
|
"poll",
|
|
292159
293552
|
"evidence",
|
|
292160
293553
|
"feedback",
|
|
293554
|
+
"continuity",
|
|
292161
293555
|
"receipt",
|
|
292162
293556
|
"serve",
|
|
292163
293557
|
"serve-http",
|
|
@@ -292222,6 +293616,7 @@ var main2 = defineCommand({
|
|
|
292222
293616
|
console.error(" knowledge Review source-backed knowledge pages and project workflows");
|
|
292223
293617
|
console.error(" evidence Inspect source-backed evidence cards");
|
|
292224
293618
|
console.error(" feedback Record or audit memory feedback");
|
|
293619
|
+
console.error(" continuity Record task requirements, decisions, verification, and outcome");
|
|
292225
293620
|
console.error(" reasoning Store/search decision rationale");
|
|
292226
293621
|
console.error(" retention Inspect stale/archive status");
|
|
292227
293622
|
console.error(" formation Inspect Memory Formation metrics");
|