@wipcomputer/memory-crystal 0.7.30 → 0.7.33

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 (114) hide show
  1. package/SKILL.md +1 -1
  2. package/cloud/wrangler.toml +30 -0
  3. package/dist/bulk-copy.js +1 -1
  4. package/dist/cc-hook.js +3 -3
  5. package/dist/cc-poller.js +2 -2
  6. package/dist/chunk-2GBYLMEF.js +1385 -0
  7. package/dist/chunk-437F27T6.js +97 -0
  8. package/dist/chunk-5I7GMRDN.js +146 -0
  9. package/dist/chunk-CGIDSAJB.js +288 -0
  10. package/dist/chunk-D3MACYZ4.js +108 -0
  11. package/dist/chunk-DFQ72B7M.js +248 -0
  12. package/dist/chunk-NX647OM3.js +310 -0
  13. package/dist/cli.js +62 -7
  14. package/dist/core.d.ts +22 -2
  15. package/dist/core.js +1 -1
  16. package/dist/crypto.js +2 -2
  17. package/dist/crystal-serve.js +3 -3
  18. package/dist/doctor.js +12 -4
  19. package/dist/dream-weaver.js +2 -2
  20. package/dist/file-sync.js +3 -3
  21. package/dist/installer.js +99 -3
  22. package/dist/ldm.js +1 -1
  23. package/dist/llm-XXLYPIOF.js +16 -0
  24. package/dist/mcp-server.js +17 -5
  25. package/dist/migrate.js +1 -1
  26. package/dist/mirror-sync.js +4 -4
  27. package/dist/mlx-setup-XKU67WCT.js +289 -0
  28. package/dist/openclaw.js +16 -5
  29. package/dist/pair.js +2 -2
  30. package/dist/poller.js +5 -5
  31. package/dist/role.js +2 -2
  32. package/dist/search-pipeline-CBV25NX7.js +99 -0
  33. package/dist/staging.js +2 -2
  34. package/package.json +15 -1
  35. package/.env.example +0 -20
  36. package/.publish-skill.json +0 -1
  37. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/.env.example +0 -20
  38. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/.publish-skill.json +0 -1
  39. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/CHANGELOG.md +0 -1297
  40. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/CLA.md +0 -19
  41. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/LICENSE +0 -52
  42. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/README-ENTERPRISE.md +0 -226
  43. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/README.md +0 -151
  44. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/RELAY.md +0 -199
  45. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/SKILL.md +0 -462
  46. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/TECHNICAL.md +0 -656
  47. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-23.md +0 -48
  48. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-25.md +0 -24
  49. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-26.md +0 -7
  50. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-28.md +0 -31
  51. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-29.md +0 -28
  52. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-4.md +0 -64
  53. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/_trash/RELEASE-NOTES-v0-7-5.md +0 -19
  54. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/cloud/README.md +0 -116
  55. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/cloud/docs/gpt-system-instructions.md +0 -69
  56. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/cloud/migrations/0001_init.sql +0 -52
  57. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/migrations/0001_init.sql +0 -51
  58. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/migrations/0002_cloud_storage.sql +0 -49
  59. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/openclaw.plugin.json +0 -11
  60. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/package-lock.json +0 -4169
  61. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/package.json +0 -61
  62. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/scripts/crystal-capture.sh +0 -29
  63. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/scripts/deploy-cloud.sh +0 -153
  64. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/scripts/ldm-backup.sh +0 -116
  65. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/scripts/migrate-lance-to-sqlite.mjs +0 -218
  66. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/skills/memory/SKILL.md +0 -438
  67. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/wrangler-demo.toml +0 -8
  68. package/.worktrees/memory-crystal-private--cc-mini-fix-home-fallback/wrangler-mcp.toml +0 -24
  69. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/.env.example +0 -20
  70. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/.publish-skill.json +0 -1
  71. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/CHANGELOG.md +0 -1297
  72. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/CLA.md +0 -19
  73. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/LICENSE +0 -52
  74. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/README-ENTERPRISE.md +0 -226
  75. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/README.md +0 -151
  76. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/RELAY.md +0 -199
  77. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/RELEASE-NOTES-v0.7.30.md +0 -29
  78. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/SKILL.md +0 -462
  79. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/TECHNICAL.md +0 -656
  80. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-23.md +0 -48
  81. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-25.md +0 -24
  82. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-26.md +0 -7
  83. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-28.md +0 -31
  84. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-29.md +0 -28
  85. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-4.md +0 -64
  86. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/_trash/RELEASE-NOTES-v0-7-5.md +0 -19
  87. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/cloud/README.md +0 -116
  88. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/cloud/docs/gpt-system-instructions.md +0 -69
  89. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/cloud/migrations/0001_init.sql +0 -52
  90. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/migrations/0001_init.sql +0 -51
  91. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/migrations/0002_cloud_storage.sql +0 -49
  92. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/openclaw.plugin.json +0 -11
  93. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/package-lock.json +0 -4169
  94. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/package.json +0 -61
  95. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/scripts/crystal-capture.sh +0 -29
  96. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/scripts/deploy-cloud.sh +0 -153
  97. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/scripts/ldm-backup.sh +0 -116
  98. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/scripts/migrate-lance-to-sqlite.mjs +0 -218
  99. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/skills/memory/SKILL.md +0 -438
  100. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/wrangler-demo.toml +0 -8
  101. package/.worktrees/memory-crystal-private--cc-mini-release-notes-v0.7.30/wrangler-mcp.toml +0 -24
  102. package/CHANGELOG.md +0 -1329
  103. package/README-ENTERPRISE.md +0 -226
  104. package/RELAY.md +0 -199
  105. package/_trash/RELEASE-NOTES-v0-7-23.md +0 -48
  106. package/_trash/RELEASE-NOTES-v0-7-25.md +0 -24
  107. package/_trash/RELEASE-NOTES-v0-7-26.md +0 -7
  108. package/_trash/RELEASE-NOTES-v0-7-28.md +0 -31
  109. package/_trash/RELEASE-NOTES-v0-7-29.md +0 -28
  110. package/_trash/RELEASE-NOTES-v0-7-4.md +0 -64
  111. package/_trash/RELEASE-NOTES-v0-7-5.md +0 -19
  112. package/_trash/RELEASE-NOTES-v0.7.30.md +0 -29
  113. package/wrangler-demo.toml +0 -8
  114. package/wrangler-mcp.toml +0 -24
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  Crystal,
4
4
  createCrystal,
