akm-cli 0.9.15-beta.1 → 0.9.15-beta.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.
@@ -35703,7 +35703,7 @@ var require_libvips = __commonJS((exports, module) => {
35703
35703
  SPDX-License-Identifier: Apache-2.0
35704
35704
  */
35705
35705
  var { spawnSync: spawnSync6 } = __require("child_process");
35706
- var { createHash: createHash10 } = __require("crypto");
35706
+ var { createHash: createHash11 } = __require("crypto");
35707
35707
  var semverCoerce = require_coerce2();
35708
35708
  var semverGreaterThanOrEqualTo = require_gte2();
35709
35709
  var semverSatisfies = require_satisfies2();
@@ -35791,7 +35791,7 @@ var require_libvips = __commonJS((exports, module) => {
35791
35791
  }
35792
35792
  return false;
35793
35793
  };
35794
- var sha512 = (s) => createHash10("sha512").update(s).digest("hex");
35794
+ var sha512 = (s) => createHash11("sha512").update(s).digest("hex");
35795
35795
  var yarnLocator = () => {
35796
35796
  try {
35797
35797
  const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
@@ -75245,11 +75245,14 @@ var EmbeddingConnectionConfigSchema = exports_external.object({
75245
75245
  apiKey: symbolicOrWarnApiKey("embedding.apiKey").optional(),
75246
75246
  dimension: positiveInt.max(4096).optional(),
75247
75247
  localModel: exports_external.string().min(1).optional(),
75248
+ maxInputTokens: positiveInt.optional(),
75248
75249
  maxTokens: positiveInt.optional(),
75249
75250
  batchSize: positiveInt.optional(),
75250
75251
  chunkSize: positiveInt.optional(),
75251
75252
  contextLength: positiveInt.optional(),
75252
- ollamaOptions: EmbeddingOllamaOptionsSchema.optional()
75253
+ ollamaOptions: EmbeddingOllamaOptionsSchema.optional(),
75254
+ timeoutMs: positiveInt.optional(),
75255
+ concurrency: positiveInt.max(16).optional()
75253
75256
  }).passthrough();
75254
75257
 
75255
75258
  // src/integrations/harnesses/ids.ts
@@ -76322,8 +76325,21 @@ function releaseLockRaw(lockPath) {
76322
76325
  function tryAcquireLockSync(lockPath, payload) {
76323
76326
  return withLockOperationMutex(lockPath, () => tryAcquireLockRaw(lockPath, payload));
76324
76327
  }
76328
+ function launcherPidFromEnv() {
76329
+ const raw = process.env.AKM_LAUNCHER_PID;
76330
+ if (!raw)
76331
+ return;
76332
+ const pid = Number.parseInt(raw, 10);
76333
+ return Number.isInteger(pid) && pid > 0 ? pid : undefined;
76334
+ }
76325
76335
  function createLockPayload(metadata = {}) {
76326
- return JSON.stringify({ ...metadata, pid: process.pid, lockId: randomUUID() });
76336
+ const launcherPid = launcherPidFromEnv();
76337
+ return JSON.stringify({
76338
+ ...metadata,
76339
+ pid: process.pid,
76340
+ ...launcherPid !== undefined ? { launcherPid } : {},
76341
+ lockId: randomUUID()
76342
+ });
76327
76343
  }
76328
76344
  function probeLock(lockPath, opts) {
76329
76345
  let snapshot;
@@ -76339,17 +76355,17 @@ function probeLock(lockPath, opts) {
76339
76355
  return { state: "absent" };
76340
76356
  const { rawContent, identity: identity3 } = snapshot;
76341
76357
  const ageMs = Date.now() - identity3.mtimeMs;
76342
- const holderPid = extractHolderPid(rawContent);
76358
+ const { holderPid, launcherPid } = extractLockIdentity(rawContent);
76343
76359
  if (holderPid === undefined) {
76344
76360
  return { state: "stale", reason: "invalid_pid", ageMs, rawContent, identity: identity3 };
76345
76361
  }
76346
76362
  if (!isProcessAlive(holderPid)) {
76347
- return { state: "stale", reason: "pid_dead", holderPid, ageMs, rawContent, identity: identity3 };
76363
+ return { state: "stale", reason: "pid_dead", holderPid, launcherPid, ageMs, rawContent, identity: identity3 };
76348
76364
  }
76349
76365
  if (opts?.staleAfterMs !== undefined && ageMs > opts.staleAfterMs) {
76350
- return { state: "stale", reason: "age_exceeded", holderPid, ageMs, rawContent, identity: identity3 };
76366
+ return { state: "stale", reason: "age_exceeded", holderPid, launcherPid, ageMs, rawContent, identity: identity3 };
76351
76367
  }
76352
- return { state: "held", holderPid, ageMs, rawContent, identity: identity3 };
76368
+ return { state: "held", holderPid, launcherPid, ageMs, rawContent, identity: identity3 };
76353
76369
  }
76354
76370
  function reclaimStaleLock(lockPath, probe, options) {
76355
76371
  if (probe.rawContent === undefined || probe.identity === undefined)
@@ -76417,21 +76433,25 @@ function releaseLock(ownership) {
76417
76433
  }
76418
76434
  });
76419
76435
  }
76420
- function extractHolderPid(content) {
76436
+ function extractLockIdentity(content) {
76421
76437
  const trimmed = content.trim();
76422
76438
  if (!trimmed)
76423
- return;
76439
+ return {};
76424
76440
  if (trimmed.startsWith("{")) {
76425
76441
  try {
76426
76442
  const parsed = JSON.parse(trimmed);
76427
76443
  const pid2 = typeof parsed.pid === "number" ? parsed.pid : Number.NaN;
76428
- return Number.isInteger(pid2) && pid2 > 0 ? pid2 : undefined;
76444
+ const rawLauncherPid = typeof parsed.launcherPid === "number" ? parsed.launcherPid : Number.NaN;
76445
+ return {
76446
+ holderPid: Number.isInteger(pid2) && pid2 > 0 ? pid2 : undefined,
76447
+ launcherPid: Number.isInteger(rawLauncherPid) && rawLauncherPid > 0 ? rawLauncherPid : undefined
76448
+ };
76429
76449
  } catch {
76430
- return;
76450
+ return {};
76431
76451
  }
76432
76452
  }
76433
76453
  const pid = Number.parseInt(trimmed, 10);
76434
- return Number.isInteger(pid) && pid > 0 ? pid : undefined;
76454
+ return { holderPid: Number.isInteger(pid) && pid > 0 ? pid : undefined };
76435
76455
  }
76436
76456
 
76437
76457
  // src/core/config/config-io.ts
@@ -95693,7 +95713,7 @@ function listTxnJournalsTolerant(predicate) {
95693
95713
  }
95694
95714
 
95695
95715
  // src/commands/proposal/repository.ts
95696
- import { createHash as createHash10, randomUUID as randomUUID6 } from "crypto";
95716
+ import { createHash as createHash11, randomUUID as randomUUID6 } from "crypto";
95697
95717
  import fs55 from "fs";
95698
95718
  init_dist();
95699
95719
  import path66 from "path";
@@ -95759,6 +95779,16 @@ import fs53 from "fs";
95759
95779
  import path64 from "path";
95760
95780
  init_errors();
95761
95781
  init_paths();
95782
+
95783
+ // src/core/run-lock.ts
95784
+ init_errors();
95785
+ function formatLockHolderPid(holder) {
95786
+ if (holder.pid === null)
95787
+ return "unknown";
95788
+ return holder.launcherPid !== null ? `${holder.pid} (launcher ${holder.launcherPid})` : String(holder.pid);
95789
+ }
95790
+
95791
+ // src/indexer/index-written-assets.ts
95762
95792
  init_warn();
95763
95793
 
95764
95794
  // src/storage/repositories/index-connection.ts
@@ -96013,6 +96043,15 @@ function isCanonicalIndexGeneration(db) {
96013
96043
  init_errors();
96014
96044
  init_warn();
96015
96045
 
96046
+ // src/storage/repositories/embedding-salvage-repository.ts
96047
+ import { createHash as createHash9 } from "crypto";
96048
+
96049
+ // src/storage/repositories/embeddings-repository.ts
96050
+ function blobToEmbedding(blob) {
96051
+ const f32 = new Float32Array(blob.buffer, blob.byteOffset, blob.byteLength / 4);
96052
+ return Array.from(f32);
96053
+ }
96054
+
96016
96055
  // src/storage/repositories/index-meta-repository.ts
96017
96056
  function getMeta(db, key) {
96018
96057
  const row = db.prepare("SELECT value FROM index_meta WHERE key = ?").get(key);
@@ -96025,6 +96064,64 @@ function deleteMeta(db, key) {
96025
96064
  db.prepare("DELETE FROM index_meta WHERE key = ?").run(key);
96026
96065
  }
96027
96066
 
96067
+ // src/storage/repositories/index-sql.ts
96068
+ var SQLITE_CHUNK_SIZE = 500;
96069
+
96070
+ // src/storage/repositories/embedding-salvage-repository.ts
96071
+ function hashEmbeddableText(searchText) {
96072
+ return createHash9("sha256").update(searchText, "utf8").digest("hex");
96073
+ }
96074
+ function purgeEmbeddingSalvage(db) {
96075
+ db.exec("DELETE FROM embedding_salvage");
96076
+ }
96077
+ function relabelEmbeddingSalvageFingerprint(db, fromFingerprint, toFingerprint) {
96078
+ db.prepare("UPDATE embedding_salvage SET fingerprint = ? WHERE fingerprint = ?").run(toFingerprint, fromFingerprint);
96079
+ }
96080
+ function reuseSalvagedEmbeddings(db, entries, fingerprint, writeReused) {
96081
+ if (entries.length === 0)
96082
+ return { reusedCount: 0, remaining: [] };
96083
+ const anySalvageForFingerprint = db.prepare("SELECT 1 FROM embedding_salvage WHERE fingerprint = ? LIMIT 1").get(fingerprint);
96084
+ if (!anySalvageForFingerprint)
96085
+ return { reusedCount: 0, remaining: [...entries] };
96086
+ const hashes = entries.map((entry) => hashEmbeddableText(entry.searchText));
96087
+ const salvageByHash = new Map;
96088
+ const uniqueHashes = [...new Set(hashes)];
96089
+ for (let offset = 0;offset < uniqueHashes.length; offset += SQLITE_CHUNK_SIZE) {
96090
+ const chunk = uniqueHashes.slice(offset, offset + SQLITE_CHUNK_SIZE);
96091
+ const placeholders = chunk.map(() => "?").join(",");
96092
+ const rows = db.prepare(`SELECT content_hash AS contentHash, embedding FROM embedding_salvage WHERE fingerprint = ? AND content_hash IN (${placeholders})`).all(fingerprint, ...chunk);
96093
+ for (const row of rows)
96094
+ salvageByHash.set(row.contentHash, row.embedding);
96095
+ }
96096
+ if (salvageByHash.size === 0)
96097
+ return { reusedCount: 0, remaining: [...entries] };
96098
+ let reusedCount = 0;
96099
+ const remaining = [];
96100
+ for (let offset = 0;offset < entries.length; offset += SQLITE_CHUNK_SIZE) {
96101
+ const end = Math.min(offset + SQLITE_CHUNK_SIZE, entries.length);
96102
+ const chunkMatches = [];
96103
+ for (let i = offset;i < end; i++) {
96104
+ const entry = entries[i];
96105
+ const blob = salvageByHash.get(hashes[i]);
96106
+ if (blob)
96107
+ chunkMatches.push({ entry, blob });
96108
+ else
96109
+ remaining.push(entry);
96110
+ }
96111
+ if (chunkMatches.length === 0)
96112
+ continue;
96113
+ db.transaction(() => {
96114
+ for (const { entry, blob } of chunkMatches) {
96115
+ if (writeReused(entry, blobToEmbedding(blob)))
96116
+ reusedCount++;
96117
+ else
96118
+ remaining.push(entry);
96119
+ }
96120
+ })();
96121
+ }
96122
+ return { reusedCount, remaining };
96123
+ }
96124
+
96028
96125
  // src/storage/repositories/index-vec-repository.ts
96029
96126
  import { createRequire as createRequire3 } from "module";
96030
96127
 
@@ -96066,9 +96163,6 @@ function cosineSimilarity(a, b) {
96066
96163
  return denom === 0 ? 0 : dot / denom;
96067
96164
  }
96068
96165
 
96069
- // src/storage/repositories/index-sql.ts
96070
- var SQLITE_CHUNK_SIZE = 500;
96071
-
96072
96166
  // src/storage/repositories/index-vec-repository.ts
96073
96167
  var vecStatus = new WeakMap;
96074
96168
  function loadVecExtension(db) {
@@ -96328,11 +96422,11 @@ init_warn();
96328
96422
 
96329
96423
  // src/core/asset/markdown-fragments.ts
96330
96424
  init_markdown();
96331
- import { createHash as createHash9 } from "crypto";
96425
+ import { createHash as createHash10 } from "crypto";
96332
96426
  var MARKDOWN_FRAGMENT_MAX_CHARS = 1600;
96333
96427
  var MARKDOWN_FRAGMENT_PREFIX = "akm-fragment-";
96334
96428
  function hash3(text) {
96335
- return createHash9("sha256").update(text).digest("hex");
96429
+ return createHash10("sha256").update(text).digest("hex");
96336
96430
  }
96337
96431
  function uniqueSlugs(body) {
96338
96432
  const out = new Map;
@@ -96613,6 +96707,7 @@ function getEmbeddableEntryCount(db) {
96613
96707
  }
96614
96708
 
96615
96709
  // src/indexer/materialize-embeddings.ts
96710
+ init_paths();
96616
96711
  init_warn();
96617
96712
 
96618
96713
  // src/llm/embedders/cache.ts
@@ -96852,6 +96947,39 @@ var DEFAULT_TOKEN_BUDGET = 8000;
96852
96947
  function estimateTokenCount(text) {
96853
96948
  return Math.round(text.length / 4);
96854
96949
  }
96950
+ var DEFAULT_MAX_INPUT_TOKENS = 512;
96951
+ function capEmbeddingText(text, maxTokens) {
96952
+ if (estimateTokenCount(text) <= maxTokens)
96953
+ return { text, truncated: false };
96954
+ const charBudget = Math.max(0, maxTokens * 4);
96955
+ let cut = Math.min(charBudget, text.length);
96956
+ if (cut > 0 && cut < text.length) {
96957
+ const code = text.charCodeAt(cut);
96958
+ if (code >= 56320 && code <= 57343)
96959
+ cut -= 1;
96960
+ }
96961
+ return { text: text.slice(0, cut), truncated: true };
96962
+ }
96963
+ var DEFAULT_EMBEDDING_TIMEOUT_MS = 120000;
96964
+ function resolveEmbeddingTimeoutMs(config) {
96965
+ return config.timeoutMs ?? DEFAULT_EMBEDDING_TIMEOUT_MS;
96966
+ }
96967
+ function scaleEmbeddingTimeoutMs(timeoutMs, requestTokens, tokenBudget) {
96968
+ const scaled = tokenBudget > 0 ? timeoutMs * (requestTokens / tokenBudget) : timeoutMs;
96969
+ return Math.min(Math.max(scaled, 30000), timeoutMs);
96970
+ }
96971
+ function isEmbeddingTimeoutError(err) {
96972
+ if (!(err instanceof Error))
96973
+ return false;
96974
+ if (err.name === "BodyReadTimeoutError")
96975
+ return true;
96976
+ return err.message.startsWith("Request timed out after ");
96977
+ }
96978
+ var embeddingTimeoutBackoffOverrideForTests;
96979
+ function embeddingTimeoutRetryBackoffMs(timeoutAttempt = 0) {
96980
+ const { baseMs, maxMs } = embeddingTimeoutBackoffOverrideForTests ?? { baseMs: 5000, maxMs: 60000 };
96981
+ return backoffDelay(timeoutAttempt, baseMs, maxMs);
96982
+ }
96855
96983
 
96856
96984
  class ContextExceededError extends Error {
96857
96985
  constructor(message) {
@@ -96859,13 +96987,15 @@ class ContextExceededError extends Error {
96859
96987
  this.name = "ContextExceededError";
96860
96988
  }
96861
96989
  }
96862
- var CONTEXT_EXCEEDED_PATTERN = /exceed_context_size_error|context size|context length|too many tokens/i;
96990
+ var CONTEXT_EXCEEDED_PATTERN = /exceed_context_size_error|context size|context length|too many tokens|input is too large to process|physical batch size|ubatch/i;
96863
96991
  function isContextExceededResponse(status, body) {
96864
96992
  if (status === 413)
96865
96993
  return true;
96866
96994
  return CONTEXT_EXCEEDED_PATTERN.test(body);
96867
96995
  }
96868
96996
  function resolveEmbeddingConcurrency(config) {
96997
+ if (typeof config.concurrency === "number")
96998
+ return config.concurrency;
96869
96999
  return defaultConcurrencyForEndpoint(config.endpoint);
96870
97000
  }
96871
97001
  function buildTokenBoundedBatches(texts, tokenBudget, maxCount) {
@@ -96921,20 +97051,21 @@ class RemoteEmbedder {
96921
97051
  if (ollamaOpts) {
96922
97052
  body.options = ollamaOpts;
96923
97053
  }
97054
+ const timeoutMs = resolveEmbeddingTimeoutMs(this.config);
96924
97055
  const response = await fetchWithTimeout(normalizeEmbeddingEndpoint(this.endpoint), {
96925
97056
  method: "POST",
96926
97057
  headers,
96927
97058
  body: JSON.stringify(body)
96928
- }, 30000, signal);
97059
+ }, timeoutMs, signal);
96929
97060
  if (!response.ok) {
96930
- const errBody = await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: 30000, signal }).catch((err) => {
97061
+ const errBody = await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: timeoutMs, signal }).catch((err) => {
96931
97062
  if (signal?.aborted)
96932
97063
  throw err;
96933
97064
  return "";
96934
97065
  });
96935
97066
  throw new Error(`Embedding request failed (${response.status}): ${this.safeErrorBody(errBody)}`);
96936
97067
  }
96937
- const json = JSON.parse(await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: 30000, signal }));
97068
+ const json = JSON.parse(await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: timeoutMs, signal }));
96938
97069
  if (!json.data?.[0]?.embedding) {
96939
97070
  throw new Error(`Unexpected embedding response format: missing data[0].embedding.${embeddingEndpointPathHint(this.endpoint)}`);
96940
97071
  }
@@ -96946,9 +97077,10 @@ class RemoteEmbedder {
96946
97077
  const results = new Array(texts.length).fill(undefined);
96947
97078
  const headers = this.buildHeaders();
96948
97079
  const ollamaOpts = resolveOllamaOptions(this.config);
96949
- const tokenBudget = this.config.maxTokens ?? this.config.contextLength ?? DEFAULT_TOKEN_BUDGET;
97080
+ const tokenBudget = this.config.maxTokens ?? DEFAULT_TOKEN_BUDGET;
96950
97081
  const maxCount = this.config.batchSize ?? DEFAULT_REMOTE_BATCH_SIZE;
96951
97082
  const textBatches = buildTokenBoundedBatches(texts, tokenBudget, maxCount);
97083
+ const configuredTimeoutMs = resolveEmbeddingTimeoutMs(this.config);
96952
97084
  const dispatchAbort = new AbortController;
96953
97085
  const stopDispatch = (reason) => {
96954
97086
  if (!dispatchAbort.signal.aborted)
@@ -96964,66 +97096,132 @@ class RemoteEmbedder {
96964
97096
  }
96965
97097
  }
96966
97098
  let firstOnBatchError;
96967
- const commitBatch = (indices, embeddings, model) => {
97099
+ const commitBatch = (indices, embeddings, model, outcome) => {
96968
97100
  if (!onBatch)
96969
97101
  return;
96970
97102
  if (firstOnBatchError !== undefined)
96971
97103
  return;
96972
97104
  try {
96973
- onBatch(indices, embeddings, model);
97105
+ onBatch(indices, embeddings, model, outcome);
96974
97106
  } catch (err) {
96975
97107
  firstOnBatchError = err;
96976
97108
  stopDispatch(err);
96977
97109
  }
96978
97110
  };
96979
- const requestAndCommit = async (indices) => {
97111
+ const requestAndCommit = async (indices, batchIndex, isTimeoutRetry = false, timeoutAttempt = 0) => {
97112
+ if (dispatchAbort.signal.aborted)
97113
+ return;
96980
97114
  const batch = indices.map((i) => texts[i]);
97115
+ const requestTokens = batch.reduce((sum, text) => sum + estimateTokenCount(text), 0);
97116
+ const requestTimeoutMs = scaleEmbeddingTimeoutMs(configuredTimeoutMs, requestTokens, tokenBudget);
97117
+ const requestStart = Date.now();
96981
97118
  let batchEmbeddings;
96982
97119
  let responseModel;
97120
+ let outcome;
97121
+ let failureReason;
96983
97122
  try {
96984
- const { vectors, model } = await this.requestBatch(batch, headers, ollamaOpts, signal);
97123
+ const { vectors, model } = await this.requestBatch(batch, headers, ollamaOpts, requestTimeoutMs, signal);
96985
97124
  for (let k2 = 0;k2 < indices.length; k2++) {
96986
97125
  results[indices[k2]] = vectors[k2];
96987
97126
  }
96988
97127
  batchEmbeddings = indices.map((i) => results[i]);
96989
97128
  responseModel = model;
97129
+ outcome = "stored";
96990
97130
  } catch (err) {
96991
97131
  if (signal?.aborted)
96992
97132
  throw err;
96993
97133
  if (err instanceof ContextExceededError && indices.length > 1) {
96994
97134
  const mid = Math.ceil(indices.length / 2);
96995
- await requestAndCommit(indices.slice(0, mid));
96996
- await requestAndCommit(indices.slice(mid));
97135
+ await requestAndCommit(indices.slice(0, mid), batchIndex, false, timeoutAttempt);
97136
+ await requestAndCommit(indices.slice(mid), batchIndex, false, timeoutAttempt);
97137
+ return;
97138
+ }
97139
+ const timedOut = isEmbeddingTimeoutError(err);
97140
+ if (timedOut && !isTimeoutRetry) {
97141
+ const backoffMs = embeddingTimeoutRetryBackoffMs(timeoutAttempt);
97142
+ warnVerbose(`[embed] batch of ${batch.length} document(s) timed out after ${requestTimeoutMs}ms; retrying once after a ${Math.round(backoffMs)}ms backoff`);
97143
+ commitBatch(indices, indices.map(() => {
97144
+ return;
97145
+ }), undefined, {
97146
+ batchIndex,
97147
+ batchCount: textBatches.length,
97148
+ docCount: indices.length,
97149
+ requestTokens,
97150
+ elapsedMs: backoffMs,
97151
+ outcome: "retrying",
97152
+ reason: "timed out"
97153
+ });
97154
+ await abortableDelay(backoffMs, signal, "embedding interrupted during retry backoff");
97155
+ if (!dispatchAbort.signal.aborted) {
97156
+ return requestAndCommit(indices, batchIndex, true, timeoutAttempt);
97157
+ }
97158
+ } else if (timedOut && indices.length > 1) {
97159
+ const mid = Math.ceil(indices.length / 2);
97160
+ await requestAndCommit(indices.slice(0, mid), batchIndex, false, timeoutAttempt + 1);
97161
+ await requestAndCommit(indices.slice(mid), batchIndex, false, timeoutAttempt + 1);
96997
97162
  return;
96998
97163
  }
96999
97164
  const message = err instanceof Error ? err.message : String(err);
97000
97165
  const reason = err instanceof ContextExceededError ? "context-window-exceeded" : "batch-request-failed";
97001
- warnVerbose(`[embed] batch of ${batch.length} document(s) failed and was skipped: ${message}`);
97002
- for (const idx of indices) {
97003
- onSkip?.({ index: idx, reason, message });
97004
- }
97166
+ const failureKind = reason === "batch-request-failed" ? timedOut ? "timeout" : "network-error" : undefined;
97167
+ let stopRequested = false;
97168
+ for (const [k2, idx] of indices.entries()) {
97169
+ if (onSkip?.({
97170
+ index: idx,
97171
+ reason,
97172
+ message,
97173
+ batchStart: k2 === 0,
97174
+ batchSize: indices.length,
97175
+ failureKind
97176
+ }) === false)
97177
+ stopRequested = true;
97178
+ }
97179
+ if (stopRequested)
97180
+ stopDispatch();
97005
97181
  batchEmbeddings = indices.map(() => {
97006
97182
  return;
97007
97183
  });
97008
- }
97009
- commitBatch(indices, batchEmbeddings, responseModel);
97184
+ outcome = "failed";
97185
+ failureReason = message;
97186
+ }
97187
+ commitBatch(indices, batchEmbeddings, responseModel, {
97188
+ batchIndex,
97189
+ batchCount: textBatches.length,
97190
+ docCount: indices.length,
97191
+ requestTokens,
97192
+ elapsedMs: Date.now() - requestStart,
97193
+ outcome,
97194
+ reason: failureReason
97195
+ });
97010
97196
  };
97011
- const runProviderBatch = async (textBatch) => {
97197
+ const runProviderBatch = async (textBatch, batchIndex) => {
97012
97198
  if (textBatch.oversized) {
97013
97199
  const idx = textBatch.indices[0];
97014
97200
  const estTokens = estimateTokenCount(texts[idx]);
97015
97201
  onSkip?.({
97016
97202
  index: idx,
97017
97203
  reason: "context-window-exceeded",
97018
- message: `Document estimated at ${estTokens} tokens exceeds the ${tokenBudget}-token embedding budget; skipped.`
97204
+ message: `Document estimated at ${estTokens} tokens exceeds the ${tokenBudget}-token embedding budget; skipped.`,
97205
+ batchStart: true,
97206
+ batchSize: 1
97207
+ });
97208
+ commitBatch([idx], [undefined], undefined, {
97209
+ batchIndex,
97210
+ batchCount: textBatches.length,
97211
+ docCount: 1,
97212
+ requestTokens: estTokens,
97213
+ elapsedMs: 0,
97214
+ outcome: "failed",
97215
+ reason: "oversized"
97019
97216
  });
97020
- commitBatch([idx], [undefined]);
97021
97217
  return;
97022
97218
  }
97023
- await requestAndCommit(textBatch.indices);
97219
+ await requestAndCommit(textBatch.indices, batchIndex);
97024
97220
  };
97025
97221
  const concurrency = resolveEmbeddingConcurrency(this.config);
97026
- await concurrentMap(textBatches, runProviderBatch, concurrency, { signal: dispatchAbort.signal });
97222
+ await concurrentMap(textBatches, (textBatch, i) => runProviderBatch(textBatch, i + 1), concurrency, {
97223
+ signal: dispatchAbort.signal
97224
+ });
97027
97225
  if (callerAbortListener)
97028
97226
  signal?.removeEventListener("abort", callerAbortListener);
97029
97227
  if (signal?.aborted) {
@@ -97034,7 +97232,7 @@ class RemoteEmbedder {
97034
97232
  }
97035
97233
  return results;
97036
97234
  }
97037
- async requestBatch(batch, headers, ollamaOpts, signal) {
97235
+ async requestBatch(batch, headers, ollamaOpts, timeoutMs, signal) {
97038
97236
  const body = {
97039
97237
  input: batch,
97040
97238
  model: this.model
@@ -97049,9 +97247,9 @@ class RemoteEmbedder {
97049
97247
  method: "POST",
97050
97248
  headers,
97051
97249
  body: JSON.stringify(body)
97052
- }, 30000, signal);
97250
+ }, timeoutMs, signal);
97053
97251
  if (!response.ok) {
97054
- const respBody = await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: 30000, signal }).catch((err) => {
97252
+ const respBody = await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: timeoutMs, signal }).catch((err) => {
97055
97253
  if (signal?.aborted)
97056
97254
  throw err;
97057
97255
  return "";
@@ -97062,7 +97260,7 @@ class RemoteEmbedder {
97062
97260
  }
97063
97261
  throw new Error(message);
97064
97262
  }
97065
- const json = JSON.parse(await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: 30000, signal }));
97263
+ const json = JSON.parse(await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: timeoutMs, signal }));
97066
97264
  if (!json.data || json.data.length !== batch.length) {
97067
97265
  throw new Error(`Unexpected embedding batch response: expected ${batch.length} embeddings, got ${json.data?.length ?? 0}.${embeddingEndpointPathHint(this.endpoint)}`);
97068
97266
  }
@@ -97131,6 +97329,15 @@ function resolveOllamaOptions(config) {
97131
97329
  function hasRemoteEndpoint(config) {
97132
97330
  return isHttpUrl(config.endpoint);
97133
97331
  }
97332
+ function describeEmbeddingCredential(apiKey) {
97333
+ if (!apiKey)
97334
+ return "none configured";
97335
+ if (SECRET_STORE_REFERENCE_PATTERN.test(apiKey))
97336
+ return `${apiKey} (store)`;
97337
+ if (ENV_REFERENCE_PATTERN.test(apiKey))
97338
+ return `${apiKey} (env)`;
97339
+ return "literal apiKey";
97340
+ }
97134
97341
 
97135
97342
  // src/llm/embedder.ts
97136
97343
  var embedderOverrides;
@@ -97181,9 +97388,12 @@ function deriveSemanticProviderFingerprint(embedding) {
97181
97388
  }
97182
97389
  return `local:${embedding?.localModel ?? DEFAULT_LOCAL_MODEL}`;
97183
97390
  }
97184
- var PROGRESS_INTERVAL = 500;
97391
+ function formatEmbeddingHeartbeat(storedCount, total, failedCount) {
97392
+ return `Still generating embeddings: ${storedCount}/${total} stored, ${failedCount} failed; waiting on embedding provider.`;
97393
+ }
97185
97394
  var CANARY_SAMPLE_SIZE = 8;
97186
97395
  var CANARY_SIMILARITY_THRESHOLD = 0.999;
97396
+ var CIRCUIT_BREAKER_THRESHOLD = 3;
97187
97397
  function decideEmbeddingCompatibility(pairs) {
97188
97398
  if (pairs.length === 0)
97189
97399
  return { outcome: "keep", medianSimilarity: undefined, verifiedSamples: 0 };
@@ -97271,11 +97481,24 @@ function throwIfAborted(signal) {
97271
97481
  }
97272
97482
  }
97273
97483
  async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds, opts) {
97484
+ if (db.inTransaction) {
97485
+ throw new Error("generateEmbeddingsForDb was called with an ambient transaction already open on `db`: per-batch commits " + "would become SAVEPOINTs inside it, losing the crash-durability contract per-batch commit exists for. " + "Run the embedding phase on a connection with no open transaction.");
97486
+ }
97274
97487
  throwIfAborted(signal);
97275
97488
  if (config.semanticSearchMode === "off") {
97489
+ purgeEmbeddingSalvage(db);
97276
97490
  onProgress({ phase: "embeddings", message: "Semantic search disabled; skipping embeddings." });
97277
97491
  return { success: false, message: "Semantic search is disabled." };
97278
97492
  }
97493
+ if (hasRemoteEndpoint(config.embedding ?? {})) {
97494
+ const endpoint = normalizeEmbeddingEndpoint(config.embedding?.endpoint ?? "");
97495
+ const credential = describeEmbeddingCredential(config.embedding?.apiKey);
97496
+ const configFileSuffix = isVerbose() ? `; config: ${getConfigPath()}` : "";
97497
+ onProgress({
97498
+ phase: "embeddings",
97499
+ message: `[embed] endpoint ${endpoint}, model ${config.embedding?.model ?? "unknown"}; credential: ${credential}${configFileSuffix}`
97500
+ });
97501
+ }
97279
97502
  const vecFastPathWasReady = isVecFastPathReady(db);
97280
97503
  const currentFingerprint = deriveSemanticProviderFingerprint(config.embedding);
97281
97504
  const storedFingerprint = getMeta(db, "embeddingFingerprint");
@@ -97284,6 +97507,7 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds,
97284
97507
  if (opts?.forceReembed) {
97285
97508
  db.transaction(() => {
97286
97509
  purgeEmbeddings(db, { dropVecTable: true });
97510
+ purgeEmbeddingSalvage(db);
97287
97511
  deleteMeta(db, "embeddingDim");
97288
97512
  setMeta(db, "embeddingFingerprint", currentFingerprint);
97289
97513
  deleteMeta(db, "embeddingIdentity");
@@ -97300,6 +97524,7 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds,
97300
97524
  if (decision.outcome === "rebuild") {
97301
97525
  db.transaction(() => {
97302
97526
  purgeEmbeddings(db, { dropVecTable: true });
97527
+ purgeEmbeddingSalvage(db);
97303
97528
  deleteMeta(db, "embeddingDim");
97304
97529
  setMeta(db, "embeddingFingerprint", currentFingerprint);
97305
97530
  if (decision.identity)
@@ -97311,6 +97536,7 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds,
97311
97536
  rebuildReason = decision.reason;
97312
97537
  } else {
97313
97538
  setMeta(db, "embeddingFingerprint", currentFingerprint);
97539
+ relabelEmbeddingSalvageFingerprint(db, storedFingerprint, currentFingerprint);
97314
97540
  if (decision.identity)
97315
97541
  setMeta(db, "embeddingIdentity", decision.identity);
97316
97542
  if (decision.verified) {
@@ -97321,28 +97547,66 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds,
97321
97547
  onProgress({ phase: "embeddings", message });
97322
97548
  }
97323
97549
  }
97550
+ } else {
97551
+ setMeta(db, "embeddingFingerprint", currentFingerprint);
97324
97552
  }
97325
97553
  try {
97326
97554
  throwIfAborted(signal);
97327
97555
  const allEntries = getAllEntriesForEmbedding(db, targetEntryIds);
97328
- if (allEntries.length === 0) {
97556
+ let vecFailedCount = 0;
97557
+ let vecUnavailableCount = 0;
97558
+ const { reusedCount, remaining: candidateEntries } = reuseSalvagedEmbeddings(db, allEntries, currentFingerprint, (entry, embedding) => {
97559
+ const result = upsertEmbedding(db, entry.id, embedding);
97560
+ if (result.vec === "failed")
97561
+ vecFailedCount++;
97562
+ if (result.vec === "unavailable")
97563
+ vecUnavailableCount++;
97564
+ return result.stored;
97565
+ });
97566
+ if (reusedCount > 0) {
97567
+ onProgress({
97568
+ phase: "embeddings",
97569
+ message: `Reused ${reusedCount} embedding${reusedCount === 1 ? "" : "s"} from the previous generation; embedding ${candidateEntries.length} new.`
97570
+ });
97571
+ }
97572
+ if (candidateEntries.length === 0) {
97329
97573
  onProgress({ phase: "embeddings", message: "Embeddings already up to date." });
97330
97574
  setMeta(db, "embeddingFingerprint", currentFingerprint);
97331
- return { success: true };
97575
+ if (reusedCount > 0) {
97576
+ const vecGenerationComplete = targetEntryIds === undefined ? isVecFastPathComplete(db) : vecFastPathWasReady;
97577
+ setVecFastPathReady(db, vecFailedCount === 0 && vecUnavailableCount === 0 && vecGenerationComplete);
97578
+ }
97579
+ purgeEmbeddingSalvage(db);
97580
+ return reusedCount > 0 ? { success: true, vecInsertFailures: vecFailedCount } : { success: true };
97581
+ }
97582
+ const maxInputTokens = config.embedding?.maxInputTokens ?? DEFAULT_MAX_INPUT_TOKENS;
97583
+ let truncatedCount = 0;
97584
+ const texts = [];
97585
+ const pendingEntries = [];
97586
+ for (const entry of candidateEntries) {
97587
+ const capped = capEmbeddingText(entry.searchText, maxInputTokens);
97588
+ if (capped.text.length === 0)
97589
+ continue;
97590
+ if (capped.truncated)
97591
+ truncatedCount++;
97592
+ pendingEntries.push(entry);
97593
+ texts.push(capped.text);
97594
+ }
97595
+ if (truncatedCount > 0) {
97596
+ const message = `[embed] ${truncatedCount} entr${truncatedCount === 1 ? "y" : "ies"} truncated to the ${maxInputTokens}-token embedding cap (embedding.maxInputTokens); rerun with a higher cap to embed the full text.`;
97597
+ onProgress({ phase: "embeddings", message });
97332
97598
  }
97333
97599
  if (rebuildReason) {
97334
- const message = `[embed] Re-embedding ${allEntries.length} entr${allEntries.length === 1 ? "y" : "ies"} because ${rebuildReason}`;
97335
- warn(message);
97600
+ const message = `[embed] Re-embedding ${pendingEntries.length} entr${pendingEntries.length === 1 ? "y" : "ies"} because ${rebuildReason}`;
97336
97601
  onProgress({ phase: "embeddings", message });
97337
97602
  }
97338
97603
  onProgress({
97339
97604
  phase: "embeddings",
97340
- message: `Generating embeddings for ${allEntries.length} entr${allEntries.length === 1 ? "y" : "ies"}.`
97605
+ message: `Generating embeddings for ${pendingEntries.length} entr${pendingEntries.length === 1 ? "y" : "ies"}.`
97341
97606
  });
97342
- const texts = allEntries.map((entry) => entry.searchText);
97343
97607
  if (isVerbose()) {
97344
97608
  if (hasRemoteEndpoint(config.embedding ?? {})) {
97345
- const tokenBudget = config.embedding?.maxTokens ?? config.embedding?.contextLength ?? DEFAULT_TOKEN_BUDGET;
97609
+ const tokenBudget = config.embedding?.maxTokens ?? DEFAULT_TOKEN_BUDGET;
97346
97610
  const maxCount = config.embedding?.batchSize ?? DEFAULT_REMOTE_BATCH_SIZE;
97347
97611
  const batches = buildTokenBoundedBatches(texts, tokenBudget, maxCount);
97348
97612
  const batchNumberByIndex = new Map;
@@ -97350,7 +97614,7 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds,
97350
97614
  for (const i of batch.indices)
97351
97615
  batchNumberByIndex.set(i, batchIdx + 1);
97352
97616
  });
97353
- for (const [i, entry] of allEntries.entries()) {
97617
+ for (const [i, entry] of pendingEntries.entries()) {
97354
97618
  const chars = entry.searchText.length;
97355
97619
  const tokens = estimateTokenCount(entry.searchText);
97356
97620
  const batch = batches[batchNumberByIndex.get(i) - 1];
@@ -97358,7 +97622,7 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds,
97358
97622
  warnVerbose(`[embed] ${entry.itemRef} (${chars} chars, est. ${tokens} tokens) \u2192 ${label}`);
97359
97623
  }
97360
97624
  } else {
97361
- for (const entry of allEntries) {
97625
+ for (const entry of pendingEntries) {
97362
97626
  warnVerbose(`[embed] ${entry.itemRef} (${entry.searchText.length} chars, est. ${estimateTokenCount(entry.searchText)} tokens)`);
97363
97627
  }
97364
97628
  }
@@ -97367,27 +97631,58 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds,
97367
97631
  let storedCount = 0;
97368
97632
  let skippedCount = 0;
97369
97633
  let embedFailedCount = 0;
97370
- let vecFailedCount = 0;
97371
- let vecUnavailableCount = 0;
97372
97634
  let storedTokens = 0;
97373
- let lastProgressBucket = 0;
97374
97635
  try {
97375
97636
  heartbeatTimer = setInterval(() => {
97376
97637
  onProgress({
97377
97638
  phase: "embeddings",
97378
- message: `Still generating embeddings: ${storedCount}/${allEntries.length} stored; waiting on embedding provider.`
97639
+ message: formatEmbeddingHeartbeat(storedCount, pendingEntries.length, embedFailedCount)
97379
97640
  });
97380
97641
  }, 15000);
97381
97642
  const skips = [];
97382
97643
  const embedStart = Date.now();
97644
+ let consecutiveSingleDocFailures = 0;
97645
+ let consecutiveNetworkErrorFailures = 0;
97646
+ let circuitBreakerReason;
97647
+ const onSkip = (skip) => {
97648
+ skips.push(skip);
97649
+ if (!skip.batchStart)
97650
+ return;
97651
+ if (skip.reason === "context-window-exceeded") {
97652
+ consecutiveSingleDocFailures = 0;
97653
+ consecutiveNetworkErrorFailures = 0;
97654
+ return;
97655
+ }
97656
+ consecutiveSingleDocFailures = skip.batchSize === 1 ? consecutiveSingleDocFailures + 1 : 0;
97657
+ consecutiveNetworkErrorFailures = skip.failureKind === "network-error" ? consecutiveNetworkErrorFailures + 1 : 0;
97658
+ if (consecutiveSingleDocFailures >= CIRCUIT_BREAKER_THRESHOLD || consecutiveNetworkErrorFailures >= CIRCUIT_BREAKER_THRESHOLD) {
97659
+ circuitBreakerReason = skip.message;
97660
+ return false;
97661
+ }
97662
+ return;
97663
+ };
97383
97664
  let observedModel;
97384
97665
  let observedVectorLen;
97385
- const onBatch = (indices, batchEmbeddings, model) => {
97666
+ const reportPerBatchLine = hasRemoteEndpoint(config.embedding ?? {});
97667
+ const onBatch = (indices, batchEmbeddings, model, outcome) => {
97668
+ if (outcome?.outcome === "retrying") {
97669
+ if (reportPerBatchLine) {
97670
+ onProgress({
97671
+ phase: "embeddings",
97672
+ message: `[embed] batch ${outcome.batchIndex}/${outcome.batchCount}: ${outcome.docCount} docs, ${outcome.requestTokens.toLocaleString()} tokens \u2192 retrying after ${(outcome.elapsedMs / 1000).toFixed(1)} s`
97673
+ });
97674
+ }
97675
+ return;
97676
+ }
97386
97677
  if (model)
97387
97678
  observedModel = model;
97679
+ if (batchEmbeddings.some((embedding) => embedding !== undefined)) {
97680
+ consecutiveSingleDocFailures = 0;
97681
+ consecutiveNetworkErrorFailures = 0;
97682
+ }
97388
97683
  db.transaction(() => {
97389
97684
  for (let k2 = 0;k2 < indices.length; k2++) {
97390
- const entry = allEntries[indices[k2]];
97685
+ const entry = pendingEntries[indices[k2]];
97391
97686
  if (!entry)
97392
97687
  continue;
97393
97688
  const embedding = batchEmbeddings[k2];
@@ -97410,29 +97705,25 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal, entryIds,
97410
97705
  vecUnavailableCount++;
97411
97706
  }
97412
97707
  })();
97413
- const bucket = Math.floor(storedCount / PROGRESS_INTERVAL);
97414
- if (bucket > lastProgressBucket) {
97415
- lastProgressBucket = bucket;
97708
+ if (outcome && outcome.reason !== "oversized" && reportPerBatchLine) {
97709
+ const elapsedSeconds2 = (outcome.elapsedMs / 1000).toFixed(1);
97710
+ const outcomeLabel = outcome.outcome === "stored" ? `${outcome.docCount} stored (${elapsedSeconds2} s)` : `failed: ${outcome.reason}`;
97416
97711
  onProgress({
97417
97712
  phase: "embeddings",
97418
- message: `Embedded ${storedCount}/${allEntries.length} entries.`
97713
+ message: `[embed] batch ${outcome.batchIndex}/${outcome.batchCount}: ${outcome.docCount} docs, ${outcome.requestTokens.toLocaleString()} tokens \u2192 ${outcomeLabel}`
97419
97714
  });
97420
97715
  }
97716
+ onProgress({
97717
+ phase: "embeddings",
97718
+ message: `Embedded ${storedCount}/${pendingEntries.length} entries.`
97719
+ });
97421
97720
  };
97422
- await embedBatch(texts, config.embedding, signal, (skip) => skips.push(skip), onBatch);
97721
+ await embedBatch(texts, config.embedding, signal, onSkip, onBatch);
97423
97722
  throwIfAborted(signal);
97424
97723
  const elapsedSeconds = Math.max((Date.now() - embedStart) / 1000, 0.001);
97425
97724
  if (skippedCount > 0) {
97426
97725
  warn(`[embed] ${skippedCount} embedding${skippedCount === 1 ? "" : "s"} skipped (entry deleted between queue and write)`);
97427
97726
  }
97428
- if (embedFailedCount > 0) {
97429
- const detail = skips.slice(0, 20).map((skip) => ` - ${allEntries[skip.index]?.itemRef ?? skip.index} (${skip.reason}): ${skip.message}`).join(`
97430
- `);
97431
- const more = skips.length > 20 ? `
97432
- ...and ${skips.length - 20} more` : "";
97433
- warn(`[embed] ${embedFailedCount} embedding${embedFailedCount === 1 ? "" : "s"} could not be generated and ${embedFailedCount === 1 ? "was" : "were"} skipped:
97434
- ${detail}${more}`);
97435
- }
97436
97727
  const vecGenerationComplete = targetEntryIds === undefined ? isVecFastPathComplete(db) : vecFastPathWasReady;
97437
97728
  setVecFastPathReady(db, vecFailedCount === 0 && vecUnavailableCount === 0 && vecGenerationComplete);
97438
97729
  if (vecFailedCount > 0) {
@@ -97440,14 +97731,39 @@ ${detail}${more}`);
97440
97731
  }
97441
97732
  const entriesPerSec = storedCount / elapsedSeconds;
97442
97733
  const tokensPerSec = storedTokens / elapsedSeconds;
97734
+ const totalStored = storedCount + reusedCount;
97735
+ const oversizedSkips = skips.filter((skip) => skip.reason === "context-window-exceeded");
97736
+ const timedOutSkips = skips.filter((skip) => skip.reason === "batch-request-failed" && skip.failureKind === "timeout");
97737
+ const failedSkips = skips.filter((skip) => skip.reason === "batch-request-failed" && skip.failureKind !== "timeout");
97738
+ const throughputLine = reusedCount > 0 ? `Stored ${totalStored} embedding${totalStored === 1 ? "" : "s"} (${reusedCount} reused, ${storedCount} newly embedded) in ${elapsedSeconds.toFixed(1)}s (${entriesPerSec.toFixed(1)} entries/s, ~${Math.round(tokensPerSec)} tokens/s)` : `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"} in ${elapsedSeconds.toFixed(1)}s (${entriesPerSec.toFixed(1)} entries/s, ~${Math.round(tokensPerSec)} tokens/s)`;
97443
97739
  onProgress({
97444
97740
  phase: "embeddings",
97445
- message: `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"} in ${elapsedSeconds.toFixed(1)}s (${entriesPerSec.toFixed(1)} entries/s, ~${Math.round(tokensPerSec)} tokens/s).`
97741
+ message: `${throughputLine}; ${oversizedSkips.length} oversized skipped, ${timedOutSkips.length} timed out, ${failedSkips.length} failed.`
97446
97742
  });
97743
+ const printSkipList = (label, skipList) => {
97744
+ if (skipList.length === 0)
97745
+ return;
97746
+ const limit = isVerbose() ? skipList.length : 20;
97747
+ const listed = skipList.slice(0, limit).map((skip) => ` - ${pendingEntries[skip.index]?.itemRef ?? skip.index}: ${skip.message}`).join(`
97748
+ `);
97749
+ const more = skipList.length > limit ? `
97750
+ ...and ${skipList.length - limit} more` : "";
97751
+ onProgress({ phase: "embeddings", message: `[embed] ${label} skipped:
97752
+ ${listed}${more}` });
97753
+ };
97754
+ printSkipList("oversized documents", oversizedSkips);
97755
+ printSkipList("timed-out documents", timedOutSkips);
97756
+ printSkipList("failed documents", failedSkips);
97447
97757
  setMeta(db, "embeddingFingerprint", currentFingerprint);
97448
97758
  const observedIdentity = deriveObservedEmbeddingIdentity(config.embedding, observedModel, observedVectorLen);
97449
97759
  if (observedIdentity)
97450
97760
  setMeta(db, "embeddingIdentity", observedIdentity);
97761
+ if (circuitBreakerReason !== undefined) {
97762
+ const message = `embedding provider failed ${CIRCUIT_BREAKER_THRESHOLD} consecutive batches ` + `(last: ${circuitBreakerReason}); stopped after ${storedCount} embedding${storedCount === 1 ? "" : "s"} ` + "were stored \u2014 rerun akm index when the endpoint is healthy";
97763
+ warn(`[embed] ${message}`);
97764
+ onProgress({ phase: "embeddings", message });
97765
+ return { success: false, message, vecInsertFailures: vecFailedCount };
97766
+ }
97451
97767
  if (storedCount === 0 && embedFailedCount > 0) {
97452
97768
  const firstMessage = skips[0]?.message ?? "All embeddings failed.";
97453
97769
  return {
@@ -97455,6 +97771,7 @@ ${detail}${more}`);
97455
97771
  message: `All ${embedFailedCount} embedding batch(es) failed: ${firstMessage}`
97456
97772
  };
97457
97773
  }
97774
+ purgeEmbeddingSalvage(db);
97458
97775
  return { success: true, vecInsertFailures: vecFailedCount };
97459
97776
  } finally {
97460
97777
  if (heartbeatTimer)
@@ -97871,7 +98188,11 @@ async function indexWrittenAssets(stashDir, filePaths, options = {}) {
97871
98188
  return await (async () => {
97872
98189
  const rebuildProbe = probeLock(getIndexRebuildLockPath());
97873
98190
  if (rebuildProbe.state === "held") {
97874
- warn(`index rebuild in progress (pid ${rebuildProbe.holderPid}); the next index pass will index ${filePaths.join(", ")}`);
98191
+ const holderLabel = formatLockHolderPid({
98192
+ pid: rebuildProbe.holderPid,
98193
+ launcherPid: rebuildProbe.launcherPid ?? null
98194
+ });
98195
+ warn(`index rebuild in progress (pid ${holderLabel}); the next index pass will index ${filePaths.join(", ")}`);
97875
98196
  return true;
97876
98197
  }
97877
98198
  const dbPath = getDbPath();
@@ -98845,7 +99166,7 @@ var PROPOSAL_TXN_PHASES = [
98845
99166
  "committed"
98846
99167
  ];
98847
99168
  function proposalHash(content) {
98848
- return createHash10("sha256").update(content).digest("hex");
99169
+ return createHash11("sha256").update(content).digest("hex");
98849
99170
  }
98850
99171
  function proposalFileHash(filePath) {
98851
99172
  return proposalHash(fs55.readFileSync(filePath));