@duckcodeailabs/dql-agent 1.11.7 → 1.11.9

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.
@@ -22,6 +22,7 @@ import { certifiedFitAllowsTier1, certifiedTerminationVerdict, evaluateCertified
22
22
  import { retrieveScopedHints } from '../hints/retrieval.js';
23
23
  import { buildFtsMatch } from '../memory/fts-query.js';
24
24
  import { cosineSimilarity, envEmbeddingProvider, HashedTokenEmbeddingProvider, } from '../embeddings/provider.js';
25
+ import { projectEmbeddingProvider } from '../embeddings/project-embeddings.js';
25
26
  import { matchExampleParaphrase } from './example-match.js';
26
27
  import { loadSkills, selectRelevantSkills } from '../skills/loader.js';
27
28
  import { buildMeaningEvidencePackage, } from './meaning-evidence.js';
@@ -284,6 +285,56 @@ export function readIndexedKnowledge360(projectRoot, identity) {
284
285
  catalog.close();
285
286
  }
286
287
  }
288
+ /**
289
+ * In-flight vector upgrades, so a caller that needs semantic retrieval right
290
+ * away (tests, `dql compile`) can await one instead of racing it.
291
+ */
292
+ const pendingVectorUpgrades = new Map();
293
+ /** Await any vector-index upgrade started by the last catalog refresh. */
294
+ export async function awaitVectorIndexUpgrade(projectRoot) {
295
+ const pending = pendingVectorUpgrades.get(projectRoot);
296
+ if (!pending)
297
+ return;
298
+ await pending;
299
+ pendingVectorUpgrades.delete(projectRoot);
300
+ }
301
+ /**
302
+ * Re-embed the vector index with the project's configured provider.
303
+ *
304
+ * The snapshot write is synchronous — vectors are produced inside a
305
+ * better-sqlite3 transaction — so it can only use the hashed provider, whose
306
+ * `embedOne` is sync. Remote providers (Ollama, OpenAI) are async-only, so a
307
+ * real embedder could never participate and the "vector" lane stayed a
308
+ * bag-of-words hash no matter what was configured.
309
+ *
310
+ * This runs AFTER the transaction, where awaiting is fine. The hashed index is
311
+ * already in place by then, so the vector lane is never empty while the upgrade
312
+ * runs or if it fails. Best-effort by design: an unreachable Ollama must not
313
+ * break indexing or answering.
314
+ */
315
+ export async function upgradeVectorIndexForProject(projectRoot, provider) {
316
+ if (provider.id.startsWith('hashed-token')) {
317
+ return { upgraded: false, providerId: provider.id, reason: 'project uses the offline hashed embedder' };
318
+ }
319
+ const catalog = openMetadataCatalog(projectRoot);
320
+ try {
321
+ if (catalog.state('vector_provider') === provider.id) {
322
+ return { upgraded: false, providerId: provider.id, reason: 'index already embedded with this provider' };
323
+ }
324
+ await catalog.rebuildVectorIndex(provider);
325
+ return { upgraded: true, providerId: provider.id };
326
+ }
327
+ catch (error) {
328
+ return {
329
+ upgraded: false,
330
+ providerId: catalog.state('vector_provider') ?? DEFAULT_VECTOR_PROVIDER.id,
331
+ reason: error instanceof Error ? error.message : String(error),
332
+ };
333
+ }
334
+ finally {
335
+ catalog.close();
336
+ }
337
+ }
287
338
  export async function ensureMetadataCatalogFresh(projectRoot, options = {}) {
288
339
  const semanticLayer = options.semanticLayer !== undefined
289
340
  ? options.semanticLayer ?? undefined
@@ -384,7 +435,13 @@ export async function retrieveMetadataSnapshotCandidates(catalog, input) {
384
435
  objectTypes: input.objectTypes,
385
436
  domains: domains.length > 0 ? domains : undefined,
386
437
  limit: Math.min(limit, 24),
387
- provider: input.embeddingProvider,
438
+ // Fall back to the PROJECT's configured embedder rather than the hashed
439
+ // default, so a project that turned on a real one actually queries with it.
440
+ // searchVectorObjects still refuses (with a reason) if the index was built
441
+ // by a different provider, which keeps a half-migrated index from silently
442
+ // returning nonsense.
443
+ provider: input.embeddingProvider
444
+ ?? (input.projectRoot ? projectEmbeddingProvider(input.projectRoot) : undefined),
388
445
  });
389
446
  const vectorObjects = vector.candidates.filter(isEligible);
390
447
  const seedKeys = mergeObjects([...exactObjects, ...lexicalObjects.slice(0, 12), ...vectorObjects.slice(0, 12)])
@@ -547,6 +604,7 @@ export async function buildLocalContextPack(projectRoot, request) {
547
604
  searchQueries,
548
605
  objectTypes: request.objectTypes,
549
606
  domainContext: effectiveDomainContext,
607
+ projectRoot,
550
608
  limit: Math.max(request.limit ?? 80, 20),
551
609
  });
552
610
  const searchRows = retrievalObjects(snapshotRetrieval.selected);