memorix 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +6 -3
  3. package/README.zh-CN.md +5 -2
  4. package/dist/cli/index.js +954 -72
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/cli/memcode.js +0 -0
  7. package/dist/index.js +632 -27
  8. package/dist/index.js.map +1 -1
  9. package/dist/maintenance-runner.js +39 -1
  10. package/dist/maintenance-runner.js.map +1 -1
  11. package/dist/memcode-runtime/CHANGELOG.md +12 -0
  12. package/dist/sdk.d.ts +1 -1
  13. package/dist/sdk.js +632 -27
  14. package/dist/sdk.js.map +1 -1
  15. package/dist/types.d.ts +38 -1
  16. package/dist/types.js.map +1 -1
  17. package/docs/1.2.7-CONTEXT-CONTINUITY.md +68 -0
  18. package/docs/API_REFERENCE.md +6 -2
  19. package/docs/dev-log/progress.txt +23 -0
  20. package/docs/hooks-architecture.md +2 -1
  21. package/package.json +1 -1
  22. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  23. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  24. package/plugins/copilot/memorix/plugin.json +1 -1
  25. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  26. package/plugins/omp/memorix/extensions/memorix.js +17 -0
  27. package/plugins/omp/memorix/package.json +1 -1
  28. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  29. package/plugins/pi/memorix/extensions/memorix.js +17 -0
  30. package/plugins/pi/memorix/package.json +1 -1
  31. package/src/cli/capability-map.ts +1 -0
  32. package/src/cli/command-guide.ts +10 -0
  33. package/src/cli/commands/checkpoint.ts +144 -0
  34. package/src/cli/index.ts +3 -1
  35. package/src/codegraph/auto-context.ts +51 -5
  36. package/src/hooks/handler.ts +103 -11
  37. package/src/hooks/normalizer.ts +85 -1
  38. package/src/hooks/types.ts +16 -0
  39. package/src/knowledge/workset.ts +43 -2
  40. package/src/memory/compaction.ts +165 -0
  41. package/src/server/tool-profile.ts +1 -0
  42. package/src/server.ts +94 -0
  43. package/src/store/compaction-checkpoint-store.ts +397 -0
  44. package/src/store/sqlite-db.ts +41 -0
  45. package/src/types.ts +46 -0
package/dist/cli/index.js CHANGED
@@ -3212,7 +3212,7 @@ ${import_picocolors.default.gray(m3)} ${s2}
3212
3212
 
3213
3213
  // src/cli/version.ts
3214
3214
  function getCliVersion() {
3215
- return true ? "1.2.6" : pkg.version;
3215
+ return true ? "1.2.7" : pkg.version;
3216
3216
  }
