crispy-recall 0.1.0 → 0.1.2

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.
@@ -5552,6 +5552,9 @@ function normalizePath(p) {
5552
5552
  return normalized;
5553
5553
  }
5554
5554
 
5555
+ // src/recall/message-ingest.ts
5556
+ init_log();
5557
+
5555
5558
  // src/adapters/claude/jsonl-reader.ts
5556
5559
  var fs = __toESM(require("fs"));
5557
5560
  init_log();
@@ -5962,8 +5965,7 @@ function emitFunctionCall(payload, base, outputIndex, _counter) {
5962
5965
  const outputRecord = outputIndex.get(callId);
5963
5966
  if (outputRecord) {
5964
5967
  const outputPayload = outputRecord.payload;
5965
- const rawOutput = outputPayload.output;
5966
- const { exitCode, body } = parseExecOutputHeader(rawOutput);
5968
+ const { exitCode, body } = parseExecOutputHeader(outputPayload.output);
5967
5969
  const isError = exitCode !== 0;
5968
5970
  const toolResult = {
5969
5971
  type: "tool_result",
@@ -6101,7 +6103,7 @@ function emitApplyPatch(callId, input, base, outputIndex) {
6101
6103
  return entries;
6102
6104
  }
6103
6105
  function buildCustomToolResult(parentUuid, callId, outputRecord, base) {
6104
- const rawOutput = outputRecord.payload.output;
6106
+ const rawOutput = coerceOutputText(outputRecord.payload.output);
6105
6107
  let content;
6106
6108
  let isError = false;
6107
6109
  try {
@@ -6182,7 +6184,7 @@ function emitOrphanedOutput(payload, subtype, base, outputIndex, _counter) {
6182
6184
  return [];
6183
6185
  if (!outputIndex.has(callId))
6184
6186
  return [];
6185
- const rawOutput = payload.output;
6187
+ const rawOutput = coerceOutputText(payload.output);
6186
6188
  let content;
6187
6189
  let isError = false;
6188
6190
  if (subtype === "function_call_output") {
@@ -6295,7 +6297,18 @@ function mapFunctionCall(name, args) {
6295
6297
  return { toolName: name, toolInput: args };
6296
6298
  }
6297
6299
  }
6298
- function parseExecOutputHeader(output) {
6300
+ function coerceOutputText(output) {
6301
+ if (typeof output === "string")
6302
+ return output;
6303
+ if (Array.isArray(output)) {
6304
+ return output.map(
6305
+ (item) => item && typeof item === "object" && typeof item.text === "string" ? item.text : ""
6306
+ ).filter(Boolean).join("\n");
6307
+ }
6308
+ return "";
6309
+ }
6310
+ function parseExecOutputHeader(rawOutput) {
6311
+ const output = coerceOutputText(rawOutput);
6299
6312
  if (!output)
6300
6313
  return { exitCode: 0, body: "" };
6301
6314
  const currentMatch = output.match(/Process exited with code (\d+)/);
@@ -6460,6 +6473,39 @@ async function ingestSessionMessages(sessionId, transcriptPath, vendor, options)
6460
6473
  }
6461
6474
  var MAX_EMBED_CHARS = 14e3;
6462
6475
  var MAX_EMBED_BATCH = 10;
6476
+ var SAFE_EMBED_CHARS = 6e3;
6477
+ async function embedRowsResilient(rows) {
6478
+ const { embedBatch: embedBatch2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
6479
+ const { quantizeToQ8: quantizeToQ82, computeNorm: computeNorm2 } = await Promise.resolve().then(() => (init_quantize(), quantize_exports));
6480
+ const toRecord = (messageId, f32) => {
6481
+ const { q8, scale } = quantizeToQ82(f32);
6482
+ return { messageId, embeddingQ8: q8, norm: computeNorm2(f32), quantScale: scale };
6483
+ };
6484
+ try {
6485
+ const vectors = await embedBatch2(rows.map((r) => r.text));
6486
+ return rows.map((r, j) => toRecord(r.messageId, vectors[j]));
6487
+ } catch {
6488
+ const records = [];
6489
+ for (const r of rows) {
6490
+ try {
6491
+ const [v] = await embedBatch2([r.text]);
6492
+ records.push(toRecord(r.messageId, v));
6493
+ } catch {
6494
+ try {
6495
+ const [v] = await embedBatch2([r.text.slice(0, SAFE_EMBED_CHARS)]);
6496
+ records.push(toRecord(r.messageId, v));
6497
+ } catch (err) {
6498
+ log({
6499
+ source: "recall:embed",
6500
+ level: "warn",
6501
+ summary: `skipped message ${r.messageId} (embed failed even at ${SAFE_EMBED_CHARS} chars): ${err.message}`
6502
+ });
6503
+ }
6504
+ }
6505
+ }
6506
+ return records;
6507
+ }
6508
+ }
6463
6509
  async function embedSessionMessages(sessionId, force) {
6464
6510
  const d = getDb(dbPath());
6465
6511
  const rows = d.all(
@@ -6484,22 +6530,7 @@ async function embedSessionMessages(sessionId, force) {
6484
6530
  if (validRows.length > MAX_EMBED_BATCH) {
6485
6531
  validRows.length = MAX_EMBED_BATCH;
6486
6532
  }
6487
- const { embedBatch: embedBatch2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
6488
- const { quantizeToQ8: quantizeToQ82, computeNorm: computeNorm2 } = await Promise.resolve().then(() => (init_quantize(), quantize_exports));
6489
- const texts = validRows.map((r) => r.text);
6490
- const vectors = await embedBatch2(texts);
6491
- const records = [];
6492
- for (let j = 0; j < validRows.length; j++) {
6493
- const f32 = vectors[j];
6494
- const { q8, scale } = quantizeToQ82(f32);
6495
- const norm = computeNorm2(f32);
6496
- records.push({
6497
- messageId: validRows[j].messageId,
6498
- embeddingQ8: q8,
6499
- norm,
6500
- quantScale: scale
6501
- });
6502
- }
6533
+ const records = await embedRowsResilient(validRows);
6503
6534
  insertMessageVectors(records);
6504
6535
  return records.length;
6505
6536
  }
@@ -6518,22 +6549,7 @@ async function embedMessageBatch(messages) {
6518
6549
  }
6519
6550
  if (truncated.length === 0)
6520
6551
  return 0;
6521
- const { embedBatch: embedBatch2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
6522
- const { quantizeToQ8: quantizeToQ82, computeNorm: computeNorm2 } = await Promise.resolve().then(() => (init_quantize(), quantize_exports));
6523
- const texts = truncated.map((r) => r.text);
6524
- const vectors = await embedBatch2(texts);
6525
- const records = [];
6526
- for (let j = 0; j < truncated.length; j++) {
6527
- const f32 = vectors[j];
6528
- const { q8, scale } = quantizeToQ82(f32);
6529
- const norm = computeNorm2(f32);
6530
- records.push({
6531
- messageId: truncated[j].messageId,
6532
- embeddingQ8: q8,
6533
- norm,
6534
- quantScale: scale
6535
- });
6536
- }
6552
+ const records = await embedRowsResilient(truncated);
6537
6553
  insertMessageVectors(records);
6538
6554
  return records.length;
6539
6555
  }
package/dist/recall.js CHANGED
@@ -13157,8 +13157,7 @@ function emitFunctionCall(payload, base, outputIndex, _counter) {
13157
13157
  const outputRecord = outputIndex.get(callId);
13158
13158
  if (outputRecord) {
13159
13159
  const outputPayload = outputRecord.payload;
13160
- const rawOutput = outputPayload.output;
13161
- const { exitCode, body } = parseExecOutputHeader(rawOutput);
13160
+ const { exitCode, body } = parseExecOutputHeader(outputPayload.output);
13162
13161
  const isError = exitCode !== 0;
13163
13162
  const toolResult = {
13164
13163
  type: "tool_result",
@@ -13296,7 +13295,7 @@ function emitApplyPatch(callId, input, base, outputIndex) {
13296
13295
  return entries;
13297
13296
  }
13298
13297
  function buildCustomToolResult(parentUuid, callId, outputRecord, base) {
13299
- const rawOutput = outputRecord.payload.output;
13298
+ const rawOutput = coerceOutputText(outputRecord.payload.output);
13300
13299
  let content;
13301
13300
  let isError = false;
13302
13301
  try {
@@ -13377,7 +13376,7 @@ function emitOrphanedOutput(payload, subtype, base, outputIndex, _counter) {
13377
13376
  return [];
13378
13377
  if (!outputIndex.has(callId))
13379
13378
  return [];
13380
- const rawOutput = payload.output;
13379
+ const rawOutput = coerceOutputText(payload.output);
13381
13380
  let content;
13382
13381
  let isError = false;
13383
13382
  if (subtype === "function_call_output") {
@@ -13490,7 +13489,18 @@ function mapFunctionCall(name, args) {
13490
13489
  return { toolName: name, toolInput: args };
13491
13490
  }
13492
13491
  }
13493
- function parseExecOutputHeader(output) {
13492
+ function coerceOutputText(output) {
13493
+ if (typeof output === "string")
13494
+ return output;
13495
+ if (Array.isArray(output)) {
13496
+ return output.map(
13497
+ (item) => item && typeof item === "object" && typeof item.text === "string" ? item.text : ""
13498
+ ).filter(Boolean).join("\n");
13499
+ }
13500
+ return "";
13501
+ }
13502
+ function parseExecOutputHeader(rawOutput) {
13503
+ const output = coerceOutputText(rawOutput);
13494
13504
  if (!output)
13495
13505
  return { exitCode: 0, body: "" };
13496
13506
  const currentMatch = output.match(/Process exited with code (\d+)/);
@@ -13658,6 +13668,38 @@ async function ingestSessionMessages(sessionId, transcriptPath, vendor, options)
13658
13668
  skipped: false
13659
13669
  };
13660
13670
  }
13671
+ async function embedRowsResilient(rows) {
13672
+ const { embedBatch: embedBatch2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
13673
+ const { quantizeToQ8: quantizeToQ82, computeNorm: computeNorm2 } = await Promise.resolve().then(() => (init_quantize(), quantize_exports));
13674
+ const toRecord = (messageId, f32) => {
13675
+ const { q8, scale } = quantizeToQ82(f32);
13676
+ return { messageId, embeddingQ8: q8, norm: computeNorm2(f32), quantScale: scale };
13677
+ };
13678
+ try {
13679
+ const vectors = await embedBatch2(rows.map((r2) => r2.text));
13680
+ return rows.map((r2, j2) => toRecord(r2.messageId, vectors[j2]));
13681
+ } catch {
13682
+ const records = [];
13683
+ for (const r2 of rows) {
13684
+ try {
13685
+ const [v2] = await embedBatch2([r2.text]);
13686
+ records.push(toRecord(r2.messageId, v2));
13687
+ } catch {
13688
+ try {
13689
+ const [v2] = await embedBatch2([r2.text.slice(0, SAFE_EMBED_CHARS)]);
13690
+ records.push(toRecord(r2.messageId, v2));
13691
+ } catch (err) {
13692
+ log({
13693
+ source: "recall:embed",
13694
+ level: "warn",
13695
+ summary: `skipped message ${r2.messageId} (embed failed even at ${SAFE_EMBED_CHARS} chars): ${err.message}`
13696
+ });
13697
+ }
13698
+ }
13699
+ }
13700
+ return records;
13701
+ }
13702
+ }
13661
13703
  async function embedMessageBatch(messages) {
13662
13704
  if (messages.length === 0)
13663
13705
  return 0;
@@ -13673,26 +13715,11 @@ async function embedMessageBatch(messages) {
13673
13715
  }
13674
13716
  if (truncated.length === 0)
13675
13717
  return 0;
13676
- const { embedBatch: embedBatch2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
13677
- const { quantizeToQ8: quantizeToQ82, computeNorm: computeNorm2 } = await Promise.resolve().then(() => (init_quantize(), quantize_exports));
13678
- const texts = truncated.map((r2) => r2.text);
13679
- const vectors = await embedBatch2(texts);
13680
- const records = [];
13681
- for (let j2 = 0; j2 < truncated.length; j2++) {
13682
- const f32 = vectors[j2];
13683
- const { q8, scale } = quantizeToQ82(f32);
13684
- const norm = computeNorm2(f32);
13685
- records.push({
13686
- messageId: truncated[j2].messageId,
13687
- embeddingQ8: q8,
13688
- norm,
13689
- quantScale: scale
13690
- });
13691
- }
13718
+ const records = await embedRowsResilient(truncated);
13692
13719
  insertMessageVectors(records);
13693
13720
  return records.length;
13694
13721
  }
13695
- var MAX_EMBED_CHARS;
13722
+ var MAX_EMBED_CHARS, SAFE_EMBED_CHARS;
13696
13723
  var init_message_ingest = __esm({
13697
13724
  "src/recall/message-ingest.ts"() {
13698
13725
  "use strict";
@@ -13701,11 +13728,13 @@ var init_message_ingest = __esm({
13701
13728
  init_db();
13702
13729
  init_paths();
13703
13730
  init_url_path_resolver();
13731
+ init_log();
13704
13732
  init_jsonl_reader();
13705
13733
  init_claude_entry_adapter();
13706
13734
  init_codex_jsonl_reader();
13707
13735
  init_codex_jsonl_adapter();
13708
13736
  MAX_EMBED_CHARS = 14e3;
13737
+ SAFE_EMBED_CHARS = 6e3;
13709
13738
  }
13710
13739
  });
13711
13740
 
@@ -14924,8 +14953,8 @@ function cudaAssetUrl() {
14924
14953
  const tag = version ? `download/v${version}` : "latest/download";
14925
14954
  return `https://github.com/${owner}/${repo}/releases/${tag}/${CUDA_ASSET_NAME}`;
14926
14955
  }
14927
- function binCudaDir() {
14928
- return (0, import_node_path9.join)(recallRoot(), "bin-cuda");
14956
+ function cudaBackendLib() {
14957
+ return (0, import_node_path9.join)(binDir(), "libggml-cuda.so");
14929
14958
  }
14930
14959
  async function readVram() {
14931
14960
  try {
@@ -14957,7 +14986,7 @@ async function detectGpu(opts = {}) {
14957
14986
  if (p === "win32" && a3 === "x64") {
14958
14987
  return { ...base, cudaAvailable: "prebuilt", plannedMode: "gpu" };
14959
14988
  }
14960
- if (p === "linux" && a3 === "x64" && (0, import_node_fs9.existsSync)(binCudaDir())) {
14989
+ if (p === "linux" && a3 === "x64" && (0, import_node_fs9.existsSync)(cudaBackendLib())) {
14961
14990
  return { ...base, cudaAvailable: "reuse-existing", plannedMode: "gpu" };
14962
14991
  }
14963
14992
  if (p === "linux" && a3 === "x64") {
@@ -14966,10 +14995,7 @@ async function detectGpu(opts = {}) {
14966
14995
  return { ...base, cudaAvailable: "none", plannedMode: "cpu" };
14967
14996
  }
14968
14997
  function stderrIndicatesOffload(stderr) {
14969
- if (/ggml_cuda_init/.test(stderr))
14970
- return true;
14971
- const m2 = /offloaded\s+(\d+)\s*\/\s*\d+\s+layers?\s+to\s+GPU/i.exec(stderr);
14972
- return m2 ? Number(m2[1]) > 0 : false;
14998
+ return /ggml_cuda_init|found\s+\d+\s+CUDA\s+devices|loaded CUDA backend|using device CUDA|CUDA0[^\n]*buffer/i.test(stderr);
14973
14999
  }
14974
15000
  async function defaultProbe(args) {
14975
15001
  const { binaryPath: binaryPath2, modelPath, libDir, ngl, platform: platform2 } = args;
@@ -15044,7 +15070,7 @@ async function runGpuPhase(opts = {}) {
15044
15070
  return { mode, libDir: libDir2, ngl: mode === "gpu" ? DEFAULT_NGL : 0, cudaAvailable: info.cudaAvailable, ...reason ? { reason } : {} };
15045
15071
  };
15046
15072
  if (info.plannedMode === "cpu") {
15047
- const reason = info.detected && info.vendor === "nvidia" ? "NVIDIA GPU detected but no usable CUDA libs (no prebuilt for this platform; no ~/.recall/bin-cuda/ build present)" : void 0;
15073
+ const reason = info.detected && info.vendor === "nvidia" ? "NVIDIA GPU detected but no usable CUDA libs (no prebuilt for this platform; no ~/.recall/bin/libggml-cuda.so present)" : void 0;
15048
15074
  return persist("cpu", null, reason);
15049
15075
  }
15050
15076
  if (info.cudaAvailable === "metal") {
@@ -15052,7 +15078,7 @@ async function runGpuPhase(opts = {}) {
15052
15078
  }
15053
15079
  let libDir;
15054
15080
  if (info.cudaAvailable === "reuse-existing") {
15055
- libDir = binCudaDir();
15081
+ libDir = binDir();
15056
15082
  } else if (info.cudaAvailable === "prebuilt" && p === "win32") {
15057
15083
  libDir = binDir();
15058
15084
  } else if (info.cudaAvailable === "prebuilt") {
@@ -15087,8 +15113,8 @@ async function runGpuPhase(opts = {}) {
15087
15113
  }
15088
15114
  }
15089
15115
  async function defaultStage(args) {
15090
- const target = binCudaDir();
15091
- const libPath = (0, import_node_path9.join)(target, "libggml-cuda.so");
15116
+ const target = binDir();
15117
+ const libPath = cudaBackendLib();
15092
15118
  if ((0, import_node_fs9.existsSync)(libPath))
15093
15119
  return target;
15094
15120
  if (args.offline)
@@ -15607,12 +15633,18 @@ var init_claudemd_nudge = __esm({
15607
15633
  // src/installer/install.ts
15608
15634
  var install_exports = {};
15609
15635
  __export(install_exports, {
15636
+ defaultDistDir: () => defaultDistDir,
15610
15637
  runInstall: () => runInstall
15611
15638
  });
15612
15639
  function defaultDistDir() {
15613
15640
  const argv1 = process.argv[1];
15614
- if (argv1)
15615
- return (0, import_node_path11.dirname)(argv1);
15641
+ if (argv1) {
15642
+ try {
15643
+ return (0, import_node_path11.dirname)((0, import_node_fs12.realpathSync)(argv1));
15644
+ } catch {
15645
+ return (0, import_node_path11.dirname)(argv1);
15646
+ }
15647
+ }
15616
15648
  return __dirname;
15617
15649
  }
15618
15650
  function resolveTemplatePath(explicit) {
package/dist/stop-hook.js CHANGED
@@ -1807,6 +1807,9 @@ function normalizePath(p) {
1807
1807
  return normalized;
1808
1808
  }
1809
1809
 
1810
+ // src/recall/message-ingest.ts
1811
+ init_log();
1812
+
1810
1813
  // src/adapters/claude/jsonl-reader.ts
1811
1814
  var fs = __toESM(require("fs"));
1812
1815
  init_log();
@@ -2217,8 +2220,7 @@ function emitFunctionCall(payload, base, outputIndex, _counter) {
2217
2220
  const outputRecord = outputIndex.get(callId);
2218
2221
  if (outputRecord) {
2219
2222
  const outputPayload = outputRecord.payload;
2220
- const rawOutput = outputPayload.output;
2221
- const { exitCode, body } = parseExecOutputHeader(rawOutput);
2223
+ const { exitCode, body } = parseExecOutputHeader(outputPayload.output);
2222
2224
  const isError = exitCode !== 0;
2223
2225
  const toolResult = {
2224
2226
  type: "tool_result",
@@ -2356,7 +2358,7 @@ function emitApplyPatch(callId, input, base, outputIndex) {
2356
2358
  return entries;
2357
2359
  }
2358
2360
  function buildCustomToolResult(parentUuid, callId, outputRecord, base) {
2359
- const rawOutput = outputRecord.payload.output;
2361
+ const rawOutput = coerceOutputText(outputRecord.payload.output);
2360
2362
  let content;
2361
2363
  let isError = false;
2362
2364
  try {
@@ -2437,7 +2439,7 @@ function emitOrphanedOutput(payload, subtype, base, outputIndex, _counter) {
2437
2439
  return [];
2438
2440
  if (!outputIndex.has(callId))
2439
2441
  return [];
2440
- const rawOutput = payload.output;
2442
+ const rawOutput = coerceOutputText(payload.output);
2441
2443
  let content;
2442
2444
  let isError = false;
2443
2445
  if (subtype === "function_call_output") {
@@ -2550,7 +2552,18 @@ function mapFunctionCall(name, args) {
2550
2552
  return { toolName: name, toolInput: args };
2551
2553
  }
2552
2554
  }
2553
- function parseExecOutputHeader(output) {
2555
+ function coerceOutputText(output) {
2556
+ if (typeof output === "string")
2557
+ return output;
2558
+ if (Array.isArray(output)) {
2559
+ return output.map(
2560
+ (item) => item && typeof item === "object" && typeof item.text === "string" ? item.text : ""
2561
+ ).filter(Boolean).join("\n");
2562
+ }
2563
+ return "";
2564
+ }
2565
+ function parseExecOutputHeader(rawOutput) {
2566
+ const output = coerceOutputText(rawOutput);
2554
2567
  if (!output)
2555
2568
  return { exitCode: 0, body: "" };
2556
2569
  const currentMatch = output.match(/Process exited with code (\d+)/);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crispy-recall",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Local session transcript memory for Claude Code and Codex — search past sessions with FTS5 + semantic vectors.",
5
5
  "license": "MIT",
6
6
  "author": "Sylvester Wong",