import le from'better-sqlite3';import {Log,loadTextFile,Filesystem,Markdown}from'@easbot/utils';import {GRAPH_ENTITY_TYPE_DEFINITIONS,GRAPH_RELATION_TYPE_DEFINITIONS,getDefaultDatabasePath,DEFAULT_SYNC_CONFIG,DEFAULT_INDEXER_CONFIG,DEFAULT_SEARCH_CONFIG,DEFAULT_NOTE_CHUNK_CONFIG}from'@easbot/types';export{GRAPH_ENTITY_TYPE_DEFINITIONS,GRAPH_RELATION_TYPE_DEFINITIONS}from'@easbot/types';import U,{readFile}from'fs/promises';import*as B from'path';import B__default,{extname}from'path';import {createHash}from'crypto';import*as x from'jieba-wasm';import {embed,embedMany,streamText,rerank}from'ai';import {xdgData}from'xdg-basedir';var de=Object.defineProperty;var ue=(E,e,t)=>e in E?de(E,e,{enumerable:true,configurable:true,writable:true,value:t}):E[e]=t;var y=(E,e,t)=>ue(E,typeof e!="symbol"?e+"":e,t);var H=(d=>(d.DATABASE_INIT_FAILED="DATABASE_INIT_FAILED",d.DATABASE_NOT_INITIALIZED="DATABASE_NOT_INITIALIZED",d.DATABASE_QUERY_FAILED="DATABASE_QUERY_FAILED",d.DOCUMENT_PARSE_FAILED="DOCUMENT_PARSE_FAILED",d.EMBEDDING_FAILED="EMBEDDING_FAILED",d.ENTITY_EXTRACTION_FAILED="ENTITY_EXTRACTION_FAILED",d.SEARCH_FAILED="SEARCH_FAILED",d.NODE_NOT_FOUND="NODE_NOT_FOUND",d.EDGE_NOT_FOUND="EDGE_NOT_FOUND",d.INVALID_CONFIG="INVALID_CONFIG",d.MODEL_INJECTION_FAILED="MODEL_INJECTION_FAILED",d))(H||{}),R=class extends Error{constructor(t,r,n){super(t,{cause:n});y(this,"code",r);y(this,"cause",n);this.name="KnowledgeBaseError";}toString(){return `${this.name}: ${this.message} (${this.code})`}};var X=class{constructor(e){y(this,"db",null);y(this,"storagePath");y(this,"initialized",false);y(this,"initPromise",null);this.storagePath=Filesystem.normalize(e);}async initialize(){if(!this.initialized){if(this.initPromise){await this.initPromise;return}this.initPromise=this.doInitialize(),await this.initPromise,this.initialized=true;}}async doInitialize(){try{this.db=await this.openDatabaseAsync(),this.createTables();}catch(e){throw new R(`Database initialization failed: ${e.message}`,"DATABASE_INIT_FAILED",e)}}async openDatabaseAsync(){return new Promise((e,t)=>{setImmediate(()=>{try{let r=new le(this.storagePath);r.pragma("journal_mode = WAL"),r.pragma("synchronous = NORMAL"),r.pragma("cache_size = -64000"),r.pragma("temp_store = MEMORY"),e(r);}catch(r){t(r);}});})}ensureInitialized(){if(!this.initialized||!this.db)throw new R("Database not initialized. Call initialize() first.","DATABASE_NOT_INITIALIZED")}createTables(){if(!this.db)throw new R("Database not initialized. Call initialize() first.","DATABASE_NOT_INITIALIZED");let e=this.db;e.exec(`
      CREATE TABLE IF NOT EXISTS nodes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        type TEXT NOT NULL,
        properties TEXT,
        createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
        updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
      );
      CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
      CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type);
      CREATE UNIQUE INDEX IF NOT EXISTS idx_nodes_name_type ON nodes(name, type);
    `),e.exec(`
      CREATE TABLE IF NOT EXISTS edges (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        source INTEGER NOT NULL,
        target INTEGER NOT NULL,
        relation TEXT NOT NULL,
        properties TEXT,
        createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (source) REFERENCES nodes(id) ON DELETE CASCADE,
        FOREIGN KEY (target) REFERENCES nodes(id) ON DELETE CASCADE
      );
      CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source);
      CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target);
      CREATE INDEX IF NOT EXISTS idx_edges_relation ON edges(relation);
      CREATE UNIQUE INDEX IF NOT EXISTS idx_edges_unique ON edges(source, target, relation);
    `),e.exec(`
      CREATE TABLE IF NOT EXISTS documents (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        path TEXT NOT NULL UNIQUE,
        title TEXT,
        contentHash TEXT NOT NULL,
        summary TEXT,
        metadata TEXT,
        lastModified DATETIME DEFAULT CURRENT_TIMESTAMP,
        createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
        updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
      );
      CREATE INDEX IF NOT EXISTS idx_documents_path ON documents(path);
    `),e.exec(`
      CREATE TABLE IF NOT EXISTS chunks (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        documentId INTEGER NOT NULL,
        startLine INTEGER NOT NULL,
        endLine INTEGER NOT NULL,
        startChar INTEGER NOT NULL,
        endChar INTEGER NOT NULL,
        content TEXT NOT NULL,
        contentHash TEXT NOT NULL,
        nodeIds TEXT, -- \u5B58\u50A8\u5173\u8054\u7684\u8282\u70B9 ID \u5217\u8868\uFF0CJSON \u683C\u5F0F
        createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (documentId) REFERENCES documents(id) ON DELETE CASCADE
      );
      CREATE INDEX IF NOT EXISTS idx_chunks_document ON chunks(documentId);
      CREATE INDEX IF NOT EXISTS idx_chunks_hash ON chunks(contentHash);
    `),e.exec(`
      CREATE TABLE IF NOT EXISTS vectors (
        chunkId INTEGER PRIMARY KEY,
        embedding BLOB NOT NULL,
        FOREIGN KEY (chunkId) REFERENCES chunks(id) ON DELETE CASCADE
      );
    `),e.exec(`
      CREATE TABLE IF NOT EXISTS meta (
        key TEXT PRIMARY KEY,
        value TEXT
      );
    `),e.exec(`
      CREATE TABLE IF NOT EXISTS embedding_cache (
        contentHash TEXT PRIMARY KEY,
        embedding BLOB NOT NULL,
        createdAt DATETIME DEFAULT CURRENT_TIMESTAMP
      );
    `),e.exec(`
      CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
        chunkId UNINDEXED,
        documentId UNINDEXED,
        content,
        tokenize='porter unicode61'
      );
    `),e.pragma("foreign_keys = ON");}query(e,t){if(!this.db)throw new Error("Database not initialized");try{let r=this.db.prepare(e);return t?r.all(...t):r.all()}catch(r){throw new R(`Query failed: ${r.message}`,"DATABASE_QUERY_FAILED",r)}}run(e,t){if(!this.db)throw new Error("Database not initialized");try{let r=this.db.prepare(e),n=t?r.run(...t):r.run();return {lastID:Number(n.lastInsertRowid),changes:n.changes}}catch(r){throw new R(`Execution failed: ${r.message}`,"DATABASE_QUERY_FAILED",r)}}transaction(e){if(!this.db)throw new Error("Database not initialized");return this.db.transaction(e)()}async close(){this.db&&(this.db.close(),this.db=null);}getPath(){return this.storagePath}getDb(){if(!this.db)throw new Error("Database not initialized");return this.db}async getDocumentsCount(){return (await this.query("SELECT COUNT(*) as count FROM documents"))[0]?.count??0}async getChunksCount(){return (await this.query("SELECT COUNT(*) as count FROM chunks"))[0]?.count??0}async getNodesCount(){return (await this.query("SELECT COUNT(*) as count FROM nodes"))[0]?.count??0}async getEdgesCount(){return (await this.query("SELECT COUNT(*) as count FROM edges"))[0]?.count??0}async isVectorSearchAvailable(){try{return ((await this.query("SELECT COUNT(*) as count FROM vectors"))[0]?.count??0)>0}catch{return  false}}async healthCheck(){try{return this.db?(await this.query("SELECT 1"),!0):!1}catch{return  false}}getMeta(e){if(!this.db)throw new Error("Database not initialized");return this.db.prepare("SELECT value FROM meta WHERE key = ?").get(e)?.value??null}setMeta(e,t){if(!this.db)throw new Error("Database not initialized");this.db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(e,t);}getAllDocumentPaths(){if(!this.db)throw new Error("Database not initialized");return this.db.prepare("SELECT path FROM documents").all().map(t=>t.path)}getDocumentIdByPath(e){if(!this.db)throw new Error("Database not initialized");return this.db.prepare("SELECT id FROM documents WHERE path = ?").get(e)?.id??null}getChunkIdsByDocumentId(e){if(!this.db)throw new Error("Database not initialized");return this.db.prepare("SELECT id FROM chunks WHERE documentId = ?").all(e).map(r=>r.id)}cleanupDocumentData(e){if(!this.db)throw new Error("Database not initialized");this.transaction(()=>{let t=this.db.prepare("SELECT id, nodeIds, contentHash FROM chunks WHERE documentId = ?").all(e);if(t.length===0){this.run("DELETE FROM documents WHERE id = ?",[e]);return}let r=t.map(s=>s.id),n=t.map(s=>s.contentHash),o=new Set;for(let s of t)if(s.nodeIds)try{let i=JSON.parse(s.nodeIds);for(let c of i)o.add(c);}catch{}if(r.length>0){let s=r.map(()=>"?").join(",");try{this.run(`DELETE FROM chunks_fts WHERE chunkId IN (${s})`,r);}catch{for(let i of r)try{this.run("DELETE FROM chunks_fts WHERE chunkId = ?",[i]);}catch{}}}if(o.size>0){let s=Array.from(o),i=this.db.prepare(`
          SELECT nodeIds FROM chunks
          WHERE documentId != ?
        `).all(e),c=new Set;for(let a of i)if(a.nodeIds)try{let l=JSON.parse(a.nodeIds);for(let d of l)c.add(d);}catch{}for(let a of s)c.has(a)||this.run("DELETE FROM nodes WHERE id = ?",[a]);}if(n.length>0)for(let s of n){let i=this.db.prepare("SELECT COUNT(*) as count FROM chunks WHERE contentHash = ? AND documentId != ?").get(s,e);(!i||i.count===0)&&this.run("DELETE FROM embedding_cache WHERE contentHash = ?",[s]);}this.run("DELETE FROM chunks WHERE documentId = ?",[e]),this.run("DELETE FROM documents WHERE id = ?",[e]);});}deleteDocumentByPath(e){let t=this.getDocumentIdByPath(e);return t===null?false:(this.cleanupDocumentData(t),true)}};var F=class{async parse(e){try{let t=await readFile(e,"utf-8"),r=extname(e).toLowerCase(),n=this.parseMarkdown(t);return {content:n.content,metadata:n.metadata}}catch(t){throw new R(`\u6587\u6863\u89E3\u6790\u5931\u8D25: ${t.message}`,"DOCUMENT_PARSE_FAILED",t)}}parseMarkdown(e){let{frontmatter:t,content:r}=Markdown.extractFrontmatter(e),n={};t.title!==void 0&&(n.title=String(t.title)),t.author!==void 0&&(n.author=String(t.author)),t.date!==void 0&&(n.date=String(t.date));for(let[s,i]of Object.entries(t))["title","author","date"].includes(s)||(n[s]=i);return {content:Markdown.format(r),metadata:n}}static computeHash(e){return createHash("sha256").update(e,"utf-8").digest("hex")}};var M=Log.create({service:"note.tokenizer"}),P=false,be=loadTextFile("./assets/jieba_dict.txt",import.meta.url);async function ht(E){if(!P)try{M.info("Initializing jieba tokenizer");let e=be();if(x.with_dict(e),M.info("Built-in dictionary loaded"),E?.dictPath&&E.dictPath.length>0)for(let t of E.dictPath)await Ie(t);P=!0,M.info("Jieba tokenizer initialized");}catch(e){throw M.error("Failed to initialize jieba tokenizer",{error:String(e)}),e}}async function Ie(E){if(!P)throw new Error("Tokenizer not initialized");try{let e=await Filesystem.readText(E);x.with_dict(e),M.info("Custom dictionary loaded",{dictPath:E});}catch(e){M.warn("Failed to load custom dictionary",{dictPath:E,error:String(e)});}}function Ne(E){if(!P)return E.split(/\s+/).filter(Boolean);try{return x.cut(E)}catch(e){return M.warn("Tokenization failed, using fallback",{error:String(e)}),E.split(/\s+/).filter(Boolean)}}function Te(E){if(!P)return E.split(/\s+/).filter(Boolean);try{return x.cut_for_search(E)}catch(e){return M.warn("Tokenization for search failed, using fallback",{error:String(e)}),E.split(/\s+/).filter(Boolean)}}function pt(E,e,t){if(P)try{x.add_word(E,e,t);}catch(r){M.warn("Failed to add word",{word:E,error:String(r)});}}function Et(){return P}async function ft(){P=false,M.info("Tokenizer closed");}function ne(E){return Ne(E).join(" ")}function se(E){return Te(E).join(" ")}var D;(u=>{let E=Log.create({service:"note-llm"}),e=null,t=()=>{if(!e)throw new Error("Llm is not initialized. Please call Llm.init() first.");return e};function r(h){n()||(e={models:h.models,options:{graph:h.options?.graph,rerankTopN:h.options?.rerankTopN}},E.debug("Llm initialized"));}u.init=r;function n(){return e!==null}u.isInitialized=n;function o(){return e?{embedding:!!e.models.embeddingLlm,graphLlm:!!e.models.graphLlm,rerankLlm:!!e.models.rerankLlm}:{embedding:false,graphLlm:false,rerankLlm:false}}u.capabilities=o;async function s(h){let f=t().models.embeddingLlm;if(!f)throw new Error("embeddingLlm model is not configured.");let b=await embed({model:f,value:h});return new Float32Array(b.embedding)}u.embedText=s;async function i(h){let f=t().models.embeddingLlm;if(!f)throw new Error("embeddingLlm model is not configured.");return (await embedMany({model:f,values:h})).embeddings.map(N=>new Float32Array(N))}u.embedTexts=i;async function c(h,g){let f=t(),b=f.models.graphLlm;if(!b)return "";let N=l(h);return (await streamText({model:b,prompt:N,maxOutputTokens:g?.maxOutputTokens??f.options.graph?.maxOutputTokens,temperature:g?.temperature??f.options.graph?.temperature})).text}u.generateGraph=c;async function a(h,g,f){let b=t();return b.models.rerankLlm?(await rerank({model:b.models.rerankLlm,query:h,documents:g,topN:f??b.options.rerankTopN??g.length})).ranking.map(T=>({index:T.originalIndex,score:T.score??0})):[]}u.rerank=a;function l(h){let g=GRAPH_ENTITY_TYPE_DEFINITIONS.map(T=>`- ${T.type}: ${T.rule}`).join(`
`),f=GRAPH_RELATION_TYPE_DEFINITIONS.map(T=>`- ${T.type}: ${T.rule}`).join(`
`),N=h.slice(0,5).join(`

`).slice(0,2e3);return `You are an information extraction engine for a general-purpose memory graph.

Extract entities and directed relations from the text, and generate a concise summary.

Entity type taxonomy:
${g}

Relation type taxonomy:
${f}

Extraction rules:
- Output valid JSON only. Do not include markdown, prose, or code fences.
- summary: A concise summary (max 250 characters) describing the main topic and key points.
- Use canonical, concise entity names (deduplicated, case-insensitive).
- Prefer specific entity types over "keyword".
- Include relation only if both source and target entities are present in the entities list.
- Do not invent facts not grounded in the input text.
- Keep relation direction meaningful (source -> target).
- properties should contain short factual attributes only (plain JSON object).

Return this exact JSON shape:
{
  "summary": "A concise summary of the text content (max 250 characters)",
  "entities": [
    { "name": "string", "type": "one_of_entity_types", "properties": {} }
  ],
  "relations": [
    { "source": "entity_name", "target": "entity_name", "relation": "one_of_relation_types" }
  ]
}

Input text:
${N}`}u.embed=s,u.embedMany=i;})(D||(D={}));var Y=Log.create({service:"note-ingestion"}),Me=new Set(GRAPH_ENTITY_TYPE_DEFINITIONS.map(E=>E.type)),xe=new Set(GRAPH_RELATION_TYPE_DEFINITIONS.map(E=>E.type)),Q=class{constructor(e,t){y(this,"db");y(this,"config");y(this,"parser");this.db=e,this.config={...DEFAULT_NOTE_CHUNK_CONFIG,...t,embeddingLlm:t.embeddingLlm},this.parser=new F,t.embeddingLlm&&D.init({models:{embeddingLlm:t.embeddingLlm,graphLlm:t.graphLlm,rerankLlm:t.rerankLlm}});}async process(e){let t=Filesystem.normalize(e),{content:r,metadata:n}=await this.parser.parse(e),o=F.computeHash(r),s=this.db.query("SELECT id, contentHash FROM documents WHERE path = ?",[t]);if(s.length>0&&s[0]?.contentHash===o){let g=this.db.query("SELECT id FROM chunks WHERE documentId = ?",[s[0].id]).flatMap(N=>{let T=this.db.query("SELECT nodeIds FROM chunks WHERE id = ?",[N.id]);return T[0]?.nodeIds?JSON.parse(T[0].nodeIds):[]}),f=new Set(g).size,b=this.db.query(`SELECT id FROM edges WHERE source IN (${Array.from({length:f},()=>"?").join(",")})`,[...new Set(g)]).length;return {documentId:s[0].id,chunksCreated:0,vectorsCreated:0,entitiesExtracted:f,relationsExtracted:b}}let i=this.chunk(r),c=await this.embedWithCache(i),a=new Map,l,d,m="",u=this.extractEntitiesSync(i);if(l=u.entities.length,d=u.relations.length,this.mapChunksToNodes(i,u.entities,a),this.storeEntities(u.entities,u.relations),this.config.graphLlm){let h=t;this.extractEntities(i).then(g=>{m=g.summary,this.mergeEntitiesAndRelations(h,g.entities,g.relations,g.summary),Y.debug("LLM entity extraction completed in background",{path:h,entities:g.entities.length,relations:g.relations.length,summary:g.summary});}).catch(g=>{Y.warn("Background graph extraction failed",{path:h,error:String(g)});});}return this.db.transaction(()=>{s.length>0&&this.db.cleanupDocumentData(s[0].id);let g=this.db.run(`INSERT INTO documents (path, title, contentHash, summary, metadata, lastModified, createdAt, updatedAt)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,[t,n.title||B.basename(e)||"Untitled",o,typeof m=="string"&&m.length>0?m:typeof u.summary=="string"?u.summary:"",JSON.stringify(n||{}),new Date().toISOString(),new Date().toISOString(),new Date().toISOString()]).lastID,f=0,b=0;r.split(`
`);let T=0,_=0,$=[];for(let L=0;L<i.length;L++){let p=i[L],I=p.split(`
`).length-1;$.push({startLine:_,endLine:_+I,startChar:T,endChar:T+p.length});let S=this.config.chunkOverlap;T=T+Math.max(1,p.length-S);let w=Math.floor(S/20);_=_+Math.max(1,I-w);}for(let L=0;L<i.length;L++){let p=i[L],I=F.computeHash(p),S=a.get(L)||[],w=$[L],q=this.db.run(`INSERT INTO chunks (documentId, startLine, endLine, startChar, endChar, content, contentHash, nodeIds, createdAt)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,[g,w.startLine,w.endLine,w.startChar,w.endChar,p,I,JSON.stringify(S),new Date().toISOString()]).lastID,z=c[L];z&&(this.db.run("INSERT INTO vectors (chunkId, embedding) VALUES (?, ?)",[q,this.float32ToBuffer(z)]),b++);let K=ne(p);this.db.run("INSERT INTO chunks_fts (chunkId, documentId, content) VALUES (?, ?, ?)",[q,g,K]),f++;}return {documentId:g,chunksCreated:f,vectorsCreated:b,entitiesExtracted:l,relationsExtracted:d}})}async processBatch(e,t){let r=[];for(let n=0;n<e.length;n++){let o=await this.process(e[n]);r.push(o),t?.(n+1,e.length,o);}return r}chunk(e){let{chunkSize:t,minChunkLength:r=100}=this.config;return Markdown.chunk(e,t,{minLength:r})}async embedWithCache(e){if(!this.config.embeddingLlm)return e.map(()=>new Float32Array(this.config.vectorDims));let t=[],r=[];for(let n=0;n<e.length;n++){let o=e[n],s=F.computeHash(o),i=this.db.query("SELECT embedding FROM embedding_cache WHERE contentHash = ?",[s]);i.length>0&&i[0]?.embedding?t[n]=new Float32Array(i[0].embedding.buffer,i[0].embedding.byteOffset,i[0].embedding.length/4):r.push({index:n,content:o,hash:s});}if(r.length>0)try{let n=r.map(s=>s.content),o=await D.embedMany(n);for(let s=0;s<r.length;s++){let{index:i,hash:c}=r[s],a=o[s];if(a){let l=new Float32Array(a);t[i]=l,this.ensureVectorDims(l.length),this.db.run("INSERT OR REPLACE INTO embedding_cache (contentHash, embedding, createdAt) VALUES (?, ?, ?)",[c,this.float32ToBuffer(l),new Date().toISOString()]);}}}catch(n){throw new R(`\u5D4C\u5165\u751F\u6210\u5931\u8D25: ${n.message}`,"EMBEDDING_FAILED",n)}return t}async embed(e){if(!this.config.embeddingLlm)return e.map(()=>new Float32Array(this.config.vectorDims));try{return await D.embedMany(e)}catch(t){throw new R(`\u5D4C\u5165\u751F\u6210\u5931\u8D25: ${t.message}`,"EMBEDDING_FAILED",t)}}async extractEntities(e){let t=e.slice(0,5).join(`

`);if(!this.config.graphLlm)return this.extractEntitiesByRules(t);try{let r=await D.generateGraph(e,{maxOutputTokens:2e3}),n=this.parseEntityExtractionResult(r);return n.entities.length===0&&n.relations.length===0?this.extractEntitiesByRules(t):n}catch(r){if(r instanceof Error)return this.extractEntitiesByRules(t);throw new R(`\u5B9E\u4F53\u63D0\u53D6\u5931\u8D25: ${String(r)}`,"ENTITY_EXTRACTION_FAILED")}}extractEntitiesSync(e){let t=e.slice(0,5).join(`

`);return this.extractEntitiesByRules(t)}parseEntityExtractionResult(e){try{let t=e.match(/\{[\s\S]*\}/);if(t){let r=JSON.parse(t[0]);return this.normalizeExtractionResult(r)}}catch(t){Y.debug("Failed to parse entity extraction result",{error:String(t)});}return {summary:"",entities:[],relations:[]}}normalizeExtractionResult(e){let t=e,r=typeof t.summary=="string"?t.summary.trim():"",n=Array.isArray(t.entities)?t.entities:[],o=Array.isArray(t.relations)?t.relations:[],s=[],i=new Set,c=new Map;for(let d of n){let m=typeof d?.name=="string"?d.name.trim():"";if(!m)continue;let u=m.toLowerCase();if(i.has(u))continue;i.add(u),c.set(u,m);let h=typeof d?.type=="string"?d.type.trim():"",g=Me.has(h)?h:"keyword",f=d?.properties&&typeof d.properties=="object"&&!Array.isArray(d.properties)?d.properties:{};s.push({name:m,type:g,properties:f});}let a=[],l=new Set;for(let d of o){let m=typeof d?.source=="string"?d.source.trim():"",u=typeof d?.target=="string"?d.target.trim():"";if(!m||!u)continue;let h=c.get(m.toLowerCase()),g=c.get(u.toLowerCase());if(!h||!g||h===g)continue;let f=typeof d?.relation=="string"?d.relation.trim():"",b=xe.has(f)?f:"related_to",N=`${h.toLowerCase()}|${g.toLowerCase()}|${b}`;l.has(N)||(l.add(N),a.push({source:h,target:g,relation:b}));}return {summary:r,entities:s,relations:a}}extractEntitiesByRules(e){let t=[],r=[],n=new Set,o=new Set,s=new Map,i=/([A-Za-z][\w.-]{1,80})\s+(is_a|part_of|has_attribute|located_in|occurs_in|causes|influences|owned_by|member_of|uses|depends_on|related_to)\s+([A-Za-z][\w.-]{1,80})/g;for(let l of e.matchAll(i)){let d=this.sanitizeEntityToken(l[1]??""),m=(l[2]??"related_to").trim(),u=this.sanitizeEntityToken(l[3]??"");if(!d||!u||d===u)continue;let h=d.toLowerCase(),g=u.toLowerCase();n.has(h)||(n.add(h),s.set(h,d),t.push({name:d,type:this.inferEntityType(d),properties:{source:"rule_extracted"}})),n.has(g)||(n.add(g),s.set(g,u),t.push({name:u,type:this.inferEntityType(u),properties:{source:"rule_extracted"}}));let f=`${h}|${g}|${m}`;o.has(f)||(o.add(f),r.push({source:d,target:u,relation:m}));}let c=e.match(/[\p{L}\p{N}][\p{L}\p{N}_.-]{1,80}/gu)??[],a=e.split(/\n\s*\n/);for(let l of c){let d=this.sanitizeEntityToken(l);if(!d)continue;let m=d.toLowerCase();n.has(m)||(n.add(m),s.set(m,d),t.push({name:d,type:this.inferEntityType(d),properties:{source:"keyword_extracted"}}));}for(let l of a){if(l.trim().length<20)continue;let d=[];for(let m of l.match(/[\p{L}\p{N}][\p{L}\p{N}_.-]{2,80}/gu)??[]){let u=this.sanitizeEntityToken(m);if(!u)continue;let h=u.toLowerCase();n.has(h)&&!d.some(g=>g.key===h)&&d.push({key:h,name:u});}for(let m=0;m<d.length;m++)for(let u=m+1;u<d.length;u++){let h=d[m],g=d[u];if(h.key===g.key)continue;let f="related_to",b=this.inferEntityType(h.name),N=this.inferEntityType(g.name);b==="document"&&N==="object"?f="defines":b==="object"&&N==="document"?f="referenced_in":b==="process"&&N==="object"?f="uses":b==="object"&&N==="process"?f="used_by":b==="concept"&&(f="relates_to");let T=`${h.key}|${g.key}|${f}`;o.has(T)||(o.add(T),r.push({source:h.name,target:g.name,relation:f}));}}return {summary:"",entities:t,relations:r}}sanitizeEntityToken(e){return e.trim().replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu,"").replace(/\s+/g," ").slice(0,120)}inferEntityType(e){return /(\.md|\.ts|\.json|readme|doc|spec|guideline)/i.test(e)?"document":/(api|service|system|engine|pipeline|server|db|database|memorysystem|contextengine)/i.test(e)?"process":/(team|group|org|company|inc|ltd|corp)/i.test(e)?"organization":/(deadline|date|day|week|month|year|q[1-4]|sprint)/i.test(e)?"time":/(score|rate|ratio|latency|accuracy|recall|precision|throughput|kpi)/i.test(e)?"metric":/(task|issue|ticket|todo|action)/i.test(e)?"task":/(event|incident|release|meeting|migration|launch)/i.test(e)?"event":/(process|workflow|procedure|flow|lifecycle)/i.test(e)?"process":/(file|model|dataset|artifact|resource|tool|sdk|ollama|sqlite|embedding|vector|fts)/i.test(e)?"object":/(concept|principle|policy|method|pattern|strategy)/i.test(e)?"concept":/[A-Z]/.test(e)?"object":"keyword"}storeEntities(e,t){if(e.length===0)return;let r=new Map,n=new Date().toISOString();for(let o of e){let s=this.db.query("SELECT id FROM nodes WHERE name = ? AND type = ?",[o.name,o.type]);s.length>0&&s[0]&&r.set(o.name,s[0].id);}this.db.transaction(()=>{for(let o of e){if(r.has(o.name))continue;let s=this.db.run("INSERT INTO nodes (name, type, properties, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?)",[o.name,o.type,JSON.stringify(o.properties),n,n]);s.lastID>0&&r.set(o.name,s.lastID);}for(let o of t){let s=r.get(o.source),i=r.get(o.target);if(s&&i){let c=this.db.query("SELECT id FROM nodes WHERE id = ?",[s]),a=this.db.query("SELECT id FROM nodes WHERE id = ?",[i]);c.length>0&&a.length>0&&this.db.run("INSERT OR REPLACE INTO edges (source, target, relation, properties, createdAt) VALUES (?, ?, ?, ?, ?)",[s,i,o.relation,"{}",n]);}}});}mergeEntitiesAndRelations(e,t,r,n){if(t.length===0&&r.length===0&&(!n||n.trim().length===0))return;let o=new Map,s=new Date().toISOString();if(t.length>0){let i=t.map(()=>"?").join(","),c=this.db.query(`SELECT id, name, type FROM nodes WHERE name IN (${i})`,t.map(a=>a.name));for(let a of c)o.set(a.name,a.id);}this.db.transaction(()=>{for(let i of t)if(o.get(i.name)===void 0){let a=this.db.run("INSERT INTO nodes (name, type, properties, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?)",[i.name,i.type,JSON.stringify(i.properties),s,s]);a.lastID>0&&o.set(i.name,a.lastID);}for(let i of r){let c=o.get(i.source),a=o.get(i.target);if(c&&a){let l=this.db.query("SELECT id FROM nodes WHERE id = ?",[c]),d=this.db.query("SELECT id FROM nodes WHERE id = ?",[a]);l.length>0&&d.length>0&&this.db.run("INSERT OR REPLACE INTO edges (source, target, relation, properties, createdAt) VALUES (?, ?, ?, ?, ?)",[c,a,i.relation,"{}",s]);}}n&&n.trim().length>0&&(this.db.run("UPDATE documents SET summary = ?, updatedAt = ? WHERE path = ?",[n,s,e]),Y.debug("Updated document summary",{path:e,summaryLength:n.length}));});}float32ToBuffer(e){return Buffer.from(e.buffer,e.byteOffset,e.byteLength)}ensureVectorDims(e){this.db.query("SELECT value FROM meta WHERE key = ?",["vectorDims"]).length===0&&this.db.run("INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)",["vectorDims",String(e)]);}mapChunksToNodes(e,t,r){let n=new Map,o=new Date().toISOString();this.db.transaction(()=>{for(let s of t){let i=this.db.query("SELECT id FROM nodes WHERE name = ? AND type = ?",[s.name,s.type]);if(i.length>0&&i[0])n.set(s.name,i[0].id);else {let c=this.db.run("INSERT INTO nodes (name, type, properties, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?)",[s.name,s.type,JSON.stringify(s.properties),o,o]);c.lastID>0&&n.set(s.name,c.lastID);}}for(let s=0;s<e.length;s++){let i=e[s],c=[];for(let a of t)if(i.includes(a.name)){let l=n.get(a.name);l&&c.push(l);}r.set(s,c);}});}};Log.create({service:"note-search"});var J=class{constructor(e,t){y(this,"db");y(this,"config");this.db=e,this.config={...DEFAULT_NOTE_CHUNK_CONFIG,...t,embeddingLlm:t.embeddingLlm},t.embeddingLlm&&D.init({models:{embeddingLlm:t.embeddingLlm,graphLlm:t.graphLlm,rerankLlm:t.rerankLlm}});}async search(e,t){if(!e.trim())return [];let r=t?.maxResults||3,n=t?.minScore??this.config.search.minScore,o=await this.hybridSearch(e,{...t,maxResults:r*2});return o.length===0?[]:this.config.rerankLlm?(await this.rerank(e,o,r)).filter(i=>i.score>=n):o.filter(s=>s.score>=n).slice(0,r)}async hybridSearch(e,t){let{maxResults:r=this.config.search.maxResults,minScore:n=this.config.search.minScore,vectorWeight:o=this.config.search.hybrid.vectorWeight,textWeight:s=this.config.search.hybrid.textWeight,enableEmbedding:i=true,enableFts:c=true}=t??{},a=i?await this.embed(e):new Float32Array(0),[l,d]=await Promise.all([i&&this.config.embeddingLlm?this.vectorSearch(a,r,n,t):Promise.resolve([]),c===false?Promise.resolve([]):this.ftsSearch(e,r,n,t)]);return this.mergeResults(l,d,o,s,n)}async vectorSearch(e,t,r,n){if(!this.config.embeddingLlm)return [];try{let o;if(n?.candidateChunkIds&&n.candidateChunkIds.length>0){let c=n.candidateChunkIds.map(()=>"?").join(",");o=this.db.query(`SELECT chunkId, embedding FROM vectors WHERE chunkId IN (${c})`,n.candidateChunkIds);}else o=this.db.query("SELECT chunkId, embedding FROM vectors LIMIT ?",[Math.max(t*100,1e3)]);let s=[];for(let c of o){let a=new Float32Array(c.embedding.buffer,c.embedding.byteOffset,c.embedding.length/4),l=this.cosineSimilarity(e,a);s.push({chunkId:c.chunkId,score:l});}s.sort((c,a)=>a.score-c.score);let i=s.slice(0,t);return this.buildSearchResults(i,"vector",n)}catch(o){throw new R(`\u5411\u91CF\u641C\u7D22\u5931\u8D25: ${o.message}`,"SEARCH_FAILED",o)}}async ftsSearch(e,t,r,n){let o=async()=>[];try{let s=se(e),i=this.db.query("SELECT chunkId, bm25(chunks_fts) as score FROM chunks_fts WHERE content MATCH ? ORDER BY score DESC LIMIT ?",[s,t]);if(i.length===0)return o();let c=i.map(a=>({chunkId:a.chunkId,score:-a.score}));return this.buildSearchResults(c,"fts",n)}catch{return o()}}mergeResults(e,t,r,n,o){let s=r+n,i=r/s,c=n/s,a=new Map;for(let m of e){let u=`${m.chunk.id}`,h=m.score*i;a.set(u,{...m,score:h,source:"hybrid"});}for(let m of t){let u=`${m.chunk.id}`,h=a.get(u);if(h)h.score+=m.score*c;else {let g=m.score*c;a.set(u,{...m,score:g,source:"hybrid"});}}return Array.from(a.values()).map(m=>{let u=m.score,h=new Date(m.document.updatedAt),g=(Date.now()-h.getTime())/864e5,f=Math.exp(-0.01*g),b=.6*u+.4*f;return {...m,score:b}}).sort((m,u)=>u.score-m.score)}async rerank(e,t,r){if(!this.config.rerankLlm)return t.slice(0,r);try{let n=t.map(a=>{let l=a.nodes&&a.nodes.length>0?a.nodes.map(d=>{let m=(d.edges??[]).map(u=>{let h=u.source===d.id?"\u2192":"\u2190",g=u.targetNode?`${u.targetNode.type}:${u.targetNode.name}`:`id=${u.target}`;return `${h} [${u.relation}] ${g}`}).join(" | ");return `node: id=${d.id} name=${d.name} type=${d.type}${m?` edges: ${m}`:""}`}).join(" | "):"";return `<doc filePath="${a.document.path}" lineStart="${a.chunk.startLine}" lineEnd="${a.chunk.endLine}"><snippet>${a.snippet}</snippet>${l?`<nodes>${l}</nodes>`:""}</doc>`}),o=await D.rerank(e,n,t.length);if(o.length===0)return t.slice(0,r);let s=new Map;for(let a of o)a.index>=0&&a.index<t.length&&s.set(a.index,a.score);let i=.5,c=t.map((a,l)=>{let d=s.get(l)??a.score,m=i*a.score+(1-i)*d;return {...a,originalScore:a.score,score:m,source:"reranked"}});return c.sort((a,l)=>l.score-a.score),c.slice(0,r)}catch{return t.slice(0,r)}}async embed(e){try{return await D.embed(e)}catch{return new Float32Array(this.config.vectorDims)}}cosineSimilarity(e,t){let r=0,n=0,o=0;for(let s=0;s<e.length;s++){let i=e[s],c=t[s];r+=i*c,n+=i*i,o+=c*c;}return n===0||o===0?0:r/(Math.sqrt(n)*Math.sqrt(o))}async buildSearchResults(e,t,r){let n=[],o=e.map(p=>p.chunkId);if(o.length===0)return n;let s=r?.includeEdges??true,i=Math.max(1,r?.edgesLimit??100),c=this.db.query("SELECT * FROM chunks WHERE id IN ("+o.map(()=>"?").join(", ")+")",o),a=new Map(c.map(p=>[p.id,p])),l=Array.from(new Set(c.map(p=>p.documentId))),d=l.length>0?this.db.query("SELECT * FROM documents WHERE id IN ("+l.map(()=>"?").join(", ")+")",l):[],m=new Map(d.map(p=>[p.id,p])),u=new Map,h=[];for(let p of c){let I=[];try{let S=p.nodeIds?JSON.parse(p.nodeIds):[];I=Array.isArray(S)?S.filter(w=>typeof w=="number"):[];}catch{I=[];}u.set(p.id,I),h.push(...I);}let g=Array.from(new Set(h)),f=g.length>0?this.db.query("SELECT * FROM nodes WHERE id IN ("+g.map(()=>"?").join(", ")+")",g):[],b=new Map(f.map(p=>[p.id,{id:p.id,name:p.name,type:p.type,properties:p.properties?JSON.parse(p.properties):void 0,createdAt:p.createdAt,updatedAt:p.updatedAt}])),N=s&&g.length>0?this.db.query("SELECT * FROM edges WHERE source IN ("+g.map(()=>"?").join(", ")+") OR target IN ("+g.map(()=>"?").join(", ")+") ORDER BY id LIMIT ?",[...g,...g,g.length*i]):[],T=[...new Set([...g,...N.map(p=>p.target)])],_=T.length>0?this.db.query("SELECT * FROM nodes WHERE id IN ("+T.map(()=>"?").join(", ")+")",T):[],$=new Map(_.map(p=>[p.id,{id:p.id,name:p.name,type:p.type,properties:p.properties?JSON.parse(p.properties):void 0,createdAt:p.createdAt,updatedAt:p.updatedAt}])),L=new Map;for(let p of N){let I={id:p.id,source:p.source,target:p.target,relation:p.relation,properties:p.properties?JSON.parse(p.properties):void 0,createdAt:p.createdAt},S=$.get(p.target);S&&(I.targetNode=S);let w=L.get(I.source)??[];w.length<i&&(w.push(I),L.set(I.source,w));let v=L.get(I.target)??[];v.length<i&&(v.push(I),L.set(I.target,v));}for(let p of e){let I=a.get(p.chunkId);if(!I)continue;let S=m.get(I.documentId);if(!S)continue;let w=u.get(I.id)??[],v=w.map(k=>b.get(k)).filter(k=>k!==void 0),q=s&&w.length>0?Array.from(new Map(w.flatMap(k=>L.get(k)??[]).map(k=>[k.id,k])).values()):[],z=new Map;for(let k of q){let te=z.get(k.source)??[];te.push(k),z.set(k.source,te);}let K=v.length>0?v.map(k=>({...k,edges:s?z.get(k.id)??[]:void 0})):[];n.push({chunk:{id:I.id,documentId:I.documentId,startLine:I.startLine,endLine:I.endLine,startChar:I.startChar,endChar:I.endChar,content:I.content,contentHash:I.contentHash,nodeIds:w,path:S.path,createdAt:I.createdAt},document:{id:S.id,path:S.path,title:S.title,contentHash:S.contentHash,summary:S.summary,lastModified:S.lastModified,createdAt:S.createdAt,updatedAt:S.updatedAt},nodes:K.length>0?K:void 0,score:p.score,snippet:I.content.slice(0,200),source:t});}return n}};var V=class{constructor(e){y(this,"db");this.db=e;}async queryNodes(e,t){try{if(typeof e=="string")return this.db.query(e,t).map(c=>this.mapNode(c));let r=[],n=[];e.ids&&e.ids.length>0&&(r.push(`id IN (${e.ids.map(()=>"?").join(",")})`),n.push(...e.ids)),e.name&&(r.push("name LIKE ?"),n.push(`%${e.name}%`)),e.type&&(r.push("type = ?"),n.push(e.type));let o="SELECT * FROM nodes";return r.length>0&&(o+=` WHERE ${r.join(" AND ")}`),o+=" ORDER BY id",e.limit&&e.limit>0&&(o+=" LIMIT ?",n.push(e.limit)),this.db.query(o,n).map(i=>this.mapNode(i))}catch(r){throw new R(`\u67E5\u8BE2\u8282\u70B9\u5931\u8D25: ${r.message}`,"SEARCH_FAILED",r)}}async queryEdges(e,t){try{if(typeof e=="string")return this.db.query(e,t).map(c=>this.mapEdge(c));let r=[],n=[];e.ids&&e.ids.length>0&&(r.push(`id IN (${e.ids.map(()=>"?").join(",")})`),n.push(...e.ids)),typeof e.source=="number"&&(r.push("source = ?"),n.push(e.source)),typeof e.target=="number"&&(r.push("target = ?"),n.push(e.target)),e.sourceOrTarget&&e.sourceOrTarget.length>0&&(r.push(`(source IN (${e.sourceOrTarget.map(()=>"?").join(", ")}) OR target IN (${e.sourceOrTarget.map(()=>"?").join(", ")}))`),n.push(...e.sourceOrTarget,...e.sourceOrTarget)),e.relation&&(r.push("relation = ?"),n.push(e.relation));let o="SELECT * FROM edges";return r.length>0&&(o+=` WHERE ${r.join(" AND ")}`),o+=" ORDER BY id",e.limit&&e.limit>0&&(o+=" LIMIT ?",n.push(e.limit)),this.db.query(o,n).map(i=>this.mapEdge(i))}catch(r){throw new R(`\u67E5\u8BE2\u8FB9\u5931\u8D25: ${r.message}`,"SEARCH_FAILED",r)}}async queryNeighbors(e,t){let r=t?.direction??"both",n=t?.relationTypes,o=t?.limit&&t.limit>0?t.limit:100,s=[],i=[];r==="incoming"?(s.push("target = ?"),i.push(e)):r==="outgoing"?(s.push("source = ?"),i.push(e)):(s.push("(source = ? OR target = ?)"),i.push(e,e)),n&&n.length>0&&(s.push(`relation IN (${n.map(()=>"?").join(",")})`),i.push(...n));let c=await this.queryEdges(`SELECT * FROM edges WHERE ${s.join(" AND ")} ORDER BY id LIMIT ?`,[...i,o]),a=Array.from(new Set(c.map(u=>u.source===e?u.target:u.source)));if(a.length===0)return {nodes:await this.queryNodes({ids:[e],limit:1}),edges:c};let l=[...new Set([e,...a])],d=await this.queryNodes({ids:l,limit:l.length}),m=new Map(d.map(u=>[u.id,u]));for(let u of c){let h=m.get(u.target);h&&(u.targetNode=h);}return {nodes:d,edges:c}}async queryCallGraph(e,t=2){let r=Math.max(1,t),n=new Map,o=[{id:e,level:0}],s=new Set;for(;o.length>0;){let i=o.shift();if(s.has(i.id)||i.level>r)continue;s.add(i.id);let c=await this.queryEdges({source:i.id,relation:"CALLS",limit:100}),a=await this.queryEdges({target:i.id,relation:"CALLS",limit:100}),l=Array.from(new Set(c.map(m=>m.target))),d=Array.from(new Set(a.map(m=>m.source)));n.set(i.id,{callers:d,callees:l});for(let m of [...d,...l])s.has(m)||o.push({id:m,level:i.level+1});}return n}async queryInheritance(e){let t=["EXTENDS","IMPLEMENTS"],r=await this.collectInheritance(e,"ancestor",t),n=await this.collectInheritance(e,"descendant",t);return {ancestors:r,descendants:n}}async findPath(e,t,r=3){let n=[],o=new Set,s=this.db.query("SELECT * FROM nodes WHERE name = ?",[e]);if(s.length===0)return [];let i=s.map(c=>({nodeId:c.id,path:[c.id]}));for(;i.length>0;){let{nodeId:c,path:a}=i.shift();if(a.length>r)continue;let l=a.join(",");if(o.has(l))continue;if(o.add(l),a.length>1){let m=this.db.query("SELECT id, name FROM nodes WHERE id = ?",[c]);if(m.length>0&&m[0]?.name===t){let u=a.map(()=>"?").join(","),h=this.db.query(`SELECT * FROM nodes WHERE id IN (${u})`,a),g=new Map(h.map(b=>[b.id,b])),f=[];for(let b of a){let N=g.get(b);N&&f.push({id:N.id,name:N.name,type:N.type,properties:N.properties?JSON.parse(N.properties):void 0,createdAt:N.createdAt,updatedAt:N.updatedAt});}n.push(f);continue}}let d=this.db.query("SELECT source, target FROM edges WHERE source = ? OR target = ?",[c,c]);for(let m of d){let u=m.source===c?m.target:m.source;a.includes(u)||i.push({nodeId:u,path:[...a,u]});}}return n}async searchNodesByName(e){return this.queryNodes("SELECT * FROM nodes WHERE name LIKE ?",[e])}async searchNodesByType(e){return this.queryNodes("SELECT * FROM nodes WHERE type = ?",[e])}async searchRelations(e){return this.queryEdges("SELECT * FROM edges WHERE relation = ?",[e])}async createNode(e,t,r){let n=new Date().toISOString(),o=this.db.run("INSERT INTO nodes (name, type, properties, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?)",[e,t,r?JSON.stringify(r):null,n,n]),i=this.db.query("SELECT * FROM nodes WHERE id = ?",[o.lastID])[0];return {id:i.id,name:i.name,type:i.type,properties:i.properties?JSON.parse(i.properties):void 0,createdAt:i.createdAt,updatedAt:i.updatedAt}}async updateNode(e,t){let r=new Date().toISOString();this.db.run("UPDATE nodes SET properties = ?, updatedAt = ? WHERE id = ?",[JSON.stringify(t),r,e]);let n=this.db.query("SELECT * FROM nodes WHERE id = ?",[e]);if(n.length===0)throw new R(`\u8282\u70B9\u4E0D\u5B58\u5728: ${e}`,"NODE_NOT_FOUND");let o=n[0];return {id:o.id,name:o.name,type:o.type,properties:o.properties?JSON.parse(o.properties):void 0,createdAt:o.createdAt,updatedAt:o.updatedAt}}async deleteNode(e){this.db.run("DELETE FROM nodes WHERE id = ?",[e]);}async createEdge(e,t,r,n){let o=this.db.query("SELECT id FROM nodes WHERE id = ?",[e]),s=this.db.query("SELECT id FROM nodes WHERE id = ?",[t]);if(o.length===0||s.length===0)throw new R("\u8282\u70B9\u4E0D\u5B58\u5728","NODE_NOT_FOUND");let i=new Date().toISOString(),c=this.db.run("INSERT INTO edges (source, target, relation, properties, createdAt) VALUES (?, ?, ?, ?, ?)",[e,t,r,n?JSON.stringify(n):null,i]),l=this.db.query("SELECT * FROM edges WHERE id = ?",[c.lastID])[0];return {id:l.id,source:l.source,target:l.target,relation:l.relation,properties:l.properties?JSON.parse(l.properties):void 0,createdAt:l.createdAt}}async deleteEdge(e){this.db.run("DELETE FROM edges WHERE id = ?",[e]);}mapNode(e){return {id:e.id,name:e.name,type:e.type,properties:e.properties?JSON.parse(e.properties):void 0,createdAt:e.createdAt,updatedAt:e.updatedAt}}mapEdge(e){return {id:e.id,source:e.source,target:e.target,relation:e.relation,properties:e.properties?JSON.parse(e.properties):void 0,createdAt:e.createdAt}}async collectInheritance(e,t,r){let n=[e],o=new Set([e]),s=new Set;for(;n.length>0;){let i=n.shift(),c=t==="ancestor"?await this.queryEdges(`SELECT * FROM edges WHERE source = ? AND relation IN (${r.map(()=>"?").join(",")})`,[i,...r]):await this.queryEdges(`SELECT * FROM edges WHERE target = ? AND relation IN (${r.map(()=>"?").join(",")})`,[i,...r]);for(let a of c){let l=t==="ancestor"?a.target:a.source;o.has(l)||(o.add(l),s.add(l),n.push(l));}}return s.size===0?[]:this.queryNodes({ids:Array.from(s),limit:s.size})}};var Z=Log.create({service:"note-scanner"}),_e=["node_modules/**",".git/**","dist/**","build/**","__pycache__/**","*.min.md"],W=class{constructor(e){y(this,"workspaceDir");y(this,"extraPaths");y(this,"sources");y(this,"ignorePatterns");this.workspaceDir=e.workspaceDir,this.extraPaths=e.extraPaths??[],this.sources=new Set(e.sources??["docs"]),this.ignorePatterns=[..._e,...e.ignorePatterns??[]];}async scanAll(e){let t=[],r=[];for(let s of this.sources){let i=B__default.join(this.workspaceDir,s);r.push({source:s,dir:i});}for(let s of this.extraPaths){let i=B__default.isAbsolute(s)?s:B__default.join(this.workspaceDir,s);r.push({source:"local",dir:i});}let n=0,o=r.length;for(let s of r){let i=await this.scanDirectory(s.dir,s.source);t.push(...i),n++,e?.(n,o,i[0]||{absolutePath:s.dir,relativePath:s.dir,source:s.source,contentHash:"",lastModified:0,size:0});}return t}async scanDirectory(e,t){let r=[];try{await U.access(e);}catch{return Z.debug("Directory not found, skipping",{dir:e}),r}let n=async o=>{try{let s=await U.readdir(o,{withFileTypes:!0});for(let i of s){let c=B__default.join(o,i.name);if(!this.shouldIgnore(i.name)){if(i.isDirectory())await n(c);else if(i.isFile()&&this.isMarkdownFile(i.name)){let a=await this.getFileInfo(c,t);a&&r.push(a);}}}}catch(s){Z.warn("Failed to scan directory",{dir:o,error:String(s)});}};return await n(e),r}async scanFile(e,t="upload"){try{let r=await U.stat(e);if(!r.isFile())return null;let n=await U.readFile(e,"utf-8"),o=createHash("sha256").update(n).digest("hex"),s=Filesystem.normalize(e);return {absolutePath:e,relativePath:s||B__default.basename(e),source:t,contentHash:o,lastModified:r.mtimeMs,size:r.size}}catch(r){return Z.error("Failed to scan file",{filePath:e,error:String(r)}),null}}async getFileInfo(e,t){try{let r=await U.readFile(e,"utf-8"),n=await U.stat(e),o=createHash("sha256").update(r).digest("hex"),s=Filesystem.normalize(e);return {absolutePath:e,relativePath:s,source:t,contentHash:o,lastModified:n.mtimeMs,size:n.size}}catch(r){return Z.error("Failed to get file info",{filePath:e,error:String(r)}),null}}shouldIgnore(e){if(e.startsWith("."))return  true;for(let t of this.ignorePatterns)if(new RegExp("^"+t.replace(/\*\*/g,".*").replace(/\*/g,"[^/]*")+"$").test(e))return  true;return  false}isMarkdownFile(e){return e.endsWith(".md")||e.endsWith(".markdown")}};var A=Log.create({service:"note"}),ee=class{constructor(e){y(this,"config");y(this,"db");y(this,"pipeline");y(this,"searchEngine");y(this,"graphInterface");y(this,"fileScanner");y(this,"initialized",false);y(this,"dirty",false);y(this,"syncTimer");y(this,"syncConfig");y(this,"syncingInProgress",false);let t=e.workspaceDir||process.cwd(),r=e.embeddingLlm;if(!r)throw new Error("embeddingLlm is required");let n=e.database?.path||getDefaultDatabasePath(xdgData,"note.db");this.config={workspaceDir:t,database:{path:n,walMode:e.database?.walMode??true},vectorDims:e.vectorDims??768,...DEFAULT_NOTE_CHUNK_CONFIG,extraPaths:e.extraPaths,sources:e.sources,search:{...DEFAULT_SEARCH_CONFIG,...e.search},indexer:{...DEFAULT_INDEXER_CONFIG,...e.indexer},sync:{...DEFAULT_SYNC_CONFIG,...e.sync},logDir:e.logDir,embeddingLlm:r,graphLlm:e.graphLlm,rerankLlm:e.rerankLlm},this.syncConfig=this.config.sync,this.db=new X(this.config.database.path),this.pipeline=new Q(this.db,this.config),this.searchEngine=new J(this.db,this.config),this.graphInterface=new V(this.db),this.fileScanner=new W({workspaceDir:this.config.workspaceDir,extraPaths:this.config.extraPaths,sources:this.config.sources,ignorePatterns:this.config?.indexer?.ignorePatterns});}async initialize(){if(this.initialized)return;D.init({models:{embeddingLlm:this.config.embeddingLlm,graphLlm:this.config.graphLlm,rerankLlm:this.config.rerankLlm},options:{graph:{maxOutputTokens:2e3,temperature:.1}}}),await this.db.initialize();let e=[["version","1.0.0"],["chunkSize",String(this.config.chunkSize)],["chunkOverlap",String(this.config.chunkOverlap)],["createdAt",new Date().toISOString()]];for(let[t,r]of e)this.db.run("INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)",[t,r]);this.initialized=true,this.syncConfig.onBoot&&(this.syncAll().catch(t=>A.warn("onBoot sync failed",{error:String(t)})),this.startSyncTimer()),A.debug("NoteKnowledge initialized",{workspaceDir:this.config.workspaceDir,dbPath:this.config.database.path,embeddingEnabled:!!this.config.embeddingLlm,graphLlmEnabled:!!this.config.graphLlm,rerankLlmEnabled:!!this.config.rerankLlm,chunkSize:this.config.chunkSize,chunkOverlap:this.config.chunkOverlap,sources:this.config.sources,syncOnBoot:this.syncConfig.onBoot,syncIntervalMs:this.syncConfig.intervalMs,hybridSearch:this.config.search?.hybrid?.enabled,vectorWeight:this.config.search?.hybrid?.vectorWeight,textWeight:this.config.search?.hybrid?.textWeight});}startSyncTimer(){this.syncTimer&&clearInterval(this.syncTimer),this.syncConfig.intervalMs>0&&(this.syncTimer=setInterval(async()=>{try{await this.syncAll();}catch(e){A.error("Interval sync failed",{error:String(e)});}},this.syncConfig.intervalMs));}ensureSynced(){this.dirty&&!this.syncingInProgress&&this.syncConfig.onSearch&&this.syncAll().catch(e=>{A.warn("Background sync failed",{error:String(e)});});}stopSyncTimer(){this.syncTimer&&(clearInterval(this.syncTimer),this.syncTimer=void 0);}async syncAll(){if(this.syncingInProgress){A.debug("Sync already in progress, skipping...");return}this.syncingInProgress=true,A.debug("Starting sync...");try{let e=await this.sync((t,r,n)=>{A.debug("Syncing file",{current:t,total:r,path:n.relativePath||B__default.basename(n.absolutePath)});});this.dirty=!1,A.debug("Sync completed",{totalFiles:e.filesAdded+e.filesUpdated+e.filesSkipped,added:e.filesAdded,updated:e.filesUpdated,deleted:e.filesDeleted,skipped:e.filesSkipped,errors:e.errors.length});}finally{this.syncingInProgress=false;}}async ingestDocument(e){return await this.ensureInitialized(),this.pipeline.process(e)}async ingestDocuments(e,t){await this.ensureInitialized();let r=[];for(let n=0;n<e.length;n++){let o=await this.pipeline.process(e[n]);r.push(o),t?.((n+1)/e.length);}return r}async scanAndIngest(e){for(await this.ensureInitialized();this.syncingInProgress;)await new Promise(n=>setTimeout(n,100));let t=await this.fileScanner.scanAll(e);if(t.length===0)return [];let r=t.map(n=>n.absolutePath);return this.ingestDocuments(r,n=>{e?.(Math.round(n*t.length),t.length,t[0]||{absolutePath:"ingesting...",relativePath:"ingesting...",source:"docs",contentHash:"",lastModified:0,size:0});})}async ingestUploadedFile(e){await this.ensureInitialized();let t=await this.fileScanner.scanFile(e,"upload");if(!t)throw new Error(`\u65E0\u6CD5\u626B\u63CF\u6587\u4EF6: ${e}`);return this.pipeline.process(t.absolutePath)}async removeDocument(e){await this.ensureInitialized();let t=Filesystem.normalize(e);try{this.db.deleteDocumentByPath(t)&&A.debug("Removed document",{path:t});}catch(r){throw A.error("Failed to remove document",{path:t,error:String(r)}),r}}markDirty(){this.dirty=true,A.debug("Marked as dirty, will sync before next search");}async sync(e){if(await this.ensureInitialized(),this.syncingInProgress)return A.debug("Sync already in progress, skipping..."),{success:true,filesAdded:0,filesUpdated:0,filesDeleted:0,filesSkipped:0,chunksCreated:0,vectorsCreated:0,entitiesExtracted:0,relationsExtracted:0,errors:[],duration:0};this.syncingInProgress=true;try{let t=Date.now(),r={success:!0,filesAdded:0,filesUpdated:0,filesDeleted:0,filesSkipped:0,chunksCreated:0,vectorsCreated:0,entitiesExtracted:0,relationsExtracted:0,errors:[],duration:0},n=await this.fileScanner.scanAll(e),o=new Set(n.map(a=>a.relativePath)),s=this.db.query("SELECT path, contentHash FROM documents"),i=new Map;for(let a of s)i.set(a.path,a.contentHash);for(let[a]of i)if(!o.has(a))try{this.db.deleteDocumentByPath(a)&&(r.filesDeleted++,A.debug("Deleted document from index",{path:a}));}catch(l){let d=l instanceof Error?l.message:String(l);r.errors.push({filePath:a,error:`Failed to delete document: ${d}`}),A.error("Failed to delete document",{path:a,error:d});}for(let a=0;a<n.length;a++){let l=n[a],d=l.relativePath;e?.(a+1,n.length,l);try{let m=i.get(d);if(m===void 0){let u=await this.pipeline.process(l.absolutePath);r.filesAdded++,r.chunksCreated+=u.chunksCreated,r.vectorsCreated+=u.vectorsCreated,r.entitiesExtracted+=u.entitiesExtracted||0,r.relationsExtracted+=u.relationsExtracted||0,A.debug("Added new document",{path:d});}else if(m!==l.contentHash){let u=await this.pipeline.process(l.absolutePath);r.filesUpdated++,r.chunksCreated+=u.chunksCreated,r.vectorsCreated+=u.vectorsCreated,r.entitiesExtracted+=u.entitiesExtracted||0,r.relationsExtracted+=u.relationsExtracted||0,A.debug("Updated document",{path:d});}else r.filesSkipped++;}catch(m){let u=m instanceof Error?m.message:String(m);r.errors.push({filePath:d,error:u}),A.error("Failed to process document",{path:d,error:u});}}r.duration=Date.now()-t,r.success=r.errors.length===0;let c={timestamp:new Date().toISOString(),success:r.success,filesAdded:r.filesAdded,filesUpdated:r.filesUpdated,filesDeleted:r.filesDeleted,filesSkipped:r.filesSkipped,chunksCreated:r.chunksCreated,vectorsCreated:r.vectorsCreated,entitiesExtracted:r.entitiesExtracted,relationsExtracted:r.relationsExtracted,errorCount:r.errors.length,duration:r.duration};return this.db.setMeta("lastSync",c.timestamp),this.db.setMeta("lastSyncResult",JSON.stringify(c)),A.info("Sync completed",{added:r.filesAdded,updated:r.filesUpdated,deleted:r.filesDeleted,skipped:r.filesSkipped,errors:r.errors.length,duration:r.duration}),r}finally{this.syncingInProgress=false;}}async search(e,t){return await this.ensureInitialized(),this.ensureSynced(),this.searchEngine.search(e,t)}async queryNodes(e,t){return await this.ensureInitialized(),typeof e=="string"?this.graphInterface.queryNodes(e,t):this.graphInterface.queryNodes(e)}async queryEdges(e,t){return await this.ensureInitialized(),typeof e=="string"?this.graphInterface.queryEdges(e,t):this.graphInterface.queryEdges(e)}async findPath(e,t,r){return await this.ensureInitialized(),this.graphInterface.findPath(e,t,r)}async queryNeighbors(e,t){return await this.ensureInitialized(),this.graphInterface.queryNeighbors(e,t)}async queryCallGraph(e,t){return await this.ensureInitialized(),this.graphInterface.queryCallGraph(e,t)}async queryInheritance(e){return await this.ensureInitialized(),this.graphInterface.queryInheritance(e)}async createNode(e,t,r){return await this.ensureInitialized(),this.graphInterface.createNode(e,t,r)}async updateNode(e,t){return await this.ensureInitialized(),this.graphInterface.updateNode(e,t)}async deleteNode(e){return await this.ensureInitialized(),this.graphInterface.deleteNode(e)}async createEdge(e,t,r,n){return await this.ensureInitialized(),this.graphInterface.createEdge(e,t,r,n)}async deleteEdge(e){return await this.ensureInitialized(),this.graphInterface.deleteEdge(e)}getStatus(){let e=this.db.getPath(),t=0,r=0,n=0,o=0;try{t=this.db.getDb().prepare("SELECT COUNT(*) as count FROM documents").get()?.count||0,r=this.db.getDb().prepare("SELECT COUNT(*) as count FROM chunks").get()?.count||0,n=this.db.getDb().prepare("SELECT COUNT(*) as count FROM nodes").get()?.count||0,o=this.db.getDb().prepare("SELECT COUNT(*) as count FROM edges").get()?.count||0;}catch{}return {dbPath:e,workspaceDir:this.config.workspaceDir,documentsCount:t,chunksCount:r,nodesCount:n,edgesCount:o,embeddingLlmAvailable:!!this.config.embeddingLlm,graphLlmAvailable:!!this.config.graphLlm,rerankLlmAvailable:!!this.config.rerankLlm,healthy:this.initialized,meta:this.getMetaMap()}}getMetaMap(){let e={};try{let t=this.db.query("SELECT key, value FROM meta");for(let r of t)e[r.key]=r.value;}catch{}return e}async healthCheck(){try{return this.db.query("SELECT 1"),!0}catch{return  false}}async clear(){this.db.transaction(()=>{this.db.run("DELETE FROM chunks"),this.db.run("DELETE FROM documents"),this.db.run("DELETE FROM vectors"),this.db.run("DELETE FROM nodes"),this.db.run("DELETE FROM edges"),this.db.run("DELETE FROM embedding_cache"),this.db.run("DELETE FROM sqlite_sequence WHERE name IN ('chunks', 'documents', 'nodes', 'edges')");});try{this.db.run("INSERT INTO chunks_fts(chunks_fts) VALUES('delete-all')");}catch(e){A.warn("Failed to clear FTS index",{error:String(e)});}A.info("Note knowledge base cleared");}async ensureInitialized(){this.initialized||await this.initialize();}async close(){this.initialized&&(this.stopSyncTimer(),await this.db.close(),this.initialized=false,A.info("NoteKnowledge closed"));}};async function Ge(E){let e=new ee(E);return await e.initialize(),e}export{W as FileScanner,R as KnowledgeBaseError,H as KnowledgeBaseErrorCode,D as Llm,ee as NoteKnowledge,pt as addWord,ft as closeTokenizer,Ge as createNoteKnowledge,ht as initTokenizer,Et as isInitialized,Ie as loadCustomDict,ne as toFtsTokens,se as toFtsTokensForSearch,Ne as tokenize,Te as tokenizeForSearch};