3217
3217
  var init_version = __esm({
3218
3218
  "src/cli/version.ts"() {
@@ -9304,6 +9304,7 @@ function getDatabase(dataDir) {
9304
9304
  db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
9305
9305
  db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
9306
9306
  db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
9307
+ db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
9307
9308
  try {
9308
9309
  db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
9309
9310
  } catch {
@@ -9344,7 +9345,7 @@ function getDatabase(dataDir) {
9344
9345
  _dbCache.set(normalized, db2);
9345
9346
  return db2;
9346
9347
  }
9347
- 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;
9348
+ 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;
9348
9349
  var init_sqlite_db = __esm({
9349
9350
  "src/store/sqlite-db.ts"() {
9350
9351
  "use strict";
@@ -9796,6 +9797,31 @@ CREATE TABLE IF NOT EXISTS maintenance_targets (
9796
9797
  data_dir TEXT NOT NULL,
9797
9798
  updated_at INTEGER NOT NULL
9798
9799
  );
9800
+ `;
9801
+ CREATE_COMPACTION_CHECKPOINTS_TABLE = `
9802
+ CREATE TABLE IF NOT EXISTS compaction_checkpoints (
9803
+ id TEXT PRIMARY KEY,
9804
+ project_id TEXT NOT NULL,
9805
+ session_id TEXT NOT NULL,
9806
+ agent TEXT NOT NULL,
9807
+ phase TEXT NOT NULL,
9808
+ capture_kind TEXT NOT NULL,
9809
+ reason TEXT NOT NULL DEFAULT 'unknown',
9810
+ source_event TEXT NOT NULL,
9811
+ source_key TEXT NOT NULL,
9812
+ summary TEXT,
9813
+ tokens_before INTEGER,
9814
+ first_kept_entry_id TEXT,
9815
+ details_json TEXT NOT NULL DEFAULT '{}',
9816
+ transcript_available INTEGER NOT NULL DEFAULT 0,
9817
+ status TEXT NOT NULL DEFAULT 'active',
9818
+ pre_captured_at TEXT NOT NULL,
9819
+ completed_at TEXT,
9820
+ delivered_at TEXT,
9821
+ delivery_count INTEGER NOT NULL DEFAULT 0,
9822
+ created_at TEXT NOT NULL,
9823
+ updated_at TEXT NOT NULL
9824
+ );
9799
9825
  `;
9800
9826
  CREATE_INDEXES = `
9801
9827
  CREATE INDEX IF NOT EXISTS idx_observations_projectId ON observations(projectId);
@@ -9843,6 +9869,9 @@ CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowl
9843
9869
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_ready ON maintenance_jobs(status, run_after);
9844
9870
  CREATE INDEX IF NOT EXISTS idx_maintenance_jobs_project ON maintenance_jobs(project_id, status, run_after);
9845
9871
  CREATE INDEX IF NOT EXISTS idx_maintenance_targets_updated ON maintenance_targets(updated_at);
9872
+ CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_project_recent ON compaction_checkpoints(project_id, status, completed_at DESC, pre_captured_at DESC);
9873
+ CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_session_pending ON compaction_checkpoints(project_id, session_id, agent, phase, status, pre_captured_at DESC);
9874
+ CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key);
9846
9875
  CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
9847
9876
  ON maintenance_jobs(project_id, kind, dedupe_key)
9848
9877
  WHERE status IN ('pending', 'running', 'retry');
@@ -9916,6 +9945,15 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
9916
9945
  db2.exec("CREATE INDEX IF NOT EXISTS idx_knowledge_workflows_workspace_status ON knowledge_workflows(workspaceId, status, updatedAt DESC)");
9917
9946
  db2.exec("CREATE INDEX IF NOT EXISTS idx_knowledge_workflow_runs_project_workflow ON knowledge_workflow_runs(projectId, workflowId, startedAt DESC)");
9918
9947
  }
9948
+ },
9949
+ {
9950
+ id: "1.2.7-compaction-checkpoints",
9951
+ apply: (db2) => {
9952
+ db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
9953
+ 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)");
9954
+ 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)");
9955
+ db2.exec("CREATE INDEX IF NOT EXISTS idx_compaction_checkpoints_source ON compaction_checkpoints(project_id, source_key)");
9956
+ }
9919
9957
  }
9920
9958
  ];
9921
9959
  _dbCache = /* @__PURE__ */ new Map();
@@ -18126,10 +18164,10 @@ var init_store = __esm({
18126
18164
  return row ? rowToSnapshot(row) : void 0;
18127
18165
  }
18128
18166
  listSnapshots(projectId, limit = 20) {
18129
- const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(200, Math.floor(limit))) : 20;
18167
+ const safeLimit2 = Number.isFinite(limit) ? Math.max(1, Math.min(200, Math.floor(limit))) : 20;
18130
18168
  return this.db.prepare(
18131
18169
  "SELECT * FROM code_state_snapshots WHERE projectId = ? ORDER BY sourceEpoch DESC LIMIT ?"
18132
- ).all(projectId, safeLimit).map(rowToSnapshot);
18170
+ ).all(projectId, safeLimit2).map(rowToSnapshot);
18133
18171
  }
18134
18172
  /**
18135
18173
  * Record a completed scan and mark all current structural facts with its
@@ -34934,7 +34972,7 @@ function renderTaskWorksetPrompt(input) {
34934
34972
  });
34935
34973
  appendLine(lines, "Task lens: " + input.lens, maxTokens, omitted, "lens");
34936
34974
  const hasContinuation = Boolean(
34937
- input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0
34975
+ input.continuation?.previousSession || (input.continuation?.memories.length ?? 0) > 0 || input.continuation?.compactCheckpoint
34938
34976
  );
34939
34977
  if (hasContinuation && input.continuation) {
34940
34978
  appendLine(lines, "", maxTokens, omitted, "continuation-heading");
@@ -34974,6 +35012,24 @@ function renderTaskWorksetPrompt(input) {
34974
35012
  }
34975
35013
  );
34976
35014
  }
35015
+ if (input.continuation.compactCheckpoint) {
35016
+ const checkpoint = input.continuation.compactCheckpoint;
35017
+ const source = `${checkpoint.agent}, ${checkpoint.captureKind}, ${checkpoint.reason}`;
35018
+ appendLine(
35019
+ lines,
35020
+ "- Recent host compact checkpoint (" + source + "): " + short(checkpoint.summary, 36),
35021
+ maxTokens,
35022
+ omitted,
35023
+ "continuation-compact-checkpoint",
35024
+ selected,
35025
+ {
35026
+ kind: "continuation",
35027
+ id: "compact:" + checkpoint.id,
35028
+ reason: "recent host-native compact lifecycle evidence",
35029
+ trust: "historical"
35030
+ }
35031
+ );
35032
+ }
34977
35033
  }
34978
35034
  if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
34979
35035
  appendLine(lines, "", maxTokens, omitted, "caution-heading");
@@ -35243,7 +35299,7 @@ async function buildTaskWorkset(input) {
35243
35299
  ...input.verificationHints
35244
35300
  ]).slice(0, 4);
35245
35301
  const normalizedCautions = unique(cautions.map((caution) => caution.kind)).map((kind) => cautions.find((caution) => caution.kind === kind)).slice(0, 6);
35246
- const continuation = input.continuation && (input.continuation.previousSession || input.continuation.memories.length > 0) ? {
35302
+ const continuation = input.continuation && (input.continuation.previousSession || input.continuation.memories.length > 0 || input.continuation.compactCheckpoint) ? {
35247
35303
  ...input.continuation.previousSession ? {
35248
35304
  previousSession: {
35249
35305
  ...input.continuation.previousSession,
@@ -35254,7 +35310,13 @@ async function buildTaskWorkset(input) {
35254
35310
  ...memory,
35255
35311
  title: short(memory.title, 20),
35256
35312
  ...memory.detail ? { detail: short(memory.detail, CONTINUATION_DETAIL_TOKEN_BUDGET) } : {}
35257
- }))
35313
+ })),
35314
+ ...input.continuation.compactCheckpoint ? {
35315
+ compactCheckpoint: {
35316
+ ...input.continuation.compactCheckpoint,
35317
+ summary: short(input.continuation.compactCheckpoint.summary, 44)
35318
+ }
35319
+ } : {}
35258
35320
  } : void 0;
35259
35321
  const base = {
35260
35322
  version: "1.2",
@@ -37477,6 +37539,311 @@ var init_session = __esm({
37477
37539
  }
37478
37540
  });
37479
37541
 
37542
+ // src/store/compaction-checkpoint-store.ts
37543
+ var compaction_checkpoint_store_exports = {};
37544
+ __export(compaction_checkpoint_store_exports, {
37545
+ CompactionCheckpointStore: () => CompactionCheckpointStore
37546
+ });
37547
+ import { createHash as createHash10, randomUUID as randomUUID9 } from "crypto";
37548
+ function optionalText5(value) {
37549
+ return typeof value === "string" && value ? value : void 0;
37550
+ }
37551
+ function optionalNumber2(value) {
37552
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
37553
+ }
37554
+ function parseDetails(value) {
37555
+ if (typeof value !== "string" || !value) return void 0;
37556
+ try {
37557
+ const parsed = JSON.parse(value);
37558
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
37559
+ } catch {
37560
+ return void 0;
37561
+ }
37562
+ }
37563
+ function sanitizeSummary(value) {
37564
+ if (!value?.trim()) return void 0;
37565
+ return sanitizeCredentials(value).slice(0, MAX_SUMMARY_CHARS).trim() || void 0;
37566
+ }
37567
+ function sanitizeDetails(value) {
37568
+ if (!value) return void 0;
37569
+ try {
37570
+ const sanitized = sanitizeCredentials(JSON.stringify(value));
37571
+ if (sanitized.length > MAX_DETAILS_CHARS) return { truncated: true };
37572
+ const parsed = JSON.parse(sanitized);
37573
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
37574
+ } catch {
37575
+ return { unavailable: true };
37576
+ }
37577
+ }
37578
+ function sourceKeyFor(input, summary) {
37579
+ if (input.sourceKey?.trim()) return input.sourceKey.trim().slice(0, 300);
37580
+ const material = [
37581
+ input.projectId,
37582
+ input.sessionId,
37583
+ input.agent,
37584
+ input.sourceEvent,
37585
+ summary ?? "",
37586
+ input.tokensBefore ?? "",
37587
+ input.firstKeptEntryId ?? ""
37588
+ ].join("\0");
37589
+ return `derived:${createHash10("sha256").update(material).digest("hex").slice(0, 32)}`;
37590
+ }
37591
+ function rowToCheckpoint(row) {
37592
+ return {
37593
+ id: row.id,
37594
+ projectId: row.project_id,
37595
+ sessionId: row.session_id,
37596
+ agent: row.agent,
37597
+ phase: row.phase,
37598
+ captureKind: row.capture_kind,
37599
+ reason: row.reason,
37600
+ sourceEvent: row.source_event,
37601
+ sourceKey: row.source_key,
37602
+ ...optionalText5(row.summary) ? { summary: row.summary } : {},
37603
+ ...optionalNumber2(row.tokens_before) !== void 0 ? { tokensBefore: Number(row.tokens_before) } : {},
37604
+ ...optionalText5(row.first_kept_entry_id) ? { firstKeptEntryId: row.first_kept_entry_id } : {},
37605
+ ...parseDetails(row.details_json) ? { details: parseDetails(row.details_json) } : {},
37606
+ transcriptAvailable: Boolean(row.transcript_available),
37607
+ status: row.status,
37608
+ preCapturedAt: row.pre_captured_at,
37609
+ ...optionalText5(row.completed_at) ? { completedAt: row.completed_at } : {},
37610
+ ...optionalText5(row.delivered_at) ? { deliveredAt: row.delivered_at } : {},
37611
+ deliveryCount: Number(row.delivery_count ?? 0),
37612
+ createdAt: row.created_at,
37613
+ updatedAt: row.updated_at
37614
+ };
37615
+ }
37616
+ function safeReason(value) {
37617
+ return value === "manual" || value === "auto" ? value : "unknown";
37618
+ }
37619
+ function safeLimit(value, fallback) {
37620
+ if (!Number.isFinite(value) || value == null) return fallback;
37621
+ return Math.max(1, Math.min(500, Math.floor(value)));
37622
+ }
37623
+ var MAX_SUMMARY_CHARS, MAX_DETAILS_CHARS, CompactionCheckpointStore;
37624
+ var init_compaction_checkpoint_store = __esm({
37625
+ "src/store/compaction-checkpoint-store.ts"() {
37626
+ "use strict";
37627
+ init_esm_shims();
37628
+ init_secret_filter();
37629
+ init_sqlite_db();
37630
+ MAX_SUMMARY_CHARS = 24e3;
37631
+ MAX_DETAILS_CHARS = 4e3;
37632
+ CompactionCheckpointStore = class {
37633
+ db;
37634
+ constructor(dataDir) {
37635
+ this.db = getDatabase(dataDir);
37636
+ }
37637
+ recordPreflight(input) {
37638
+ const pending = this.db.prepare(`
37639
+ SELECT * FROM compaction_checkpoints
37640
+ WHERE project_id = ? AND session_id = ? AND agent = ?
37641
+ AND phase = 'pre' AND status = 'active'
37642
+ ORDER BY pre_captured_at DESC, created_at DESC
37643
+ LIMIT 1
37644
+ `).get(input.projectId, input.sessionId, input.agent);
37645
+ if (pending) return rowToCheckpoint(pending);
37646
+ const now3 = input.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString();
37647
+ const checkpoint = {
37648
+ id: randomUUID9(),
37649
+ projectId: input.projectId,
37650
+ sessionId: input.sessionId,
37651
+ agent: input.agent,
37652
+ phase: "pre",
37653
+ captureKind: "preflight",
37654
+ reason: safeReason(input.reason),
37655
+ sourceEvent: input.sourceEvent,
37656
+ sourceKey: `pre:${randomUUID9()}`,
37657
+ transcriptAvailable: Boolean(input.transcriptAvailable),
37658
+ status: "active",
37659
+ preCapturedAt: now3,
37660
+ deliveryCount: 0,
37661
+ createdAt: now3,
37662
+ updatedAt: now3
37663
+ };
37664
+ this.insert(checkpoint);
37665
+ return checkpoint;
37666
+ }
37667
+ complete(input) {
37668
+ const completedAt = input.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
37669
+ const summary = sanitizeSummary(input.summary);
37670
+ const pending = this.db.prepare(`
37671
+ SELECT * FROM compaction_checkpoints
37672
+ WHERE project_id = ? AND session_id = ? AND agent = ?
37673
+ AND phase = 'pre' AND status = 'active'
37674
+ ORDER BY pre_captured_at DESC, created_at DESC
37675
+ LIMIT 1
37676
+ `).get(input.projectId, input.sessionId, input.agent);
37677
+ if (!input.sourceKey?.trim() && !summary && input.tokensBefore === void 0 && !input.firstKeptEntryId && !pending) {
37678
+ const priorLifecycle = this.db.prepare(`
37679
+ SELECT * FROM compaction_checkpoints
37680
+ WHERE project_id = ? AND session_id = ? AND agent = ?
37681
+ AND phase = 'complete' AND capture_kind = 'lifecycle'
37682
+ AND source_event = ? AND status = 'active'
37683
+ ORDER BY completed_at DESC, created_at DESC
37684
+ LIMIT 1
37685
+ `).get(input.projectId, input.sessionId, input.agent, input.sourceEvent);
37686
+ if (priorLifecycle) return rowToCheckpoint(priorLifecycle);
37687
+ }
37688
+ const sourceKey = input.sourceKey?.trim() ? input.sourceKey.trim().slice(0, 300) : pending ? `lifecycle:${pending.id}` : sourceKeyFor(input, summary);
37689
+ const existing = this.db.prepare(`
37690
+ SELECT * FROM compaction_checkpoints
37691
+ WHERE project_id = ? AND source_key = ?
37692
+ LIMIT 1
37693
+ `).get(input.projectId, sourceKey);
37694
+ if (existing) return rowToCheckpoint(existing);
37695
+ const captureKind = summary ? "native-summary" : "lifecycle";
37696
+ const details = sanitizeDetails(input.details);
37697
+ if (pending) {
37698
+ const checkpoint2 = rowToCheckpoint(pending);
37699
+ const nextReason = safeReason(input.reason) === "unknown" ? checkpoint2.reason : safeReason(input.reason);
37700
+ this.db.prepare(`
37701
+ UPDATE compaction_checkpoints
37702
+ SET phase = 'complete', capture_kind = ?, reason = ?, source_event = ?, source_key = ?,
37703
+ summary = ?, tokens_before = ?, first_kept_entry_id = ?, details_json = ?,
37704
+ completed_at = ?, updated_at = ?
37705
+ WHERE id = ?
37706
+ `).run(
37707
+ captureKind,
37708
+ nextReason,
37709
+ input.sourceEvent,
37710
+ sourceKey,
37711
+ summary ?? null,
37712
+ input.tokensBefore ?? null,
37713
+ input.firstKeptEntryId ?? null,
37714
+ JSON.stringify(details ?? {}),
37715
+ completedAt,
37716
+ completedAt,
37717
+ checkpoint2.id
37718
+ );
37719
+ return this.get(checkpoint2.id);
37720
+ }
37721
+ const checkpoint = {
37722
+ id: randomUUID9(),
37723
+ projectId: input.projectId,
37724
+ sessionId: input.sessionId,
37725
+ agent: input.agent,
37726
+ phase: "complete",
37727
+ captureKind,
37728
+ reason: safeReason(input.reason),
37729
+ sourceEvent: input.sourceEvent,
37730
+ sourceKey,
37731
+ ...summary ? { summary } : {},
37732
+ ...input.tokensBefore !== void 0 ? { tokensBefore: input.tokensBefore } : {},
37733
+ ...input.firstKeptEntryId ? { firstKeptEntryId: input.firstKeptEntryId } : {},
37734
+ ...details ? { details } : {},
37735
+ transcriptAvailable: false,
37736
+ status: "active",
37737
+ preCapturedAt: completedAt,
37738
+ completedAt,
37739
+ deliveryCount: 0,
37740
+ createdAt: completedAt,
37741
+ updatedAt: completedAt
37742
+ };
37743
+ this.insert(checkpoint);
37744
+ return checkpoint;
37745
+ }
37746
+ get(id) {
37747
+ const row = this.db.prepare("SELECT * FROM compaction_checkpoints WHERE id = ?").get(id);
37748
+ return row ? rowToCheckpoint(row) : void 0;
37749
+ }
37750
+ list(options2) {
37751
+ const conditions = ["project_id = ?"];
37752
+ const values = [options2.projectId];
37753
+ if (options2.sessionId) {
37754
+ conditions.push("session_id = ?");
37755
+ values.push(options2.sessionId);
37756
+ }
37757
+ if (options2.agent) {
37758
+ conditions.push("agent = ?");
37759
+ values.push(options2.agent);
37760
+ }
37761
+ if (!options2.includeArchived) {
37762
+ conditions.push("status = 'active'");
37763
+ }
37764
+ const rows = this.db.prepare(`
37765
+ SELECT * FROM compaction_checkpoints
37766
+ WHERE ${conditions.join(" AND ")}
37767
+ ORDER BY COALESCE(completed_at, pre_captured_at) DESC, created_at DESC
37768
+ LIMIT ?
37769
+ `).all(...values, safeLimit(options2.limit, 20));
37770
+ return rows.map(rowToCheckpoint);
37771
+ }
37772
+ findUndelivered(projectId, sessionId, agent) {
37773
+ const row = this.db.prepare(`
37774
+ SELECT * FROM compaction_checkpoints
37775
+ WHERE project_id = ? AND session_id = ? AND agent = ?
37776
+ AND phase = 'complete' AND status = 'active' AND delivered_at IS NULL
37777
+ ORDER BY completed_at DESC, created_at DESC
37778
+ LIMIT 1
37779
+ `).get(projectId, sessionId, agent);
37780
+ return row ? rowToCheckpoint(row) : void 0;
37781
+ }
37782
+ findLatestCompleted(projectId, options2 = {}) {
37783
+ const excludeSession = options2.excludeSession;
37784
+ const exclusion = excludeSession ? " AND NOT (session_id = ? AND agent = ? )" : "";
37785
+ const values = excludeSession ? [projectId, excludeSession.sessionId, excludeSession.agent] : [projectId];
37786
+ const row = this.db.prepare(`
37787
+ SELECT * FROM compaction_checkpoints
37788
+ WHERE project_id = ? AND phase = 'complete' AND status = 'active'
37789
+ ${exclusion}
37790
+ ORDER BY completed_at DESC, created_at DESC
37791
+ LIMIT 1
37792
+ `).get(...values);
37793
+ return row ? rowToCheckpoint(row) : void 0;
37794
+ }
37795
+ archive(id, archivedAt = (/* @__PURE__ */ new Date()).toISOString()) {
37796
+ const result = this.db.prepare(`
37797
+ UPDATE compaction_checkpoints
37798
+ SET status = 'archived', updated_at = ?
37799
+ WHERE id = ? AND status = 'active'
37800
+ `).run(archivedAt, id);
37801
+ return Number(result.changes ?? 0) > 0 ? this.get(id) : void 0;
37802
+ }
37803
+ markDelivered(id, deliveredAt = (/* @__PURE__ */ new Date()).toISOString()) {
37804
+ const result = this.db.prepare(`
37805
+ UPDATE compaction_checkpoints
37806
+ SET delivered_at = ?, delivery_count = delivery_count + 1, updated_at = ?
37807
+ WHERE id = ? AND phase = 'complete' AND status = 'active' AND delivered_at IS NULL
37808
+ `).run(deliveredAt, deliveredAt, id);
37809
+ return Number(result.changes ?? 0) > 0 ? this.get(id) : void 0;
37810
+ }
37811
+ insert(checkpoint) {
37812
+ this.db.prepare(`
37813
+ INSERT INTO compaction_checkpoints (
37814
+ id, project_id, session_id, agent, phase, capture_kind, reason,
37815
+ source_event, source_key, summary, tokens_before, first_kept_entry_id,
37816
+ details_json, transcript_available, status, pre_captured_at, completed_at,
37817
+ delivered_at, delivery_count, created_at, updated_at
37818
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
37819
+ `).run(
37820
+ checkpoint.id,
37821
+ checkpoint.projectId,
37822
+ checkpoint.sessionId,
37823
+ checkpoint.agent,
37824
+ checkpoint.phase,
37825
+ checkpoint.captureKind,
37826
+ checkpoint.reason,
37827
+ checkpoint.sourceEvent,
37828
+ checkpoint.sourceKey,
37829
+ checkpoint.summary ?? null,
37830
+ checkpoint.tokensBefore ?? null,
37831
+ checkpoint.firstKeptEntryId ?? null,
37832
+ JSON.stringify(checkpoint.details ?? {}),
37833
+ checkpoint.transcriptAvailable ? 1 : 0,
37834
+ checkpoint.status,
37835
+ checkpoint.preCapturedAt,
37836
+ checkpoint.completedAt ?? null,
37837
+ checkpoint.deliveredAt ?? null,
37838
+ checkpoint.deliveryCount,
37839
+ checkpoint.createdAt,
37840
+ checkpoint.updatedAt
37841
+ );
37842
+ }
37843
+ };
37844
+ }
37845
+ });
37846
+
37480
37847
  // src/codegraph/auto-context.ts
