toolnet-memory 0.2.9 → 0.2.11

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.
@@ -1,4 +1,4 @@
1
- import"dotenv/config";import{existsSync as ae,readFileSync as ce}from"node:fs";import{homedir as de}from"node:os";import{join as pe}from"node:path";function me(s){let e=s.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function le(){let s=process.env.TOOLNET_GLOBAL_ENV??pe(de(),".config","toolnet-memory",".env");if(!ae(s))return;let e=ce(s,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let i=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=me(r.slice(o+1)))}}le();function C(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function R(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function K(){return{memory:{autoCapture:C(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:C(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:C(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:C(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:R(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:R(process.env.MEMORY_RERANK_TOP,10),finalContext:R(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:R(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:R(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as ge}from"node:crypto";import{existsSync as F,mkdirSync as ue,readFileSync as fe,renameSync as ye,writeFileSync as he}from"node:fs";import{basename as Se,dirname as E,join as T,parse as J,resolve as k}from"node:path";var H=".toolnet",ve="project.json";function xe(s){return ge("sha256").update(s).digest("hex").slice(0,16)}function L(s){return T(s,H,ve)}function je(s){return F(L(s))}function be(s,e){let t=k(s),r=J(t).root;for(;;){if(je(t))return t;if(t===r||e&&t===k(e))break;let o=E(t);if(o===t)break;t=o}return null}function we(s){let e=k(s),t=J(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(i=>F(T(e,i))))return e;if(e===t)break;let o=E(e);if(o===e)break;e=o}return k(s)}function Pe(s){let e;try{e=JSON.parse(fe(s,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${s}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${s}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${s}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${s}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:E(E(s)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function V(s,e){let t=T(s,H);ue(t,{recursive:!0});let r=L(s),o=`${r}.tmp-${process.pid}`;he(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),ye(o,r)}function G(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var A=class{detect(e=process.cwd()){let t=k(e),r=we(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>F(T(r,m))),n=be(t,i?r:void 0);if(n){let m=L(n),g=Pe(m);return g.rootPath!==n&&(g.rootPath=n,g.updatedAt=new Date().toISOString(),V(n,g)),G(g,n)}let l=new Date().toISOString(),d=Se(r),c={version:1,id:xe(r),name:d,remote:d,rootPath:r,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return V(r,c),G(c,r)}};import{homedir as ze}from"node:os";import{join as Be}from"node:path";import{DeleteObjectCommand as Re,GetObjectCommand as ke,HeadObjectCommand as Me,ListObjectsV2Command as Ce,PutObjectCommand as Ee,S3Client as Ae}from"@aws-sdk/client-s3";import{getSignedUrl as Te}from"@aws-sdk/s3-request-presigner";var I=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ae({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ee({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Te(this.client,new ke({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Me({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Re({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Ce({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as U,mkdir as Ie,readFile as Oe,readdir as $e,rm as Ne,stat as W,writeFile as _e}from"node:fs/promises";import{dirname as De,join as Fe,relative as Y,resolve as Le}from"node:path";var M=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Le(this.root,t)}async put(e,t){let r=this.path(e);await Ie(De(r),{recursive:!0}),await _e(r,t)}async get(e){try{return await Oe(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await U(this.path(e)),!0}catch{return!1}}async delete(e){await Ne(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await U(t)}catch{return r}let o=async n=>{let l=await $e(n,{withFileTypes:!0});for(let d of l){let c=Fe(n,d.name);if(d.isDirectory()){await o(c);continue}let m=await W(c);r.push({key:Y(this.root,c),size:m.size,updatedAt:m.mtime.toISOString()})}},i=await W(t);return i.isDirectory()?await o(t):r.push({key:Y(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),r}};function Z(s){let e=s.localRoot??Be(ze(),".toolnet-memory","storage");if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new I({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):(console.warn("[storage] Hugging Face credentials missing. Using local fallback."),new M(e))}return new M(e)}var O=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var $=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","manifest.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function X(s){let e=s.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function Q(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var N=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=X(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let n;try{n=JSON.parse(o)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${l instanceof Error?l.message:String(l)}`)}if(typeof n.id=="string"&&n.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${n.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof n.createdAt=="string"&&(r=n.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(i,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=Q(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as qe}from"node:path";import{readdir as Je}from"node:fs/promises";import{extname as He,join as Ue,relative as We}from"node:path";import{basename as Ke}from"node:path";var Ve=new Set([".env",".env.local",".env.production",".env.development","id_rsa","id_ed25519","credentials","credentials.json","service-account.json"]),Ge=new Set([".pem",".key",".p12",".pfx"]);function q(s){let e=Ke(s).toLowerCase();if(Ve.has(e))return!0;for(let t of Ge)if(e.endsWith(t))return!0;return!1}var Ye=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".mts",".cts"]),Ze=new Set(["node_modules",".git","dist","build","coverage",".next",".cache",".toolnet-memory"]);async function ee(s){let e=[];async function t(r){let o=await Je(r,{withFileTypes:!0});for(let i of o){if(Ze.has(i.name))continue;let n=Ue(r,i.name);if(i.isDirectory()){await t(n);continue}Ye.has(He(i.name))&&(q(n)||e.push(We(s,n)))}}return await t(s),e.sort()}import{createHash as Xe}from"node:crypto";import{readFile as Qe}from"node:fs/promises";async function te(s){let e=await Qe(s);return Xe("sha256").update(e).digest("hex")}async function re(s,e){let t=await ee(e),r={};for(let o of t)r[o]={path:o,hash:await te(qe(e,o))};return{version:1,projectId:s,updatedAt:new Date().toISOString(),files:r}}function se(s,e){let t=s?.files??{},r=e.files,o=[],i=[],n=[],l=[];for(let[d,c]of Object.entries(r)){let m=t[d];m?m.hash!==c.hash?i.push(d):l.push(d):o.push(d)}for(let d of Object.keys(t))r[d]||n.push(d);return{added:o.sort(),modified:i.sort(),deleted:n.sort(),unchanged:l.sort()}}import{readFile as et}from"node:fs/promises";import{join as tt}from"node:path";import{createHash as rt}from"node:crypto";import*as p from"typescript";function oe(s,e){return rt("sha256").update(`${s}:${e}`).digest("hex").slice(0,24)}function z(s,e){return s.getLineAndCharacterOfPosition(e.getStart(s)).line+1}async function ne(s,e,t){let r=tt(e,t),o=await et(r,"utf8"),i=p.createSourceFile(t,o,p.ScriptTarget.Latest,!0,t.endsWith(".tsx")||t.endsWith(".jsx")?p.ScriptKind.TSX:p.ScriptKind.TS),n=[],l=[],d=[],c=[],m={id:oe(s,`file:${t}`),projectId:s,name:t,qualifiedName:t,type:"file",filePath:t,startLine:1,endLine:i.getLineAndCharacterOfPosition(i.end).line+1};n.push(m);let g=[],v=[];function y(a,S,h,f){let j=oe(s,`${t}:${h}:${f??S}:${a.pos}`);return n.push({id:j,projectId:s,name:S,qualifiedName:f??S,type:h,filePath:t,startLine:z(i,a),endLine:i.getLineAndCharacterOfPosition(a.end).line+1}),j}function w(a){if(!p.isStringLiteral(a.moduleSpecifier))return;let S=[],h=a.importClause;if(h?.name&&S.push({localName:h.name.text,importedName:"default",kind:"default"}),h?.namedBindings&&p.isNamedImports(h.namedBindings))for(let f of h.namedBindings.elements)S.push({localName:f.name.text,importedName:f.propertyName?.text??f.name.text,kind:"named"});h?.namedBindings&&p.isNamespaceImport(h.namedBindings)&&S.push({localName:h.namedBindings.name.text,importedName:"*",kind:"namespace"}),l.push({source:a.moduleSpecifier.text,bindings:S})}function x(a,S){for(let h of a.heritageClauses??[])for(let f of h.types){let j=f.expression.getText(i).split(".").at(-1)??"";j&&c.push({fromId:S,targetName:j,type:h.token===p.SyntaxKind.ExtendsKeyword?"INHERITS":"IMPLEMENTS"})}}function u(a){let S=!1,h=!1;if(p.isImportDeclaration(a)&&w(a),p.isInterfaceDeclaration(a)&&y(a,a.name.text,"interface",a.name.text),p.isClassDeclaration(a)&&a.name){let f=y(a,a.name.text,"class",a.name.text);x(a,f),v.push({id:f,name:a.name.text}),h=!0}if(p.isFunctionDeclaration(a)&&a.name){let f=y(a,a.name.text,"function",a.name.text);g.push(f),S=!0}if(p.isMethodDeclaration(a)&&a.name){let f=a.name.getText(i),j=v.at(-1)?.name,ie=y(a,f,"method",j?`${j}.${f}`:f);g.push(ie),S=!0}if(p.isCallExpression(a)&&(p.isIdentifier(a.expression)&&d.push({callerId:g.at(-1),calleeName:a.expression.text,line:z(i,a)}),p.isPropertyAccessExpression(a.expression))){let f=a.expression;d.push({callerId:g.at(-1),qualifier:f.expression.getText(i),calleeName:f.name.text,line:z(i,a)})}p.forEachChild(a,u),S&&g.pop(),h&&v.pop()}return u(i),{filePath:t,symbols:n,imports:l,calls:d,heritage:c}}import{createHash as st}from"node:crypto";import{dirname as ot,extname as nt,normalize as it,posix as at}from"node:path";var P=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};function ct(s,e,t,r){return st("sha256").update(`${s}:${e}:${t}:${r}`).digest("hex").slice(0,24)}function b(s){return it(s).replaceAll("\\","/")}function B(s,e,t){if(!e.startsWith("."))return;let r=b(at.join(ot(s),e)),o=nt(r),i;if(o===".js"){let n=r.slice(0,-3);i=[`${n}.ts`,`${n}.tsx`,`${n}.js`,`${n}.jsx`]}else if(o===".mjs"){let n=r.slice(0,-4);i=[`${n}.mts`,`${n}.ts`,`${n}.mjs`]}else if(o===".cjs"){let n=r.slice(0,-4);i=[`${n}.cts`,`${n}.ts`,`${n}.cjs`]}else o?i=[r]:i=[`${r}.ts`,`${r}.tsx`,`${r}.mts`,`${r}.cts`,`${r}.js`,`${r}.jsx`,`${r}.mjs`,`${r}.cjs`,`${r}/index.ts`,`${r}/index.tsx`,`${r}/index.js`,`${r}/index.jsx`];return i.find(n=>t.has(b(n)))}var _=class{build(e,t){let r=new P,o=new Map;for(let n of t)for(let l of n.symbols)r.addSymbol(l),l.type==="file"&&o.set(b(n.filePath),l);let i=new Set(o.keys());for(let n of t){let l=b(n.filePath),d=o.get(l);if(d){for(let c of n.symbols)c.id!==d.id&&r.addEdge(this.edge(e,d.id,"DEFINES",c.id));for(let c of n.imports){let m=B(l,c.source,i);if(!m)continue;let g=o.get(m);g&&r.addEdge(this.edge(e,d.id,"IMPORTS",g.id,{source:c.source}))}for(let c of n.heritage){let m=r.findByName(e,c.targetName).filter(g=>g.type==="class"||g.type==="interface");for(let g of m)r.addEdge(this.edge(e,c.fromId,c.type,g.id))}for(let c of n.calls){if(!c.callerId)continue;let m=this.resolveCallTargets(e,n,c.calleeName,c.qualifier,t,r,i);for(let g of m)g.id!==c.callerId&&r.addEdge(this.edge(e,c.callerId,"CALLS",g.id,{line:c.line,qualifier:c.qualifier}))}}}return r}resolveCallTargets(e,t,r,o,i,n,l){let d=b(t.filePath),c=n.findByName(e,r).filter(m=>b(m.filePath)===d);if(!o&&c.length)return c;if(o)for(let m of t.imports){if(!m.bindings.find(y=>y.kind==="namespace"&&y.localName===o))continue;let v=B(d,m.source,l);if(v)return n.findByName(e,r).filter(y=>b(y.filePath)===v)}for(let m of t.imports){let g=m.bindings.find(x=>x.localName===r);if(!g)continue;let v=B(d,m.source,l);if(!v)continue;let y=g.importedName==="default"?r:g.importedName,w=n.findByName(e,y).filter(x=>b(x.filePath)===v);if(w.length)return w}return n.findByName(e,r).filter(m=>m.type==="function"||m.type==="method")}edge(e,t,r,o,i){return{id:ct(e,t,r,o),projectId:e,from:t,to:o,type:r,metadata:i}}};var D=class{constructor(e){this.storage=e}storage;async index(e,t){let r=new $(this.storage),o=new O(this.storage),i=await r.load(e),n=await o.load(e),l=await re(e,t),d=se(i,l),c=[...d.added,...d.modified];if(i&&n&&c.length===0&&d.deleted.length===0)return{firstRun:!1,scanned:Object.keys(l.files).length,parsed:0,added:0,modified:0,deleted:0,unchanged:d.unchanged.length,symbols:n.symbols.length,edges:n.edges.length,saved:!1};let m=n?.symbols.filter(u=>d.unchanged.includes(u.filePath))??[],g=[];for(let u of c)g.push(await ne(e,t,u));let v=new _().build(e,g),y=new P;for(let u of m)y.addSymbol(u);for(let u of v.allSymbols(e))y.addSymbol(u);let w=new Set(y.allSymbols(e).map(u=>u.id));for(let u of n?.edges??[])w.has(u.from)&&w.has(u.to)&&!c.includes(n.symbols.find(a=>a.id===u.from)?.filePath??"")&&!c.includes(n.symbols.find(a=>a.id===u.to)?.filePath??"")&&y.addEdge(u);for(let u of v.allEdges(e))y.addEdge(u);let x={version:1,projectId:e,updatedAt:new Date().toISOString(),files:Object.keys(l.files).length,symbols:y.allSymbols(e),edges:y.allEdges(e)};return await o.save(x),await r.save(l),{firstRun:i===null,scanned:Object.keys(l.files).length,parsed:c.length,added:d.added.length,modified:d.modified.length,deleted:d.deleted.length,unchanged:d.unchanged.length,symbols:x.symbols.length,edges:x.edges.length,saved:!0}}};async function dt(){let s=K(),e=new A().detect(),t=Z({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),r=new N(t,e.id,e.name,e.remote??e.name),o=await new D(r).index(e.id,e.rootPath);console.log({ok:!0,storage:r.name,...o})}dt().catch(s=>{console.error(s),process.exit(1)});
1
+ import"dotenv/config";import{existsSync as de,readFileSync as pe}from"node:fs";import{homedir as me}from"node:os";import{join as le}from"node:path";function ue(s){let e=s.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ge(){let s=process.env.TOOLNET_GLOBAL_ENV??le(me(),".config","toolnet-memory",".env");if(!de(s))return;let e=pe(s,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let i=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=ue(r.slice(o+1)))}}ge();function k(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function C(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function G(){return{memory:{autoCapture:k(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:k(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:k(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:k(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:C(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:C(process.env.MEMORY_RERANK_TOP,10),finalContext:C(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:C(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:k(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:C(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as fe}from"node:crypto";import{existsSync as F,mkdirSync as ye,readFileSync as he,renameSync as Se,writeFileSync as ve}from"node:fs";import{basename as xe,dirname as I,join as T,parse as J,resolve as E}from"node:path";var Y=".toolnet",be="project.json";function je(s){return fe("sha256").update(s).digest("hex").slice(0,16)}function L(s){return T(s,Y,be)}function we(s){return F(L(s))}function Pe(s,e){let t=E(s),r=J(t).root;for(;;){if(we(t))return t;if(t===r||e&&t===E(e))break;let o=I(t);if(o===t)break;t=o}return null}function ke(s){let e=E(s),t=J(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(i=>F(T(e,i))))return e;if(e===t)break;let o=I(e);if(o===e)break;e=o}return E(s)}function Ce(s){let e;try{e=JSON.parse(he(s,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${s}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${s}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${s}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${s}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:I(I(s)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function U(s,e){let t=T(s,Y);ye(t,{recursive:!0});let r=L(s),o=`${r}.tmp-${process.pid}`;ve(o,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Se(o,r)}function H(s,e){return{id:s.id,name:s.name,remote:s.remote,rootPath:e,createdAt:s.createdAt,updatedAt:s.updatedAt,graphVersion:s.graphVersion,memoryVersion:s.memoryVersion,metadata:s.metadata}}var M=class{detect(e=process.cwd()){let t=E(e),r=ke(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>F(T(r,m))),n=Pe(t,i?r:void 0);if(n){let m=L(n),u=Ce(m);return u.rootPath!==n&&(u.rootPath=n,u.updatedAt=new Date().toISOString(),U(n,u)),H(u,n)}let l=new Date().toISOString(),d=xe(r),c={version:1,id:je(r),name:d,remote:d,rootPath:r,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return U(r,c),H(c,r)}};import{homedir as We}from"node:os";import{join as Ze}from"node:path";import{DeleteObjectCommand as Ee,GetObjectCommand as Ae,HeadObjectCommand as Re,ListObjectsV2Command as Ie,PutObjectCommand as Me,S3Client as Te}from"@aws-sdk/client-s3";import{getSignedUrl as Oe}from"@aws-sdk/s3-request-presigner";var O=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Te({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Me({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Oe(this.client,new Ae({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Re({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ee({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Ie({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as W,mkdir as $e,readFile as Ne,readdir as _e,rm as Ke,stat as Z,writeFile as De}from"node:fs/promises";import{dirname as Fe,join as Le,relative as Q,resolve as Be}from"node:path";var A=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Be(this.root,t)}async put(e,t){let r=this.path(e);await $e(Fe(r),{recursive:!0}),await De(r,t)}async get(e){try{return await Ne(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await W(this.path(e)),!0}catch{return!1}}async delete(e){await Ke(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await W(t)}catch{return r}let o=async n=>{let l=await _e(n,{withFileTypes:!0});for(let d of l){let c=Le(n,d.name);if(d.isDirectory()){await o(c);continue}let m=await Z(c);r.push({key:Q(this.root,c),size:m.size,updatedAt:m.mtime.toISOString()})}},i=await Z(t);return i.isDirectory()?await o(t):r.push({key:Q(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),r}};import{DeleteObjectCommand as ze,GetObjectCommand as Ve,HeadObjectCommand as Ge,ListObjectsV2Command as Ue,PutObjectCommand as He,S3Client as Je}from"@aws-sdk/client-s3";import{getSignedUrl as Ye}from"@aws-sdk/s3-request-presigner";var R=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Je({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new He({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Ye(this.client,new Ve({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ge({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new ze({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new Ue({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};function B(s,e){return console.warn(e),new A(s)}function X(s){let e=s.localRoot??Ze(We(),".toolnet-memory","storage");if(s.provider==="r2"){let t=s.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new R({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(s.provider==="s3"){let t=s.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new R({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] S3 credentials missing. Using local fallback.")}if(s.provider==="huggingface"){let t=s.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new O({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):B(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new A(e)}var $=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var N=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","manifest.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function q(s){let e=s.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function ee(s){let e=s.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var _=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=q(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let n;try{n=JSON.parse(o)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${l instanceof Error?l.message:String(l)}`)}if(typeof n.id=="string"&&n.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${n.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof n.createdAt=="string"&&(r=n.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(i,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=ee(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as ct}from"node:path";import{readdir as et}from"node:fs/promises";import{extname as tt,join as rt,relative as st}from"node:path";import{basename as Qe}from"node:path";var Xe=new Set([".env",".env.local",".env.production",".env.development","id_rsa","id_ed25519","credentials","credentials.json","service-account.json"]),qe=new Set([".pem",".key",".p12",".pfx"]);function te(s){let e=Qe(s).toLowerCase();if(Xe.has(e))return!0;for(let t of qe)if(e.endsWith(t))return!0;return!1}var ot=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".mts",".cts"]),nt=new Set(["node_modules",".git","dist","build","coverage",".next",".cache",".toolnet-memory"]);async function re(s){let e=[];async function t(r){let o=await et(r,{withFileTypes:!0});for(let i of o){if(nt.has(i.name))continue;let n=rt(r,i.name);if(i.isDirectory()){await t(n);continue}ot.has(tt(i.name))&&(te(n)||e.push(st(s,n)))}}return await t(s),e.sort()}import{createHash as it}from"node:crypto";import{readFile as at}from"node:fs/promises";async function se(s){let e=await at(s);return it("sha256").update(e).digest("hex")}async function oe(s,e){let t=await re(e),r={};for(let o of t)r[o]={path:o,hash:await se(ct(e,o))};return{version:1,projectId:s,updatedAt:new Date().toISOString(),files:r}}function ne(s,e){let t=s?.files??{},r=e.files,o=[],i=[],n=[],l=[];for(let[d,c]of Object.entries(r)){let m=t[d];m?m.hash!==c.hash?i.push(d):l.push(d):o.push(d)}for(let d of Object.keys(t))r[d]||n.push(d);return{added:o.sort(),modified:i.sort(),deleted:n.sort(),unchanged:l.sort()}}import{readFile as dt}from"node:fs/promises";import{join as pt}from"node:path";import{createHash as mt}from"node:crypto";import*as p from"typescript";function ie(s,e){return mt("sha256").update(`${s}:${e}`).digest("hex").slice(0,24)}function z(s,e){return s.getLineAndCharacterOfPosition(e.getStart(s)).line+1}async function ae(s,e,t){let r=pt(e,t),o=await dt(r,"utf8"),i=p.createSourceFile(t,o,p.ScriptTarget.Latest,!0,t.endsWith(".tsx")||t.endsWith(".jsx")?p.ScriptKind.TSX:p.ScriptKind.TS),n=[],l=[],d=[],c=[],m={id:ie(s,`file:${t}`),projectId:s,name:t,qualifiedName:t,type:"file",filePath:t,startLine:1,endLine:i.getLineAndCharacterOfPosition(i.end).line+1};n.push(m);let u=[],v=[];function y(a,S,h,f){let b=ie(s,`${t}:${h}:${f??S}:${a.pos}`);return n.push({id:b,projectId:s,name:S,qualifiedName:f??S,type:h,filePath:t,startLine:z(i,a),endLine:i.getLineAndCharacterOfPosition(a.end).line+1}),b}function w(a){if(!p.isStringLiteral(a.moduleSpecifier))return;let S=[],h=a.importClause;if(h?.name&&S.push({localName:h.name.text,importedName:"default",kind:"default"}),h?.namedBindings&&p.isNamedImports(h.namedBindings))for(let f of h.namedBindings.elements)S.push({localName:f.name.text,importedName:f.propertyName?.text??f.name.text,kind:"named"});h?.namedBindings&&p.isNamespaceImport(h.namedBindings)&&S.push({localName:h.namedBindings.name.text,importedName:"*",kind:"namespace"}),l.push({source:a.moduleSpecifier.text,bindings:S})}function x(a,S){for(let h of a.heritageClauses??[])for(let f of h.types){let b=f.expression.getText(i).split(".").at(-1)??"";b&&c.push({fromId:S,targetName:b,type:h.token===p.SyntaxKind.ExtendsKeyword?"INHERITS":"IMPLEMENTS"})}}function g(a){let S=!1,h=!1;if(p.isImportDeclaration(a)&&w(a),p.isInterfaceDeclaration(a)&&y(a,a.name.text,"interface",a.name.text),p.isClassDeclaration(a)&&a.name){let f=y(a,a.name.text,"class",a.name.text);x(a,f),v.push({id:f,name:a.name.text}),h=!0}if(p.isFunctionDeclaration(a)&&a.name){let f=y(a,a.name.text,"function",a.name.text);u.push(f),S=!0}if(p.isMethodDeclaration(a)&&a.name){let f=a.name.getText(i),b=v.at(-1)?.name,ce=y(a,f,"method",b?`${b}.${f}`:f);u.push(ce),S=!0}if(p.isCallExpression(a)&&(p.isIdentifier(a.expression)&&d.push({callerId:u.at(-1),calleeName:a.expression.text,line:z(i,a)}),p.isPropertyAccessExpression(a.expression))){let f=a.expression;d.push({callerId:u.at(-1),qualifier:f.expression.getText(i),calleeName:f.name.text,line:z(i,a)})}p.forEachChild(a,g),S&&u.pop(),h&&v.pop()}return g(i),{filePath:t,symbols:n,imports:l,calls:d,heritage:c}}import{createHash as lt}from"node:crypto";import{dirname as ut,extname as gt,normalize as ft,posix as yt}from"node:path";var P=class{symbols=new Map;edges=new Map;addSymbol(e){this.symbols.set(e.id,e)}addEdge(e){this.edges.set(e.id,e)}getSymbol(e){return this.symbols.get(e)}findByName(e,t){return[...this.symbols.values()].filter(r=>r.projectId===e&&(r.name===t||r.qualifiedName===t))}allSymbols(e){return[...this.symbols.values()].filter(t=>t.projectId===e)}allEdges(e){return[...this.edges.values()].filter(t=>t.projectId===e)}import(e,t){for(let r of e)this.addSymbol(r);for(let r of t)this.addEdge(r)}clearProject(e){for(let[t,r]of this.symbols)r.projectId===e&&this.symbols.delete(t);for(let[t,r]of this.edges)r.projectId===e&&this.edges.delete(t)}};function ht(s,e,t,r){return lt("sha256").update(`${s}:${e}:${t}:${r}`).digest("hex").slice(0,24)}function j(s){return ft(s).replaceAll("\\","/")}function V(s,e,t){if(!e.startsWith("."))return;let r=j(yt.join(ut(s),e)),o=gt(r),i;if(o===".js"){let n=r.slice(0,-3);i=[`${n}.ts`,`${n}.tsx`,`${n}.js`,`${n}.jsx`]}else if(o===".mjs"){let n=r.slice(0,-4);i=[`${n}.mts`,`${n}.ts`,`${n}.mjs`]}else if(o===".cjs"){let n=r.slice(0,-4);i=[`${n}.cts`,`${n}.ts`,`${n}.cjs`]}else o?i=[r]:i=[`${r}.ts`,`${r}.tsx`,`${r}.mts`,`${r}.cts`,`${r}.js`,`${r}.jsx`,`${r}.mjs`,`${r}.cjs`,`${r}/index.ts`,`${r}/index.tsx`,`${r}/index.js`,`${r}/index.jsx`];return i.find(n=>t.has(j(n)))}var K=class{build(e,t){let r=new P,o=new Map;for(let n of t)for(let l of n.symbols)r.addSymbol(l),l.type==="file"&&o.set(j(n.filePath),l);let i=new Set(o.keys());for(let n of t){let l=j(n.filePath),d=o.get(l);if(d){for(let c of n.symbols)c.id!==d.id&&r.addEdge(this.edge(e,d.id,"DEFINES",c.id));for(let c of n.imports){let m=V(l,c.source,i);if(!m)continue;let u=o.get(m);u&&r.addEdge(this.edge(e,d.id,"IMPORTS",u.id,{source:c.source}))}for(let c of n.heritage){let m=r.findByName(e,c.targetName).filter(u=>u.type==="class"||u.type==="interface");for(let u of m)r.addEdge(this.edge(e,c.fromId,c.type,u.id))}for(let c of n.calls){if(!c.callerId)continue;let m=this.resolveCallTargets(e,n,c.calleeName,c.qualifier,t,r,i);for(let u of m)u.id!==c.callerId&&r.addEdge(this.edge(e,c.callerId,"CALLS",u.id,{line:c.line,qualifier:c.qualifier}))}}}return r}resolveCallTargets(e,t,r,o,i,n,l){let d=j(t.filePath),c=n.findByName(e,r).filter(m=>j(m.filePath)===d);if(!o&&c.length)return c;if(o)for(let m of t.imports){if(!m.bindings.find(y=>y.kind==="namespace"&&y.localName===o))continue;let v=V(d,m.source,l);if(v)return n.findByName(e,r).filter(y=>j(y.filePath)===v)}for(let m of t.imports){let u=m.bindings.find(x=>x.localName===r);if(!u)continue;let v=V(d,m.source,l);if(!v)continue;let y=u.importedName==="default"?r:u.importedName,w=n.findByName(e,y).filter(x=>j(x.filePath)===v);if(w.length)return w}return n.findByName(e,r).filter(m=>m.type==="function"||m.type==="method")}edge(e,t,r,o,i){return{id:ht(e,t,r,o),projectId:e,from:t,to:o,type:r,metadata:i}}};var D=class{constructor(e){this.storage=e}storage;async index(e,t){let r=new N(this.storage),o=new $(this.storage),i=await r.load(e),n=await o.load(e),l=await oe(e,t),d=ne(i,l),c=[...d.added,...d.modified];if(i&&n&&c.length===0&&d.deleted.length===0)return{firstRun:!1,scanned:Object.keys(l.files).length,parsed:0,added:0,modified:0,deleted:0,unchanged:d.unchanged.length,symbols:n.symbols.length,edges:n.edges.length,saved:!1};let m=n?.symbols.filter(g=>d.unchanged.includes(g.filePath))??[],u=[];for(let g of c)u.push(await ae(e,t,g));let v=new K().build(e,u),y=new P;for(let g of m)y.addSymbol(g);for(let g of v.allSymbols(e))y.addSymbol(g);let w=new Set(y.allSymbols(e).map(g=>g.id));for(let g of n?.edges??[])w.has(g.from)&&w.has(g.to)&&!c.includes(n.symbols.find(a=>a.id===g.from)?.filePath??"")&&!c.includes(n.symbols.find(a=>a.id===g.to)?.filePath??"")&&y.addEdge(g);for(let g of v.allEdges(e))y.addEdge(g);let x={version:1,projectId:e,updatedAt:new Date().toISOString(),files:Object.keys(l.files).length,symbols:y.allSymbols(e),edges:y.allEdges(e)};return await o.save(x),await r.save(l),{firstRun:i===null,scanned:Object.keys(l.files).length,parsed:c.length,added:d.added.length,modified:d.modified.length,deleted:d.deleted.length,unchanged:d.unchanged.length,symbols:x.symbols.length,edges:x.edges.length,saved:!0}}};async function St(){let s=G(),e=new M().detect(),t=X({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),r=new _(t,e.id,e.name,e.remote??e.name),o=await new D(r).index(e.id,e.rootPath);console.log({ok:!0,storage:r.name,...o})}St().catch(s=>{console.error(s),process.exit(1)});
package/bundle/learner.js CHANGED
@@ -1,8 +1,8 @@
1
- import{existsSync as X,readFileSync as ee}from"node:fs";import{homedir as te}from"node:os";import{join as re}from"node:path";function oe(o){let e=o.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ne(){let o=process.env.TOOLNET_GLOBAL_ENV??re(te(),".config","toolnet-memory",".env");if(!X(o))return;let e=ee(o,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let n=r.indexOf("=");if(n<=0)continue;let s=r.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=oe(r.slice(n+1)))}}ne();function f(o,e){return o===void 0?e:["1","true","yes","on"].includes(o.toLowerCase())}function u(o,e){if(!o)return e;let t=Number(o);return Number.isFinite(t)?t:e}function O(){return{memory:{autoCapture:f(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:f(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:f(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:f(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:u(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:u(process.env.MEMORY_RERANK_TOP,10),finalContext:u(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:u(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:u(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}var se={critical:100,high:75,normal:50,temporary:20};function C(o){return se[o]}function _(o,e){let t=e.toLowerCase();return t.includes("kh\xF4ng \u0111\u01B0\u1EE3c")||t.includes("b\u1EAFt bu\u1ED9c")||t.includes("must not")||t.includes("critical")?"critical":o==="rule"||o==="decision"?"high":o==="todo"?"normal":o==="activity"||o==="summary"?"temporary":"normal"}import{createHash as ie}from"node:crypto";import{existsSync as A,mkdirSync as ae,readFileSync as ce,renameSync as pe,writeFileSync as de}from"node:fs";import{basename as me,dirname as y,join as S,parse as z,resolve as g}from"node:path";var L=".toolnet",ue="project.json";function ge(o){return ie("sha256").update(o).digest("hex").slice(0,16)}function M(o){return S(o,L,ue)}function le(o){return A(M(o))}function fe(o,e){let t=g(o),r=z(t).root;for(;;){if(le(t))return t;if(t===r||e&&t===g(e))break;let n=y(t);if(n===t)break;t=n}return null}function ye(o){let e=g(o),t=z(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(s=>A(S(e,s))))return e;if(e===t)break;let n=y(e);if(n===e)break;e=n}return g(o)}function he(o){let e;try{e=JSON.parse(ce(o,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${o}: ${n instanceof Error?n.message:String(n)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${o}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${o}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${o}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:y(y(o)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function N(o,e){let t=S(o,L);ae(t,{recursive:!0});let r=M(o),n=`${r}.tmp-${process.pid}`;de(n,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),pe(n,r)}function $(o,e){return{id:o.id,name:o.name,remote:o.remote,rootPath:e,createdAt:o.createdAt,updatedAt:o.updatedAt,graphVersion:o.graphVersion,memoryVersion:o.memoryVersion,metadata:o.metadata}}var h=class{detect(e=process.cwd()){let t=g(e),r=ye(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>A(S(r,m))),i=fe(t,s?r:void 0);if(i){let m=M(i),p=he(m);return p.rootPath!==i&&(p.rootPath=i,p.updatedAt=new Date().toISOString(),N(i,p)),$(p,i)}let a=new Date().toISOString(),c=me(r),d={version:1,id:ge(r),name:c,remote:c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return N(r,d),$(d,r)}};import{randomUUID as ke}from"node:crypto";var Se=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_\-]{20,}\b/g},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._\-]{16,}\b/gi},{type:"jwt",regex:/\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b/g},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g},{type:"password_assignment",regex:/\b(password|passwd|pwd)\s*[:=]\s*["']?[^"'\s]{6,}["']?/gi},{type:"secret_assignment",regex:/\b(secret|api[_-]?key|access[_-]?token|refresh[_-]?token)\s*[:=]\s*["']?[^"'\s]{8,}["']?/gi},{type:"cookie",regex:/\b(cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi}],j=class{scan(e){let t=[];for(let r of Se){let n=new RegExp(r.regex.source,r.regex.flags);for(let s of e.matchAll(n))t.push({type:r.type,value:s[0]})}return t}hasSecrets(e){return this.scan(e).length>0}};var v=class{scanner=new j;sanitize(e){let t=e,r=this.scanner.scan(e),n=new Set;for(let s of r)n.add(s.type),t=t.split(s.value).join(`[REDACTED:${s.type}]`);return{text:t,redacted:r.length,secretTypes:[...n]}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(t=>this.sanitizeValue(t));if(e&&typeof e=="object"){let t={};for(let[r,n]of Object.entries(e)){let s=r.toLowerCase();s.includes("password")||s.includes("secret")||s.includes("token")||s.includes("cookie")||s.includes("authorization")?t[r]="[REDACTED]":t[r]=this.sanitizeValue(n)}return t}return e}};var je=new Set(["decision","rule","todo","summary","activity","user","error","file","write","command","learned","session","architecture","fix","context","opencode","agy","codex"]),ve=new Set(["d\xF9ng","s\u1EED","d\u1EE5ng","use","using","the","a","an","cho","v\xE0","l\xE0","c\u1EE7a","to","for","with","quy\u1EBFt","\u0111\u1ECBnh"]);function D(o){return new Set(o.toLowerCase().normalize("NFKC").replace(/[^\p{L}\p{N}_-]+/gu," ").split(/\s+/).filter(e=>e.length>2&&!ve.has(e)))}function we(o,e){let t=D(o),r=D(e);if(t.size===0||r.size===0)return 0;let n=0;for(let i of t)r.has(i)&&n++;let s=new Set([...t,...r]).size;return n/s}function K(o){return new Set(o.tags.filter(e=>!je.has(e.toLowerCase())))}var w=class{findSuperseded(e,t){if(e.type!=="rule"&&e.type!=="decision")return[];let r=typeof e.metadata?.topic=="string"?e.metadata.topic:void 0,n=K(e);return t.filter(s=>{if(s.id===e.id||s.projectId!==e.projectId||s.type!==e.type||s.metadata?.supersededBy)return!1;let i=typeof s.metadata?.topic=="string"?s.metadata.topic:void 0;if(r&&i&&r===i)return!0;let a=K(s);for(let c of n)if(a.has(c))return!0;return we(e.content,s.content)>=.6})}};var xe={critical:100,high:75,normal:50,temporary:20},Pe={activity:14,summary:30,todo:90,decision:365,rule:730,code:180};function V(o,e,t=new Date){let r;if(e==="temporary"&&(r=30),o==="activity"&&(r=Math.min(r??14,14)),o==="summary"&&(r=Math.min(r??30,30)),!!r)return new Date(t.getTime()+r*864e5).toISOString()}function T(o,e=Date.now()){return o.expiresAt?new Date(o.expiresAt).getTime()<=e:!1}function be(o){return!!o.metadata?.supersededBy}function I(o,e=Date.now()){return!T(o,e)&&!be(o)}function E(o,e=Date.now()){if(!I(o,e))return 0;let t=xe[o.importance];if(o.importance==="critical")return t;let r=Pe[o.type]??180,s=Math.max(0,e-new Date(o.updatedAt).getTime())/864e5,i=Math.pow(.5,s/r);return t*i}var x=class{memories=new Map;sanitizer=new v;conflicts=new w;remember(e){let t=new Date().toISOString(),r=this.sanitizer.sanitize(e.content.trim()).text,n=e.importance??_(e.type,r),s={id:ke(),projectId:e.projectId,type:e.type,content:r,importance:n,importanceScore:C(n),tags:e.tags??[],source:e.source??"agent",createdAt:t,updatedAt:t,expiresAt:e.expiresAt??V(e.type,n,new Date(t)),metadata:this.sanitizer.sanitizeValue(e.metadata)},i=this.conflicts.findSuperseded(s,this.list(e.projectId));if(i.length){s.metadata={...s.metadata??{},supersedes:i.map(a=>a.id)};for(let a of i)a.updatedAt=t,a.metadata={...a.metadata??{},supersededBy:s.id,supersededAt:t},this.memories.set(a.id,a)}return this.memories.set(s.id,s),s}importRecords(e){let t=0;for(let r of e)r?.id&&(this.memories.set(r.id,r),t++);return t}exportProject(e){return this.listAll(e)}get(e){return this.memories.get(e)}delete(e){return this.memories.delete(e)}listAll(e){return[...this.memories.values()].filter(t=>t.projectId===e).sort((t,r)=>r.createdAt.localeCompare(t.createdAt))}list(e){return this.listAll(e).filter(t=>I(t))}recent(e,t=10){return this.list(e).slice(0,t)}byType(e,t){return this.list(e).filter(r=>r.type===t)}search(e){let t=e.query.trim().toLowerCase(),r=e.limit??10,n=t.split(/\s+/).filter(Boolean);return this.list(e.projectId).filter(s=>{if(e.types&&!e.types.includes(s.type))return!1;let i=E(s);return!(e.minImportanceScore&&i<e.minImportanceScore||e.tags?.length&&!e.tags.some(c=>s.tags.includes(c)))}).map(s=>{let i=s.content.toLowerCase(),a=n.filter(m=>i.includes(m)).length,c=n.length===0?0:a/n.length,d=E(s)/500;return{memory:s,score:c+d,source:"memory"}}).filter(s=>t.length===0||s.score>0).sort((s,i)=>i.score-s.score).slice(0,r)}pruneExpired(e,t=Date.now()){let r=0;for(let[n,s]of this.memories)s.projectId===e&&T(s,t)&&(this.memories.delete(n),r++);return r}pruneSuperseded(e,t=30,r=Date.now()){let n=0,s=t*864e5;for(let[i,a]of this.memories){if(a.projectId!==e)continue;let c=a.metadata?.supersededAt;if(typeof c!="string")continue;r-new Date(c).getTime()>=s&&(this.memories.delete(i),n++)}return n}clearProject(e){let t=0;for(let[r,n]of this.memories)n.projectId===e&&(this.memories.delete(r),t++);return t}};import{homedir as Ve}from"node:os";import{join as Je}from"node:path";import{DeleteObjectCommand as Re,GetObjectCommand as Ae,HeadObjectCommand as Me,ListObjectsV2Command as Te,PutObjectCommand as Ie,S3Client as Ee}from"@aws-sdk/client-s3";import{getSignedUrl as Oe}from"@aws-sdk/s3-request-presigner";var P=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ee({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ie({Bucket:this.bucket,Key:e,Body:n,ContentType:r}))}async get(e){let t=await Oe(this.client,new Ae({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Me({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Re({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let n=await this.client.send(new Te({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=n.IsTruncated?n.NextContinuationToken:void 0}while(r);return t}};import{access as J,mkdir as Ce,readFile as _e,readdir as Ne,rm as $e,stat as B,writeFile as ze}from"node:fs/promises";import{dirname as Le,join as De,relative as F,resolve as Ke}from"node:path";var l=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Ke(this.root,t)}async put(e,t){let r=this.path(e);await Ce(Le(r),{recursive:!0}),await ze(r,t)}async get(e){try{return await _e(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await J(this.path(e)),!0}catch{return!1}}async delete(e){await $e(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await J(t)}catch{return r}let n=async i=>{let a=await Ne(i,{withFileTypes:!0});for(let c of a){let d=De(i,c.name);if(c.isDirectory()){await n(d);continue}let m=await B(d);r.push({key:F(this.root,d),size:m.size,updatedAt:m.mtime.toISOString()})}},s=await B(t);return s.isDirectory()?await n(t):r.push({key:F(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),r}};function H(o){let e=o.localRoot??Je(Ve(),".toolnet-memory","storage");if(o.provider==="huggingface"){let t=o.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new P({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):(console.warn("[storage] Hugging Face credentials missing. Using local fallback."),new l(e))}return new l(e)}var b=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"memories","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return[];let r=JSON.parse(t);if(!Array.isArray(r))throw new Error("Invalid memory snapshot");return r}async save(e,t){let r=JSON.stringify(t,null,2);await this.storage.put(this.key(e),r,"application/json")}};function Be(o){return new Promise(e=>setTimeout(e,o))}async function U(o,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,n=e.maxDelayMs??2e3,s;for(let i=1;i<=t;i++)try{return await o()}catch(a){if(s=a,i>=t)break;let c=Math.min(n,r*2**(i-1)),d=Math.floor(Math.random()*Math.max(1,c*.2));await Be(c+d)}throw s}var Fe=new Set(["put","get","getText","delete","list"]);function Y(o,e={}){return new Proxy(o,{get(t,r){let n=Reflect.get(t,r,t);return typeof n!="function"?n:Fe.has(r)?(...s)=>U(()=>Promise.resolve(n.apply(t,s)),e):n.bind(t)}})}function G(o){let e=o.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function W(o){let e=o.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var k=class{constructor(e,t,r,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=G(n??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,n=await this.provider.getText(e);if(n){let i;try{i=JSON.parse(n)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=W(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{closeSync as He,mkdirSync as Ue,openSync as Ye,readFileSync as Ge,unlinkSync as Z,writeFileSync as We}from"node:fs";import{tmpdir as Ze}from"node:os";import{join as q}from"node:path";function qe(o){try{return process.kill(o,0),!0}catch(e){return e?.code==="EPERM"}}var R=class{acquired=!1;path;exitHandler=()=>{this.releaseSync()};constructor(e){let t=q(Ze(),"toolnet-memory-locks");Ue(t,{recursive:!0});let r=e.replace(/[^A-Za-z0-9_.-]/g,"_");this.path=q(t,`${r}.lock`)}async acquire(){if(!this.acquired){for(let e=0;e<2;e++)try{let t=Ye(this.path,"wx");We(t,JSON.stringify({pid:process.pid,createdAt:new Date().toISOString()})),He(t),this.acquired=!0,process.once("exit",this.exitHandler);return}catch(t){if(t?.code!=="EEXIST")throw t;let r=!0;try{let n=JSON.parse(Ge(this.path,"utf8"));Number.isInteger(n.pid)&&qe(n.pid)&&(r=!1)}catch{r=!0}if(!r)throw new Error("ToolNet Memory is already running for this project");try{Z(this.path)}catch{}}throw new Error("Unable to acquire ToolNet Memory project lock")}}async release(){this.releaseSync()}releaseSync(){if(this.acquired){try{Z(this.path)}catch{}this.acquired=!1,process.off("exit",this.exitHandler)}}};function Qe(o){let e=o.metadata?.learningFingerprint;return typeof e=="string"?e:void 0}async function Xe(o,e){let t=`projects/${o.id}/memory/learned/`,r=await e.list(t),n=[];for(let s of r.filter(i=>i.key.includes("/batches/")&&i.key.endsWith(".json")).sort((i,a)=>i.key.localeCompare(a.key))){let i=await e.getText(s.key);if(i)try{let a=JSON.parse(i);if(a.version!==1||!Array.isArray(a.candidates))continue;n.push(a)}catch{}}return n}async function Q(o,e){let t=await Xe(o,e),r=new b(e),n=await r.load(o.id),s=new x;s.importRecords(n);let i=new Set(n.map(m=>Qe(m)).filter(m=>!!m)),a=0,c=0,d=0;for(let m of t)for(let p of m.candidates){if(a+=1,i.has(p.fingerprint)){d+=1;continue}s.remember({projectId:o.id,type:p.type,content:p.content,importance:p.importance,tags:p.tags,source:"session-memory-learner",metadata:{learningFingerprint:p.fingerprint,learningKind:p.kind,confidence:p.confidence,provenance:p.provenance,sourceCreatedAt:p.createdAt,sessionKey:p.sessionKey,agent:p.agent,nativeSessionId:p.nativeSessionId}}),i.add(p.fingerprint),c+=1}return c>0&&await r.save(o.id,s.exportProject(o.id)),{batches:t.length,candidates:a,added:c,duplicates:d,memories:s.exportProject(o.id).length}}function et(o,e){let t=o.indexOf(e);return t>=0?o[t+1]:void 0}async function tt(){let[o="help",...e]=process.argv.slice(2);if(o!=="reconcile"){console.log(`Session Memory Learner
1
+ import{existsSync as te,readFileSync as re}from"node:fs";import{homedir as oe}from"node:os";import{join as ne}from"node:path";function se(o){let e=o.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function ie(){let o=process.env.TOOLNET_GLOBAL_ENV??ne(oe(),".config","toolnet-memory",".env");if(!te(o))return;let e=re(o,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let n=r.indexOf("=");if(n<=0)continue;let s=r.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=se(r.slice(n+1)))}}ie();function u(o,e){return o===void 0?e:["1","true","yes","on"].includes(o.toLowerCase())}function g(o,e){if(!o)return e;let t=Number(o);return Number.isFinite(t)?t:e}function _(){return{memory:{autoCapture:u(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:u(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:u(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:u(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:g(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:g(process.env.MEMORY_RERANK_TOP,10),finalContext:g(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:g(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:u(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:g(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}var ae={critical:100,high:75,normal:50,temporary:20};function N(o){return ae[o]}function K(o,e){let t=e.toLowerCase();return t.includes("kh\xF4ng \u0111\u01B0\u1EE3c")||t.includes("b\u1EAFt bu\u1ED9c")||t.includes("must not")||t.includes("critical")?"critical":o==="rule"||o==="decision"?"high":o==="todo"?"normal":o==="activity"||o==="summary"?"temporary":"normal"}import{createHash as ce}from"node:crypto";import{existsSync as I,mkdirSync as pe,readFileSync as de,renameSync as me,writeFileSync as ue}from"node:fs";import{basename as ge,dirname as h,join as v,parse as z,resolve as l}from"node:path";var L=".toolnet",le="project.json";function fe(o){return ce("sha256").update(o).digest("hex").slice(0,16)}function M(o){return v(o,L,le)}function ye(o){return I(M(o))}function he(o,e){let t=l(o),r=z(t).root;for(;;){if(ye(t))return t;if(t===r||e&&t===l(e))break;let n=h(t);if(n===t)break;t=n}return null}function Se(o){let e=l(o),t=z(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(s=>I(v(e,s))))return e;if(e===t)break;let n=h(e);if(n===e)break;e=n}return l(o)}function ve(o){let e;try{e=JSON.parse(de(o,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${o}: ${n instanceof Error?n.message:String(n)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${o}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${o}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${o}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:h(h(o)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function $(o,e){let t=v(o,L);pe(t,{recursive:!0});let r=M(o),n=`${r}.tmp-${process.pid}`;ue(n,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),me(n,r)}function D(o,e){return{id:o.id,name:o.name,remote:o.remote,rootPath:e,createdAt:o.createdAt,updatedAt:o.updatedAt,graphVersion:o.graphVersion,memoryVersion:o.memoryVersion,metadata:o.metadata}}var S=class{detect(e=process.cwd()){let t=l(e),r=Se(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>I(v(r,m))),i=he(t,s?r:void 0);if(i){let m=M(i),p=ve(m);return p.rootPath!==i&&(p.rootPath=i,p.updatedAt=new Date().toISOString(),$(i,p)),D(p,i)}let a=new Date().toISOString(),c=ge(r),d={version:1,id:fe(r),name:c,remote:c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return $(r,d),D(d,r)}};import{randomUUID as Re}from"node:crypto";var je=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_\-]{20,}\b/g},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._\-]{16,}\b/gi},{type:"jwt",regex:/\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b/g},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g},{type:"password_assignment",regex:/\b(password|passwd|pwd)\s*[:=]\s*["']?[^"'\s]{6,}["']?/gi},{type:"secret_assignment",regex:/\b(secret|api[_-]?key|access[_-]?token|refresh[_-]?token)\s*[:=]\s*["']?[^"'\s]{8,}["']?/gi},{type:"cookie",regex:/\b(cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi}],j=class{scan(e){let t=[];for(let r of je){let n=new RegExp(r.regex.source,r.regex.flags);for(let s of e.matchAll(n))t.push({type:r.type,value:s[0]})}return t}hasSecrets(e){return this.scan(e).length>0}};var w=class{scanner=new j;sanitize(e){let t=e,r=this.scanner.scan(e),n=new Set;for(let s of r)n.add(s.type),t=t.split(s.value).join(`[REDACTED:${s.type}]`);return{text:t,redacted:r.length,secretTypes:[...n]}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(t=>this.sanitizeValue(t));if(e&&typeof e=="object"){let t={};for(let[r,n]of Object.entries(e)){let s=r.toLowerCase();s.includes("password")||s.includes("secret")||s.includes("token")||s.includes("cookie")||s.includes("authorization")?t[r]="[REDACTED]":t[r]=this.sanitizeValue(n)}return t}return e}};var we=new Set(["decision","rule","todo","summary","activity","user","error","file","write","command","learned","session","architecture","fix","context","opencode","agy","codex"]),xe=new Set(["d\xF9ng","s\u1EED","d\u1EE5ng","use","using","the","a","an","cho","v\xE0","l\xE0","c\u1EE7a","to","for","with","quy\u1EBFt","\u0111\u1ECBnh"]);function B(o){return new Set(o.toLowerCase().normalize("NFKC").replace(/[^\p{L}\p{N}_-]+/gu," ").split(/\s+/).filter(e=>e.length>2&&!xe.has(e)))}function be(o,e){let t=B(o),r=B(e);if(t.size===0||r.size===0)return 0;let n=0;for(let i of t)r.has(i)&&n++;let s=new Set([...t,...r]).size;return n/s}function V(o){return new Set(o.tags.filter(e=>!we.has(e.toLowerCase())))}var x=class{findSuperseded(e,t){if(e.type!=="rule"&&e.type!=="decision")return[];let r=typeof e.metadata?.topic=="string"?e.metadata.topic:void 0,n=V(e);return t.filter(s=>{if(s.id===e.id||s.projectId!==e.projectId||s.type!==e.type||s.metadata?.supersededBy)return!1;let i=typeof s.metadata?.topic=="string"?s.metadata.topic:void 0;if(r&&i&&r===i)return!0;let a=V(s);for(let c of n)if(a.has(c))return!0;return be(e.content,s.content)>=.6})}};var ke={critical:100,high:75,normal:50,temporary:20},Pe={activity:14,summary:30,todo:90,decision:365,rule:730,code:180};function J(o,e,t=new Date){let r;if(e==="temporary"&&(r=30),o==="activity"&&(r=Math.min(r??14,14)),o==="summary"&&(r=Math.min(r??30,30)),!!r)return new Date(t.getTime()+r*864e5).toISOString()}function E(o,e=Date.now()){return o.expiresAt?new Date(o.expiresAt).getTime()<=e:!1}function Ae(o){return!!o.metadata?.supersededBy}function C(o,e=Date.now()){return!E(o,e)&&!Ae(o)}function T(o,e=Date.now()){if(!C(o,e))return 0;let t=ke[o.importance];if(o.importance==="critical")return t;let r=Pe[o.type]??180,s=Math.max(0,e-new Date(o.updatedAt).getTime())/864e5,i=Math.pow(.5,s/r);return t*i}var b=class{memories=new Map;sanitizer=new w;conflicts=new x;remember(e){let t=new Date().toISOString(),r=this.sanitizer.sanitize(e.content.trim()).text,n=e.importance??K(e.type,r),s={id:Re(),projectId:e.projectId,type:e.type,content:r,importance:n,importanceScore:N(n),tags:e.tags??[],source:e.source??"agent",createdAt:t,updatedAt:t,expiresAt:e.expiresAt??J(e.type,n,new Date(t)),metadata:this.sanitizer.sanitizeValue(e.metadata)},i=this.conflicts.findSuperseded(s,this.list(e.projectId));if(i.length){s.metadata={...s.metadata??{},supersedes:i.map(a=>a.id)};for(let a of i)a.updatedAt=t,a.metadata={...a.metadata??{},supersededBy:s.id,supersededAt:t},this.memories.set(a.id,a)}return this.memories.set(s.id,s),s}importRecords(e){let t=0;for(let r of e)r?.id&&(this.memories.set(r.id,r),t++);return t}exportProject(e){return this.listAll(e)}get(e){return this.memories.get(e)}delete(e){return this.memories.delete(e)}listAll(e){return[...this.memories.values()].filter(t=>t.projectId===e).sort((t,r)=>r.createdAt.localeCompare(t.createdAt))}list(e){return this.listAll(e).filter(t=>C(t))}recent(e,t=10){return this.list(e).slice(0,t)}byType(e,t){return this.list(e).filter(r=>r.type===t)}search(e){let t=e.query.trim().toLowerCase(),r=e.limit??10,n=t.split(/\s+/).filter(Boolean);return this.list(e.projectId).filter(s=>{if(e.types&&!e.types.includes(s.type))return!1;let i=T(s);return!(e.minImportanceScore&&i<e.minImportanceScore||e.tags?.length&&!e.tags.some(c=>s.tags.includes(c)))}).map(s=>{let i=s.content.toLowerCase(),a=n.filter(m=>i.includes(m)).length,c=n.length===0?0:a/n.length,d=T(s)/500;return{memory:s,score:c+d,source:"memory"}}).filter(s=>t.length===0||s.score>0).sort((s,i)=>i.score-s.score).slice(0,r)}pruneExpired(e,t=Date.now()){let r=0;for(let[n,s]of this.memories)s.projectId===e&&E(s,t)&&(this.memories.delete(n),r++);return r}pruneSuperseded(e,t=30,r=Date.now()){let n=0,s=t*864e5;for(let[i,a]of this.memories){if(a.projectId!==e)continue;let c=a.metadata?.supersededAt;if(typeof c!="string")continue;r-new Date(c).getTime()>=s&&(this.memories.delete(i),n++)}return n}clearProject(e){let t=0;for(let[r,n]of this.memories)n.projectId===e&&(this.memories.delete(r),t++);return t}};import{homedir as Ze}from"node:os";import{join as qe}from"node:path";import{DeleteObjectCommand as Ie,GetObjectCommand as Me,HeadObjectCommand as Ee,ListObjectsV2Command as Ce,PutObjectCommand as Te,S3Client as Oe}from"@aws-sdk/client-s3";import{getSignedUrl as _e}from"@aws-sdk/s3-request-presigner";var k=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Oe({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Te({Bucket:this.bucket,Key:e,Body:n,ContentType:r}))}async get(e){let t=await _e(this.client,new Me({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ee({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ie({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let n=await this.client.send(new Ce({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=n.IsTruncated?n.NextContinuationToken:void 0}while(r);return t}};import{access as F,mkdir as Ne,readFile as Ke,readdir as $e,rm as De,stat as U,writeFile as ze}from"node:fs/promises";import{dirname as Le,join as Be,relative as H,resolve as Ve}from"node:path";var f=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Ve(this.root,t)}async put(e,t){let r=this.path(e);await Ne(Le(r),{recursive:!0}),await ze(r,t)}async get(e){try{return await Ke(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await F(this.path(e)),!0}catch{return!1}}async delete(e){await De(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await F(t)}catch{return r}let n=async i=>{let a=await $e(i,{withFileTypes:!0});for(let c of a){let d=Be(i,c.name);if(c.isDirectory()){await n(d);continue}let m=await U(d);r.push({key:H(this.root,d),size:m.size,updatedAt:m.mtime.toISOString()})}},s=await U(t);return s.isDirectory()?await n(t):r.push({key:H(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),r}};import{DeleteObjectCommand as Je,GetObjectCommand as Fe,HeadObjectCommand as Ue,ListObjectsV2Command as He,PutObjectCommand as Ye,S3Client as Ge}from"@aws-sdk/client-s3";import{getSignedUrl as We}from"@aws-sdk/s3-request-presigner";var y=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Ge({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ye({Bucket:this.bucket,Key:e,Body:n,ContentType:r}))}async get(e){let t=await We(this.client,new Fe({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ue({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Je({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let n=await this.client.send(new He({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=n.IsTruncated?n.NextContinuationToken:void 0}while(r);return t}};function O(o,e){return console.warn(e),new f(o)}function Y(o){let e=o.localRoot??qe(Ze(),".toolnet-memory","storage");if(o.provider==="r2"){let t=o.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new y({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(o.provider==="s3"){let t=o.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new y({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] S3 credentials missing. Using local fallback.")}if(o.provider==="huggingface"){let t=o.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new k({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new f(e)}var P=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"memories","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return[];let r=JSON.parse(t);if(!Array.isArray(r))throw new Error("Invalid memory snapshot");return r}async save(e,t){let r=JSON.stringify(t,null,2);await this.storage.put(this.key(e),r,"application/json")}};function Qe(o){return new Promise(e=>setTimeout(e,o))}async function G(o,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,n=e.maxDelayMs??2e3,s;for(let i=1;i<=t;i++)try{return await o()}catch(a){if(s=a,i>=t)break;let c=Math.min(n,r*2**(i-1)),d=Math.floor(Math.random()*Math.max(1,c*.2));await Qe(c+d)}throw s}var Xe=new Set(["put","get","getText","delete","list"]);function W(o,e={}){return new Proxy(o,{get(t,r){let n=Reflect.get(t,r,t);return typeof n!="function"?n:Xe.has(r)?(...s)=>G(()=>Promise.resolve(n.apply(t,s)),e):n.bind(t)}})}function Z(o){let e=o.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function q(o){let e=o.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var A=class{constructor(e,t,r,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=Z(n??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,n=await this.provider.getText(e);if(n){let i;try{i=JSON.parse(n)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=q(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{closeSync as et,mkdirSync as tt,openSync as rt,readFileSync as ot,unlinkSync as Q,writeFileSync as nt}from"node:fs";import{tmpdir as st}from"node:os";import{join as X}from"node:path";function it(o){try{return process.kill(o,0),!0}catch(e){return e?.code==="EPERM"}}var R=class{acquired=!1;path;exitHandler=()=>{this.releaseSync()};constructor(e){let t=X(st(),"toolnet-memory-locks");tt(t,{recursive:!0});let r=e.replace(/[^A-Za-z0-9_.-]/g,"_");this.path=X(t,`${r}.lock`)}async acquire(){if(!this.acquired){for(let e=0;e<2;e++)try{let t=rt(this.path,"wx");nt(t,JSON.stringify({pid:process.pid,createdAt:new Date().toISOString()})),et(t),this.acquired=!0,process.once("exit",this.exitHandler);return}catch(t){if(t?.code!=="EEXIST")throw t;let r=!0;try{let n=JSON.parse(ot(this.path,"utf8"));Number.isInteger(n.pid)&&it(n.pid)&&(r=!1)}catch{r=!0}if(!r)throw new Error("ToolNet Memory is already running for this project");try{Q(this.path)}catch{}}throw new Error("Unable to acquire ToolNet Memory project lock")}}async release(){this.releaseSync()}releaseSync(){if(this.acquired){try{Q(this.path)}catch{}this.acquired=!1,process.off("exit",this.exitHandler)}}};function at(o){let e=o.metadata?.learningFingerprint;return typeof e=="string"?e:void 0}async function ct(o,e){let t=`projects/${o.id}/memory/learned/`,r=await e.list(t),n=[];for(let s of r.filter(i=>i.key.includes("/batches/")&&i.key.endsWith(".json")).sort((i,a)=>i.key.localeCompare(a.key))){let i=await e.getText(s.key);if(i)try{let a=JSON.parse(i);if(a.version!==1||!Array.isArray(a.candidates))continue;n.push(a)}catch{}}return n}async function ee(o,e){let t=await ct(o,e),r=new P(e),n=await r.load(o.id),s=new b;s.importRecords(n);let i=new Set(n.map(m=>at(m)).filter(m=>!!m)),a=0,c=0,d=0;for(let m of t)for(let p of m.candidates){if(a+=1,i.has(p.fingerprint)){d+=1;continue}s.remember({projectId:o.id,type:p.type,content:p.content,importance:p.importance,tags:p.tags,source:"session-memory-learner",metadata:{learningFingerprint:p.fingerprint,learningKind:p.kind,confidence:p.confidence,provenance:p.provenance,sourceCreatedAt:p.createdAt,sessionKey:p.sessionKey,agent:p.agent,nativeSessionId:p.nativeSessionId}}),i.add(p.fingerprint),c+=1}return c>0&&await r.save(o.id,s.exportProject(o.id)),{batches:t.length,candidates:a,added:c,duplicates:d,memories:s.exportProject(o.id).length}}function pt(o,e){let t=o.indexOf(e);return t>=0?o[t+1]:void 0}async function dt(){let[o="help",...e]=process.argv.slice(2);if(o!=="reconcile"){console.log(`Session Memory Learner
5
5
 
6
6
  Commands:
7
7
  reconcile [--project PATH]
8
- `);return}let t=et(e,"--project")??process.cwd(),r=new h().detect(t),n=O(),s=Y(H({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:3}),i=new k(s,r.id,r.name,r.remote??r.name),a=new R(r.id);await a.acquire();try{let c=await Q(r,i);console.log(JSON.stringify({project:r.name,...c},null,2))}finally{await a.release()}}tt().catch(o=>{console.error(o instanceof Error?o.message:o),process.exit(1)});
8
+ `);return}let t=pt(e,"--project")??process.cwd(),r=new S().detect(t),n=_(),s=W(Y({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:3}),i=new A(s,r.id,r.name,r.remote??r.name),a=new R(r.id);await a.acquire();try{let c=await ee(r,i);console.log(JSON.stringify({project:r.name,...c},null,2))}finally{await a.release()}}dt().catch(o=>{console.error(o instanceof Error?o.message:o),process.exit(1)});