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.
Files changed (53) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +6 -3
  3. package/README.zh-CN.md +5 -2
  4. package/dist/cli/index.js +1272 -105
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/dashboard/static/app.js +7 -2
  7. package/dist/index.js +809 -52
  8. package/dist/index.js.map +1 -1
  9. package/dist/maintenance-runner.d.ts +6 -0
  10. package/dist/maintenance-runner.js +204 -25
  11. package/dist/maintenance-runner.js.map +1 -1
  12. package/dist/memcode-runtime/CHANGELOG.md +20 -0
  13. package/dist/sdk.d.ts +1 -1
  14. package/dist/sdk.js +809 -52
  15. package/dist/sdk.js.map +1 -1
  16. package/dist/types.d.ts +38 -1
  17. package/dist/types.js.map +1 -1
  18. package/docs/1.2.7-CONTEXT-CONTINUITY.md +68 -0
  19. package/docs/API_REFERENCE.md +6 -2
  20. package/docs/dev-log/progress.txt +48 -0
  21. package/docs/hooks-architecture.md +2 -1
  22. package/package.json +3 -3
  23. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  24. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  25. package/plugins/copilot/memorix/plugin.json +1 -1
  26. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  27. package/plugins/omp/memorix/extensions/memorix.js +17 -0
  28. package/plugins/omp/memorix/package.json +1 -1
  29. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  30. package/plugins/pi/memorix/extensions/memorix.js +17 -0
  31. package/plugins/pi/memorix/package.json +1 -1
  32. package/src/cli/capability-map.ts +1 -0
  33. package/src/cli/command-guide.ts +10 -0
  34. package/src/cli/commands/agent-integrations.ts +102 -5
  35. package/src/cli/commands/checkpoint.ts +144 -0
  36. package/src/cli/commands/serve-http.ts +14 -3
  37. package/src/cli/commands/setup.ts +44 -0
  38. package/src/cli/index.ts +3 -1
  39. package/src/codegraph/auto-context.ts +51 -5
  40. package/src/dashboard/server.ts +11 -0
  41. package/src/hooks/handler.ts +103 -11
  42. package/src/hooks/normalizer.ts +85 -1
  43. package/src/hooks/types.ts +16 -0
  44. package/src/knowledge/workset.ts +43 -2
  45. package/src/memory/compaction.ts +165 -0
  46. package/src/memory/observations.ts +67 -20
  47. package/src/runtime/maintenance-jobs.ts +84 -4
  48. package/src/runtime/project-maintenance.ts +63 -6
  49. package/src/server/tool-profile.ts +1 -0
  50. package/src/server.ts +94 -0
  51. package/src/store/compaction-checkpoint-store.ts +397 -0
  52. package/src/store/sqlite-db.ts +41 -0
  53. 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 = 'pending', run_after = ?, attempts = CASE WHEN ? THEN 0 ELSE attempts END,
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(now3 + delayMs, options.resetAttempts ? 1 : 0, payloadJson, now3, id, workerId);
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 safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(200, Math.floor(limit))) : 20;
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, safeLimit).map(rowToSnapshot);
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
- for (const id of ids) {
9170
- const obs = observationById.get(id);
9171
- if (!obs) {
9172
- vectorMissingIds.delete(id);
9173
- continue;
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
- const text = [obs.title, obs.narrative, ...obs.facts].join(" ");
9176
- try {
9177
- const embedding = await generateEmbedding(text);
9178
- if (embedding) {
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
- lastFailure = `dimension mismatch: provider returned ${embedding.length}d, index expects ${vectorDimensions ?? "unknown"}d`;
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
- } else if (isEmbeddingExplicitlyDisabled()) {
9227
- vectorMissingIds.delete(id);
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 { attempted: ids.length, succeeded, failed };
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) return { action: "complete" };
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) return { action: "complete" };
15452
+ if (after.missing === 0) {
15453
+ resolveSupersededVectorFailures(projectDir2, projectId);
15454
+ return { action: "complete" };
15455
+ }
15299
15456
  if (result.failed > 0 && result.succeeded === 0) {
15300
- throw new Error(`vector backfill made no progress (${result.failed}/${result.attempted} failed)`);
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 safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 20;
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, safeLimit).map((item) => item.observation);
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 randomUUID8 } from "crypto";
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 || randomUUID8();
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 = randomUUID8();
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 = randomUUID8();
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 = randomUUID8();
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 ?? randomUUID8(),
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 randomUUID9 } from "crypto";
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 = randomUUID9();
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 createHash13 } from "crypto";
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 = createHash13("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
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.6" : "1.0.1"
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 safeLimit = limit != null ? coerceNumber(limit, 20) : void 0;
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: safeLimit,
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 safeLimit = limit != null ? coerceNumber(limit, 10) : 10;
29383
+ const safeLimit2 = limit != null ? coerceNumber(limit, 10) : 10;
28707
29384
  const result = await withFreshIndex(() => compactSearch({
28708
29385
  query,
28709
- limit: safeLimit,
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 safeLimit = limit != null ? coerceNumber(limit, 3) : 3;
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, safeLimit, getObservationReader());
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
  {