5
5
  resolveConfig
6
- } from "./chunk-FBQWSDPC.js";
6
+ } from "./chunk-2GBYLMEF.js";
7
7
  import {
8
8
  deployBackupScript,
9
9
  ensureLdm,
@@ -12,7 +12,7 @@ import {
12
12
  installCron,
13
13
  ldmPaths,
14
14
  removeCron
15
- } from "./chunk-EXEZZADG.js";
15
+ } from "./chunk-DFQ72B7M.js";
16
16
 
17
17
  // src/cli.ts
18
18
  import { existsSync, copyFileSync, symlinkSync, lstatSync, unlinkSync, readFileSync, readdirSync, statSync } from "fs";
@@ -23,7 +23,7 @@ var USAGE = `
23
23
  crystal \u2014 Sovereign memory system
24
24
 
25
25
  Commands:
26
- crystal search <query> [-n limit] [--agent <id>] [--since <time>] [--deep] [--provider <openai|ollama|google>]
26
+ crystal search <query> [-n limit] [--agent <id>] [--since <time>] [--until <date>] [--intent <context>] [--candidates <n>] [--explain] [--provider <openai|ollama|google>]
27
27
  crystal remember <text> [--category fact|preference|event|opinion|skill]
28
28
  crystal forget <id>
29
29
  crystal status [--provider <openai|ollama|google>]
@@ -43,6 +43,10 @@ Commands:
43
43
  crystal backup setup Install daily backup (LaunchAgent, 03:00)
44
44
  crystal backup --keep <n> Keep last n backups (default: 7)
45
45
 
46
+ crystal mlx setup [--yes] Install MLX local LLM (Apple Silicon only)
47
+ crystal mlx status Show MLX server status
48
+ crystal mlx stop Stop MLX server
49
+
46
50
  crystal bridge setup Install + register Bridge MCP server
47
51
  crystal bridge status Show Bridge install state
48
52
 
@@ -71,11 +75,24 @@ async function main() {
71
75
  console.log(USAGE);
72
76
  process.exit(0);
73
77
  }
78
+ if (args[0] === "--version" || args[0] === "-v") {
79
+ try {
80
+ const { readFileSync: readFileSync2 } = await import("fs");
81
+ const { dirname, join: join2 } = await import("path");
82
+ const { fileURLToPath } = await import("url");
83
+ const thisDir = dirname(fileURLToPath(import.meta.url));
84
+ const pkg = JSON.parse(readFileSync2(join2(thisDir, "..", "package.json"), "utf-8"));
85
+ console.log(pkg.version);
86
+ } catch {
87
+ console.log("unknown");
88
+ }
89
+ process.exit(0);
90
+ }
74
91
  const command = args[0];
75
92
  const flags = {};
76
93
  let positional = [];
77
94
  for (let i = 1; i < args.length; i++) {
78
- if (args[i] === "--dry-run" || args[i] === "--yes" || args[i] === "-y" || args[i] === "--skip-discover" || args[i] === "--include-secrets" || args[i] === "--deep" || args[i] === "--core" || args[i] === "--node" || args[i] === "--update") {
95
+ if (args[i] === "--dry-run" || args[i] === "--yes" || args[i] === "-y" || args[i] === "--skip-discover" || args[i] === "--include-secrets" || args[i] === "--deep" || args[i] === "--core" || args[i] === "--node" || args[i] === "--update" || args[i] === "--explain") {
79
96
  flags[args[i].replace(/^-+/, "")] = "true";
80
97
  } else if (args[i].startsWith("--") || args[i] === "-n") {
81
98
  const key = args[i].replace(/^-+/, "");
@@ -156,7 +173,7 @@ ${fails === 0 && warns === 0 ? "All checks passed." : `${fails} failures, ${warn
156
173
  console.log("Backup LaunchAgent installed.");
157
174
  console.log(` Runs daily at 03:00`);
158
175
  console.log(` Plist: ${plistPath}`);
159
- console.log(` Log: /tmp/ldm-dev-tools/ldm-backup.log`);
176
+ console.log(` Log: ~/.ldm/logs/ldm-backup.log`);
160
177
  } catch (err) {
161
178
  console.error(`Setup failed: ${err.message}`);
162
179
  process.exit(1);
@@ -295,6 +312,36 @@ Resuming capture cron...`);
295
312
  }
296
313
  return;
297
314
  }
315
+ if (command === "mlx") {
316
+ const subCmd = positional[0] || "status";
317
+ const { setupMlx, isServerRunning, stopServer, doctorCheck, MLX_CONFIG } = await import("./mlx-setup-XKU67WCT.js");
318
+ if (subCmd === "setup") {
319
+ const yes = "yes" in flags || "y" in flags;
320
+ const result = await setupMlx({ yes });
321
+ for (const step of result.steps) {
322
+ console.log(` ${result.ok ? "[OK]" : "[!!]"} ${step}`);
323
+ }
324
+ if (!result.ok) process.exit(1);
325
+ } else if (subCmd === "status") {
326
+ const check = doctorCheck();
327
+ const icon = check.status === "ok" ? "[OK]" : check.status === "warn" ? "[!!]" : "[XX]";
328
+ console.log(`MLX LLM: ${icon} ${check.detail}`);
329
+ if (check.fix) console.log(` Fix: ${check.fix}`);
330
+ console.log(` Port: ${MLX_CONFIG.port}`);
331
+ console.log(` Model: ${MLX_CONFIG.model}`);
332
+ console.log(` Log: ${MLX_CONFIG.logPath}`);
333
+ } else if (subCmd === "stop") {
334
+ if (stopServer()) {
335
+ console.log("MLX server stopped.");
336
+ } else {
337
+ console.log("MLX server was not running.");
338
+ }
339
+ } else {
340
+ console.error(`Unknown mlx subcommand: ${subCmd}. Use: setup, status, stop`);
341
+ process.exit(1);
342
+ }
343
+ return;
344
+ }
298
345
  if (command === "bridge") {
299
346
  const { isBridgeInstalled, isBridgeRegistered, registerBridgeMcp, registerBridgeDesktop, isBridgeDesktopRegistered } = await import("./bridge.js");
300
347
  const subCmd = positional[0] || "status";
@@ -371,7 +418,11 @@ Resuming capture cron...`);
371
418
  const filter = {};
