toolnet-memory 0.3.3 → 0.3.4

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 B,readFileSync as V}from"node:fs";import{homedir as U}from"node:os";import{join as J}from"node:path";function F(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 Y(){let s=process.env.TOOLNET_GLOBAL_ENV??J(U(),".config","toolnet-memory",".env");if(!B(s))return;let e=V(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 n=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=F(r.slice(o+1)))}}Y();function m(s,e){return s===void 0?e:["1","true","yes","on"].includes(s.toLowerCase())}function g(s,e){if(!s)return e;let t=Number(s);return Number.isFinite(t)?t:e}function A(){return{memory:{autoCapture:m(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:m(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:m(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:m(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:m(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)}}}import{createHash as H}from"node:crypto";import{existsSync as b,mkdirSync as G,readFileSync as W,renameSync as Q,writeFileSync as Z}from"node:fs";import{basename as q,dirname as S,join as w,parse as T,resolve as f}from"node:path";var E=".toolnet",X="project.json";function ee(s){return H("sha256").update(s).digest("hex").slice(0,16)}function k(s){return w(s,E,X)}function te(s){return b(k(s))}function re(s,e){let t=f(s),r=T(t).root;for(;;){if(te(t))return t;if(t===r||e&&t===f(e))break;let o=S(t);if(o===t)break;t=o}return null}function se(s){let e=f(s),t=T(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(n=>b(w(e,n))))return e;if(e===t)break;let o=S(e);if(o===e)break;e=o}return f(s)}function oe(s){let e;try{e=JSON.parse(W(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:S(S(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 M(s,e){let t=w(s,E);G(t,{recursive:!0});let r=k(s),o=`${r}.tmp-${process.pid}`;Z(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),Q(o,r)}function C(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 v=class{detect(e=process.cwd()){let t=f(e),r=se(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>b(w(r,u))),i=re(t,n?r:void 0);if(i){let u=k(i),d=oe(u);return d.rootPath!==i&&(d.rootPath=i,d.updatedAt=new Date().toISOString(),M(i,d)),C(d,i)}let a=new Date().toISOString(),c=q(r),p={version:1,id:ee(r),name:c,remote:c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return M(r,p),C(p,r)}};import{homedir as Ae}from"node:os";import{join as Me}from"node:path";import{DeleteObjectCommand as ne,GetObjectCommand as ie,HeadObjectCommand as ae,ListObjectsV2Command as ce,PutObjectCommand as pe,S3Client as ue}from"@aws-sdk/client-s3";import{getSignedUrl as de}from"@aws-sdk/s3-request-presigner";var j=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new ue({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 pe({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await de(this.client,new ie({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 ae({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 ne({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 n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as I,mkdir as le,readFile as me,readdir as ge,rm as fe,stat as O,writeFile as ye}from"node:fs/promises";import{dirname as he,join as Se,relative as _,resolve as ve}from"node:path";var y=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 le(he(r),{recursive:!0}),await ye(r,t)}async get(e){try{return await me(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 I(this.path(e)),!0}catch{return!1}}async delete(e){await fe(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await I(t)}catch{return r}let o=async i=>{let a=await ge(i,{withFileTypes:!0});for(let c of a){let p=Se(i,c.name);if(c.isDirectory()){await o(p);continue}let u=await O(p);r.push({key:_(this.root,p),size:u.size,updatedAt:u.mtime.toISOString()})}},n=await O(t);return n.isDirectory()?await o(t):r.push({key:_(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};import{DeleteObjectCommand as we,GetObjectCommand as je,HeadObjectCommand as xe,ListObjectsV2Command as be,PutObjectCommand as ke,S3Client as Pe}from"@aws-sdk/client-s3";import{getSignedUrl as Re}from"@aws-sdk/s3-request-presigner";var h=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Pe({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 ke({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Re(this.client,new je({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 xe({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 we({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new be({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};function P(s,e){return console.warn(e),new y(s)}function $(s){let e=s.localRoot??Me(Ae(),".toolnet-memory","storage");if(s.provider==="r2"){let t=s.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new h({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):P(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 h({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):P(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 j({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):P(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new y(e)}function Ce(s){return new Promise(e=>setTimeout(e,s))}async function K(s,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,o=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await s()}catch(a){if(n=a,i>=t)break;let c=Math.min(o,r*2**(i-1)),p=Math.floor(Math.random()*Math.max(1,c*.2));await Ce(c+p)}throw n}var Te=new Set(["put","get","getText","delete","list"]);function N(s,e={}){return new Proxy(s,{get(t,r){let o=Reflect.get(t,r,t);return typeof o!="function"?o:Te.has(r)?(...n)=>K(()=>Promise.resolve(o.apply(t,n)),e):o.bind(t)}})}function D(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 L(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 x=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=D(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 i;try{i=JSON.parse(o)}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 n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=L(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))}};var Ee=["memories/current.json","vectors/current.json","graph/current.json","graph/manifest.json","graph/resolution/current.json","code/chunks/current.json","code/vectors/current.json","code/architecture/current.json","code/architecture/entry-points.json","code/architecture/hotspots.json","code/architecture/layers.json","code/architecture/clusters.json","code/analysis/current.json","code/analysis/dead-code.json","code/analysis/dependencies.json","code/visualization/graph.json"],l=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",r){let o=new Date().toISOString(),n=o.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,n),c=[];for(let u of Ee){let d=`${i}/${u}`,R=await this.storage.get(d);R&&(await this.storage.put(`${a}/${u}`,R,"application/json"),c.push(u))}if(c.length===0)return null;let p={version:1,id:n,projectId:e,createdAt:o,reason:t,objects:c,metadata:r};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(p,null,2),"application/json"),p}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),o=(await this.storage.list(t)).filter(i=>i.key.endsWith("/snapshot.json")),n=[];for(let i of o){let a=await this.storage.getText(i.key);if(a)try{n.push(JSON.parse(a))}catch{}}return n.sort((i,a)=>a.createdAt.localeCompare(i.createdAt))}async restore(e,t){let r=this.snapshotPrefix(e,t),o=await this.storage.getText(`${r}/snapshot.json`);if(!o)throw new Error(`Snapshot not found: ${t}`);let n=JSON.parse(o);if(n.projectId!==e)throw new Error("Snapshot project mismatch");let i=this.projectPrefix(e),a=[];for(let c of n.objects){let p=await this.storage.get(`${r}/${c}`);p&&(await this.storage.put(`${i}/${c}`,p,"application/json"),a.push(c))}return{restored:a}}async remove(e,t){let r=this.snapshotPrefix(e,t),o=await this.storage.list(r);for(let n of o)await this.storage.delete(n.key);return o.length}async prune(e,t=10){let o=(await this.list(e)).slice(t),n=0;for(let i of o)await this.remove(e,i.id),n++;return n}};async function z(s,e){let t=new l(s),o=(await t.list(e))[0];if(!o)throw new Error("No snapshot available");let n=await t.restore(e,o.id);return{snapshot:o.id,createdAt:o.createdAt,...n}}async function Ie(){let s=process.argv[2],e=A(),t=new v().detect(),r=N($({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),o=new x(r,t.id,t.name,t.remote??t.name),n=new l(o);if(s==="list"){let i=await n.list(t.id);console.log(i.map(a=>({id:a.id,createdAt:a.createdAt,reason:a.reason})));return}if(s==="create"){let i=process.argv.slice(3).join(" ")||"manual-cli";console.log(await n.create(t.id,i));return}if(s==="restore"){let i=process.argv[3];if(!i)throw new Error("snapshot id required");await n.create(t.id,"before-cli-restore"),console.log(await n.restore(t.id,i));return}if(s==="recover-latest"){console.log(await z(o,t.id));return}throw new Error("Usage: snapshot-cli list|create|restore|recover-latest")}Ie().catch(s=>{console.error(s instanceof Error?s.message:s),process.exit(1)});
1
+ import"dotenv/config";import{existsSync as B,readFileSync as V}from"node:fs";import{homedir as U}from"node:os";import{join as J}from"node:path";function F(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 Y(){let o=process.env.TOOLNET_GLOBAL_ENV??J(U(),".config","toolnet-memory",".env");if(!B(o))return;let e=V(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 s=r.indexOf("=");if(s<=0)continue;let n=r.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=F(r.slice(s+1)))}}Y();function g(o,e){return o===void 0?e:["1","true","yes","on"].includes(o.toLowerCase())}function l(o,e){if(!o)return e;let t=Number(o);return Number.isFinite(t)?t:e}function R(){return{memory:{autoCapture:g(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:g(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:g(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:g(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:l(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:l(process.env.MEMORY_RERANK_TOP,10),finalContext:l(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:l(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:g(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:l(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as H}from"node:crypto";import{existsSync as P,mkdirSync as G,readFileSync as W,renameSync as Z,writeFileSync as Q}from"node:fs";import{basename as X,dirname as S,join as v,parse as T,resolve as f}from"node:path";var I=".toolnet",q="project.json";function ee(o){return H("sha256").update(o).digest("hex").slice(0,16)}function b(o){return v(o,I,q)}function te(o){return P(b(o))}function re(o,e){let t=f(o),r=T(t).root;for(;;){if(te(t))return t;if(t===r||e&&t===f(e))break;let s=S(t);if(s===t)break;t=s}return null}function oe(o){let e=f(o),t=T(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(n=>P(v(e,n))))return e;if(e===t)break;let s=S(e);if(s===e)break;e=s}return f(o)}function se(o){let e;try{e=JSON.parse(W(o,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${o}: ${s instanceof Error?s.message:String(s)}`)}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:S(S(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 E(o,e){let t=v(o,I);G(t,{recursive:!0});let r=b(o),s=`${r}.tmp-${process.pid}`;Q(s,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Z(s,r)}function C(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 j=class{detect(e=process.cwd()){let t=f(e),r=oe(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>P(v(r,d))),i=re(t,n?r:void 0);if(i){let d=b(i),u=se(d);return u.rootPath!==i&&(u.rootPath=i,u.updatedAt=new Date().toISOString(),E(i,u)),C(u,i)}let a=new Date().toISOString(),c=X(r),p={version:1,id:ee(r),name:c,remote:c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return E(r,p),C(p,r)}};import{homedir as Re}from"node:os";import{join as Ee}from"node:path";import{DeleteObjectCommand as ne,GetObjectCommand as ie,HeadObjectCommand as ae,ListObjectsV2Command as ce,PutObjectCommand as pe,S3Client as de}from"@aws-sdk/client-s3";import{getSignedUrl as ue}from"@aws-sdk/s3-request-presigner";var w=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new de({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 s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new pe({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await ue(this.client,new ie({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 ae({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 ne({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new ce({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};import{access as M,mkdir as me,readFile as ge,readdir as le,rm as fe,stat as O,writeFile as ye}from"node:fs/promises";import{dirname as he,join as Se,relative as _,resolve as je}from"node:path";var y=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return je(this.root,t)}async put(e,t){let r=this.path(e);await me(he(r),{recursive:!0}),await ye(r,t)}async get(e){try{return await ge(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 M(this.path(e)),!0}catch{return!1}}async delete(e){await fe(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),r=[];try{await M(t)}catch{return r}let s=async i=>{let a=await le(i,{withFileTypes:!0});for(let c of a){let p=Se(i,c.name);if(c.isDirectory()){await s(p);continue}let d=await O(p);r.push({key:_(this.root,p),size:d.size,updatedAt:d.mtime.toISOString()})}},n=await O(t);return n.isDirectory()?await s(t):r.push({key:_(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),r}};import{DeleteObjectCommand as ve,GetObjectCommand as we,HeadObjectCommand as xe,ListObjectsV2Command as Pe,PutObjectCommand as be,S3Client as ke}from"@aws-sdk/client-s3";import{getSignedUrl as Ae}from"@aws-sdk/s3-request-presigner";var h=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ke({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 s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new be({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let t=await Ae(this.client,new we({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 xe({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 ve({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let s=await this.client.send(new Pe({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return t}};function k(o,e){return console.warn(e),new y(o)}function $(o){let e=o.localRoot??Ee(Re(),".toolnet-memory","storage");if(o.provider==="r2"){let t=o.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new h({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):k(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 h({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):k(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 w({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):k(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new y(e)}function Ce(o){return new Promise(e=>setTimeout(e,o))}async function K(o,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,s=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await o()}catch(a){if(n=a,i>=t)break;let c=Math.min(s,r*2**(i-1)),p=Math.floor(Math.random()*Math.max(1,c*.2));await Ce(c+p)}throw n}var Te=new Set(["put","get","getText","delete","list"]);function N(o,e={}){return new Proxy(o,{get(t,r){let s=Reflect.get(t,r,t);return typeof s!="function"?s:Te.has(r)?(...n)=>K(()=>Promise.resolve(s.apply(t,n)),e):s.bind(t)}})}function D(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 z(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 s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var x=class{constructor(e,t,r,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=D(s??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,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}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 n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(n,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=z(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))}};var Ie=["memories/current.json","vectors/current.json","graph/current.json","graph/manifest.json","graph/resolution/current.json","code/chunks/current.json","code/vectors/current.json","code/architecture/current.json","code/architecture/entry-points.json","code/architecture/hotspots.json","code/architecture/layers.json","code/architecture/clusters.json","code/analysis/current.json","code/analysis/dead-code.json","code/analysis/dependencies.json","code/visualization/graph.json"],m=class{constructor(e){this.storage=e}storage;projectPrefix(e){return`projects/${e}`}snapshotPrefix(e,t){return[this.projectPrefix(e),"snapshots",t].join("/")}async create(e,t="automatic",r){let s=new Date().toISOString(),n=s.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,n),c=[];for(let d of Ie){let u=`${i}/${d}`,A=await this.storage.get(u);A&&(await this.storage.put(`${a}/${d}`,A,"application/json"),c.push(d))}if(c.length===0)return null;let p={version:1,id:n,projectId:e,createdAt:s,reason:t,objects:c,metadata:r};return await this.storage.put(`${a}/snapshot.json`,JSON.stringify(p,null,2),"application/json"),p}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),s=(await this.storage.list(t)).filter(i=>i.key.endsWith("/snapshot.json")),n=[];for(let i of s){let a=await this.storage.getText(i.key);if(a)try{n.push(JSON.parse(a))}catch{}}return n.sort((i,a)=>a.createdAt.localeCompare(i.createdAt))}async restore(e,t){let r=this.snapshotPrefix(e,t),s=await this.storage.getText(`${r}/snapshot.json`);if(!s)throw new Error(`Snapshot not found: ${t}`);let n=JSON.parse(s);if(n.projectId!==e)throw new Error("Snapshot project mismatch");let i=this.projectPrefix(e),a=[];for(let c of n.objects){let p=await this.storage.get(`${r}/${c}`);p&&(await this.storage.put(`${i}/${c}`,p,"application/json"),a.push(c))}return{restored:a}}async remove(e,t){let r=this.snapshotPrefix(e,t),s=await this.storage.list(r);for(let n of s)await this.storage.delete(n.key);return s.length}async prune(e,t=10){let s=(await this.list(e)).slice(t),n=0;for(let i of s)await this.remove(e,i.id),n++;return n}};async function L(o,e){let t=new m(o),s=(await t.list(e))[0];if(!s)throw new Error("No snapshot available");let n=await t.restore(e,s.id);return{snapshot:s.id,createdAt:s.createdAt,...n}}async function Me(){let o=process.argv[2],e=R(),t=new j().detect(),r=N($({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),s=new x(r,t.id,t.name,t.remote??t.name),n=new m(s);if(o==="list"){let i=await n.list(t.id);console.log(i.map(a=>({id:a.id,createdAt:a.createdAt,reason:a.reason})));return}if(o==="create"){let i=process.argv.slice(3).join(" ")||"manual-cli";console.log(await n.create(t.id,i));return}if(o==="restore"){let i=process.argv[3];if(!i)throw new Error("snapshot id required");await n.create(t.id,"before-cli-restore"),console.log(await n.restore(t.id,i));return}if(o==="recover-latest"){console.log(await L(s,t.id));return}throw new Error("Usage: snapshot-cli list|create|restore|recover-latest")}Me().catch(o=>{console.error(o instanceof Error?o.message:o),process.exit(1)});
package/bundle/update.js CHANGED
@@ -1,4 +1 @@
1
- import p from"node:fs";import c from"node:path";import{fileURLToPath as v}from"node:url";import{spawn as $}from"node:child_process";var m=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],i={clear:"\r\x1B[2K",cyan:"\x1B[36m",green:"\x1B[32m",red:"\x1B[31m",yellow:"\x1B[33m",dim:"\x1B[2m",reset:"\x1B[0m"};function f(r){let e=Date.now()-r;return e<1e3?`${e}ms`:e<1e4?`${(e/1e3).toFixed(1)}s`:`${Math.round(e/1e3)}s`}var d=class{stream;enabled;interactive;color;intervalMs;label;frame=0;startedAt=0;timer;active=!1;constructor(e,s={}){this.label=e,this.stream=s.stream??process.stderr,this.enabled=s.enabled??!0,this.interactive=s.interactive??this.stream.isTTY===!0,this.color=s.color??(this.interactive&&process.env.NO_COLOR===void 0),this.intervalMs=Math.max(40,s.intervalMs??80)}start(){return!this.enabled||this.active?this:(this.active=!0,this.startedAt=Date.now(),this.interactive?(this.render(),this.timer=setInterval(()=>{this.frame=(this.frame+1)%m.length,this.render()},this.intervalMs),this.timer.unref?.(),this):(this.stream.write(`\u2192 ${this.label}
2
- `),this))}update(e){return this.label=e,this.enabled&&this.active&&this.interactive&&this.render(),this}succeed(e){this.finish("\u2713",e??this.label,i.green)}fail(e){this.finish("\u2717",e??this.label,i.red)}warn(e){this.finish("!",e??this.label,i.yellow)}stop(){this.active&&(this.timer&&(clearInterval(this.timer),this.timer=void 0),this.enabled&&this.interactive&&this.stream.write(i.clear),this.active=!1)}render(){if(!this.enabled||!this.active||!this.interactive)return;let e=m[this.frame],s=this.color?`${i.cyan}${e}${i.reset}`:e,o=f(this.startedAt),t=this.color?`${i.dim}${o}${i.reset}`:o;this.stream.write(`${i.clear}${s} ${this.label} ${t}`)}finish(e,s,o){if(!this.enabled){this.active=!1;return}this.startedAt||(this.startedAt=Date.now()),this.timer&&(clearInterval(this.timer),this.timer=void 0);let t=f(this.startedAt),a=this.color?`${o}${e}${i.reset}`:e,l=this.color?`${i.dim}${t}${i.reset}`:t;this.interactive?this.stream.write(`${i.clear}${a} ${s} ${l}
3
- `):this.stream.write(`${a} ${s} (${t})
4
- `),this.active=!1}};var h="toolnet-memory";function g(r,e){return new Promise((s,o)=>{let t=$(r,e,{stdio:["ignore","pipe","pipe"],env:process.env}),a="",l="";t.stdout?.setEncoding("utf8"),t.stderr?.setEncoding("utf8"),t.stdout?.on("data",n=>{a+=n}),t.stderr?.on("data",n=>{l+=n}),t.once("error",o),t.once("close",n=>{s({status:n??1,stdout:a,stderr:l})})})}function b(){let r=c.dirname(v(import.meta.url));return c.resolve(r,"..")}function w(r){try{let e=JSON.parse(p.readFileSync(c.join(r,"package.json"),"utf8"));return String(e.version??"unknown")}catch{return"unknown"}}function y(r){let e=`${c.sep}lib${c.sep}node_modules${c.sep}${h}`;return r.endsWith(e)?r.slice(0,-e.length)||c.parse(r).root:null}async function x(){let r=b(),e=w(r);console.log(""),console.log("ToolNet Memory Update"),console.log("====================="),console.log("");let s=new d("Checking npm registry").start(),o;try{o=await g("npm",["view",`${h}@latest`,"version","--silent"])}catch(u){throw s.fail("Unable to reach npm registry"),u}if(o.status!==0){s.fail("Unable to check latest version"),o.stderr.trim()&&console.error(o.stderr.trim()),process.exitCode=1;return}let t=o.stdout.trim();if(s.succeed(`Latest version: v${t}`),console.log(""),console.log(`Current: v${e}`),console.log(`Latest : v${t}`),console.log(""),e===t){console.log("\u2713 ToolNet Memory is already up to date"),console.log("");return}let a=y(r);if(!a){console.log("This appears to be a development checkout."),console.log(""),console.log("Self-update is disabled for source checkouts."),console.log(""),console.log("For the published CLI, install/update with:"),console.log(" npm install -g toolnet-memory@latest"),console.log(""),process.exitCode=1;return}let l=new d(`Installing ToolNet Memory v${t}`).start(),n;try{n=await g("npm",["install","-g","--prefix",a,`${h}@${t}`,"--no-fund","--no-audit","--loglevel=error"])}catch(u){throw l.fail("ToolNet Memory update failed"),u}if(n.status!==0){l.fail("ToolNet Memory update failed"),n.stderr.trim()&&(console.error(""),console.error(n.stderr.trim())),console.error(""),console.error("Retry with:"),console.error(` npm install -g ${h}@${t}`),console.error(""),process.exitCode=n.status||1;return}l.succeed(`Installed ToolNet Memory v${t}`),console.log(""),console.log(`\u2713 ToolNet Memory updated to v${t}`),console.log("")}x().catch(r=>{console.error(""),console.error(r instanceof Error?r.message:String(r)),console.error(""),process.exitCode=1});
1
+ import a from"node:fs";import n from"node:path";import{fileURLToPath as u}from"node:url";import{spawnSync as p}from"node:child_process";var s="toolnet-memory";function i(o,e,t={}){return p(o,e,{encoding:"utf8",stdio:t.capture?["ignore","pipe","pipe"]:"inherit"})}function g(){let o=n.dirname(u(import.meta.url));return n.resolve(o,"..")}function m(o){try{let e=JSON.parse(a.readFileSync(n.join(o,"package.json"),"utf8"));return String(e.version??"unknown")}catch{return"unknown"}}function d(o){let e=`${n.sep}lib${n.sep}node_modules${n.sep}${s}`;return o.endsWith(e)?o.slice(0,-e.length)||n.parse(o).root:null}function f(){let o=g(),e=m(o),t=i("npm",["view",`${s}@latest`,"version","--silent"],{capture:!0});t.status!==0&&(console.error("Unable to check the latest ToolNet Memory version."),t.stderr&&console.error(t.stderr.trim()),process.exit(1));let r=(t.stdout??"").trim();if(console.log(""),console.log("ToolNet Memory Update"),console.log("====================="),console.log(""),console.log(`Current: v${e}`),console.log(`Latest : v${r}`),console.log(""),e===r){console.log("\u2713 ToolNet Memory is already up to date"),console.log("");return}let l=d(o);l||(console.log("This appears to be a development checkout."),console.log(""),console.log("Self-update is disabled for source checkouts."),console.log(""),console.log("For the published CLI, install/update with:"),console.log(" npx toolnet-memory-install"),console.log(""),process.exit(1)),console.log(`Updating to v${r}...`),console.log("");let c=i("npm",["install","-g","--prefix",l,`${s}@latest`,"--no-fund","--no-audit","--loglevel=error"]);c.status!==0&&(console.error(""),console.error("\u2717 Update failed"),console.error(""),console.error("You can retry using:"),console.error(" npx toolnet-memory-install"),process.exit(c.status??1)),console.log(""),console.log(`\u2713 ToolNet Memory updated to v${r}`),console.log("")}f();
@@ -1,9 +1,9 @@
1
- import{existsSync as ae,readFileSync as ce}from"node:fs";import{homedir as ue}from"node:os";import{join as pe}from"node:path";function de(r){let e=r.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 r=process.env.TOOLNET_GLOBAL_ENV??pe(ue(),".config","toolnet-memory",".env");if(!ae(r))return;let e=ce(r,"utf8");for(let t of e.split(/\r?\n/)){let o=t.trim();if(!o||o.startsWith("#"))continue;o.startsWith("export ")&&(o=o.slice(7));let s=o.indexOf("=");if(s<=0)continue;let i=o.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=de(o.slice(s+1)))}}le();function h(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function S(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function z(){return{memory:{autoCapture:h(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:h(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:h(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:h(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:S(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:S(process.env.MEMORY_RERANK_TOP,10),finalContext:S(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:S(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:h(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:S(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as me}from"node:crypto";import{existsSync as I,mkdirSync as ge,readFileSync as fe,renameSync as ye,writeFileSync as he}from"node:fs";import{basename as Se,dirname as j,join as A,parse as W,resolve as k}from"node:path";var F=".toolnet",ke="project.json";function we(r){return me("sha256").update(r).digest("hex").slice(0,16)}function T(r){return A(r,F,ke)}function ve(r){return I(T(r))}function be(r,e){let t=k(r),o=W(t).root;for(;;){if(ve(t))return t;if(t===o||e&&t===k(e))break;let s=j(t);if(s===t)break;t=s}return null}function je(r){let e=k(r),t=W(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(i=>I(A(e,i))))return e;if(e===t)break;let s=j(e);if(s===e)break;e=s}return k(r)}function xe(r){let e;try{e=JSON.parse(fe(r,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${r}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let o=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:j(j(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:o,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:o,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 B(r,e){let t=A(r,F);ge(t,{recursive:!0});let o=T(r),s=`${o}.tmp-${process.pid}`;he(s,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),ye(s,o)}function V(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var x=class{detect(e=process.cwd()){let t=k(e),o=je(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>I(A(o,d))),a=be(t,i?o:void 0);if(a){let d=T(a),m=xe(d);return m.rootPath!==a&&(m.rootPath=a,m.updatedAt=new Date().toISOString(),B(a,m)),V(m,a)}let c=new Date().toISOString(),u=Se(o),p={version:1,id:we(o),name:u,remote:u,rootPath:o,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return B(o,p),V(p,o)}};import{homedir as He}from"node:os";import{join as Ye}from"node:path";import{DeleteObjectCommand as Ae,GetObjectCommand as Pe,HeadObjectCommand as Re,ListObjectsV2Command as Me,PutObjectCommand as Ce,S3Client as Ie}from"@aws-sdk/client-s3";import{getSignedUrl as Te}from"@aws-sdk/s3-request-presigner";var P=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ie({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,o="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ce({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Te(this.client,new Pe({Bucket:this.bucket,Key:e}),{expiresIn:60}),o=await fetch(t,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`HF download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.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 Ae({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new Me({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let i of s.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};import{access as J,mkdir as Ee,readFile as Oe,readdir as _e,rm as Ne,stat as U,writeFile as $e}from"node:fs/promises";import{dirname as Ke,join as De,relative as H,resolve as Le}from"node:path";var w=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 o=this.path(e);await Ee(Ke(o),{recursive:!0}),await $e(o,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 J(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),o=[];try{await J(t)}catch{return o}let s=async a=>{let c=await _e(a,{withFileTypes:!0});for(let u of c){let p=De(a,u.name);if(u.isDirectory()){await s(p);continue}let d=await U(p);o.push({key:H(this.root,p),size:d.size,updatedAt:d.mtime.toISOString()})}},i=await U(t);return i.isDirectory()?await s(t):o.push({key:H(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),o}};import{DeleteObjectCommand as ze,GetObjectCommand as Be,HeadObjectCommand as Ve,ListObjectsV2Command as We,PutObjectCommand as Fe,S3Client as Je}from"@aws-sdk/client-s3";import{getSignedUrl as Ue}from"@aws-sdk/s3-request-presigner";var v=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,o="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Fe({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ue(this.client,new Be({Bucket:this.bucket,Key:e}),{expiresIn:60}),o=await fetch(t,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`${this.name} download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.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 Ve({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=[],o;do{let s=await this.client.send(new We({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let i of s.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};function E(r,e){return console.warn(e),new w(r)}function Y(r){let e=r.localRoot??Ye(He(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new v({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):E(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new v({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):E(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new P({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):E(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new w(e)}function Ge(r){return new Promise(e=>setTimeout(e,r))}async function G(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,s=e.maxDelayMs??2e3,i;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(i=c,a>=t)break;let u=Math.min(s,o*2**(a-1)),p=Math.floor(Math.random()*Math.max(1,u*.2));await Ge(u+p)}throw i}var Qe=new Set(["put","get","getText","delete","list"]);function Q(r,e={}){return new Proxy(r,{get(t,o){let s=Reflect.get(t,o,t);return typeof s!="function"?s:Qe.has(o)?(...i)=>G(()=>Promise.resolve(s.apply(t,i)),e):s.bind(t)}})}function Z(r){let e=r.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 X(r){let e=r.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 o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(o)return`${o[1]}memory/records/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),o)return`${o[1]}memory/vectors/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),o)return`${o[1]}code/graph/${o[2]}`;let s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var R=class{constructor(e,t,o,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=o,this.folder=Z(s??o),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(),o=t,s=await this.provider.getText(e);if(s){let a;try{a=JSON.parse(s)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(o=a.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,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=X(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,o){return await this.ensureRegistered(),this.provider.put(this.key(e),t,o)}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 te}from"node:path";import{mkdirSync as rt}from"node:fs";import{createHash as Ze}from"node:crypto";import{dirname as Xe}from"node:path";import{mkdirSync as qe,readFileSync as ao,renameSync as et,writeFileSync as tt}from"node:fs";function q(r){return Ze("sha256").update(r).digest("hex")}function ee(r,e){qe(Xe(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;tt(t,JSON.stringify(e,null,2)+`
5
- `,{encoding:"utf8",mode:384}),et(t,r)}function ot(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function y(r,e=20){let t=[],o=new Set;for(let s of r.slice().reverse()){let i=ot(s);if(!(!i||o.has(i))&&(o.add(i),t.push(s),t.length>=e))break}return t.reverse()}function st(r){return r.kind==="phase"?/^Phase\s+\d+$/iu.test(r.text):r.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(r.text):!1}function re(r,e){let t=e.status??r?.status??"pending",o=t;r&&(r.status==="completed"&&t!=="completed"?o="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(o=r.status));let s=r&&st(e)?r.title:e.text;return{id:r?.id??q(e.key).slice(0,24),title:s,status:o,order:e.order??r?.order,confidence:Math.max(e.confidence,r?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function nt(r,e){let t=`projects/${r.id}/work/observations/`,o=await e.list(t),s=[];for(let i of o.filter(a=>a.key.endsWith(".json")).sort((a,c)=>a.key.localeCompare(c.key))){let a=await e.getText(i.key);if(a)try{let c=JSON.parse(a);c.version===1&&Array.isArray(c.observations)&&s.push(c)}catch{}}return s}async function oe(r,e){let o=(await nt(r,e)).flatMap(n=>n.observations).sort((n,f)=>{let D=n.occurredAt.localeCompare(f.occurredAt);if(D!==0)return D;let L=n.sequence-f.sequence;return L!==0?L:n.id.localeCompare(f.id)}),s=new Map,i=new Map,a,c,u,p=[],d=[],m=[],_=[],N=[],$=[];for(let n of o)switch(n.kind){case"goal":a=n.text;break;case"plan":c=n.text;break;case"phase":s.set(n.key,re(s.get(n.key),n));break;case"task":i.set(n.key,re(i.get(n.key),n));break;case"decision":p.push(n.text);break;case"blocker":d.push(n.text);break;case"warning":m.push(n.text);break;case"next_action":_.push(n.text);break;case"file":N.push(n.text);break;case"test":$.push(n.text);break;case"session":u={agent:n.agent,nativeSessionId:n.nativeSessionId,sessionKey:n.sessionKey,updatedAt:n.occurredAt};break}let g=Array.from(s.values()).sort((n,f)=>(n.order??Number.MAX_SAFE_INTEGER)-(f.order??Number.MAX_SAFE_INTEGER)),l=Array.from(i.values()).sort((n,f)=>(n.order??Number.MAX_SAFE_INTEGER)-(f.order??Number.MAX_SAFE_INTEGER)),M=g.find(n=>n.status==="in_progress")??g.find(n=>n.status==="blocked")??g.find(n=>n.status==="pending"),b=l.find(n=>n.status==="in_progress")??l.find(n=>n.status==="blocked")??l.find(n=>n.status==="pending"),ne=y([..._,...b?[b.title]:[],...!b&&M?[M.title]:[],...l.filter(n=>n.status==="pending").slice(0,5).map(n=>n.title)],8),ie=y([...d,...g.filter(n=>n.status==="blocked").map(n=>n.title),...l.filter(n=>n.status==="blocked").map(n=>n.title)],20),C={version:1,projectId:r.id,projectName:r.name,goal:a,plan:c,phases:g,tasks:l,decisions:y(p,20),blockers:ie,warnings:y(m,20),nextActions:ne,filesTouched:y(N,30),tests:y($,20),currentPhase:M,currentTask:b,progress:{phasesTotal:g.length,phasesCompleted:g.filter(n=>n.status==="completed").length,tasksTotal:l.length,tasksCompleted:l.filter(n=>n.status==="completed").length,blocked:g.filter(n=>n.status==="blocked").length+l.filter(n=>n.status==="blocked").length},lastSession:u,updatedAt:o.length?o[o.length-1].occurredAt:new Date().toISOString()},K=te(r.rootPath,".toolnet","work");return rt(K,{recursive:!0}),ee(te(K,"current.json"),C),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(C,null,2)+`
6
- `,"application/json"),C}async function O(r,e){let t=await e.getText(`projects/${r.id}/work/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function it(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function at(r){let e=z(),t=Q(Y({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new R(t,r.id,r.name,r.remote??r.name)}function se(r){console.log(),console.log(`Project: ${r.projectName}`),r.goal&&console.log(`Goal: ${r.goal}`),r.currentPhase&&console.log(`Current phase: ${r.currentPhase.title} [${r.currentPhase.status}]`),r.currentTask&&console.log(`Current task: ${r.currentTask.title} [${r.currentTask.status}]`),console.log(`Phases: ${r.progress.phasesCompleted}/${r.progress.phasesTotal}`),console.log(`Tasks: ${r.progress.tasksCompleted}/${r.progress.tasksTotal}`),r.nextActions.length&&(console.log(),console.log("Next actions:"),r.nextActions.forEach((e,t)=>console.log(`${t+1}. ${e}`))),r.blockers.length&&(console.log(),console.log("Blockers:"),r.blockers.forEach(e=>console.log(`- ${e}`))),r.lastSession&&(console.log(),console.log(`Last session: ${r.lastSession.agent} / ${r.lastSession.nativeSessionId}`))}async function ct(){let[r="help",...e]=process.argv.slice(2),t=new x().detect(it(e,"--project")??process.cwd()),o=at(t);if(r==="reconcile"){let s=await oe(t,o);se(s);return}if(r==="status"){let s=await O(t,o);if(!s){console.log("No active work state yet.");return}se(s);return}if(r==="json"){let s=await O(t,o);console.log(JSON.stringify(s,null,2));return}console.log(`Work Continuity
1
+ import{existsSync as ae,readFileSync as ce}from"node:fs";import{homedir as pe}from"node:os";import{join as ue}from"node:path";function de(r){let e=r.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 r=process.env.TOOLNET_GLOBAL_ENV??ue(pe(),".config","toolnet-memory",".env");if(!ae(r))return;let e=ce(r,"utf8");for(let t of e.split(/\r?\n/)){let o=t.trim();if(!o||o.startsWith("#"))continue;o.startsWith("export ")&&(o=o.slice(7));let s=o.indexOf("=");if(s<=0)continue;let i=o.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=de(o.slice(s+1)))}}le();function h(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function S(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function B(){return{memory:{autoCapture:h(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:h(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:h(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:h(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:S(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:S(process.env.MEMORY_RERANK_TOP,10),finalContext:S(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:S(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:h(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:S(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as me}from"node:crypto";import{existsSync as E,mkdirSync as ge,readFileSync as fe,renameSync as ye,writeFileSync as he}from"node:fs";import{basename as Se,dirname as x,join as P,parse as W,resolve as k}from"node:path";var J=".toolnet",ke="project.json";function je(r){return me("sha256").update(r).digest("hex").slice(0,16)}function C(r){return P(r,J,ke)}function ve(r){return E(C(r))}function we(r,e){let t=k(r),o=W(t).root;for(;;){if(ve(t))return t;if(t===o||e&&t===k(e))break;let s=x(t);if(s===t)break;t=s}return null}function xe(r){let e=k(r),t=W(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(i=>E(P(e,i))))return e;if(e===t)break;let s=x(e);if(s===e)break;e=s}return k(r)}function be(r){let e;try{e=JSON.parse(fe(r,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${r}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let o=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:x(x(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:o,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:o,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 L(r,e){let t=P(r,J);ge(t,{recursive:!0});let o=C(r),s=`${o}.tmp-${process.pid}`;he(s,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),ye(s,o)}function V(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var b=class{detect(e=process.cwd()){let t=k(e),o=xe(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>E(P(o,d))),a=we(t,i?o:void 0);if(a){let d=C(a),m=be(d);return m.rootPath!==a&&(m.rootPath=a,m.updatedAt=new Date().toISOString(),L(a,m)),V(m,a)}let c=new Date().toISOString(),p=Se(o),u={version:1,id:je(o),name:p,remote:p,rootPath:o,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return L(o,u),V(u,o)}};import{homedir as He}from"node:os";import{join as Ye}from"node:path";import{DeleteObjectCommand as Pe,GetObjectCommand as Ae,HeadObjectCommand as Re,ListObjectsV2Command as Te,PutObjectCommand as Ie,S3Client as Ee}from"@aws-sdk/client-s3";import{getSignedUrl as Ce}from"@aws-sdk/s3-request-presigner";var A=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,o="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ie({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ce(this.client,new Ae({Bucket:this.bucket,Key:e}),{expiresIn:60}),o=await fetch(t,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`HF download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.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 Pe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new Te({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let i of s.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};import{access as F,mkdir as Oe,readFile as Me,readdir as _e,rm as Ne,stat as U,writeFile as $e}from"node:fs/promises";import{dirname as Ke,join as De,relative as H,resolve as ze}from"node:path";var j=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return ze(this.root,t)}async put(e,t){let o=this.path(e);await Oe(Ke(o),{recursive:!0}),await $e(o,t)}async get(e){try{return await Me(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 Ne(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await F(t)}catch{return o}let s=async a=>{let c=await _e(a,{withFileTypes:!0});for(let p of c){let u=De(a,p.name);if(p.isDirectory()){await s(u);continue}let d=await U(u);o.push({key:H(this.root,u),size:d.size,updatedAt:d.mtime.toISOString()})}},i=await U(t);return i.isDirectory()?await s(t):o.push({key:H(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),o}};import{DeleteObjectCommand as Be,GetObjectCommand as Le,HeadObjectCommand as Ve,ListObjectsV2Command as We,PutObjectCommand as Je,S3Client as Fe}from"@aws-sdk/client-s3";import{getSignedUrl as Ue}from"@aws-sdk/s3-request-presigner";var v=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Fe({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,o="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Je({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ue(this.client,new Le({Bucket:this.bucket,Key:e}),{expiresIn:60}),o=await fetch(t,{redirect:"follow"});if(o.status===404)return null;if(!o.ok)throw new Error(`${this.name} download failed: ${o.status} ${o.statusText}`);return new Uint8Array(await o.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 Ve({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 Be({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new We({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let i of s.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};function O(r,e){return console.warn(e),new j(r)}function Y(r){let e=r.localRoot??Ye(He(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new v({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(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new v({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(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new A({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new j(e)}function Ge(r){return new Promise(e=>setTimeout(e,r))}async function G(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,s=e.maxDelayMs??2e3,i;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(i=c,a>=t)break;let p=Math.min(s,o*2**(a-1)),u=Math.floor(Math.random()*Math.max(1,p*.2));await Ge(p+u)}throw i}var Ze=new Set(["put","get","getText","delete","list"]);function Z(r,e={}){return new Proxy(r,{get(t,o){let s=Reflect.get(t,o,t);return typeof s!="function"?s:Ze.has(o)?(...i)=>G(()=>Promise.resolve(s.apply(t,i)),e):s.bind(t)}})}function Q(r){let e=r.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 X(r){let e=r.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 o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(o)return`${o[1]}memory/records/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),o)return`${o[1]}memory/vectors/${o[2]}`;if(o=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),o)return`${o[1]}code/graph/${o[2]}`;let s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var R=class{constructor(e,t,o,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=o,this.folder=Q(s??o),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(),o=t,s=await this.provider.getText(e);if(s){let a;try{a=JSON.parse(s)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(o=a.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,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=X(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,o){return await this.ensureRegistered(),this.provider.put(this.key(e),t,o)}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 te}from"node:path";import{mkdirSync as rt}from"node:fs";import{createHash as Qe}from"node:crypto";import{dirname as Xe}from"node:path";import{mkdirSync as qe,readFileSync as to,renameSync as et,writeFileSync as tt}from"node:fs";function q(r){return Qe("sha256").update(r).digest("hex")}function ee(r,e){qe(Xe(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;tt(t,JSON.stringify(e,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),et(t,r)}function ot(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function y(r,e=20){let t=[],o=new Set;for(let s of r.slice().reverse()){let i=ot(s);if(!(!i||o.has(i))&&(o.add(i),t.push(s),t.length>=e))break}return t.reverse()}function st(r){return r.kind==="phase"?/^Phase\s+\d+$/iu.test(r.text):r.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(r.text):!1}function re(r,e){let t=e.status??r?.status??"pending",o=t;r&&(r.status==="completed"&&t!=="completed"?o="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(o=r.status));let s=r&&st(e)?r.title:e.text;return{id:r?.id??q(e.key).slice(0,24),title:s,status:o,order:e.order??r?.order,confidence:Math.max(e.confidence,r?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function nt(r,e){let t=`projects/${r.id}/work/observations/`,o=await e.list(t),s=[];for(let i of o.filter(a=>a.key.endsWith(".json")).sort((a,c)=>a.key.localeCompare(c.key))){let a=await e.getText(i.key);if(a)try{let c=JSON.parse(a);c.version===1&&Array.isArray(c.observations)&&s.push(c)}catch{}}return s}async function oe(r,e){let o=(await nt(r,e)).flatMap(n=>n.observations).sort((n,f)=>{let D=n.occurredAt.localeCompare(f.occurredAt);if(D!==0)return D;let z=n.sequence-f.sequence;return z!==0?z:n.id.localeCompare(f.id)}),s=new Map,i=new Map,a,c,p,u=[],d=[],m=[],_=[],N=[],$=[];for(let n of o)switch(n.kind){case"goal":a=n.text;break;case"plan":c=n.text;break;case"phase":s.set(n.key,re(s.get(n.key),n));break;case"task":i.set(n.key,re(i.get(n.key),n));break;case"decision":u.push(n.text);break;case"blocker":d.push(n.text);break;case"warning":m.push(n.text);break;case"next_action":_.push(n.text);break;case"file":N.push(n.text);break;case"test":$.push(n.text);break;case"session":p={agent:n.agent,nativeSessionId:n.nativeSessionId,sessionKey:n.sessionKey,updatedAt:n.occurredAt};break}let g=Array.from(s.values()).sort((n,f)=>(n.order??Number.MAX_SAFE_INTEGER)-(f.order??Number.MAX_SAFE_INTEGER)),l=Array.from(i.values()).sort((n,f)=>(n.order??Number.MAX_SAFE_INTEGER)-(f.order??Number.MAX_SAFE_INTEGER)),T=g.find(n=>n.status==="in_progress")??g.find(n=>n.status==="blocked")??g.find(n=>n.status==="pending"),w=l.find(n=>n.status==="in_progress")??l.find(n=>n.status==="blocked")??l.find(n=>n.status==="pending"),ne=y([..._,...w?[w.title]:[],...!w&&T?[T.title]:[],...l.filter(n=>n.status==="pending").slice(0,5).map(n=>n.title)],8),ie=y([...d,...g.filter(n=>n.status==="blocked").map(n=>n.title),...l.filter(n=>n.status==="blocked").map(n=>n.title)],20),I={version:1,projectId:r.id,projectName:r.name,goal:a,plan:c,phases:g,tasks:l,decisions:y(u,20),blockers:ie,warnings:y(m,20),nextActions:ne,filesTouched:y(N,30),tests:y($,20),currentPhase:T,currentTask:w,progress:{phasesTotal:g.length,phasesCompleted:g.filter(n=>n.status==="completed").length,tasksTotal:l.length,tasksCompleted:l.filter(n=>n.status==="completed").length,blocked:g.filter(n=>n.status==="blocked").length+l.filter(n=>n.status==="blocked").length},lastSession:p,updatedAt:o.length?o[o.length-1].occurredAt:new Date().toISOString()},K=te(r.rootPath,".toolnet","work");return rt(K,{recursive:!0}),ee(te(K,"current.json"),I),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(I,null,2)+`
6
+ `,"application/json"),I}async function M(r,e){let t=await e.getText(`projects/${r.id}/work/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function it(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function at(r){let e=B(),t=Z(Y({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new R(t,r.id,r.name,r.remote??r.name)}function se(r){console.log(),console.log(`Project: ${r.projectName}`),r.goal&&console.log(`Goal: ${r.goal}`),r.currentPhase&&console.log(`Current phase: ${r.currentPhase.title} [${r.currentPhase.status}]`),r.currentTask&&console.log(`Current task: ${r.currentTask.title} [${r.currentTask.status}]`),console.log(`Phases: ${r.progress.phasesCompleted}/${r.progress.phasesTotal}`),console.log(`Tasks: ${r.progress.tasksCompleted}/${r.progress.tasksTotal}`),r.nextActions.length&&(console.log(),console.log("Next actions:"),r.nextActions.forEach((e,t)=>console.log(`${t+1}. ${e}`))),r.blockers.length&&(console.log(),console.log("Blockers:"),r.blockers.forEach(e=>console.log(`- ${e}`))),r.lastSession&&(console.log(),console.log(`Last session: ${r.lastSession.agent} / ${r.lastSession.nativeSessionId}`))}async function ct(){let[r="help",...e]=process.argv.slice(2),t=new b().detect(it(e,"--project")??process.cwd()),o=at(t);if(r==="reconcile"){let s=await oe(t,o);se(s);return}if(r==="status"){let s=await M(t,o);if(!s){console.log("No active work state yet.");return}se(s);return}if(r==="json"){let s=await M(t,o);console.log(JSON.stringify(s,null,2));return}console.log(`Work Continuity
7
7
 
8
8
  Commands:
9
9
  status [--project PATH]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolnet-memory",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Persistent project memory, work continuity, and code intelligence for AI coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/bundle/api.js DELETED
@@ -1,52 +0,0 @@
1
- import"dotenv/config";import{existsSync as Hr,readFileSync as Gr}from"node:fs";import{homedir as Wr}from"node:os";import{join as Ur}from"node:path";function Br(r){let e=r.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 Vr(){let r=process.env.TOOLNET_GLOBAL_ENV??Ur(Wr(),".config","toolnet-memory",".env");if(!Hr(r))return;let e=Gr(r,"utf8");for(let t of e.split(/\r?\n/)){let n=t.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let i=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=Br(n.slice(o+1)))}}Vr();function C(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function T(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function $e(){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:T(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:T(process.env.MEMORY_RERANK_TOP,10),finalContext:T(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:T(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:C(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:T(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}var zr={critical:100,high:75,normal:50,temporary:20};function Fe(r){return zr[r]}function He(r,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":r==="rule"||r==="decision"?"high":r==="todo"?"normal":r==="activity"||r==="summary"?"temporary":"normal"}import{createHash as qr}from"node:crypto";import{existsSync as be,mkdirSync as Jr,readFileSync as Yr,renameSync as Qr,writeFileSync as Xr}from"node:fs";import{basename as Zr,dirname as Y,join as X,parse as Ue,resolve as O}from"node:path";var Be=".toolnet",en="project.json";function tn(r){return qr("sha256").update(r).digest("hex").slice(0,16)}function Se(r){return X(r,Be,en)}function rn(r){return be(Se(r))}function nn(r,e){let t=O(r),n=Ue(t).root;for(;;){if(rn(t))return t;if(t===n||e&&t===O(e))break;let o=Y(t);if(o===t)break;t=o}return null}function on(r){let e=O(r),t=Ue(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(i=>be(X(e,i))))return e;if(e===t)break;let o=Y(e);if(o===e)break;e=o}return O(r)}function sn(r){let e;try{e=JSON.parse(Yr(r,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${r}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let n=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(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:n,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:n,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 Ge(r,e){let t=X(r,Be);Jr(t,{recursive:!0});let n=Se(r),o=`${n}.tmp-${process.pid}`;Xr(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),Qr(o,n)}function We(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var Q=class{detect(e=process.cwd()){let t=O(e),n=on(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>be(X(n,u))),s=nn(t,i?n:void 0);if(s){let u=Se(s),p=sn(u);return p.rootPath!==s&&(p.rootPath=s,p.updatedAt=new Date().toISOString(),Ge(s,p)),We(p,s)}let a=new Date().toISOString(),c=Zr(n),d={version:1,id:tn(n),name:c,remote:c,rootPath:n,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return Ge(n,d),We(d,n)}};import{randomUUID as bn}from"node:crypto";var an=[{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}],Z=class{scan(e){let t=[];for(let n of an){let o=new RegExp(n.regex.source,n.regex.flags);for(let i of e.matchAll(o))t.push({type:n.type,value:i[0]})}return t}hasSecrets(e){return this.scan(e).length>0}};var ee=class{scanner=new Z;sanitize(e){let t=e,n=this.scanner.scan(e),o=new Set;for(let i of n)o.add(i.type),t=t.split(i.value).join(`[REDACTED:${i.type}]`);return{text:t,redacted:n.length,secretTypes:[...o]}}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[n,o]of Object.entries(e)){let i=n.toLowerCase();i.includes("password")||i.includes("secret")||i.includes("token")||i.includes("cookie")||i.includes("authorization")?t[n]="[REDACTED]":t[n]=this.sanitizeValue(o)}return t}return e}};var cn=new Set(["decision","rule","todo","summary","activity","user","error","file","write","command","learned","session","architecture","fix","context","opencode","agy","codex"]),un=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 Ve(r){return new Set(r.toLowerCase().normalize("NFKC").replace(/[^\p{L}\p{N}_-]+/gu," ").split(/\s+/).filter(e=>e.length>2&&!un.has(e)))}function ln(r,e){let t=Ve(r),n=Ve(e);if(t.size===0||n.size===0)return 0;let o=0;for(let s of t)n.has(s)&&o++;let i=new Set([...t,...n]).size;return o/i}function ze(r){return new Set(r.tags.filter(e=>!cn.has(e.toLowerCase())))}var te=class{findSuperseded(e,t){if(e.type!=="rule"&&e.type!=="decision")return[];let n=typeof e.metadata?.topic=="string"?e.metadata.topic:void 0,o=ze(e);return t.filter(i=>{if(i.id===e.id||i.projectId!==e.projectId||i.type!==e.type||i.metadata?.supersededBy)return!1;let s=typeof i.metadata?.topic=="string"?i.metadata.topic:void 0;if(n&&s&&n===s)return!0;let a=ze(i);for(let c of o)if(a.has(c))return!0;return ln(e.content,i.content)>=.6})}};var dn=new Set(["code","decision","rule","todo","summary"]);function mn(r){return r.normalize("NFKC").toLowerCase().replace(/[“”"'`]/gu,"").replace(/[.!?]+$/gu,"").replace(/\s+/gu," ").trim()}function qe(r){return r.tags.includes("class:permanent")?4:r.tags.includes("class:task")?3:r.tags.includes("class:session")?2:r.tags.includes("class:transient")?1:0}function pn(r,e){let t=qe(e)-qe(r);if(t!==0)return t;let n=e.importanceScore-r.importanceScore;if(n!==0)return n;let o=e.updatedAt.localeCompare(r.updatedAt);if(o!==0)return o;let i=e.createdAt.localeCompare(r.createdAt);return i!==0?i:r.id.localeCompare(e.id)}function Je(r){let e=new Set,t=[];for(let n of r){let o=n.trim();if(!o)continue;let i=o.normalize("NFKC").toLowerCase();e.has(i)||(e.add(i),t.push(o))}return t}function Ye(r){let e=new Map;for(let o of r){if(!dn.has(o.type)||o.metadata?.supersededBy)continue;let i=mn(o.content);if(!i)continue;let s=[o.projectId,o.type,i].join("\0"),a=e.get(s)??[];a.push(o),e.set(s,a)}let t=[],n=0;for(let o of e.values()){if(o.length<2)continue;let i=[...o].sort(pn),s=i[0];if(!s)continue;let a=i.slice(1);t.push({canonicalId:s.id,duplicateIds:a.map(c=>c.id),mergedTags:Je(i.flatMap(c=>c.tags)),mergedSources:Je(i.map(c=>c.source))}),n+=a.length}return{groups:t,duplicates:n}}function Ae(r){if(r.tags.includes("class:permanent"))return"permanent";if(r.tags.includes("class:task"))return"task";if(r.tags.includes("class:session"))return"session";if(r.tags.includes("class:transient"))return"transient"}function fn(r,e){let t=new Date(r.updatedAt).getTime();return Number.isFinite(t)?Math.max(0,(e-t)/864e5):0}function gn(r){let e=Ae(r);if(e==="permanent")return 730;if(e==="task")return 180;if(e==="session")return 45;if(e==="transient")return 7;switch(r.type){case"rule":return 730;case"decision":return 365;case"code":return 180;case"todo":return 120;case"summary":return 45;case"activity":return 14}}function yn(r){return Ae(r)==="permanent"||r.type==="rule"||r.type==="decision"||r.importance==="critical"}function hn(r){let e=r.normalize("NFKC").toLowerCase().trim();return e.length<8?!0:[/^ok(?:ay)?[.!]?$/u,/^done[.!]?$/u,/^thanks?[.!]?$/u,/^test(?:ing)?[.!]?$/u,/^npm notice\b/u,/^changed \d+ packages?/u,/^packages? are looking for funding/u].some(t=>t.test(e))}function Qe(r,e=Date.now()){let t=50,n=[],o=Ae(r);switch(o==="permanent"?(t+=25,n.push("permanent project knowledge")):o==="task"?(t+=15,n.push("active task knowledge")):o==="session"?(t+=2,n.push("session-scoped knowledge")):o==="transient"&&(t-=25,n.push("transient knowledge")),r.importance){case"critical":t+=25,n.push("critical importance");break;case"high":t+=15,n.push("high importance");break;case"normal":break;case"temporary":t-=15,n.push("temporary importance");break}(r.type==="rule"||r.type==="decision")&&(t+=10,n.push("durable knowledge type"));let i=r.content.trim().length;i<12?(t-=30,n.push("very short content")):i<30?(t-=10,n.push("limited context")):i<=1200?(t+=8,n.push("useful context density")):i>5e3&&(t-=15,n.push("excessively large memory")),hn(r.content)&&(t-=35,n.push("matches transient/noise pattern"));let s=r.metadata?.consolidation;s&&typeof s=="object"&&Array.isArray(s.sources)&&s.sources.length>1&&(t+=10,n.push("confirmed across multiple sources"));let a=fn(r,e)>=gn(r);a&&(t-=15,n.push("stale for lifecycle class"));let c=yn(r);t=Math.max(0,Math.min(100,Math.round(t)));let d;t>=80?d="trusted":t>=55?d="useful":t>=30?d="weak":d="noise";let u=r.importance==="temporary"||r.type==="activity"||o==="transient";return{score:t,tier:d,stale:a,protected:c,pruneEligible:!c&&(d==="noise"&&u||a&&(d==="noise"||d==="weak")),reasons:n}}var vn={critical:100,high:75,normal:50,temporary:20},wn={activity:14,summary:30,todo:90,decision:365,rule:730,code:180};function Xe(r,e,t=new Date){let n;if(e==="temporary"&&(n=30),r==="activity"&&(n=Math.min(n??14,14)),r==="summary"&&(n=Math.min(n??30,30)),!!n)return new Date(t.getTime()+n*864e5).toISOString()}function Me(r,e=Date.now()){return r.expiresAt?new Date(r.expiresAt).getTime()<=e:!1}function kn(r){return!!r.metadata?.supersededBy}function xe(r,e=Date.now()){return!Me(r,e)&&!kn(r)}function _(r,e=Date.now()){if(!xe(r,e))return 0;let t=vn[r.importance];if(r.importance==="critical")return t;let n=wn[r.type]??180,i=Math.max(0,e-new Date(r.updatedAt).getTime())/864e5,s=Math.pow(.5,i/n);return t*s}var re=class{memories=new Map;sanitizer=new ee;conflicts=new te;remember(e){let t=new Date().toISOString(),n=this.sanitizer.sanitize(e.content.trim()).text,o=e.importance??He(e.type,n),i={id:bn(),projectId:e.projectId,type:e.type,content:n,importance:o,importanceScore:Fe(o),tags:e.tags??[],source:e.source??"agent",createdAt:t,updatedAt:t,expiresAt:e.expiresAt??Xe(e.type,o,new Date(t)),metadata:this.sanitizer.sanitizeValue(e.metadata)},s=this.conflicts.findSuperseded(i,this.list(e.projectId));if(s.length){i.metadata={...i.metadata??{},supersedes:s.map(a=>a.id)};for(let a of s)a.updatedAt=t,a.metadata={...a.metadata??{},supersededBy:i.id,supersededAt:t},this.memories.set(a.id,a)}return this.memories.set(i.id,i),i}importRecords(e){let t=0;for(let n of e)n?.id&&(this.memories.set(n.id,n),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,n)=>n.createdAt.localeCompare(t.createdAt))}list(e){return this.listAll(e).filter(t=>xe(t))}recent(e,t=10){return this.list(e).slice(0,t)}byType(e,t){return this.list(e).filter(n=>n.type===t)}search(e){let t=e.query.trim().toLowerCase(),n=e.limit??10,o=t.split(/\s+/).filter(Boolean);return this.list(e.projectId).filter(i=>{if(e.types&&!e.types.includes(i.type))return!1;let s=_(i);return!(e.minImportanceScore&&s<e.minImportanceScore||e.tags?.length&&!e.tags.some(c=>i.tags.includes(c)))}).map(i=>{let s=i.content.toLowerCase(),a=o.filter(u=>s.includes(u)).length,c=o.length===0?0:a/o.length,d=_(i)/500;return{memory:i,score:c+d,source:"memory"}}).filter(i=>t.length===0||i.score>0).sort((i,s)=>s.score-i.score).slice(0,n)}consolidate(e,t=Date.now()){let n=Ye(this.list(e)),o=[],i=0;for(let s of n.groups){let a=this.memories.get(s.canonicalId);if(a){a.tags=s.mergedTags,a.metadata={...a.metadata??{},consolidation:{version:1,consolidatedAt:new Date(t).toISOString(),mergedIds:s.duplicateIds,sources:s.mergedSources}},this.memories.set(a.id,a),o.push(a.id);for(let c of s.duplicateIds)this.memories.delete(c)&&(i+=1)}}return{groupsConsolidated:o.length,duplicatesRemoved:i,canonicalIds:o}}reviewLifecycle(e,t=Date.now()){let n={reviewed:0,trusted:0,useful:0,weak:0,noise:0,stale:0,protected:0,pruned:0};for(let o of this.list(e)){let i=Qe(o,t);n.reviewed+=1,n[i.tier]+=1,i.stale&&(n.stale+=1),i.protected&&(n.protected+=1),o.metadata={...o.metadata??{},lifecycle:{version:1,reviewedAt:new Date(t).toISOString(),qualityScore:i.score,tier:i.tier,stale:i.stale,protected:i.protected,reasons:i.reasons}},this.memories.set(o.id,o),i.pruneEligible&&this.memories.delete(o.id)&&(n.pruned+=1)}return n}pruneExpired(e,t=Date.now()){let n=0;for(let[o,i]of this.memories)i.projectId===e&&Me(i,t)&&(this.memories.delete(o),n++);return n}pruneSuperseded(e,t=30,n=Date.now()){let o=0,i=t*864e5;for(let[s,a]of this.memories){if(a.projectId!==e)continue;let c=a.metadata?.supersededAt;if(typeof c!="string")continue;n-new Date(c).getTime()>=i&&(this.memories.delete(s),o++)}return o}clearProject(e){let t=0;for(let[n,o]of this.memories)o.projectId===e&&(this.memories.delete(n),t++);return t}};function Pe(r){return r.toLowerCase().normalize("NFKC").replace(/[^\p{L}\p{N}_\-./]+/gu," ").split(/\s+/).map(e=>e.trim()).filter(e=>e.length>1)}var ne=class{score(e,t){let n=Pe(e),o=Pe([t.content,...t.tags??[],t.type].join(" "));if(n.length===0||o.length===0)return 0;let i=new Map;for(let a of o)i.set(a,(i.get(a)??0)+1);let s=0;for(let a of n){let c=i.get(a)??0;c!==0&&(s+=1+Math.log(1+c))}return s/n.length}};function Ze(r,e=Date.now()){let t=r.updatedAt??r.createdAt,n=new Date(t).getTime();if(!Number.isFinite(n))return 0;let o=Math.max(0,e-n)/864e5;return Math.exp(-Math.LN2*o/30)}function et(r){return _(r)/100}var Sn={rule:1,decision:.9,todo:.8,summary:.6,activity:.3};function tt(r){return Sn[r]??.4}var oe=class{analyze(e){let t=e.toLowerCase(),n=[];return(t.includes("quy\u1EBFt \u0111\u1ECBnh")||t.includes("decision")||t.includes("\u0111\xE3 ch\u1ECDn"))&&n.push("decision"),(t.includes("todo")||t.includes("c\u1EA7n l\xE0m")||t.includes("ti\u1EBFp theo")||t.includes("ch\u01B0a xong"))&&n.push("todo"),(t.includes("quy t\u1EAFc")||t.includes("rule")||t.includes("kh\xF4ng \u0111\u01B0\u1EE3c")||t.includes("b\u1EAFt bu\u1ED9c"))&&n.push("rule"),(t.includes("t\xF3m t\u1EAFt")||t.includes("summary")||t.includes("\u0111\xE3 l\xE0m"))&&n.push("summary"),{query:e,preferredTypes:n}}};var ie=class{bm25=new ne;analyzer=new oe;search(e,t,n={}){let o=n.topK??8,i=n.minScore??.05,s=this.analyzer.analyze(e),a=[];for(let c of t){if(n.types?.length&&!n.types.includes(c.type))continue;let d=this.bm25.score(e,c),u=Ze(c),p=et(c),h=tt(c.type);s.preferredTypes.includes(c.type)&&(h=Math.min(1,h+.25));let v=d*.55+p*.2+u*.15+h*.1;d<=0||v<i||a.push({memory:c,score:v,scores:{keyword:d,recency:u,importance:p,type:h}})}return a.sort((c,d)=>d.score-c.score).slice(0,o)}};var se=class{build(e,t=6e3){let n=[],o=0;for(let i of e){let s=i.memory,a=[`[${s.type.toUpperCase()}]`,s.content].join(" ");if(o+a.length>t)break;n.push(a),o+=a.length}return n.join(`
4
- `)}};var ae=class{constructor(e){this.memory=e}memory;searcher=new ie;contextBuilder=new se;search(e,t,n={}){return this.searcher.search(t,this.memory.list(e),n)}context(e,t,n={}){let o=this.search(e,t,n);return this.contextBuilder.build(o)}};import{homedir as Bn}from"node:os";import{join as Vn}from"node:path";import{DeleteObjectCommand as An,GetObjectCommand as Mn,HeadObjectCommand as xn,ListObjectsV2Command as Pn,PutObjectCommand as In,S3Client as En}from"@aws-sdk/client-s3";import{getSignedUrl as Rn}from"@aws-sdk/s3-request-presigner";var ce=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new En({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,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new In({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Rn(this.client,new Mn({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.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 xn({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 An({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Pn({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};import{access as rt,mkdir as Cn,readFile as Tn,readdir as On,rm as _n,stat as nt,writeFile as jn}from"node:fs/promises";import{dirname as Kn,join as Ln,relative as ot,resolve as Nn}from"node:path";var j=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Nn(this.root,t)}async put(e,t){let n=this.path(e);await Cn(Kn(n),{recursive:!0}),await jn(n,t)}async get(e){try{return await Tn(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 rt(this.path(e)),!0}catch{return!1}}async delete(e){await _n(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await rt(t)}catch{return n}let o=async s=>{let a=await On(s,{withFileTypes:!0});for(let c of a){let d=Ln(s,c.name);if(c.isDirectory()){await o(d);continue}let u=await nt(d);n.push({key:ot(this.root,d),size:u.size,updatedAt:u.mtime.toISOString()})}},i=await nt(t);return i.isDirectory()?await o(t):n.push({key:ot(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),n}};import{DeleteObjectCommand as Dn,GetObjectCommand as $n,HeadObjectCommand as Fn,ListObjectsV2Command as Hn,PutObjectCommand as Gn,S3Client as Wn}from"@aws-sdk/client-s3";import{getSignedUrl as Un}from"@aws-sdk/s3-request-presigner";var K=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Wn({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,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Gn({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Un(this.client,new $n({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`${this.name} download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.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 Fn({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 Dn({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Hn({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};function Ie(r,e){return console.warn(e),new j(r)}function it(r){let e=r.localRoot??Vn(Bn(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new K({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Ie(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new K({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Ie(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new ce({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):Ie(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new j(e)}var ue=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 n=JSON.parse(t);if(!Array.isArray(n))throw new Error("Invalid memory snapshot");return n}async save(e,t){let n=JSON.stringify(t,null,2);await this.storage.put(this.key(e),n,"application/json")}};function zn(r){return new Promise(e=>setTimeout(e,r))}async function st(r,e={}){let t=Math.max(1,e.attempts??3),n=e.baseDelayMs??150,o=e.maxDelayMs??2e3,i;for(let s=1;s<=t;s++)try{return await r()}catch(a){if(i=a,s>=t)break;let c=Math.min(o,n*2**(s-1)),d=Math.floor(Math.random()*Math.max(1,c*.2));await zn(c+d)}throw i}var qn=new Set(["put","get","getText","delete","list"]);function at(r,e={}){return new Proxy(r,{get(t,n){let o=Reflect.get(t,n,t);return typeof o!="function"?o:qn.has(n)?(...i)=>st(()=>Promise.resolve(o.apply(t,i)),e):o.bind(t)}})}function ct(r){let e=r.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 ut(r){let e=r.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 n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[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 le=class{constructor(e,t,n,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=ct(o??n),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(),n=t,o=await this.provider.getText(e);if(o){let s;try{s=JSON.parse(o)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof s.id=="string"&&s.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${s.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof s.createdAt=="string"&&(n=s.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:t};await this.provider.put(e,JSON.stringify(i,null,2)+`
5
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=ut(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,n){return await this.ensureRegistered(),this.provider.put(this.key(e),t,n)}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))}};var A=["hub:read","teams:write","agents:write","acl:manage","loadouts:write","observability:read","wiki:read","wiki:write","governance:read","governance:write"];var lt="hub/state.v1.json";function Jn(r,e){let t=new Date().toISOString();return{schema:"toolnet.memory-hub.v1",version:1,ownerPrincipal:e,project:{id:r.id,name:r.name,remote:r.remote??r.name},teams:[],agents:[],acl:[{principal:e,role:"owner",scopes:[...A],createdAt:t,updatedAt:t}],loadouts:[],observability:{requests:0,mutations:0,errors:0,events:[]},createdAt:t,updatedAt:t}}function Yn(r,e){let t=JSON.parse(r);if(t.schema!=="toolnet.memory-hub.v1"||t.version!==1||!t.project||t.project.id!==e.id||!Array.isArray(t.teams)||!Array.isArray(t.agents)||!Array.isArray(t.acl)||!Array.isArray(t.loadouts)||!t.observability||!Array.isArray(t.observability.events))throw new Error("Invalid ToolNet Memory Hub state");return t}var de=class{constructor(e,t,n){this.storage=e;this.project=t;this.ownerPrincipal=n}storage;project;ownerPrincipal;async load(){let e=await this.storage.getText(lt);if(!e){let t=Jn(this.project,this.ownerPrincipal);return await this.save(t),t}return Yn(e,this.project)}async save(e){await this.storage.put(lt,JSON.stringify(e,null,2),"application/json")}};import{randomUUID as Ee}from"node:crypto";var dt=["memory_search","memory_agent_ask","skill_memory_search","context_offload_read","wiki_search","wiki_read","knowledge_governance_status"],w=class extends Error{constructor(t,n){super(t);this.statusCode=n}statusCode};function Qn(r){return r==="owner"||r==="admin"?[...A]:["hub:read"]}function Re(r){return[...new Set(r.map(e=>e.trim()).filter(Boolean))]}function Xn(r){return structuredClone(r)}var me=class{constructor(e,t=100){this.store=e;this.maxEvents=t}store;maxEvents;state;queue=Promise.resolve();async initialize(){await this.ensureState()}async ensureState(){return this.state||(this.state=await this.store.load()),this.state}async mutate(e){let t,n=this.queue.then(async()=>{let o=await this.ensureState();t=e(o),o.updatedAt=new Date().toISOString(),await this.store.save(o)});return this.queue=n.then(()=>{},()=>{}),await n,t}grantFor(e,t){return e.acl.find(n=>n.principal===t)}async authorize(e,t){let n=await this.ensureState(),o=this.grantFor(n,e);return!!(o&&(o.role==="owner"||o.role==="admin"||o.scopes.includes(t)))}async requireScope(e,t){if(!await this.authorize(e,t))throw new w(`Principal '${e}' lacks '${t}'`,403)}event(e,t,n,o){return{id:Ee(),kind:e,action:t,principal:n,ok:o,timestamp:new Date().toISOString()}}pushEvent(e,t){e.observability.events.push(t),e.observability.events.length>this.maxEvents&&(e.observability.events=e.observability.events.slice(-this.maxEvents)),e.observability.lastActivityAt=t.timestamp}mutation(e,t,n){e.observability.mutations+=1,this.pushEvent(e,this.event("mutation",t,n,!0))}async summary(e){await this.requireScope(e,"hub:read");let t=await this.ensureState();return{schema:t.schema,project:{...t.project},teams:t.teams.length,agents:t.agents.length,aclGrants:t.acl.length,loadouts:t.loadouts.length,updatedAt:t.updatedAt}}async listTeams(e){return await this.requireScope(e,"hub:read"),structuredClone((await this.ensureState()).teams)}async createTeam(e,t){return await this.requireScope(e,"teams:write"),this.mutate(n=>{let o=new Date().toISOString(),i=t.id?.trim()||`team-${Ee().slice(0,8)}`;if(n.teams.some(a=>a.id===i))throw new w(`Team already exists: ${i}`,409);let s={id:i,name:t.name.trim(),...t.description?.trim()?{description:t.description.trim()}:{},createdAt:o,updatedAt:o};return n.teams.push(s),this.mutation(n,`team:create:${i}`,e),structuredClone(s)})}async listAgents(e){return await this.requireScope(e,"hub:read"),structuredClone((await this.ensureState()).agents)}async createAgent(e,t){return await this.requireScope(e,"agents:write"),this.mutate(n=>{let o=new Date().toISOString(),i=t.id?.trim()||`agent-${Ee().slice(0,8)}`;if(n.agents.some(c=>c.id===i))throw new w(`Agent already exists: ${i}`,409);let s=Re(t.teamIds??[]);for(let c of s)if(!n.teams.some(d=>d.id===c))throw new w(`Team not found: ${c}`,404);let a={id:i,name:t.name.trim(),...t.kind?.trim()?{kind:t.kind.trim()}:{},teamIds:s,...t.metadata?{metadata:structuredClone(t.metadata)}:{},createdAt:o,updatedAt:o};return n.agents.push(a),n.loadouts.push({agentId:i,tools:[...dt],memoryMode:"local",skillMemory:!0,contextOffload:!0,maxContextChars:6400,updatedAt:o}),this.mutation(n,`agent:create:${i}`,e),structuredClone(a)})}async listAcl(e){return await this.requireScope(e,"acl:manage"),structuredClone((await this.ensureState()).acl)}async grantAcl(e,t){return await this.requireScope(e,"acl:manage"),this.mutate(n=>{let o=new Date().toISOString(),i=Re(t.scopes??Qn(t.role));for(let c of i)if(!A.includes(c))throw new w(`Unknown Memory Hub scope: ${c}`,400);let s=n.acl.find(c=>c.principal===t.principal);if(s)return s.role=t.role,s.scopes=[...i],s.updatedAt=o,this.mutation(n,`acl:update:${t.principal}`,e),structuredClone(s);let a={principal:t.principal,role:t.role,scopes:[...i],createdAt:o,updatedAt:o};return n.acl.push(a),this.mutation(n,`acl:grant:${t.principal}`,e),structuredClone(a)})}async revokeAcl(e,t){await this.requireScope(e,"acl:manage"),await this.mutate(n=>{if(t===n.ownerPrincipal)throw new w("Memory Hub owner ACL cannot be revoked",409);let o=n.acl.length;if(n.acl=n.acl.filter(i=>i.principal!==t),n.acl.length===o)throw new w(`ACL principal not found: ${t}`,404);this.mutation(n,`acl:revoke:${t}`,e)})}async listLoadouts(e){return await this.requireScope(e,"hub:read"),structuredClone((await this.ensureState()).loadouts)}async getLoadout(e,t){await this.requireScope(e,"hub:read");let n=(await this.ensureState()).loadouts.find(o=>o.agentId===t);if(!n)throw new w(`Agent loadout not found: ${t}`,404);return structuredClone(n)}async setLoadout(e,t){return await this.requireScope(e,"loadouts:write"),this.mutate(n=>{if(!n.agents.some(s=>s.id===t.agentId))throw new w(`Agent not found: ${t.agentId}`,404);let o=new Date().toISOString(),i=n.loadouts.find(s=>s.agentId===t.agentId);return i||(i={agentId:t.agentId,tools:[...dt],memoryMode:"local",skillMemory:!0,contextOffload:!0,maxContextChars:6400,updatedAt:o},n.loadouts.push(i)),t.tools&&(i.tools=Re(t.tools)),t.memoryMode&&(i.memoryMode=t.memoryMode),t.skillMemory!==void 0&&(i.skillMemory=t.skillMemory),t.contextOffload!==void 0&&(i.contextOffload=t.contextOffload),t.maxContextChars!==void 0&&(i.maxContextChars=t.maxContextChars),i.updatedAt=o,this.mutation(n,`loadout:set:${t.agentId}`,e),structuredClone(i)})}async observability(e){return await this.requireScope(e,"observability:read"),structuredClone((await this.ensureState()).observability)}async recordRequest(e){await this.mutate(t=>{let n=e.statusCode<400;t.observability.requests+=1,n||(t.observability.errors+=1);let o={...this.event("request",`${e.method} ${e.path}`,e.principal,n),statusCode:e.statusCode,durationMs:Math.max(0,Math.floor(e.durationMs))};this.pushEvent(t,o)})}async snapshot(){return Xn(await this.ensureState())}};var mt="wiki/state.v1.json";function Zn(r){let e=new Date().toISOString();return{schema:"toolnet.wiki.v1",version:1,projectId:r.id,pages:[],revisions:[],createdAt:e,updatedAt:e}}function eo(r,e){let t=JSON.parse(r);if(t.schema!=="toolnet.wiki.v1"||t.version!==1||t.projectId!==e.id||!Array.isArray(t.pages)||!Array.isArray(t.revisions))throw new Error("Invalid ToolNet Wiki state");return t}var L=class{constructor(e,t){this.storage=e;this.project=t}storage;project;async load(){let e=await this.storage.getText(mt);if(!e){let t=Zn(this.project);return await this.save(t),t}return eo(e,this.project)}async save(e){await this.storage.put(mt,JSON.stringify(e,null,2),"application/json")}};import{randomUUID as gt}from"node:crypto";var f=class extends Error{constructor(t,n){super(t);this.statusCode=n}statusCode};function N(r){let e=new Set,t=[];for(let n of r){let o=n.replace(/\s+/gu," ").trim();if(!o)continue;let i=o.normalize("NFKC").toLowerCase();e.has(i)||(e.add(i),t.push(o))}return t}function D(r){let e=r.normalize("NFKD").replace(/[\u0300-\u036f]/gu,"").toLowerCase().replace(/[^a-z0-9]+/gu,"-").replace(/^-+|-+$/gu,"").slice(0,120);if(!e)throw new f("Invalid Wiki slug",400);return e}function pt(r){let e=[];for(let t of r.matchAll(/\[\[([^\[\]]+)\]\]/gu)){let n=t[1]?.trim();n&&e.push(D(n))}return N(e)}function to(r){return r.normalize("NFKC").toLowerCase().split(/[^\p{L}\p{N}_-]+/u).map(e=>e.trim()).filter(e=>e.length>=2)}function ft(r){return{id:`revision-${gt()}`,pageId:r.id,slug:r.slug,revision:r.revision,title:r.title,...r.summary?{summary:r.summary}:{},content:r.content,tags:[...r.tags],links:[...r.links],createdAt:r.updatedAt}}function x(r){return structuredClone(r)}var $=class{constructor(e){this.store=e}store;state;queue=Promise.resolve();async initialize(){await this.ensureState()}async ensureState(){return this.state||(this.state=await this.store.load()),this.state}async mutate(e){let t,n=this.queue.then(async()=>{let o=await this.ensureState();t=e(o),o.updatedAt=new Date().toISOString(),await this.store.save(o)});return this.queue=n.then(()=>{},()=>{}),await n,t}async summary(){let e=await this.ensureState(),t=new Set(e.pages.flatMap(n=>n.links));return{schema:"toolnet.wiki-summary.v1",projectId:e.projectId,pages:e.pages.length,revisions:e.revisions.length,tags:N(e.pages.flatMap(n=>n.tags)).sort((n,o)=>n.localeCompare(o)),links:e.pages.reduce((n,o)=>n+o.links.length,0),orphanPages:e.pages.filter(n=>n.links.length===0&&!t.has(n.slug)).length,automatedPages:e.pages.filter(n=>n.tags.some(o=>o.startsWith("toolnet-auto-"))).length,updatedAt:e.updatedAt}}async listPages(){let e=await this.ensureState();return x([...e.pages].sort((t,n)=>t.title.localeCompare(n.title)))}async getPage(e){let t=await this.ensureState(),n=D(e),o=t.pages.find(i=>i.slug===n||i.id===e);if(!o)throw new f(`Wiki page not found: ${e}`,404);return x(o)}async createPage(e){return this.mutate(t=>{let n=e.title.trim(),o=e.content.trim();if(!n)throw new f("Wiki title is required",400);let i=D(e.slug??n);if(t.pages.some(c=>c.slug===i))throw new f(`Wiki page already exists: ${i}`,409);let s=new Date().toISOString(),a={id:`wiki-${gt()}`,slug:i,title:n,...e.summary?.trim()?{summary:e.summary.trim()}:{},content:o,tags:N(e.tags??[]),links:pt(o),revision:1,createdAt:s,updatedAt:s};return t.pages.push(a),t.revisions.push(ft(a)),x(a)})}async updatePage(e,t){return this.mutate(n=>{let o=D(e),i=n.pages.find(s=>s.slug===o||s.id===e);if(!i)throw new f(`Wiki page not found: ${e}`,404);if(t.title!==void 0){let s=t.title.trim();if(!s)throw new f("Wiki title is required",400);i.title=s}if(t.summary!==void 0){let s=t.summary.trim();s?i.summary=s:delete i.summary}return t.content!==void 0&&(i.content=t.content.trim(),i.links=pt(i.content)),t.tags!==void 0&&(i.tags=N(t.tags)),i.revision+=1,i.updatedAt=new Date().toISOString(),n.revisions.push(ft(i)),x(i)})}async history(e){let t=await this.getPage(e),n=await this.ensureState();return x(n.revisions.filter(o=>o.pageId===t.id).sort((o,i)=>i.revision-o.revision))}async backlinks(e){let t=await this.getPage(e),n=await this.ensureState();return x(n.pages.filter(o=>o.links.includes(t.slug)).sort((o,i)=>o.title.localeCompare(i.title)))}async search(e,t=10){let n=await this.ensureState(),o=N(to(e));if(o.length===0)return[];let i=Math.max(1,Math.min(20,Math.floor(t))),s=[];for(let a of n.pages){let c=a.title.toLowerCase(),d=a.slug.toLowerCase(),u=a.summary?.toLowerCase()??"",p=a.content.toLowerCase(),h=a.tags.map(b=>b.toLowerCase()),v=0;for(let b of o)d===b&&(v+=12),c===b&&(v+=10),c.includes(b)&&(v+=6),d.includes(b)&&(v+=5),h.some(ke=>ke===b)?v+=5:h.some(ke=>ke.includes(b))&&(v+=3),u.includes(b)&&(v+=2),p.includes(b)&&(v+=1);v>0&&s.push({page:x(a),score:v})}return s.sort((a,c)=>c.score-a.score||c.page.updatedAt.localeCompare(a.page.updatedAt)).slice(0,i)}};import{createHash as ro,randomUUID as yt}from"node:crypto";var ht="wiki/governance.v1.json",bt="toolnet.knowledge-governance.v1",vt=500,F={autoApproveThreshold:.86,criticalApproveThreshold:.94,staleAfterDays:90};function no(r,e=0,t=1){return Math.max(e,Math.min(t,r))}function Ce(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/gu," ").trim()}function wt(r){return ro("sha256").update(r.normalize("NFKC").replace(/\s+/gu," ").trim()).digest("hex")}function oo(r){let e=[r.title,r.summary??"",r.content.slice(0,2e3),...r.tags].join(" ").toLowerCase();return/\b(?:architecture|security|authentication|authorization|auth|database|production|deploy|deployment|payment|billing|permission|permissions|acl|credential|secret|migration)\b/u.test(e)}function io(r){let e=r.sourceType==="skill"?.96:r.sourceType==="memory"?.94:.88,t=r.tags.map(n=>n.toLowerCase());return(t.includes("permanent")||t.includes("task"))&&(e+=.03),r.content.length>=200&&(e+=.02),r.content.length<80&&(e-=.05),r.title.length<4&&(e-=.05),no(e)}function kt(r){let e=new Date().toISOString();return{schema:bt,version:1,projectId:r,policy:{...F},reviews:[],audit:[],createdAt:e,updatedAt:e}}function St(r){let e=r.autoApproveThreshold??F.autoApproveThreshold,t=r.criticalApproveThreshold??F.criticalApproveThreshold,n=r.staleAfterDays??F.staleAfterDays;if(!Number.isFinite(e)||e<.5||e>1)throw new f("Invalid autoApproveThreshold",400);if(!Number.isFinite(t)||t<.5||t>1)throw new f("Invalid criticalApproveThreshold",400);if(!Number.isInteger(n)||n<1||n>3650)throw new f("Invalid staleAfterDays",400);return{autoApproveThreshold:e,criticalApproveThreshold:t,staleAfterDays:n}}var H=class{constructor(e,t){this.storage=e;this.project=t}storage;project;async load(){let e=await this.storage.getText(ht);if(!e){let t=kt(this.project.id);return await this.save(t),t}try{let t=JSON.parse(e);if(t.schema!==bt||t.version!==1||t.projectId!==this.project.id||!Array.isArray(t.reviews)||!Array.isArray(t.audit))throw new Error("invalid");return{...t,policy:St(t.policy??F)}}catch{let t=kt(this.project.id);return await this.save(t),t}}async save(e){await this.storage.put(ht,JSON.stringify(e,null,2),"application/json")}},G=class{constructor(e){this.store=e}store;state;queue=Promise.resolve();async initialize(){await this.ensureState()}async ensureState(){return this.state||(this.state=await this.store.load()),this.state}audit(e,t,n,o={}){e.audit.push({id:yt(),action:t,principal:n,...o.reviewId?{reviewId:o.reviewId}:{},...o.sourceKey?{sourceKey:o.sourceKey}:{},timestamp:new Date().toISOString(),...o.metadata?{metadata:o.metadata}:{}}),e.audit.length>vt&&(e.audit=e.audit.slice(-vt))}async mutate(e){let t,n=this.queue.then(async()=>{let o=await this.ensureState();t=await e(o),o.updatedAt=new Date().toISOString(),await this.store.save(o)});return this.queue=n.then(()=>{},()=>{}),await n,t}async policy(){return{...(await this.ensureState()).policy}}async setPolicy(e,t){return this.mutate(n=>(n.policy=St({...n.policy,...e}),this.audit(n,"policy:update",t,{metadata:{...n.policy}}),{...n.policy}))}async summary(){let e=await this.ensureState(),t=n=>e.reviews.filter(o=>o.status===n).length;return{schema:"toolnet.knowledge-governance-summary.v1",projectId:e.projectId,pending:t("pending"),approved:t("approved"),rejected:t("rejected"),superseded:t("superseded"),criticalPending:e.reviews.filter(n=>n.status==="pending"&&n.risk==="critical").length,conflictPending:e.reviews.filter(n=>n.status==="pending"&&n.risk==="conflict").length,auditEvents:e.audit.length,policy:{...e.policy},updatedAt:e.updatedAt}}async listReviews(e){let t=await this.ensureState();return structuredClone(t.reviews.filter(n=>!e||n.status===e).sort((n,o)=>o.updatedAt.localeCompare(n.updatedAt)))}async auditLog(e=100){let t=await this.ensureState(),n=Math.max(1,Math.min(500,Math.floor(e)));return structuredClone(t.audit.slice(-n).reverse())}async assess(e,t){let n=await this.ensureState(),o=io(e),i=Ce(e.title),s=t.filter(u=>u.slug!==e.slug&&Ce(u.title)===i&&wt(u.content)!==wt(e.content)).map(u=>u.slug),a=oo(e),c=[];o<n.policy.autoApproveThreshold&&c.push(`confidence:${o.toFixed(2)}`),a&&o<n.policy.criticalApproveThreshold&&c.push("critical-knowledge"),s.length>0&&c.push("conflicting-knowledge");let d=s.length>0?"conflict":a?"critical":"normal";return{confidence:o,risk:d,requiresReview:s.length>0||o<n.policy.autoApproveThreshold||a&&o<n.policy.criticalApproveThreshold,reasons:c,conflicts:s}}async gate(e,t){let n=await this.assess(e,t);return this.mutate(o=>{let i=o.reviews.find(c=>c.sourceKey===e.sourceKey&&c.digest===e.digest);if(i?.status==="approved")return{allowed:!0,mode:"review-approved",assessment:n,review:structuredClone(i)};if(i?.status==="rejected")return{allowed:!1,mode:"rejected",assessment:n,review:structuredClone(i)};if(!n.requiresReview)return this.audit(o,"knowledge:auto-approved","system",{sourceKey:e.sourceKey,metadata:{confidence:n.confidence,risk:n.risk}}),{allowed:!0,mode:"auto-approved",assessment:n};if(i?.status==="pending")return{allowed:!1,mode:"pending-review",assessment:n,review:structuredClone(i)};let s=new Date().toISOString(),a={id:yt(),sourceKey:e.sourceKey,sourceType:e.sourceType,slug:e.slug,marker:e.marker,digest:e.digest,title:e.title,...e.summary?{summary:e.summary}:{},content:e.content,tags:[...new Set([...e.tags,e.marker])],confidence:n.confidence,risk:n.risk,reasons:[...n.reasons],conflicts:[...n.conflicts],status:"pending",createdAt:s,updatedAt:s};return o.reviews.push(a),this.audit(o,"knowledge:review-requested","system",{reviewId:a.id,sourceKey:a.sourceKey,metadata:{confidence:a.confidence,risk:a.risk}}),{allowed:!1,mode:"pending-review",assessment:n,review:structuredClone(a)}})}async markApplied(e,t){await this.mutate(n=>{let o=n.reviews.find(i=>i.sourceKey===e&&i.digest===t&&i.status==="approved");o&&(o.appliedAt=new Date().toISOString(),o.updatedAt=o.appliedAt,this.audit(n,"knowledge:applied",o.reviewedBy??"system",{reviewId:o.id,sourceKey:e}))})}async decide(e,t,n){return this.mutate(async o=>{let i=o.reviews.find(d=>d.id===e);if(!i)throw new f(`Governance review not found: ${e}`,404);if(i.status!=="pending")throw new f("Governance review is already resolved",409);let s=new Date().toISOString();if(i.reviewedAt=s,i.reviewedBy=t.principal,i.updatedAt=s,t.note?.trim()&&(i.reviewNote=t.note.trim()),t.action==="reject")return i.status="rejected",this.audit(o,"knowledge:rejected",t.principal,{reviewId:e,sourceKey:i.sourceKey}),structuredClone(i);if(t.action==="supersede")return i.status="superseded",t.targetReviewId&&(i.supersededBy=t.targetReviewId),this.audit(o,"knowledge:superseded",t.principal,{reviewId:e,sourceKey:i.sourceKey,metadata:{targetReviewId:t.targetReviewId}}),structuredClone(i);if(t.action==="merge"){if(!t.targetReviewId)throw new f("targetReviewId is required for merge",400);let d=o.reviews.find(u=>u.id===t.targetReviewId);if(!d)throw new f("Merge target review not found",404);return i.status="superseded",i.mergedInto=d.id,this.audit(o,"knowledge:merged",t.principal,{reviewId:e,sourceKey:i.sourceKey,metadata:{targetReviewId:d.id}}),structuredClone(i)}i.status="approved";let c=(await n.listPages()).find(d=>d.slug===i.slug);if(c&&!c.tags.includes(i.marker))throw new f(`Wiki page '${i.slug}' is manually managed`,409);return c?await n.updatePage(i.slug,{title:i.title,summary:i.summary??"",content:i.content,tags:i.tags}):await n.createPage({slug:i.slug,title:i.title,...i.summary?{summary:i.summary}:{},content:i.content,tags:i.tags}),i.appliedAt=s,this.audit(o,"knowledge:approved",t.principal,{reviewId:e,sourceKey:i.sourceKey}),structuredClone(i)})}async quality(e){let t=await this.ensureState(),n=await e.listPages(),o=Date.now(),i=t.policy.staleAfterDays*864e5,s=n.map(u=>{let p=o-Date.parse(u.updatedAt);return{slug:u.slug,title:u.title,updatedAt:u.updatedAt,ageDays:Math.max(0,Math.floor(p/864e5)),stale:Number.isFinite(p)&&p>i}}).filter(u=>u.stale).map(({stale:u,...p})=>p),a=new Map;for(let u of n){let p=Ce(u.title),h=a.get(p)??[];h.push(u),a.set(p,h)}let c=[...a.entries()].filter(([,u])=>u.length>1).map(([u,p])=>({title:u,pages:p.map(h=>h.slug)})),d=t.reviews.filter(u=>u.status==="pending");return{schema:"toolnet.knowledge-quality.v1",totalPages:n.length,automatedPages:n.filter(u=>u.tags.some(p=>p.startsWith("toolnet-auto-"))).length,manualPages:n.filter(u=>!u.tags.some(p=>p.startsWith("toolnet-auto-"))).length,stalePages:s,duplicateTitles:c,pendingReviews:d.length,lowConfidenceReviews:d.filter(u=>u.confidence<t.policy.autoApproveThreshold).length,conflicts:d.filter(u=>u.risk==="conflict").length,generatedAt:new Date().toISOString()}}};import{timingSafeEqual as hi}from"node:crypto";import{createServer as vi}from"node:http";function At(r){return{ok:!0,service:"toolnet-memory",schema:"toolnet.api-health.v1",project:{id:r.id,name:r.name,remote:r.remote??r.name}}}function Mt(r){return{schema:"toolnet.api-project.v1",project:{id:r.id,name:r.name,remote:r.remote??r.name,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,createdAt:r.createdAt,updatedAt:r.updatedAt}}}import{z as Ht}from"zod";import{existsSync as yo,readFileSync as ho}from"node:fs";import{join as vo}from"node:path";import{existsSync as so,mkdirSync as ic,readFileSync as ao,renameSync as sc,writeFileSync as ac}from"node:fs";import{dirname as uc,join as co}from"node:path";function uo(r){return co(r.rootPath,".toolnet","work","current.json")}function Te(r){let e=uo(r);if(!so(e))return null;try{let t=JSON.parse(ao(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}import{existsSync as lo,mkdirSync as mc,readFileSync as mo,renameSync as pc,writeFileSync as fc}from"node:fs";import{dirname as yc,join as po}from"node:path";function fo(r){return po(r.rootPath,".toolnet","context","session-origin.json")}function Oe(r){let e=fo(r);if(!lo(e))return null;try{let t=JSON.parse(mo(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}function go(r){return r.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function pe(r){let e=go(r),t=/(?:agent trước|previous agent|ai trước|phiên trước)/u.test(e),n=/(?:đang làm gì|làm tới đâu|dừng ở đâu|dở ở đâu|stopped where|working on|current task|file nào|last touched|làm gì tiếp|next action|next step)/u.test(e);return t&&n?"summary":/(?:agent trước là ai|previous agent|ai trước là ai|phiên trước là ai)/u.test(e)?"previous_agent":/(?:task hiện tại|current task|đang làm gì|đang làm task nào|dở ở đâu|dừng ở đâu|stopped where)/u.test(e)?"current_task":/(?:tiếp theo|next action|next step|làm gì tiếp|phải làm gì tiếp)/u.test(e)?"next_action":/(?:file cuối|last file|file nào|đang sửa file|last touched)/u.test(e)?"last_file":/(?:blocker|vướng|kẹt|lỗi gì|blocked)/u.test(e)?"blocker":/(?:decision|quyết định|đã chốt|important decision)/u.test(e)?"decision":/(?:đã xong|completed|hoàn thành|done|todo nào xong)/u.test(e)?"completed":/(?:status|trạng thái|tiến độ|progress)/u.test(e)?"status":"summary"}var wo={previous_agent:{previous_agent:100,task:35,phase:25},current_task:{task:100,phase:75,file:65,next_action:55,blocker:45},next_action:{next_action:100,todo:80,task:65,blocker:60,file:40},last_file:{file:100,task:65,next_action:40},blocker:{blocker:100,task:60,next_action:50,file:35},decision:{decision:100,task:45,file:30},completed:{completed:100,progress:70,task:30},status:{progress:100,task:85,phase:75,blocker:65,next_action:60},summary:{task:100,next_action:95,file:90,blocker:85,previous_agent:80,phase:75,completed:60,todo:60,decision:55,progress:50,test:45,goal:35}},ko={handoff:18,"session-origin":14,"work-state":8};function bo(r){return r.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function g(r,e){let t=e.value.replace(/\s+/g," ").trim();t&&r.push({...e,value:t,score:0})}function So(r){let e=vo(r.rootPath,".toolnet","work","handoff-latest.json");if(!yo(e))return null;try{let n=JSON.parse(ho(e,"utf8")).continuity;return n?.schema!=="toolnet.handoff.v2"||n.project.id!==r.id?null:n}catch{return null}}function Ao(r){let e=So(r);if(!e)return[];let t=[];g(t,{kind:"previous_agent",value:e.source.agent,source:"handoff"}),e.goal&&g(t,{kind:"goal",value:e.goal,source:"handoff"}),e.current.phase&&g(t,{kind:"phase",value:e.current.phase.title,source:"handoff"}),e.current.task&&g(t,{kind:"task",value:e.current.task.title,source:"handoff"}),e.files.current&&g(t,{kind:"file",value:e.files.current,source:"handoff"}),e.nextAction&&g(t,{kind:"next_action",value:e.nextAction,source:"handoff"});for(let n of e.blockers)g(t,{kind:"blocker",value:n,source:"handoff"});for(let n of e.decisions)g(t,{kind:"decision",value:n,source:"handoff"});for(let n of e.completed.tasks)g(t,{kind:"completed",value:n,source:"handoff"});for(let n of e.remaining.todos)g(t,{kind:"todo",value:n,source:"handoff"});return g(t,{kind:"test",value:`Test status: ${e.tests.status}`,source:"handoff"}),g(t,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"handoff"}),t}function Mo(r){let e=Oe(r);if(!e)return[];let t=[];return g(t,{kind:"previous_agent",value:e.agent,source:"session-origin"}),e.currentPhase&&g(t,{kind:"phase",value:e.currentPhase,source:"session-origin"}),e.currentTask&&g(t,{kind:"task",value:e.currentTask,source:"session-origin"}),e.lastTouchedFile&&g(t,{kind:"file",value:e.lastTouchedFile,source:"session-origin"}),e.latestNextAction&&g(t,{kind:"next_action",value:e.latestNextAction,source:"session-origin"}),e.latestBlocker&&g(t,{kind:"blocker",value:e.latestBlocker,source:"session-origin"}),e.latestDecision&&g(t,{kind:"decision",value:e.latestDecision,source:"session-origin"}),t}function xo(r){let e=Te(r);if(!e)return[];let t=[];e.goal&&g(t,{kind:"goal",value:e.goal,source:"work-state"}),e.currentPhase&&g(t,{kind:"phase",value:e.currentPhase.title,source:"work-state"}),e.currentTask&&g(t,{kind:"task",value:e.currentTask.title,source:"work-state"});let n=e.filesTouched.at(-1);n&&g(t,{kind:"file",value:n,source:"work-state"});for(let o of e.nextActions.slice(0,5))g(t,{kind:"next_action",value:o,source:"work-state"});for(let o of e.blockers.slice(-5))g(t,{kind:"blocker",value:o,source:"work-state"});for(let o of e.decisions.slice(-5))g(t,{kind:"decision",value:o,source:"work-state"});for(let o of e.tasks.filter(i=>i.status==="completed").slice(-8))g(t,{kind:"completed",value:o.title,source:"work-state"});for(let o of e.tasks.filter(i=>i.status!=="completed"&&i.status!=="cancelled").slice(0,8))g(t,{kind:"todo",value:o.title,source:"work-state"});for(let o of e.tests.slice(-5))g(t,{kind:"test",value:o,source:"work-state"});return g(t,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"work-state"}),t}function Po(r,e){let t=wo[r];return e.map((n,o)=>({...n,score:(t[n.kind]??10)+ko[n.source]+Math.max(0,5-o*.01)}))}function Io(r){let e=new Map;for(let t of r){let n=`${t.kind}:${bo(t.value)}`,o=e.get(n);(!o||t.score>o.score)&&e.set(n,t)}return[...e.values()]}function E(r,e,t={}){let n=pe(e),o=[...Ao(r),...Mo(r),...xo(r)],i=Io(Po(n,o)).sort((u,p)=>p.score-u.score),s=Math.max(1,t.maxFacts??12),a=Math.max(300,t.maxChars??3200),c=[],d=0;for(let u of i){if(c.length>=s)break;let p=u.kind.length+u.value.length+u.source.length+16;c.length>0&&d+p>a||(c.push(u),d+=p)}return{intent:n,facts:c,context:{project:{id:r.id,name:r.name},intent:n,selectedFacts:c.map(u=>({kind:u.kind,value:u.value,source:u.source}))},stats:{candidates:o.length,selected:c.length,chars:d}}}var xt={previous_agent:"Agent tr\u01B0\u1EDBc",goal:"M\u1EE5c ti\xEAu",phase:"Phase",task:"Task hi\u1EC7n t\u1EA1i",file:"File hi\u1EC7n t\u1EA1i",todo:"TODO",next_action:"Ti\u1EBFp theo",blocker:"Blocker",decision:"Quy\u1EBFt \u0111\u1ECBnh",completed:"\u0110\xE3 ho\xE0n th\xE0nh",test:"Test",progress:"Ti\u1EBFn \u0111\u1ED9"},Pt=["previous_agent","phase","task","file","todo","next_action","blocker","decision","completed","test","progress","goal"],Eo={previous_agent:["previous_agent"],current_task:["task","file","next_action"],next_action:["next_action","task","file","todo"],last_file:["file","task","next_action"],blocker:["blocker"],decision:["decision"],completed:["completed"],status:["progress","task","next_action","blocker"],summary:Pt};function Ro(r){let e=r.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim();return/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(e)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(e)}function Co(r){let e=r.filter(t=>t.source==="handoff");return e.length>0?e:r}function To(r){let e=new Set,t=[];for(let n of r){let o=`${n.kind}:${n.value.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}`;e.has(o)||(e.add(o),t.push(n))}return t}function It(r){switch(r){case"todo":case"blocker":case"decision":case"completed":return 3;default:return 1}}function Oo(r,e){let t=[];for(let n of e){let o=r.filter(i=>i.kind===n).slice(0,It(n));for(let i of o)t.push(`${xt[n]}: ${i.value}`)}return t.join(`
6
- `)}function _o(r,e){let t=Eo[e]??Pt,n=[];for(let o of t){let i=r.filter(s=>s.kind===o).slice(0,It(o));for(let s of i)n.push(`${xt[o]}: ${s.value}`)}return n.join(`
7
- `)}function W(r,e){let t=E(r,e,{maxFacts:32,maxChars:6400}),n=To(Co(t.facts)),o=Ro(e)?Oo(n,["todo","task","next_action","file"]):_o(n,String(t.intent));return o?{intent:String(t.intent),answer:o,source:n[0]?.source??"none"}:{intent:String(t.intent),answer:"ToolNet ch\u01B0a c\xF3 \u0111\u1EE7 memory cho c\xE2u h\u1ECFi n\xE0y.",source:"none"}}var Et=[{id:"openai-compatible",label:"OpenAI-compatible",requiresApiKey:!0,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"alibaba",label:"Alibaba / DashScope",requiresApiKey:!0,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"openrouter",label:"OpenRouter",defaultBaseUrl:"https://openrouter.ai/api/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"groq",label:"Groq",defaultBaseUrl:"https://api.groq.com/openai/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"deepseek",label:"DeepSeek",defaultBaseUrl:"https://api.deepseek.com",defaultModel:"deepseek-v4-flash",requiresApiKey:!0,transport:"openai-compatible"},{id:"nvidia",label:"NVIDIA NIM",defaultBaseUrl:"https://integrate.api.nvidia.com/v1",defaultModel:"deepseek-ai/deepseek-v4-pro",requiresApiKey:!0,transport:"openai-compatible"},{id:"gemini",label:"Gemini",defaultBaseUrl:"https://generativelanguage.googleapis.com/v1beta",requiresApiKey:!0,transport:"gemini"},{id:"huggingface",label:"Hugging Face",defaultBaseUrl:"https://router.huggingface.co/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"ollama",label:"Ollama / Local",defaultBaseUrl:"http://127.0.0.1:11434/v1",requiresApiKey:!1,transport:"openai-compatible"},{id:"custom",label:"Custom endpoint",requiresApiKey:!1,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"cloudflare",label:"Cloudflare Workers AI",requiresApiKey:!0,requiresAccountId:!0,transport:"cloudflare"}];function P(r){let e=Et.find(t=>t.id===r);if(!e)throw new Error(`Unsupported AI provider: ${r}`);return e}function U(r){return Et.some(e=>e.id===r)}function l(r){return process.env[r]?.trim()||void 0}function y(...r){return r.find(e=>!!e?.trim())}function Rt(){if(l("GROQ_API_KEY"))return"groq";if(l("DEEPSEEK_API_KEY"))return"deepseek";if(l("NVIDIA_API_KEY")||l("NVIDIA_NIM_API_KEY"))return"nvidia";if(l("OPENROUTER_API_KEY"))return"openrouter";if(l("ALIBABA_API_KEY")||l("DASHSCOPE_API_KEY"))return"alibaba";if(l("GEMINI_API_KEY")||l("GOOGLE_API_KEY"))return"gemini";if(l("CLOUDFLARE_API_TOKEN")&&l("CLOUDFLARE_ACCOUNT_ID"))return"cloudflare";if(l("HF_TOKEN"))return"huggingface";if(l("OLLAMA_MODEL")||l("OLLAMA_BASE_URL"))return"ollama"}function Ct(){let r=l("TOOLNET_LLM_PROVIDER");return r&&U(r)?r:Rt()??"openai-compatible"}function fe(r){let e=P(r);switch(r){case"alibaba":return{provider:r,apiKey:y(l("ALIBABA_API_KEY"),l("DASHSCOPE_API_KEY")),baseUrl:y(l("ALIBABA_BASE_URL"),l("DASHSCOPE_BASE_URL"),e.defaultBaseUrl),model:y(l("ALIBABA_MODEL"),l("DASHSCOPE_MODEL"))};case"openrouter":return{provider:r,apiKey:l("OPENROUTER_API_KEY"),baseUrl:y(l("OPENROUTER_BASE_URL"),e.defaultBaseUrl),model:l("OPENROUTER_MODEL")};case"groq":return{provider:r,apiKey:l("GROQ_API_KEY"),baseUrl:y(l("GROQ_BASE_URL"),e.defaultBaseUrl),model:l("GROQ_MODEL")};case"deepseek":return{provider:r,apiKey:l("DEEPSEEK_API_KEY"),baseUrl:y(l("DEEPSEEK_BASE_URL"),e.defaultBaseUrl),model:y(l("DEEPSEEK_MODEL"),e.defaultModel)};case"nvidia":return{provider:r,apiKey:y(l("NVIDIA_API_KEY"),l("NVIDIA_NIM_API_KEY")),baseUrl:y(l("NVIDIA_BASE_URL"),l("NVIDIA_NIM_BASE_URL"),e.defaultBaseUrl),model:y(l("NVIDIA_MODEL"),l("NVIDIA_NIM_MODEL"),e.defaultModel)};case"gemini":return{provider:r,apiKey:y(l("GEMINI_API_KEY"),l("GOOGLE_API_KEY")),baseUrl:y(l("GEMINI_BASE_URL"),e.defaultBaseUrl),model:l("GEMINI_MODEL")};case"huggingface":return{provider:r,apiKey:l("HF_TOKEN"),baseUrl:y(l("HF_INFERENCE_BASE_URL"),e.defaultBaseUrl),model:y(l("HF_LLM_MODEL"),l("HF_MODEL"))};case"ollama":return{provider:r,apiKey:l("OLLAMA_API_KEY"),baseUrl:y(l("OLLAMA_BASE_URL"),e.defaultBaseUrl),model:l("OLLAMA_MODEL")};case"cloudflare":return{provider:r,accountId:l("CLOUDFLARE_ACCOUNT_ID"),apiKey:l("CLOUDFLARE_API_TOKEN"),baseUrl:l("CLOUDFLARE_AI_BASE_URL"),model:l("CLOUDFLARE_MODEL")};case"custom":return{provider:r,apiKey:l("CUSTOM_AI_API_KEY"),baseUrl:l("CUSTOM_AI_BASE_URL"),model:l("CUSTOM_AI_MODEL")};default:return{provider:"openai-compatible",apiKey:y(l("OPENAI_API_KEY"),l("MODEL_API_KEY")),baseUrl:y(l("OPENAI_BASE_URL"),l("MODEL_BASE_URL")),model:y(l("OPENAI_MODEL"),l("MODEL_NAME"))}}}function jo(){let r=l("TOOLNET_LLM_PROVIDER"),e=r&&U(r)?r:Ct(),t=P(e),n=fe(e);return{provider:e,apiKey:y(l("TOOLNET_LLM_API_KEY"),n.apiKey),baseUrl:y(l("TOOLNET_LLM_BASE_URL"),n.baseUrl,t.defaultBaseUrl),model:y(l("TOOLNET_LLM_MODEL"),n.model,t.defaultModel),accountId:y(l("TOOLNET_LLM_ACCOUNT_ID"),n.accountId)}}function Ko(){let r=l("TOOLNET_EMBEDDING_PROVIDER");return r==="local"?"local":r&&U(r)?r:l("HF_TOKEN")||l("HF_EMBEDDING_MODEL")?"huggingface":"local"}function Lo(){let r=Ko();if(r==="local")return{provider:"local",model:y(l("TOOLNET_EMBEDDING_MODEL"),l("LOCAL_EMBEDDING_MODEL"))};let e=P(r),t,n,o,i;switch(r){case"huggingface":t=l("HF_TOKEN"),n=l("HF_INFERENCE_BASE_URL"),o=l("HF_EMBEDDING_MODEL");break;case"openai-compatible":t=l("OPENAI_API_KEY"),n=l("OPENAI_BASE_URL"),o=l("OPENAI_EMBEDDING_MODEL");break;case"cloudflare":t=l("CLOUDFLARE_API_TOKEN"),n=l("CLOUDFLARE_AI_BASE_URL"),o=l("CLOUDFLARE_EMBEDDING_MODEL"),i=l("CLOUDFLARE_ACCOUNT_ID");break;default:t=fe(r).apiKey,n=fe(r).baseUrl,o=l(`${r.toUpperCase().replace(/-/g,"_")}_EMBEDDING_MODEL`)}let s=y(l("TOOLNET_EMBEDDING_ACCOUNT_ID"),i),a=y(l("TOOLNET_EMBEDDING_BASE_URL"),n,e.defaultBaseUrl),c=r==="cloudflare"&&!a&&s?`https://api.cloudflare.com/client/v4/accounts/${s}/ai/v1`:a;return{provider:r,apiKey:y(l("TOOLNET_EMBEDDING_API_KEY"),t),baseUrl:c,model:y(l("TOOLNET_EMBEDDING_MODEL"),o),accountId:s}}function _e(){let r=jo(),e=Lo();return{llm:r,embedding:e,legacy:{llm:!l("TOOLNET_LLM_PROVIDER")&&!!Rt(),embedding:!l("TOOLNET_EMBEDDING_PROVIDER")&&!!(l("HF_TOKEN")||l("HF_EMBEDDING_MODEL"))}}}function Tt(r=Ct()){let e=_e().llm;if(r===e.provider)return{id:r,apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,accountId:e.accountId};let t=fe(r);return{id:r,apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,accountId:t.accountId}}function B(r,e){return l(`TOOLNET_LLM_FALLBACK_${r}_${e}`)}function Ot(){let r=[];for(let e of[1,2]){let t=B(e,"PROVIDER");if(!t||!U(t))continue;let n=P(t);r.push({slot:e,config:{provider:t,apiKey:B(e,"API_KEY"),baseUrl:y(B(e,"BASE_URL"),n.defaultBaseUrl),model:y(B(e,"MODEL"),n.defaultModel),accountId:B(e,"ACCOUNT_ID")}})}return r}function _t(){let r=l("TOOLNET_LLM_FALLBACK_COOLDOWN_MS"),e=l("TOOLNET_LLM_MAX_RETRIES"),t=Number(r??6e4),n=Number(e??1);return{cooldownMs:Number.isFinite(t)&&t>=0?t:6e4,maxRetries:Number.isFinite(n)&&n>=0?Math.floor(n):1}}var V=class extends Error{status;constructor(e,t){super(e),this.name="AiHttpError",this.status=t}};async function R(r,e,t=3e4){let n=new AbortController,o=setTimeout(()=>n.abort(),t);o.unref?.();try{let i=await fetch(r,{...e,signal:n.signal}),s=await i.text();if(!i.ok){let a=s;try{let c=JSON.parse(s);a=c.error?.message??c.message??s}catch{}throw new V(a||`HTTP ${i.status}`,i.status)}return s.trim()?JSON.parse(s):{}}finally{clearTimeout(o)}}function ge(r,e){return`${r.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`}var ye=class{constructor(e){this.config=e}config;id="cloudflare";model(){let e=this.config.model?.trim();if(!e)throw new Error("cloudflare: MODEL is not configured");return e}async generate(e){let t=this.config.accountId?.trim(),n=this.config.apiKey?.trim();if(!t)throw new Error("cloudflare: ACCOUNT ID is not configured");if(!n)throw new Error("cloudflare: API TOKEN is not configured");let o=this.model(),s=`${(this.config.baseUrl?.trim()||`https://api.cloudflare.com/client/v4/accounts/${t}/ai/run`).replace(/\/+$/,"")}/${o}`,a=await R(s,{method:"POST",headers:{authorization:`Bearer ${n}`,"content-type":"application/json",...this.config.headers},body:JSON.stringify({messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens})}),c=a.result?.response?.trim();if(!c)throw new Error(a.errors?.[0]?.message??"cloudflare: empty model response");return{text:c,provider:"cloudflare",model:o}}async healthCheck(){let e=Date.now();try{return{ok:!0,provider:"cloudflare",model:(await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8})).model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:"cloudflare",model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};var he=class{constructor(e){this.config=e}config;id="gemini";model(){let e=this.config.model?.trim();if(!e)throw new Error("gemini: MODEL is not configured");return e.replace(/^models\//,"")}async generate(e){let t=this.config.apiKey?.trim();if(!t)throw new Error("gemini: API KEY is not configured");let n=this.config.baseUrl?.trim()||"https://generativelanguage.googleapis.com/v1beta",o=this.model(),i=e.messages.filter(u=>u.role==="system"),s=e.messages.filter(u=>u.role!=="system").map(u=>({role:u.role==="assistant"?"model":"user",parts:[{text:u.content}]})),a=`${ge(n,`models/${encodeURIComponent(o)}:generateContent`)}?key=${encodeURIComponent(t)}`,c=await R(a,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({...i.length?{systemInstruction:{parts:[{text:i.map(u=>u.content).join(`
8
-
9
- `)}]}}:{},contents:s,generationConfig:{temperature:e.temperature,maxOutputTokens:e.maxTokens}})}),d=c.candidates?.[0]?.content?.parts?.map(u=>u.text??"").join("").trim();if(!d)throw new Error("gemini: empty model response");return{text:d,provider:"gemini",model:o,usage:c.usageMetadata?{inputTokens:c.usageMetadata.promptTokenCount,outputTokens:c.usageMetadata.candidatesTokenCount,totalTokens:c.usageMetadata.totalTokenCount}:void 0}}async healthCheck(){let e=Date.now();try{return{ok:!0,provider:"gemini",model:(await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8})).model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:"gemini",model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};var ve=class{constructor(e){this.config=e;this.id=e.id}config;id;baseUrl(){let e=this.config.baseUrl?.trim();if(!e)throw new Error(`${this.id}: BASE URL is not configured`);return e}model(){let e=this.config.model?.trim();if(!e)throw new Error(`${this.id}: MODEL is not configured`);return e}headers(){let e={"content-type":"application/json",...this.config.headers};return this.config.apiKey&&(e.authorization=`Bearer ${this.config.apiKey}`),e}async generate(e){let t=this.model(),n=await R(ge(this.baseUrl(),"chat/completions"),{method:"POST",headers:this.headers(),body:JSON.stringify({model:t,messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens,...this.id==="alibaba"?{enable_thinking:!1}:{}})}),o=n.choices?.[0]?.message?.content?.trim();if(!o)throw new Error(`${this.id}: empty model response`);return{text:o,provider:this.id,model:t,usage:n.usage?{inputTokens:n.usage.prompt_tokens,outputTokens:n.usage.completion_tokens,totalTokens:n.usage.total_tokens}:void 0}}async healthCheck(){let e=Date.now();try{let t=await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8});return{ok:!0,provider:this.id,model:t.model,message:"Provider reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:this.id,model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}};function jt(r=Tt()){switch(P(r.id).transport){case"gemini":return new he(r);case"cloudflare":return new ye(r);default:return new ve(r)}}function No(r){return new Promise(e=>{setTimeout(e,r)})}function Do(r){if(r instanceof V){let t=r.status;return t===void 0||t===408||t===409||t===425||t===429?!0:t>=500}if(!(r instanceof Error))return!0;let e=r.message.toLowerCase();return e.includes("401")||e.includes("403")||e.includes("400")||e.includes("invalid api key")||e.includes("unauthorized")||e.includes("forbidden")?!1:e.includes("timeout")||e.includes("timed out")||e.includes("abort")||e.includes("network")||e.includes("fetch failed")||e.includes("econnreset")||e.includes("econnrefused")||e.includes("429")||e.includes("rate limit")||e.includes("500")||e.includes("502")||e.includes("503")||e.includes("504")}var we=class{id;config;providers;states=new Map;cooldownMs;maxRetries;constructor(e,t=[],n={}){this.id=e.config.id,this.config=e.config,this.cooldownMs=n.cooldownMs??6e4,this.maxRetries=Math.max(0,n.maxRetries??1),this.providers=[e,...t].map(o=>({provider:jt(o.config),label:o.label}))}key(e){let t=this.providers[e];return`${e}:${t.provider.id}:${t.provider.config.model??""}`}state(e){let t=this.key(e),n=this.states.get(t);return n||(n={failures:0,cooldownUntil:0},this.states.set(t,n)),n}available(e){return this.state(e).cooldownUntil<=Date.now()}success(e){let t=this.state(e);t.failures=0,t.cooldownUntil=0,t.lastError=void 0}fail(e,t){let n=this.state(e);n.failures+=1,n.lastError=t instanceof Error?t.message:String(t),n.cooldownUntil=Date.now()+this.cooldownMs}async generate(e){let t=[];for(let n=0;n<this.providers.length;n+=1){if(!this.available(n)){t.push(`${this.providers[n].label}: cooldown`);continue}let o=this.providers[n];for(let i=0;i<=this.maxRetries;i+=1)try{let s=await o.provider.generate(e);return this.success(n),s}catch(s){let a=s instanceof Error?s.message:String(s),c=Do(s);if(t.push(`${o.label}: ${a}`),!c)throw s;if(i<this.maxRetries){await No(Math.min(250*2**i,2e3));continue}this.fail(n,s)}}throw new Error(`All ToolNet LLM providers failed: ${t.join(" | ")}`)}async healthCheck(){let e=Date.now();try{let t=await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8});return{ok:!0,provider:t.provider,model:t.model,message:"Fallback chain reachable",latencyMs:Date.now()-e}}catch(t){return{ok:!1,provider:this.id,model:this.config.model,message:t instanceof Error?t.message:String(t),latencyMs:Date.now()-e}}}status(){return this.providers.map((e,t)=>{let n=this.state(t);return{position:t,provider:e.provider.id,model:e.provider.config.model,cooldown:n.cooldownUntil>Date.now(),cooldownUntil:n.cooldownUntil||void 0,failures:n.failures,lastError:n.lastError}})}};function Kt(){let r=_e(),e=Ot();return new we({label:`Primary (${r.llm.provider})`,config:{id:r.llm.provider,apiKey:r.llm.apiKey,baseUrl:r.llm.baseUrl,model:r.llm.model,accountId:r.llm.accountId}},e.map(t=>({label:`Fallback ${t.slot} (${t.config.provider})`,config:{id:t.config.provider,apiKey:t.config.apiKey,baseUrl:t.config.baseUrl,model:t.config.model,accountId:t.config.accountId}})),_t())}var je=class{constructor(e=Kt()){this.provider=e}provider;get activeProvider(){return this.provider}setProvider(e){this.provider=e}generate(e){return this.provider.generate(e)}};function Lt(){return new je(Kt())}function $o(r,e){let t=JSON.stringify(r,null,2);return t.length<=e?t:t.slice(0,e)+`
10
- [truncated]`}function Fo(r,e){let t=W(r,e),n=E(r,e,{maxFacts:12,maxChars:3200});return $o({project:n.context.project,intent:n.intent,directAnswer:t.answer,selectedMemory:n.context.selectedFacts,retrievalStats:n.stats},4200)}async function Nt(r,e,t={}){let n=W(r,e),o=Fo(r,e),i=t.router??Lt();try{let s=await i.generate({temperature:.1,maxTokens:450,messages:[{role:"system",content:`
11
- You are ToolNet Memory Agent.
12
-
13
- You are NOT the coding agent.
14
-
15
- Your job is to help another coding AI continue work from previous sessions.
16
-
17
- Rules:
18
-
19
- 1. Answer only from the supplied ToolNet project memory.
20
- 2. Never invent completed work, files, blockers, decisions, or test results.
21
- 3. Prefer the latest session/work state over old information.
22
- 4. Be concise.
23
- 5. Do not dump raw JSON or full memory files.
24
- 6. Tell the coding agent exactly:
25
- - what the previous agent was doing,
26
- - what is already completed,
27
- - what remains unfinished,
28
- - what file was last touched,
29
- - what should be done next,
30
- when those facts are available.
31
- 7. If information is missing, say it is not recorded.
32
- 8. Do not explain ToolNet internals unless asked.
33
- 9. Do not repeat irrelevant history.
34
- 10. Respond in the same language as the question.
35
-
36
- Think of yourself as the project's memory coworker talking directly to another AI.
37
- `.trim()},{role:"user",content:`
38
- Question from coding agent:
39
-
40
- ${e}
41
-
42
- ToolNet selected memory:
43
-
44
- ${o}
45
-
46
- Answer the coding agent directly.
47
- `.trim()}]}),a=s.text.trim();return a?{answer:a,provider:s.provider,model:s.model,usedAi:!0}:{answer:n.answer,usedAi:!1}}catch{return{answer:n.answer,usedAi:!1}}}import{existsSync as Ho,mkdirSync as Go,readFileSync as Wo,renameSync as Uo,rmSync as Bo,writeFileSync as Vo}from"node:fs";import{join as Dt}from"node:path";var zo=720*60*1e3;function $t(r){return Dt(r.rootPath,".toolnet","context","memory-agent-conversation.json")}function z(r){return r.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}function qo(r){let e=z(r);return[/\btiếp tục\b/u,/\btiếp tục task\b/u,/\blúc nãy\b/u,/\btrước đó\b/u,/\btask trước\b/u,/\bprevious (?:task|session|work)\b/u,/\bcontinue\b/u,/\bresume\b/u,/\bcurrent task\b/u,/\bđang làm gì\b/u].some(t=>t.test(e))}function Jo(r){if(qo(r))return!1;let e=z(r),t=e.split(/\s+/u).filter(Boolean);return[/^(?:tại sao|vì sao|sao vậy)\b/u,/^(?:why|why so|why that)\b/u,/^(?:còn|thế|vậy|rồi sao|sau đó)\b/u,/^(?:what about|and what|then what)\b/u,/^(?:cái đó|việc đó|nó|phần đó)\b/u].some(n=>n.test(e))?!0:t.length<=5&&/\b(?:nó|đó|vậy|thế|that|it|this)\b/u.test(e)}function Yo(r,e,t){let n=$t(r);if(!Ho(n))return null;try{let o=JSON.parse(Wo(n,"utf8"));if(o.schema!=="toolnet.memory-conversation.v1"||o.version!==1||o.projectId!==r.id||!Array.isArray(o.focus))return null;let i=Date.parse(o.updatedAt);return!Number.isFinite(i)||e-i>t?null:o}catch{return null}}function Qo(r,e=8){let t=new Set,n=[];for(let o of r){let i=o.value.replace(/\s+/gu," ").trim();if(!i)continue;let s=[o.kind,z(i)].join(":");if(!t.has(s)&&(t.add(s),n.push({...o,value:i}),n.length>=e))break}return n}function Xo(r,e){let t=$t(r),n=Dt(r.rootPath,".toolnet","context");Go(n,{recursive:!0,mode:448});let o=`${t}.${process.pid}.tmp`;try{Vo(o,JSON.stringify(e,null,2)+`
48
- `,{encoding:"utf8",mode:384}),Uo(o,t)}finally{Bo(o,{force:!0})}}function Zo(r){return r.focus.slice(0,8).map(e=>`- ${e.kind}: ${e.value}`).join(`
49
- `)}function ei(r,e){return r.priorFocus.find(t=>t.kind===e)?.value}function Ke(r){if(!r.usedPriorFocus||r.currentIntent!=="summary"||r.priorFocus.length===0)return null;let e=z(r.originalQuestion),t=/^(?:tại sao|vì sao|sao vậy|why|why so|why that)\b/u.test(e),n=/^(?:rồi sao|sau đó|tiếp thì sao|thế tiếp theo|then what|what next)\b/u.test(e),o=t?["decision","blocker","next_action","task","file"]:n?["next_action","todo","task","blocker","file","decision"]:["task","next_action","file","decision","blocker"],i={task:"Task li\xEAn quan",next_action:"Vi\u1EC7c ti\u1EBFp theo",file:"File li\xEAn quan",decision:"Quy\u1EBFt \u0111\u1ECBnh li\xEAn quan",blocker:"Blocker",todo:"TODO"},s=[];for(let a of o){let c=ei(r,a);if(c&&(s.push(`${i[a]??a}: ${c}.`),t&&s.length>=2||!t&&s.length>=3))break}return s.length===0?null:{answer:s.join(" "),intent:"summary",source:"conversation-focus"}}function ti(r){let e=z(r);return/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(e)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(e)||/(?:còn|các)\s+(?:todo|việc)\b/u.test(e)}function Ft(r,e,t={}){let n=t.now??Date.now(),o=t.maxAgeMs??zo,i=Yo(r,n,o),s=!!(i&&i.focus.length>0&&Jo(e)&&!ti(e)),a=s&&i?[e.trim(),"","Previous ToolNet Memory conversation focus:",Zo(i),"","Use the compact focus only to resolve the current follow-up question.","Do not reconstruct or request raw session transcripts."].join(`
50
- `):e.trim(),c=pe(e),u=E(r,a,{maxFacts:8,maxChars:1800}).facts.map(h=>({kind:h.kind,value:h.value,source:h.source})),p=Qo(s&&i?[...u,...i.focus]:u);return Xo(r,{schema:"toolnet.memory-conversation.v1",version:1,projectId:r.id,updatedAt:new Date(n).toISOString(),lastIntent:c,focus:p}),{question:a,originalQuestion:e,usedPriorFocus:s,previousIntent:i?.lastIntent,currentIntent:c,priorFocus:i?.focus??[],focusCount:p.length}}var lu={question:Ht.string().min(2).max(4e3).describe("Question about previous project work, unfinished tasks, handoff, blockers, decisions, files or next actions."),mode:Ht.enum(["ai","local"]).optional().describe("ai uses ToolNet Memory Agent with configured LLM fallback chain. local returns deterministic local memory only.")};async function Gt(r,e){let t=Ft(r.project,e.question);if(e.mode==="local"){let o=Ke(t);if(o)return{answer:o.answer,mode:"local",usedAi:!1,source:o.source,intent:o.intent};let i=W(r.project,t.originalQuestion);return{answer:i.answer,mode:"local",usedAi:!1,source:i.source,intent:i.intent}}let n=await Nt(r.project,t.question);if(!n.usedAi){let o=Ke(t);if(o)return{answer:o.answer,mode:"ai",usedAi:!1,provider:n.provider,model:n.model}}return{answer:n.answer,mode:"ai",usedAi:n.usedAi,provider:n.provider,model:n.model}}import{z as Jt}from"zod";import{chmodSync as pu,existsSync as Wt,mkdirSync as fu,readFileSync as Ut,renameSync as gu,writeFileSync as yu}from"node:fs";import{dirname as vu,join as q}from"node:path";var Bt="toolnet.context-offload.v1",ri="toolnet.context-offload-asset.v1";function Vt(r){return q(r,".toolnet","offload")}function zt(r){return q(Vt(r),"assets")}function ni(r){return q(Vt(r),"graph.json")}function Le(){return{schema:Bt,version:1,updatedAt:new Date(0).toISOString(),nodes:[]}}function oi(r){let e=ni(r);if(!Wt(e))return Le();try{let t=JSON.parse(Ut(e,"utf8"));return t.schema!==Bt||t.version!==1||!Array.isArray(t.nodes)?Le():t}catch{return Le()}}function ii(r,e){let t=e.trim().toLowerCase();if(!/^[a-f0-9]{8,64}$/u.test(t))throw new Error("Invalid context offload asset reference.");if(t.length===64&&Wt(q(zt(r),`${t}.json`)))return t;let n=oi(r).nodes.map(o=>o.id).filter(o=>o.startsWith(t));if(n.length===0)throw new Error(`Context offload asset not found: ${e}`);if(n.length>1)throw new Error(`Ambiguous context offload asset reference: ${e}`);return n[0]}function qt(r,e,t=6e3){let n=ii(r,e),o=Ut(q(zt(r),`${n}.json`),"utf8"),i=JSON.parse(o);if(i.schema!==ri||i.version!==1||i.assetId!==n)throw new Error(`Invalid context offload asset: ${e}`);let s=Math.max(200,Math.min(2e4,Math.floor(t)));return{assetId:n,kind:i.event.type,bytes:Buffer.byteLength(o,"utf8"),truncated:o.length>s,content:o.length>s?`${o.slice(0,s)}
51
- [truncated]`:o}}var Su={assetId:Jt.string().regex(/^[a-f0-9]{8,64}$/iu).describe("Asset reference from the ToolNet Context Offload Graph."),maxChars:Jt.number().int().min(200).max(2e4).optional().describe("Maximum asset characters to return. Defaults to 6000.")};async function Yt(r,e){return qt(r.project.rootPath,e.assetId,e.maxChars??6e3)}async function Qt(r,e){return{schema:"toolnet.api-memory-ask.v1",result:await Gt({project:r},e)}}async function Xt(r,e){return{schema:"toolnet.api-context-offload.v1",result:await Yt({project:r},e)}}import{z as Zt}from"zod";var Eu={query:Zt.string(),limit:Zt.number().int().min(1).max(20).optional()};async function er(r,e){return r.retrieval.search(r.project.id,e.query,{topK:e.limit??8}).map(n=>({id:n.memory.id,type:n.memory.type,content:n.memory.content,importance:n.memory.importance,score:n.score,tags:n.memory.tags}))}import{z as nr}from"zod";import{chmodSync as Tu,existsSync as si,mkdirSync as Ou,readFileSync as ai,readdirSync as ci,renameSync as _u,statSync as ui,writeFileSync as ju}from"node:fs";import{join as tr}from"node:path";var li="toolnet.skill-memory.v1";function di(r,e=Number.MAX_SAFE_INTEGER){let t=new Set,n=[];for(let o of r){let i=o.replace(/\s+/gu," ").trim();if(!i)continue;let s=i.normalize("NFKC").toLowerCase();if(!t.has(s)&&(t.add(s),n.push(i),n.length>=e))break}return n}function mi(r){return tr(r.rootPath,".toolnet","memory","skills")}function pi(r,e){if(!r||typeof r!="object")return!1;let t=r;return t.schema===li&&t.version===1&&t.projectId===e&&typeof t.id=="string"&&typeof t.task=="string"&&Array.isArray(t.steps)&&Array.isArray(t.verification)&&Array.isArray(t.files)}function fi(r){let e=mi(r);if(!si(e))return[];if(!ui(e).isDirectory())return[];let t=[];for(let n of ci(e)){if(!n.endsWith(".json"))continue;let o=tr(e,n);try{let i=JSON.parse(ai(o,"utf8"));pi(i,r.id)&&t.push(i)}catch{}}return t.sort((n,o)=>{let i=o.createdAt.localeCompare(n.createdAt);return i!==0?i:n.id.localeCompare(o.id)})}function gi(r){return di(r.normalize("NFKC").toLowerCase().split(/[^\p{L}\p{N}_./-]+/gu).filter(e=>e.length>=2),32)}function yi(r,e){let t=e.normalize("NFKC").toLowerCase().trim();if(!t)return 0;if(r.id.toLowerCase()===t||r.fingerprint.toLowerCase()===t)return 1e3;let n=[r.title,r.task,r.summary,...r.steps,...r.verification,...r.files].join(`
52
- `).normalize("NFKC").toLowerCase(),o=0;r.task.normalize("NFKC").toLowerCase().includes(t)&&(o+=40),r.title.normalize("NFKC").toLowerCase().includes(t)&&(o+=30);for(let i of gi(t))n.includes(i)&&(o+=5),r.task.normalize("NFKC").toLowerCase().includes(i)&&(o+=4),r.files.some(s=>s.toLowerCase().includes(i))&&(o+=3);return o}function rr(r,e,t=5){let n=Math.max(1,Math.min(10,Math.floor(t)));return fi(r).map(o=>({...o,score:yi(o,e)})).filter(o=>o.score>0).sort((o,i)=>i.score!==o.score?i.score-o.score:i.createdAt.localeCompare(o.createdAt)).slice(0,n)}var $u={query:nr.string().min(2).max(500).describe("Task, problem, file, command or workflow to match against successful reusable Skill Memory SOPs."),limit:nr.number().int().min(1).max(10).optional().describe("Maximum matching reusable SOPs to return. Default 5.")};async function or(r,e){let t=rr(r.project,e.query,e.limit??5);return{schema:"toolnet.skill-memory-search.v1",query:e.query,count:t.length,matches:t}}async function ir(r,e,t){return{schema:"toolnet.api-memory-search.v1",results:await er({project:r,retrieval:e},t)}}async function sr(r,e){return{schema:"toolnet.api-skill-search.v1",result:await or({project:r},e)}}function J(r){if(!r||typeof r!="object"||Array.isArray(r))throw new Error("Invalid Memory Hub request");return r}function S(r,e,t=1,n=200){if(typeof r!="string"||r.trim().length<t||r.trim().length>n)throw new Error(`Invalid ${e}`);return r.trim()}function ar(r,e,t=500){if(r!==void 0)return S(r,e,1,t)}function Ne(r,e,t=64){if(r!==void 0){if(!Array.isArray(r)||r.length>t||r.some(n=>typeof n!="string"||!n.trim()))throw new Error(`Invalid ${e}`);return r.map(n=>n.trim())}}async function cr(r,e){return{schema:"toolnet.api-hub-summary.v1",hub:await r.summary(e)}}async function ur(r,e){return{schema:"toolnet.api-hub-teams.v1",teams:await r.listTeams(e)}}async function lr(r,e,t){let n=J(t),o={...n.id!==void 0?{id:S(n.id,"team id",2,100)}:{},name:S(n.name,"team name",2,100),...n.description!==void 0?{description:ar(n.description,"team description",500)}:{}};return{schema:"toolnet.api-hub-team.v1",team:await r.createTeam(e,o)}}async function dr(r,e){return{schema:"toolnet.api-hub-agents.v1",agents:await r.listAgents(e)}}async function mr(r,e,t){let n=J(t),o={...n.id!==void 0?{id:S(n.id,"agent id",2,100)}:{},name:S(n.name,"agent name",2,100),...n.kind!==void 0?{kind:ar(n.kind,"agent kind",100)}:{},...n.teamIds!==void 0?{teamIds:Ne(n.teamIds,"teamIds",64)}:{},...n.metadata&&typeof n.metadata=="object"&&!Array.isArray(n.metadata)?{metadata:n.metadata}:{}};return{schema:"toolnet.api-hub-agent.v1",agent:await r.createAgent(e,o)}}async function pr(r,e){return{schema:"toolnet.api-hub-acl.v1",grants:await r.listAcl(e)}}async function fr(r,e,t){let n=J(t),o=S(n.role,"role",4,20);if(!["owner","admin","member","viewer"].includes(o))throw new Error("Invalid role");let i=Ne(n.scopes,"scopes",A.length);if(i?.some(a=>!A.includes(a)))throw new Error("Invalid Memory Hub scope");let s={principal:S(n.principal,"principal",1,120),role:o,...i?{scopes:i}:{}};return{schema:"toolnet.api-hub-acl-grant.v1",grant:await r.grantAcl(e,s)}}async function gr(r,e,t){let n=J(t),o=S(n.principal,"principal",1,120);return await r.revokeAcl(e,o),{schema:"toolnet.api-hub-acl-revoke.v1",principal:o,revoked:!0}}async function yr(r,e){return{schema:"toolnet.api-hub-loadouts.v1",loadouts:await r.listLoadouts(e)}}async function hr(r,e,t){let n=J(t),o=n.maxContextChars;if(o!==void 0&&(!Number.isInteger(o)||o<500||o>5e4))throw new Error("Invalid maxContextChars");let i=n.memoryMode;if(i!==void 0&&i!=="local"&&i!=="ai")throw new Error("Invalid memoryMode");if(n.skillMemory!==void 0&&typeof n.skillMemory!="boolean")throw new Error("Invalid skillMemory");if(n.contextOffload!==void 0&&typeof n.contextOffload!="boolean")throw new Error("Invalid contextOffload");let s={agentId:S(n.agentId,"agentId",2,100),...n.tools!==void 0?{tools:Ne(n.tools,"tools",64)}:{},...i?{memoryMode:i}:{},...n.skillMemory!==void 0?{skillMemory:n.skillMemory}:{},...n.contextOffload!==void 0?{contextOffload:n.contextOffload}:{},...o!==void 0?{maxContextChars:o}:{}};return{schema:"toolnet.api-hub-loadout.v1",loadout:await r.setLoadout(e,s)}}async function vr(r,e){return{schema:"toolnet.api-hub-observability.v1",observability:await r.observability(e)}}async function M(r,e,t){if(!await r.authorize(e,t))throw new f(`Principal '${e}' lacks '${t}'`,403)}function wr(r){if(!r||typeof r!="object"||Array.isArray(r))throw new f("Invalid Wiki request",400);return r}function De(r,e,t){if(typeof r!="string"||!r.trim()||r.length>t)throw new f(`Invalid ${e}`,400);return r.trim()}function kr(r,e,t){if(r!==void 0){if(typeof r!="string"||r.length>t)throw new f(`Invalid ${e}`,400);return r}}function br(r){if(r!==void 0){if(!Array.isArray(r)||r.length>50||r.some(e=>typeof e!="string"||e.length>100))throw new f("Invalid Wiki tags",400);return r}}async function Sr(r,e,t){return await M(e,t,"wiki:read"),{schema:"toolnet.api-wiki-summary.v1",wiki:await r.summary()}}async function Ar(r,e,t){return await M(e,t,"wiki:read"),{schema:"toolnet.api-wiki-pages.v1",pages:await r.listPages()}}async function Mr(r,e,t,n){await M(e,t,"wiki:write");let o=wr(n),i={...o.slug!==void 0?{slug:De(o.slug,"Wiki slug",120)}:{},title:De(o.title,"Wiki title",200),...o.summary!==void 0?{summary:kr(o.summary,"Wiki summary",1e3)}:{},content:typeof o.content=="string"?o.content:"",...o.tags!==void 0?{tags:br(o.tags)}:{}};return{schema:"toolnet.api-wiki-page.v1",page:await r.createPage(i)}}async function xr(r,e,t,n){return await M(e,t,"wiki:read"),{schema:"toolnet.api-wiki-page.v1",page:await r.getPage(n)}}async function Pr(r,e,t,n,o){await M(e,t,"wiki:write");let i=wr(o),s={...i.title!==void 0?{title:De(i.title,"Wiki title",200)}:{},...i.summary!==void 0?{summary:kr(i.summary,"Wiki summary",1e3)}:{},...i.content!==void 0?{content:typeof i.content=="string"?i.content:(()=>{throw new f("Invalid Wiki content",400)})()}:{},...i.tags!==void 0?{tags:br(i.tags)}:{}};return{schema:"toolnet.api-wiki-page.v1",page:await r.updatePage(n,s)}}async function Ir(r,e,t,n,o){return await M(e,t,"wiki:read"),{schema:"toolnet.api-wiki-search.v1",query:n,results:await r.search(n,o)}}async function Er(r,e,t,n){return await M(e,t,"wiki:read"),{schema:"toolnet.api-wiki-history.v1",revisions:await r.history(n)}}async function Rr(r,e,t,n){return await M(e,t,"wiki:read"),{schema:"toolnet.api-wiki-backlinks.v1",pages:await r.backlinks(n)}}async function I(r,e,t){if(!await r.authorize(e,t))throw new f(`Principal '${e}' lacks '${t}'`,403)}function Cr(r){if(!r||typeof r!="object"||Array.isArray(r))throw new f("Invalid governance request",400);return r}async function Tr(r,e,t){return await I(e,t,"governance:read"),{schema:"toolnet.api-governance-summary.v1",governance:await r.summary()}}async function Or(r,e,t,n){if(await I(e,t,"governance:read"),n&&!new Set(["pending","approved","rejected","superseded"]).has(n))throw new f("Invalid review status",400);return{schema:"toolnet.api-governance-reviews.v1",reviews:await r.listReviews(n)}}async function _r(r,e,t,n,o,i){await I(t,n,"governance:write");let s=Cr(i),a=s.action;if(a!=="approve"&&a!=="reject"&&a!=="supersede"&&a!=="merge")throw new f("Invalid governance action",400);if(s.note!==void 0&&typeof s.note!="string")throw new f("Invalid review note",400);if(s.targetReviewId!==void 0&&typeof s.targetReviewId!="string")throw new f("Invalid targetReviewId",400);return{schema:"toolnet.api-governance-review.v1",review:await r.decide(o,{action:a,principal:n,...typeof s.note=="string"?{note:s.note}:{},...typeof s.targetReviewId=="string"?{targetReviewId:s.targetReviewId}:{}},e)}}async function jr(r,e,t,n){return await I(t,n,"governance:read"),{schema:"toolnet.api-knowledge-quality.v1",quality:await r.quality(e)}}async function Kr(r,e,t){return await I(e,t,"governance:read"),{schema:"toolnet.api-governance-policy.v1",policy:await r.policy()}}async function Lr(r,e,t,n){await I(e,t,"governance:write");let o=Cr(n),i={};if(o.autoApproveThreshold!==void 0){if(typeof o.autoApproveThreshold!="number")throw new f("Invalid autoApproveThreshold",400);i.autoApproveThreshold=o.autoApproveThreshold}if(o.criticalApproveThreshold!==void 0){if(typeof o.criticalApproveThreshold!="number")throw new f("Invalid criticalApproveThreshold",400);i.criticalApproveThreshold=o.criticalApproveThreshold}if(o.staleAfterDays!==void 0){if(typeof o.staleAfterDays!="number")throw new f("Invalid staleAfterDays",400);i.staleAfterDays=o.staleAfterDays}return{schema:"toolnet.api-governance-policy.v1",policy:await r.setPolicy(i,t)}}async function Nr(r,e,t,n){return await I(e,t,"governance:read"),{schema:"toolnet.api-governance-audit.v1",events:await r.auditLog(n)}}function wi(r){let e=r.headers["x-toolnet-principal"];return(Array.isArray(e)?e[0]:e)?.trim()||"anonymous"}function ki(r,e){if(!e)return!0;let t=r.headers.authorization;if(!t?.startsWith("Bearer "))return!1;let n=Buffer.from(t.slice(7)),o=Buffer.from(e);return n.length===o.length&&hi(n,o)}function m(r,e,t){r.statusCode=e,r.setHeader("content-type","application/json; charset=utf-8"),r.setHeader("cache-control","no-store"),r.end(JSON.stringify(t))}async function k(r){let e=[],t=0;for await(let n of r){let o=Buffer.isBuffer(n)?n:Buffer.from(n);if(t+=o.length,t>16384)throw new Error("Request body too large");e.push(o)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function bi(r){if(!r||typeof r!="object"||Array.isArray(r))return null;let e=r;return typeof e.question!="string"||e.question.length<2||e.question.length>4e3||e.mode!==void 0&&e.mode!=="ai"&&e.mode!=="local"?null:{question:e.question,...e.mode?{mode:e.mode}:{}}}function Si(r){if(!r||typeof r!="object"||Array.isArray(r))return null;let e=r;return typeof e.query!="string"||e.limit!==void 0&&(!Number.isInteger(e.limit)||e.limit<1||e.limit>20)?null:{query:e.query,...e.limit!==void 0?{limit:e.limit}:{}}}function Ai(r){if(!r||typeof r!="object"||Array.isArray(r))return null;let e=r;return typeof e.query!="string"||e.query.length<2||e.query.length>500||e.limit!==void 0&&(!Number.isInteger(e.limit)||e.limit<1||e.limit>10)?null:{query:e.query,...e.limit!==void 0?{limit:e.limit}:{}}}function Mi(r){if(!r||typeof r!="object"||Array.isArray(r))return null;let e=r;return typeof e.assetId!="string"||!/^[a-f0-9]{8,64}$/iu.test(e.assetId)||e.maxChars!==void 0&&(!Number.isInteger(e.maxChars)||e.maxChars<200||e.maxChars>2e4)?null:{assetId:e.assetId,...e.maxChars!==void 0?{maxChars:e.maxChars}:{}}}function Dr(r){return vi(async(e,t)=>{try{let n=new URL(e.url??"/",`http://${e.headers.host??"localhost"}`),o=wi(e),i=Date.now();if(t.once("finish",()=>{r.hub&&r.hub.recordRequest({method:e.method??"GET",path:n.pathname,principal:o,statusCode:t.statusCode,durationMs:Date.now()-i}).catch(()=>{})}),n.pathname.startsWith("/v1/")&&!ki(e,r.token)){m(t,401,{error:"Unauthorized"});return}if(e.method==="GET"&&n.pathname==="/v1/health"){m(t,200,At(r.project));return}if(e.method==="GET"&&n.pathname==="/v1/project"){m(t,200,Mt(r.project));return}if(e.method==="POST"&&n.pathname==="/v1/memory/ask"){let u=bi(await k(e));if(!u){m(t,400,{error:"Invalid memory ask request"});return}m(t,200,await Qt(r.project,u));return}if(e.method==="POST"&&n.pathname==="/v1/memory/search"){let u=Si(await k(e));if(!u){m(t,400,{error:"Invalid memory search request"});return}m(t,200,await ir(r.project,r.retrieval,u));return}if(e.method==="POST"&&n.pathname==="/v1/skills/search"){let u=Ai(await k(e));if(!u){m(t,400,{error:"Invalid skill search request"});return}m(t,200,await sr(r.project,u));return}if(e.method==="POST"&&n.pathname==="/v1/offload/read"){let u=Mi(await k(e));if(!u){m(t,400,{error:"Invalid context offload request"});return}m(t,200,await Xt(r.project,u));return}if(n.pathname.startsWith("/v1/hub")&&!r.hub){m(t,503,{error:"Memory Hub unavailable"});return}if(e.method==="GET"&&n.pathname==="/v1/hub"){m(t,200,await cr(r.hub,o));return}if(e.method==="GET"&&n.pathname==="/v1/hub/teams"){m(t,200,await ur(r.hub,o));return}if(e.method==="POST"&&n.pathname==="/v1/hub/teams"){m(t,201,await lr(r.hub,o,await k(e)));return}if(e.method==="GET"&&n.pathname==="/v1/hub/agents"){m(t,200,await dr(r.hub,o));return}if(e.method==="POST"&&n.pathname==="/v1/hub/agents"){m(t,201,await mr(r.hub,o,await k(e)));return}if(e.method==="GET"&&n.pathname==="/v1/hub/acl"){m(t,200,await pr(r.hub,o));return}if(e.method==="POST"&&n.pathname==="/v1/hub/acl/grant"){m(t,200,await fr(r.hub,o,await k(e)));return}if(e.method==="POST"&&n.pathname==="/v1/hub/acl/revoke"){m(t,200,await gr(r.hub,o,await k(e)));return}if(e.method==="GET"&&n.pathname==="/v1/hub/loadouts"){m(t,200,await yr(r.hub,o));return}if(e.method==="POST"&&n.pathname==="/v1/hub/loadouts"){m(t,200,await hr(r.hub,o,await k(e)));return}if(e.method==="GET"&&n.pathname==="/v1/hub/observability"){m(t,200,await vr(r.hub,o));return}if(n.pathname.startsWith("/v1/wiki")&&(!r.wiki||!r.hub)){m(t,503,{error:"Wiki unavailable"});return}if(e.method==="GET"&&n.pathname==="/v1/wiki"){m(t,200,await Sr(r.wiki,r.hub,o));return}if(e.method==="GET"&&n.pathname==="/v1/wiki/pages"){m(t,200,await Ar(r.wiki,r.hub,o));return}if(e.method==="POST"&&n.pathname==="/v1/wiki/pages"){m(t,201,await Mr(r.wiki,r.hub,o,await k(e)));return}if(e.method==="GET"&&n.pathname==="/v1/wiki/search"){let u=n.searchParams.get("q")??"",p=Number(n.searchParams.get("limit")??10),h=Number.isInteger(p)?Math.max(1,Math.min(20,p)):10;m(t,200,await Ir(r.wiki,r.hub,o,u,h));return}let s=/^\/v1\/wiki\/pages\/([^/]+)\/history$/u.exec(n.pathname);if(e.method==="GET"&&s){m(t,200,await Er(r.wiki,r.hub,o,decodeURIComponent(s[1])));return}let a=/^\/v1\/wiki\/pages\/([^/]+)\/backlinks$/u.exec(n.pathname);if(e.method==="GET"&&a){m(t,200,await Rr(r.wiki,r.hub,o,decodeURIComponent(a[1])));return}let c=/^\/v1\/wiki\/pages\/([^/]+)$/u.exec(n.pathname);if(e.method==="GET"&&c){m(t,200,await xr(r.wiki,r.hub,o,decodeURIComponent(c[1])));return}if(e.method==="PUT"&&c){m(t,200,await Pr(r.wiki,r.hub,o,decodeURIComponent(c[1]),await k(e)));return}if(n.pathname.startsWith("/v1/governance")&&(!r.governance||!r.wiki||!r.hub)){m(t,503,{error:"Knowledge Governance unavailable"});return}if(e.method==="GET"&&n.pathname==="/v1/governance"){m(t,200,await Tr(r.governance,r.hub,o));return}if(e.method==="GET"&&n.pathname==="/v1/governance/reviews"){m(t,200,await Or(r.governance,r.hub,o,n.searchParams.get("status")??void 0));return}let d=/^\/v1\/governance\/reviews\/([^/]+)$/u.exec(n.pathname);if(e.method==="POST"&&d){m(t,200,await _r(r.governance,r.wiki,r.hub,o,decodeURIComponent(d[1]),await k(e)));return}if(e.method==="GET"&&n.pathname==="/v1/governance/quality"){m(t,200,await jr(r.governance,r.wiki,r.hub,o));return}if(e.method==="GET"&&n.pathname==="/v1/governance/policy"){m(t,200,await Kr(r.governance,r.hub,o));return}if(e.method==="PUT"&&n.pathname==="/v1/governance/policy"){m(t,200,await Lr(r.governance,r.hub,o,await k(e)));return}if(e.method==="GET"&&n.pathname==="/v1/governance/audit"){let u=Number(n.searchParams.get("limit")??100),p=Number.isInteger(u)?Math.max(1,Math.min(500,u)):100;m(t,200,await Nr(r.governance,r.hub,o,p));return}m(t,404,{error:"Not found"})}catch(n){if(n instanceof w||n instanceof f){m(t,n.statusCode,{error:n.message});return}if(n instanceof Error&&n.message.startsWith("Invalid ")){m(t,400,{error:n.message});return}m(t,500,{error:n instanceof Error?n.message:String(n)})}})}var $r=process.env.TOOLNET_API_HOST??"127.0.0.1",Fr=Number(process.env.TOOLNET_API_PORT??9750);async function xi(){let r=$e(),e=new Q().detect(),t=it({provider:r.storage.provider,r2:r.storage.r2,s3:r.storage.s3,huggingface:r.storage.huggingface,localRoot:r.storage.localRoot}),n=at(t,{attempts:Number(process.env.TOOLNET_STORAGE_RETRIES??3)}),o=new le(n,e.id,e.name,e.remote??e.name),i=new de(o,e,process.env.TOOLNET_HUB_OWNER?.trim()||"owner"),s=new me(i,Number(process.env.TOOLNET_HUB_MAX_EVENTS??100));await s.initialize();let a=new L(o,e),c=new $(a);await c.initialize();let d=new G(new H(o,e));await d.initialize();let u=new re,p=new ue(o);u.importRecords(await p.load(e.id));let h=new ae(u);Dr({project:e,retrieval:h,token:process.env.TOOLNET_API_TOKEN?.trim()||void 0,hub:s,wiki:c,governance:d}).listen(Fr,$r,()=>{console.log("ToolNet Memory API"),console.log(`Project: ${e.name}`),console.log(`Listening: http://${$r}:${Fr}`)})}xi().catch(r=>{console.error(r),process.exit(1)});