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/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 = 'pending', run_after = ?, attempts = CASE WHEN ? THEN 0 ELSE attempts END,
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(now3 + delayMs, options.resetAttempts ? 1 : 0, payloadJson, now3, id, workerId);
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 safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(200, Math.floor(limit))) : 20;
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, safeLimit).map(rowToSnapshot);
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
- for (const id of ids) {
9166
- const obs = observationById.get(id);
9167
- if (!obs) {
9168
- vectorMissingIds.delete(id);
9169
- continue;
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
- const text = [obs.title, obs.narrative, ...obs.facts].join(" ");
9172
- try {
9173
- const embedding = await generateEmbedding(text);
9174
- if (embedding) {
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
- lastFailure = `dimension mismatch: provider returned ${embedding.length}d, index expects ${vectorDimensions ?? "unknown"}d`;
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
- } else if (isEmbeddingExplicitlyDisabled()) {
9223
- vectorMissingIds.delete(id);
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 { attempted: ids.length, succeeded, failed };
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) return { action: "complete" };
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) return { action: "complete" };
15448
+ if (after.missing === 0) {
15449
+ resolveSupersededVectorFailures(projectDir2, projectId);
15450
+ return { action: "complete" };
15451
+ }
15295
15452
  if (result.failed > 0 && result.succeeded === 0) {
15296
- throw new Error(`vector backfill made no progress (${result.failed}/${result.attempted} failed)`);
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 safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 20;
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, safeLimit).map((item) => item.observation);
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 randomUUID8 } from "crypto";
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 || randomUUID8();
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 = randomUUID8();
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 = randomUUID8();
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 = randomUUID8();
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 ?? randomUUID8(),
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 randomUUID9 } from "crypto";
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 = randomUUID9();
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 createHash13 } from "crypto";
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 = createHash13("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
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.6" : "1.0.1"
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 safeLimit = limit != null ? coerceNumber(limit, 20) : void 0;
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: safeLimit,
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 safeLimit = limit != null ? coerceNumber(limit, 10) : 10;
29467
+ const safeLimit2 = limit != null ? coerceNumber(limit, 10) : 10;
28791
29468
  const result = await withFreshIndex(() => compactSearch({
28792
29469
  query,
28793
- limit: safeLimit,
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 safeLimit = limit != null ? coerceNumber(limit, 3) : 3;
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, safeLimit, getObservationReader());
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
  {