372
419
  if (flags.agent) filter.agent_id = flags.agent;
373
420
  if (flags.since) filter.since = flags.since;
374
- const results = await crystal.deepSearch(query, limit, filter);
421
+ if (flags.until) filter.until = flags.until;
422
+ const intent = flags.intent;
423
+ const candidateLimit = flags.candidates ? parseInt(flags.candidates, 10) : void 0;
424
+ const explainMode = "explain" in flags;
425
+ const results = await crystal.deepSearch(query, limit, filter, { intent, candidateLimit, explain: explainMode });
375
426
  if (results.length === 0) {
376
427
  console.log("No results found.");
377
428
  } else {
@@ -383,6 +434,10 @@ Resuming capture cron...`);
383
434
  const fresh = r.freshness ? `${icon[r.freshness]} ${r.freshness}, ` : "";
384
435
  console.log(`[${i + 1}] (${fresh}${score}% match, ${r.agent_id}, ${date}, ${r.role})`);
385
436
  console.log(r.text.slice(0, 300) + (r.text.length > 300 ? "..." : ""));
437
+ if (explainMode && r.explain) {
438
+ const e = r.explain;
439
+ console.log(` explain: fts=${e.fts_score?.toFixed(3) || "n/a"} vec=${e.vec_score?.toFixed(3) || "n/a"} rrf_rank=${e.rrf_rank} rerank=${e.rerank_score.toFixed(3)} recency=${e.recency_weight.toFixed(3)} final=${e.final_score.toFixed(4)}`);
440
+ }
386
441
  console.log("---");
387
442
  }
388
443
  }
@@ -832,7 +887,7 @@ Backfill summary:`);
832
887
  (dry run, no embeddings created)`);
833
888
  return;
834
889
  }
835
- let role = "standalone";
890
+ let role = "core";
836
891
  try {
837
892
  const { detectRole } = await import("./role.js");
838
893
  role = detectRole().role;
package/dist/core.d.ts CHANGED
@@ -65,6 +65,11 @@ interface SearchResult {
65
65
  created_at: string;
66
66
  freshness?: "fresh" | "recent" | "aging" | "stale";
67
67
  }
68
+ /** Pre-expanded query for unified search API. Skip LLM expansion when you know what you want. */
69
+ interface StructuredQuery {
70
+ type: 'lex' | 'vec' | 'hyde';
71
+ text: string;
72
+ }
68
73
  interface CrystalStatus {
69
74
  chunks: number;
70
75
  memories: number;
@@ -149,13 +154,28 @@ declare class Crystal {
149
154
  agent_id?: string;
150
155
  source_type?: string;
151
156
  since?: string;
157
+ until?: string;
152
158
  }): Promise<SearchResult[]>;
153
159
  /** Deep search: query expansion + LLM re-ranking + position-aware blending.
154
- * Falls back to standard search if no LLM provider is available. */
160
+ * Falls back to standard search if no LLM provider is available.
161
+ * Supports intent disambiguation, candidateLimit tuning, and explain traces. */
155
162
  deepSearch(query: string, limit?: number, filter?: {
156
163
  agent_id?: string;
157
164
  source_type?: string;
158
165
  since?: string;
166
+ until?: string;
167
+ }, options?: {
168
+ intent?: string;
169
+ candidateLimit?: number;
170
+ explain?: boolean;
171
+ }): Promise<SearchResult[]>;
172
+ /** Structured search: pass pre-expanded queries to skip LLM expansion.
173
+ * Each query is typed (lex, vec, hyde) and searched independently, then fused with RRF. */
174
+ structuredSearch(queries: StructuredQuery[], limit?: number, filter?: {
175
+ agent_id?: string;
176
+ source_type?: string;
177
+ since?: string;
178
+ until?: string;
159
179
  }): Promise<SearchResult[]>;
160
180
  /** Vector search via sqlite-vec. Two-step pattern: MATCH first, then JOIN. */
161
181
  private searchVec;
@@ -229,4 +249,4 @@ declare class RemoteCrystal {
229
249
  /** Create the appropriate Crystal instance based on config. */
230
250
  declare function createCrystal(config: CrystalConfig): Crystal | RemoteCrystal;
231
251
 
232
- export { type Chunk, Crystal, type CrystalConfig, type CrystalStatus, type ExportedChunk, type Memory, RemoteCrystal, type SearchResult, type SourceCollection, type SourceFile, type SourcesStatus, type SyncResult, createCrystal, resolveConfig };
252
+ export { type Chunk, Crystal, type CrystalConfig, type CrystalStatus, type ExportedChunk, type Memory, RemoteCrystal, type SearchResult, type SourceCollection, type SourceFile, type SourcesStatus, type StructuredQuery, type SyncResult, createCrystal, resolveConfig };
package/dist/core.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  RemoteCrystal,
4
4
  createCrystal,
5
5
  resolveConfig
6
- } from "./chunk-FBQWSDPC.js";
6
+ } from "./chunk-2GBYLMEF.js";
7
7
  export {
8
8
  Crystal,
9
9
  RemoteCrystal,
package/dist/crypto.js CHANGED
@@ -10,8 +10,8 @@ import {
10
10
  generateRelayKey,
11
11
  hashBuffer,
12
12
  loadRelayKey
13
- } from "./chunk-KCQUXVYT.js";
14
- import "./chunk-EXEZZADG.js";
13
+ } from "./chunk-D3MACYZ4.js";
14
+ import "./chunk-DFQ72B7M.js";
15
15
  export {
16
16
  RELAY_KEY_PATH,
17
17
  decodePairingString,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ldmPaths
4
- } from "./chunk-EXEZZADG.js";
4
+ } from "./chunk-DFQ72B7M.js";
5
5
 
6
6
  // src/crystal-serve.ts
7
7
  import { createServer } from "http";
@@ -158,12 +158,12 @@ function handleStatus(_req, res) {
158
158
  } catch {
159
159
  }
160
160
  }
161
- let role = "standalone";
161
+ let role = "core";
162
162
  const rolePath = join(paths.state, "role.json");
163
163
  if (existsSync(rolePath)) {
164
164
  try {
165
165
  const r = JSON.parse(readFileSync(rolePath, "utf-8"));
166
- role = r.role || "standalone";
166
+ role = r.role || "core";
167
167
  } catch {
168
168
  }
169
169
  }
package/dist/doctor.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  detectRole
3
- } from "./chunk-3S6TI23B.js";
3
+ } from "./chunk-437F27T6.js";
4
4
  import {
5
5
  isBridgeInstalled,
6
6
  isBridgeRegistered
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  ldmPaths,
10
10
  resolveStatePath
11
- } from "./chunk-EXEZZADG.js";
11
+ } from "./chunk-DFQ72B7M.js";
12
12
 
13
13
  // src/doctor.ts
14
14
  import { existsSync, readFileSync, readdirSync } from "fs";
@@ -35,6 +35,14 @@ async function runDoctor() {
35
35
  checks.push(checkBridge());
36
36
  checks.push(checkLdmDirectory(paths));
37
37
  checks.push(checkPrivateMode());
38
+ try {
39
+ const { doctorCheck } = await import("./mlx-setup-XKU67WCT.js");
40
+ const mlx = doctorCheck();
41
+ if (mlx.status !== "skip") {
42
+ checks.push({ name: "MLX LLM", status: mlx.status, detail: mlx.detail, fix: mlx.fix });
43
+ }
44
+ } catch {
45
+ }
38
46
  return checks;
39
47
  }
40
48
  function checkOpEmbeddings() {
@@ -202,8 +210,8 @@ function checkCaptureCron() {
202
210
  };
203
211
  }
204
212
  function checkRelayConfig(role) {
205
- if (role.role === "standalone") {
206
- return { name: "Relay", status: "ok", detail: "not needed (standalone)" };
213
+ if (role.role === "core" && !role.relayUrl) {
214
+ return { name: "Relay", status: "ok", detail: "not needed (core, no relay configured)" };
207
215
  }
208
216
  if (role.role === "node") {
209
217
  if (!role.relayUrl) {
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  Crystal,
3
3
  resolveConfig
4
- } from "./chunk-FBQWSDPC.js";
4
+ } from "./chunk-2GBYLMEF.js";
5
5
  import {
6
6
  ldmPaths
7
- } from "./chunk-EXEZZADG.js";
7
+ } from "./chunk-DFQ72B7M.js";
8
8
 
9
9
  // src/dream-weaver.ts
10
10
  import {
package/dist/file-sync.js CHANGED
@@ -5,9 +5,9 @@ import {
5
5
  pullFileSync,
6
6
  pushFileSync,
7
7
  saveFileSyncState
8
- } from "./chunk-3G3SFYYI.js";
9
- import "./chunk-KCQUXVYT.js";
10
- import "./chunk-EXEZZADG.js";
8
+ } from "./chunk-CGIDSAJB.js";
9
+ import "./chunk-D3MACYZ4.js";
10
+ import "./chunk-DFQ72B7M.js";
11
11
  export {
12
12
  compareManifest,
13
13
  generateManifest,
package/dist/installer.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  loadAgentConfig,
8
8
  saveAgentConfig,
9
9
  scaffoldLdm
10
- } from "./chunk-EXEZZADG.js";
10
+ } from "./chunk-DFQ72B7M.js";
11
11
 
12
12
  // src/installer.ts
13
13
  import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, copyFileSync, readdirSync, statSync } from "fs";
@@ -119,7 +119,7 @@ function detectInstallState() {
119
119
  cronInstalled = crontab.includes("crystal-capture");
120
120
  } catch {
121
121
  }
122
- const role = "standalone";
122
+ const role = "core";
123
123
  const relayKeyExists = existsSync(join(LDM_ROOT, "secrets", "crystal-relay-key"));
124
124
  return {
125
125
  ldmExists: existsSync(LDM_ROOT),
@@ -385,7 +385,7 @@ function bootstrapLdmOs(steps) {
385
385
  steps.push("LDM OS installed.");
386
386
  return true;
387
387
  } catch {
388
- steps.push("LDM OS install skipped (npm offline or permissions issue). Using standalone.");
388
+ steps.push("LDM OS install skipped (npm offline or permissions issue). Using core mode.");
389
389
  return false;
390
390
  }
391
391
  }
@@ -599,6 +599,19 @@ async function runInstallOrUpdate(options) {
599
599
  } catch (err) {
600
600
  steps.push(`Backup script failed: ${err.message}`);
601
601
  }
602
+ try {
603
+ const { canRunMlx, isMlxLmInstalled, isServerRunning } = await import("./mlx-setup-XKU67WCT.js");
604
+ if (canRunMlx()) {
605
+ if (isServerRunning()) {
606
+ steps.push("MLX LLM: already running");
607
+ } else if (isMlxLmInstalled()) {
608
+ steps.push("MLX LLM: installed but not running. Start with: launchctl kickstart -kp gui/$(id -u)/ai.ldm.mlx-server");
609
+ } else {
610
+ steps.push('MLX LLM: Apple Silicon detected. Run "crystal mlx setup" to install local LLM for free, fast, offline search quality.');
611
+ }
612
+ }
613
+ } catch {
614
+ }
602
615
  if (!ldmDelegated && state.ocDetected) {
603
616
  try {
604
617
  const ocResult = deployToOpenClaw();
@@ -620,6 +633,24 @@ async function runInstallOrUpdate(options) {
620
633
  steps.push(`OC MCP config failed: ${err.message}`);
621
634
  }
622
635
  }
636
+ if (!options.role && process.stdin.isTTY) {
637
+ const { createInterface } = await import("readline");
638
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
639
+ const answer = await new Promise((resolve) => {
640
+ rl.question("\n Is this your primary machine (always on), or adding a device?\n [1] Primary (Crystal Core)\n [2] Adding a device (Crystal Node)\n > ", resolve);
641
+ });
642
+ rl.close();
643
+ if (answer.trim() === "2") {
644
+ options.role = "node";
645
+ const rl2 = createInterface({ input: process.stdin, output: process.stdout });
646
+ options.pairCode = await new Promise((resolve) => {
647
+ rl2.question(' Pairing code from Core (run "crystal pair" on Core): ', resolve);
648
+ });
649
+ rl2.close();
650
+ } else {
651
+ options.role = "core";
652
+ }
653
+ }
623
654
  if (options.role === "core") {
624
655
  try {
625
656
  const { promoteToCore } = await import("./role.js");
@@ -646,6 +677,71 @@ async function runInstallOrUpdate(options) {
646
677
  steps.push(`Pairing failed: ${err.message}`);
647
678
  }
648
679
  }
680
+ if (options.role === "node" || process.env.CRYSTAL_RELAY_URL) {
681
+ const secretsDir = join(LDM_ROOT, "secrets");
682
+ const envPath = join(secretsDir, "crystal-relay.env");
683
+ if (!existsSync(envPath)) {
684
+ const relayUrl = "https://memory-crystal-relay.wipcomputer.workers.dev";
685
+ let token = "";
686
+ try {
687
+ const saTokenPath = join(OC_ROOT, "secrets", "op-sa-token");
688
+ if (existsSync(saTokenPath)) {
689
+ const saToken = readFileSync(saTokenPath, "utf8").trim();
690
+ token = execSync(
691
+ `OP_SERVICE_ACCOUNT_TOKEN=${saToken} op item get "Memory Crystal Relay Auth Tokens" --vault "Agent Secrets" --fields label=${agentId}-token --reveal 2>/dev/null`,
692
+ { encoding: "utf8", timeout: 15e3 }
693
+ ).trim();
694
+ }
695
+ } catch {
696
+ }
697
+ if (!token && process.stdin.isTTY) {
698
+ const { createInterface } = await import("readline");
699
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
700
+ token = await new Promise((resolve) => {
701
+ rl.question(" Relay auth token: ", resolve);
702
+ });
703
+ rl.close();
704
+ token = token.trim();
705
+ }
706
+ if (token) {
707
+ mkdirSync(secretsDir, { recursive: true });
708
+ writeFileSync(envPath, `export CRYSTAL_RELAY_URL=${relayUrl}
709
+ export CRYSTAL_RELAY_TOKEN=${token}
710
+ export CRYSTAL_AGENT_ID=${agentId}
711
+ `);
712
+ process.env.CRYSTAL_RELAY_URL = relayUrl;
713
+ process.env.CRYSTAL_RELAY_TOKEN = token;
714
+ steps.push("Relay config written to ~/.ldm/secrets/crystal-relay.env");
715
+ const shellProfile = join(HOME, ".zshrc");
716
+ const sourceLine = `source ${envPath}`;
717
+ let alreadySourced = false;
718
+ try {
719
+ alreadySourced = readFileSync(shellProfile, "utf8").includes(sourceLine);
720
+ } catch {
721
+ }
722
+ if (!alreadySourced && process.stdin.isTTY) {
723
+ const { createInterface: createRL } = await import("readline");
724
+ const rl2 = createRL({ input: process.stdin, output: process.stdout });
725
+ const ans = await new Promise((resolve) => {
726
+ rl2.question(" Add relay config to ~/.zshrc? [Y/n] ", resolve);
727
+ });
728
+ rl2.close();
729
+ if (ans.trim().toLowerCase() !== "n") {
730
+ const { appendFileSync } = await import("fs");
731
+ appendFileSync(shellProfile, `
732
+ # Memory Crystal relay
733
+ ${sourceLine}
734
+ `);
735
+ steps.push("Added relay source to ~/.zshrc");
736
+ }
737
+ }
738
+ } else {
739
+ steps.push("No relay token. Set CRYSTAL_RELAY_TOKEN manually.");
740
+ }
741
+ } else {
742
+ steps.push("Relay config exists at ~/.ldm/secrets/crystal-relay.env");
743
+ }
744
+ }
649
745
  if (hasLdmCli) {
650
746
  steps.push('Tip: Run "ldm install" to see more components you can add.');
651
747
  } else if (!ldmDelegated) {
package/dist/ldm.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  saveAgentConfig,
14
14
  scaffoldLdm,
15
15
  stateWritePath
16
- } from "./chunk-EXEZZADG.js";
16
+ } from "./chunk-DFQ72B7M.js";
17
17
  export {
18
18
  deployBackupScript,
19
19
  deployCaptureScript,
@@ -0,0 +1,16 @@
1
+ import {
2
+ detectProvider,
3
+ expandQuery,
4
+ hasSampling,
5
+ rerankResults,
6
+ setLLMCacheDb,
7
+ setSamplingServer
8
+ } from "./chunk-NX647OM3.js";
9
+ export {
10
+ detectProvider,
11
+ expandQuery,
12
+ hasSampling,
13
+ rerankResults,
14
+ setLLMCacheDb,
15
+ setSamplingServer
16
+ };
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  setSamplingServer
4
- } from "./chunk-7IUE7ODU.js";
4
+ } from "./chunk-NX647OM3.js";
5
5
  import {
6
6
  RemoteCrystal,
7
7
  createCrystal,
8
8
  resolveConfig
9
- } from "./chunk-FBQWSDPC.js";
9
+ } from "./chunk-2GBYLMEF.js";
10
10
  import {
11
11
  resolveStatePath,
12
12
  stateWritePath
13
- } from "./chunk-EXEZZADG.js";
13
+ } from "./chunk-DFQ72B7M.js";
14
14
 
15
15
  // src/mcp-server.ts
16
16
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -63,7 +63,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
63
63
  limit: { type: "number", description: "Max results (default: 5)" },
64
64
  agent_id: { type: "string", description: 'Filter by agent (e.g. "main", "claude-code")' },
65
65
  time_filter: { type: "string", description: 'Only return results newer than this. Relative ("24h", "7d", "30d") or ISO date.' },
66
- quality: { type: "string", enum: ["fast", "deep"], description: 'Search quality mode. "fast" (default) uses hybrid search. "deep" adds LLM query expansion + re-ranking.' }
66
+ until: { type: "string", description: 'Only return results older than this. ISO date (e.g. "2026-03-25"). Use with time_filter for date ranges.' },
67
+ quality: { type: "string", enum: ["fast", "deep"], description: 'Search quality mode. "fast" (default) uses hybrid search. "deep" adds LLM query expansion + re-ranking.' },
68
+ intent: { type: "string", description: 'Disambiguate the query without adding search terms. E.g. query "security" + intent "1Password automation" steers toward 1Password results.' },
69
+ candidate_limit: { type: "number", description: "Number of candidates for LLM re-ranking (default: 40). More = better recall, slower." },
70
+ explain: { type: "boolean", description: "Return per-result scoring breakdown (FTS, vector, RRF, rerank, recency scores)." }
67
71
  },
68
72
  required: ["query"]
69
73
  }
@@ -141,6 +145,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
141
145
  const { name, arguments: args } = request.params;
142
146
  try {
143
147
  await crystal.init();
148
+ if (!isRemote && crystal.sqliteDb) {
149
+ const { setLLMCacheDb } = await import("./llm-XXLYPIOF.js");
150
+ setLLMCacheDb(crystal.sqliteDb);
151
+ }
144
152
  switch (name) {
145
153
  case "crystal_search": {
146
154
  const query = args?.query;
@@ -148,7 +156,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
148
156
  const filter = {};
149
157
  if (args?.agent_id) filter.agent_id = args.agent_id;
150
158
  if (args?.time_filter) filter.since = args.time_filter;
151
- const results = isRemote ? await crystal.search(query, limit, filter) : await crystal.deepSearch(query, limit, filter);
159
+ if (args?.until) filter.until = args.until;
160
+ const intent = args?.intent;
161
+ const candidateLimit = args?.candidate_limit;
162
+ const explain = args?.explain;
163
+ const results = isRemote ? await crystal.search(query, limit, filter) : await crystal.deepSearch(query, limit, filter, { intent, candidateLimit, explain });
152
164
  logSearchMetric("crystal_search", query, results.length);
153
165
  if (results.length === 0) {
154
166
  return { content: [{ type: "text", text: "No results found." }] };
package/dist/migrate.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  Crystal,
4
4
  resolveConfig
5
- } from "./chunk-FBQWSDPC.js";
5
+ } from "./chunk-2GBYLMEF.js";
6
6
 
7
7
  // src/migrate.ts
8
8
  import Database from "better-sqlite3";
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  pullFileSync
4
- } from "./chunk-3G3SFYYI.js";
4
+ } from "./chunk-CGIDSAJB.js";
5
5
  import {
6
6
  Crystal,
7
7
  resolveConfig
8
- } from "./chunk-FBQWSDPC.js";
8
+ } from "./chunk-2GBYLMEF.js";
9
9
  import {
10
10
  decryptJSON,
11
11
  loadRelayKey
12
- } from "./chunk-KCQUXVYT.js";
12
+ } from "./chunk-D3MACYZ4.js";
13
13
  import {
14
14
  ldmPaths,
15
15
  resolveStatePath,
16
16
  stateWritePath
17
- } from "./chunk-EXEZZADG.js";
17
+ } from "./chunk-DFQ72B7M.js";
18
18
 
19
19
  // src/mirror-sync.ts
20
20
  import { readFileSync, writeFileSync, existsSync } from "fs";