memorix 1.2.6 → 1.2.8
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 +20 -0
- package/README.md +6 -3
- package/README.zh-CN.md +5 -2
- package/dist/cli/index.js +1272 -105
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/app.js +7 -2
- package/dist/index.js +809 -52
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +6 -0
- package/dist/maintenance-runner.js +204 -25
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +20 -0
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +809 -52
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +38 -1
- package/dist/types.js.map +1 -1
- package/docs/1.2.7-CONTEXT-CONTINUITY.md +68 -0
- package/docs/API_REFERENCE.md +6 -2
- package/docs/dev-log/progress.txt +48 -0
- package/docs/hooks-architecture.md +2 -1
- package/package.json +3 -3
- package/plugins/claude/memorix/.claude-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/omp/memorix/extensions/memorix.js +17 -0
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/extensions/memorix.js +17 -0
- package/plugins/pi/memorix/package.json +1 -1
- package/src/cli/capability-map.ts +1 -0
- package/src/cli/command-guide.ts +10 -0
- package/src/cli/commands/agent-integrations.ts +102 -5
- package/src/cli/commands/checkpoint.ts +144 -0
- package/src/cli/commands/serve-http.ts +14 -3
- package/src/cli/commands/setup.ts +44 -0
- package/src/cli/index.ts +3 -1
- package/src/codegraph/auto-context.ts +51 -5
- package/src/dashboard/server.ts +11 -0
- package/src/hooks/handler.ts +103 -11
- package/src/hooks/normalizer.ts +85 -1
- package/src/hooks/types.ts +16 -0
- package/src/knowledge/workset.ts +43 -2
- package/src/memory/compaction.ts +165 -0
- package/src/memory/observations.ts +67 -20
- package/src/runtime/maintenance-jobs.ts +84 -4
- package/src/runtime/project-maintenance.ts +63 -6
- package/src/server/tool-profile.ts +1 -0
- package/src/server.ts +94 -0
- package/src/store/compaction-checkpoint-store.ts +397 -0
- package/src/store/sqlite-db.ts +41 -0
- package/src/types.ts +46 -0
package/dist/sdk.js
CHANGED
|
@@ -285,6 +285,7 @@ function getDatabase(dataDir) {
|
|
|
285
285
|
db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
|
|
286
286
|
db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
|
|
287
287
|
db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
|
|
288
|
+
db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
|
|
288
289
|
try {
|
|
289
290
|
db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
|
|
290
291
|
} catch {
|
|
@@ -325,7 +326,7 @@ function getDatabase(dataDir) {
|
|
|
325
326
|
_dbCache.set(normalized, db2);
|
|
326
327
|
return db2;
|
|
327
328
|
}
|
|
328
|
-
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_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_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
|
|
329
|
+
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_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_MAINTENANCE_JOBS_TABLE, CREATE_MAINTENANCE_TARGETS_TABLE, CREATE_COMPACTION_CHECKPOINTS_TABLE, CREATE_INDEXES, SCHEMA_MIGRATIONS, _dbCache;
|
|
329
330
|
var init_sqlite_db = __esm({
|
|
330
331
|
"src/store/sqlite-db.ts"() {
|
|
331
332
|
"use strict";
|
|
@@ -777,6 +778,31 @@ CREATE TABLE IF NOT EXISTS maintenance_targets (
|
|
|
777
778
|
data_dir TEXT NOT NULL,
|
|
778
779
|
updated_at INTEGER NOT NULL
|
|
779
780
|
);
|
|
781
|
+
`;
|
|
782
|
+
CREATE_COMPACTION_CHECKPOINTS_TABLE = `
|
|
783
|
+
CREATE TABLE IF NOT EXISTS compaction_checkpoints (
|
|
784
|
+
id TEXT PRIMARY KEY,
|
|
785
|
+
project_id TEXT NOT NULL,
|
|
786
|
+
session_id TEXT NOT NULL,
|
|
787
|
+
agent TEXT NOT NULL,
|
|
788
|
+
phase TEXT NOT NULL,
|
|
789
|
+
capture_kind TEXT NOT NULL,
|
|
790
|
+
reason TEXT NOT NULL DEFAULT 'unknown',
|
|
791
|
+
source_event TEXT NOT NULL,
|
|
792
|
+
source_key TEXT NOT NULL,
|
|
793
|
+
summary TEXT,
|
|
794
|
+
tokens_before INTEGER,
|
|
795
|
+
first_kept_entry_id TEXT,
|
|
796
|
+
details_json TEXT NOT NULL DEFAULT '{}',
|
|
797
|
+
transcript_available INTEGER NOT NULL DEFAULT 0,
|
|
798
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
799
|
+
pre_captured_at TEXT NOT NULL,
|
|
800
|
+
completed_at TEXT,
|
|
801
|
+
delivered_at TEXT,
|
|
802
|
+
delivery_count INTEGER NOT NULL DEFAULT 0,
|
|
803
|
+
created_at TEXT NOT NULL,
|
|
804
|
+
updated_at TEXT NOT NULL
|
|
805
|
+
);
|
|
780
806
|
`;
|
|
781
807
|
CREATE_INDEXES = `
|
|
782
808
|
CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
|
|
@@ -824,6 +850,9 @@ CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowl
|
|
|
824
850
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_ready ON maintenance_jobs(status, run_after);
|
|
825
851
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_project ON maintenance_jobs(project_id, status, run_after);
|
|
826
852
|
CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_targets(updated_at);
|
|
853
|
+
CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
|
|
854
|
+
CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
|
|
855
|
+
CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
|
|
827
856
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
828
857
|
ON maintenance_jobs(project_id, kind, dedupe_key)
|
|
829
858
|
WHERE status IN ('pending', 'running', 'retry');
|
|
@@ -897,6 +926,15 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
897
926
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_knowledge_workflows_workspace_status ON knowledge_workflows(workspaceId, status, updatedAt DESC)");
|
|
898
927
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowledge_workflow_runs(projectId, workflowId, startedAt DESC)");
|
|
899
928
|
}
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
id: "1.2.7-compaction-checkpoints",
|
|
932
|
+
apply: (db2) => {
|
|
933
|
+
db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
|
|
934
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC)");
|
|
935
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC)");
|
|
936
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key)");
|
|
937
|
+
}
|
|
900
938
|
}
|
|
901
939
|
];
|
|
902
940
|
_dbCache = /* @__PURE__ */ new Map();
|
|
@@ -7147,6 +7185,10 @@ var init_maintenance_jobs = __esm({
|
|
|
7147
7185
|
LIMIT 1
|
|
7148
7186
|
`).get(input.projectId, input.kind, dedupeKey);
|
|
7149
7187
|
if (existing) {
|
|
7188
|
+
if (input.kind === "vector-backfill" && existing.status === "retry") {
|
|
7189
|
+
this.commit();
|
|
7190
|
+
return rowToJob(existing);
|
|
7191
|
+
}
|
|
7150
7192
|
const nextRunAfter = Math.min(Number(existing.run_after), runAfter);
|
|
7151
7193
|
this.db.prepare(`
|
|
7152
7194
|
UPDATE maintenance_jobs
|
|
@@ -7157,6 +7199,26 @@ var init_maintenance_jobs = __esm({
|
|
|
7157
7199
|
this.commit();
|
|
7158
7200
|
return rowToJob(updated);
|
|
7159
7201
|
}
|
|
7202
|
+
if (input.kind === "vector-backfill") {
|
|
7203
|
+
const failed = this.db.prepare(`
|
|
7204
|
+
SELECT * FROM maintenance_jobs
|
|
7205
|
+
WHERE project_id = ? AND kind = ? AND dedupe_key = ? AND status = 'failed'
|
|
7206
|
+
ORDER BY updated_at DESC
|
|
7207
|
+
LIMIT 1
|
|
7208
|
+
`).get(input.projectId, input.kind, dedupeKey);
|
|
7209
|
+
if (failed) {
|
|
7210
|
+
this.db.prepare(`
|
|
7211
|
+
UPDATE maintenance_jobs
|
|
7212
|
+
SET status = 'pending', attempts = 0, max_attempts = ?, run_after = ?,
|
|
7213
|
+
payload_json = ?, lease_owner = NULL, lease_expires_at = NULL,
|
|
7214
|
+
last_error = NULL, completed_at = NULL, updated_at = ?
|
|
7215
|
+
WHERE id = ?
|
|
7216
|
+
`).run(maxAttempts, runAfter, payloadJson, now3, failed.id);
|
|
7217
|
+
const revived = this.getRow(failed.id);
|
|
7218
|
+
this.commit();
|
|
7219
|
+
return rowToJob(revived);
|
|
7220
|
+
}
|
|
7221
|
+
}
|
|
7160
7222
|
const id = randomUUID2();
|
|
7161
7223
|
this.db.prepare(`
|
|
7162
7224
|
INSERT INTO maintenance_jobs (
|
|
@@ -7328,15 +7390,42 @@ var init_maintenance_jobs = __esm({
|
|
|
7328
7390
|
const now3 = options.now ?? Date.now();
|
|
7329
7391
|
const delayMs = Number.isFinite(options.delayMs) ? Math.max(0, Math.floor(options.delayMs)) : 0;
|
|
7330
7392
|
const payloadJson = options.payload === void 0 ? null : JSON.stringify(options.payload);
|
|
7393
|
+
const status = options.status === "retry" ? "retry" : "pending";
|
|
7394
|
+
const hasLastError = options.lastError !== void 0;
|
|
7395
|
+
const lastError = hasLastError ? errorText(options.lastError) : null;
|
|
7331
7396
|
this.db.prepare(`
|
|
7332
7397
|
UPDATE maintenance_jobs
|
|
7333
|
-
SET status =
|
|
7398
|
+
SET status = ?, run_after = ?, attempts = CASE WHEN ? THEN 0 ELSE attempts END,
|
|
7334
7399
|
payload_json = COALESCE(?, payload_json),
|
|
7400
|
+
last_error = CASE
|
|
7401
|
+
WHEN ? THEN NULL
|
|
7402
|
+
WHEN ? THEN ?
|
|
7403
|
+
ELSE last_error
|
|
7404
|
+
END,
|
|
7335
7405
|
lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
|
|
7336
7406
|
WHERE id = ? AND status = 'running' AND lease_owner = ?
|
|
7337
|
-
`).run(
|
|
7407
|
+
`).run(
|
|
7408
|
+
status,
|
|
7409
|
+
now3 + delayMs,
|
|
7410
|
+
options.resetAttempts ? 1 : 0,
|
|
7411
|
+
payloadJson,
|
|
7412
|
+
options.clearLastError ? 1 : 0,
|
|
7413
|
+
hasLastError ? 1 : 0,
|
|
7414
|
+
lastError,
|
|
7415
|
+
now3,
|
|
7416
|
+
id,
|
|
7417
|
+
workerId
|
|
7418
|
+
);
|
|
7338
7419
|
return this.get(id);
|
|
7339
7420
|
}
|
|
7421
|
+
resolveFailedVectorBackfills(projectId, dedupeKey = "vector-backfill", now3 = Date.now()) {
|
|
7422
|
+
const result = this.db.prepare(`
|
|
7423
|
+
UPDATE maintenance_jobs
|
|
7424
|
+
SET status = 'completed', completed_at = ?, updated_at = ?
|
|
7425
|
+
WHERE project_id = ? AND kind = 'vector-backfill' AND dedupe_key = ? AND status = 'failed'
|
|
7426
|
+
`).run(now3, now3, projectId, dedupeKey);
|
|
7427
|
+
return Number(result.changes ?? 0);
|
|
7428
|
+
}
|
|
7340
7429
|
fail(id, workerId, error, now3 = Date.now()) {
|
|
7341
7430
|
this.begin();
|
|
7342
7431
|
try {
|
|
@@ -7432,6 +7521,9 @@ var init_maintenance_jobs = __esm({
|
|
|
7432
7521
|
delayMs: result.delayMs,
|
|
7433
7522
|
resetAttempts: result.resetAttempts,
|
|
7434
7523
|
payload: result.payload,
|
|
7524
|
+
status: result.status,
|
|
7525
|
+
lastError: result.lastError,
|
|
7526
|
+
clearLastError: result.clearLastError,
|
|
7435
7527
|
now: now3
|
|
7436
7528
|
});
|
|
7437
7529
|
return { state: "rescheduled", job: updated2 };
|
|
@@ -8127,10 +8219,10 @@ var init_store = __esm({
|
|
|
8127
8219
|
return row ? rowToSnapshot(row) : void 0;
|
|
8128
8220
|
}
|
|
8129
8221
|
listSnapshots(projectId, limit = 20) {
|
|
8130
|
-
const
|
|
8222
|
+
const safeLimit2 = Number.isFinite(limit) ? Math.max(1, Math.min(200, Math.floor(limit))) : 20;
|
|
8131
8223
|
return this.db.prepare(
|
|
8132
8224
|
"SELECT * FROM code_state_snapshots WHERE projectId = ? ORDER BY sourceEpoch DESC LIMIT ?"
|
|
8133
|
-
).all(projectId,
|
|
8225
|
+
).all(projectId, safeLimit2).map(rowToSnapshot);
|
|
8134
8226
|
}
|
|
8135
8227
|
/**
|
|
8136
8228
|
* Record a completed scan and mark all current structural facts with its
|
|
@@ -8410,6 +8502,9 @@ function normalizeEmbeddingFailure(error) {
|
|
|
8410
8502
|
message: raw
|
|
8411
8503
|
};
|
|
8412
8504
|
}
|
|
8505
|
+
function vectorBackfillError(error) {
|
|
8506
|
+
return sanitizeCredentials(normalizeEmbeddingFailure(error).message).slice(0, 1e3);
|
|
8507
|
+
}
|
|
8413
8508
|
function queueVectorBackfill(projectId) {
|
|
8414
8509
|
const dataDir = projectDir;
|
|
8415
8510
|
if (!dataDir) return;
|
|
@@ -9165,17 +9260,53 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9165
9260
|
let succeeded = 0;
|
|
9166
9261
|
let failed = 0;
|
|
9167
9262
|
let lastFailure;
|
|
9263
|
+
const recordFailure = (message) => {
|
|
9264
|
+
if (!lastFailure) lastFailure = message;
|
|
9265
|
+
};
|
|
9266
|
+
const candidates = [];
|
|
9267
|
+
for (const id of ids) {
|
|
9268
|
+
const observation = observationById.get(id);
|
|
9269
|
+
if (!observation) {
|
|
9270
|
+
vectorMissingIds.delete(id);
|
|
9271
|
+
continue;
|
|
9272
|
+
}
|
|
9273
|
+
candidates.push({
|
|
9274
|
+
id,
|
|
9275
|
+
observation,
|
|
9276
|
+
text: [observation.title, observation.narrative, ...observation.facts].join(" ")
|
|
9277
|
+
});
|
|
9278
|
+
}
|
|
9168
9279
|
try {
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9280
|
+
if (candidates.length === 0) {
|
|
9281
|
+
return { attempted: ids.length, succeeded, failed };
|
|
9282
|
+
}
|
|
9283
|
+
let embeddings = null;
|
|
9284
|
+
try {
|
|
9285
|
+
const provider2 = await getEmbeddingProvider();
|
|
9286
|
+
if (!provider2) {
|
|
9287
|
+
if (isEmbeddingExplicitlyDisabled()) {
|
|
9288
|
+
for (const candidate of candidates) vectorMissingIds.delete(candidate.id);
|
|
9289
|
+
} else {
|
|
9290
|
+
recordFailure("embedding provider unavailable");
|
|
9291
|
+
failed += candidates.length;
|
|
9292
|
+
}
|
|
9293
|
+
} else {
|
|
9294
|
+
embeddings = await provider2.embedBatch(candidates.map((candidate) => candidate.text));
|
|
9174
9295
|
}
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9296
|
+
} catch (error) {
|
|
9297
|
+
recordFailure(vectorBackfillError(error));
|
|
9298
|
+
failed += candidates.length;
|
|
9299
|
+
}
|
|
9300
|
+
if (embeddings) {
|
|
9301
|
+
for (let index = 0; index < candidates.length; index++) {
|
|
9302
|
+
const { id, observation: obs } = candidates[index];
|
|
9303
|
+
const embedding = embeddings[index];
|
|
9304
|
+
if (!embedding || embedding.length === 0) {
|
|
9305
|
+
recordFailure("embedding provider returned no vector");
|
|
9306
|
+
failed++;
|
|
9307
|
+
continue;
|
|
9308
|
+
}
|
|
9309
|
+
try {
|
|
9179
9310
|
if (!isVectorCompatibleWithCurrentIndex(embedding)) {
|
|
9180
9311
|
await upgradeVectorSchemaAfterFirstEmbedding(embedding);
|
|
9181
9312
|
}
|
|
@@ -9184,7 +9315,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9184
9315
|
console.error(
|
|
9185
9316
|
`[memorix] Backfill embedding mismatch for obs-${id}: provider returned ${embedding.length}d, index expects ${vectorDimensions ?? "unknown"}d (kept in queue)`
|
|
9186
9317
|
);
|
|
9187
|
-
|
|
9318
|
+
recordFailure(`dimension mismatch: provider returned ${embedding.length}d, index expects ${vectorDimensions ?? "unknown"}d`);
|
|
9188
9319
|
failed++;
|
|
9189
9320
|
continue;
|
|
9190
9321
|
}
|
|
@@ -9223,15 +9354,10 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9223
9354
|
await insertObservation(doc);
|
|
9224
9355
|
vectorMissingIds.delete(id);
|
|
9225
9356
|
succeeded++;
|
|
9226
|
-
}
|
|
9227
|
-
|
|
9228
|
-
} else {
|
|
9229
|
-
lastFailure = "embedding provider unavailable";
|
|
9357
|
+
} catch (error) {
|
|
9358
|
+
recordFailure(vectorBackfillError(error));
|
|
9230
9359
|
failed++;
|
|
9231
9360
|
}
|
|
9232
|
-
} catch (err) {
|
|
9233
|
-
lastFailure = err instanceof Error ? err.message : String(err);
|
|
9234
|
-
failed++;
|
|
9235
9361
|
}
|
|
9236
9362
|
}
|
|
9237
9363
|
} finally {
|
|
@@ -9244,7 +9370,12 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9244
9370
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9245
9371
|
};
|
|
9246
9372
|
}
|
|
9247
|
-
return {
|
|
9373
|
+
return {
|
|
9374
|
+
attempted: ids.length,
|
|
9375
|
+
succeeded,
|
|
9376
|
+
failed,
|
|
9377
|
+
...lastFailure ? { lastError: lastFailure } : {}
|
|
9378
|
+
};
|
|
9248
9379
|
}
|
|
9249
9380
|
var observations, nextId, projectDir, searchIndexPrepared, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
9250
9381
|
var init_observations = __esm({
|
|
@@ -14881,6 +15012,26 @@ function vectorBatchSize(payload) {
|
|
|
14881
15012
|
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_VECTOR_BATCH_SIZE;
|
|
14882
15013
|
return Math.min(100, Math.max(1, Math.floor(value)));
|
|
14883
15014
|
}
|
|
15015
|
+
function vectorBackfillFailureStreak(payload) {
|
|
15016
|
+
const value = payload.vectorBackfillFailureStreak;
|
|
15017
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.min(16, Math.max(0, Math.floor(value))) : 0;
|
|
15018
|
+
}
|
|
15019
|
+
function vectorBackfillRetryDelayMs(streak) {
|
|
15020
|
+
return Math.min(
|
|
15021
|
+
VECTOR_FAILURE_MAX_RETRY_DELAY_MS,
|
|
15022
|
+
VECTOR_FAILURE_BASE_RETRY_DELAY_MS * 2 ** Math.max(0, streak - 1)
|
|
15023
|
+
);
|
|
15024
|
+
}
|
|
15025
|
+
function withoutVectorBackfillFailureStreak(payload) {
|
|
15026
|
+
const { vectorBackfillFailureStreak: _streak, ...rest } = payload;
|
|
15027
|
+
return rest;
|
|
15028
|
+
}
|
|
15029
|
+
function resolveSupersededVectorFailures(projectDir2, projectId) {
|
|
15030
|
+
try {
|
|
15031
|
+
new MaintenanceJobStore(projectDir2).resolveFailedVectorBackfills(projectId);
|
|
15032
|
+
} catch {
|
|
15033
|
+
}
|
|
15034
|
+
}
|
|
14884
15035
|
function retentionBatchSize(payload) {
|
|
14885
15036
|
const value = payload.limit;
|
|
14886
15037
|
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_RETENTION_BATCH_SIZE;
|
|
@@ -15289,20 +15440,45 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
15289
15440
|
if (isEmbeddingExplicitlyDisabled2()) return { action: "complete" };
|
|
15290
15441
|
const { backfillVectorEmbeddings: backfillVectorEmbeddings2, getVectorStatus: getVectorStatus2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
15291
15442
|
const before = getVectorStatus2(projectId);
|
|
15292
|
-
if (before.missing === 0)
|
|
15443
|
+
if (before.missing === 0) {
|
|
15444
|
+
resolveSupersededVectorFailures(projectDir2, projectId);
|
|
15445
|
+
return { action: "complete" };
|
|
15446
|
+
}
|
|
15293
15447
|
const result = await backfillVectorEmbeddings2({
|
|
15294
15448
|
projectId,
|
|
15295
15449
|
limit: vectorBatchSize(job.payload)
|
|
15296
15450
|
});
|
|
15297
15451
|
const after = getVectorStatus2(projectId);
|
|
15298
|
-
if (after.missing === 0)
|
|
15452
|
+
if (after.missing === 0) {
|
|
15453
|
+
resolveSupersededVectorFailures(projectDir2, projectId);
|
|
15454
|
+
return { action: "complete" };
|
|
15455
|
+
}
|
|
15299
15456
|
if (result.failed > 0 && result.succeeded === 0) {
|
|
15300
|
-
|
|
15457
|
+
const streak = vectorBackfillFailureStreak(job.payload) + 1;
|
|
15458
|
+
return {
|
|
15459
|
+
action: "reschedule",
|
|
15460
|
+
status: "retry",
|
|
15461
|
+
delayMs: vectorBackfillRetryDelayMs(streak),
|
|
15462
|
+
// This is a recoverable provider/index state, not a terminal job error.
|
|
15463
|
+
// Keeping one retry row prevents new MCP processes from creating a storm.
|
|
15464
|
+
resetAttempts: true,
|
|
15465
|
+
payload: {
|
|
15466
|
+
...job.payload,
|
|
15467
|
+
vectorBackfillFailureStreak: streak
|
|
15468
|
+
},
|
|
15469
|
+
lastError: result.lastError ?? `vector backfill made no progress (${result.failed}/${result.attempted} failed)`
|
|
15470
|
+
};
|
|
15301
15471
|
}
|
|
15472
|
+
const priorFailureStreak = vectorBackfillFailureStreak(job.payload);
|
|
15473
|
+
const payload = withoutVectorBackfillFailureStreak(job.payload);
|
|
15302
15474
|
return {
|
|
15303
15475
|
action: "reschedule",
|
|
15304
15476
|
delayMs: result.attempted === 0 ? VECTOR_RETRY_DELAY_MS : 0,
|
|
15305
|
-
resetAttempts: result.succeeded > 0
|
|
15477
|
+
resetAttempts: result.succeeded > 0,
|
|
15478
|
+
...result.succeeded > 0 ? {
|
|
15479
|
+
...priorFailureStreak > 0 ? { payload } : {},
|
|
15480
|
+
...result.failed > 0 && result.lastError ? { lastError: result.lastError } : priorFailureStreak > 0 ? { clearLastError: true } : {}
|
|
15481
|
+
} : {}
|
|
15306
15482
|
};
|
|
15307
15483
|
};
|
|
15308
15484
|
}
|
|
@@ -15315,11 +15491,12 @@ function createProjectMaintenanceDispatcher(projectId, projectDir2, projectRoot,
|
|
|
15315
15491
|
return isolatedRunner({ job, projectRoot, dataDir: projectDir2 });
|
|
15316
15492
|
};
|
|
15317
15493
|
}
|
|
15318
|
-
var DEFAULT_VECTOR_BATCH_SIZE, DEFAULT_RETENTION_BATCH_SIZE, DEFAULT_CONSOLIDATION_BATCH_SIZE, DEFAULT_CODEGRAPH_MAX_FILES, DEFAULT_CLAIM_DERIVATION_BATCH_SIZE, DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE, VECTOR_RETRY_DELAY_MS, DEDUP_PER_PAIR_TIMEOUT_MS;
|
|
15494
|
+
var DEFAULT_VECTOR_BATCH_SIZE, DEFAULT_RETENTION_BATCH_SIZE, DEFAULT_CONSOLIDATION_BATCH_SIZE, DEFAULT_CODEGRAPH_MAX_FILES, DEFAULT_CLAIM_DERIVATION_BATCH_SIZE, DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE, VECTOR_RETRY_DELAY_MS, VECTOR_FAILURE_BASE_RETRY_DELAY_MS, VECTOR_FAILURE_MAX_RETRY_DELAY_MS, DEDUP_PER_PAIR_TIMEOUT_MS;
|
|
15319
15495
|
var init_project_maintenance = __esm({
|
|
15320
15496
|
"src/runtime/project-maintenance.ts"() {
|
|
15321
15497
|
"use strict";
|
|
15322
15498
|
init_esm_shims();
|
|
15499
|
+
init_maintenance_jobs();
|
|
15323
15500
|
init_isolated_maintenance();
|
|
15324
15501
|
init_lifecycle();
|
|
15325
15502
|
init_timeout();
|
|
@@ -15330,6 +15507,8 @@ var init_project_maintenance = __esm({
|
|
|
15330
15507
|
DEFAULT_CLAIM_DERIVATION_BATCH_SIZE = 100;
|
|
15331
15508
|
DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE = 100;
|
|
15332
15509
|
VECTOR_RETRY_DELAY_MS = 5e3;
|
|
15510
|
+
VECTOR_FAILURE_BASE_RETRY_DELAY_MS = 6e4;
|
|
15511
|
+
VECTOR_FAILURE_MAX_RETRY_DELAY_MS = 15 * 6e4;
|
|
15333
15512
|
DEDUP_PER_PAIR_TIMEOUT_MS = 5e3;
|
|
15334
15513
|
}
|
|
15335
15514
|
});
|
|
@@ -15987,7 +16166,7 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15987
16166
|
});
|
|
15988
16167
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
15989
16168
|
const hasContinuation = Boolean(
|
|
15990
|
-
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0
|
|
16169
|
+
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
|
|
15991
16170
|
);
|
|
15992
16171
|
if (hasContinuation && input.continuation) {
|
|
15993
16172
|
appendLine(lines, "", maxTokens, omitted, "continuation-heading");
|
|
@@ -16027,6 +16206,24 @@ function renderTaskWorksetPrompt(input) {
|
|
|
16027
16206
|
}
|
|
16028
16207
|
);
|
|
16029
16208
|
}
|
|
16209
|
+
if (input.continuation.compactCheckpoint) {
|
|
16210
|
+
const checkpoint = input.continuation.compactCheckpoint;
|
|
16211
|
+
const source = `${checkpoint.agent}, ${checkpoint.captureKind}, ${checkpoint.reason}`;
|
|
16212
|
+
appendLine(
|
|
16213
|
+
lines,
|
|
16214
|
+
"- Recent host compact checkpoint (" + source + "): " + short(checkpoint.summary, 36),
|
|
16215
|
+
maxTokens,
|
|
16216
|
+
omitted,
|
|
16217
|
+
"continuation-compact-checkpoint",
|
|
16218
|
+
selected,
|
|
16219
|
+
{
|
|
16220
|
+
kind: "continuation",
|
|
16221
|
+
id: "compact:" + checkpoint.id,
|
|
16222
|
+
reason: "recent host-native compact lifecycle evidence",
|
|
16223
|
+
trust: "historical"
|
|
16224
|
+
}
|
|
16225
|
+
);
|
|
16226
|
+
}
|
|
16030
16227
|
}
|
|
16031
16228
|
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
16032
16229
|
appendLine(lines, "", maxTokens, omitted, "caution-heading");
|
|
@@ -16296,7 +16493,7 @@ async function buildTaskWorkset(input) {
|
|
|
16296
16493
|
...input.verificationHints
|
|
16297
16494
|
]).slice(0, 4);
|
|
16298
16495
|
const normalizedCautions = unique(cautions.map((caution) => caution.kind)).map((kind) => cautions.find((caution) => caution.kind === kind)).slice(0, 6);
|
|
16299
|
-
const continuation = input.continuation && (input.continuation.previousSession || input.continuation.memories.length > 0) ? {
|
|
16496
|
+
const continuation = input.continuation && (input.continuation.previousSession || input.continuation.memories.length > 0 || input.continuation.compactCheckpoint) ? {
|
|
16300
16497
|
...input.continuation.previousSession ? {
|
|
16301
16498
|
previousSession: {
|
|
16302
16499
|
...input.continuation.previousSession,
|
|
@@ -16307,7 +16504,13 @@ async function buildTaskWorkset(input) {
|
|
|
16307
16504
|
...memory,
|
|
16308
16505
|
title: short(memory.title, 20),
|
|
16309
16506
|
...memory.detail ? { detail: short(memory.detail, CONTINUATION_DETAIL_TOKEN_BUDGET) } : {}
|
|
16310
|
-
}))
|
|
16507
|
+
})),
|
|
16508
|
+
...input.continuation.compactCheckpoint ? {
|
|
16509
|
+
compactCheckpoint: {
|
|
16510
|
+
...input.continuation.compactCheckpoint,
|
|
16511
|
+
summary: short(input.continuation.compactCheckpoint.summary, 44)
|
|
16512
|
+
}
|
|
16513
|
+
} : {}
|
|
16311
16514
|
} : void 0;
|
|
16312
16515
|
const base = {
|
|
16313
16516
|
version: "1.2",
|
|
@@ -17123,6 +17326,311 @@ var init_project_context = __esm({
|
|
|
17123
17326
|
}
|
|
17124
17327
|
});
|
|
17125
17328
|
|
|
17329
|
+
// src/store/compaction-checkpoint-store.ts
|
|
17330
|
+
var compaction_checkpoint_store_exports = {};
|
|
17331
|
+
__export(compaction_checkpoint_store_exports, {
|
|
17332
|
+
CompactionCheckpointStore: () => CompactionCheckpointStore
|
|
17333
|
+
});
|
|
17334
|
+
import { createHash as createHash13, randomUUID as randomUUID8 } from "crypto";
|
|
17335
|
+
function optionalText5(value) {
|
|
17336
|
+
return typeof value === "string" && value ? value : void 0;
|
|
17337
|
+
}
|
|
17338
|
+
function optionalNumber2(value) {
|
|
17339
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
17340
|
+
}
|
|
17341
|
+
function parseDetails(value) {
|
|
17342
|
+
if (typeof value !== "string" || !value) return void 0;
|
|
17343
|
+
try {
|
|
17344
|
+
const parsed = JSON.parse(value);
|
|
17345
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
17346
|
+
} catch {
|
|
17347
|
+
return void 0;
|
|
17348
|
+
}
|
|
17349
|
+
}
|
|
17350
|
+
function sanitizeSummary(value) {
|
|
17351
|
+
if (!value?.trim()) return void 0;
|
|
17352
|
+
return sanitizeCredentials(value).slice(0, MAX_SUMMARY_CHARS).trim() || void 0;
|
|
17353
|
+
}
|
|
17354
|
+
function sanitizeDetails(value) {
|
|
17355
|
+
if (!value) return void 0;
|
|
17356
|
+
try {
|
|
17357
|
+
const sanitized = sanitizeCredentials(JSON.stringify(value));
|
|
17358
|
+
if (sanitized.length > MAX_DETAILS_CHARS) return { truncated: true };
|
|
17359
|
+
const parsed = JSON.parse(sanitized);
|
|
17360
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
17361
|
+
} catch {
|
|
17362
|
+
return { unavailable: true };
|
|
17363
|
+
}
|
|
17364
|
+
}
|
|
17365
|
+
function sourceKeyFor(input, summary) {
|
|
17366
|
+
if (input.sourceKey?.trim()) return input.sourceKey.trim().slice(0, 300);
|
|
17367
|
+
const material = [
|
|
17368
|
+
input.projectId,
|
|
17369
|
+
input.sessionId,
|
|
17370
|
+
input.agent,
|
|
17371
|
+
input.sourceEvent,
|
|
17372
|
+
summary ?? "",
|
|
17373
|
+
input.tokensBefore ?? "",
|
|
17374
|
+
input.firstKeptEntryId ?? ""
|
|
17375
|
+
].join("\0");
|
|
17376
|
+
return `derived:${createHash13("sha256").update(material).digest("hex").slice(0, 32)}`;
|
|
17377
|
+
}
|
|
17378
|
+
function rowToCheckpoint(row) {
|
|
17379
|
+
return {
|
|
17380
|
+
id: row.id,
|
|
17381
|
+
projectId: row.project_id,
|
|
17382
|
+
sessionId: row.session_id,
|
|
17383
|
+
agent: row.agent,
|
|
17384
|
+
phase: row.phase,
|
|
17385
|
+
captureKind: row.capture_kind,
|
|
17386
|
+
reason: row.reason,
|
|
17387
|
+
sourceEvent: row.source_event,
|
|
17388
|
+
sourceKey: row.source_key,
|
|
17389
|
+
...optionalText5(row.summary) ? { summary: row.summary } : {},
|
|
17390
|
+
...optionalNumber2(row.tokens_before) !== void 0 ? { tokensBefore: Number(row.tokens_before) } : {},
|
|
17391
|
+
...optionalText5(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
|
|
17392
|
+
...parseDetails(row.details_json) ? { details: parseDetails(row.details_json) } : {},
|
|
17393
|
+
transcriptAvailable: Boolean(row.transcript_available),
|
|
17394
|
+
status: row.status,
|
|
17395
|
+
preCapturedAt: row.pre_captured_at,
|
|
17396
|
+
...optionalText5(row.completed_at) ? { completedAt: row.completed_at } : {},
|
|
17397
|
+
...optionalText5(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
|
|
17398
|
+
deliveryCount: Number(row.delivery_count ?? 0),
|
|
17399
|
+
createdAt: row.created_at,
|
|
17400
|
+
updatedAt: row.updated_at
|
|
17401
|
+
};
|
|
17402
|
+
}
|
|
17403
|
+
function safeReason(value) {
|
|
17404
|
+
return value === "manual" || value === "auto" ? value : "unknown";
|
|
17405
|
+
}
|
|
17406
|
+
function safeLimit(value, fallback) {
|
|
17407
|
+
if (!Number.isFinite(value) || value == null) return fallback;
|
|
17408
|
+
return Math.max(1, Math.min(500, Math.floor(value)));
|
|
17409
|
+
}
|
|
17410
|
+
var MAX_SUMMARY_CHARS, MAX_DETAILS_CHARS, CompactionCheckpointStore;
|
|
17411
|
+
var init_compaction_checkpoint_store = __esm({
|
|
17412
|
+
"src/store/compaction-checkpoint-store.ts"() {
|
|
17413
|
+
"use strict";
|
|
17414
|
+
init_esm_shims();
|
|
17415
|
+
init_secret_filter();
|
|
17416
|
+
init_sqlite_db();
|
|
17417
|
+
MAX_SUMMARY_CHARS = 24e3;
|
|
17418
|
+
MAX_DETAILS_CHARS = 4e3;
|
|
17419
|
+
CompactionCheckpointStore = class {
|
|
17420
|
+
db;
|
|
17421
|
+
constructor(dataDir) {
|
|
17422
|
+
this.db = getDatabase(dataDir);
|
|
17423
|
+
}
|
|
17424
|
+
recordPreflight(input) {
|
|
17425
|
+
const pending = this.db.prepare(`
|
|
17426
|
+
SELECT * FROM compaction_checkpoints
|
|
17427
|
+
WHERE project_id = ? AND session_id = ? AND agent = ?
|
|
17428
|
+
AND phase = 'pre' AND status = 'active'
|
|
17429
|
+
ORDER BY pre_captured_at DESC, created_at DESC
|
|
17430
|
+
LIMIT 1
|
|
17431
|
+
`).get(input.projectId, input.sessionId, input.agent);
|
|
17432
|
+
if (pending) return rowToCheckpoint(pending);
|
|
17433
|
+
const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
17434
|
+
const checkpoint = {
|
|
17435
|
+
id: randomUUID8(),
|
|
17436
|
+
projectId: input.projectId,
|
|
17437
|
+
sessionId: input.sessionId,
|
|
17438
|
+
agent: input.agent,
|
|
17439
|
+
phase: "pre",
|
|
17440
|
+
captureKind: "preflight",
|
|
17441
|
+
reason: safeReason(input.reason),
|
|
17442
|
+
sourceEvent: input.sourceEvent,
|
|
17443
|
+
sourceKey: `pre:${randomUUID8()}`,
|
|
17444
|
+
transcriptAvailable: Boolean(input.transcriptAvailable),
|
|
17445
|
+
status: "active",
|
|
17446
|
+
preCapturedAt: now3,
|
|
17447
|
+
deliveryCount: 0,
|
|
17448
|
+
createdAt: now3,
|
|
17449
|
+
updatedAt: now3
|
|
17450
|
+
};
|
|
17451
|
+
this.insert(checkpoint);
|
|
17452
|
+
return checkpoint;
|
|
17453
|
+
}
|
|
17454
|
+
complete(input) {
|
|
17455
|
+
const completedAt = input.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
17456
|
+
const summary = sanitizeSummary(input.summary);
|
|
17457
|
+
const pending = this.db.prepare(`
|
|
17458
|
+
SELECT * FROM compaction_checkpoints
|
|
17459
|
+
WHERE project_id = ? AND session_id = ? AND agent = ?
|
|
17460
|
+
AND phase = 'pre' AND status = 'active'
|
|
17461
|
+
ORDER BY pre_captured_at DESC, created_at DESC
|
|
17462
|
+
LIMIT 1
|
|
17463
|
+
`).get(input.projectId, input.sessionId, input.agent);
|
|
17464
|
+
if (!input.sourceKey?.trim() && !summary && input.tokensBefore === void 0 && !input.firstKeptEntryId && !pending) {
|
|
17465
|
+
const priorLifecycle = this.db.prepare(`
|
|
17466
|
+
SELECT * FROM compaction_checkpoints
|
|
17467
|
+
WHERE project_id = ? AND session_id = ? AND agent = ?
|
|
17468
|
+
AND phase = 'complete' AND capture_kind = 'lifecycle'
|
|
17469
|
+
AND source_event = ? AND status = 'active'
|
|
17470
|
+
ORDER BY completed_at DESC, created_at DESC
|
|
17471
|
+
LIMIT 1
|
|
17472
|
+
`).get(input.projectId, input.sessionId, input.agent, input.sourceEvent);
|
|
17473
|
+
if (priorLifecycle) return rowToCheckpoint(priorLifecycle);
|
|
17474
|
+
}
|
|
17475
|
+
const sourceKey = input.sourceKey?.trim() ? input.sourceKey.trim().slice(0, 300) : pending ? `lifecycle:${pending.id}` : sourceKeyFor(input, summary);
|
|
17476
|
+
const existing = this.db.prepare(`
|
|
17477
|
+
SELECT * FROM compaction_checkpoints
|
|
17478
|
+
WHERE project_id = ? AND source_key = ?
|
|
17479
|
+
LIMIT 1
|
|
17480
|
+
`).get(input.projectId, sourceKey);
|
|
17481
|
+
if (existing) return rowToCheckpoint(existing);
|
|
17482
|
+
const captureKind = summary ? "native-summary" : "lifecycle";
|
|
17483
|
+
const details = sanitizeDetails(input.details);
|
|
17484
|
+
if (pending) {
|
|
17485
|
+
const checkpoint2 = rowToCheckpoint(pending);
|
|
17486
|
+
const nextReason = safeReason(input.reason) === "unknown" ? checkpoint2.reason : safeReason(input.reason);
|
|
17487
|
+
this.db.prepare(`
|
|
17488
|
+
UPDATE compaction_checkpoints
|
|
17489
|
+
SET phase = 'complete', capture_kind = ?, reason = ?, source_event = ?, source_key = ?,
|
|
17490
|
+
summary = ?, tokens_before = ?, first_kept_entry_id = ?, details_json = ?,
|
|
17491
|
+
completed_at = ?, updated_at = ?
|
|
17492
|
+
WHERE id = ?
|
|
17493
|
+
`).run(
|
|
17494
|
+
captureKind,
|
|
17495
|
+
nextReason,
|
|
17496
|
+
input.sourceEvent,
|
|
17497
|
+
sourceKey,
|
|
17498
|
+
summary ?? null,
|
|
17499
|
+
input.tokensBefore ?? null,
|
|
17500
|
+
input.firstKeptEntryId ?? null,
|
|
17501
|
+
JSON.stringify(details ?? {}),
|
|
17502
|
+
completedAt,
|
|
17503
|
+
completedAt,
|
|
17504
|
+
checkpoint2.id
|
|
17505
|
+
);
|
|
17506
|
+
return this.get(checkpoint2.id);
|
|
17507
|
+
}
|
|
17508
|
+
const checkpoint = {
|
|
17509
|
+
id: randomUUID8(),
|
|
17510
|
+
projectId: input.projectId,
|
|
17511
|
+
sessionId: input.sessionId,
|
|
17512
|
+
agent: input.agent,
|
|
17513
|
+
phase: "complete",
|
|
17514
|
+
captureKind,
|
|
17515
|
+
reason: safeReason(input.reason),
|
|
17516
|
+
sourceEvent: input.sourceEvent,
|
|
17517
|
+
sourceKey,
|
|
17518
|
+
...summary ? { summary } : {},
|
|
17519
|
+
...input.tokensBefore !== void 0 ? { tokensBefore: input.tokensBefore } : {},
|
|
17520
|
+
...input.firstKeptEntryId ? { firstKeptEntryId: input.firstKeptEntryId } : {},
|
|
17521
|
+
...details ? { details } : {},
|
|
17522
|
+
transcriptAvailable: false,
|
|
17523
|
+
status: "active",
|
|
17524
|
+
preCapturedAt: completedAt,
|
|
17525
|
+
completedAt,
|
|
17526
|
+
deliveryCount: 0,
|
|
17527
|
+
createdAt: completedAt,
|
|
17528
|
+
updatedAt: completedAt
|
|
17529
|
+
};
|
|
17530
|
+
this.insert(checkpoint);
|
|
17531
|
+
return checkpoint;
|
|
17532
|
+
}
|
|
17533
|
+
get(id) {
|
|
17534
|
+
const row = this.db.prepare("SELECT * FROM compaction_checkpoints WHERE id = ?").get(id);
|
|
17535
|
+
return row ? rowToCheckpoint(row) : void 0;
|
|
17536
|
+
}
|
|
17537
|
+
list(options) {
|
|
17538
|
+
const conditions = ["project_id = ?"];
|
|
17539
|
+
const values = [options.projectId];
|
|
17540
|
+
if (options.sessionId) {
|
|
17541
|
+
conditions.push("session_id = ?");
|
|
17542
|
+
values.push(options.sessionId);
|
|
17543
|
+
}
|
|
17544
|
+
if (options.agent) {
|
|
17545
|
+
conditions.push("agent = ?");
|
|
17546
|
+
values.push(options.agent);
|
|
17547
|
+
}
|
|
17548
|
+
if (!options.includeArchived) {
|
|
17549
|
+
conditions.push("status = 'active'");
|
|
17550
|
+
}
|
|
17551
|
+
const rows = this.db.prepare(`
|
|
17552
|
+
SELECT * FROM compaction_checkpoints
|
|
17553
|
+
WHERE ${conditions.join(" AND ")}
|
|
17554
|
+
ORDER BY COALESCE(completed_at, pre_captured_at) DESC, created_at DESC
|
|
17555
|
+
LIMIT ?
|
|
17556
|
+
`).all(...values, safeLimit(options.limit, 20));
|
|
17557
|
+
return rows.map(rowToCheckpoint);
|
|
17558
|
+
}
|
|
17559
|
+
findUndelivered(projectId, sessionId, agent) {
|
|
17560
|
+
const row = this.db.prepare(`
|
|
17561
|
+
SELECT * FROM compaction_checkpoints
|
|
17562
|
+
WHERE project_id = ? AND session_id = ? AND agent = ?
|
|
17563
|
+
AND phase = 'complete' AND status = 'active' AND delivered_at IS NULL
|
|
17564
|
+
ORDER BY completed_at DESC, created_at DESC
|
|
17565
|
+
LIMIT 1
|
|
17566
|
+
`).get(projectId, sessionId, agent);
|
|
17567
|
+
return row ? rowToCheckpoint(row) : void 0;
|
|
17568
|
+
}
|
|
17569
|
+
findLatestCompleted(projectId, options = {}) {
|
|
17570
|
+
const excludeSession = options.excludeSession;
|
|
17571
|
+
const exclusion = excludeSession ? " AND NOT (session_id = ? AND agent = ? )" : "";
|
|
17572
|
+
const values = excludeSession ? [projectId, excludeSession.sessionId, excludeSession.agent] : [projectId];
|
|
17573
|
+
const row = this.db.prepare(`
|
|
17574
|
+
SELECT * FROM compaction_checkpoints
|
|
17575
|
+
WHERE project_id = ? AND phase = 'complete' AND status = 'active'
|
|
17576
|
+
${exclusion}
|
|
17577
|
+
ORDER BY completed_at DESC, created_at DESC
|
|
17578
|
+
LIMIT 1
|
|
17579
|
+
`).get(...values);
|
|
17580
|
+
return row ? rowToCheckpoint(row) : void 0;
|
|
17581
|
+
}
|
|
17582
|
+
archive(id, archivedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
17583
|
+
const result = this.db.prepare(`
|
|
17584
|
+
UPDATE compaction_checkpoints
|
|
17585
|
+
SET status = 'archived', updated_at = ?
|
|
17586
|
+
WHERE id = ? AND status = 'active'
|
|
17587
|
+
`).run(archivedAt, id);
|
|
17588
|
+
return Number(result.changes ?? 0) > 0 ? this.get(id) : void 0;
|
|
17589
|
+
}
|
|
17590
|
+
markDelivered(id, deliveredAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
17591
|
+
const result = this.db.prepare(`
|
|
17592
|
+
UPDATE compaction_checkpoints
|
|
17593
|
+
SET delivered_at = ?, delivery_count = delivery_count + 1, updated_at = ?
|
|
17594
|
+
WHERE id = ? AND phase = 'complete' AND status = 'active' AND delivered_at IS NULL
|
|
17595
|
+
`).run(deliveredAt, deliveredAt, id);
|
|
17596
|
+
return Number(result.changes ?? 0) > 0 ? this.get(id) : void 0;
|
|
17597
|
+
}
|
|
17598
|
+
insert(checkpoint) {
|
|
17599
|
+
this.db.prepare(`
|
|
17600
|
+
INSERT INTO compaction_checkpoints (
|
|
17601
|
+
id, project_id, session_id, agent, phase, capture_kind, reason,
|
|
17602
|
+
source_event, source_key, summary, tokens_before, first_kept_entry_id,
|
|
17603
|
+
details_json, transcript_available, status, pre_captured_at, completed_at,
|
|
17604
|
+
delivered_at, delivery_count, created_at, updated_at
|
|
17605
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
17606
|
+
`).run(
|
|
17607
|
+
checkpoint.id,
|
|
17608
|
+
checkpoint.projectId,
|
|
17609
|
+
checkpoint.sessionId,
|
|
17610
|
+
checkpoint.agent,
|
|
17611
|
+
checkpoint.phase,
|
|
17612
|
+
checkpoint.captureKind,
|
|
17613
|
+
checkpoint.reason,
|
|
17614
|
+
checkpoint.sourceEvent,
|
|
17615
|
+
checkpoint.sourceKey,
|
|
17616
|
+
checkpoint.summary ?? null,
|
|
17617
|
+
checkpoint.tokensBefore ?? null,
|
|
17618
|
+
checkpoint.firstKeptEntryId ?? null,
|
|
17619
|
+
JSON.stringify(checkpoint.details ?? {}),
|
|
17620
|
+
checkpoint.transcriptAvailable ? 1 : 0,
|
|
17621
|
+
checkpoint.status,
|
|
17622
|
+
checkpoint.preCapturedAt,
|
|
17623
|
+
checkpoint.completedAt ?? null,
|
|
17624
|
+
checkpoint.deliveredAt ?? null,
|
|
17625
|
+
checkpoint.deliveryCount,
|
|
17626
|
+
checkpoint.createdAt,
|
|
17627
|
+
checkpoint.updatedAt
|
|
17628
|
+
);
|
|
17629
|
+
}
|
|
17630
|
+
};
|
|
17631
|
+
}
|
|
17632
|
+
});
|
|
17633
|
+
|
|
17126
17634
|
// src/codegraph/auto-context.ts
|
|
17127
17635
|
var auto_context_exports = {};
|
|
17128
17636
|
__export(auto_context_exports, {
|
|
@@ -17308,6 +17816,22 @@ async function buildAutoProjectContext(input) {
|
|
|
17308
17816
|
if (continuationRequested) {
|
|
17309
17817
|
await initSessionStore(input.dataDir);
|
|
17310
17818
|
continuation = await getSessionResumeBrief(input.project.id, task, input.reader);
|
|
17819
|
+
const { CompactionCheckpointStore: CompactionCheckpointStore2 } = await Promise.resolve().then(() => (init_compaction_checkpoint_store(), compaction_checkpoint_store_exports));
|
|
17820
|
+
const checkpoint = new CompactionCheckpointStore2(input.dataDir).findLatestCompleted(
|
|
17821
|
+
input.project.id,
|
|
17822
|
+
input.excludeCompactionCheckpointFor ? { excludeSession: input.excludeCompactionCheckpointFor } : void 0
|
|
17823
|
+
);
|
|
17824
|
+
const completedAtMs = checkpoint ? Date.parse(checkpoint.completedAt ?? checkpoint.preCapturedAt) : Number.NaN;
|
|
17825
|
+
if (checkpoint && Number.isFinite(completedAtMs) && completedAtMs <= now3.getTime() && now3.getTime() - completedAtMs <= COMPACT_CHECKPOINT_MAX_AGE_MS) {
|
|
17826
|
+
continuation.compactCheckpoint = {
|
|
17827
|
+
id: checkpoint.id,
|
|
17828
|
+
agent: checkpoint.agent,
|
|
17829
|
+
captureKind: checkpoint.captureKind === "native-summary" ? "native-summary" : "lifecycle",
|
|
17830
|
+
reason: checkpoint.reason,
|
|
17831
|
+
...checkpoint.completedAt ? { completedAt: checkpoint.completedAt } : {},
|
|
17832
|
+
summary: checkpoint.summary ?? "The host completed context compaction without exposing a native summary. Reconstruct only what the current task needs from current code and durable evidence."
|
|
17833
|
+
};
|
|
17834
|
+
}
|
|
17311
17835
|
}
|
|
17312
17836
|
const workset = await buildTaskWorkset({
|
|
17313
17837
|
projectId: input.project.id,
|
|
@@ -17516,7 +18040,7 @@ function formatAutoProjectContextSummary(context) {
|
|
|
17516
18040
|
reliableSources.length > 0 ? `- ${reliableSources.length} current code-bound memory link(s)` : "- none yet"
|
|
17517
18041
|
);
|
|
17518
18042
|
const continuation = context.workset.continuation;
|
|
17519
|
-
if (continuation?.previousSession || continuation?.memories.length) {
|
|
18043
|
+
if (continuation?.previousSession || continuation?.memories.length || continuation?.compactCheckpoint) {
|
|
17520
18044
|
lines.push("", "Resume from prior work");
|
|
17521
18045
|
if (continuation.previousSession) {
|
|
17522
18046
|
const session = continuation.previousSession;
|
|
@@ -17531,6 +18055,12 @@ function formatAutoProjectContextSummary(context) {
|
|
|
17531
18055
|
"- #" + memory.id + " " + memory.type + ": " + compactContinuationText(memory.title, 18) + detail
|
|
17532
18056
|
);
|
|
17533
18057
|
}
|
|
18058
|
+
if (continuation.compactCheckpoint) {
|
|
18059
|
+
const checkpoint = continuation.compactCheckpoint;
|
|
18060
|
+
lines.push(
|
|
18061
|
+
"- Recent host compact checkpoint (" + [checkpoint.agent, checkpoint.captureKind, checkpoint.reason].join(", ") + "): " + compactContinuationText(checkpoint.summary, 36)
|
|
18062
|
+
);
|
|
18063
|
+
}
|
|
17534
18064
|
}
|
|
17535
18065
|
return lines.join("\n");
|
|
17536
18066
|
}
|
|
@@ -17597,7 +18127,7 @@ function formatLegacyAutoProjectContextPrompt(context) {
|
|
|
17597
18127
|
);
|
|
17598
18128
|
return lines.join("\n");
|
|
17599
18129
|
}
|
|
17600
|
-
var DEFAULT_MAX_AGE_MS;
|
|
18130
|
+
var DEFAULT_MAX_AGE_MS, COMPACT_CHECKPOINT_MAX_AGE_MS;
|
|
17601
18131
|
var init_auto_context = __esm({
|
|
17602
18132
|
"src/codegraph/auto-context.ts"() {
|
|
17603
18133
|
"use strict";
|
|
@@ -17617,6 +18147,7 @@ var init_auto_context = __esm({
|
|
|
17617
18147
|
init_session_store();
|
|
17618
18148
|
init_task_lens();
|
|
17619
18149
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
18150
|
+
COMPACT_CHECKPOINT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
17620
18151
|
}
|
|
17621
18152
|
});
|
|
17622
18153
|
|
|
@@ -17664,7 +18195,7 @@ function addDefaultVerificationHints(input) {
|
|
|
17664
18195
|
return uniq2(hints);
|
|
17665
18196
|
}
|
|
17666
18197
|
function selectRelevantObservations(observations2, task, limit) {
|
|
17667
|
-
const
|
|
18198
|
+
const safeLimit2 = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 20;
|
|
17668
18199
|
const taskTokens2 = tokenize3(task);
|
|
17669
18200
|
const ranked = observations2.map((observation, index) => ({
|
|
17670
18201
|
observation,
|
|
@@ -17674,7 +18205,7 @@ function selectRelevantObservations(observations2, task, limit) {
|
|
|
17674
18205
|
}));
|
|
17675
18206
|
const positives = ranked.filter((item) => item.score > 0);
|
|
17676
18207
|
const pool = positives.length > 0 ? positives : ranked;
|
|
17677
|
-
return pool.sort((a, b) => b.score - a.score || b.time - a.time || b.index - a.index).slice(0,
|
|
18208
|
+
return pool.sort((a, b) => b.score - a.score || b.time - a.time || b.index - a.index).slice(0, safeLimit2).map((item) => item.observation);
|
|
17678
18209
|
}
|
|
17679
18210
|
function assembleContextPackForTask(input) {
|
|
17680
18211
|
const selected = selectRelevantObservations(input.observations, input.task, input.limit ?? 20);
|
|
@@ -18234,7 +18765,7 @@ __export(team_store_exports, {
|
|
|
18234
18765
|
isTeamStoreInitialized: () => isTeamStoreInitialized,
|
|
18235
18766
|
resetTeamStore: () => resetTeamStore
|
|
18236
18767
|
});
|
|
18237
|
-
import { randomUUID as
|
|
18768
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
18238
18769
|
import path20 from "path";
|
|
18239
18770
|
import fs15 from "fs";
|
|
18240
18771
|
function getTeamStore() {
|
|
@@ -18523,7 +19054,7 @@ var init_team_store = __esm({
|
|
|
18523
19054
|
* reactivate it (preserving agent_id). Otherwise create a new one.
|
|
18524
19055
|
*/
|
|
18525
19056
|
registerAgent(input) {
|
|
18526
|
-
const instanceId = input.instanceId ||
|
|
19057
|
+
const instanceId = input.instanceId || randomUUID9();
|
|
18527
19058
|
const now3 = Date.now();
|
|
18528
19059
|
const existing = this.stmtAgentFindByInstance.get(
|
|
18529
19060
|
input.projectId,
|
|
@@ -18549,7 +19080,7 @@ var init_team_store = __esm({
|
|
|
18549
19080
|
this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
|
|
18550
19081
|
return row2;
|
|
18551
19082
|
}
|
|
18552
|
-
const agentId =
|
|
19083
|
+
const agentId = randomUUID9();
|
|
18553
19084
|
this.stmtAgentUpsert.run({
|
|
18554
19085
|
agent_id: agentId,
|
|
18555
19086
|
project_id: input.projectId,
|
|
@@ -18651,7 +19182,7 @@ var init_team_store = __esm({
|
|
|
18651
19182
|
return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
|
|
18652
19183
|
}
|
|
18653
19184
|
}
|
|
18654
|
-
const id =
|
|
19185
|
+
const id = randomUUID9();
|
|
18655
19186
|
const now3 = Date.now();
|
|
18656
19187
|
const row = {
|
|
18657
19188
|
id,
|
|
@@ -18701,7 +19232,7 @@ var init_team_store = __esm({
|
|
|
18701
19232
|
// Task Operations (with atomic claim semantics)
|
|
18702
19233
|
// ═══════════════════════════════════════════════════════════════════
|
|
18703
19234
|
createTask(input) {
|
|
18704
|
-
const taskId =
|
|
19235
|
+
const taskId = randomUUID9();
|
|
18705
19236
|
const now3 = Date.now();
|
|
18706
19237
|
const row = {
|
|
18707
19238
|
task_id: taskId,
|
|
@@ -18973,7 +19504,7 @@ var init_team_store = __esm({
|
|
|
18973
19504
|
for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
|
|
18974
19505
|
for (const msg of msgs) {
|
|
18975
19506
|
this.stmtMsgInsert.run({
|
|
18976
|
-
id: msg.id ??
|
|
19507
|
+
id: msg.id ?? randomUUID9(),
|
|
18977
19508
|
project_id: "migrated",
|
|
18978
19509
|
sender_agent_id: msg.from ?? "unknown",
|
|
18979
19510
|
recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
|
|
@@ -19213,6 +19744,140 @@ var init_poll = __esm({
|
|
|
19213
19744
|
}
|
|
19214
19745
|
});
|
|
19215
19746
|
|
|
19747
|
+
// src/memory/compaction.ts
|
|
19748
|
+
var compaction_exports = {};
|
|
19749
|
+
__export(compaction_exports, {
|
|
19750
|
+
buildCompactionWorkset: () => buildCompactionWorkset,
|
|
19751
|
+
captureCompactionCheckpoint: () => captureCompactionCheckpoint,
|
|
19752
|
+
consumeCompactionWorkset: () => consumeCompactionWorkset
|
|
19753
|
+
});
|
|
19754
|
+
function normalizeBudget(value) {
|
|
19755
|
+
if (!Number.isFinite(value) || value == null) return DEFAULT_WORKSET_BUDGET;
|
|
19756
|
+
return Math.max(MIN_WORKSET_BUDGET, Math.min(2e3, Math.floor(value)));
|
|
19757
|
+
}
|
|
19758
|
+
function sourceLabel(checkpoint) {
|
|
19759
|
+
if (checkpoint.captureKind === "native-summary") return "native host summary";
|
|
19760
|
+
if (checkpoint.captureKind === "preflight") return "pre-compact lifecycle marker";
|
|
19761
|
+
return "host lifecycle marker";
|
|
19762
|
+
}
|
|
19763
|
+
function buildCompactionWorkset(checkpoint, options = {}) {
|
|
19764
|
+
const budget = normalizeBudget(options.maxTokens);
|
|
19765
|
+
const task = options.task?.trim() ? truncateToTokenBudget(sanitizeCredentials(options.task.trim()), Math.max(8, Math.floor(budget * 0.25))) : "";
|
|
19766
|
+
const headerLines = [
|
|
19767
|
+
"## Compact Continuation",
|
|
19768
|
+
`- Host: ${checkpoint.agent} (${sourceLabel(checkpoint)})`,
|
|
19769
|
+
`- Trigger: ${checkpoint.reason}`,
|
|
19770
|
+
task ? `- Task now: ${task}` : "",
|
|
19771
|
+
"- Current code remains authoritative."
|
|
19772
|
+
].filter(Boolean);
|
|
19773
|
+
const header = `${headerLines.join("\n")}
|
|
19774
|
+
|
|
19775
|
+
`;
|
|
19776
|
+
const fallback = checkpoint.captureKind === "native-summary" ? "" : "The host did not expose a native compact summary. Reconstruct only what the current task needs from current code and Memorix evidence.";
|
|
19777
|
+
const source = sanitizeCredentials(checkpoint.summary?.trim() || fallback);
|
|
19778
|
+
const available = Math.max(0, budget - countTextTokens(header) - 4);
|
|
19779
|
+
let excerpt = source ? truncateToTokenBudget(source, available) : "";
|
|
19780
|
+
let text = `${header}${excerpt ? `### Host checkpoint
|
|
19781
|
+
${excerpt}` : ""}`.trim();
|
|
19782
|
+
while (excerpt && countTextTokens(text) > budget) {
|
|
19783
|
+
const nextExcerptBudget = Math.max(1, Math.floor(countTextTokens(excerpt) * 0.8));
|
|
19784
|
+
excerpt = truncateToTokenBudget(excerpt, nextExcerptBudget);
|
|
19785
|
+
text = `${header}${excerpt ? `### Host checkpoint
|
|
19786
|
+
${excerpt}` : ""}`.trim();
|
|
19787
|
+
}
|
|
19788
|
+
if (countTextTokens(text) > budget) text = truncateToTokenBudget(text, budget);
|
|
19789
|
+
return {
|
|
19790
|
+
checkpointId: checkpoint.id,
|
|
19791
|
+
text,
|
|
19792
|
+
tokens: countTextTokens(text)
|
|
19793
|
+
};
|
|
19794
|
+
}
|
|
19795
|
+
function sourceEvent(input) {
|
|
19796
|
+
const raw = input.raw;
|
|
19797
|
+
const event = raw.hook_event_name ?? raw.event ?? raw.type;
|
|
19798
|
+
return typeof event === "string" && event ? event : input.event;
|
|
19799
|
+
}
|
|
19800
|
+
async function captureCompactionCheckpoint(input) {
|
|
19801
|
+
const isCompactResume = input.event === "session_start" && input.sessionStartReason?.trim().toLowerCase() === "compact";
|
|
19802
|
+
if (input.event !== "pre_compact" && input.event !== "post_compact" && !isCompactResume) return null;
|
|
19803
|
+
if (!input.sessionId || !input.cwd) return null;
|
|
19804
|
+
const [
|
|
19805
|
+
{ detectProject: detectProject2 },
|
|
19806
|
+
{ getProjectDataDir: getProjectDataDir2 },
|
|
19807
|
+
{ initAliasRegistry: initAliasRegistry2, registerAlias: registerAlias2 }
|
|
19808
|
+
] = await Promise.all([
|
|
19809
|
+
Promise.resolve().then(() => (init_detector(), detector_exports)),
|
|
19810
|
+
Promise.resolve().then(() => (init_persistence(), persistence_exports)),
|
|
19811
|
+
Promise.resolve().then(() => (init_aliases(), aliases_exports))
|
|
19812
|
+
]);
|
|
19813
|
+
const project = detectProject2(input.cwd);
|
|
19814
|
+
if (!project) return null;
|
|
19815
|
+
const dataDir = await getProjectDataDir2(project.id);
|
|
19816
|
+
initAliasRegistry2(dataDir);
|
|
19817
|
+
const projectId = await registerAlias2(project);
|
|
19818
|
+
const store = new CompactionCheckpointStore(dataDir);
|
|
19819
|
+
if (input.event === "pre_compact") {
|
|
19820
|
+
return store.recordPreflight({
|
|
19821
|
+
projectId,
|
|
19822
|
+
sessionId: input.sessionId,
|
|
19823
|
+
agent: input.agent,
|
|
19824
|
+
reason: input.compaction?.reason,
|
|
19825
|
+
sourceEvent: sourceEvent(input),
|
|
19826
|
+
transcriptAvailable: Boolean(input.transcriptPath),
|
|
19827
|
+
capturedAt: input.timestamp
|
|
19828
|
+
});
|
|
19829
|
+
}
|
|
19830
|
+
return store.complete({
|
|
19831
|
+
projectId,
|
|
19832
|
+
sessionId: input.sessionId,
|
|
19833
|
+
agent: input.agent,
|
|
19834
|
+
reason: input.compaction?.reason,
|
|
19835
|
+
sourceEvent: sourceEvent(input),
|
|
19836
|
+
sourceKey: input.compaction?.sourceKey,
|
|
19837
|
+
summary: input.compaction?.summary,
|
|
19838
|
+
tokensBefore: input.compaction?.tokensBefore,
|
|
19839
|
+
firstKeptEntryId: input.compaction?.firstKeptEntryId,
|
|
19840
|
+
details: input.compaction?.details,
|
|
19841
|
+
completedAt: input.timestamp
|
|
19842
|
+
});
|
|
19843
|
+
}
|
|
19844
|
+
async function consumeCompactionWorkset(input, options = {}) {
|
|
19845
|
+
if (!input.sessionId || !input.cwd) return null;
|
|
19846
|
+
const [
|
|
19847
|
+
{ detectProject: detectProject2 },
|
|
19848
|
+
{ getProjectDataDir: getProjectDataDir2 },
|
|
19849
|
+
{ initAliasRegistry: initAliasRegistry2, registerAlias: registerAlias2 }
|
|
19850
|
+
] = await Promise.all([
|
|
19851
|
+
Promise.resolve().then(() => (init_detector(), detector_exports)),
|
|
19852
|
+
Promise.resolve().then(() => (init_persistence(), persistence_exports)),
|
|
19853
|
+
Promise.resolve().then(() => (init_aliases(), aliases_exports))
|
|
19854
|
+
]);
|
|
19855
|
+
const project = detectProject2(input.cwd);
|
|
19856
|
+
if (!project) return null;
|
|
19857
|
+
const dataDir = await getProjectDataDir2(project.id);
|
|
19858
|
+
initAliasRegistry2(dataDir);
|
|
19859
|
+
const projectId = await registerAlias2(project);
|
|
19860
|
+
const store = new CompactionCheckpointStore(dataDir);
|
|
19861
|
+
const checkpoint = store.findUndelivered(projectId, input.sessionId, input.agent);
|
|
19862
|
+
if (!checkpoint) return null;
|
|
19863
|
+
const workset = buildCompactionWorkset(checkpoint, options);
|
|
19864
|
+
if (!workset.text) return null;
|
|
19865
|
+
store.markDelivered(checkpoint.id);
|
|
19866
|
+
return workset;
|
|
19867
|
+
}
|
|
19868
|
+
var DEFAULT_WORKSET_BUDGET, MIN_WORKSET_BUDGET;
|
|
19869
|
+
var init_compaction = __esm({
|
|
19870
|
+
"src/memory/compaction.ts"() {
|
|
19871
|
+
"use strict";
|
|
19872
|
+
init_esm_shims();
|
|
19873
|
+
init_token_budget();
|
|
19874
|
+
init_secret_filter();
|
|
19875
|
+
init_compaction_checkpoint_store();
|
|
19876
|
+
DEFAULT_WORKSET_BUDGET = 420;
|
|
19877
|
+
MIN_WORKSET_BUDGET = 48;
|
|
19878
|
+
}
|
|
19879
|
+
});
|
|
19880
|
+
|
|
19216
19881
|
// src/memory/export-import.ts
|
|
19217
19882
|
var export_import_exports = {};
|
|
19218
19883
|
__export(export_import_exports, {
|
|
@@ -20178,6 +20843,17 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
20178
20843
|
sourceCounts,
|
|
20179
20844
|
recentObservations: sorted,
|
|
20180
20845
|
embedding: embeddingStatus,
|
|
20846
|
+
// A standalone dashboard has no access to an active MCP
|
|
20847
|
+
// process's in-memory Orama index. Keep this explicit so the
|
|
20848
|
+
// UI never presents an empty local singleton as a healthy
|
|
20849
|
+
// all-vectors-indexed result.
|
|
20850
|
+
vectorStatus: {
|
|
20851
|
+
available: false,
|
|
20852
|
+
total: 0,
|
|
20853
|
+
missing: 0,
|
|
20854
|
+
missingIds: [],
|
|
20855
|
+
backfillRunning: false
|
|
20856
|
+
},
|
|
20181
20857
|
storage: storageInfo,
|
|
20182
20858
|
maintenance,
|
|
20183
20859
|
...lifecycle ? { lifecycle } : {},
|
|
@@ -21291,7 +21967,7 @@ __export(planner_exports, {
|
|
|
21291
21967
|
materializeTaskGraph: () => materializeTaskGraph,
|
|
21292
21968
|
seedAutonomousPipeline: () => seedAutonomousPipeline
|
|
21293
21969
|
});
|
|
21294
|
-
import { randomUUID as
|
|
21970
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
21295
21971
|
function isPlannerTask(metadata) {
|
|
21296
21972
|
if (!metadata) return null;
|
|
21297
21973
|
try {
|
|
@@ -21349,7 +22025,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
|
|
|
21349
22025
|
const maxIterations = config.maxIterations ?? 3;
|
|
21350
22026
|
const taskBudget = config.taskBudget ?? 15;
|
|
21351
22027
|
const structuredPlan = opts?.structuredPlan ?? true;
|
|
21352
|
-
const pipelineId =
|
|
22028
|
+
const pipelineId = randomUUID10();
|
|
21353
22029
|
const meta = {
|
|
21354
22030
|
plannerType: "plan",
|
|
21355
22031
|
pipelineId,
|
|
@@ -23332,7 +24008,7 @@ init_freshness();
|
|
|
23332
24008
|
init_obs_store();
|
|
23333
24009
|
init_mini_skill_store();
|
|
23334
24010
|
init_session_store();
|
|
23335
|
-
import { createHash as
|
|
24011
|
+
import { createHash as createHash14 } from "crypto";
|
|
23336
24012
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23337
24013
|
import { z as z2 } from "zod";
|
|
23338
24014
|
|
|
@@ -26802,6 +27478,7 @@ var TOOL_PROFILES = Object.freeze({
|
|
|
26802
27478
|
memorix_rules_sync: ["full"],
|
|
26803
27479
|
memorix_workspace_sync: ["full"],
|
|
26804
27480
|
memorix_ingest_image: ["full"],
|
|
27481
|
+
memorix_compaction_checkpoint: ["full"],
|
|
26805
27482
|
// ── MCP Official Memory Server compatibility (KG tools) ──────────
|
|
26806
27483
|
// These are only useful to users specifically migrating from the
|
|
26807
27484
|
// reference mcp-memory server. Hide them unless explicitly enabled.
|
|
@@ -26968,7 +27645,7 @@ function coerceObjectArray(val) {
|
|
|
26968
27645
|
return [];
|
|
26969
27646
|
}
|
|
26970
27647
|
function createDeterministicInstanceId(projectId, agentType, agentName) {
|
|
26971
|
-
const digest2 =
|
|
27648
|
+
const digest2 = createHash14("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
|
|
26972
27649
|
return `auto-${digest2}`;
|
|
26973
27650
|
}
|
|
26974
27651
|
var BOOTSTRAP_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -27249,7 +27926,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
27249
27926
|
};
|
|
27250
27927
|
const server = existingServer ?? new McpServer({
|
|
27251
27928
|
name: "memorix",
|
|
27252
|
-
version: true ? "1.2.
|
|
27929
|
+
version: true ? "1.2.8" : "1.0.1"
|
|
27253
27930
|
});
|
|
27254
27931
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
27255
27932
|
server.registerTool = ((name, ...args) => {
|
|
@@ -27897,12 +28574,12 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
27897
28574
|
if (boundary) return boundary;
|
|
27898
28575
|
}
|
|
27899
28576
|
return withFreshIndex(async () => {
|
|
27900
|
-
const
|
|
28577
|
+
const safeLimit2 = limit != null ? coerceNumber(limit, 20) : void 0;
|
|
27901
28578
|
const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
|
|
27902
28579
|
const TIMEOUT_MS = 3e4;
|
|
27903
28580
|
const searchPromise = compactSearch({
|
|
27904
28581
|
query,
|
|
27905
|
-
limit:
|
|
28582
|
+
limit: safeLimit2,
|
|
27906
28583
|
type,
|
|
27907
28584
|
maxTokens: safeMaxTokens,
|
|
27908
28585
|
since,
|
|
@@ -28703,10 +29380,10 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
28703
29380
|
const unresolved = requireResolvedProject("search reasoning in the current project");
|
|
28704
29381
|
if (unresolved) return unresolved;
|
|
28705
29382
|
}
|
|
28706
|
-
const
|
|
29383
|
+
const safeLimit2 = limit != null ? coerceNumber(limit, 10) : 10;
|
|
28707
29384
|
const result = await withFreshIndex(() => compactSearch({
|
|
28708
29385
|
query,
|
|
28709
|
-
limit:
|
|
29386
|
+
limit: safeLimit2,
|
|
28710
29387
|
type: "reasoning",
|
|
28711
29388
|
projectId: scope === "global" ? void 0 : project.id,
|
|
28712
29389
|
status: "active",
|
|
@@ -30029,9 +30706,9 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
30029
30706
|
}
|
|
30030
30707
|
},
|
|
30031
30708
|
async ({ limit }) => {
|
|
30032
|
-
const
|
|
30709
|
+
const safeLimit2 = limit != null ? coerceNumber(limit, 3) : 3;
|
|
30033
30710
|
const { getSessionContext: getSessionContext2, listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
30034
|
-
const context = await getSessionContext2(projectDir2, project.id,
|
|
30711
|
+
const context = await getSessionContext2(projectDir2, project.id, safeLimit2, getObservationReader());
|
|
30035
30712
|
const sessions = await listSessions2(projectDir2, project.id);
|
|
30036
30713
|
const activeSessions = sessions.filter((s) => s.status === "active");
|
|
30037
30714
|
const completedSessions = sessions.filter((s) => s.status === "completed");
|
|
@@ -30052,6 +30729,86 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
30052
30729
|
};
|
|
30053
30730
|
}
|
|
30054
30731
|
);
|
|
30732
|
+
server.registerTool(
|
|
30733
|
+
"memorix_compaction_checkpoint",
|
|
30734
|
+
{
|
|
30735
|
+
title: "Compact Continuity Checkpoints",
|
|
30736
|
+
description: "Inspect, preview, or archive bounded checkpoints recorded around a host-native context compaction. Use only to debug or explicitly review compaction continuity. These are lifecycle records, not durable memories or transcript backups. CLI equivalent: memorix checkpoint list|show|context|archive.",
|
|
30737
|
+
inputSchema: {
|
|
30738
|
+
action: z2.enum(["list", "show", "context", "archive"]).default("list"),
|
|
30739
|
+
id: z2.string().optional().describe("Checkpoint ID for show, context, or archive"),
|
|
30740
|
+
sessionId: z2.string().optional().describe("Optional host session ID filter"),
|
|
30741
|
+
agent: z2.string().optional().describe("Optional host agent filter"),
|
|
30742
|
+
task: z2.string().optional().describe("Current task for a bounded context preview"),
|
|
30743
|
+
maxTokens: z2.number().optional().describe("Maximum tokens for action=context (default: 420)"),
|
|
30744
|
+
limit: z2.number().optional().describe("Maximum records for action=list (default: 20)"),
|
|
30745
|
+
includeArchived: z2.boolean().optional().default(false).describe("Include archived records in action=list")
|
|
30746
|
+
}
|
|
30747
|
+
},
|
|
30748
|
+
async ({ action, id, sessionId, agent, task, maxTokens, limit, includeArchived }) => {
|
|
30749
|
+
const unresolved = requireResolvedProject("inspect compact continuity checkpoints");
|
|
30750
|
+
if (unresolved) return unresolved;
|
|
30751
|
+
const [{ CompactionCheckpointStore: CompactionCheckpointStore2 }, { buildCompactionWorkset: buildCompactionWorkset2 }] = await Promise.all([
|
|
30752
|
+
Promise.resolve().then(() => (init_compaction_checkpoint_store(), compaction_checkpoint_store_exports)),
|
|
30753
|
+
Promise.resolve().then(() => (init_compaction(), compaction_exports))
|
|
30754
|
+
]);
|
|
30755
|
+
const store = new CompactionCheckpointStore2(projectDir2);
|
|
30756
|
+
const assertCheckpoint = (checkpointId) => {
|
|
30757
|
+
const checkpoint2 = store.get(checkpointId);
|
|
30758
|
+
if (!checkpoint2 || checkpoint2.projectId !== project.id) return null;
|
|
30759
|
+
return checkpoint2;
|
|
30760
|
+
};
|
|
30761
|
+
if (action === "list") {
|
|
30762
|
+
const checkpoints = store.list({
|
|
30763
|
+
projectId: project.id,
|
|
30764
|
+
sessionId: sessionId?.trim() || void 0,
|
|
30765
|
+
agent: agent?.trim() || void 0,
|
|
30766
|
+
includeArchived: Boolean(includeArchived),
|
|
30767
|
+
limit: limit != null ? Math.max(1, Math.min(100, coerceNumber(limit, 20))) : 20
|
|
30768
|
+
});
|
|
30769
|
+
return {
|
|
30770
|
+
content: [{ type: "text", text: JSON.stringify({ projectId: project.id, checkpoints }, null, 2) }]
|
|
30771
|
+
};
|
|
30772
|
+
}
|
|
30773
|
+
if (!id?.trim()) {
|
|
30774
|
+
return {
|
|
30775
|
+
content: [{ type: "text", text: "id is required for this checkpoint action." }],
|
|
30776
|
+
isError: true
|
|
30777
|
+
};
|
|
30778
|
+
}
|
|
30779
|
+
const checkpoint = assertCheckpoint(id.trim());
|
|
30780
|
+
if (!checkpoint) {
|
|
30781
|
+
return {
|
|
30782
|
+
content: [{ type: "text", text: `Checkpoint "${id.trim()}" was not found for the current project.` }],
|
|
30783
|
+
isError: true
|
|
30784
|
+
};
|
|
30785
|
+
}
|
|
30786
|
+
if (action === "show") {
|
|
30787
|
+
return {
|
|
30788
|
+
content: [{ type: "text", text: JSON.stringify({ projectId: project.id, checkpoint }, null, 2) }]
|
|
30789
|
+
};
|
|
30790
|
+
}
|
|
30791
|
+
if (action === "context") {
|
|
30792
|
+
const workset = buildCompactionWorkset2(checkpoint, {
|
|
30793
|
+
task: task?.trim(),
|
|
30794
|
+
maxTokens: maxTokens != null ? coerceNumber(maxTokens, 420) : 420
|
|
30795
|
+
});
|
|
30796
|
+
return {
|
|
30797
|
+
content: [{ type: "text", text: workset.text }]
|
|
30798
|
+
};
|
|
30799
|
+
}
|
|
30800
|
+
const archived = store.archive(checkpoint.id);
|
|
30801
|
+
if (!archived) {
|
|
30802
|
+
return {
|
|
30803
|
+
content: [{ type: "text", text: `Checkpoint "${checkpoint.id}" is already archived.` }],
|
|
30804
|
+
isError: true
|
|
30805
|
+
};
|
|
30806
|
+
}
|
|
30807
|
+
return {
|
|
30808
|
+
content: [{ type: "text", text: `Archived compact checkpoint ${archived.id}.` }]
|
|
30809
|
+
};
|
|
30810
|
+
}
|
|
30811
|
+
);
|
|
30055
30812
|
server.registerTool(
|
|
30056
30813
|
"memorix_transfer",
|
|
30057
30814
|
{
|