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/index.js
CHANGED
|
@@ -234,6 +234,7 @@ function getDatabase(dataDir) {
|
|
|
234
234
|
db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
|
|
235
235
|
db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
|
|
236
236
|
db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
|
|
237
|
+
db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
|
|
237
238
|
try {
|
|
238
239
|
db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
|
|
239
240
|
} catch {
|
|
@@ -274,7 +275,7 @@ function getDatabase(dataDir) {
|
|
|
274
275
|
_dbCache.set(normalized, db2);
|
|
275
276
|
return db2;
|
|
276
277
|
}
|
|
277
|
-
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;
|
|
278
|
+
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;
|
|
278
279
|
var init_sqlite_db = __esm({
|
|
279
280
|
"src/store/sqlite-db.ts"() {
|
|
280
281
|
"use strict";
|
|
@@ -726,6 +727,31 @@ CREATE TABLE IF NOT EXISTS maintenance_targets (
|
|
|
726
727
|
data_dir TEXT NOT NULL,
|
|
727
728
|
updated_at INTEGER NOT NULL
|
|
728
729
|
);
|
|
730
|
+
`;
|
|
731
|
+
CREATE_COMPACTION_CHECKPOINTS_TABLE = `
|
|
732
|
+
CREATE TABLE IF NOT EXISTS compaction_checkpoints (
|
|
733
|
+
id TEXT PRIMARY KEY,
|
|
734
|
+
project_id TEXT NOT NULL,
|
|
735
|
+
session_id TEXT NOT NULL,
|
|
736
|
+
agent TEXT NOT NULL,
|
|
737
|
+
phase TEXT NOT NULL,
|
|
738
|
+
capture_kind TEXT NOT NULL,
|
|
739
|
+
reason TEXT NOT NULL DEFAULT 'unknown',
|
|
740
|
+
source_event TEXT NOT NULL,
|
|
741
|
+
source_key TEXT NOT NULL,
|
|
742
|
+
summary TEXT,
|
|
743
|
+
tokens_before INTEGER,
|
|
744
|
+
first_kept_entry_id TEXT,
|
|
745
|
+
details_json TEXT NOT NULL DEFAULT '{}',
|
|
746
|
+
transcript_available INTEGER NOT NULL DEFAULT 0,
|
|
747
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
748
|
+
pre_captured_at TEXT NOT NULL,
|
|
749
|
+
completed_at TEXT,
|
|
750
|
+
delivered_at TEXT,
|
|
751
|
+
delivery_count INTEGER NOT NULL DEFAULT 0,
|
|
752
|
+
created_at TEXT NOT NULL,
|
|
753
|
+
updated_at TEXT NOT NULL
|
|
754
|
+
);
|
|
729
755
|
`;
|
|
730
756
|
CREATE_INDEXES = `
|
|
731
757
|
CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
|
|
@@ -773,6 +799,9 @@ CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowl
|
|
|
773
799
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_ready ON maintenance_jobs(status, run_after);
|
|
774
800
|
CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_project ON maintenance_jobs(project_id, status, run_after);
|
|
775
801
|
CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_targets(updated_at);
|
|
802
|
+
CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
|
|
803
|
+
CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
|
|
804
|
+
CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
|
|
776
805
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
777
806
|
ON maintenance_jobs(project_id, kind, dedupe_key)
|
|
778
807
|
WHERE status IN ('pending', 'running', 'retry');
|
|
@@ -846,6 +875,15 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
|
|
|
846
875
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_knowledge_workflows_workspace_status ON knowledge_workflows(workspaceId, status, updatedAt DESC)");
|
|
847
876
|
db2.exec("CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowledge_workflow_runs(projectId, workflowId, startedAt DESC)");
|
|
848
877
|
}
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
id: "1.2.7-compaction-checkpoints",
|
|
881
|
+
apply: (db2) => {
|
|
882
|
+
db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
|
|
883
|
+
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)");
|
|
884
|
+
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)");
|
|
885
|
+
db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key)");
|
|
886
|
+
}
|
|
849
887
|
}
|
|
850
888
|
];
|
|
851
889
|
_dbCache = /* @__PURE__ */ new Map();
|
|
@@ -7143,6 +7181,10 @@ var init_maintenance_jobs = __esm({
|
|
|
7143
7181
|
LIMIT 1
|
|
7144
7182
|
`).get(input.projectId, input.kind, dedupeKey);
|
|
7145
7183
|
if (existing) {
|
|
7184
|
+
if (input.kind === "vector-backfill" && existing.status === "retry") {
|
|
7185
|
+
this.commit();
|
|
7186
|
+
return rowToJob(existing);
|
|
7187
|
+
}
|
|
7146
7188
|
const nextRunAfter = Math.min(Number(existing.run_after), runAfter);
|
|
7147
7189
|
this.db.prepare(`
|
|
7148
7190
|
UPDATE maintenance_jobs
|
|
@@ -7153,6 +7195,26 @@ var init_maintenance_jobs = __esm({
|
|
|
7153
7195
|
this.commit();
|
|
7154
7196
|
return rowToJob(updated);
|
|
7155
7197
|
}
|
|
7198
|
+
if (input.kind === "vector-backfill") {
|
|
7199
|
+
const failed = this.db.prepare(`
|
|
7200
|
+
SELECT * FROM maintenance_jobs
|
|
7201
|
+
WHERE project_id = ? AND kind = ? AND dedupe_key = ? AND status = 'failed'
|
|
7202
|
+
ORDER BY updated_at DESC
|
|
7203
|
+
LIMIT 1
|
|
7204
|
+
`).get(input.projectId, input.kind, dedupeKey);
|
|
7205
|
+
if (failed) {
|
|
7206
|
+
this.db.prepare(`
|
|
7207
|
+
UPDATE maintenance_jobs
|
|
7208
|
+
SET status = 'pending', attempts = 0, max_attempts = ?, run_after = ?,
|
|
7209
|
+
payload_json = ?, lease_owner = NULL, lease_expires_at = NULL,
|
|
7210
|
+
last_error = NULL, completed_at = NULL, updated_at = ?
|
|
7211
|
+
WHERE id = ?
|
|
7212
|
+
`).run(maxAttempts, runAfter, payloadJson, now3, failed.id);
|
|
7213
|
+
const revived = this.getRow(failed.id);
|
|
7214
|
+
this.commit();
|
|
7215
|
+
return rowToJob(revived);
|
|
7216
|
+
}
|
|
7217
|
+
}
|
|
7156
7218
|
const id = randomUUID2();
|
|
7157
7219
|
this.db.prepare(`
|
|
7158
7220
|
INSERT INTO maintenance_jobs (
|
|
@@ -7324,15 +7386,42 @@ var init_maintenance_jobs = __esm({
|
|
|
7324
7386
|
const now3 = options.now ?? Date.now();
|
|
7325
7387
|
const delayMs = Number.isFinite(options.delayMs) ? Math.max(0, Math.floor(options.delayMs)) : 0;
|
|
7326
7388
|
const payloadJson = options.payload === void 0 ? null : JSON.stringify(options.payload);
|
|
7389
|
+
const status = options.status === "retry" ? "retry" : "pending";
|
|
7390
|
+
const hasLastError = options.lastError !== void 0;
|
|
7391
|
+
const lastError = hasLastError ? errorText(options.lastError) : null;
|
|
7327
7392
|
this.db.prepare(`
|
|
7328
7393
|
UPDATE maintenance_jobs
|
|
7329
|
-
SET status =
|
|
7394
|
+
SET status = ?, run_after = ?, attempts = CASE WHEN ? THEN 0 ELSE attempts END,
|
|
7330
7395
|
payload_json = COALESCE(?, payload_json),
|
|
7396
|
+
last_error = CASE
|
|
7397
|
+
WHEN ? THEN NULL
|
|
7398
|
+
WHEN ? THEN ?
|
|
7399
|
+
ELSE last_error
|
|
7400
|
+
END,
|
|
7331
7401
|
lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
|
|
7332
7402
|
WHERE id = ? AND status = 'running' AND lease_owner = ?
|
|
7333
|
-
`).run(
|
|
7403
|
+
`).run(
|
|
7404
|
+
status,
|
|
7405
|
+
now3 + delayMs,
|
|
7406
|
+
options.resetAttempts ? 1 : 0,
|
|
7407
|
+
payloadJson,
|
|
7408
|
+
options.clearLastError ? 1 : 0,
|
|
7409
|
+
hasLastError ? 1 : 0,
|
|
7410
|
+
lastError,
|
|
7411
|
+
now3,
|
|
7412
|
+
id,
|
|
7413
|
+
workerId
|
|
7414
|
+
);
|
|
7334
7415
|
return this.get(id);
|
|
7335
7416
|
}
|
|
7417
|
+
resolveFailedVectorBackfills(projectId, dedupeKey = "vector-backfill", now3 = Date.now()) {
|
|
7418
|
+
const result = this.db.prepare(`
|
|
7419
|
+
UPDATE maintenance_jobs
|
|
7420
|
+
SET status = 'completed', completed_at = ?, updated_at = ?
|
|
7421
|
+
WHERE project_id = ? AND kind = 'vector-backfill' AND dedupe_key = ? AND status = 'failed'
|
|
7422
|
+
`).run(now3, now3, projectId, dedupeKey);
|
|
7423
|
+
return Number(result.changes ?? 0);
|
|
7424
|
+
}
|
|
7336
7425
|
fail(id, workerId, error, now3 = Date.now()) {
|
|
7337
7426
|
this.begin();
|
|
7338
7427
|
try {
|
|
@@ -7428,6 +7517,9 @@ var init_maintenance_jobs = __esm({
|
|
|
7428
7517
|
delayMs: result.delayMs,
|
|
7429
7518
|
resetAttempts: result.resetAttempts,
|
|
7430
7519
|
payload: result.payload,
|
|
7520
|
+
status: result.status,
|
|
7521
|
+
lastError: result.lastError,
|
|
7522
|
+
clearLastError: result.clearLastError,
|
|
7431
7523
|
now: now3
|
|
7432
7524
|
});
|
|
7433
7525
|
return { state: "rescheduled", job: updated2 };
|
|
@@ -8123,10 +8215,10 @@ var init_store = __esm({
|
|
|
8123
8215
|
return row ? rowToSnapshot(row) : void 0;
|
|
8124
8216
|
}
|
|
8125
8217
|
listSnapshots(projectId, limit = 20) {
|
|
8126
|
-
const
|
|
8218
|
+
const safeLimit2 = Number.isFinite(limit) ? Math.max(1, Math.min(200, Math.floor(limit))) : 20;
|
|
8127
8219
|
return this.db.prepare(
|
|
8128
8220
|
"SELECT * FROM code_state_snapshots WHERE projectId = ? ORDER BY sourceEpoch DESC LIMIT ?"
|
|
8129
|
-
).all(projectId,
|
|
8221
|
+
).all(projectId, safeLimit2).map(rowToSnapshot);
|
|
8130
8222
|
}
|
|
8131
8223
|
/**
|
|
8132
8224
|
* Record a completed scan and mark all current structural facts with its
|
|
@@ -8406,6 +8498,9 @@ function normalizeEmbeddingFailure(error) {
|
|
|
8406
8498
|
message: raw
|
|
8407
8499
|
};
|
|
8408
8500
|
}
|
|
8501
|
+
function vectorBackfillError(error) {
|
|
8502
|
+
return sanitizeCredentials(normalizeEmbeddingFailure(error).message).slice(0, 1e3);
|
|
8503
|
+
}
|
|
8409
8504
|
function queueVectorBackfill(projectId) {
|
|
8410
8505
|
const dataDir = projectDir;
|
|
8411
8506
|
if (!dataDir) return;
|
|
@@ -9161,17 +9256,53 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9161
9256
|
let succeeded = 0;
|
|
9162
9257
|
let failed = 0;
|
|
9163
9258
|
let lastFailure;
|
|
9259
|
+
const recordFailure = (message) => {
|
|
9260
|
+
if (!lastFailure) lastFailure = message;
|
|
9261
|
+
};
|
|
9262
|
+
const candidates = [];
|
|
9263
|
+
for (const id of ids) {
|
|
9264
|
+
const observation = observationById.get(id);
|
|
9265
|
+
if (!observation) {
|
|
9266
|
+
vectorMissingIds.delete(id);
|
|
9267
|
+
continue;
|
|
9268
|
+
}
|
|
9269
|
+
candidates.push({
|
|
9270
|
+
id,
|
|
9271
|
+
observation,
|
|
9272
|
+
text: [observation.title, observation.narrative, ...observation.facts].join(" ")
|
|
9273
|
+
});
|
|
9274
|
+
}
|
|
9164
9275
|
try {
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9276
|
+
if (candidates.length === 0) {
|
|
9277
|
+
return { attempted: ids.length, succeeded, failed };
|
|
9278
|
+
}
|
|
9279
|
+
let embeddings = null;
|
|
9280
|
+
try {
|
|
9281
|
+
const provider2 = await getEmbeddingProvider();
|
|
9282
|
+
if (!provider2) {
|
|
9283
|
+
if (isEmbeddingExplicitlyDisabled()) {
|
|
9284
|
+
for (const candidate of candidates) vectorMissingIds.delete(candidate.id);
|
|
9285
|
+
} else {
|
|
9286
|
+
recordFailure("embedding provider unavailable");
|
|
9287
|
+
failed += candidates.length;
|
|
9288
|
+
}
|
|
9289
|
+
} else {
|
|
9290
|
+
embeddings = await provider2.embedBatch(candidates.map((candidate) => candidate.text));
|
|
9170
9291
|
}
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9292
|
+
} catch (error) {
|
|
9293
|
+
recordFailure(vectorBackfillError(error));
|
|
9294
|
+
failed += candidates.length;
|
|
9295
|
+
}
|
|
9296
|
+
if (embeddings) {
|
|
9297
|
+
for (let index = 0; index < candidates.length; index++) {
|
|
9298
|
+
const { id, observation: obs } = candidates[index];
|
|
9299
|
+
const embedding = embeddings[index];
|
|
9300
|
+
if (!embedding || embedding.length === 0) {
|
|
9301
|
+
recordFailure("embedding provider returned no vector");
|
|
9302
|
+
failed++;
|
|
9303
|
+
continue;
|
|
9304
|
+
}
|
|
9305
|
+
try {
|
|
9175
9306
|
if (!isVectorCompatibleWithCurrentIndex(embedding)) {
|
|
9176
9307
|
await upgradeVectorSchemaAfterFirstEmbedding(embedding);
|
|
9177
9308
|
}
|
|
@@ -9180,7 +9311,7 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9180
9311
|
console.error(
|
|
9181
9312
|
`[memorix] Backfill embedding mismatch for obs-${id}: provider returned ${embedding.length}d, index expects ${vectorDimensions ?? "unknown"}d (kept in queue)`
|
|
9182
9313
|
);
|
|
9183
|
-
|
|
9314
|
+
recordFailure(`dimension mismatch: provider returned ${embedding.length}d, index expects ${vectorDimensions ?? "unknown"}d`);
|
|
9184
9315
|
failed++;
|
|
9185
9316
|
continue;
|
|
9186
9317
|
}
|
|
@@ -9219,15 +9350,10 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9219
9350
|
await insertObservation(doc);
|
|
9220
9351
|
vectorMissingIds.delete(id);
|
|
9221
9352
|
succeeded++;
|
|
9222
|
-
}
|
|
9223
|
-
|
|
9224
|
-
} else {
|
|
9225
|
-
lastFailure = "embedding provider unavailable";
|
|
9353
|
+
} catch (error) {
|
|
9354
|
+
recordFailure(vectorBackfillError(error));
|
|
9226
9355
|
failed++;
|
|
9227
9356
|
}
|
|
9228
|
-
} catch (err) {
|
|
9229
|
-
lastFailure = err instanceof Error ? err.message : String(err);
|
|
9230
|
-
failed++;
|
|
9231
9357
|
}
|
|
9232
9358
|
}
|
|
9233
9359
|
} finally {
|
|
@@ -9240,7 +9366,12 @@ async function backfillVectorEmbeddings(options = {}) {
|
|
|
9240
9366
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9241
9367
|
};
|
|
9242
9368
|
}
|
|
9243
|
-
return {
|
|
9369
|
+
return {
|
|
9370
|
+
attempted: ids.length,
|
|
9371
|
+
succeeded,
|
|
9372
|
+
failed,
|
|
9373
|
+
...lastFailure ? { lastError: lastFailure } : {}
|
|
9374
|
+
};
|
|
9244
9375
|
}
|
|
9245
9376
|
var observations, nextId, projectDir, searchIndexPrepared, vectorMissingIds, vectorBackfillRunning, vectorSchemaUpgradePromise, lastVectorBackfill, embeddingFailureLogTimestamps, EMBEDDING_FAILURE_LOG_COOLDOWN_MS;
|
|
9246
9377
|
var init_observations = __esm({
|
|
@@ -14877,6 +15008,26 @@ function vectorBatchSize(payload) {
|
|
|
14877
15008
|
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_VECTOR_BATCH_SIZE;
|
|
14878
15009
|
return Math.min(100, Math.max(1, Math.floor(value)));
|
|
14879
15010
|
}
|
|
15011
|
+
function vectorBackfillFailureStreak(payload) {
|
|
15012
|
+
const value = payload.vectorBackfillFailureStreak;
|
|
15013
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.min(16, Math.max(0, Math.floor(value))) : 0;
|
|
15014
|
+
}
|
|
15015
|
+
function vectorBackfillRetryDelayMs(streak) {
|
|
15016
|
+
return Math.min(
|
|
15017
|
+
VECTOR_FAILURE_MAX_RETRY_DELAY_MS,
|
|
15018
|
+
VECTOR_FAILURE_BASE_RETRY_DELAY_MS * 2 ** Math.max(0, streak - 1)
|
|
15019
|
+
);
|
|
15020
|
+
}
|
|
15021
|
+
function withoutVectorBackfillFailureStreak(payload) {
|
|
15022
|
+
const { vectorBackfillFailureStreak: _streak, ...rest } = payload;
|
|
15023
|
+
return rest;
|
|
15024
|
+
}
|
|
15025
|
+
function resolveSupersededVectorFailures(projectDir2, projectId) {
|
|
15026
|
+
try {
|
|
15027
|
+
new MaintenanceJobStore(projectDir2).resolveFailedVectorBackfills(projectId);
|
|
15028
|
+
} catch {
|
|
15029
|
+
}
|
|
15030
|
+
}
|
|
14880
15031
|
function retentionBatchSize(payload) {
|
|
14881
15032
|
const value = payload.limit;
|
|
14882
15033
|
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_RETENTION_BATCH_SIZE;
|
|
@@ -15285,20 +15436,45 @@ function createProjectMaintenanceHandler(projectId, projectDir2, projectRoot, op
|
|
|
15285
15436
|
if (isEmbeddingExplicitlyDisabled2()) return { action: "complete" };
|
|
15286
15437
|
const { backfillVectorEmbeddings: backfillVectorEmbeddings2, getVectorStatus: getVectorStatus2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
15287
15438
|
const before = getVectorStatus2(projectId);
|
|
15288
|
-
if (before.missing === 0)
|
|
15439
|
+
if (before.missing === 0) {
|
|
15440
|
+
resolveSupersededVectorFailures(projectDir2, projectId);
|
|
15441
|
+
return { action: "complete" };
|
|
15442
|
+
}
|
|
15289
15443
|
const result = await backfillVectorEmbeddings2({
|
|
15290
15444
|
projectId,
|
|
15291
15445
|
limit: vectorBatchSize(job.payload)
|
|
15292
15446
|
});
|
|
15293
15447
|
const after = getVectorStatus2(projectId);
|
|
15294
|
-
if (after.missing === 0)
|
|
15448
|
+
if (after.missing === 0) {
|
|
15449
|
+
resolveSupersededVectorFailures(projectDir2, projectId);
|
|
15450
|
+
return { action: "complete" };
|
|
15451
|
+
}
|
|
15295
15452
|
if (result.failed > 0 && result.succeeded === 0) {
|
|
15296
|
-
|
|
15453
|
+
const streak = vectorBackfillFailureStreak(job.payload) + 1;
|
|
15454
|
+
return {
|
|
15455
|
+
action: "reschedule",
|
|
15456
|
+
status: "retry",
|
|
15457
|
+
delayMs: vectorBackfillRetryDelayMs(streak),
|
|
15458
|
+
// This is a recoverable provider/index state, not a terminal job error.
|
|
15459
|
+
// Keeping one retry row prevents new MCP processes from creating a storm.
|
|
15460
|
+
resetAttempts: true,
|
|
15461
|
+
payload: {
|
|
15462
|
+
...job.payload,
|
|
15463
|
+
vectorBackfillFailureStreak: streak
|
|
15464
|
+
},
|
|
15465
|
+
lastError: result.lastError ?? `vector backfill made no progress (${result.failed}/${result.attempted} failed)`
|
|
15466
|
+
};
|
|
15297
15467
|
}
|
|
15468
|
+
const priorFailureStreak = vectorBackfillFailureStreak(job.payload);
|
|
15469
|
+
const payload = withoutVectorBackfillFailureStreak(job.payload);
|
|
15298
15470
|
return {
|
|
15299
15471
|
action: "reschedule",
|
|
15300
15472
|
delayMs: result.attempted === 0 ? VECTOR_RETRY_DELAY_MS : 0,
|
|
15301
|
-
resetAttempts: result.succeeded > 0
|
|
15473
|
+
resetAttempts: result.succeeded > 0,
|
|
15474
|
+
...result.succeeded > 0 ? {
|
|
15475
|
+
...priorFailureStreak > 0 ? { payload } : {},
|
|
15476
|
+
...result.failed > 0 && result.lastError ? { lastError: result.lastError } : priorFailureStreak > 0 ? { clearLastError: true } : {}
|
|
15477
|
+
} : {}
|
|
15302
15478
|
};
|
|
15303
15479
|
};
|
|
15304
15480
|
}
|
|
@@ -15311,11 +15487,12 @@ function createProjectMaintenanceDispatcher(projectId, projectDir2, projectRoot,
|
|
|
15311
15487
|
return isolatedRunner({ job, projectRoot, dataDir: projectDir2 });
|
|
15312
15488
|
};
|
|
15313
15489
|
}
|
|
15314
|
-
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;
|
|
15490
|
+
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;
|
|
15315
15491
|
var init_project_maintenance = __esm({
|
|
15316
15492
|
"src/runtime/project-maintenance.ts"() {
|
|
15317
15493
|
"use strict";
|
|
15318
15494
|
init_esm_shims();
|
|
15495
|
+
init_maintenance_jobs();
|
|
15319
15496
|
init_isolated_maintenance();
|
|
15320
15497
|
init_lifecycle();
|
|
15321
15498
|
init_timeout();
|
|
@@ -15326,6 +15503,8 @@ var init_project_maintenance = __esm({
|
|
|
15326
15503
|
DEFAULT_CLAIM_DERIVATION_BATCH_SIZE = 100;
|
|
15327
15504
|
DEFAULT_OBSERVATION_QUALIFICATION_BATCH_SIZE = 100;
|
|
15328
15505
|
VECTOR_RETRY_DELAY_MS = 5e3;
|
|
15506
|
+
VECTOR_FAILURE_BASE_RETRY_DELAY_MS = 6e4;
|
|
15507
|
+
VECTOR_FAILURE_MAX_RETRY_DELAY_MS = 15 * 6e4;
|
|
15329
15508
|
DEDUP_PER_PAIR_TIMEOUT_MS = 5e3;
|
|
15330
15509
|
}
|
|
15331
15510
|
});
|
|
@@ -15983,7 +16162,7 @@ function renderTaskWorksetPrompt(input) {
|
|
|
15983
16162
|
});
|
|
15984
16163
|
appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
|
|
15985
16164
|
const hasContinuation = Boolean(
|
|
15986
|
-
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0
|
|
16165
|
+
input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
|
|
15987
16166
|
);
|
|
15988
16167
|
if (hasContinuation && input.continuation) {
|
|
15989
16168
|
appendLine(lines, "", maxTokens, omitted, "continuation-heading");
|
|
@@ -16023,6 +16202,24 @@ function renderTaskWorksetPrompt(input) {
|
|
|
16023
16202
|
}
|
|
16024
16203
|
);
|
|
16025
16204
|
}
|
|
16205
|
+
if (input.continuation.compactCheckpoint) {
|
|
16206
|
+
const checkpoint = input.continuation.compactCheckpoint;
|
|
16207
|
+
const source = `${checkpoint.agent}, ${checkpoint.captureKind}, ${checkpoint.reason}`;
|
|
16208
|
+
appendLine(
|
|
16209
|
+
lines,
|
|
16210
|
+
"- Recent host compact checkpoint (" + source + "): " + short(checkpoint.summary, 36),
|
|
16211
|
+
maxTokens,
|
|
16212
|
+
omitted,
|
|
16213
|
+
"continuation-compact-checkpoint",
|
|
16214
|
+
selected,
|
|
16215
|
+
{
|
|
16216
|
+
kind: "continuation",
|
|
16217
|
+
id: "compact:" + checkpoint.id,
|
|
16218
|
+
reason: "recent host-native compact lifecycle evidence",
|
|
16219
|
+
trust: "historical"
|
|
16220
|
+
}
|
|
16221
|
+
);
|
|
16222
|
+
}
|
|
16026
16223
|
}
|
|
16027
16224
|
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
16028
16225
|
appendLine(lines, "", maxTokens, omitted, "caution-heading");
|
|
@@ -16292,7 +16489,7 @@ async function buildTaskWorkset(input) {
|
|
|
16292
16489
|
...input.verificationHints
|
|
16293
16490
|
]).slice(0, 4);
|
|
16294
16491
|
const normalizedCautions = unique(cautions.map((caution) => caution.kind)).map((kind) => cautions.find((caution) => caution.kind === kind)).slice(0, 6);
|
|
16295
|
-
const continuation = input.continuation && (input.continuation.previousSession || input.continuation.memories.length > 0) ? {
|
|
16492
|
+
const continuation = input.continuation && (input.continuation.previousSession || input.continuation.memories.length > 0 || input.continuation.compactCheckpoint) ? {
|
|
16296
16493
|
...input.continuation.previousSession ? {
|
|
16297
16494
|
previousSession: {
|
|
16298
16495
|
...input.continuation.previousSession,
|
|
@@ -16303,7 +16500,13 @@ async function buildTaskWorkset(input) {
|
|
|
16303
16500
|
...memory,
|
|
16304
16501
|
title: short(memory.title, 20),
|
|
16305
16502
|
...memory.detail ? { detail: short(memory.detail, CONTINUATION_DETAIL_TOKEN_BUDGET) } : {}
|
|
16306
|
-
}))
|
|
16503
|
+
})),
|
|
16504
|
+
...input.continuation.compactCheckpoint ? {
|
|
16505
|
+
compactCheckpoint: {
|
|
16506
|
+
...input.continuation.compactCheckpoint,
|
|
16507
|
+
summary: short(input.continuation.compactCheckpoint.summary, 44)
|
|
16508
|
+
}
|
|
16509
|
+
} : {}
|
|
16307
16510
|
} : void 0;
|
|
16308
16511
|
const base = {
|
|
16309
16512
|
version: "1.2",
|
|
@@ -17119,6 +17322,311 @@ var init_project_context = __esm({
|
|
|
17119
17322
|
}
|
|
17120
17323
|
});
|
|
17121
17324
|
|
|
17325
|
+
// src/store/compaction-checkpoint-store.ts
|
|
17326
|
+
var compaction_checkpoint_store_exports = {};
|
|
17327
|
+
__export(compaction_checkpoint_store_exports, {
|
|
17328
|
+
CompactionCheckpointStore: () => CompactionCheckpointStore
|
|
17329
|
+
});
|
|
17330
|
+
import { createHash as createHash13, randomUUID as randomUUID8 } from "crypto";
|
|
17331
|
+
function optionalText5(value) {
|
|
17332
|
+
return typeof value === "string" && value ? value : void 0;
|
|
17333
|
+
}
|
|
17334
|
+
function optionalNumber2(value) {
|
|
17335
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
17336
|
+
}
|
|
17337
|
+
function parseDetails(value) {
|
|
17338
|
+
if (typeof value !== "string" || !value) return void 0;
|
|
17339
|
+
try {
|
|
17340
|
+
const parsed = JSON.parse(value);
|
|
17341
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
17342
|
+
} catch {
|
|
17343
|
+
return void 0;
|
|
17344
|
+
}
|
|
17345
|
+
}
|
|
17346
|
+
function sanitizeSummary(value) {
|
|
17347
|
+
if (!value?.trim()) return void 0;
|
|
17348
|
+
return sanitizeCredentials(value).slice(0, MAX_SUMMARY_CHARS).trim() || void 0;
|
|
17349
|
+
}
|
|
17350
|
+
function sanitizeDetails(value) {
|
|
17351
|
+
if (!value) return void 0;
|
|
17352
|
+
try {
|
|
17353
|
+
const sanitized = sanitizeCredentials(JSON.stringify(value));
|
|
17354
|
+
if (sanitized.length > MAX_DETAILS_CHARS) return { truncated: true };
|
|
17355
|
+
const parsed = JSON.parse(sanitized);
|
|
17356
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
17357
|
+
} catch {
|
|
17358
|
+
return { unavailable: true };
|
|
17359
|
+
}
|
|
17360
|
+
}
|
|
17361
|
+
function sourceKeyFor(input, summary) {
|
|
17362
|
+
if (input.sourceKey?.trim()) return input.sourceKey.trim().slice(0, 300);
|
|
17363
|
+
const material = [
|
|
17364
|
+
input.projectId,
|
|
17365
|
+
input.sessionId,
|
|
17366
|
+
input.agent,
|
|
17367
|
+
input.sourceEvent,
|
|
17368
|
+
summary ?? "",
|
|
17369
|
+
input.tokensBefore ?? "",
|
|
17370
|
+
input.firstKeptEntryId ?? ""
|
|
17371
|
+
].join("\0");
|
|
17372
|
+
return `derived:${createHash13("sha256").update(material).digest("hex").slice(0, 32)}`;
|
|
17373
|
+
}
|
|
17374
|
+
function rowToCheckpoint(row) {
|
|
17375
|
+
return {
|
|
17376
|
+
id: row.id,
|
|
17377
|
+
projectId: row.project_id,
|
|
17378
|
+
sessionId: row.session_id,
|
|
17379
|
+
agent: row.agent,
|
|
17380
|
+
phase: row.phase,
|
|
17381
|
+
captureKind: row.capture_kind,
|
|
17382
|
+
reason: row.reason,
|
|
17383
|
+
sourceEvent: row.source_event,
|
|
17384
|
+
sourceKey: row.source_key,
|
|
17385
|
+
...optionalText5(row.summary) ? { summary: row.summary } : {},
|
|
17386
|
+
...optionalNumber2(row.tokens_before) !== void 0 ? { tokensBefore: Number(row.tokens_before) } : {},
|
|
17387
|
+
...optionalText5(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
|
|
17388
|
+
...parseDetails(row.details_json) ? { details: parseDetails(row.details_json) } : {},
|
|
17389
|
+
transcriptAvailable: Boolean(row.transcript_available),
|
|
17390
|
+
status: row.status,
|
|
17391
|
+
preCapturedAt: row.pre_captured_at,
|
|
17392
|
+
...optionalText5(row.completed_at) ? { completedAt: row.completed_at } : {},
|
|
17393
|
+
...optionalText5(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
|
|
17394
|
+
deliveryCount: Number(row.delivery_count ?? 0),
|
|
17395
|
+
createdAt: row.created_at,
|
|
17396
|
+
updatedAt: row.updated_at
|
|
17397
|
+
};
|
|
17398
|
+
}
|
|
17399
|
+
function safeReason(value) {
|
|
17400
|
+
return value === "manual" || value === "auto" ? value : "unknown";
|
|
17401
|
+
}
|
|
17402
|
+
function safeLimit(value, fallback) {
|
|
17403
|
+
if (!Number.isFinite(value) || value == null) return fallback;
|
|
17404
|
+
return Math.max(1, Math.min(500, Math.floor(value)));
|
|
17405
|
+
}
|
|
17406
|
+
var MAX_SUMMARY_CHARS, MAX_DETAILS_CHARS, CompactionCheckpointStore;
|
|
17407
|
+
var init_compaction_checkpoint_store = __esm({
|
|
17408
|
+
"src/store/compaction-checkpoint-store.ts"() {
|
|
17409
|
+
"use strict";
|
|
17410
|
+
init_esm_shims();
|
|
17411
|
+
init_secret_filter();
|
|
17412
|
+
init_sqlite_db();
|
|
17413
|
+
MAX_SUMMARY_CHARS = 24e3;
|
|
17414
|
+
MAX_DETAILS_CHARS = 4e3;
|
|
17415
|
+
CompactionCheckpointStore = class {
|
|
17416
|
+
db;
|
|
17417
|
+
constructor(dataDir) {
|
|
17418
|
+
this.db = getDatabase(dataDir);
|
|
17419
|
+
}
|
|
17420
|
+
recordPreflight(input) {
|
|
17421
|
+
const pending = this.db.prepare(`
|
|
17422
|
+
SELECT * FROM compaction_checkpoints
|
|
17423
|
+
WHERE project_id = ? AND session_id = ? AND agent = ?
|
|
17424
|
+
AND phase = 'pre' AND status = 'active'
|
|
17425
|
+
ORDER BY pre_captured_at DESC, created_at DESC
|
|
17426
|
+
LIMIT 1
|
|
17427
|
+
`).get(input.projectId, input.sessionId, input.agent);
|
|
17428
|
+
if (pending) return rowToCheckpoint(pending);
|
|
17429
|
+
const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
17430
|
+
const checkpoint = {
|
|
17431
|
+
id: randomUUID8(),
|
|
17432
|
+
projectId: input.projectId,
|
|
17433
|
+
sessionId: input.sessionId,
|
|
17434
|
+
agent: input.agent,
|
|
17435
|
+
phase: "pre",
|
|
17436
|
+
captureKind: "preflight",
|
|
17437
|
+
reason: safeReason(input.reason),
|
|
17438
|
+
sourceEvent: input.sourceEvent,
|
|
17439
|
+
sourceKey: `pre:${randomUUID8()}`,
|
|
17440
|
+
transcriptAvailable: Boolean(input.transcriptAvailable),
|
|
17441
|
+
status: "active",
|
|
17442
|
+
preCapturedAt: now3,
|
|
17443
|
+
deliveryCount: 0,
|
|
17444
|
+
createdAt: now3,
|
|
17445
|
+
updatedAt: now3
|
|
17446
|
+
};
|
|
17447
|
+
this.insert(checkpoint);
|
|
17448
|
+
return checkpoint;
|
|
17449
|
+
}
|
|
17450
|
+
complete(input) {
|
|
17451
|
+
const completedAt = input.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
17452
|
+
const summary = sanitizeSummary(input.summary);
|
|
17453
|
+
const pending = this.db.prepare(`
|
|
17454
|
+
SELECT * FROM compaction_checkpoints
|
|
17455
|
+
WHERE project_id = ? AND session_id = ? AND agent = ?
|
|
17456
|
+
AND phase = 'pre' AND status = 'active'
|
|
17457
|
+
ORDER BY pre_captured_at DESC, created_at DESC
|
|
17458
|
+
LIMIT 1
|
|
17459
|
+
`).get(input.projectId, input.sessionId, input.agent);
|
|
17460
|
+
if (!input.sourceKey?.trim() && !summary && input.tokensBefore === void 0 && !input.firstKeptEntryId && !pending) {
|
|
17461
|
+
const priorLifecycle = this.db.prepare(`
|
|
17462
|
+
SELECT * FROM compaction_checkpoints
|
|
17463
|
+
WHERE project_id = ? AND session_id = ? AND agent = ?
|
|
17464
|
+
AND phase = 'complete' AND capture_kind = 'lifecycle'
|
|
17465
|
+
AND source_event = ? AND status = 'active'
|
|
17466
|
+
ORDER BY completed_at DESC, created_at DESC
|
|
17467
|
+
LIMIT 1
|
|
17468
|
+
`).get(input.projectId, input.sessionId, input.agent, input.sourceEvent);
|
|
17469
|
+
if (priorLifecycle) return rowToCheckpoint(priorLifecycle);
|
|
17470
|
+
}
|
|
17471
|
+
const sourceKey = input.sourceKey?.trim() ? input.sourceKey.trim().slice(0, 300) : pending ? `lifecycle:${pending.id}` : sourceKeyFor(input, summary);
|
|
17472
|
+
const existing = this.db.prepare(`
|
|
17473
|
+
SELECT * FROM compaction_checkpoints
|
|
17474
|
+
WHERE project_id = ? AND source_key = ?
|
|
17475
|
+
LIMIT 1
|
|
17476
|
+
`).get(input.projectId, sourceKey);
|
|
17477
|
+
if (existing) return rowToCheckpoint(existing);
|
|
17478
|
+
const captureKind = summary ? "native-summary" : "lifecycle";
|
|
17479
|
+
const details = sanitizeDetails(input.details);
|
|
17480
|
+
if (pending) {
|
|
17481
|
+
const checkpoint2 = rowToCheckpoint(pending);
|
|
17482
|
+
const nextReason = safeReason(input.reason) === "unknown" ? checkpoint2.reason : safeReason(input.reason);
|
|
17483
|
+
this.db.prepare(`
|
|
17484
|
+
UPDATE compaction_checkpoints
|
|
17485
|
+
SET phase = 'complete', capture_kind = ?, reason = ?, source_event = ?, source_key = ?,
|
|
17486
|
+
summary = ?, tokens_before = ?, first_kept_entry_id = ?, details_json = ?,
|
|
17487
|
+
completed_at = ?, updated_at = ?
|
|
17488
|
+
WHERE id = ?
|
|
17489
|
+
`).run(
|
|
17490
|
+
captureKind,
|
|
17491
|
+
nextReason,
|
|
17492
|
+
input.sourceEvent,
|
|
17493
|
+
sourceKey,
|
|
17494
|
+
summary ?? null,
|
|
17495
|
+
input.tokensBefore ?? null,
|
|
17496
|
+
input.firstKeptEntryId ?? null,
|
|
17497
|
+
JSON.stringify(details ?? {}),
|
|
17498
|
+
completedAt,
|
|
17499
|
+
completedAt,
|
|
17500
|
+
checkpoint2.id
|
|
17501
|
+
);
|
|
17502
|
+
return this.get(checkpoint2.id);
|
|
17503
|
+
}
|
|
17504
|
+
const checkpoint = {
|
|
17505
|
+
id: randomUUID8(),
|
|
17506
|
+
projectId: input.projectId,
|
|
17507
|
+
sessionId: input.sessionId,
|
|
17508
|
+
agent: input.agent,
|
|
17509
|
+
phase: "complete",
|
|
17510
|
+
captureKind,
|
|
17511
|
+
reason: safeReason(input.reason),
|
|
17512
|
+
sourceEvent: input.sourceEvent,
|
|
17513
|
+
sourceKey,
|
|
17514
|
+
...summary ? { summary } : {},
|
|
17515
|
+
...input.tokensBefore !== void 0 ? { tokensBefore: input.tokensBefore } : {},
|
|
17516
|
+
...input.firstKeptEntryId ? { firstKeptEntryId: input.firstKeptEntryId } : {},
|
|
17517
|
+
...details ? { details } : {},
|
|
17518
|
+
transcriptAvailable: false,
|
|
17519
|
+
status: "active",
|
|
17520
|
+
preCapturedAt: completedAt,
|
|
17521
|
+
completedAt,
|
|
17522
|
+
deliveryCount: 0,
|
|
17523
|
+
createdAt: completedAt,
|
|
17524
|
+
updatedAt: completedAt
|
|
17525
|
+
};
|
|
17526
|
+
this.insert(checkpoint);
|
|
17527
|
+
return checkpoint;
|
|
17528
|
+
}
|
|
17529
|
+
get(id) {
|
|
17530
|
+
const row = this.db.prepare("SELECT * FROM compaction_checkpoints WHERE id = ?").get(id);
|
|
17531
|
+
return row ? rowToCheckpoint(row) : void 0;
|
|
17532
|
+
}
|
|
17533
|
+
list(options) {
|
|
17534
|
+
const conditions = ["project_id = ?"];
|
|
17535
|
+
const values = [options.projectId];
|
|
17536
|
+
if (options.sessionId) {
|
|
17537
|
+
conditions.push("session_id = ?");
|
|
17538
|
+
values.push(options.sessionId);
|
|
17539
|
+
}
|
|
17540
|
+
if (options.agent) {
|
|
17541
|
+
conditions.push("agent = ?");
|
|
17542
|
+
values.push(options.agent);
|
|
17543
|
+
}
|
|
17544
|
+
if (!options.includeArchived) {
|
|
17545
|
+
conditions.push("status = 'active'");
|
|
17546
|
+
}
|
|
17547
|
+
const rows = this.db.prepare(`
|
|
17548
|
+
SELECT * FROM compaction_checkpoints
|
|
17549
|
+
WHERE ${conditions.join(" AND ")}
|
|
17550
|
+
ORDER BY COALESCE(completed_at, pre_captured_at) DESC, created_at DESC
|
|
17551
|
+
LIMIT ?
|
|
17552
|
+
`).all(...values, safeLimit(options.limit, 20));
|
|
17553
|
+
return rows.map(rowToCheckpoint);
|
|
17554
|
+
}
|
|
17555
|
+
findUndelivered(projectId, sessionId, agent) {
|
|
17556
|
+
const row = this.db.prepare(`
|
|
17557
|
+
SELECT * FROM compaction_checkpoints
|
|
17558
|
+
WHERE project_id = ? AND session_id = ? AND agent = ?
|
|
17559
|
+
AND phase = 'complete' AND status = 'active' AND delivered_at IS NULL
|
|
17560
|
+
ORDER BY completed_at DESC, created_at DESC
|
|
17561
|
+
LIMIT 1
|
|
17562
|
+
`).get(projectId, sessionId, agent);
|
|
17563
|
+
return row ? rowToCheckpoint(row) : void 0;
|
|
17564
|
+
}
|
|
17565
|
+
findLatestCompleted(projectId, options = {}) {
|
|
17566
|
+
const excludeSession = options.excludeSession;
|
|
17567
|
+
const exclusion = excludeSession ? " AND NOT (session_id = ? AND agent = ? )" : "";
|
|
17568
|
+
const values = excludeSession ? [projectId, excludeSession.sessionId, excludeSession.agent] : [projectId];
|
|
17569
|
+
const row = this.db.prepare(`
|
|
17570
|
+
SELECT * FROM compaction_checkpoints
|
|
17571
|
+
WHERE project_id = ? AND phase = 'complete' AND status = 'active'
|
|
17572
|
+
${exclusion}
|
|
17573
|
+
ORDER BY completed_at DESC, created_at DESC
|
|
17574
|
+
LIMIT 1
|
|
17575
|
+
`).get(...values);
|
|
17576
|
+
return row ? rowToCheckpoint(row) : void 0;
|
|
17577
|
+
}
|
|
17578
|
+
archive(id, archivedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
17579
|
+
const result = this.db.prepare(`
|
|
17580
|
+
UPDATE compaction_checkpoints
|
|
17581
|
+
SET status = 'archived', updated_at = ?
|
|
17582
|
+
WHERE id = ? AND status = 'active'
|
|
17583
|
+
`).run(archivedAt, id);
|
|
17584
|
+
return Number(result.changes ?? 0) > 0 ? this.get(id) : void 0;
|
|
17585
|
+
}
|
|
17586
|
+
markDelivered(id, deliveredAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
17587
|
+
const result = this.db.prepare(`
|
|
17588
|
+
UPDATE compaction_checkpoints
|
|
17589
|
+
SET delivered_at = ?, delivery_count = delivery_count + 1, updated_at = ?
|
|
17590
|
+
WHERE id = ? AND phase = 'complete' AND status = 'active' AND delivered_at IS NULL
|
|
17591
|
+
`).run(deliveredAt, deliveredAt, id);
|
|
17592
|
+
return Number(result.changes ?? 0) > 0 ? this.get(id) : void 0;
|
|
17593
|
+
}
|
|
17594
|
+
insert(checkpoint) {
|
|
17595
|
+
this.db.prepare(`
|
|
17596
|
+
INSERT INTO compaction_checkpoints (
|
|
17597
|
+
id, project_id, session_id, agent, phase, capture_kind, reason,
|
|
17598
|
+
source_event, source_key, summary, tokens_before, first_kept_entry_id,
|
|
17599
|
+
details_json, transcript_available, status, pre_captured_at, completed_at,
|
|
17600
|
+
delivered_at, delivery_count, created_at, updated_at
|
|
17601
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
17602
|
+
`).run(
|
|
17603
|
+
checkpoint.id,
|
|
17604
|
+
checkpoint.projectId,
|
|
17605
|
+
checkpoint.sessionId,
|
|
17606
|
+
checkpoint.agent,
|
|
17607
|
+
checkpoint.phase,
|
|
17608
|
+
checkpoint.captureKind,
|
|
17609
|
+
checkpoint.reason,
|
|
17610
|
+
checkpoint.sourceEvent,
|
|
17611
|
+
checkpoint.sourceKey,
|
|
17612
|
+
checkpoint.summary ?? null,
|
|
17613
|
+
checkpoint.tokensBefore ?? null,
|
|
17614
|
+
checkpoint.firstKeptEntryId ?? null,
|
|
17615
|
+
JSON.stringify(checkpoint.details ?? {}),
|
|
17616
|
+
checkpoint.transcriptAvailable ? 1 : 0,
|
|
17617
|
+
checkpoint.status,
|
|
17618
|
+
checkpoint.preCapturedAt,
|
|
17619
|
+
checkpoint.completedAt ?? null,
|
|
17620
|
+
checkpoint.deliveredAt ?? null,
|
|
17621
|
+
checkpoint.deliveryCount,
|
|
17622
|
+
checkpoint.createdAt,
|
|
17623
|
+
checkpoint.updatedAt
|
|
17624
|
+
);
|
|
17625
|
+
}
|
|
17626
|
+
};
|
|
17627
|
+
}
|
|
17628
|
+
});
|
|
17629
|
+
|
|
17122
17630
|
// src/codegraph/auto-context.ts
|
|
17123
17631
|
var auto_context_exports = {};
|
|
17124
17632
|
__export(auto_context_exports, {
|
|
@@ -17304,6 +17812,22 @@ async function buildAutoProjectContext(input) {
|
|
|
17304
17812
|
if (continuationRequested) {
|
|
17305
17813
|
await initSessionStore(input.dataDir);
|
|
17306
17814
|
continuation = await getSessionResumeBrief(input.project.id, task, input.reader);
|
|
17815
|
+
const { CompactionCheckpointStore: CompactionCheckpointStore2 } = await Promise.resolve().then(() => (init_compaction_checkpoint_store(), compaction_checkpoint_store_exports));
|
|
17816
|
+
const checkpoint = new CompactionCheckpointStore2(input.dataDir).findLatestCompleted(
|
|
17817
|
+
input.project.id,
|
|
17818
|
+
input.excludeCompactionCheckpointFor ? { excludeSession: input.excludeCompactionCheckpointFor } : void 0
|
|
17819
|
+
);
|
|
17820
|
+
const completedAtMs = checkpoint ? Date.parse(checkpoint.completedAt ?? checkpoint.preCapturedAt) : Number.NaN;
|
|
17821
|
+
if (checkpoint && Number.isFinite(completedAtMs) && completedAtMs <= now3.getTime() && now3.getTime() - completedAtMs <= COMPACT_CHECKPOINT_MAX_AGE_MS) {
|
|
17822
|
+
continuation.compactCheckpoint = {
|
|
17823
|
+
id: checkpoint.id,
|
|
17824
|
+
agent: checkpoint.agent,
|
|
17825
|
+
captureKind: checkpoint.captureKind === "native-summary" ? "native-summary" : "lifecycle",
|
|
17826
|
+
reason: checkpoint.reason,
|
|
17827
|
+
...checkpoint.completedAt ? { completedAt: checkpoint.completedAt } : {},
|
|
17828
|
+
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."
|
|
17829
|
+
};
|
|
17830
|
+
}
|
|
17307
17831
|
}
|
|
17308
17832
|
const workset = await buildTaskWorkset({
|
|
17309
17833
|
projectId: input.project.id,
|
|
@@ -17512,7 +18036,7 @@ function formatAutoProjectContextSummary(context) {
|
|
|
17512
18036
|
reliableSources.length > 0 ? `- ${reliableSources.length} current code-bound memory link(s)` : "- none yet"
|
|
17513
18037
|
);
|
|
17514
18038
|
const continuation = context.workset.continuation;
|
|
17515
|
-
if (continuation?.previousSession || continuation?.memories.length) {
|
|
18039
|
+
if (continuation?.previousSession || continuation?.memories.length || continuation?.compactCheckpoint) {
|
|
17516
18040
|
lines.push("", "Resume from prior work");
|
|
17517
18041
|
if (continuation.previousSession) {
|
|
17518
18042
|
const session = continuation.previousSession;
|
|
@@ -17527,6 +18051,12 @@ function formatAutoProjectContextSummary(context) {
|
|
|
17527
18051
|
"- #" + memory.id + " " + memory.type + ": " + compactContinuationText(memory.title, 18) + detail
|
|
17528
18052
|
);
|
|
17529
18053
|
}
|
|
18054
|
+
if (continuation.compactCheckpoint) {
|
|
18055
|
+
const checkpoint = continuation.compactCheckpoint;
|
|
18056
|
+
lines.push(
|
|
18057
|
+
"- Recent host compact checkpoint (" + [checkpoint.agent, checkpoint.captureKind, checkpoint.reason].join(", ") + "): " + compactContinuationText(checkpoint.summary, 36)
|
|
18058
|
+
);
|
|
18059
|
+
}
|
|
17530
18060
|
}
|
|
17531
18061
|
return lines.join("\n");
|
|
17532
18062
|
}
|
|
@@ -17593,7 +18123,7 @@ function formatLegacyAutoProjectContextPrompt(context) {
|
|
|
17593
18123
|
);
|
|
17594
18124
|
return lines.join("\n");
|
|
17595
18125
|
}
|
|
17596
|
-
var DEFAULT_MAX_AGE_MS;
|
|
18126
|
+
var DEFAULT_MAX_AGE_MS, COMPACT_CHECKPOINT_MAX_AGE_MS;
|
|
17597
18127
|
var init_auto_context = __esm({
|
|
17598
18128
|
"src/codegraph/auto-context.ts"() {
|
|
17599
18129
|
"use strict";
|
|
@@ -17613,6 +18143,7 @@ var init_auto_context = __esm({
|
|
|
17613
18143
|
init_session_store();
|
|
17614
18144
|
init_task_lens();
|
|
17615
18145
|
DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
|
|
18146
|
+
COMPACT_CHECKPOINT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
17616
18147
|
}
|
|
17617
18148
|
});
|
|
17618
18149
|
|
|
@@ -17660,7 +18191,7 @@ function addDefaultVerificationHints(input) {
|
|
|
17660
18191
|
return uniq2(hints);
|
|
17661
18192
|
}
|
|
17662
18193
|
function selectRelevantObservations(observations2, task, limit) {
|
|
17663
|
-
const
|
|
18194
|
+
const safeLimit2 = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 20;
|
|
17664
18195
|
const taskTokens2 = tokenize3(task);
|
|
17665
18196
|
const ranked = observations2.map((observation, index) => ({
|
|
17666
18197
|
observation,
|
|
@@ -17670,7 +18201,7 @@ function selectRelevantObservations(observations2, task, limit) {
|
|
|
17670
18201
|
}));
|
|
17671
18202
|
const positives = ranked.filter((item) => item.score > 0);
|
|
17672
18203
|
const pool = positives.length > 0 ? positives : ranked;
|
|
17673
|
-
return pool.sort((a, b) => b.score - a.score || b.time - a.time || b.index - a.index).slice(0,
|
|
18204
|
+
return pool.sort((a, b) => b.score - a.score || b.time - a.time || b.index - a.index).slice(0, safeLimit2).map((item) => item.observation);
|
|
17674
18205
|
}
|
|
17675
18206
|
function assembleContextPackForTask(input) {
|
|
17676
18207
|
const selected = selectRelevantObservations(input.observations, input.task, input.limit ?? 20);
|
|
@@ -18230,7 +18761,7 @@ __export(team_store_exports, {
|
|
|
18230
18761
|
isTeamStoreInitialized: () => isTeamStoreInitialized,
|
|
18231
18762
|
resetTeamStore: () => resetTeamStore
|
|
18232
18763
|
});
|
|
18233
|
-
import { randomUUID as
|
|
18764
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
18234
18765
|
import path20 from "path";
|
|
18235
18766
|
import fs15 from "fs";
|
|
18236
18767
|
function getTeamStore() {
|
|
@@ -18519,7 +19050,7 @@ var init_team_store = __esm({
|
|
|
18519
19050
|
* reactivate it (preserving agent_id). Otherwise create a new one.
|
|
18520
19051
|
*/
|
|
18521
19052
|
registerAgent(input) {
|
|
18522
|
-
const instanceId = input.instanceId ||
|
|
19053
|
+
const instanceId = input.instanceId || randomUUID9();
|
|
18523
19054
|
const now3 = Date.now();
|
|
18524
19055
|
const existing = this.stmtAgentFindByInstance.get(
|
|
18525
19056
|
input.projectId,
|
|
@@ -18545,7 +19076,7 @@ var init_team_store = __esm({
|
|
|
18545
19076
|
this.eventBus?.emit("agent:joined", { agentId: row2.agent_id, projectId: input.projectId, agentType: input.agentType });
|
|
18546
19077
|
return row2;
|
|
18547
19078
|
}
|
|
18548
|
-
const agentId =
|
|
19079
|
+
const agentId = randomUUID9();
|
|
18549
19080
|
this.stmtAgentUpsert.run({
|
|
18550
19081
|
agent_id: agentId,
|
|
18551
19082
|
project_id: input.projectId,
|
|
@@ -18647,7 +19178,7 @@ var init_team_store = __esm({
|
|
|
18647
19178
|
return { error: `Recipient agent '${input.recipientAgentId}' not found \u2014 cannot send to unknown agent` };
|
|
18648
19179
|
}
|
|
18649
19180
|
}
|
|
18650
|
-
const id =
|
|
19181
|
+
const id = randomUUID9();
|
|
18651
19182
|
const now3 = Date.now();
|
|
18652
19183
|
const row = {
|
|
18653
19184
|
id,
|
|
@@ -18697,7 +19228,7 @@ var init_team_store = __esm({
|
|
|
18697
19228
|
// Task Operations (with atomic claim semantics)
|
|
18698
19229
|
// ═══════════════════════════════════════════════════════════════════
|
|
18699
19230
|
createTask(input) {
|
|
18700
|
-
const taskId =
|
|
19231
|
+
const taskId = randomUUID9();
|
|
18701
19232
|
const now3 = Date.now();
|
|
18702
19233
|
const row = {
|
|
18703
19234
|
task_id: taskId,
|
|
@@ -18969,7 +19500,7 @@ var init_team_store = __esm({
|
|
|
18969
19500
|
for (const [agentId, msgs] of Object.entries(snap.messages.inboxes)) {
|
|
18970
19501
|
for (const msg of msgs) {
|
|
18971
19502
|
this.stmtMsgInsert.run({
|
|
18972
|
-
id: msg.id ??
|
|
19503
|
+
id: msg.id ?? randomUUID9(),
|
|
18973
19504
|
project_id: "migrated",
|
|
18974
19505
|
sender_agent_id: msg.from ?? "unknown",
|
|
18975
19506
|
recipient_agent_id: msg.to === "__broadcast__" ? null : msg.to ?? agentId,
|
|
@@ -19209,6 +19740,140 @@ var init_poll = __esm({
|
|
|
19209
19740
|
}
|
|
19210
19741
|
});
|
|
19211
19742
|
|
|
19743
|
+
// src/memory/compaction.ts
|
|
19744
|
+
var compaction_exports = {};
|
|
19745
|
+
__export(compaction_exports, {
|
|
19746
|
+
buildCompactionWorkset: () => buildCompactionWorkset,
|
|
19747
|
+
captureCompactionCheckpoint: () => captureCompactionCheckpoint,
|
|
19748
|
+
consumeCompactionWorkset: () => consumeCompactionWorkset
|
|
19749
|
+
});
|
|
19750
|
+
function normalizeBudget(value) {
|
|
19751
|
+
if (!Number.isFinite(value) || value == null) return DEFAULT_WORKSET_BUDGET;
|
|
19752
|
+
return Math.max(MIN_WORKSET_BUDGET, Math.min(2e3, Math.floor(value)));
|
|
19753
|
+
}
|
|
19754
|
+
function sourceLabel(checkpoint) {
|
|
19755
|
+
if (checkpoint.captureKind === "native-summary") return "native host summary";
|
|
19756
|
+
if (checkpoint.captureKind === "preflight") return "pre-compact lifecycle marker";
|
|
19757
|
+
return "host lifecycle marker";
|
|
19758
|
+
}
|
|
19759
|
+
function buildCompactionWorkset(checkpoint, options = {}) {
|
|
19760
|
+
const budget = normalizeBudget(options.maxTokens);
|
|
19761
|
+
const task = options.task?.trim() ? truncateToTokenBudget(sanitizeCredentials(options.task.trim()), Math.max(8, Math.floor(budget * 0.25))) : "";
|
|
19762
|
+
const headerLines = [
|
|
19763
|
+
"## Compact Continuation",
|
|
19764
|
+
`- Host: ${checkpoint.agent} (${sourceLabel(checkpoint)})`,
|
|
19765
|
+
`- Trigger: ${checkpoint.reason}`,
|
|
19766
|
+
task ? `- Task now: ${task}` : "",
|
|
19767
|
+
"- Current code remains authoritative."
|
|
19768
|
+
].filter(Boolean);
|
|
19769
|
+
const header = `${headerLines.join("\n")}
|
|
19770
|
+
|
|
19771
|
+
`;
|
|
19772
|
+
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.";
|
|
19773
|
+
const source = sanitizeCredentials(checkpoint.summary?.trim() || fallback);
|
|
19774
|
+
const available = Math.max(0, budget - countTextTokens(header) - 4);
|
|
19775
|
+
let excerpt = source ? truncateToTokenBudget(source, available) : "";
|
|
19776
|
+
let text = `${header}${excerpt ? `### Host checkpoint
|
|
19777
|
+
${excerpt}` : ""}`.trim();
|
|
19778
|
+
while (excerpt && countTextTokens(text) > budget) {
|
|
19779
|
+
const nextExcerptBudget = Math.max(1, Math.floor(countTextTokens(excerpt) * 0.8));
|
|
19780
|
+
excerpt = truncateToTokenBudget(excerpt, nextExcerptBudget);
|
|
19781
|
+
text = `${header}${excerpt ? `### Host checkpoint
|
|
19782
|
+
${excerpt}` : ""}`.trim();
|
|
19783
|
+
}
|
|
19784
|
+
if (countTextTokens(text) > budget) text = truncateToTokenBudget(text, budget);
|
|
19785
|
+
return {
|
|
19786
|
+
checkpointId: checkpoint.id,
|
|
19787
|
+
text,
|
|
19788
|
+
tokens: countTextTokens(text)
|
|
19789
|
+
};
|
|
19790
|
+
}
|
|
19791
|
+
function sourceEvent(input) {
|
|
19792
|
+
const raw = input.raw;
|
|
19793
|
+
const event = raw.hook_event_name ?? raw.event ?? raw.type;
|
|
19794
|
+
return typeof event === "string" && event ? event : input.event;
|
|
19795
|
+
}
|
|
19796
|
+
async function captureCompactionCheckpoint(input) {
|
|
19797
|
+
const isCompactResume = input.event === "session_start" && input.sessionStartReason?.trim().toLowerCase() === "compact";
|
|
19798
|
+
if (input.event !== "pre_compact" && input.event !== "post_compact" && !isCompactResume) return null;
|
|
19799
|
+
if (!input.sessionId || !input.cwd) return null;
|
|
19800
|
+
const [
|
|
19801
|
+
{ detectProject: detectProject2 },
|
|
19802
|
+
{ getProjectDataDir: getProjectDataDir2 },
|
|
19803
|
+
{ initAliasRegistry: initAliasRegistry2, registerAlias: registerAlias2 }
|
|
19804
|
+
] = await Promise.all([
|
|
19805
|
+
Promise.resolve().then(() => (init_detector(), detector_exports)),
|
|
19806
|
+
Promise.resolve().then(() => (init_persistence(), persistence_exports)),
|
|
19807
|
+
Promise.resolve().then(() => (init_aliases(), aliases_exports))
|
|
19808
|
+
]);
|
|
19809
|
+
const project = detectProject2(input.cwd);
|
|
19810
|
+
if (!project) return null;
|
|
19811
|
+
const dataDir = await getProjectDataDir2(project.id);
|
|
19812
|
+
initAliasRegistry2(dataDir);
|
|
19813
|
+
const projectId = await registerAlias2(project);
|
|
19814
|
+
const store = new CompactionCheckpointStore(dataDir);
|
|
19815
|
+
if (input.event === "pre_compact") {
|
|
19816
|
+
return store.recordPreflight({
|
|
19817
|
+
projectId,
|
|
19818
|
+
sessionId: input.sessionId,
|
|
19819
|
+
agent: input.agent,
|
|
19820
|
+
reason: input.compaction?.reason,
|
|
19821
|
+
sourceEvent: sourceEvent(input),
|
|
19822
|
+
transcriptAvailable: Boolean(input.transcriptPath),
|
|
19823
|
+
capturedAt: input.timestamp
|
|
19824
|
+
});
|
|
19825
|
+
}
|
|
19826
|
+
return store.complete({
|
|
19827
|
+
projectId,
|
|
19828
|
+
sessionId: input.sessionId,
|
|
19829
|
+
agent: input.agent,
|
|
19830
|
+
reason: input.compaction?.reason,
|
|
19831
|
+
sourceEvent: sourceEvent(input),
|
|
19832
|
+
sourceKey: input.compaction?.sourceKey,
|
|
19833
|
+
summary: input.compaction?.summary,
|
|
19834
|
+
tokensBefore: input.compaction?.tokensBefore,
|
|
19835
|
+
firstKeptEntryId: input.compaction?.firstKeptEntryId,
|
|
19836
|
+
details: input.compaction?.details,
|
|
19837
|
+
completedAt: input.timestamp
|
|
19838
|
+
});
|
|
19839
|
+
}
|
|
19840
|
+
async function consumeCompactionWorkset(input, options = {}) {
|
|
19841
|
+
if (!input.sessionId || !input.cwd) return null;
|
|
19842
|
+
const [
|
|
19843
|
+
{ detectProject: detectProject2 },
|
|
19844
|
+
{ getProjectDataDir: getProjectDataDir2 },
|
|
19845
|
+
{ initAliasRegistry: initAliasRegistry2, registerAlias: registerAlias2 }
|
|
19846
|
+
] = await Promise.all([
|
|
19847
|
+
Promise.resolve().then(() => (init_detector(), detector_exports)),
|
|
19848
|
+
Promise.resolve().then(() => (init_persistence(), persistence_exports)),
|
|
19849
|
+
Promise.resolve().then(() => (init_aliases(), aliases_exports))
|
|
19850
|
+
]);
|
|
19851
|
+
const project = detectProject2(input.cwd);
|
|
19852
|
+
if (!project) return null;
|
|
19853
|
+
const dataDir = await getProjectDataDir2(project.id);
|
|
19854
|
+
initAliasRegistry2(dataDir);
|
|
19855
|
+
const projectId = await registerAlias2(project);
|
|
19856
|
+
const store = new CompactionCheckpointStore(dataDir);
|
|
19857
|
+
const checkpoint = store.findUndelivered(projectId, input.sessionId, input.agent);
|
|
19858
|
+
if (!checkpoint) return null;
|
|
19859
|
+
const workset = buildCompactionWorkset(checkpoint, options);
|
|
19860
|
+
if (!workset.text) return null;
|
|
19861
|
+
store.markDelivered(checkpoint.id);
|
|
19862
|
+
return workset;
|
|
19863
|
+
}
|
|
19864
|
+
var DEFAULT_WORKSET_BUDGET, MIN_WORKSET_BUDGET;
|
|
19865
|
+
var init_compaction = __esm({
|
|
19866
|
+
"src/memory/compaction.ts"() {
|
|
19867
|
+
"use strict";
|
|
19868
|
+
init_esm_shims();
|
|
19869
|
+
init_token_budget();
|
|
19870
|
+
init_secret_filter();
|
|
19871
|
+
init_compaction_checkpoint_store();
|
|
19872
|
+
DEFAULT_WORKSET_BUDGET = 420;
|
|
19873
|
+
MIN_WORKSET_BUDGET = 48;
|
|
19874
|
+
}
|
|
19875
|
+
});
|
|
19876
|
+
|
|
19212
19877
|
// src/memory/export-import.ts
|
|
19213
19878
|
var export_import_exports = {};
|
|
19214
19879
|
__export(export_import_exports, {
|
|
@@ -20174,6 +20839,17 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
20174
20839
|
sourceCounts,
|
|
20175
20840
|
recentObservations: sorted,
|
|
20176
20841
|
embedding: embeddingStatus,
|
|
20842
|
+
// A standalone dashboard has no access to an active MCP
|
|
20843
|
+
// process's in-memory Orama index. Keep this explicit so the
|
|
20844
|
+
// UI never presents an empty local singleton as a healthy
|
|
20845
|
+
// all-vectors-indexed result.
|
|
20846
|
+
vectorStatus: {
|
|
20847
|
+
available: false,
|
|
20848
|
+
total: 0,
|
|
20849
|
+
missing: 0,
|
|
20850
|
+
missingIds: [],
|
|
20851
|
+
backfillRunning: false
|
|
20852
|
+
},
|
|
20177
20853
|
storage: storageInfo,
|
|
20178
20854
|
maintenance,
|
|
20179
20855
|
...lifecycle ? { lifecycle } : {},
|
|
@@ -21287,7 +21963,7 @@ __export(planner_exports, {
|
|
|
21287
21963
|
materializeTaskGraph: () => materializeTaskGraph,
|
|
21288
21964
|
seedAutonomousPipeline: () => seedAutonomousPipeline
|
|
21289
21965
|
});
|
|
21290
|
-
import { randomUUID as
|
|
21966
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
21291
21967
|
function isPlannerTask(metadata) {
|
|
21292
21968
|
if (!metadata) return null;
|
|
21293
21969
|
try {
|
|
@@ -21345,7 +22021,7 @@ function seedAutonomousPipeline(teamStore, projectId, config, opts) {
|
|
|
21345
22021
|
const maxIterations = config.maxIterations ?? 3;
|
|
21346
22022
|
const taskBudget = config.taskBudget ?? 15;
|
|
21347
22023
|
const structuredPlan = opts?.structuredPlan ?? true;
|
|
21348
|
-
const pipelineId =
|
|
22024
|
+
const pipelineId = randomUUID10();
|
|
21349
22025
|
const meta = {
|
|
21350
22026
|
plannerType: "plan",
|
|
21351
22027
|
pipelineId,
|
|
@@ -23416,7 +24092,7 @@ init_freshness();
|
|
|
23416
24092
|
init_obs_store();
|
|
23417
24093
|
init_mini_skill_store();
|
|
23418
24094
|
init_session_store();
|
|
23419
|
-
import { createHash as
|
|
24095
|
+
import { createHash as createHash14 } from "crypto";
|
|
23420
24096
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
23421
24097
|
import { z as z2 } from "zod";
|
|
23422
24098
|
|
|
@@ -26886,6 +27562,7 @@ var TOOL_PROFILES = Object.freeze({
|
|
|
26886
27562
|
memorix_rules_sync: ["full"],
|
|
26887
27563
|
memorix_workspace_sync: ["full"],
|
|
26888
27564
|
memorix_ingest_image: ["full"],
|
|
27565
|
+
memorix_compaction_checkpoint: ["full"],
|
|
26889
27566
|
// ── MCP Official Memory Server compatibility (KG tools) ──────────
|
|
26890
27567
|
// These are only useful to users specifically migrating from the
|
|
26891
27568
|
// reference mcp-memory server. Hide them unless explicitly enabled.
|
|
@@ -27052,7 +27729,7 @@ function coerceObjectArray(val) {
|
|
|
27052
27729
|
return [];
|
|
27053
27730
|
}
|
|
27054
27731
|
function createDeterministicInstanceId(projectId, agentType, agentName) {
|
|
27055
|
-
const digest2 =
|
|
27732
|
+
const digest2 = createHash14("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
|
|
27056
27733
|
return `auto-${digest2}`;
|
|
27057
27734
|
}
|
|
27058
27735
|
var BOOTSTRAP_SAFE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -27333,7 +28010,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
27333
28010
|
};
|
|
27334
28011
|
const server = existingServer ?? new McpServer({
|
|
27335
28012
|
name: "memorix",
|
|
27336
|
-
version: true ? "1.2.
|
|
28013
|
+
version: true ? "1.2.8" : "1.0.1"
|
|
27337
28014
|
});
|
|
27338
28015
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
27339
28016
|
server.registerTool = ((name, ...args) => {
|
|
@@ -27981,12 +28658,12 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
|
|
|
27981
28658
|
if (boundary) return boundary;
|
|
27982
28659
|
}
|
|
27983
28660
|
return withFreshIndex(async () => {
|
|
27984
|
-
const
|
|
28661
|
+
const safeLimit2 = limit != null ? coerceNumber(limit, 20) : void 0;
|
|
27985
28662
|
const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
|
|
27986
28663
|
const TIMEOUT_MS = 3e4;
|
|
27987
28664
|
const searchPromise = compactSearch({
|
|
27988
28665
|
query,
|
|
27989
|
-
limit:
|
|
28666
|
+
limit: safeLimit2,
|
|
27990
28667
|
type,
|
|
27991
28668
|
maxTokens: safeMaxTokens,
|
|
27992
28669
|
since,
|
|
@@ -28787,10 +29464,10 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
|
|
|
28787
29464
|
const unresolved = requireResolvedProject("search reasoning in the current project");
|
|
28788
29465
|
if (unresolved) return unresolved;
|
|
28789
29466
|
}
|
|
28790
|
-
const
|
|
29467
|
+
const safeLimit2 = limit != null ? coerceNumber(limit, 10) : 10;
|
|
28791
29468
|
const result = await withFreshIndex(() => compactSearch({
|
|
28792
29469
|
query,
|
|
28793
|
-
limit:
|
|
29470
|
+
limit: safeLimit2,
|
|
28794
29471
|
type: "reasoning",
|
|
28795
29472
|
projectId: scope === "global" ? void 0 : project.id,
|
|
28796
29473
|
status: "active",
|
|
@@ -30113,9 +30790,9 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
30113
30790
|
}
|
|
30114
30791
|
},
|
|
30115
30792
|
async ({ limit }) => {
|
|
30116
|
-
const
|
|
30793
|
+
const safeLimit2 = limit != null ? coerceNumber(limit, 3) : 3;
|
|
30117
30794
|
const { getSessionContext: getSessionContext2, listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
30118
|
-
const context = await getSessionContext2(projectDir2, project.id,
|
|
30795
|
+
const context = await getSessionContext2(projectDir2, project.id, safeLimit2, getObservationReader());
|
|
30119
30796
|
const sessions = await listSessions2(projectDir2, project.id);
|
|
30120
30797
|
const activeSessions = sessions.filter((s) => s.status === "active");
|
|
30121
30798
|
const completedSessions = sessions.filter((s) => s.status === "completed");
|
|
@@ -30136,6 +30813,86 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
|
|
|
30136
30813
|
};
|
|
30137
30814
|
}
|
|
30138
30815
|
);
|
|
30816
|
+
server.registerTool(
|
|
30817
|
+
"memorix_compaction_checkpoint",
|
|
30818
|
+
{
|
|
30819
|
+
title: "Compact Continuity Checkpoints",
|
|
30820
|
+
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.",
|
|
30821
|
+
inputSchema: {
|
|
30822
|
+
action: z2.enum(["list", "show", "context", "archive"]).default("list"),
|
|
30823
|
+
id: z2.string().optional().describe("Checkpoint ID for show, context, or archive"),
|
|
30824
|
+
sessionId: z2.string().optional().describe("Optional host session ID filter"),
|
|
30825
|
+
agent: z2.string().optional().describe("Optional host agent filter"),
|
|
30826
|
+
task: z2.string().optional().describe("Current task for a bounded context preview"),
|
|
30827
|
+
maxTokens: z2.number().optional().describe("Maximum tokens for action=context (default: 420)"),
|
|
30828
|
+
limit: z2.number().optional().describe("Maximum records for action=list (default: 20)"),
|
|
30829
|
+
includeArchived: z2.boolean().optional().default(false).describe("Include archived records in action=list")
|
|
30830
|
+
}
|
|
30831
|
+
},
|
|
30832
|
+
async ({ action, id, sessionId, agent, task, maxTokens, limit, includeArchived }) => {
|
|
30833
|
+
const unresolved = requireResolvedProject("inspect compact continuity checkpoints");
|
|
30834
|
+
if (unresolved) return unresolved;
|
|
30835
|
+
const [{ CompactionCheckpointStore: CompactionCheckpointStore2 }, { buildCompactionWorkset: buildCompactionWorkset2 }] = await Promise.all([
|
|
30836
|
+
Promise.resolve().then(() => (init_compaction_checkpoint_store(), compaction_checkpoint_store_exports)),
|
|
30837
|
+
Promise.resolve().then(() => (init_compaction(), compaction_exports))
|
|
30838
|
+
]);
|
|
30839
|
+
const store = new CompactionCheckpointStore2(projectDir2);
|
|
30840
|
+
const assertCheckpoint = (checkpointId) => {
|
|
30841
|
+
const checkpoint2 = store.get(checkpointId);
|
|
30842
|
+
if (!checkpoint2 || checkpoint2.projectId !== project.id) return null;
|
|
30843
|
+
return checkpoint2;
|
|
30844
|
+
};
|
|
30845
|
+
if (action === "list") {
|
|
30846
|
+
const checkpoints = store.list({
|
|
30847
|
+
projectId: project.id,
|
|
30848
|
+
sessionId: sessionId?.trim() || void 0,
|
|
30849
|
+
agent: agent?.trim() || void 0,
|
|
30850
|
+
includeArchived: Boolean(includeArchived),
|
|
30851
|
+
limit: limit != null ? Math.max(1, Math.min(100, coerceNumber(limit, 20))) : 20
|
|
30852
|
+
});
|
|
30853
|
+
return {
|
|
30854
|
+
content: [{ type: "text", text: JSON.stringify({ projectId: project.id, checkpoints }, null, 2) }]
|
|
30855
|
+
};
|
|
30856
|
+
}
|
|
30857
|
+
if (!id?.trim()) {
|
|
30858
|
+
return {
|
|
30859
|
+
content: [{ type: "text", text: "id is required for this checkpoint action." }],
|
|
30860
|
+
isError: true
|
|
30861
|
+
};
|
|
30862
|
+
}
|
|
30863
|
+
const checkpoint = assertCheckpoint(id.trim());
|
|
30864
|
+
if (!checkpoint) {
|
|
30865
|
+
return {
|
|
30866
|
+
content: [{ type: "text", text: `Checkpoint "${id.trim()}" was not found for the current project.` }],
|
|
30867
|
+
isError: true
|
|
30868
|
+
};
|
|
30869
|
+
}
|
|
30870
|
+
if (action === "show") {
|
|
30871
|
+
return {
|
|
30872
|
+
content: [{ type: "text", text: JSON.stringify({ projectId: project.id, checkpoint }, null, 2) }]
|
|
30873
|
+
};
|
|
30874
|
+
}
|
|
30875
|
+
if (action === "context") {
|
|
30876
|
+
const workset = buildCompactionWorkset2(checkpoint, {
|
|
30877
|
+
task: task?.trim(),
|
|
30878
|
+
maxTokens: maxTokens != null ? coerceNumber(maxTokens, 420) : 420
|
|
30879
|
+
});
|
|
30880
|
+
return {
|
|
30881
|
+
content: [{ type: "text", text: workset.text }]
|
|
30882
|
+
};
|
|
30883
|
+
}
|
|
30884
|
+
const archived = store.archive(checkpoint.id);
|
|
30885
|
+
if (!archived) {
|
|
30886
|
+
return {
|
|
30887
|
+
content: [{ type: "text", text: `Checkpoint "${checkpoint.id}" is already archived.` }],
|
|
30888
|
+
isError: true
|
|
30889
|
+
};
|
|
30890
|
+
}
|
|
30891
|
+
return {
|
|
30892
|
+
content: [{ type: "text", text: `Archived compact checkpoint ${archived.id}.` }]
|
|
30893
|
+
};
|
|
30894
|
+
}
|
|
30895
|
+
);
|
|
30139
30896
|
server.registerTool(
|
|
30140
30897
|
"memorix_transfer",
|
|
30141
30898
|
{
|