@cerefox/memory 1.0.3 → 1.0.5

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.
@@ -7184,7 +7184,7 @@ var exports_meta = {};
7184
7184
  __export(exports_meta, {
7185
7185
  PKG_VERSION: () => PKG_VERSION
7186
7186
  });
7187
- var PKG_VERSION = "1.0.3";
7187
+ var PKG_VERSION = "1.0.5";
7188
7188
  var init_meta = () => {};
7189
7189
 
7190
7190
  // ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
@@ -25225,6 +25225,13 @@ function getMinSearchScore() {
25225
25225
  const n = Number.parseFloat(raw);
25226
25226
  return Number.isNaN(n) || n < 0 || n > 1 ? fallback : n;
25227
25227
  }
25228
+ function getMinTermCoverage() {
25229
+ const raw = globalThis.process?.env?.CEREFOX_MIN_TERM_COVERAGE;
25230
+ if (raw === undefined || raw === "")
25231
+ return;
25232
+ const n = Number.parseFloat(raw);
25233
+ return Number.isNaN(n) || n < 0 || n > 1 ? undefined : n;
25234
+ }
25228
25235
  function applyByteBudget(rows, maxBytes) {
25229
25236
  const accepted = [];
25230
25237
  let usedBytes = 0;
@@ -55572,6 +55579,8 @@ async function handler9(supabase, args, ctx) {
55572
55579
  const mode = args.mode ?? "docs";
55573
55580
  const alpha = args.alpha ?? 0.7;
55574
55581
  const min_score = args.min_score ?? getMinSearchScore();
55582
+ const min_term_coverage = args.min_term_coverage ?? getMinTermCoverage();
55583
+ const coverageParam = min_term_coverage !== undefined ? { p_min_term_coverage: min_term_coverage } : {};
55575
55584
  const metadata_filter = args.metadata_filter ?? null;
55576
55585
  const requested_max_bytes = args.max_bytes;
55577
55586
  const ceiling = getMaxResponseBytes();
@@ -55603,7 +55612,8 @@ async function handler9(supabase, args, ctx) {
55603
55612
  p_query_text: query,
55604
55613
  p_match_count: match_count,
55605
55614
  p_project_id: projectId,
55606
- ...metaFilterParam
55615
+ ...metaFilterParam,
55616
+ ...coverageParam
55607
55617
  };
55608
55618
  } else if (mode === "hybrid") {
55609
55619
  rpcName = "cerefox_hybrid_search";
@@ -55615,7 +55625,8 @@ async function handler9(supabase, args, ctx) {
55615
55625
  p_use_upgrade: false,
55616
55626
  p_project_id: projectId,
55617
55627
  p_min_score: min_score,
55618
- ...metaFilterParam
55628
+ ...metaFilterParam,
55629
+ ...coverageParam
55619
55630
  };
55620
55631
  } else {
55621
55632
  rpcName = "cerefox_search_docs";
@@ -55626,7 +55637,8 @@ async function handler9(supabase, args, ctx) {
55626
55637
  p_alpha: alpha,
55627
55638
  p_project_id: projectId,
55628
55639
  p_min_score: min_score,
55629
- ...metaFilterParam
55640
+ ...metaFilterParam,
55641
+ ...coverageParam
55630
55642
  };
55631
55643
  }
55632
55644
  const { data, error: error2 } = await supabase.rpc(rpcName, rpcParams);
@@ -55699,6 +55711,23 @@ var init_search = __esm(() => {
55699
55711
  description: 'Optional JSONB containment filter. Only documents whose metadata contains ALL specified key-value pairs are returned. Example: {"type": "decision", "status": "active"}. Call cerefox_list_metadata_keys first to discover available keys and values. Omit to search all documents.',
55700
55712
  additionalProperties: { type: "string" }
55701
55713
  },
55714
+ mode: {
55715
+ type: "string",
55716
+ enum: ["docs", "hybrid", "fts", "semantic"],
55717
+ description: "Search mode (default: docs — full reconstructed documents). hybrid: ranked chunks; fts: keyword-only (no embedding); semantic: vector-only."
55718
+ },
55719
+ alpha: {
55720
+ type: "number",
55721
+ description: "Hybrid fusion weight 0–1 (default 0.7): 1 = pure semantic, 0 = pure keyword."
55722
+ },
55723
+ min_score: {
55724
+ type: "number",
55725
+ description: "Minimum cosine similarity for vector-side results (default: server-configured, 0.5 OpenAI / 0.6 local embedder)."
55726
+ },
55727
+ min_term_coverage: {
55728
+ type: "number",
55729
+ description: "Keyword OR-fallback confidence bar 0–1 (default 0.5): fraction of the query's meaningful terms a result must match to count as a confident hit; weaker matches return flagged below-confidence. 0 = any matching term. Needs schema ≥ 0.9.1."
55730
+ },
55702
55731
  max_bytes: {
55703
55732
  type: "integer",
55704
55733
  description: "Optional response size budget in bytes. Results are dropped whole until the budget is satisfied; a truncated flag is set when results are dropped. Defaults to the server maximum (200000). Pass a smaller value if your context window is limited. Values above the server maximum are silently capped."
@@ -69032,10 +69061,35 @@ init_cli_core();
69032
69061
 
69033
69062
  // src/cli/commands/backup.ts
69034
69063
  init_cli_core();
69035
- init_client();
69036
69064
  import { existsSync as existsSync2, mkdirSync, writeFileSync } from "node:fs";
69037
69065
  import { homedir as homedir2 } from "node:os";
69038
69066
  import { join as join2, resolve } from "node:path";
69067
+
69068
+ // ../../_shared/db-client/paginate.ts
69069
+ async function fetchAllPages(makeQuery, batchSize = 200) {
69070
+ const results = [];
69071
+ let offset = 0;
69072
+ let serverTotal = null;
69073
+ for (;; ) {
69074
+ const { data, error, count } = await makeQuery(offset, offset + batchSize - 1);
69075
+ if (error)
69076
+ throw new Error(error.message ?? JSON.stringify(error));
69077
+ if (typeof count === "number")
69078
+ serverTotal = count;
69079
+ const page = data ?? [];
69080
+ results.push(...page);
69081
+ if (page.length < batchSize)
69082
+ break;
69083
+ offset += batchSize;
69084
+ }
69085
+ if (serverTotal !== null && results.length !== serverTotal) {
69086
+ throw new Error(`Paginated read returned ${results.length} row(s) but the server reports ` + `${serverTotal}. Refusing to return a partial result — retry, and if this ` + `persists please report it (the rows may be changing under the read).`);
69087
+ }
69088
+ return results;
69089
+ }
69090
+
69091
+ // src/cli/commands/backup.ts
69092
+ init_client();
69039
69093
  function expandHome(path) {
69040
69094
  if (path === "~")
69041
69095
  return homedir2();
@@ -69056,21 +69110,25 @@ async function action(options) {
69056
69110
  const filename = `cerefox-${stamp}${options.label ? "-" + options.label : ""}.json`;
69057
69111
  const dest = join2(outDir, filename);
69058
69112
  const client = getClient();
69059
- const { data: docsData, error: docsErr } = await client.raw.from("cerefox_documents").select("id, title, content_hash, source, metadata, total_chars, chunk_count, " + "review_status, created_at, updated_at, deleted_at").is("deleted_at", null).order("created_at", { ascending: true });
69060
- if (docsErr)
69061
- throw systemError(`Document fetch failed: ${docsErr.message}`);
69062
- const docs = docsData ?? [];
69113
+ let docs;
69114
+ try {
69115
+ docs = await fetchAllPages((from, to) => client.raw.from("cerefox_documents").select("id, title, content_hash, source, metadata, total_chars, chunk_count, " + "review_status, created_at, updated_at, deleted_at").is("deleted_at", null).order("created_at", { ascending: true }).order("id", { ascending: true }).range(from, to));
69116
+ } catch (err) {
69117
+ throw systemError(`Document fetch failed: ${err instanceof Error ? err.message : String(err)}`);
69118
+ }
69063
69119
  let chunkTotal = 0;
69064
69120
  const enriched = [];
69065
69121
  for (let i = 0;i < docs.length; i++) {
69066
69122
  const doc = docs[i];
69067
69123
  const docId = doc.id;
69068
- const { data: chunks, error: chunkErr } = await client.raw.from("cerefox_chunks").select("*").eq("document_id", docId).is("version_id", null).order("chunk_index", { ascending: true });
69069
- if (chunkErr) {
69070
- throw systemError(`Chunk fetch failed for ${docId}: ${chunkErr.message}`);
69124
+ let chunks;
69125
+ try {
69126
+ chunks = await fetchAllPages((from, to) => client.raw.from("cerefox_chunks").select("*").eq("document_id", docId).is("version_id", null).order("chunk_index", { ascending: true }).range(from, to));
69127
+ } catch (err) {
69128
+ throw systemError(`Chunk fetch failed for ${docId}: ${err instanceof Error ? err.message : String(err)}`);
69071
69129
  }
69072
- chunkTotal += (chunks ?? []).length;
69073
- enriched.push({ ...doc, chunks: chunks ?? [] });
69130
+ chunkTotal += chunks.length;
69131
+ enriched.push({ ...doc, chunks });
69074
69132
  if (process.stdout.isTTY) {
69075
69133
  process.stderr.write(`\r Dumping documents: ${i + 1}/${docs.length} (${chunkTotal} chunks so far)…`);
69076
69134
  }
@@ -74948,8 +75006,8 @@ import { homedir as homedir6 } from "node:os";
74948
75006
  import { join as join9 } from "node:path";
74949
75007
 
74950
75008
  // ../../_shared/ef-meta/index.ts
74951
- var EF_VERSION = "1.0.3";
74952
- var EF_LAST_CHANGED = "1.0.3";
75009
+ var EF_VERSION = "1.0.5";
75010
+ var EF_LAST_CHANGED = "1.0.4";
74953
75011
 
74954
75012
  // src/cli/util/checks.ts
74955
75013
  init_config();
@@ -75475,9 +75533,9 @@ async function checkEdgeFunctionsCompat() {
75475
75533
  }
75476
75534
  return {
75477
75535
  name: "edge functions",
75478
- status: "skipped",
75479
- detail: `Deployed Edge Functions (v${deployed}) are older than the ones bundled with this client (v${EF_VERSION}). Compatible — nothing is broken — but redeploying ` + `picks up the latest server-side fixes.`,
75480
- hint: "Redeploy with: cerefox server deploy --functions-only"
75536
+ status: "warn",
75537
+ detail: `Deployed Edge Functions (v${deployed}) are older than this client's bundled v${EF_VERSION} and predate server-side fixes shipped in v${EF_LAST_CHANGED}. Still compatible, but you are missing those fixes.`,
75538
+ hint: "Update the Edge Functions (see remediation below)."
75481
75539
  };
75482
75540
  default:
75483
75541
  return {
@@ -75576,11 +75634,11 @@ async function action15(options) {
75576
75634
  const needsEf = stale("edge functions");
75577
75635
  let remediation = null;
75578
75636
  if (needsSchema && needsEf) {
75579
- remediation = "Update the server (schema + RPCs + Edge Functions): cerefox server deploy";
75637
+ remediation = "This release needs a server update (schema + RPCs + Edge Functions): cerefox server deploy";
75580
75638
  } else if (needsSchema) {
75581
- remediation = "Update the schema + RPCs: cerefox server deploy --schema-only";
75639
+ remediation = "This release needs a schema + RPC update: cerefox server deploy --schema-only";
75582
75640
  } else if (needsEf) {
75583
- remediation = "Update the Edge Functions: cerefox server deploy --functions-only";
75641
+ remediation = "This release needs an Edge Function update: cerefox server deploy --functions-only";
75584
75642
  }
75585
75643
  if (remediation) {
75586
75644
  println(cErr.yellow("→ " + remediation));
@@ -75807,7 +75865,7 @@ class IngestionDbBridge {
75807
75865
  return rows.length > 0 ? rows[0] : null;
75808
75866
  }
75809
75867
  async listChunksForDocument(documentId) {
75810
- const { data } = await this.supabase.from("cerefox_chunks").select("id, document_id, chunk_index, heading_path, heading_level, title, content, char_count").eq("document_id", documentId).is("version_id", null).order("chunk_index");
75868
+ const data = await fetchAllPages((from, to) => this.supabase.from("cerefox_chunks").select("id, document_id, chunk_index, heading_path, heading_level, title, content, char_count").eq("document_id", documentId).is("version_id", null).order("chunk_index").range(from, to));
75811
75869
  return data ?? [];
75812
75870
  }
75813
75871
  async getDocumentProjectIds(documentId) {
@@ -77239,18 +77297,22 @@ async function action27(options) {
77239
77297
  }
77240
77298
  const reindexAll = Boolean(options.all);
77241
77299
  const dryRun = Boolean(options.dryRun);
77242
- let query = supabase.from("cerefox_chunks").select("id, document_id, content, embedder_primary, cerefox_documents(title)").is("version_id", null);
77243
- if (options.documentId) {
77244
- query = query.eq("document_id", options.documentId);
77245
- }
77246
77300
  const targetModel = activeEmbedderName();
77247
- if (!reindexAll) {
77248
- query = query.neq("embedder_primary", targetModel);
77301
+ let chunks;
77302
+ try {
77303
+ chunks = await fetchAllPages((from, to) => {
77304
+ let query = supabase.from("cerefox_chunks").select("id, document_id, content, embedder_primary, cerefox_documents(title)").is("version_id", null);
77305
+ if (options.documentId) {
77306
+ query = query.eq("document_id", options.documentId);
77307
+ }
77308
+ if (!reindexAll) {
77309
+ query = query.neq("embedder_primary", targetModel);
77310
+ }
77311
+ return query.order("id", { ascending: true }).range(from, to);
77312
+ }, 1000);
77313
+ } catch (err) {
77314
+ throw systemError(`Failed to list chunks: ${err instanceof Error ? err.message : String(err)}`);
77249
77315
  }
77250
- const { data, error: error3 } = await query;
77251
- if (error3)
77252
- throw systemError(`Failed to list chunks: ${error3.message}`);
77253
- const chunks = data ?? [];
77254
77316
  if (chunks.length === 0) {
77255
77317
  println(c.dim("(nothing to reindex)"));
77256
77318
  return;
@@ -77420,6 +77482,8 @@ async function action29(query, options) {
77420
77482
  const matchCount = parsePositiveInt(options.matchCount, "--match-count", 5);
77421
77483
  const alpha = parseFloat01(options.alpha, "--alpha", 0.7);
77422
77484
  const minScore = parseFloat01(options.minScore, "--min-score", getMinSearchScore());
77485
+ const envCoverage = getMinTermCoverage();
77486
+ const coverageParam = options.minTermCoverage !== undefined ? { p_min_term_coverage: parseFloat01(options.minTermCoverage, "--min-term-coverage", envCoverage ?? 0.5) } : envCoverage !== undefined ? { p_min_term_coverage: envCoverage } : {};
77423
77487
  const maxBytes = parseNonNegativeInt(options.maxBytes, "--max-bytes", getMaxResponseBytes());
77424
77488
  const mode = options.mode ?? "docs";
77425
77489
  if (!["docs", "hybrid", "fts"].includes(mode)) {
@@ -77450,7 +77514,8 @@ async function action29(query, options) {
77450
77514
  p_query_text: query,
77451
77515
  p_match_count: matchCount,
77452
77516
  p_project_id: projectId,
77453
- ...metaFilterParam
77517
+ ...metaFilterParam,
77518
+ ...coverageParam
77454
77519
  };
77455
77520
  } else if (mode === "hybrid") {
77456
77521
  rpcName = "cerefox_hybrid_search";
@@ -77462,7 +77527,8 @@ async function action29(query, options) {
77462
77527
  p_use_upgrade: false,
77463
77528
  p_project_id: projectId,
77464
77529
  p_min_score: minScore,
77465
- ...metaFilterParam
77530
+ ...metaFilterParam,
77531
+ ...coverageParam
77466
77532
  };
77467
77533
  } else {
77468
77534
  rpcName = "cerefox_search_docs";
@@ -77473,7 +77539,8 @@ async function action29(query, options) {
77473
77539
  p_alpha: alpha,
77474
77540
  p_project_id: projectId,
77475
77541
  p_min_score: minScore,
77476
- ...metaFilterParam
77542
+ ...metaFilterParam,
77543
+ ...coverageParam
77477
77544
  };
77478
77545
  }
77479
77546
  const results = await client.rpc(rpcName, rpcParams);
@@ -77572,7 +77639,7 @@ async function action29(query, options) {
77572
77639
  }
77573
77640
  }
77574
77641
  function registerSearch(program2) {
77575
- program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE; else 0.5, or 0.6 with the local embedder).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").option("--only-metadata", "List matching docs (id, score, chunks, chars, partial/full) WITHOUT their content — like the web UI's collapsed result list. Grab a [id:…] then `cerefox document get <id>`.").action(action29);
77642
+ program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold (default: CEREFOX_MIN_SEARCH_SCORE; else 0.5, or 0.6 with the local embedder).").option("--min-term-coverage <float>", "OR-fallback keyword matches must cover at least this fraction of the query's meaningful terms to count as confident hits (default: CEREFOX_MIN_TERM_COVERAGE; else the server default 0.5; needs schema ≥ 0.9.1).").option("--max-bytes <n>", "Response size budget in bytes (default: CEREFOX_MAX_RESPONSE_BYTES or 200000).").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").option("--only-metadata", "List matching docs (id, score, chunks, chars, partial/full) WITHOUT their content — like the web UI's collapsed result list. Grab a [id:…] then `cerefox document get <id>`.").action(action29);
77576
77643
  }
77577
77644
 
77578
77645
  // src/cli/commands/self-update.ts
@@ -81390,23 +81457,18 @@ function parseMetadataFilter(raw2) {
81390
81457
  }
81391
81458
  async function listDocuments(ctx, opts) {
81392
81459
  const { projectId, limit, offset = 0 } = opts;
81393
- let docIds = null;
81394
- if (projectId) {
81395
- const { data: data2, error: error4 } = await ctx.supabase.from("cerefox_document_projects").select("document_id").eq("project_id", projectId);
81396
- if (error4)
81397
- throw error4;
81398
- docIds = (data2 ?? []).map((r) => r.document_id);
81399
- if (docIds.length === 0)
81400
- return [];
81401
- }
81402
- let q = ctx.supabase.from("cerefox_documents").select(DOC_COLS).is("deleted_at", null).order("updated_at", { ascending: false });
81403
- if (docIds)
81404
- q = q.in("id", docIds);
81460
+ const selectCols = projectId ? `${DOC_COLS}, cerefox_document_projects!inner(project_id)` : DOC_COLS;
81461
+ let q = ctx.supabase.from("cerefox_documents").select(selectCols).is("deleted_at", null).order("updated_at", { ascending: false });
81462
+ if (projectId)
81463
+ q = q.eq("cerefox_document_projects.project_id", projectId);
81405
81464
  q = q.range(offset, offset + limit - 1);
81406
81465
  const { data, error: error3 } = await q;
81407
81466
  if (error3)
81408
81467
  throw error3;
81409
- return data ?? [];
81468
+ return (data ?? []).map((row) => {
81469
+ const { cerefox_document_projects: _j, ...doc2 } = row;
81470
+ return doc2;
81471
+ });
81410
81472
  }
81411
81473
  async function listAllProjects(ctx) {
81412
81474
  const { data, error: error3 } = await ctx.supabase.from("cerefox_projects").select("*").order("name");
@@ -81444,10 +81506,8 @@ async function getProjectDocCounts(ctx, projectIds) {
81444
81506
  if (projectIds.length === 0)
81445
81507
  return { active, deleted };
81446
81508
  try {
81447
- const { data, error: error3 } = await ctx.supabase.from("cerefox_document_projects").select("project_id, cerefox_documents(deleted_at)").in("project_id", projectIds);
81448
- if (error3)
81449
- throw error3;
81450
- for (const row of data ?? []) {
81509
+ const rows = await fetchAllPages((from, to) => ctx.supabase.from("cerefox_document_projects").select("project_id, cerefox_documents(deleted_at)").in("project_id", projectIds).order("document_id", { ascending: true }).order("project_id", { ascending: true }).range(from, to));
81510
+ for (const row of rows) {
81451
81511
  const pid = row.project_id;
81452
81512
  if (!(pid in active))
81453
81513
  continue;
@@ -81491,16 +81551,13 @@ async function getRecentDocAuthors(ctx, docIds) {
81491
81551
  return out;
81492
81552
  }
81493
81553
  async function countDocumentsForProject(ctx, projectId) {
81494
- const { data, error: error3 } = await ctx.supabase.from("cerefox_document_projects").select("document_id, cerefox_documents(deleted_at)").eq("project_id", projectId);
81554
+ const { count, error: error3 } = await ctx.supabase.from("cerefox_document_projects").select("document_id, cerefox_documents!inner(deleted_at)", {
81555
+ count: "exact",
81556
+ head: true
81557
+ }).eq("project_id", projectId).is("cerefox_documents.deleted_at", null);
81495
81558
  if (error3)
81496
81559
  throw error3;
81497
- let n = 0;
81498
- for (const row of data ?? []) {
81499
- if (row.cerefox_documents && row.cerefox_documents.deleted_at === null) {
81500
- n += 1;
81501
- }
81502
- }
81503
- return n;
81560
+ return count ?? 0;
81504
81561
  }
81505
81562
  function dashboardDocFromRow(row, projectIds) {
81506
81563
  return {
@@ -81527,7 +81584,8 @@ function projectDocResult(r) {
81527
81584
  chunk_count: r.chunk_count ?? 0,
81528
81585
  total_chars: r.total_chars ?? 0,
81529
81586
  doc_updated_at: r.doc_updated_at ?? null,
81530
- is_partial: r.is_partial ?? false
81587
+ is_partial: r.is_partial ?? false,
81588
+ below_confidence: r.below_confidence ?? false
81531
81589
  };
81532
81590
  }
81533
81591
  function projectChunkResult(r) {
@@ -81543,7 +81601,8 @@ function projectChunkResult(r) {
81543
81601
  doc_title: r.doc_title ?? "",
81544
81602
  doc_source: r.doc_source ?? null,
81545
81603
  doc_project_ids: r.doc_project_ids ?? [],
81546
- doc_metadata: r.doc_metadata ?? {}
81604
+ doc_metadata: r.doc_metadata ?? {},
81605
+ below_confidence: r.below_confidence ?? false
81547
81606
  };
81548
81607
  }
81549
81608
  async function runSearch(ctx, opts) {
@@ -82023,7 +82082,7 @@ function registerDocumentReadRoutes(app, ctx) {
82023
82082
  });
82024
82083
  app.get("/api/v1/documents/:document_id/chunks", async (c2) => {
82025
82084
  const documentId = c2.req.param("document_id");
82026
- const { data, error: error3 } = await ctx.supabase.from("cerefox_chunks").select("id, document_id, chunk_index, heading_path, heading_level, title, content, char_count").eq("document_id", documentId).is("version_id", null).order("chunk_index");
82085
+ const { data, error: error3 } = await ctx.supabase.from("cerefox_chunks").select("id, document_id, chunk_index, heading_path, heading_level, title, content, char_count").eq("document_id", documentId).is("version_id", null).order("chunk_index").range(0, 4999);
82027
82086
  if (error3)
82028
82087
  return c2.json({ detail: error3.message }, 500);
82029
82088
  const rows = data ?? [];