37481
37848
  var auto_context_exports = {};
37482
37849
  __export(auto_context_exports, {
@@ -37662,6 +38029,22 @@ async function buildAutoProjectContext(input) {
37662
38029
  if (continuationRequested) {
37663
38030
  await initSessionStore(input.dataDir);
37664
38031
  continuation = await getSessionResumeBrief(input.project.id, task, input.reader);
38032
+ const { CompactionCheckpointStore: CompactionCheckpointStore2 } = await Promise.resolve().then(() => (init_compaction_checkpoint_store(), compaction_checkpoint_store_exports));
38033
+ const checkpoint = new CompactionCheckpointStore2(input.dataDir).findLatestCompleted(
38034
+ input.project.id,
38035
+ input.excludeCompactionCheckpointFor ? { excludeSession: input.excludeCompactionCheckpointFor } : void 0
38036
+ );
38037
+ const completedAtMs = checkpoint ? Date.parse(checkpoint.completedAt ?? checkpoint.preCapturedAt) : Number.NaN;
38038
+ if (checkpoint && Number.isFinite(completedAtMs) && completedAtMs <= now3.getTime() && now3.getTime() - completedAtMs <= COMPACT_CHECKPOINT_MAX_AGE_MS) {
38039
+ continuation.compactCheckpoint = {
38040
+ id: checkpoint.id,
38041
+ agent: checkpoint.agent,
38042
+ captureKind: checkpoint.captureKind === "native-summary" ? "native-summary" : "lifecycle",
38043
+ reason: checkpoint.reason,
38044
+ ...checkpoint.completedAt ? { completedAt: checkpoint.completedAt } : {},
38045
+ 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."
38046
+ };
38047
+ }
37665
38048
  }
37666
38049
  const workset = await buildTaskWorkset({
37667
38050
  projectId: input.project.id,
@@ -37870,7 +38253,7 @@ function formatAutoProjectContextSummary(context) {
37870
38253
  reliableSources.length > 0 ? `- ${reliableSources.length} current code-bound memory link(s)` : "- none yet"
37871
38254
  );
37872
38255
  const continuation = context.workset.continuation;
37873
- if (continuation?.previousSession || continuation?.memories.length) {
38256
+ if (continuation?.previousSession || continuation?.memories.length || continuation?.compactCheckpoint) {
37874
38257
  lines.push("", "Resume from prior work");
37875
38258
  if (continuation.previousSession) {
37876
38259
  const session = continuation.previousSession;
@@ -37885,6 +38268,12 @@ function formatAutoProjectContextSummary(context) {
37885
38268
  "- #" + memory.id + " " + memory.type + ": " + compactContinuationText(memory.title, 18) + detail
37886
38269
  );
37887
38270
  }
38271
+ if (continuation.compactCheckpoint) {
38272
+ const checkpoint = continuation.compactCheckpoint;
38273
+ lines.push(
38274
+ "- Recent host compact checkpoint (" + [checkpoint.agent, checkpoint.captureKind, checkpoint.reason].join(", ") + "): " + compactContinuationText(checkpoint.summary, 36)
38275
+ );
38276
+ }
37888
38277
  }
37889
38278
  return lines.join("\n");
37890
38279
  }
@@ -37951,7 +38340,7 @@ function formatLegacyAutoProjectContextPrompt(context) {
37951
38340
  );
37952
38341
  return lines.join("\n");
37953
38342
  }
37954
- var DEFAULT_MAX_AGE_MS;
38343
+ var DEFAULT_MAX_AGE_MS, COMPACT_CHECKPOINT_MAX_AGE_MS;
37955
38344
  var init_auto_context = __esm({
37956
38345
  "src/codegraph/auto-context.ts"() {
37957
38346
  "use strict";
@@ -37971,6 +38360,7 @@ var init_auto_context = __esm({
37971
38360
  init_session_store();
37972
38361
  init_task_lens();
37973
38362
  DEFAULT_MAX_AGE_MS = 10 * 60 * 1e3;
38363
+ COMPACT_CHECKPOINT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
37974
38364
  }
37975
38365
  });
37976
38366
 
@@ -38235,7 +38625,7 @@ function addDefaultVerificationHints(input) {
38235
38625
  return uniq2(hints);
38236
38626
  }
38237
38627
  function selectRelevantObservations(observations2, task, limit) {
38238
- const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 20;
38628
+ const safeLimit2 = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 20;
38239
38629
  const taskTokens2 = tokenize4(task);
38240
38630
  const ranked = observations2.map((observation, index) => ({
38241
38631
  observation,
@@ -38245,7 +38635,7 @@ function selectRelevantObservations(observations2, task, limit) {
38245
38635
  }));
38246
38636
  const positives = ranked.filter((item) => item.score > 0);
38247
38637
  const pool = positives.length > 0 ? positives : ranked;
38248
- return pool.sort((a3, b3) => b3.score - a3.score || b3.time - a3.time || b3.index - a3.index).slice(0, safeLimit).map((item) => item.observation);
38638
+ return pool.sort((a3, b3) => b3.score - a3.score || b3.time - a3.time || b3.index - a3.index).slice(0, safeLimit2).map((item) => item.observation);
38249
38639
  }
38250
38640
  function assembleContextPackForTask(input) {
38251
38641
  const selected = selectRelevantObservations(input.observations, input.task, input.limit ?? 20);
@@ -38642,12 +39032,277 @@ ${formatUsageHint()}`;
38642
39032
  }
38643
39033
  });
38644
39034
 
39035
+ // src/memory/compaction.ts
39036
+ var compaction_exports = {};
39037
+ __export(compaction_exports, {
39038
+ buildCompactionWorkset: () => buildCompactionWorkset,
39039
+ captureCompactionCheckpoint: () => captureCompactionCheckpoint,
39040
+ consumeCompactionWorkset: () => consumeCompactionWorkset
39041
+ });
39042
+ function normalizeBudget(value) {
39043
+ if (!Number.isFinite(value) || value == null) return DEFAULT_WORKSET_BUDGET;
39044
+ return Math.max(MIN_WORKSET_BUDGET, Math.min(2e3, Math.floor(value)));
39045
+ }
39046
+ function sourceLabel(checkpoint) {
39047
+ if (checkpoint.captureKind === "native-summary") return "native host summary";
39048
+ if (checkpoint.captureKind === "preflight") return "pre-compact lifecycle marker";
39049
+ return "host lifecycle marker";
39050
+ }
39051
+ function buildCompactionWorkset(checkpoint, options2 = {}) {
39052
+ const budget = normalizeBudget(options2.maxTokens);
39053
+ const task = options2.task?.trim() ? truncateToTokenBudget(sanitizeCredentials(options2.task.trim()), Math.max(8, Math.floor(budget * 0.25))) : "";
39054
+ const headerLines = [
39055
+ "## Compact Continuation",
39056
+ `- Host: ${checkpoint.agent} (${sourceLabel(checkpoint)})`,
39057
+ `- Trigger: ${checkpoint.reason}`,
39058
+ task ? `- Task now: ${task}` : "",
39059
+ "- Current code remains authoritative."
39060
+ ].filter(Boolean);
39061
+ const header = `${headerLines.join("\n")}
39062
+
39063
+ `;
39064
+ 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.";
39065
+ const source = sanitizeCredentials(checkpoint.summary?.trim() || fallback);
39066
+ const available = Math.max(0, budget - countTextTokens(header) - 4);
39067
+ let excerpt = source ? truncateToTokenBudget(source, available) : "";
39068
+ let text = `${header}${excerpt ? `### Host checkpoint
39069
+ ${excerpt}` : ""}`.trim();
39070
+ while (excerpt && countTextTokens(text) > budget) {
39071
+ const nextExcerptBudget = Math.max(1, Math.floor(countTextTokens(excerpt) * 0.8));
39072
+ excerpt = truncateToTokenBudget(excerpt, nextExcerptBudget);
39073
+ text = `${header}${excerpt ? `### Host checkpoint
39074
+ ${excerpt}` : ""}`.trim();
39075
+ }
39076
+ if (countTextTokens(text) > budget) text = truncateToTokenBudget(text, budget);
39077
+ return {
39078
+ checkpointId: checkpoint.id,
39079
+ text,
39080
+ tokens: countTextTokens(text)
39081
+ };
39082
+ }
39083
+ function sourceEvent(input) {
39084
+ const raw = input.raw;
39085
+ const event = raw.hook_event_name ?? raw.event ?? raw.type;
39086
+ return typeof event === "string" && event ? event : input.event;
39087
+ }
39088
+ async function captureCompactionCheckpoint(input) {
39089
+ const isCompactResume = input.event === "session_start" && input.sessionStartReason?.trim().toLowerCase() === "compact";
39090
+ if (input.event !== "pre_compact" && input.event !== "post_compact" && !isCompactResume) return null;
39091
+ if (!input.sessionId || !input.cwd) return null;
39092
+ const [
39093
+ { detectProject: detectProject2 },
39094
+ { getProjectDataDir: getProjectDataDir2 },
39095
+ { initAliasRegistry: initAliasRegistry2, registerAlias: registerAlias2 }
39096
+ ] = await Promise.all([
39097
+ Promise.resolve().then(() => (init_detector(), detector_exports)),
39098
+ Promise.resolve().then(() => (init_persistence(), persistence_exports)),
39099
+ Promise.resolve().then(() => (init_aliases(), aliases_exports))
39100
+ ]);
39101
+ const project = detectProject2(input.cwd);
39102
+ if (!project) return null;
39103
+ const dataDir = await getProjectDataDir2(project.id);
39104
+ initAliasRegistry2(dataDir);
39105
+ const projectId = await registerAlias2(project);
39106
+ const store2 = new CompactionCheckpointStore(dataDir);
39107
+ if (input.event === "pre_compact") {
39108
+ return store2.recordPreflight({
39109
+ projectId,
39110
+ sessionId: input.sessionId,
39111
+ agent: input.agent,
39112
+ reason: input.compaction?.reason,
39113
+ sourceEvent: sourceEvent(input),
39114
+ transcriptAvailable: Boolean(input.transcriptPath),
39115
+ capturedAt: input.timestamp
39116
+ });
39117
+ }
39118
+ return store2.complete({
39119
+ projectId,
39120
+ sessionId: input.sessionId,
39121
+ agent: input.agent,
39122
+ reason: input.compaction?.reason,
39123
+ sourceEvent: sourceEvent(input),
39124
+ sourceKey: input.compaction?.sourceKey,
39125
+ summary: input.compaction?.summary,
39126
+ tokensBefore: input.compaction?.tokensBefore,
39127
+ firstKeptEntryId: input.compaction?.firstKeptEntryId,
39128
+ details: input.compaction?.details,
39129
+ completedAt: input.timestamp
39130
+ });
39131
+ }
39132
+ async function consumeCompactionWorkset(input, options2 = {}) {
39133
+ if (!input.sessionId || !input.cwd) return null;
39134
+ const [
39135
+ { detectProject: detectProject2 },
39136
+ { getProjectDataDir: getProjectDataDir2 },
39137
+ { initAliasRegistry: initAliasRegistry2, registerAlias: registerAlias2 }
39138
+ ] = await Promise.all([
39139
+ Promise.resolve().then(() => (init_detector(), detector_exports)),
39140
+ Promise.resolve().then(() => (init_persistence(), persistence_exports)),
39141
+ Promise.resolve().then(() => (init_aliases(), aliases_exports))
39142
+ ]);
39143
+ const project = detectProject2(input.cwd);
39144
+ if (!project) return null;
39145
+ const dataDir = await getProjectDataDir2(project.id);
39146
+ initAliasRegistry2(dataDir);
39147
+ const projectId = await registerAlias2(project);
39148
+ const store2 = new CompactionCheckpointStore(dataDir);
39149
+ const checkpoint = store2.findUndelivered(projectId, input.sessionId, input.agent);
39150
+ if (!checkpoint) return null;
39151
+ const workset = buildCompactionWorkset(checkpoint, options2);
39152
+ if (!workset.text) return null;
39153
+ store2.markDelivered(checkpoint.id);
39154
+ return workset;
39155
+ }
39156
+ var DEFAULT_WORKSET_BUDGET, MIN_WORKSET_BUDGET;
39157
+ var init_compaction = __esm({
39158
+ "src/memory/compaction.ts"() {
39159
+ "use strict";
39160
+ init_esm_shims();
39161
+ init_token_budget();
39162
+ init_secret_filter();
39163
+ init_compaction_checkpoint_store();
39164
+ DEFAULT_WORKSET_BUDGET = 420;
39165
+ MIN_WORKSET_BUDGET = 48;
39166
+ }
39167
+ });
39168
+
39169
+ // src/cli/commands/checkpoint.ts
39170
+ var checkpoint_exports = {};
39171
+ __export(checkpoint_exports, {
39172
+ default: () => checkpoint_default
39173
+ });
39174
+ async function getCheckpointContext() {
39175
+ const context = await getCliProjectContext();
39176
+ const { initAliasRegistry: initAliasRegistry2, registerAlias: registerAlias2 } = await Promise.resolve().then(() => (init_aliases(), aliases_exports));
39177
+ initAliasRegistry2(context.dataDir);
39178
+ const canonicalId = await registerAlias2(context.project);
39179
+ return {
39180
+ ...context,
39181
+ project: { ...context.project, id: canonicalId },
39182
+ store: new CompactionCheckpointStore(context.dataDir)
39183
+ };
39184
+ }
39185
+ function assertProjectCheckpoint(checkpoint, projectId, id) {
39186
+ if (!checkpoint || checkpoint.projectId !== projectId) {
39187
+ throw new Error(`No compact checkpoint "${id}" exists for this project.`);
39188
+ }
39189
+ return checkpoint;
39190
+ }
39191
+ function formatCheckpoint(checkpoint) {
39192
+ const completion = checkpoint.completedAt ? `, completed ${checkpoint.completedAt}` : "";
39193
+ const delivery = checkpoint.deliveredAt ? `, delivered ${checkpoint.deliveryCount}x` : "";
39194
+ return [
39195
+ `${shortId(checkpoint.id)} ${checkpoint.phase} ${checkpoint.agent} (${checkpoint.captureKind}, ${checkpoint.reason})`,
39196
+ ` session: ${checkpoint.sessionId}`,
39197
+ ` source: ${checkpoint.sourceEvent}${completion}${delivery}`,
39198
+ ` status: ${checkpoint.status}${checkpoint.transcriptAvailable ? ", transcript marker available" : ""}`,
39199
+ ...checkpoint.summary ? [` summary: ${checkpoint.summary.replace(/\s+/g, " ").slice(0, 220)}`] : []
39200
+ ].join("\n");
39201
+ }
39202
+ function usage() {
39203
+ console.log("Memorix Compact Checkpoints");
39204
+ console.log("");
39205
+ console.log("Usage:");
39206
+ console.log(" memorix checkpoint list [--session <id>] [--agent <name>] [--all]");
39207
+ console.log(" memorix checkpoint show --id <checkpoint-id>");
39208
+ console.log(' memorix checkpoint context [--id <checkpoint-id>] [--task "..."] [--budget 420]');
39209
+ console.log(" memorix checkpoint archive --id <checkpoint-id>");
39210
+ }
39211
+ var checkpoint_default;
39212
+ var init_checkpoint = __esm({
39213
+ "src/cli/commands/checkpoint.ts"() {
39214
+ "use strict";
39215
+ init_esm_shims();
39216
+ init_dist2();
39217
+ init_compaction();
39218
+ init_compaction_checkpoint_store();
39219
+ init_operator_shared();
39220
+ checkpoint_default = defineCommand({
39221
+ meta: {
39222
+ name: "checkpoint",
39223
+ description: "Inspect and manage native compact continuity checkpoints"
39224
+ },
39225
+ args: {
39226
+ id: { type: "string", description: "Checkpoint ID" },
39227
+ session: { type: "string", description: "Filter by host session ID" },
39228
+ agent: { type: "string", description: "Filter by host agent name" },
39229
+ task: { type: "string", description: "Current task for a bounded continuation workset" },
39230
+ budget: { type: "string", description: "Maximum workset token budget (default: 420)" },
39231
+ limit: { type: "string", description: "Maximum checkpoints to list (default: 20)" },
39232
+ all: { type: "boolean", description: "Include archived checkpoints in list output" },
39233
+ json: { type: "boolean", description: "Emit machine-readable JSON output" }
39234
+ },
39235
+ run: async ({ args }) => {
39236
+ const positional = args._ ?? [];
39237
+ const action = (positional[0] ?? "list").toLowerCase();
39238
+ const asJson = Boolean(args.json);
39239
+ try {
39240
+ const { project, store: store2 } = await getCheckpointContext();
39241
+ const id = args.id?.trim();
39242
+ switch (action) {
39243
+ case "list": {
39244
+ const checkpoints = store2.list({
39245
+ projectId: project.id,
39246
+ sessionId: args.session?.trim() || void 0,
39247
+ agent: args.agent?.trim() || void 0,
39248
+ includeArchived: Boolean(args.all),
39249
+ limit: parsePositiveInt(args.limit, 20)
39250
+ });
39251
+ emitResult(
39252
+ { project, checkpoints },
39253
+ checkpoints.length ? checkpoints.map(formatCheckpoint).join("\n\n") : "No compact checkpoints for this project.",
39254
+ asJson
39255
+ );
39256
+ return;
39257
+ }
39258
+ case "show": {
39259
+ if (!id) throw new Error('id is required for "memorix checkpoint show".');
39260
+ const checkpoint = assertProjectCheckpoint(store2.get(id), project.id, id);
39261
+ emitResult({ project, checkpoint }, formatCheckpoint(checkpoint), asJson);
39262
+ return;
39263
+ }
39264
+ case "context": {
39265
+ const checkpoint = id ? assertProjectCheckpoint(store2.get(id), project.id, id) : store2.list({
39266
+ projectId: project.id,
39267
+ sessionId: args.session?.trim() || void 0,
39268
+ agent: args.agent?.trim() || void 0,
39269
+ limit: parsePositiveInt(args.limit, 20)
39270
+ }).find((entry) => entry.phase === "complete");
39271
+ if (!checkpoint) {
39272
+ throw new Error("No completed compact checkpoint is available for this project and filter.");
39273
+ }
39274
+ const workset = buildCompactionWorkset(checkpoint, {
39275
+ task: args.task?.trim(),
39276
+ maxTokens: parsePositiveInt(args.budget, 420)
39277
+ });
39278
+ emitResult({ project, checkpoint, workset }, workset.text, asJson);
39279
+ return;
39280
+ }
39281
+ case "archive": {
39282
+ if (!id) throw new Error('id is required for "memorix checkpoint archive".');
39283
+ assertProjectCheckpoint(store2.get(id), project.id, id);
39284
+ const checkpoint = store2.archive(id);
39285
+ if (!checkpoint) throw new Error(`Compact checkpoint "${id}" is already archived.`);
39286
+ emitResult({ project, checkpoint }, `Archived compact checkpoint ${shortId(checkpoint.id)}.`, asJson);
39287
+ return;
39288
+ }
39289
+ default:
39290
+ usage();
39291
+ }
39292
+ } catch (error2) {
39293
+ emitError(error2 instanceof Error ? error2.message : String(error2), asJson);
39294
+ }
39295
+ }
39296
+ });
39297
+ }
39298
+ });
39299
+
38645
39300
  // src/knowledge/markdown.ts
38646
- import { createHash as createHash10 } from "crypto";
39301
+ import { createHash as createHash11 } from "crypto";
38647
39302
  import { promises as fs16 } from "fs";
38648
39303
  import path23 from "path";
38649
39304
  function hashContent(content) {
38650
- return createHash10("sha256").update(content).digest("hex");
39305
+ return createHash11("sha256").update(content).digest("hex");
38651
39306
  }
38652
39307
  function stringField(data, key) {
38653
39308
  const value = data[key];
@@ -38773,11 +39428,11 @@ __export(wiki_exports, {
38773
39428
  lintKnowledgeWorkspace: () => lintKnowledgeWorkspace,
38774
39429
  readKnowledgePage: () => readKnowledgePage
38775
39430
  });
38776
- import { createHash as createHash11 } from "crypto";
39431
+ import { createHash as createHash12 } from "crypto";
38777
39432
  import { promises as fs17 } from "fs";
38778
39433
  import path24 from "path";
38779
39434
  function hash3(value) {
38780
- return createHash11("sha256").update(value).digest("hex");
39435
+ return createHash12("sha256").update(value).digest("hex");
38781
39436
  }
38782
39437
  function now2() {
38783
39438
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -39218,7 +39873,7 @@ function modeFrom(value) {
39218
39873
  if (value === "versioned") return "versioned";
39219
39874
  throw new Error("knowledge mode must be local or versioned");
39220
39875
  }
39221
- function usage() {
39876
+ function usage2() {
39222
39877
  return [
39223
39878
  "Memorix Knowledge Commands",
39224
39879
  "",
@@ -39439,7 +40094,7 @@ var init_knowledge = __esm({
39439
40094
  );
39440
40095
  return;
39441
40096
  }
39442
- emitResult({ usage: usage() }, usage(), asJson);
40097
+ emitResult({ usage: usage2() }, usage2(), asJson);
39443
40098
  return;
39444
40099
  }
39445
40100
  switch (action) {
@@ -39498,7 +40153,7 @@ var init_knowledge = __esm({
39498
40153
  return;
39499
40154
  }
39500
40155
  default:
39501
- emitResult({ usage: usage() }, usage(), asJson);
40156
+ emitResult({ usage: usage2() }, usage2(), asJson);
39502
40157
  }
39503
40158
  } catch (error2) {
39504
40159
  emitError(error2 instanceof Error ? error2.message : String(error2), asJson);
@@ -46812,7 +47467,7 @@ __export(planner_exports, {
46812
47467
  materializeTaskGraph: () => materializeTaskGraph,
46813
47468
  seedAutonomousPipeline: () => seedAutonomousPipeline
46814
47469
  });
46815
- import { randomUUID as randomUUID9 } from "crypto";
47470
+ import { randomUUID as randomUUID10 } from "crypto";
46816
47471
  function isPlannerTask(metadata) {
46817
47472
  if (!metadata) return null;
46818
47473
  try {
@@ -46870,7 +47525,7 @@ function seedAutonomousPipeline(teamStore, projectId, config2, opts) {
46870
47525
  const maxIterations = config2.maxIterations ?? 3;
46871
47526
  const taskBudget = config2.taskBudget ?? 15;
46872
47527
  const structuredPlan = opts?.structuredPlan ?? true;
46873
- const pipelineId = randomUUID9();
47528
+ const pipelineId = randomUUID10();
46874
47529
  const meta = {
46875
47530
  plannerType: "plan",
46876
47531
  pipelineId,
@@ -47789,11 +48444,11 @@ __export(receipt_service_exports, {
47789
48444
  buildHandoffReceipt: () => buildHandoffReceipt,
47790
48445
  formatHandoffReceipt: () => formatHandoffReceipt
47791
48446
  });
47792
- import { createHash as createHash12 } from "crypto";
48447
+ import { createHash as createHash13 } from "crypto";
47793
48448
  import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
47794
48449
  import path26 from "path";
47795
48450
  function sha256(value) {
47796
- return `sha256:${createHash12("sha256").update(value).digest("hex")}`;
48451
+ return `sha256:${createHash13("sha256").update(value).digest("hex")}`;
47797
48452
  }
47798
48453
  function hashId(projectId, id) {
47799
48454
  return sha256(`${projectId}:obs:${id}`);
@@ -48039,6 +48694,7 @@ var init_tool_profile = __esm({
48039
48694
  memorix_rules_sync: ["full"],
48040
48695
  memorix_workspace_sync: ["full"],
48041
48696
  memorix_ingest_image: ["full"],
48697
+ memorix_compaction_checkpoint: ["full"],
48042
48698
  // ── MCP Official Memory Server compatibility (KG tools) ──────────
48043
48699
  // These are only useful to users specifically migrating from the
48044
48700
  // reference mcp-memory server. Hide them unless explicitly enabled.
@@ -66294,9 +66950,9 @@ var init_graph_scope = __esm({
66294
66950
  });
66295
66951
 
66296
66952
  // src/rules/utils.ts
66297
- import { createHash as createHash13 } from "crypto";
66953
+ import { createHash as createHash14 } from "crypto";
66298
66954
  function hashContent2(content) {
66299
- return createHash13("sha256").update(content.trim()).digest("hex").substring(0, 16);
66955
+ return createHash14("sha256").update(content.trim()).digest("hex").substring(0, 16);
66300
66956
  }
66301
66957
  function generateRuleId(source, filePath) {
66302
66958
  const sanitized = filePath.replace(/[\/\\]/g, "-").replace(/^\./, "");
@@ -70075,7 +70731,7 @@ __export(server_exports2, {
70075
70731
  createMemorixServer: () => createMemorixServer,
70076
70732
  shouldAwaitProjectRuntime: () => shouldAwaitProjectRuntime
70077
70733
  });
70078
- import { createHash as createHash14 } from "crypto";
70734
+ import { createHash as createHash15 } from "crypto";
70079
70735
  function formatFormationStageDurations(stageDurationsMs) {
70080
70736
  const orderedStages = ["extract", "resolve", "evaluate"];
70081
70737
  const parts = orderedStages.filter((stage) => stageDurationsMs[stage] !== void 0).map((stage) => `${stage}=${stageDurationsMs[stage]}ms`);
@@ -70157,7 +70813,7 @@ function coerceObjectArray(val) {
70157
70813
  return [];
70158
70814
  }
70159
70815
  function createDeterministicInstanceId(projectId, agentType, agentName) {
70160
- const digest2 = createHash14("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
70816
+ const digest2 = createHash15("sha256").update(projectId).update("\n").update(agentType).update("\n").update(agentName ?? "").digest("hex").slice(0, 24);
70161
70817
  return `auto-${digest2}`;
70162
70818
  }
70163
70819
  function shouldAwaitProjectRuntime(toolName) {
@@ -70430,7 +71086,7 @@ The path should point to a directory containing a .git folder.`
70430
71086
  };
70431
71087
  const server = existingServer ?? new McpServer({
70432
71088
  name: "memorix",
70433
- version: true ? "1.2.6" : "1.0.1"
71089
+ version: true ? "1.2.7" : "1.0.1"
70434
71090
  });
70435
71091
  const originalRegisterTool = server.registerTool.bind(server);
70436
71092
  server.registerTool = ((name, ...args) => {
@@ -71078,12 +71734,12 @@ Use this as the \`topicKey\` parameter in \`memorix_store\` to enable upsert beh
71078
71734
  if (boundary) return boundary;
71079
71735
  }
71080
71736
  return withFreshIndex(async () => {
71081
- const safeLimit = limit != null ? coerceNumber(limit, 20) : void 0;
71737
+ const safeLimit2 = limit != null ? coerceNumber(limit, 20) : void 0;
71082
71738
  const safeMaxTokens = maxTokens != null ? coerceNumber(maxTokens, 0) : void 0;
71083
71739
  const TIMEOUT_MS = 3e4;
71084
71740
  const searchPromise = compactSearch({
71085
71741
  query,
71086
- limit: safeLimit,
71742
+ limit: safeLimit2,
71087
71743
  type,
71088
71744
  maxTokens: safeMaxTokens,
71089
71745
  since,
@@ -71884,10 +72540,10 @@ Entity: ${entityName} | ${facts.length} facts | ${obs.tokens} tokens${reasoningA
71884
72540
  const unresolved = requireResolvedProject("search reasoning in the current project");
71885
72541
  if (unresolved) return unresolved;
71886
72542
  }
71887
- const safeLimit = limit != null ? coerceNumber(limit, 10) : 10;
72543
+ const safeLimit2 = limit != null ? coerceNumber(limit, 10) : 10;
71888
72544
  const result = await withFreshIndex(() => compactSearch({
71889
72545
  query,
71890
- limit: safeLimit,
72546
+ limit: safeLimit2,
71891
72547
  type: "reasoning",
71892
72548
  projectId: scope === "global" ? void 0 : project.id,
71893
72549
  status: "active",
@@ -73210,9 +73866,9 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
73210
73866
  }
73211
73867
  },
73212
73868
  async ({ limit }) => {
73213
- const safeLimit = limit != null ? coerceNumber(limit, 3) : 3;
73869
+ const safeLimit2 = limit != null ? coerceNumber(limit, 3) : 3;
73214
73870
  const { getSessionContext: getSessionContext2, listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session(), session_exports));
73215
- const context = await getSessionContext2(projectDir2, project.id, safeLimit, getObservationReader());
73871
+ const context = await getSessionContext2(projectDir2, project.id, safeLimit2, getObservationReader());
73216
73872
  const sessions = await listSessions2(projectDir2, project.id);
73217
73873
  const activeSessions = sessions.filter((s2) => s2.status === "active");
73218
73874
  const completedSessions = sessions.filter((s2) => s2.status === "completed");
@@ -73233,6 +73889,86 @@ ${summary ? "Summary saved for next session context injection." : "No summary pr
73233
73889
  };
73234
73890
  }
73235
73891
  );
73892
+ server.registerTool(
73893
+ "memorix_compaction_checkpoint",
73894
+ {
73895
+ title: "Compact Continuity Checkpoints",
73896
+ 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.",
73897
+ inputSchema: {
73898
+ action: external_exports.enum(["list", "show", "context", "archive"]).default("list"),
73899
+ id: external_exports.string().optional().describe("Checkpoint ID for show, context, or archive"),
73900
+ sessionId: external_exports.string().optional().describe("Optional host session ID filter"),
73901
+ agent: external_exports.string().optional().describe("Optional host agent filter"),
73902
+ task: external_exports.string().optional().describe("Current task for a bounded context preview"),
73903
+ maxTokens: external_exports.number().optional().describe("Maximum tokens for action=context (default: 420)"),
73904
+ limit: external_exports.number().optional().describe("Maximum records for action=list (default: 20)"),
73905
+ includeArchived: external_exports.boolean().optional().default(false).describe("Include archived records in action=list")
73906
+ }
73907
+ },
73908
+ async ({ action, id, sessionId, agent, task, maxTokens, limit, includeArchived }) => {
73909
+ const unresolved = requireResolvedProject("inspect compact continuity checkpoints");
73910
+ if (unresolved) return unresolved;
73911
+ const [{ CompactionCheckpointStore: CompactionCheckpointStore2 }, { buildCompactionWorkset: buildCompactionWorkset2 }] = await Promise.all([
73912
+ Promise.resolve().then(() => (init_compaction_checkpoint_store(), compaction_checkpoint_store_exports)),
73913
+ Promise.resolve().then(() => (init_compaction(), compaction_exports))
73914
+ ]);
73915
+ const store2 = new CompactionCheckpointStore2(projectDir2);
73916
+ const assertCheckpoint = (checkpointId) => {
73917
+ const checkpoint2 = store2.get(checkpointId);
73918
+ if (!checkpoint2 || checkpoint2.projectId !== project.id) return null;
73919
+ return checkpoint2;
73920
+ };
73921
+ if (action === "list") {
73922
+ const checkpoints = store2.list({
73923
+ projectId: project.id,
73924
+ sessionId: sessionId?.trim() || void 0,
73925
+ agent: agent?.trim() || void 0,
73926
+ includeArchived: Boolean(includeArchived),
73927
+ limit: limit != null ? Math.max(1, Math.min(100, coerceNumber(limit, 20))) : 20
73928
+ });
73929
+ return {
73930
+ content: [{ type: "text", text: JSON.stringify({ projectId: project.id, checkpoints }, null, 2) }]
73931
+ };
73932
+ }
73933
+ if (!id?.trim()) {
73934
+ return {
73935
+ content: [{ type: "text", text: "id is required for this checkpoint action." }],
73936
+ isError: true
73937
+ };
73938
+ }
73939
+ const checkpoint = assertCheckpoint(id.trim());
73940
+ if (!checkpoint) {
73941
+ return {
73942
+ content: [{ type: "text", text: `Checkpoint "${id.trim()}" was not found for the current project.` }],
73943
+ isError: true
73944
+ };
73945
+ }
73946
+ if (action === "show") {
73947
+ return {
73948
+ content: [{ type: "text", text: JSON.stringify({ projectId: project.id, checkpoint }, null, 2) }]
73949
+ };
73950
+ }
73951
+ if (action === "context") {
73952
+ const workset = buildCompactionWorkset2(checkpoint, {
73953
+ task: task?.trim(),
73954
+ maxTokens: maxTokens != null ? coerceNumber(maxTokens, 420) : 420
73955
+ });
73956
+ return {
73957
+ content: [{ type: "text", text: workset.text }]
73958
+ };
73959
+ }
73960
+ const archived = store2.archive(checkpoint.id);
73961
+ if (!archived) {
73962
+ return {
73963
+ content: [{ type: "text", text: `Checkpoint "${checkpoint.id}" is already archived.` }],
73964
+ isError: true
73965
+ };
73966
+ }
73967
+ return {
73968
+ content: [{ type: "text", text: `Archived compact checkpoint ${archived.id}.` }]
73969
+ };
73970
+ }
73971
+ );
73236
73972
  server.registerTool(
73237
73973
  "memorix_transfer",
73238
73974
  {
@@ -76147,7 +76883,7 @@ var init_serve_http = __esm({
76147
76883
  },
76148
76884
  run: async ({ args }) => {
76149
76885
  const { createServer: createServer2 } = await import("http");
76150
- const { randomUUID: randomUUID10 } = await import("crypto");
76886
+ const { randomUUID: randomUUID11 } = await import("crypto");
76151
76887
  const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp(), streamableHttp_exports));
76152
76888
  const { isInitializeRequest: isInitializeRequest2 } = await Promise.resolve().then(() => (init_types4(), types_exports));
76153
76889
  const { createMemorixServer: createMemorixServer2 } = await Promise.resolve().then(() => (init_server4(), server_exports2));
@@ -76331,7 +77067,7 @@ var init_serve_http = __esm({
76331
77067
  if (!sessionId && isInitializeRequest2(body)) {
76332
77068
  let createdState = null;
76333
77069
  const transport = new StreamableHTTPServerTransport2({
76334
- sessionIdGenerator: () => randomUUID10(),
77070
+ sessionIdGenerator: () => randomUUID11(),
76335
77071
  enableJsonResponse: true,
76336
77072
  onsessioninitialized: (sid) => {
76337
77073
  if (createdState) sessions.set(sid, createdState);
@@ -78121,6 +78857,44 @@ function stringifyValue(value) {
78121
78857
  return String(value);
78122
78858
  }
78123
78859
  }
78860
+ function firstNumber2(...values) {
78861
+ for (const value of values) {
78862
+ if (typeof value === "number" && Number.isFinite(value)) return value;
78863
+ if (typeof value === "string" && value.trim()) {
78864
+ const parsed = Number(value);
78865
+ if (Number.isFinite(parsed)) return parsed;
78866
+ }
78867
+ }
78868
+ return void 0;
78869
+ }
78870
+ function normalizeCompactionReason(value) {
78871
+ const normalized = value?.trim().toLowerCase();
78872
+ if (normalized === "manual" || normalized === "auto") return normalized;
78873
+ return "unknown";
78874
+ }
78875
+ function normalizeCompactionMetadata(payload, ...records) {
78876
+ const nestedSources = records.filter((record2) => Boolean(record2));
78877
+ const sources = [payload, ...nestedSources];
78878
+ const from = (field) => sources.map((source) => source[field]);
78879
+ const fromNested = (field) => nestedSources.map((source) => source[field]);
78880
+ const summary = firstString(...from("summary"), ...from("compaction_summary"));
78881
+ const sourceKey = firstString(...from("compaction_id"), ...from("entry_id"), ...fromNested("id"));
78882
+ const reasonValue = firstString(...from("trigger"), ...from("reason"), ...from("compaction_reason"));
78883
+ const tokensBefore = firstNumber2(...from("tokensBefore"), ...from("tokens_before"));
78884
+ const firstKeptEntryId = firstString(...from("firstKeptEntryId"), ...from("first_kept_entry_id"));
78885
+ const details = sources.map((source) => asRecord(source.details)).find((value) => Boolean(value));
78886
+ if (!summary && !sourceKey && !tokensBefore && !firstKeptEntryId && !details && !reasonValue) {
78887
+ return void 0;
78888
+ }
78889
+ return {
78890
+ ...reasonValue ? { reason: normalizeCompactionReason(reasonValue) } : {},
78891
+ ...sourceKey ? { sourceKey } : {},
78892
+ ...summary ? { summary } : {},
78893
+ ...tokensBefore !== void 0 ? { tokensBefore } : {},
78894
+ ...firstKeptEntryId ? { firstKeptEntryId } : {},
78895
+ ...details ? { details } : {}
78896
+ };
78897
+ }
78124
78898
  function detectAgent(payload) {
78125
78899
  if (typeof payload._memorix_agent === "string") {
78126
78900
  return payload._memorix_agent;
@@ -78206,6 +78980,10 @@ function normalizeClaude(payload, event) {
78206
78980
  if (assistantMessage) {
78207
78981
  result.aiResponse = assistantMessage;
78208
78982
  }
78983
+ if (event === "session_start") {
78984
+ const source = firstString(payload.source, payload.session_start_reason, payload.reason);
78985
+ if (source) result.sessionStartReason = source;
78986
+ }
78209
78987
  return result;
78210
78988
  }
78211
78989
  function normalizeWindsurf(payload, event) {
@@ -78344,6 +79122,10 @@ function normalizeGemini(payload, event) {
78344
79122
  if (event === "user_prompt") {
78345
79123
  result.userPrompt = payload.prompt ?? "";
78346
79124
  }
79125
+ if (event === "session_start") {
79126
+ const source = firstString(payload.source, payload.session_start_reason, payload.reason);
79127
+ if (source) result.sessionStartReason = source;
79128
+ }
78347
79129
  return result;
78348
79130
  }
78349
79131
  function normalizeOpenCode(payload, event) {
@@ -78380,7 +79162,8 @@ function normalizeOpenCode(payload, event) {
78380
79162
  function normalizePi(payload, event) {
78381
79163
  const result = {
78382
79164
  sessionId: payload.session_id ?? payload.sessionId ?? "",
78383
- cwd: payload.cwd ?? ""
79165
+ cwd: payload.cwd ?? "",
79166
+ transcriptPath: payload.transcript_path ?? payload.transcriptPath
78384
79167
  };
78385
79168
  const toolName = payload.tool_name ?? "";
78386
79169
  if (toolName) {
@@ -78402,6 +79185,10 @@ function normalizePi(payload, event) {
78402
79185
  if (event === "post_command") {
78403
79186
  result.command = payload.command ?? "";
78404
79187
  }
79188
+ if (event === "session_start") {
79189
+ const source = firstString(payload.source, payload.reason);
79190
+ if (source) result.sessionStartReason = source;
79191
+ }
78405
79192
  if (result.toolInput && typeof result.toolInput === "object") {
78406
79193
  const filePath = result.toolInput.file_path ?? result.toolInput.filePath ?? result.toolInput.path;
78407
79194
  if (filePath) result.filePath = filePath;
@@ -78454,6 +79241,10 @@ function normalizeBridgePayload(payload, event) {
78454
79241
  openclawContext?.message
78455
79242
  ) ?? stringifyValue(openclawEvent?.message) ?? "";
78456
79243
  }
79244
+ if (event === "session_start") {
79245
+ const source = firstString(payload.source, bridgePayload?.source, bridgeKwargs?.source, openclawEvent?.reason);
79246
+ if (source) result.sessionStartReason = source;
79247
+ }
78457
79248
  return result;
78458
79249
  }
78459
79250
  function normalizeHookInput(payload) {
@@ -78499,6 +79290,15 @@ function normalizeHookInput(payload) {
78499
79290
  default:
78500
79291
  agentSpecific = { sessionId: "", cwd: "" };
78501
79292
  }
79293
+ const openclawEvent = asRecord(payload.openclaw_event);
79294
+ const genericCompaction = event === "pre_compact" || event === "post_compact" ? normalizeCompactionMetadata(
79295
+ payload,
79296
+ asRecord(payload.compaction),
79297
+ asRecord(payload.compaction_entry),
79298
+ asRecord(payload.compactionEntry),
79299
+ asRecord(openclawEvent?.compaction),
79300
+ asRecord(openclawEvent?.compaction_entry)
79301
+ ) : void 0;
78502
79302
  return {
78503
79303
  event,
78504
79304
  agent,
@@ -78506,7 +79306,8 @@ function normalizeHookInput(payload) {
78506
79306
  sessionId: agentSpecific.sessionId ?? "",
78507
79307
  cwd: agentSpecific.cwd ?? "",
78508
79308
  raw: payload,
78509
- ...agentSpecific
79309
+ ...agentSpecific,
79310
+ ...genericCompaction ? { compaction: genericCompaction } : {}
78510
79311
  };
78511
79312
  }
78512
79313
  var EVENT_MAP;
@@ -78760,10 +79561,10 @@ __export(handler_exports, {
78760
79561
  resetCooldowns: () => resetCooldowns,
78761
79562
  runHook: () => runHook
78762
79563
  });
78763
- import { createHash as createHash15 } from "crypto";
79564
+ import { createHash as createHash16 } from "crypto";
78764
79565
  function deriveHookActorId(input) {
78765
79566
  const material = `${input.agent ?? "unknown"}\0${input.sessionId ?? "unknown"}`;
78766
- return `hook:${createHash15("sha256").update(material).digest("hex").slice(0, 24)}`;
79567
+ return `hook:${createHash16("sha256").update(material).digest("hex").slice(0, 24)}`;
78767
79568
  }
78768
79569
  function classifyTool(input) {
78769
79570
  if (input.event === "post_edit") return "file_modify";
@@ -78944,6 +79745,15 @@ async function buildHookProjectContext(input, task, target) {
78944
79745
  refresh: "auto",
78945
79746
  reader: { projectId: canonicalId },
78946
79747
  continuation: "always",
79748
+ ...input.sessionId ? {
79749
+ // A native compact recovery is delivered once per host session.
79750
+ // Do not echo that same checkpoint back through the generic
79751
+ // continuation brief on later events from this session.
79752
+ excludeCompactionCheckpointFor: {
79753
+ sessionId: input.sessionId,
79754
+ agent: input.agent
79755
+ }
79756
+ } : {},
78947
79757
  enqueueRefresh: () => Promise.resolve().then(() => (init_lifecycle(), lifecycle_exports)).then(({ enqueueCodegraphRefresh: enqueueCodegraphRefresh2 }) => {
78948
79758
  enqueueCodegraphRefresh2({
78949
79759
  dataDir,
@@ -78965,27 +79775,80 @@ async function buildHookProjectContext(input, task, target) {
78965
79775
  return null;
78966
79776
  }
78967
79777
  }
79778
+ function isCompactSessionStart(input) {
79779
+ return input.event === "session_start" && input.sessionStartReason?.trim().toLowerCase() === "compact";
79780
+ }
79781
+ async function consumeCompactContinuation(input, task) {
79782
+ try {
79783
+ const { consumeCompactionWorkset: consumeCompactionWorkset2 } = await Promise.resolve().then(() => (init_compaction(), compaction_exports));
79784
+ const workset = await consumeCompactionWorkset2(input, { task, maxTokens: 420 });
79785
+ return workset?.text;
79786
+ } catch (error2) {
79787
+ console.error("[memorix] compact continuation failed:", error2?.message ?? error2);
79788
+ return void 0;
79789
+ }
79790
+ }
78968
79791
  async function buildClaudeContinuationPromptContext(input) {
78969
79792
  if (input.agent !== "claude" || input.event !== "user_prompt" || !input.userPrompt?.trim()) {
78970
79793
  return void 0;
78971
79794
  }
78972
- const { isContinuationTask: isContinuationTask2 } = await Promise.resolve().then(() => (init_task_lens(), task_lens_exports));
78973
- if (!isContinuationTask2(input.userPrompt)) return void 0;
78974
79795
  if (await getHookInjectionMode(input) === "silent") return void 0;
78975
- const context = await buildHookProjectContext(input, input.userPrompt, "hook-user-prompt");
78976
- if (!context?.hasContinuation) return void 0;
78977
- return [
78978
- "Memorix prepared a bounded prior-work brief for this explicit continuation request.",
78979
- "Treat it as background context; current code wins. Use it before broad Git or file-history archaeology.",
78980
- "",
78981
- context.prompt
78982
- ].join("\n");
79796
+ const compactContinuation = await consumeCompactContinuation(input, input.userPrompt);
79797
+ if (compactContinuation) {
79798
+ return [
79799
+ "Memorix recovered one bounded checkpoint after the host compacted this session.",
79800
+ "Treat it as background context; current code and the user request win.",
79801
+ "",
79802
+ compactContinuation
79803
+ ].join("\n");
79804
+ }
79805
+ const { isContinuationTask: isContinuationTask2, resolveTaskLens: resolveTaskLens2 } = await Promise.resolve().then(() => (init_task_lens(), task_lens_exports));
79806
+ const continuationRequested = isContinuationTask2(input.userPrompt);
79807
+ if (continuationRequested) {
79808
+ const context = await buildHookProjectContext(input, input.userPrompt, "hook-user-prompt");
79809
+ if (context?.hasContinuation) {
79810
+ return [
79811
+ "Memorix prepared a bounded prior-work brief for this explicit continuation request.",
79812
+ "Treat it as background context; current code wins. Use it before broad Git or file-history archaeology.",
79813
+ "",
79814
+ context.prompt
79815
+ ].join("\n");
79816
+ }
79817
+ }
79818
+ if (continuationRequested || resolveTaskLens2(input.userPrompt).id === "onboarding") {
79819
+ return [
79820
+ "Memorix is available for this handoff or continuation.",
79821
+ "Before broad file or Git exploration, call memorix_project_context with the user's actual task.",
79822
+ "If it is not visible yet, use Claude Code tool search for memorix_project_context; use CLI only when MCP discovery is unavailable."
79823
+ ].join(" ");
79824
+ }
79825
+ return void 0;
78983
79826
  }
78984
79827
  async function handleSessionStart(input) {
78985
79828
  const injectMode = await getHookInjectionMode(input);
78986
79829
  if (injectMode === "silent") {
78987
79830
  return { observation: null, output: { continue: true } };
78988
79831
  }
79832
+ if (input.agent === "codex" && isCompactSessionStart(input)) {
79833
+ const compactContinuation = await consumeCompactContinuation(
79834
+ input,
79835
+ "Continue after the host compacted the current session."
79836
+ );
79837
+ if (compactContinuation) {
79838
+ return {
79839
+ observation: null,
79840
+ output: {
79841
+ continue: true,
79842
+ systemMessage: [
79843
+ "Memorix recovered one bounded checkpoint after the host compacted this session.",
79844
+ "Treat it as background context; current code and the user request win.",
79845
+ "",
79846
+ compactContinuation
79847
+ ].join("\n")
79848
+ }
79849
+ };
79850
+ }
79851
+ }
78989
79852
  let contextSummary = "";
78990
79853
  if (injectMode === "full") {
78991
79854
  const context = await buildHookProjectContext(input, "Continue the current task.", "hook-session-start");
@@ -79194,6 +80057,12 @@ async function runHook(agentOverride, eventOverride) {
79194
80057
  payload._memorix_event = eventOverride;
79195
80058
  }
79196
80059
  const input = normalizeHookInput(payload);
80060
+ try {
80061
+ const { captureCompactionCheckpoint: captureCompactionCheckpoint2 } = await Promise.resolve().then(() => (init_compaction(), compaction_exports));
80062
+ await captureCompactionCheckpoint2(input);
80063
+ } catch (checkpointError) {
80064
+ console.error("[memorix] compact checkpoint failed:", checkpointError?.message ?? checkpointError);
80065
+ }
79197
80066
  const { observation, output } = await handleHookEvent(input, { deferMaintenance: true });
79198
80067
  if (observation) {
79199
80068
  try {
@@ -84986,32 +85855,32 @@ __export(cost_tracker_exports, {
84986
85855
  formatCostSummary: () => formatCostSummary,
84987
85856
  isBudgetExceeded: () => isBudgetExceeded
84988
85857
  });
84989
- function calculateModelCost(model, usage2, customPrices) {
85858
+ function calculateModelCost(model, usage3, customPrices) {
84990
85859
  const prices = { ...DEFAULT_PRICES, ...customPrices };
84991
85860
  const price = findPrice(model, prices);
84992
85861
  if (!price) return null;
84993
- const inputCost = usage2.inputTokens / 1e6 * price.inputPer1M;
84994
- const outputCost = usage2.outputTokens / 1e6 * price.outputPer1M;
84995
- const cacheReadCost = usage2.cacheReadTokens / 1e6 * (price.cacheReadPer1M ?? 0);
84996
- const cacheWriteCost = usage2.cacheWriteTokens / 1e6 * (price.cacheWritePer1M ?? 0);
85862
+ const inputCost = usage3.inputTokens / 1e6 * price.inputPer1M;
85863
+ const outputCost = usage3.outputTokens / 1e6 * price.outputPer1M;
85864
+ const cacheReadCost = usage3.cacheReadTokens / 1e6 * (price.cacheReadPer1M ?? 0);
85865
+ const cacheWriteCost = usage3.cacheWriteTokens / 1e6 * (price.cacheWritePer1M ?? 0);
84997
85866
  return inputCost + outputCost + cacheReadCost + cacheWriteCost;
84998
85867
  }
84999
85868
  function calculatePipelineCost(tokenUsage, budgetUSD, customPrices) {
85000
85869
  const models2 = [];
85001
85870
  let totalUSD = 0;
85002
85871
  let hasAnyPrice = false;
85003
- for (const [model, usage2] of Object.entries(tokenUsage)) {
85004
- const cost = calculateModelCost(model, usage2, customPrices);
85872
+ for (const [model, usage3] of Object.entries(tokenUsage)) {
85873
+ const cost = calculateModelCost(model, usage3, customPrices);
85005
85874
  if (cost !== null) {
85006
85875
  hasAnyPrice = true;
85007
85876
  totalUSD = (totalUSD ?? 0) + cost;
85008
85877
  }
85009
85878
  models2.push({
85010
85879
  model,
85011
- inputTokens: usage2.inputTokens,
85012
- outputTokens: usage2.outputTokens,
85013
- cacheReadTokens: usage2.cacheReadTokens,
85014
- cacheWriteTokens: usage2.cacheWriteTokens,
85880
+ inputTokens: usage3.inputTokens,
85881
+ outputTokens: usage3.outputTokens,
85882
+ cacheReadTokens: usage3.cacheReadTokens,
85883
+ cacheWriteTokens: usage3.cacheWriteTokens,
85015
85884
  costUSD: cost !== null ? Math.round(cost * 1e4) / 1e4 : null
85016
85885
  // Round to 4 decimals
85017
85886
  });
@@ -85310,7 +86179,7 @@ var init_permission = __esm({
85310
86179
  });
85311
86180
 
85312
86181
  // src/orchestrate/memorix-bridge.ts
85313
- import { createHash as createHash16 } from "crypto";
86182
+ import { createHash as createHash17 } from "crypto";
85314
86183
  async function getStoreObservation() {
85315
86184
  if (!_storeObservation) {
85316
86185
  const mod = await Promise.resolve().then(() => (init_observations(), observations_exports));
@@ -85326,7 +86195,7 @@ async function getSearchObservations() {
85326
86195
  return _searchObservations;
85327
86196
  }
85328
86197
  function automaticActorId(...parts) {
85329
- return `orchestrator:${createHash16("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
86198
+ return `orchestrator:${createHash17("sha256").update(parts.join("\0")).digest("hex").slice(0, 24)}`;
85330
86199
  }
85331
86200
  function isEligibleAutomaticLesson(entry) {
85332
86201
  return entry.admissionState !== "candidate" && entry.admissionState !== "ephemeral";
@@ -85473,7 +86342,7 @@ function sanitizeErrorPattern(text) {
85473
86342
  }
85474
86343
  function hashErrorPattern(errorOutput) {
85475
86344
  const sanitized = sanitizeErrorPattern(errorOutput).slice(0, 500);
85476
- return createHash16("sha256").update(sanitized).digest("hex").slice(0, 12);
86345
+ return createHash17("sha256").update(sanitized).digest("hex").slice(0, 12);
85477
86346
  }
85478
86347
  function sleep2(ms) {
85479
86348
  return new Promise((resolve6) => setTimeout(() => resolve6([]), ms));
@@ -85934,12 +86803,12 @@ ${formatCostSummary(costSummary)}`);
85934
86803
  activeDispatches.splice(settled.idx, 1);
85935
86804
  const { dispatch, result } = settled;
85936
86805
  if (result.tokenUsage) {
85937
- for (const [model, usage2] of Object.entries(result.tokenUsage)) {
86806
+ for (const [model, usage3] of Object.entries(result.tokenUsage)) {
85938
86807
  const prev = pipelineUsage[model] ?? { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, model };
85939
- prev.inputTokens += usage2.inputTokens;
85940
- prev.outputTokens += usage2.outputTokens;
85941
- prev.cacheReadTokens += usage2.cacheReadTokens;
85942
- prev.cacheWriteTokens += usage2.cacheWriteTokens;
86808
+ prev.inputTokens += usage3.inputTokens;
86809
+ prev.outputTokens += usage3.outputTokens;
86810
+ prev.cacheReadTokens += usage3.cacheReadTokens;
86811
+ prev.cacheWriteTokens += usage3.cacheWriteTokens;
85943
86812
  pipelineUsage[model] = prev;
85944
86813
  }
85945
86814
  if (traceDb && pipelineId) {
@@ -86885,10 +87754,10 @@ ${"\u2550".repeat(60)}`);
86885
87754
  if (result.tokenUsage) {
86886
87755
  console.error(`
86887
87756
  [COST] Token Usage:`);
86888
- for (const [model, usage2] of Object.entries(result.tokenUsage)) {
86889
- const total = usage2.inputTokens + usage2.outputTokens;
86890
- const cacheHits = usage2.cacheReadTokens;
86891
- console.error(` ${model}: ${total.toLocaleString()} tokens (in: ${usage2.inputTokens.toLocaleString()}, out: ${usage2.outputTokens.toLocaleString()}${cacheHits > 0 ? `, cache: ${cacheHits.toLocaleString()}` : ""})`);
87757
+ for (const [model, usage3] of Object.entries(result.tokenUsage)) {
87758
+ const total = usage3.inputTokens + usage3.outputTokens;
87759
+ const cacheHits = usage3.cacheReadTokens;
87760
+ console.error(` ${model}: ${total.toLocaleString()} tokens (in: ${usage3.inputTokens.toLocaleString()}, out: ${usage3.outputTokens.toLocaleString()}${cacheHits > 0 ? `, cache: ${cacheHits.toLocaleString()}` : ""})`);
86892
87761
  }
86893
87762
  }
86894
87763
  if (result.costSummary) {
@@ -91394,6 +92263,16 @@ var GUIDES = {
91394
92263
  'memorix codegraph context-pack --task "trace the auth flow" [--limit 20]'
91395
92264
  ]
91396
92265
  },
92266
+ checkpoint: {
92267
+ summary: "Inspect bounded recovery checkpoints created around host-native context compaction.",
92268
+ usage: [
92269
+ "memorix checkpoint list [--session <id>] [--agent <name>]",
92270
+ "memorix checkpoint show --id <checkpoint-id>",
92271
+ 'memorix checkpoint context [--id <checkpoint-id>] [--task "continue auth fix"]',
92272
+ "memorix checkpoint archive --id <checkpoint-id>"
92273
+ ],
92274
+ notes: ["Checkpoints are host lifecycle evidence, not durable project memory and not transcript backups."]
92275
+ },
91397
92276
  knowledge: {
91398
92277
  summary: "Manage the reviewed, source-backed Knowledge Workspace and project workflows.",
91399
92278
  usage: [
@@ -91709,6 +92588,7 @@ var main = defineCommand({
91709
92588
  resume: () => Promise.resolve().then(() => (init_resume(), resume_exports)).then((m4) => m4.default),
91710
92589
  explain: () => Promise.resolve().then(() => (init_explain(), explain_exports)).then((m4) => m4.default),
91711
92590
  codegraph: () => Promise.resolve().then(() => (init_codegraph(), codegraph_exports)).then((m4) => m4.default),
92591
+ checkpoint: () => Promise.resolve().then(() => (init_checkpoint(), checkpoint_exports)).then((m4) => m4.default),
91712
92592
  knowledge: () => Promise.resolve().then(() => (init_knowledge(), knowledge_exports)).then((m4) => m4.default),
91713
92593
  reasoning: () => Promise.resolve().then(() => (init_reasoning(), reasoning_exports)).then((m4) => m4.default),
91714
92594
  retention: () => Promise.resolve().then(() => (init_retention2(), retention_exports2)).then((m4) => m4.default),
@@ -91790,6 +92670,7 @@ var main = defineCommand({
91790
92670
  "resume",
91791
92671
  "explain",
91792
92672
  "codegraph",
92673
+ "checkpoint",
91793
92674
  "knowledge",
91794
92675
  "reasoning",
91795
92676
  "retention",
@@ -91858,6 +92739,7 @@ var main = defineCommand({
91858
92739
  console.error(" resume Resume prior work with one bounded project brief");
91859
92740
  console.error(" explain Explain where Memorix project context comes from");
91860
92741
  console.error(" codegraph Refresh/status/context-pack for CodeGraph Memory");
92742
+ console.error(" checkpoint Inspect native compact continuity checkpoints");
91861
92743
  console.error(" knowledge Review source-backed knowledge pages and project workflows");
91862
92744
  console.error(" reasoning Store/search decision rationale");
91863
92745
  console.error(" retention Inspect stale/archive status");