codemem 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { DEDUP_KEY_BACKFILL_JOB, DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, REF_BACKFILL_JOB, RawEventSweeper, RefBackfillRunner, SESSION_CONTEXT_BACKFILL_JOB, SessionContextBackfillRunner, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, ensureDeviceIdentity, ensureSchemaBootstrapped, exportMemories, extractApplyPatchPaths, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, getExtractionBenchmarkProfile, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMaintenanceJob, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingSessionContextBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, loadObserverConfig, loadPublicKey, loadSqliteVec, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
2
+ import { DEDUP_KEY_BACKFILL_JOB, DEFAULT_COORDINATOR_DB_PATH, DedupKeyBackfillRunner, MUTATING_TOOL_NAMES, MemoryStore, ObserverClient, REF_BACKFILL_JOB, RawEventSweeper, RefBackfillRunner, SESSION_CONTEXT_BACKFILL_JOB, SUMMARY_DEDUP_BACKFILL_JOB, SessionContextBackfillRunner, SummaryDedupBackfillRunner, SyncRetentionRunner, VERSION, VectorModelMigrationRunner, aiBackfillStructuredContent, applyBootstrapSnapshot, backfillMemoryDedupKeys, backfillNarrativeFromBody, backfillTagsText, backfillVectors, buildAuthHeaders, buildBaseUrl, buildRawEventEnvelopeFromHook, compareMemoryRoleReports, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, createBetterSqliteCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, dedupNearDuplicateMemories, ensureDeviceIdentity, ensureSchemaBootstrapped, exportMemories, extractApplyPatchPaths, fetchAllSnapshotPages, fingerprintPublicKey, flushRawEvents, getExtractionBenchmarkProfile, getInjectionEvalScenarioPack, getInjectionEvalScenarioPrompts, getMaintenanceJob, getMemoryRoleReport, getRawEventRelinkPlan, getRawEventRelinkReport, getRawEventStatus, getSemanticIndexDiagnostics, getSessionExtractionEval, getSessionExtractionEvalScenario, getWorkspaceCodememConfigPath, hasPendingDedupKeyBackfill, hasPendingRefBackfill, hasPendingSessionContextBackfill, hasPendingSummaryDedupBackfill, hasUnsyncedSharedMemoryChanges, importMemories, initDatabase, isEmbeddingDisabled, listMaintenanceJobs, loadObserverConfig, loadPublicKey, loadSqliteVec, mdnsEnabled, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCodememConfigFileAtPath, readCoordinatorSyncConfig, readImportPayload, replayBatchExtraction, replayBatchExtractionWithTierRouting, requestJson, resolveCodememConfigPath, resolveDbPath, resolveHookProject, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
3
3
  import { Command, Option } from "commander";
4
4
  import omelette from "omelette";
5
5
  import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
6
6
  import { styleText } from "node:util";
7
- import { createHash, randomInt } from "node:crypto";
7
+ import { randomInt } from "node:crypto";
8
8
  import { homedir, networkInterfaces } from "node:os";
9
9
  import { dirname, join } from "node:path";
10
10
  import * as p from "@clack/prompts";
@@ -536,6 +536,15 @@ var MAX_FILES_MODIFIED = 64;
536
536
  var MAX_WORKING_SET_PATHS = 8;
537
537
  var MAX_QUERY_CHARS = 500;
538
538
  var MAX_QUERY_FILE_BASENAMES = 5;
539
+ var SESSION_FILE_LABEL_CHARS = 24;
540
+ function stableSessionSuffix(sessionId) {
541
+ let hash = 14695981039346656037n;
542
+ for (const byte of Buffer.from(sessionId, "utf8")) {
543
+ hash ^= BigInt(byte);
544
+ hash = BigInt.asUintN(64, hash * 1099511628211n);
545
+ }
546
+ return hash.toString(16).padStart(16, "0");
547
+ }
539
548
  function defaultSessionState() {
540
549
  return {
541
550
  first_prompt: "",
@@ -553,9 +562,13 @@ function contextDir() {
553
562
  const override = process.env.CODEMEM_CLAUDE_HOOK_CONTEXT_DIR;
554
563
  return expandHome(override?.trim() ? override : "~/.codemem/claude-hook-context");
555
564
  }
565
+ function sessionFileStem(sessionId) {
566
+ const trimmed = sessionId.trim();
567
+ if (!trimmed) return "session-state";
568
+ return `${trimmed.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, SESSION_FILE_LABEL_CHARS) || "session"}-${stableSessionSuffix(trimmed)}`;
569
+ }
556
570
  function statePathForSession(sessionId) {
557
- const digest = createHash("sha1").update(sessionId).digest("hex").slice(0, 16);
558
- return join(contextDir(), `${digest}.json`);
571
+ return join(contextDir(), `${sessionFileStem(sessionId)}.json`);
559
572
  }
560
573
  /**
561
574
  * Normalize a prompt-shaped payload field: drop non-strings, trim
@@ -2562,6 +2575,78 @@ cmd$1.action((inputFile, opts) => {
2562
2575
  });
2563
2576
  var importMemoriesCommand = cmd$1;
2564
2577
  //#endregion
2578
+ //#region src/commands/maintenance.ts
2579
+ var maintenanceCmd = new Command("maintenance").configureHelp(helpStyle).description("Inspect background maintenance / backfill jobs");
2580
+ var statusCmd$1 = new Command("status").configureHelp(helpStyle).description("Print current status of all maintenance jobs");
2581
+ addDbOption(statusCmd$1);
2582
+ addJsonOption(statusCmd$1);
2583
+ statusCmd$1.action((opts) => {
2584
+ const store = new MemoryStore(resolveDbPath(resolveDbOpt(opts)));
2585
+ try {
2586
+ const jobs = listMaintenanceJobs(store.db);
2587
+ if (opts.json) {
2588
+ console.log(JSON.stringify({ jobs }, null, 2));
2589
+ return;
2590
+ }
2591
+ printJobsTable(jobs);
2592
+ } finally {
2593
+ store.close();
2594
+ }
2595
+ });
2596
+ maintenanceCmd.addCommand(statusCmd$1);
2597
+ var maintenanceCommand = maintenanceCmd;
2598
+ function printJobsTable(jobs) {
2599
+ p.intro("codemem maintenance");
2600
+ if (jobs.length === 0) {
2601
+ p.log.info("No maintenance jobs recorded in this database.");
2602
+ p.outro("done");
2603
+ return;
2604
+ }
2605
+ const statusRank = {
2606
+ running: 0,
2607
+ pending: 1,
2608
+ failed: 2,
2609
+ cancelled: 3,
2610
+ completed: 4
2611
+ };
2612
+ const sorted = [...jobs].sort((a, b) => {
2613
+ const rankDiff = (statusRank[a.status] ?? 9) - (statusRank[b.status] ?? 9);
2614
+ if (rankDiff !== 0) return rankDiff;
2615
+ return b.updated_at.localeCompare(a.updated_at);
2616
+ });
2617
+ for (const job of sorted) {
2618
+ const progress = formatProgress(job);
2619
+ const when = job.finished_at ? `finished ${shortAge(job.finished_at)} ago` : job.started_at ? `started ${shortAge(job.started_at)} ago` : `updated ${shortAge(job.updated_at)} ago`;
2620
+ const header = `${job.kind} · ${job.status}${progress ? ` · ${progress}` : ""} · ${when}`;
2621
+ const body = [
2622
+ job.title,
2623
+ job.message,
2624
+ job.error ? `error: ${job.error}` : null
2625
+ ].filter(Boolean).join("\n");
2626
+ (job.status === "failed" ? p.log.error : job.status === "completed" ? p.log.success : p.log.step)(`${header}\n${body}`);
2627
+ }
2628
+ p.outro("done");
2629
+ }
2630
+ function formatProgress(job) {
2631
+ const { current, total, unit } = job.progress;
2632
+ if (!current && !total) return null;
2633
+ const unitLabel = unit || "items";
2634
+ if (total == null || total <= 0) return `${current.toLocaleString()} ${unitLabel}`;
2635
+ const pct = Math.round(100 * current / total);
2636
+ return `${current.toLocaleString()}/${total.toLocaleString()} ${unitLabel} (${pct}%)`;
2637
+ }
2638
+ function shortAge(iso) {
2639
+ const parsed = Date.parse(iso);
2640
+ if (!Number.isFinite(parsed)) return iso;
2641
+ const seconds = Math.max(0, Math.round((Date.now() - parsed) / 1e3));
2642
+ if (seconds < 60) return `${seconds}s`;
2643
+ const minutes = Math.round(seconds / 60);
2644
+ if (minutes < 60) return `${minutes}m`;
2645
+ const hours = Math.round(minutes / 60);
2646
+ if (hours < 48) return `${hours}h`;
2647
+ return `${Math.round(hours / 24)}d`;
2648
+ }
2649
+ //#endregion
2565
2650
  //#region src/commands/mcp.ts
2566
2651
  var mcpCmd = new Command("mcp").configureHelp(helpStyle).description("Start the MCP stdio server");
2567
2652
  addDbOption(mcpCmd);
@@ -3868,7 +3953,10 @@ async function startForegroundViewer(invocation) {
3868
3953
  dbPath,
3869
3954
  signal: retentionAbort.signal
3870
3955
  });
3871
- const vectorMigrationRunner = new VectorModelMigrationRunner({ dbPath });
3956
+ const vectorMigrationRunner = new VectorModelMigrationRunner({
3957
+ dbPath,
3958
+ signal: backfillAbort.signal
3959
+ });
3872
3960
  const backfillCoordinator = createSequentialBackfillCoordinator(store, [
3873
3961
  {
3874
3962
  name: "Dedup-key",
@@ -3896,6 +3984,16 @@ async function startForegroundViewer(invocation) {
3896
3984
  dbPath,
3897
3985
  signal: backfillAbort.signal
3898
3986
  })
3987
+ },
3988
+ {
3989
+ name: "Session-summary dedup",
3990
+ kind: SUMMARY_DEDUP_BACKFILL_JOB,
3991
+ isPending: hasPendingSummaryDedupBackfill,
3992
+ createRunner: () => new SummaryDedupBackfillRunner({
3993
+ dbPath,
3994
+ deviceId: store.deviceId,
3995
+ signal: backfillAbort.signal
3996
+ })
3899
3997
  }
3900
3998
  ], backfillAbort.signal);
3901
3999
  const syncRuntimeStatus = {
@@ -4100,10 +4198,12 @@ serveCmd.addOption(new Option("--stop", "stop background viewer").hideHelp());
4100
4198
  serveCmd.addOption(new Option("--restart", "restart background viewer").hideHelp());
4101
4199
  var serveCommand = serveCmd.action(async (action, opts) => {
4102
4200
  try {
4103
- if (opts.stop) emitDeprecationWarning("--stop", "codemem serve stop");
4104
- if (opts.restart) emitDeprecationWarning("--restart", "codemem serve restart");
4105
- if (opts.background) emitDeprecationWarning("--background", "codemem serve start");
4106
- if (opts.foreground) emitDeprecationWarning("--foreground", "codemem serve start --foreground");
4201
+ if (action === void 0) {
4202
+ if (opts.stop) emitDeprecationWarning("--stop", "codemem serve stop");
4203
+ if (opts.restart) emitDeprecationWarning("--restart", "codemem serve restart");
4204
+ if (opts.background) emitDeprecationWarning("--background", "codemem serve start");
4205
+ if (opts.foreground) emitDeprecationWarning("--foreground", "codemem serve start --foreground");
4206
+ }
4107
4207
  const normalizedAction = action === void 0 ? void 0 : action === "start" || action === "stop" || action === "restart" ? action : null;
4108
4208
  if (normalizedAction === null) {
4109
4209
  p.log.error(`Unknown serve action: ${action}`);
@@ -4884,11 +4984,15 @@ doctorCmd.action(async (opts) => {
4884
4984
  if (reach !== "ok") issues.push(`peer ${peer.peer_device_id} unreachable`);
4885
4985
  if (!pinned || !hasKey) issues.push(`peer ${peer.peer_device_id} not pinned`);
4886
4986
  }
4987
+ const mdnsActive = mdnsEnabled();
4988
+ const mdnsSource = process.env.CODEMEM_SYNC_MDNS ? "env" : config.sync_mdns ? "config" : null;
4989
+ const mdnsLabel = mdnsActive ? mdnsSource === "env" ? "enabled (env)" : "enabled (config)" : "disabled";
4887
4990
  if (opts.json) {
4888
4991
  console.log(JSON.stringify({
4889
4992
  enabled: config.sync_enabled === true,
4890
4993
  listen: `${syncHost}:${syncPort}`,
4891
- mdns: process.env.CODEMEM_SYNC_MDNS ? "env-configured" : "default/off",
4994
+ mdns: mdnsLabel,
4995
+ mdns_source: mdnsSource,
4892
4996
  daemon: reachable ? "running" : "not running",
4893
4997
  identity: device?.device_id ?? null,
4894
4998
  daemon_error: daemonState?.last_error ?? null,
@@ -4901,7 +5005,7 @@ doctorCmd.action(async (opts) => {
4901
5005
  console.log("Sync doctor");
4902
5006
  console.log(`- Enabled: ${config.sync_enabled === true}`);
4903
5007
  console.log(`- Listen: ${syncHost}:${syncPort}`);
4904
- console.log(`- mDNS: ${process.env.CODEMEM_SYNC_MDNS ? "env-configured" : "default/off"}`);
5008
+ console.log(`- mDNS: ${mdnsLabel}`);
4905
5009
  console.log(`- Daemon: ${reachable ? "running" : "not running"}`);
4906
5010
  if (!device) console.log("- Identity: missing (run `codemem sync enable`)");
4907
5011
  else console.log(`- Identity: ${device.device_id}`);
@@ -5394,6 +5498,7 @@ program.addCommand(dbCommand);
5394
5498
  program.addCommand(exportMemoriesCommand);
5395
5499
  program.addCommand(importMemoriesCommand);
5396
5500
  program.addCommand(statsCommand);
5501
+ program.addCommand(maintenanceCommand);
5397
5502
  program.addCommand(embedCommand);
5398
5503
  program.addCommand(recentCommand);
5399
5504
  program.addCommand(searchCommand);