@customize-agent/knowledge 4.0.40 → 4.0.41

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.
@@ -75,6 +75,7 @@ export declare class KnowledgeBaseManager {
75
75
  collections?: string[];
76
76
  weights?: RetrievalWeights;
77
77
  generationMode?: boolean;
78
+ disableReranker?: boolean;
78
79
  }): Promise<FederatedResult>;
79
80
  semanticSearch(query: string, options?: {
80
81
  limit?: number;
@@ -83,6 +84,10 @@ export declare class KnowledgeBaseManager {
83
84
  }): Promise<FederatedResult>;
84
85
  listRelationships(filePath?: string): FileRelationship[];
85
86
  listFiles(): IndexStateRecord[];
87
+ listChunks(options?: {
88
+ relativePath?: string;
89
+ limit?: number;
90
+ }): import("./index-state-store.js").StoredChunk[];
86
91
  getFileDetail(relativePath: string, options?: {
87
92
  maxChunkContentChars?: number;
88
93
  }): {
@@ -164,6 +169,10 @@ export declare class KnowledgeBaseManager {
164
169
  private llmExpandQueries;
165
170
  private retrievalWeights;
166
171
  private heuristicRerank;
172
+ private normalizeSearchText;
173
+ private isLowQualityCadText;
174
+ private queryTerms;
175
+ private queryFactLabels;
167
176
  private hydrateVectorResultsFromSqlite;
168
177
  private toFederatedItem;
169
178
  private mergeHybridRankedLists;
@@ -403,7 +403,11 @@ export class KnowledgeBaseManager {
403
403
  // 2. 对这些子块进行交叉编码器重排(Cross-Encoder Rerank)
404
404
  let reranked = mergedChildChunks;
405
405
  let rerankerName = 'local-heuristic-fallback';
406
- if (mergedChildChunks.length > 0) {
406
+ if (options.disableReranker) {
407
+ reranked = this.heuristicRerank(query, mergedChildChunks);
408
+ rerankerName = 'local-heuristic-disabled-reranker';
409
+ }
410
+ else if (mergedChildChunks.length > 0) {
407
411
  const rerankLimit = requestedLimit ? Math.min(30, mergeLimit) : mergedChildChunks.length;
408
412
  const candidates = mergedChildChunks.slice(0, rerankLimit);
409
413
  // 这里使用的是子块自身内容,通常在 500 tokens 左右,不仅相关性判断最准,而且不会超出 Reranker 的 max_length
@@ -470,6 +474,9 @@ export class KnowledgeBaseManager {
470
474
  listFiles() {
471
475
  return this.store.listRecords();
472
476
  }
477
+ listChunks(options = {}) {
478
+ return this.store.listChunks(options);
479
+ }
473
480
  getFileDetail(relativePath, options = {}) {
474
481
  const normalized = this.normalizeRelativePath(relativePath);
475
482
  const file = this.store.listRecords().find(record => record.relativePath === normalized);
@@ -746,28 +753,63 @@ export class KnowledgeBaseManager {
746
753
  };
747
754
  }
748
755
  heuristicRerank(query, items) {
749
- const terms = query.toLowerCase().split(/[\s,,。;;::、]+/u).filter(Boolean);
756
+ const terms = this.queryTerms(query);
750
757
  const phrase = query.toLowerCase().trim();
758
+ const normalizedPhrase = this.normalizeSearchText(query);
759
+ const factLabels = this.queryFactLabels(query);
760
+ const wantsTable = /表|行|列|金额|数量|报价|评分|清单|明细|统计|数据/u.test(query);
761
+ const wantsDrawing = /图纸|图层|轴网|标注|块|实体|cad|dwg|dxf|step|iges|模型/u.test(query);
762
+ const wantsData = /json|xml|yaml|字段|配置|数据|路径|price|id|name/u.test(query);
751
763
  return items.map(item => {
752
- const content = `${item.filePath}\n${item.titlePath ?? ''}\n${item.sectionTitle ?? ''}\n${item.chunkKind ?? ''}\n${item.content}`.toLowerCase();
764
+ const rawContent = `${item.filePath}\n${item.titlePath ?? ''}\n${item.sectionTitle ?? ''}\n${item.chunkKind ?? ''}\n${item.content}`;
765
+ const content = rawContent.toLowerCase();
766
+ const normalizedContent = this.normalizeSearchText(rawContent);
753
767
  let rerankBoost = 0;
754
768
  if (phrase && content.includes(phrase))
755
- rerankBoost += 120;
769
+ rerankBoost += 160;
770
+ if (normalizedPhrase.length >= 4 && normalizedContent.includes(normalizedPhrase))
771
+ rerankBoost += 220;
756
772
  const titleText = `${item.titlePath ?? ''}\n${item.sectionTitle ?? ''}`.toLowerCase();
773
+ const normalizedTitle = this.normalizeSearchText(titleText);
774
+ let matchedTermCount = 0;
757
775
  for (const term of terms) {
758
776
  if (!term)
759
777
  continue;
760
- if (content.includes(term))
761
- rerankBoost += 8;
762
- if (titleText.includes(term))
763
- rerankBoost += 18;
778
+ const normalizedTerm = this.normalizeSearchText(term);
779
+ const matchedContent = content.includes(term) || normalizedContent.includes(normalizedTerm);
780
+ const matchedTitle = titleText.includes(term) || normalizedTitle.includes(normalizedTerm);
781
+ if (matchedContent) {
782
+ matchedTermCount += 1;
783
+ rerankBoost += 12;
784
+ }
785
+ if (matchedTitle)
786
+ rerankBoost += 22;
787
+ }
788
+ if (matchedTermCount >= 2)
789
+ rerankBoost += matchedTermCount * 18;
790
+ for (const label of factLabels) {
791
+ const normalizedLabel = this.normalizeSearchText(label);
792
+ if (normalizedContent.includes(normalizedLabel))
793
+ rerankBoost += 80;
794
+ if (normalizedTitle.includes(normalizedLabel))
795
+ rerankBoost += 120;
764
796
  }
765
- if (item.chunkKind === 'table' && /表|行|列|金额|数量|报价|评分|清单|明细|统计|数据/u.test(query))
766
- rerankBoost += 40;
767
- if (item.chunkKind === 'metadata' && /图纸|图层|轴网|标注|块|实体|cad|dxf|step|iges|模型/u.test(query))
768
- rerankBoost += 60;
769
- if (item.chunkKind === 'data' && /json|xml|yaml|字段|配置|数据|路径|price|id|name/u.test(query))
770
- rerankBoost += 30;
797
+ if (item.chunkKind === 'table')
798
+ rerankBoost += wantsTable ? 25 : -80;
799
+ if (item.chunkKind === 'metadata')
800
+ rerankBoost += wantsDrawing ? 60 : -100;
801
+ if (item.chunkKind === 'data')
802
+ rerankBoost += wantsData ? 30 : -12;
803
+ if (/\.(?:dwg|dxf)(?:$|[?#])/iu.test(item.filePath)) {
804
+ rerankBoost += wantsDrawing ? 120 : -60;
805
+ if (this.isLowQualityCadText(item.content))
806
+ rerankBoost -= wantsDrawing ? 60 : 160;
807
+ }
808
+ if (/工作表:|COL\d+|专业工程暂估价计价表|分部分项工程量清单计价表|材料(工程设备)暂估单价一览表/u.test(item.content)) {
809
+ rerankBoost -= wantsTable ? 35 : 140;
810
+ }
811
+ if (/第\s*\d+\s*页\s*共\s*\d+\s*页|PDF\s*第\s*\d+\s*页/iu.test(item.content) && factLabels.length > 0)
812
+ rerankBoost -= 12;
771
813
  const score = item.score + rerankBoost;
772
814
  return {
773
815
  ...item,
@@ -780,6 +822,47 @@ export class KnowledgeBaseManager {
780
822
  };
781
823
  }).sort((a, b) => b.score - a.score);
782
824
  }
825
+ normalizeSearchText(value) {
826
+ return value.toLowerCase().replace(/\s+/gu, '');
827
+ }
828
+ isLowQualityCadText(value) {
829
+ const compact = value.replace(/\s+/gu, '');
830
+ if (compact.length === 0)
831
+ return true;
832
+ const readable = compact.match(/[\u4e00-\u9fa5A-Za-z0-9()()【】《》、,。;;::,.\-/㎡%]/gu)?.length || 0;
833
+ return readable / compact.length < 0.55;
834
+ }
835
+ queryTerms(query) {
836
+ const base = query.toLowerCase().split(/[\s,,。;;::、]+/u).filter(Boolean);
837
+ const labels = this.queryFactLabels(query).map(label => label.toLowerCase());
838
+ return [...new Set([...base, ...labels].filter(term => term.length > 0))];
839
+ }
840
+ queryFactLabels(query) {
841
+ const labels = [];
842
+ if (/建设地点|工程地点|项目地点|地点|在哪里|位于/u.test(query))
843
+ labels.push('建设地点', '工程地点', '项目地点', '项目位于', '位于');
844
+ if (/出资比例|资金比例|资金来源|出资/u.test(query))
845
+ labels.push('项目出资比例', '出资比例', '资金来源', '资金落实情况');
846
+ if (/项目名称|招标项目名称|工程名称/u.test(query))
847
+ labels.push('招标项目名称', '工程名称', '项目名称');
848
+ if (/项目编号|招标编号/u.test(query))
849
+ labels.push('招标项目编号', '项目编号');
850
+ if (/工期|日历天|计划开工|计划竣工/u.test(query))
851
+ labels.push('计划工期', '总工期', '工期', '计划开工日期', '计划竣工日期');
852
+ if (/质量标准|质量要求|合格/u.test(query))
853
+ labels.push('质量标准', '质量要求');
854
+ if (/招标范围|工程范围|承包范围|施工范围/u.test(query))
855
+ labels.push('招标范围', '工程承包范围', '施工范围');
856
+ if (/建设单位|招标人|项目业主/u.test(query))
857
+ labels.push('招标人', '项目业主', '建设单位');
858
+ if (/临水|临电|临时水电|水电接引|接驳点|挂表计量|施工水电/u.test(query))
859
+ labels.push('临水临电', '临时水电接引', '施工水电接引费', '接驳点挂表计量', '挂表计量');
860
+ if (/场地限制|材料堆场|办公区|生活区|加工区/u.test(query))
861
+ labels.push('场地限制', '不具备材料堆场', '搭设加工区', '搭设办公区', '搭设生活区');
862
+ if (/拆除|修补|破损处|改造维修/u.test(query))
863
+ labels.push('改造维修项目', '拆除内容比较多', '破损处进行修补');
864
+ return [...new Set(labels)];
865
+ }
783
866
  hydrateVectorResultsFromSqlite(items) {
784
867
  return items.map(item => {
785
868
  if (!item.rowid)
@@ -27,6 +27,7 @@ export declare class MultiProjectManager {
27
27
  filters?: SearchFilters;
28
28
  weights?: RetrievalWeights;
29
29
  generationMode?: boolean;
30
+ disableReranker?: boolean;
30
31
  }): Promise<FederatedResult>;
31
32
  semanticSearch(projectRoot: string, query: string, options?: {
32
33
  limit?: number;
@@ -63,16 +63,16 @@ export class MultiProjectManager {
63
63
  const scope = options.scope ?? 'project';
64
64
  const project = await this.getProject(projectRoot);
65
65
  if (scope === 'project')
66
- return project.hybridSearch(query, { limit, filters: options.filters, weights: options.weights, generationMode: options.generationMode });
66
+ return project.hybridSearch(query, { limit, filters: options.filters, weights: options.weights, generationMode: options.generationMode, disableReranker: options.disableReranker });
67
67
  const projectResults = scope === 'all'
68
- ? await project.hybridSearch(query, { limit, filters: options.filters, weights: options.weights, generationMode: options.generationMode })
68
+ ? await project.hybridSearch(query, { limit, filters: options.filters, weights: options.weights, generationMode: options.generationMode, disableReranker: options.disableReranker })
69
69
  : { results: [], scopesSearched: [], queryTimeMs: 0 };
70
70
  if (scope === 'global') {
71
71
  const global = await this.getGlobalKB();
72
- return global.hybridSearch(query, { limit, filters: options.filters, weights: options.weights, generationMode: options.generationMode });
72
+ return global.hybridSearch(query, { limit, filters: options.filters, weights: options.weights, generationMode: options.generationMode, disableReranker: options.disableReranker });
73
73
  }
74
74
  const global = await this.getGlobalKB();
75
- const globalResults = await global.hybridSearch(query, { limit, filters: options.filters, weights: options.weights, generationMode: options.generationMode });
75
+ const globalResults = await global.hybridSearch(query, { limit, filters: options.filters, weights: options.weights, generationMode: options.generationMode, disableReranker: options.disableReranker });
76
76
  const mergeLimit = limit ?? (projectResults.results.length + globalResults.results.length);
77
77
  const merged = new FederationSearch().merge([...projectResults.results, ...globalResults.results], mergeLimit, 'all');
78
78
  return {
@@ -13,6 +13,7 @@ export declare class ContentExtractor {
13
13
  private swapUtf16Bytes;
14
14
  private extractReadableFragments;
15
15
  private cleanCadReadableText;
16
+ private isLikelyGarbledCadText;
16
17
  private isReadableCadValue;
17
18
  private cleanExtractedText;
18
19
  private textScore;
@@ -7,6 +7,7 @@ import { resolveAndImport, resolvePackage } from './module-resolver.js';
7
7
  import { createOcrProvider } from './ocr-providers.js';
8
8
  const CAD_INTERNAL_TOKEN_RE = /\b(?:TDbPipe|TDbPipeValve|TDbPipeFitting|TDbWellh|AcDb\w+|Dwg\w+|ObjectId|Handle|ByLayer|Continuous|Model|Layout\d*)\b/giu;
9
9
  const CAD_INTERNAL_LINE_RE = /^(?:TDb\w+|AcDb\w+|[A-F0-9]{8,}|\d+|Model|Layout\d*|ByLayer|Continuous)$/iu;
10
+ const CAD_DOMAIN_SIGNAL_RE = /工程|项目|施工|建筑|结构|装饰|电气|给排水|消防|暖通|平面|立面|剖面|节点|详图|材料|尺寸|标高|轴线|图层|门窗|墙|地面|顶面|照明|配电|弱电|空调|卫生间|楼梯|屋面|基础|柱|梁|板|图号|设计|说明/u;
10
11
  const OCR_NATIVE_NOISE_PATTERNS = [/^Image too small to scale!!/u, /^Line cannot be recognized!!$/u];
11
12
  /** 文件内容提取器,支持文档、表格、图片、CAD 等多种文件格式的内容抽取 */
12
13
  export class ContentExtractor {
@@ -146,9 +147,34 @@ export class ContentExtractor {
146
147
  .replace(/\s+/gu, ' ')
147
148
  .trim();
148
149
  }
150
+ isLikelyGarbledCadText(value) {
151
+ const compact = value.replace(/\s+/gu, '');
152
+ if (!compact)
153
+ return true;
154
+ const chars = [...compact];
155
+ const readable = chars.filter(char => /[\p{Script=Han}\p{Script=Latin}\d()()【】《》、,。;;::,.\-/㎡%]/u.test(char)).length;
156
+ const cjk = chars.filter(char => /[\p{Script=Han}]/u.test(char)).length;
157
+ const latin = chars.filter(char => /[\p{Script=Latin}]/u.test(char)).length;
158
+ const digits = chars.filter(char => /\d/u.test(char)).length;
159
+ const symbols = chars.length - readable;
160
+ const readableRatio = readable / chars.length;
161
+ const symbolRatio = symbols / chars.length;
162
+ const hasDomainSignal = CAD_DOMAIN_SIGNAL_RE.test(compact);
163
+ const hasCommonTextShape = /[,。;:、,.\-/()()]|\d+(?:\.\d+)?\s*(?:mm|cm|m|㎡|%|°)?/iu.test(compact);
164
+ const latinVowelCount = chars.filter(char => /[aAeEiIoOuU]/u.test(char)).length;
165
+ if (readableRatio < 0.6)
166
+ return true;
167
+ if (symbolRatio > 0.35 && !hasDomainSignal)
168
+ return true;
169
+ if (latin >= 12 && latinVowelCount === 0 && !hasDomainSignal)
170
+ return true;
171
+ if (cjk >= 8 && digits === 0 && !hasDomainSignal && !hasCommonTextShape)
172
+ return true;
173
+ return false;
174
+ }
149
175
  isReadableCadValue(value) {
150
176
  const cleaned = this.cleanCadReadableText(value);
151
- return cleaned.length >= 2 && !CAD_INTERNAL_LINE_RE.test(cleaned) && /[\p{Script=Han}\p{Letter}\d]/u.test(cleaned);
177
+ return cleaned.length >= 2 && !CAD_INTERNAL_LINE_RE.test(cleaned) && /[\p{Script=Han}\p{Letter}\d]/u.test(cleaned) && !this.isLikelyGarbledCadText(cleaned);
152
178
  }
153
179
  cleanExtractedText(value, file) {
154
180
  const normalized = [...value]
@@ -162,7 +188,7 @@ export class ContentExtractor {
162
188
  return normalized
163
189
  .split(/\r?\n/u)
164
190
  .map(line => this.cleanCadReadableText(line))
165
- .filter(line => line && !CAD_INTERNAL_LINE_RE.test(line))
191
+ .filter(line => line && !CAD_INTERNAL_LINE_RE.test(line) && !this.isLikelyGarbledCadText(line))
166
192
  .join('\n')
167
193
  .replace(/\n{3,}/gu, '\n\n');
168
194
  }
@@ -172,7 +198,7 @@ export class ContentExtractor {
172
198
  return cjk * 4 + alnum + Math.min(value.length, 200) / 20;
173
199
  }
174
200
  async extractCad(file) {
175
- const metadata = { extractionMode: 'builtin_cad_structural', vectorizable: true };
201
+ const metadata = { extractionMode: 'builtin_cad_structural', vectorizable: true, preferredExtractionMode: 'dwg_to_dxf_semantic' };
176
202
  const warnings = [];
177
203
  const ext = path.extname(file.absolutePath).toLowerCase();
178
204
  if (ext === '.dxf')
@@ -180,7 +206,7 @@ export class ContentExtractor {
180
206
  if (ext === '.dwg') {
181
207
  const converted = await this.tryConvertDwgToDxf(file.absolutePath);
182
208
  if (converted?.dxfText) {
183
- const parsed = await this.extractDxf(file, converted.dxfText, { ...metadata, extractionMode: converted.tool, convertedFrom: 'dwg' });
209
+ const parsed = await this.extractDxf(file, converted.dxfText, { ...metadata, extractionMode: converted.tool, convertedFrom: 'dwg', professionalConversionUsed: true });
184
210
  parsed.warnings.push(...converted.warnings);
185
211
  return parsed;
186
212
  }
@@ -264,14 +290,20 @@ export class ContentExtractor {
264
290
  if (result.text.trim())
265
291
  return result;
266
292
  }
267
- const readable = this.extractBinaryReadableFragments(file.absolutePath).filter(value => this.isReadableCadValue(value)).slice(0, 5000);
293
+ const binaryFragments = this.extractBinaryReadableFragments(file.absolutePath);
294
+ const readable = binaryFragments.filter(value => this.isReadableCadValue(value)).slice(0, 5000);
295
+ const filteredCount = Math.max(0, binaryFragments.length - readable.length);
268
296
  metadata.extractionMode = 'builtin_cad_readable_fragments';
269
- metadata.contentCoverage = readable.length > 0 ? 'cad_readable_text_fragments' : 'metadata';
297
+ metadata.professionalConversionUsed = false;
298
+ metadata.contentCoverage = readable.length > 0 ? 'cad_readable_text_fragments_filtered' : 'metadata';
299
+ metadata.contentConfidence = readable.length > 0 ? 'low_fallback_filtered' : 'metadata_only';
300
+ metadata.stringCandidateCount = binaryFragments.length;
270
301
  metadata.stringCount = readable.length;
302
+ metadata.filteredGarbledStringCount = filteredCount;
271
303
  if (readable.length === 0)
272
304
  warnings.push(`${file.format} 内置 CAD 解析器未提取到可用文本,仅记录文件元数据,未生成可检索正文切片`);
273
305
  else
274
- warnings.push(`${file.format} 未检测到专业 DWG 转换器,已使用内置可读标注/标题块抽取;如需完整图纸结构,请安装 ODA File Converter 或 LibreDWG 并配置外部解析器`);
306
+ warnings.push(`${file.format} 内置 DWG→DXF 转换未成功,已使用低置信度可读标注/标题块兜底抽取并过滤疑似乱码 ${filteredCount} 条;该结果仅作为兜底证据,不应等同于完整图层、块、标注和尺寸语义解析`);
275
307
  return {
276
308
  text: readable.length > 0 ? [this.metadataOnlyText(file), `CAD 图纸可读标注/标题块/属性:\n${readable.join('\n')}`].join('\n') : this.metadataOnlyText(file),
277
309
  metadata,
@@ -373,15 +405,29 @@ export class ContentExtractor {
373
405
  const mod = await resolveAndImport('dwgdxf');
374
406
  if (!mod.convertDwgToDxf)
375
407
  return { tool: 'dwgdxf_wasm', warnings: ['内置 dwgdxf WASM 转换器未导出 convertDwgToDxf'] };
376
- const wasmBase = pathToFileURL(path.join(path.dirname(resolvePackage('dwgdxf')), 'wasm')).href;
377
- const dxfBytes = await mod.convertDwgToDxf(fs.readFileSync(filePath), { wasmBase });
378
- const dxfText = Buffer.from(dxfBytes).toString('utf8');
379
- return dxfText.trim()
380
- ? { dxfText, tool: 'dwgdxf_wasm', warnings: [] }
381
- : { tool: 'dwgdxf_wasm', warnings: ['内置 dwgdxf WASM 转换器未输出 DXF 文本'] };
408
+ const dwgBytes = fs.readFileSync(filePath);
409
+ const attempts = [
410
+ { label: 'package-default' },
411
+ { label: 'package-dist-wasm', options: { wasmBase: pathToFileURL(path.join(path.dirname(resolvePackage('dwgdxf')), 'wasm')).href } },
412
+ { label: 'package-root-dist-wasm', options: { wasmBase: pathToFileURL(path.join(path.dirname(path.dirname(resolvePackage('dwgdxf'))), 'wasm')).href } },
413
+ ];
414
+ const failures = [];
415
+ for (const attempt of attempts) {
416
+ try {
417
+ const dxfBytes = await mod.convertDwgToDxf(dwgBytes, attempt.options);
418
+ const dxfText = Buffer.from(dxfBytes).toString('utf8');
419
+ if (dxfText.trim())
420
+ return { dxfText, tool: `dwgdxf_wasm:${attempt.label}`, warnings: [] };
421
+ failures.push(`${attempt.label}: 未输出 DXF 文本`);
422
+ }
423
+ catch (error) {
424
+ failures.push(`${attempt.label}: ${error instanceof Error ? error.message : String(error)}`);
425
+ }
426
+ }
427
+ return { tool: 'dwgdxf_wasm', warnings: [`内置 dwgdxf WASM 转换失败: ${failures.join(';')}`] };
382
428
  }
383
429
  catch (error) {
384
- return { tool: 'dwgdxf_wasm', warnings: [`内置 dwgdxf WASM 转换失败: ${error instanceof Error ? error.message : String(error)}`] };
430
+ return { tool: 'dwgdxf_wasm', warnings: [`内置 dwgdxf WASM 加载失败: ${error instanceof Error ? error.message : String(error)}`] };
385
431
  }
386
432
  }
387
433
  async tryConvertDwgToDxf(filePath) {
package/dist/index.d.ts CHANGED
@@ -17,5 +17,5 @@ export { HNSWVectorStore } from './vector/hnsw-vector-store.js';
17
17
  export { CollectionManager, globalCollectionName, projectCollectionName } from './vector/collection-manager.js';
18
18
  export type { CollectionClient, VectorCollectionInfo, VectorDocument, VectorSearchQuery, VectorSearchResult, VectorStoreInterface, VectorWriteOptions } from './vector/types.js';
19
19
  export { VectorIndexer, type VectorIndexResult } from './vector/vector-indexer.js';
20
- export { FederationSearch, type FederatedQuery, type FederatedResult, type FederatedSearchItem, type SearchFilters, type SearchScope } from './search/federation-search.js';
20
+ export { FederationSearch, type FederatedQuery, type FederatedResult, type FederatedSearchItem, type RetrievalWeights, type SearchFilters, type SearchScope } from './search/federation-search.js';
21
21
  export type { LLMChatMessage, LLMChatOptions, LLMChatResponse, LLMSearchProvider } from './llm/llm-search-provider.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@customize-agent/knowledge",
3
- "version": "4.0.40",
3
+ "version": "4.0.41",
4
4
  "description": "Local knowledge base infrastructure for customize-agent",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",