toolnet-memory 0.3.4 → 0.3.5

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.
package/bundle/context.js CHANGED
@@ -1,8 +1,8 @@
1
- import{existsSync as ve,readFileSync as we}from"node:fs";import{homedir as be}from"node:os";import{join as xe}from"node:path";function Pe(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 Ae(){let r=process.env.TOOLNET_GLOBAL_ENV??xe(be(),".config","toolnet-memory",".env");if(!ve(r))return;let e=we(r,"utf8");for(let t of e.split(/\r?\n/)){let s=t.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let o=s.indexOf("=");if(o<=0)continue;let n=s.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=Pe(s.slice(o+1)))}}Ae();function b(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function x(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function q(){return{memory:{autoCapture:b(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:b(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:b(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:b(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:x(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:x(process.env.MEMORY_RERANK_TOP,10),finalContext:x(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:x(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:b(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:x(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Te}from"node:crypto";import{existsSync as B,mkdirSync as Re,readFileSync as Ee,renameSync as Ie,writeFileSync as Oe}from"node:fs";import{basename as Ce,dirname as I,join as C,parse as ee,resolve as P}from"node:path";var te=".toolnet",Me="project.json";function Ne(r){return Te("sha256").update(r).digest("hex").slice(0,16)}function J(r){return C(r,te,Me)}function $e(r){return B(J(r))}function _e(r,e){let t=P(r),s=ee(t).root;for(;;){if($e(t))return t;if(t===s||e&&t===P(e))break;let o=I(t);if(o===t)break;t=o}return null}function Ke(r){let e=P(r),t=ee(e).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(n=>B(C(e,n))))return e;if(e===t)break;let o=I(e);if(o===e)break;e=o}return P(r)}function We(r){let e;try{e=JSON.parse(Ee(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 s=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:I(I(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:s,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:s,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 Z(r,e){let t=C(r,te);Re(t,{recursive:!0});let s=J(r),o=`${s}.tmp-${process.pid}`;Oe(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),Ie(o,s)}function X(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 O=class{detect(e=process.cwd()){let t=P(e),s=Ke(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>B(C(s,f))),p=_e(t,n?s:void 0);if(p){let f=J(p),a=We(f);return a.rootPath!==p&&(a.rootPath=p,a.updatedAt=new Date().toISOString(),Z(p,a)),X(a,p)}let l=new Date().toISOString(),c=Ce(s),d={version:1,id:Ne(s),name:c,remote:c,rootPath:s,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return Z(s,d),X(d,s)}};import{homedir as at}from"node:os";import{join as ct}from"node:path";import{DeleteObjectCommand as De,GetObjectCommand as Le,HeadObjectCommand as Ve,ListObjectsV2Command as Be,PutObjectCommand as Je,S3Client as ze}from"@aws-sdk/client-s3";import{getSignedUrl as Fe}from"@aws-sdk/s3-request-presigner";var M=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new ze({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,s="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Je({Bucket:this.bucket,Key:e,Body:o,ContentType:s}))}async get(e){let t=await Fe(this.client,new Le({Bucket:this.bucket,Key:e}),{expiresIn:60}),s=await fetch(t,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.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 De({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],s;do{let o=await this.client.send(new Be({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:s}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});s=o.IsTruncated?o.NextContinuationToken:void 0}while(s);return t}};import{access as re,mkdir as Ue,readFile as He,readdir as Ye,rm as Ge,stat as se,writeFile as Qe}from"node:fs/promises";import{dirname as qe,join as Ze,relative as oe,resolve as Xe}from"node:path";var A=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Xe(this.root,t)}async put(e,t){let s=this.path(e);await Ue(qe(s),{recursive:!0}),await Qe(s,t)}async get(e){try{return await He(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 re(this.path(e)),!0}catch{return!1}}async delete(e){await Ge(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),s=[];try{await re(t)}catch{return s}let o=async p=>{let l=await Ye(p,{withFileTypes:!0});for(let c of l){let d=Ze(p,c.name);if(c.isDirectory()){await o(d);continue}let f=await se(d);s.push({key:oe(this.root,d),size:f.size,updatedAt:f.mtime.toISOString()})}},n=await se(t);return n.isDirectory()?await o(t):s.push({key:oe(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),s}};import{DeleteObjectCommand as et,GetObjectCommand as tt,HeadObjectCommand as rt,ListObjectsV2Command as st,PutObjectCommand as ot,S3Client as nt}from"@aws-sdk/client-s3";import{getSignedUrl as it}from"@aws-sdk/s3-request-presigner";var T=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new nt({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,s="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ot({Bucket:this.bucket,Key:e,Body:o,ContentType:s}))}async get(e){let t=await it(this.client,new tt({Bucket:this.bucket,Key:e}),{expiresIn:60}),s=await fetch(t,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.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 rt({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 et({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],s;do{let o=await this.client.send(new st({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:s}));for(let n of o.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});s=o.IsTruncated?o.NextContinuationToken:void 0}while(s);return t}};function z(r,e){return console.warn(e),new A(r)}function ne(r){let e=r.localRoot??ct(at(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new T({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):z(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 T({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):z(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 M({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):z(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new A(e)}function pt(r){return new Promise(e=>setTimeout(e,r))}async function ie(r,e={}){let t=Math.max(1,e.attempts??3),s=e.baseDelayMs??150,o=e.maxDelayMs??2e3,n;for(let p=1;p<=t;p++)try{return await r()}catch(l){if(n=l,p>=t)break;let c=Math.min(o,s*2**(p-1)),d=Math.floor(Math.random()*Math.max(1,c*.2));await pt(c+d)}throw n}var ut=new Set(["put","get","getText","delete","list"]);function ae(r,e={}){return new Proxy(r,{get(t,s){let o=Reflect.get(t,s,t);return typeof o!="function"?o:ut.has(s)?(...n)=>ie(()=>Promise.resolve(o.apply(t,n)),e):o.bind(t)}})}function ce(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 pe(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 s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(s)return`${s[1]}memory/records/${s[2]}`;if(s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),s)return`${s[1]}memory/vectors/${s[2]}`;if(s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),s)return`${s[1]}code/graph/${s[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var N=class{constructor(e,t,s,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=s,this.folder=ce(o??s),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(),s=t,o=await this.provider.getText(e);if(o){let p;try{p=JSON.parse(o)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${l instanceof Error?l.message:String(l)}`)}if(typeof p.id=="string"&&p.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${p.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof p.createdAt=="string"&&(s=p.createdAt)}let n={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:s,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=pe(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,s){return await this.ensureRegistered(),this.provider.put(this.key(e),t,s)}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 ht}from"node:path";import{createHash as lt}from"node:crypto";import{dirname as dt}from"node:path";import{mkdirSync as mt,readFileSync as Is,renameSync as ft,writeFileSync as gt}from"node:fs";function y(r){return lt("sha256").update(r).digest("hex")}function v(r,e){mt(dt(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;gt(t,JSON.stringify(e,null,2)+`
5
- `,{encoding:"utf8",mode:384}),ft(t,r)}function ue(r,e){let t=r.trim(),s=t.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),o=y(t).slice(0,12);if(!s||s==="."||s==="..")return`${e}--${o}`;let n=s.slice(0,100);return n===t&&t.length<=100?n:`${n.slice(0,85)}--${o}`}function le(r,e,t){let s=e.trim(),o=t.trim();if(!s)throw new Error("Session agent is required");if(!o)throw new Error("Native session ID is required");let n=ue(s.toLowerCase(),"agent"),p=ue(o,"session");return{projectId:r.id,projectName:r.name,projectRoot:r.rootPath,agent:s,nativeSessionId:o,sessionKey:`${s}:${o}`,remotePrefix:["projects",r.id,"sessions",n,p].join("/"),localDirectory:ht(r.rootPath,".toolnet","sessions",n,p)}}import{existsSync as fe,mkdirSync as yt,readFileSync as St,statSync as de,writeFileSync as jt}from"node:fs";import{dirname as kt,join as vt}from"node:path";var me=64*1024,wt=`# ToolNet Project Operating Manual
1
+ import{existsSync as Ne,readFileSync as $e}from"node:fs";import{homedir as _e}from"node:os";import{join as We}from"node:path";function Ke(r){let t=r.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function Fe(){let r=process.env.TOOLNET_GLOBAL_ENV??We(_e(),".config","toolnet-memory",".env");if(!Ne(r))return;let t=$e(r,"utf8");for(let e of t.split(/\r?\n/)){let s=e.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let n=s.indexOf("=");if(n<=0)continue;let i=s.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=Ke(s.slice(n+1)))}}Fe();function P(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function A(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function ie(){return{memory:{autoCapture:P(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:P(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:P(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:P(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:A(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:A(process.env.MEMORY_RERANK_TOP,10),finalContext:A(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:A(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:P(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:A(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as De}from"node:crypto";import{existsSync as U,mkdirSync as Le,readFileSync as Ve,renameSync as Be,writeFileSync as ze}from"node:fs";import{basename as He,dirname as N,join as _,parse as ue,resolve as I}from"node:path";var le=".toolnet",Je="project.json";function Ue(r){return De("sha256").update(r).digest("hex").slice(0,16)}function q(r){return _(r,le,Je)}function qe(r){return U(q(r))}function Ge(r,t){let e=I(r),s=ue(e).root;for(;;){if(qe(e))return e;if(e===s||t&&e===I(t))break;let n=N(e);if(n===e)break;e=n}return null}function Ye(r){let t=I(r),e=ue(t).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(i=>U(_(t,i))))return t;if(t===e)break;let n=N(t);if(n===t)break;t=n}return I(r)}function Qe(r){let t;try{t=JSON.parse(Ve(r,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${r}: ${n instanceof Error?n.message:String(n)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let s=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:N(N(r)),createdAt:typeof e.createdAt=="string"?e.createdAt:s,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:s,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function ae(r,t){let e=_(r,le);Le(e,{recursive:!0});let s=q(r),n=`${s}.tmp-${process.pid}`;ze(n,JSON.stringify(t,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Be(n,s)}function ce(r,t){return{id:r.id,name:r.name,remote:r.remote,rootPath:t,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var $=class{detect(t=process.cwd()){let e=I(t),s=Ye(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(g=>U(_(s,g))),u=Ge(e,i?s:void 0);if(u){let g=q(u),a=Qe(g);return a.rootPath!==u&&(a.rootPath=u,a.updatedAt=new Date().toISOString(),ae(u,a)),ce(a,u)}let d=new Date().toISOString(),c=He(s),p={version:1,id:Ue(s),name:c,remote:c,rootPath:s,createdAt:d,updatedAt:d,graphVersion:0,memoryVersion:0};return ae(s,p),ce(p,s)}};import{homedir as vt}from"node:os";import{join as wt}from"node:path";import{DeleteObjectCommand as Ze,GetObjectCommand as Xe,HeadObjectCommand as et,ListObjectsV2Command as tt,PutObjectCommand as rt,S3Client as st}from"@aws-sdk/client-s3";import{getSignedUrl as nt}from"@aws-sdk/s3-request-presigner";var W=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new st({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let n=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new rt({Bucket:this.bucket,Key:t,Body:n,ContentType:s}))}async get(t){let e=await nt(this.client,new Xe({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new et({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Ze({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let n=await this.client.send(new tt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let i of n.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});s=n.IsTruncated?n.NextContinuationToken:void 0}while(s);return e}};import{access as de,mkdir as ot,readFile as it,readdir as at,rm as ct,stat as pe,writeFile as ut}from"node:fs/promises";import{dirname as lt,join as dt,relative as me,resolve as pt}from"node:path";var M=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return pt(this.root,e)}async put(t,e){let s=this.path(t);await ot(lt(s),{recursive:!0}),await ut(s,e)}async get(t){try{return await it(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await de(this.path(t)),!0}catch{return!1}}async delete(t){await ct(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),s=[];try{await de(e)}catch{return s}let n=async u=>{let d=await at(u,{withFileTypes:!0});for(let c of d){let p=dt(u,c.name);if(c.isDirectory()){await n(p);continue}let g=await pe(p);s.push({key:me(this.root,p),size:g.size,updatedAt:g.mtime.toISOString()})}},i=await pe(e);return i.isDirectory()?await n(e):s.push({key:me(this.root,e),size:i.size,updatedAt:i.mtime.toISOString()}),s}};import{DeleteObjectCommand as mt,GetObjectCommand as ft,HeadObjectCommand as gt,ListObjectsV2Command as ht,PutObjectCommand as yt,S3Client as kt}from"@aws-sdk/client-s3";import{getSignedUrl as St}from"@aws-sdk/s3-request-presigner";var T=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new kt({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,s="application/octet-stream"){let n=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new yt({Bucket:this.bucket,Key:t,Body:n,ContentType:s}))}async get(t){let e=await St(this.client,new ft({Bucket:this.bucket,Key:t}),{expiresIn:60}),s=await fetch(e,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new gt({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new mt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],s;do{let n=await this.client.send(new ht({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:s}));for(let i of n.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});s=n.IsTruncated?n.NextContinuationToken:void 0}while(s);return e}};function G(r,t){return console.warn(t),new M(r)}function fe(r){let t=r.localRoot??wt(vt(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new T({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):G(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let e=r.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new T({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):G(t,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let e=r.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new W({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):G(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new M(t)}function bt(r){return new Promise(t=>setTimeout(t,r))}async function ge(r,t={}){let e=Math.max(1,t.attempts??3),s=t.baseDelayMs??150,n=t.maxDelayMs??2e3,i;for(let u=1;u<=e;u++)try{return await r()}catch(d){if(i=d,u>=e)break;let c=Math.min(n,s*2**(u-1)),p=Math.floor(Math.random()*Math.max(1,c*.2));await bt(c+p)}throw i}var jt=new Set(["put","get","getText","delete","list"]);function he(r,t={}){return new Proxy(r,{get(e,s){let n=Reflect.get(e,s,e);return typeof n!="function"?n:jt.has(s)?(...i)=>ge(()=>Promise.resolve(n.apply(e,i)),t):n.bind(e)}})}function ye(r){let t=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!t||t==="."||t==="..")throw new Error("Invalid project storage folder");return t}function ke(r){let t=r.replace(/^\/+/,"");if(t.startsWith("memories/"))return"memory/records/"+t.slice(9);if(t.startsWith("vectors/"))return"memory/vectors/"+t.slice(8);if(t.startsWith("graph/"))return"code/graph/"+t.slice(6);let e=t.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(e)return`${e[1]}memory/records/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),e)return`${e[1]}memory/vectors/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),e)return`${e[1]}code/graph/${e[2]}`;let s=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(s)return`${s[1]}memory/records/${s[2]}`;if(s=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),s)return`${s[1]}memory/vectors/${s[2]}`;if(s=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),s)return`${s[1]}code/graph/${s[2]}`;let n=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:t))}var K=class{constructor(t,e,s,n){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=s,this.folder=ye(n??s),this.sourcePrefix=`projects/${e}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let t=`${this.targetPrefix}/project.json`,e=new Date().toISOString(),s=e,n=await this.provider.getText(t);if(n){let u;try{u=JSON.parse(n)}catch(d){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${d instanceof Error?d.message:String(d)}`)}if(typeof u.id=="string"&&u.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${u.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof u.createdAt=="string"&&(s=u.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:s,updatedAt:e};await this.provider.put(t,JSON.stringify(i,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=ke(t),t===this.sourcePrefix)return this.targetPrefix;if(t.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+t.slice(this.sourcePrefix.length);if(t===this.targetPrefix||t.startsWith(`${this.targetPrefix}/`))return t;if(t.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${t}`].join(" "));return t}async put(t,e,s){return await this.ensureRegistered(),this.provider.put(this.key(t),e,s)}async get(t){return await this.ensureRegistered(),this.provider.get(this.key(t))}async getText(t){return await this.ensureRegistered(),this.provider.getText(this.key(t))}async delete(t){return await this.ensureRegistered(),this.provider.delete(this.key(t))}async exists(t){return await this.ensureRegistered(),this.provider.exists(this.key(t))}async list(t){return await this.ensureRegistered(),this.provider.list(this.key(t))}};import{join as Tt}from"node:path";import{createHash as xt}from"node:crypto";import{dirname as Pt}from"node:path";import{mkdirSync as At,readFileSync as sn,renameSync as It,writeFileSync as Mt}from"node:fs";function S(r){return xt("sha256").update(r).digest("hex")}function x(r,t){At(Pt(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;Mt(e,JSON.stringify(t,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),It(e,r)}function Se(r,t){let e=r.trim(),s=e.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),n=S(e).slice(0,12);if(!s||s==="."||s==="..")return`${t}--${n}`;let i=s.slice(0,100);return i===e&&e.length<=100?i:`${i.slice(0,85)}--${n}`}function ve(r,t,e){let s=t.trim(),n=e.trim();if(!s)throw new Error("Session agent is required");if(!n)throw new Error("Native session ID is required");let i=Se(s.toLowerCase(),"agent"),u=Se(n,"session");return{projectId:r.id,projectName:r.name,projectRoot:r.rootPath,agent:s,nativeSessionId:n,sessionKey:`${s}:${n}`,remotePrefix:["projects",r.id,"sessions",i,u].join("/"),localDirectory:Tt(r.rootPath,".toolnet","sessions",i,u)}}import{existsSync as je,mkdirSync as Ct,readFileSync as Rt,statSync as we,writeFileSync as Ot}from"node:fs";import{dirname as Et,join as Nt}from"node:path";var be=64*1024,$t=`# ToolNet Project Operating Manual
6
6
 
7
7
  This file contains persistent instructions for AI agents working on this project.
8
8
 
@@ -45,18 +45,19 @@ Things an AI agent should always remember.
45
45
  <!--
46
46
  - [advisory] Prefer small focused files.
47
47
  -->
48
- `;function $(r){return vt(r.rootPath,".toolnet","PROJECT.md")}function bt(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function xt(r){let e=[],t=new Set,s=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,o;for(;o=s.exec(r);){let n=o[1].toLowerCase(),p=bt(o[2]);if(!p)continue;let l=`${n}:${p.toLowerCase()}`;t.has(l)||(t.add(l),e.push({id:y(l).slice(0,24),mode:n,text:p,source:"manual"}))}return e}function Pt(r){let e=$(r);return fe(e)||(yt(kt(e),{recursive:!0}),jt(e,wt,{encoding:"utf8",mode:384})),e}function _(r,e=!1){let t=e?Pt(r):$(r);if(!fe(t))return null;if(de(t).size>me)throw new Error(`PROJECT.md exceeds ${me} bytes`);let o=St(t,"utf8");return{path:t,content:o,digest:y(o),rules:xt(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(de(t).mtimeMs).toISOString()}}import{mkdirSync as It}from"node:fs";import{join as F}from"node:path";import{join as ge}from"node:path";import{mkdirSync as At}from"node:fs";function Tt(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function w(r,e=20){let t=[],s=new Set;for(let o of r.slice().reverse()){let n=Tt(o);if(!(!n||s.has(n))&&(s.add(n),t.push(o),t.length>=e))break}return t.reverse()}function Rt(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 he(r,e){let t=e.status??r?.status??"pending",s=t;r&&(r.status==="completed"&&t!=="completed"?s="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(s=r.status));let o=r&&Rt(e)?r.title:e.text;return{id:r?.id??y(e.key).slice(0,24),title:o,status:s,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 Et(r,e){let t=`projects/${r.id}/work/observations/`,s=await e.list(t),o=[];for(let n of s.filter(p=>p.key.endsWith(".json")).sort((p,l)=>p.key.localeCompare(l.key))){let p=await e.getText(n.key);if(p)try{let l=JSON.parse(p);l.version===1&&Array.isArray(l.observations)&&o.push(l)}catch{}}return o}async function ye(r,e){let s=(await Et(r,e)).flatMap(i=>i.observations).sort((i,k)=>{let G=i.occurredAt.localeCompare(k.occurredAt);if(G!==0)return G;let Q=i.sequence-k.sequence;return Q!==0?Q:i.id.localeCompare(k.id)}),o=new Map,n=new Map,p,l,c,d=[],f=[],a=[],g=[],u=[],h=[];for(let i of s)switch(i.kind){case"goal":p=i.text;break;case"plan":l=i.text;break;case"phase":o.set(i.key,he(o.get(i.key),i));break;case"task":n.set(i.key,he(n.get(i.key),i));break;case"decision":d.push(i.text);break;case"blocker":f.push(i.text);break;case"warning":a.push(i.text);break;case"next_action":g.push(i.text);break;case"file":u.push(i.text);break;case"test":h.push(i.text);break;case"session":c={agent:i.agent,nativeSessionId:i.nativeSessionId,sessionKey:i.sessionKey,updatedAt:i.occurredAt};break}let j=Array.from(o.values()).sort((i,k)=>(i.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),S=Array.from(n.values()).sort((i,k)=>(i.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),L=j.find(i=>i.status==="in_progress")??j.find(i=>i.status==="blocked")??j.find(i=>i.status==="pending"),E=S.find(i=>i.status==="in_progress")??S.find(i=>i.status==="blocked")??S.find(i=>i.status==="pending"),je=w([...g,...E?[E.title]:[],...!E&&L?[L.title]:[],...S.filter(i=>i.status==="pending").slice(0,5).map(i=>i.title)],8),ke=w([...f,...j.filter(i=>i.status==="blocked").map(i=>i.title),...S.filter(i=>i.status==="blocked").map(i=>i.title)],20),V={version:1,projectId:r.id,projectName:r.name,goal:p,plan:l,phases:j,tasks:S,decisions:w(d,20),blockers:ke,warnings:w(a,20),nextActions:je,filesTouched:w(u,30),tests:w(h,20),currentPhase:L,currentTask:E,progress:{phasesTotal:j.length,phasesCompleted:j.filter(i=>i.status==="completed").length,tasksTotal:S.length,tasksCompleted:S.filter(i=>i.status==="completed").length,blocked:j.filter(i=>i.status==="blocked").length+S.filter(i=>i.status==="blocked").length},lastSession:c,updatedAt:s.length?s[s.length-1].occurredAt:new Date().toISOString()},Y=ge(r.rootPath,".toolnet","work");return At(Y,{recursive:!0}),v(ge(Y,"current.json"),V),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(V,null,2)+`
49
- `,"application/json"),V}async function K(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 Ot(r){return{goal:r.goal,plan:r.plan,phases:r.phases.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),tasks:r.tasks.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),decisions:r.decisions,blockers:r.blockers,warnings:r.warnings,nextActions:r.nextActions,filesTouched:r.filesTouched,tests:r.tests}}function Ct(r){return r.phases.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||r.tasks.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||r.nextActions.length>0||r.blockers.length>0}var W=class{constructor(e){this.options=e}options;async capture(e,t){let s=await K(this.options.project,this.options.storage);if(s||(s=await ye(this.options.project,this.options.storage)),!Ct(s))return null;let o=_(this.options.project,!1),n=o?o.rules.filter(g=>g.mode==="enforce").map(g=>g.text):[],p=y(JSON.stringify(Ot(s))),l=y([this.options.project.id,this.options.identity.sessionKey,p].join("|")).slice(0,24),c=new Date().toISOString(),d={version:1,id:l,projectId:this.options.project.id,projectName:this.options.project.name,createdAt:c,reason:e,sourceSession:{agent:this.options.identity.agent,nativeSessionId:this.options.identity.nativeSessionId,sessionKey:this.options.identity.sessionKey,sequence:t},goal:s.goal,plan:s.plan,progress:s.progress,currentPhase:s.currentPhase,currentTask:s.currentTask,incompletePhases:s.phases.filter(g=>g.status!=="completed"&&g.status!=="cancelled"),incompleteTasks:s.tasks.filter(g=>g.status!=="completed"&&g.status!=="cancelled"),nextActions:s.nextActions.slice(0,10),blockers:s.blockers.slice(0,10),decisions:s.decisions.slice(-10),warnings:s.warnings.slice(-10),attention:[...n,...s.warnings].slice(0,20),filesTouched:s.filesTouched.slice(-20),tests:s.tests.slice(-15),stateDigest:p},f=`projects/${this.options.project.id}/work/handoffs/${l}.json`;await this.options.storage.exists(f)||await this.options.storage.put(f,JSON.stringify(d,null,2)+`
50
- `,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(d,null,2)+`
51
- `,"application/json");let a=F(this.options.project.rootPath,".toolnet","work","handoffs");return It(a,{recursive:!0}),v(F(a,`${l}.json`),d),v(F(this.options.project.rootPath,".toolnet","work","handoff-latest.json"),d),d}};async function D(r,e){let t=await e.getText(`projects/${r.id}/work/handoff-latest.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}async function Se(r,e){let t=await e.getText(`projects/${r.id}/work/semantic/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function U(r){if(!r)return 0;let e=Array.from(r).length,t=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,t*1.3))}function m(r,e){let t=r.replace(/\s+/g," ").trim();return t.length<=e?t:t.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function Mt(r){let e=[],t=!1;for(let s of r.split(/\r?\n/u)){let o=s.trim();if(o.includes("<!--")&&(t=!0),t){o.includes("-->")&&(t=!1);continue}let n=o.toLowerCase();if(!(!o||o.startsWith("#")||o==="```"||n.startsWith("- [enforce]")||n.startsWith("* [enforce]")||n.startsWith("- [advisory]")||n.startsWith("* [advisory]"))&&(o=o.replace(/^[-*]\s+/u,""),o&&e.push(m(o,280)),e.length>=16))break}return e}function Nt(r){let e=[],t=[];for(let s of r.split(/\\r?\\n/u)){let o=s.trim(),n=o.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(d=>n.startsWith(d));if(!l)continue;let c=o.slice(l.length).trim();c&&(l.includes("enforce")?e.push(c):t.push(c))}return{enforce:e,advisory:t}}function $t(r,e){let t=[];for(let s of r){let o=[...t,s].join(`
52
- `);if(U(o)<=e){t.push(s);continue}let n=U(t.join(`
53
- `)),p=Math.max(0,e-n);if(p>=16){let l=Math.floor(p*3.2),c=m(s,l);c&&t.push(c)}break}return t.join(`
54
- `).trim()}async function H(r){let e=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),t=_(r.project,!1),s=t?.content??"";s||(s=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let o=Nt(s),n=t?t.rules.filter(u=>u.mode==="enforce").map(u=>u.text):o.enforce,p=t?t.rules.filter(u=>u.mode==="advisory").map(u=>u.text):o.advisory,l=s?Mt(s):[],c=await K(r.project,r.storage),d=await Se(r.project,r.storage),f=await D(r.project,r.storage),a=[];if(a.push("[TOOLNET PROJECT CONTEXT]"),a.push(`Project: ${r.project.name}`),a.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),s&&a.push(`Full operating manual: ${$(r.project)}`),n.length){a.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of n.slice(0,24))a.push(`- [ENFORCE] ${m(u,240)}`)}if(p.length){a.push("","PROJECT PREFERENCES");for(let u of p.slice(0,10))a.push(`- ${m(u,220)}`)}if(d&&(d.mission&&a.push("","MISSION",m(d.mission.value,420)),d.activeObjective&&a.push("","CURRENT OBJECTIVE",m(d.activeObjective.value,420)),d.why&&a.push("","WHY THIS WORK MATTERS",m(d.why.value,420)),d.desiredOutcome&&a.push("","DESIRED OUTCOME",m(d.desiredOutcome.value,420)),d.planRationale&&a.push("","WHY THIS APPROACH",m(d.planRationale.value,420))),c){if(a.push("","ACTIVE WORK"),c.goal&&a.push(`Goal: ${m(c.goal,320)}`),c.plan&&a.push(`Plan: ${m(c.plan,320)}`),a.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&a.push(`Current phase: ${c.currentPhase.title} [${c.currentPhase.status}]`),c.currentPhase&&d){let u=d.phases.find(h=>h.order===c.currentPhase?.order);u&&(u.objective&&a.push(`Phase objective: ${m(u.objective.value,340)}`),u.why?a.push(`Why this phase: ${m(u.why.value,340)}`):a.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&a.push(`Deliverable: ${m(u.deliverable.value,340)}`),u.dependencies.length&&a.push(`Depends on: ${u.dependencies.slice(0,4).map(h=>m(h.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(a.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(h=>{a.push(`- ${m(h.value,260)}`)})),u.openQuestions.length&&(a.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(h=>{a.push(`- ${m(h.value,260)}`)})))}c.currentTask&&a.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(a.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((u,h)=>{a.push(`${h+1}. ${m(u,260)}`)})),c.blockers.length&&(a.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(u=>{a.push(`- ${m(u,260)}`)})),c.warnings.length&&(a.push("","ATTENTION"),c.warnings.slice(-5).forEach(u=>{a.push(`- ${m(u,260)}`)})),c.decisions.length&&(a.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(u=>{a.push(`- ${m(u,260)}`)})),c.lastSession&&a.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(d&&d.openQuestions.length&&(a.push("","UNRESOLVED QUESTIONS"),d.openQuestions.slice(0,5).forEach(u=>{a.push(`- ${m(u.value,260)}`)})),f&&a.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),l.length){a.push("","OPERATING NOTES");for(let u of l)a.push(`- ${u}`)}a.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let g=$t(a,e);return{version:1,projectId:r.project.id,projectName:r.project.name,text:g,estimatedTokens:U(g),maxTokens:e,hasManual:!!s,hasWorkState:!!c,hasHandoff:!!f,generatedAt:new Date().toISOString()}}function R(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function _t(r){let e=q(),t=ae(ne({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new N(t,r.id,r.name,r.remote??r.name)}async function Kt(){let[r="help",...e]=process.argv.slice(2),t=new O().detect(R(e,"--project")??process.cwd()),s=_t(t);if(r==="brief"){let o=Number(R(e,"--tokens")??1e3),n=await H({project:t,storage:s,maxTokens:Number.isFinite(o)?o:1e3});process.stdout.write(n.text+`
55
- `);return}if(r==="brief-json"){let o=await H({project:t,storage:s,maxTokens:Number(R(e,"--tokens")??1e3)});console.log(JSON.stringify(o,null,2));return}if(r==="handoff-latest"){let o=await D(t,s);console.log(JSON.stringify(o,null,2));return}if(r==="handoff-create"){let o=R(e,"--agent")??"manual";if(o==="manual")throw new Error("--agent must be opencode, agy, or codex");let n=R(e,"--session");if(!n)throw new Error("--session is required");let p=le(t,o,n),c=await new W({project:t,storage:s,identity:p}).capture("manual",0);console.log(JSON.stringify(c,null,2));return}console.log(`Work Context
48
+ `;function F(r){return Nt(r.rootPath,".toolnet","PROJECT.md")}function _t(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function Wt(r){let t=[],e=new Set,s=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,n;for(;n=s.exec(r);){let i=n[1].toLowerCase(),u=_t(n[2]);if(!u)continue;let d=`${i}:${u.toLowerCase()}`;e.has(d)||(e.add(d),t.push({id:S(d).slice(0,24),mode:i,text:u,source:"manual"}))}return t}function Kt(r){let t=F(r);return je(t)||(Ct(Et(t),{recursive:!0}),Ot(t,$t,{encoding:"utf8",mode:384})),t}function D(r,t=!1){let e=t?Kt(r):F(r);if(!je(e))return null;if(we(e).size>be)throw new Error(`PROJECT.md exceeds ${be} bytes`);let n=Rt(e,"utf8");return{path:e,content:n,digest:S(n),rules:Wt(n),bytes:Buffer.byteLength(n,"utf8"),updatedAt:new Date(we(e).mtimeMs).toISOString()}}import{existsSync as Gt,mkdirSync as Yt}from"node:fs";import{join as Y}from"node:path";import{join as xe}from"node:path";import{mkdirSync as Ft}from"node:fs";function Ae(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function v(r,t=20){let e=[],s=new Set;for(let n of r.slice().reverse()){let i=Ae(n);if(!(!i||s.has(i))&&(s.add(i),e.push(n),e.length>=t))break}return e.reverse()}function Dt(r,t=20){let e=new Map;for(let s of r){let n=`${s.kind}|${Ae(s.command)}`;e.delete(n),e.set(n,s)}return Array.from(e.values()).slice(-t)}function Lt(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 Pe(r,t){let e=t.status??r?.status??"pending",s=e;r&&(r.status==="completed"&&e!=="completed"?s="completed":e==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(s=r.status));let n=r&&Lt(t)?r.title:t.text;return{id:r?.id??S(t.key).slice(0,24),title:n,status:s,order:t.order??r?.order,confidence:Math.max(t.confidence,r?.confidence??0),updatedAt:t.occurredAt,updatedBy:{agent:t.agent,nativeSessionId:t.nativeSessionId,eventId:t.eventId}}}async function Vt(r,t){let e=`projects/${r.id}/work/observations/`,s=await t.list(e),n=[];for(let i of s.filter(u=>u.key.endsWith(".json")).sort((u,d)=>u.key.localeCompare(d.key))){let u=await t.getText(i.key);if(u)try{let d=JSON.parse(u);d.version===1&&Array.isArray(d.observations)&&n.push(d)}catch{}}return n}async function Ie(r,t){let s=(await Vt(r,t)).flatMap(o=>o.observations).sort((o,k)=>{let ne=o.occurredAt.localeCompare(k.occurredAt);if(ne!==0)return ne;let oe=o.sequence-k.sequence;return oe!==0?oe:o.id.localeCompare(k.id)}),n=new Map,i=new Map,u,d,c,p,g,a=[],j=[],l=[],h=[],z=[],R=new Map,O=[],m=[],X=[],ee=[],te=[],re=[];for(let o of s)switch(o.kind){case"request":u=o.text;break;case"activity":d=o.text;break;case"goal":c=o.text;break;case"plan":p=o.text;break;case"phase":n.set(o.key,Pe(n.get(o.key),o));break;case"task":i.set(o.key,Pe(i.get(o.key),o));break;case"decision":a.push(o.text);break;case"blocker":j.push(o.text);break;case"warning":l.push(o.text);break;case"next_action":h.push(o.text);break;case"file":{z.push(o.text);let k=o.fileAction??"active";R.delete(o.text),R.set(o.text,k),k==="modified"?O.push(o.text):k==="created"?m.push(o.text):k==="deleted"&&X.push(o.text);break}case"command":ee.push(o.text);break;case"test":te.push(o.text),o.checkKind&&re.push({kind:o.checkKind,command:o.text,status:o.checkStatus??"unknown",updatedAt:o.occurredAt,agent:o.agent,nativeSessionId:o.nativeSessionId});break;case"session":g={agent:o.agent,nativeSessionId:o.nativeSessionId,sessionKey:o.sessionKey,updatedAt:o.occurredAt};break}let b=Array.from(n.values()).sort((o,k)=>(o.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),w=Array.from(i.values()).sort((o,k)=>(o.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),H=b.find(o=>o.status==="in_progress")??b.find(o=>o.status==="blocked")??b.find(o=>o.status==="pending"),E=w.find(o=>o.status==="in_progress")??w.find(o=>o.status==="blocked")??w.find(o=>o.status==="pending"),Oe=v([...h,...E?[E.title]:[],...!E&&H?[H.title]:[],...w.filter(o=>o.status==="pending").slice(0,5).map(o=>o.title)],8),Ee=v([...j,...b.filter(o=>o.status==="blocked").map(o=>o.title),...w.filter(o=>o.status==="blocked").map(o=>o.title)],20),J={version:1,projectId:r.id,projectName:r.name,currentRequest:u,currentActivity:d,goal:c,plan:p,phases:b,tasks:w,decisions:v(a,20),blockers:Ee,warnings:v(l,20),nextActions:Oe,filesTouched:v(z,30),activeFiles:Array.from(R.entries()).filter(([,o])=>o!=="deleted").map(([o])=>o).slice(-5),modifiedFiles:v(O,30),createdFiles:v(m,30),deletedFiles:v(X,30),commands:v(ee,20),tests:v(te,20),checks:Dt(re,20),currentPhase:H,currentTask:E,progress:{phasesTotal:b.length,phasesCompleted:b.filter(o=>o.status==="completed").length,tasksTotal:w.length,tasksCompleted:w.filter(o=>o.status==="completed").length,blocked:b.filter(o=>o.status==="blocked").length+w.filter(o=>o.status==="blocked").length},lastSession:g,updatedAt:s.length?s[s.length-1].occurredAt:new Date().toISOString()},se=xe(r.rootPath,".toolnet","work");return Ft(se,{recursive:!0}),x(xe(se,"current.json"),J),await t.put(`projects/${r.id}/work/current.json`,JSON.stringify(J,null,2)+`
49
+ `,"application/json"),J}async function L(r,t){let e=await t.getText(`projects/${r.id}/work/current.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}import{existsSync as Bt,mkdirSync as kn,readFileSync as zt,renameSync as Sn,writeFileSync as vn}from"node:fs";import{dirname as bn,join as Ht}from"node:path";function Jt(r){return Ht(r.rootPath,".toolnet","work","current.json")}function Me(r){let t=Jt(r);if(!Bt(t))return null;try{let e=JSON.parse(zt(t,"utf8"));return e.version!==1||e.projectId!==r.id?null:e}catch{return null}}function y(r,t){let e=new Set,s=[];for(let n of r){let i=n.replace(/\s+/g," ").trim();if(!i)continue;let u=i.normalize("NFKC").toLowerCase();if(!e.has(u)&&(e.add(u),s.push(i),s.length>=t))break}return s}function Te(r){if(r)return{id:r.id,title:r.title,status:r.status}}function Ut(r,t=[]){let e=t.slice(-10);if(e.some(n=>n.status==="failed"))return"failing";if(e.some(n=>n.status==="passed"))return"passing";let s=r.slice(-10).join(`
50
+ `).toLowerCase();return/(?:failed|failing|failure|error|✗|❌)/u.test(s)?"failing":/(?:passed|passing|green|success|✓|✅)/u.test(s)?"passing":"unknown"}function qt(r){return S(JSON.stringify(r))}function Ce(r){let{project:t,identity:e,state:s}=r,n=s.activeFiles?.at(-1)??s.filesTouched.at(-1),i=s.phases.filter(m=>m.status==="completed").map(m=>m.title),u=s.tasks.filter(m=>m.status==="completed").map(m=>m.title),d=s.phases.filter(m=>m.status!=="completed"&&m.status!=="cancelled").map(m=>m.title),c=s.tasks.filter(m=>m.status!=="completed"&&m.status!=="cancelled").map(m=>m.title),p=new Set(s.tasks.filter(m=>m.status==="completed").map(m=>m.title.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim())),g=y(s.nextActions.filter(m=>!p.has(m.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim())),10),a=y([...c,...g],15),j=y(s.tests.slice().reverse(),10),l=y([...(s.activeFiles??[]).slice().reverse(),...s.filesTouched.slice().reverse()],20),h={schema:"toolnet.handoff.v2",version:2,project:{id:t.id,name:t.name},source:{agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,sequence:r.sequence,reason:r.reason},capturedAt:r.capturedAt??new Date().toISOString(),goal:s.goal,request:s.currentRequest,activity:s.currentActivity,current:{phase:Te(s.currentPhase),task:Te(s.currentTask),file:n},completed:{phases:y(i,20),tasks:y(u,30)},remaining:{phases:y(d,20),tasks:y(c,30),todos:a},nextAction:g[0],blockers:y(s.blockers.slice().reverse(),10),decisions:y(s.decisions.slice().reverse(),10),files:{current:n,recent:l,active:y(s.activeFiles??[],10),modified:y(s.modifiedFiles??[],20),created:y(s.createdFiles??[],20),deleted:y(s.deletedFiles??[],20)},tests:{status:Ut(s.tests,s.checks),recent:j,checks:(s.checks??[]).slice(-10).map(m=>({kind:m.kind,status:m.status,command:m.command}))},attention:y(r.attention??[],20),progress:s.progress},{capturedAt:z,source:R,...O}=h;return{...h,stateDigest:qt(O)}}function Qt(r){return!!(r.currentRequest||r.currentActivity||r.goal||r.plan||r.phases.length>0||r.tasks.length>0||r.nextActions.length>0||r.blockers.length>0||r.decisions.length>0||r.filesTouched.length>0)}function Zt(r,t,e,s,n){if(!Qt(e))return null;let i=D(r,!1),d=[...i?i.rules.filter(a=>a.mode==="enforce").map(a=>a.text):[],...e.warnings].slice(0,20),c=Ce({project:r,identity:t,state:e,reason:s,sequence:n,attention:d}),p=c.stateDigest;return{version:1,id:S([r.id,t.sessionKey,p].join("|")).slice(0,24),projectId:r.id,projectName:r.name,createdAt:new Date().toISOString(),reason:s,sourceSession:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,sequence:n},currentRequest:e.currentRequest,currentActivity:e.currentActivity,goal:e.goal,plan:e.plan,progress:e.progress,currentPhase:e.currentPhase,currentTask:e.currentTask,incompletePhases:e.phases.filter(a=>a.status!=="completed"&&a.status!=="cancelled"),incompleteTasks:e.tasks.filter(a=>a.status!=="completed"&&a.status!=="cancelled"),nextActions:c.remaining.todos.slice(0,10),blockers:e.blockers.slice(-10),decisions:e.decisions.slice(-10),warnings:e.warnings.slice(-10),attention:d,filesTouched:e.filesTouched.slice(-20),activeFiles:e.activeFiles?.slice(-10),modifiedFiles:e.modifiedFiles?.slice(-20),createdFiles:e.createdFiles?.slice(-20),deletedFiles:e.deletedFiles?.slice(-20),tests:e.tests.slice(-15),checks:e.checks?.slice(-10),stateDigest:p,continuity:c}}function Xt(r,t){let e=Y(r.rootPath,".toolnet","work","handoffs");Yt(e,{recursive:!0});let s=Y(e,`${t.id}.json`);Gt(s)||x(s,t),x(Y(r.rootPath,".toolnet","work","handoff-latest.json"),t)}var V=class{constructor(t){this.options=t}options;async capture(t,e){let s=Me(this.options.project);s||(s=await L(this.options.project,this.options.storage)),s||(s=await Ie(this.options.project,this.options.storage));let n=Zt(this.options.project,this.options.identity,s,t,e);if(!n)return null;Xt(this.options.project,n);let i=`projects/${this.options.project.id}/work/handoffs/${n.id}.json`;return await this.options.storage.exists(i)||await this.options.storage.put(i,JSON.stringify(n,null,2)+`
51
+ `,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(n,null,2)+`
52
+ `,"application/json"),n}};async function B(r,t){let e=await t.getText(`projects/${r.id}/work/handoff-latest.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}async function Re(r,t){let e=await t.getText(`projects/${r.id}/work/semantic/current.json`);if(!e)return null;try{return JSON.parse(e)}catch{return null}}function Q(r){if(!r)return 0;let t=Array.from(r).length,e=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(t/3.5,e*1.3))}function f(r,t){let e=r.replace(/\s+/g," ").trim();return e.length<=t?e:e.slice(0,Math.max(0,t-1)).trimEnd()+"\u2026"}function er(r){let t=[],e=!1;for(let s of r.split(/\r?\n/u)){let n=s.trim();if(n.includes("<!--")&&(e=!0),e){n.includes("-->")&&(e=!1);continue}let i=n.toLowerCase();if(!(!n||n.startsWith("#")||n==="```"||i.startsWith("- [enforce]")||i.startsWith("* [enforce]")||i.startsWith("- [advisory]")||i.startsWith("* [advisory]"))&&(n=n.replace(/^[-*]\s+/u,""),n&&t.push(f(n,280)),t.length>=16))break}return t}function tr(r){let t=[],e=[];for(let s of r.split(/\\r?\\n/u)){let n=s.trim(),i=n.toLowerCase(),d=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(p=>i.startsWith(p));if(!d)continue;let c=n.slice(d.length).trim();c&&(d.includes("enforce")?t.push(c):e.push(c))}return{enforce:t,advisory:e}}function rr(r,t){let e=[];for(let s of r){let n=[...e,s].join(`
53
+ `);if(Q(n)<=t){e.push(s);continue}let i=Q(e.join(`
54
+ `)),u=Math.max(0,t-i);if(u>=16){let d=Math.floor(u*3.2),c=f(s,d);c&&e.push(c)}break}return e.join(`
55
+ `).trim()}async function Z(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=D(r.project,!1),s=e?.content??"";s||(s=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let n=tr(s),i=e?e.rules.filter(l=>l.mode==="enforce").map(l=>l.text):n.enforce,u=e?e.rules.filter(l=>l.mode==="advisory").map(l=>l.text):n.advisory,d=s?er(s):[],c=await L(r.project,r.storage),p=await Re(r.project,r.storage),g=await B(r.project,r.storage),a=[];if(a.push("[TOOLNET PROJECT CONTEXT]"),a.push(`Project: ${r.project.name}`),a.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),s&&a.push(`Full operating manual: ${F(r.project)}`),i.length){a.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let l of i.slice(0,24))a.push(`- [ENFORCE] ${f(l,240)}`)}if(u.length){a.push("","PROJECT PREFERENCES");for(let l of u.slice(0,10))a.push(`- ${f(l,220)}`)}if(p&&(p.mission&&a.push("","MISSION",f(p.mission.value,420)),p.activeObjective&&a.push("","CURRENT OBJECTIVE",f(p.activeObjective.value,420)),p.why&&a.push("","WHY THIS WORK MATTERS",f(p.why.value,420)),p.desiredOutcome&&a.push("","DESIRED OUTCOME",f(p.desiredOutcome.value,420)),p.planRationale&&a.push("","WHY THIS APPROACH",f(p.planRationale.value,420))),c){if(a.push("","ACTIVE WORK"),c.goal&&a.push(`Goal: ${f(c.goal,320)}`),c.plan&&a.push(`Plan: ${f(c.plan,320)}`),a.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&a.push(`Current phase: ${c.currentPhase.title} [${c.currentPhase.status}]`),c.currentPhase&&p){let l=p.phases.find(h=>h.order===c.currentPhase?.order);l&&(l.objective&&a.push(`Phase objective: ${f(l.objective.value,340)}`),l.why?a.push(`Why this phase: ${f(l.why.value,340)}`):a.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),l.deliverable&&a.push(`Deliverable: ${f(l.deliverable.value,340)}`),l.dependencies.length&&a.push(`Depends on: ${l.dependencies.slice(0,4).map(h=>f(h.value,180)).join("; ")}`),l.acceptanceCriteria.length&&(a.push("","DEFINITION OF DONE"),l.acceptanceCriteria.slice(0,6).forEach(h=>{a.push(`- ${f(h.value,260)}`)})),l.openQuestions.length&&(a.push("","OPEN QUESTIONS FOR CURRENT PHASE"),l.openQuestions.slice(0,4).forEach(h=>{a.push(`- ${f(h.value,260)}`)})))}c.currentTask&&a.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(a.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((l,h)=>{a.push(`${h+1}. ${f(l,260)}`)})),c.blockers.length&&(a.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(l=>{a.push(`- ${f(l,260)}`)})),c.warnings.length&&(a.push("","ATTENTION"),c.warnings.slice(-5).forEach(l=>{a.push(`- ${f(l,260)}`)})),c.decisions.length&&(a.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(l=>{a.push(`- ${f(l,260)}`)})),c.lastSession&&a.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(p&&p.openQuestions.length&&(a.push("","UNRESOLVED QUESTIONS"),p.openQuestions.slice(0,5).forEach(l=>{a.push(`- ${f(l.value,260)}`)})),g&&a.push(`Latest handoff: ${g.reason} / ${g.sourceSession.agent}`),d.length){a.push("","OPERATING NOTES");for(let l of d)a.push(`- ${l}`)}a.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let j=rr(a,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:j,estimatedTokens:Q(j),maxTokens:t,hasManual:!!s,hasWorkState:!!c,hasHandoff:!!g,generatedAt:new Date().toISOString()}}function C(r,t){let e=r.indexOf(t);return e>=0?r[e+1]:void 0}function sr(r){let t=ie(),e=he(fe({provider:t.storage.provider,huggingface:t.storage.huggingface,localRoot:t.storage.localRoot}),{attempts:3});return new K(e,r.id,r.name,r.remote??r.name)}async function nr(){let[r="help",...t]=process.argv.slice(2),e=new $().detect(C(t,"--project")??process.cwd()),s=sr(e);if(r==="brief"){let n=Number(C(t,"--tokens")??1e3),i=await Z({project:e,storage:s,maxTokens:Number.isFinite(n)?n:1e3});process.stdout.write(i.text+`
56
+ `);return}if(r==="brief-json"){let n=await Z({project:e,storage:s,maxTokens:Number(C(t,"--tokens")??1e3)});console.log(JSON.stringify(n,null,2));return}if(r==="handoff-latest"){let n=await B(e,s);console.log(JSON.stringify(n,null,2));return}if(r==="handoff-create"){let n=C(t,"--agent")??"manual";if(n==="manual")throw new Error("--agent must be opencode, agy, or codex");let i=C(t,"--session");if(!i)throw new Error("--session is required");let u=ve(e,n,i),c=await new V({project:e,storage:s,identity:u}).capture("manual",0);console.log(JSON.stringify(c,null,2));return}console.log(`Work Context
56
57
 
57
58
  Commands:
58
59
  brief [--project PATH] [--tokens N]
59
60
  brief-json [--project PATH] [--tokens N]
60
61
  handoff-latest [--project PATH]
61
62
  handoff-create --agent <opencode|agy|codex> --session <id>
62
- `)}Kt().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
63
+ `)}nr().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
@@ -0,0 +1,50 @@
1
+ import{mkdirSync as G,mkdtempSync as Tt,rmSync as Pt,writeFileSync as he}from"node:fs";import{tmpdir as Ot}from"node:os";import{join as k}from"node:path";import{z as pe}from"zod";import{existsSync as qe,readFileSync as ze}from"node:fs";import{join as We}from"node:path";import{existsSync as Ae,mkdirSync as $t,readFileSync as Me,renameSync as Ut,writeFileSync as jt}from"node:fs";import{dirname as qt,join as xe}from"node:path";function we(n){return xe(n.rootPath,".toolnet","work","current.json")}function B(n){let e=we(n);if(!Ae(e))return null;try{let t=JSON.parse(Me(e,"utf8"));return t.version!==1||t.projectId!==n.id?null:t}catch{return null}}import{existsSync as Se,mkdirSync as Ht,readFileSync as Ee,renameSync as Gt,writeFileSync as Yt}from"node:fs";import{dirname as Qt,join as Ce}from"node:path";function _e(n){return Ce(n.rootPath,".toolnet","context","session-origin.json")}function q(n){let e=_e(n);if(!Se(e))return null;try{let t=JSON.parse(Ee(e,"utf8"));return t.version!==1||t.projectId!==n.id?null:t}catch{return null}}function Ie(n){return n.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function C(n){let e=Ie(n),t=/(?:agent trước|previous agent|ai trước|phiên trước)/u.test(e),r=/(?:đ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&&r?"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"}import{existsSync as Ke,mkdirSync as Bn,readFileSync as $e}from"node:fs";import{join as V}from"node:path";var Pe=[{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}],_=class{scan(e){let t=[];for(let r of Pe){let o=new RegExp(r.regex.source,r.regex.flags);for(let i of e.matchAll(o))t.push({type:r.type,value:i[0]})}return t}hasSecrets(e){return this.scan(e).length>0}};var I=class{scanner=new _;sanitize(e){let t=e,r=this.scanner.scan(e),o=new Set;for(let i of r)o.add(i.type),t=t.split(i.value).join(`[REDACTED:${i.type}]`);return{text:t,redacted:r.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[r,o]of Object.entries(e)){let i=r.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");i.includes("password")||i.includes("passwd")||i==="pwd"||i.includes("secret")||i.includes("token")||i.includes("cookie")||i.includes("authorization")||i.includes("apikey")||i.includes("accesskey")||i.includes("privatekey")||i.includes("clientsecret")||i.includes("credential")?t[r]="[REDACTED]":t[r]=this.sanitizeValue(o)}return t}return e}};import{existsSync as Oe,readFileSync as Re}from"node:fs";import{homedir as Fe}from"node:os";import{join as Ne}from"node:path";function Le(n){let e=n.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 De(){let n=process.env.TOOLNET_GLOBAL_ENV??Ne(Fe(),".config","toolnet-memory",".env");if(!Oe(n))return;let e=Re(n,"utf8");for(let t of e.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let i=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=Le(r.slice(o+1)))}}De();var Tn=new I;function Ue(n){return V(n.rootPath,".toolnet","memory","checkpoints")}function je(n){return V(Ue(n),"latest.json")}function Be(n){let e=je(n);if(!Ke(e))return null;try{let t=JSON.parse($e(e,"utf8"));return t.schema!=="toolnet.memory.checkpoint.v1"||t.project.id!==n.id?null:t}catch{return null}}function Q(n){return Be(n)}var He={previous_agent:{previous_agent:100,task:35,phase:25},current_task:{task:100,request:95,activity:90,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,rule:85,architecture:80,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,request:98,activity:96,next_action:95,file:90,blocker:85,previous_agent:80,phase:75,completed:60,todo:60,decision:55,rule:54,architecture:53,fix:52,progress:50,test:45,goal:35}},Ge={checkpoint:20,handoff:18,"session-origin":14,"work-state":8};function Ye(n){return n.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function c(n,e){let t=e.value.replace(/\s+/g," ").trim();t&&n.push({...e,value:t,score:0})}function Ve(n){let e=We(n.rootPath,".toolnet","work","handoff-latest.json");if(!qe(e))return null;try{let r=JSON.parse(ze(e,"utf8")).continuity;return r?.schema!=="toolnet.handoff.v2"||r.project.id!==n.id?null:r}catch{return null}}function Qe(n){let e=Q(n);if(!e)return[];let t=[];e.request&&c(t,{kind:"request",value:e.request,source:"checkpoint"}),e.activity&&c(t,{kind:"activity",value:e.activity,source:"checkpoint"}),e.current.phase&&c(t,{kind:"phase",value:e.current.phase.title,source:"checkpoint"}),e.current.task&&c(t,{kind:"task",value:e.current.task.title,source:"checkpoint"});let r=e.files.active.at(-1);r&&c(t,{kind:"file",value:r,source:"checkpoint"});for(let o of e.nextActions)c(t,{kind:"next_action",value:o,source:"checkpoint"});for(let o of e.blockers)c(t,{kind:"blocker",value:o,source:"checkpoint"});for(let o of e.completed.tasks)c(t,{kind:"completed",value:o,source:"checkpoint"});for(let o of e.remaining.tasks)c(t,{kind:"todo",value:o,source:"checkpoint"});for(let o of e.durableFacts)c(t,{kind:o.kind,value:o.content,source:"checkpoint"});return t}function Je(n){let e=Ve(n);if(!e)return[];let t=[];c(t,{kind:"previous_agent",value:e.source.agent,source:"handoff"}),e.request&&c(t,{kind:"request",value:e.request,source:"handoff"}),e.activity&&c(t,{kind:"activity",value:e.activity,source:"handoff"}),e.goal&&c(t,{kind:"goal",value:e.goal,source:"handoff"}),e.current.phase&&c(t,{kind:"phase",value:e.current.phase.title,source:"handoff"}),e.current.task&&c(t,{kind:"task",value:e.current.task.title,source:"handoff"}),e.files.current&&c(t,{kind:"file",value:e.files.current,source:"handoff"}),e.nextAction&&c(t,{kind:"next_action",value:e.nextAction,source:"handoff"});for(let r of e.blockers)c(t,{kind:"blocker",value:r,source:"handoff"});for(let r of e.decisions)c(t,{kind:"decision",value:r,source:"handoff"});for(let r of e.completed.tasks)c(t,{kind:"completed",value:r,source:"handoff"});for(let r of e.remaining.todos)c(t,{kind:"todo",value:r,source:"handoff"});return c(t,{kind:"test",value:`Test status: ${e.tests.status}`,source:"handoff"}),c(t,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"handoff"}),t}function Xe(n){let e=q(n);if(!e)return[];let t=[];return c(t,{kind:"previous_agent",value:e.agent,source:"session-origin"}),e.currentPhase&&c(t,{kind:"phase",value:e.currentPhase,source:"session-origin"}),e.currentTask&&c(t,{kind:"task",value:e.currentTask,source:"session-origin"}),e.lastTouchedFile&&c(t,{kind:"file",value:e.lastTouchedFile,source:"session-origin"}),e.latestNextAction&&c(t,{kind:"next_action",value:e.latestNextAction,source:"session-origin"}),e.latestBlocker&&c(t,{kind:"blocker",value:e.latestBlocker,source:"session-origin"}),e.latestDecision&&c(t,{kind:"decision",value:e.latestDecision,source:"session-origin"}),t}function Ze(n){let e=B(n);if(!e)return[];let t=[];e.currentRequest&&c(t,{kind:"request",value:e.currentRequest,source:"work-state"}),e.currentActivity&&c(t,{kind:"activity",value:e.currentActivity,source:"work-state"}),e.goal&&c(t,{kind:"goal",value:e.goal,source:"work-state"}),e.currentPhase&&c(t,{kind:"phase",value:e.currentPhase.title,source:"work-state"}),e.currentTask&&c(t,{kind:"task",value:e.currentTask.title,source:"work-state"});let r=e.activeFiles?.at(-1)??e.filesTouched.at(-1);r&&c(t,{kind:"file",value:r,source:"work-state"});for(let o of e.nextActions.slice(0,5))c(t,{kind:"next_action",value:o,source:"work-state"});for(let o of e.blockers.slice(-5))c(t,{kind:"blocker",value:o,source:"work-state"});for(let o of e.decisions.slice(-5))c(t,{kind:"decision",value:o,source:"work-state"});for(let o of e.tasks.filter(i=>i.status==="completed").slice(-8))c(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))c(t,{kind:"todo",value:o.title,source:"work-state"});for(let o of e.tests.slice(-5))c(t,{kind:"test",value:o,source:"work-state"});return c(t,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"work-state"}),t}function et(n,e){let t=He[n];return e.map((r,o)=>({...r,score:(t[r.kind]??10)+Ge[r.source]+Math.max(0,5-o*.01)}))}function tt(n){let e=new Map;for(let t of n){let r=`${t.kind}:${Ye(t.value)}`,o=e.get(r);(!o||t.score>o.score)&&e.set(r,t)}return[...e.values()]}function y(n,e,t={}){let r=C(e),o=[...Qe(n),...Je(n),...Xe(n),...Ze(n)],i=tt(et(r,o)).sort((d,g)=>g.score-d.score),a=Math.max(1,t.maxFacts??12),p=Math.max(300,t.maxChars??3200),l=[],f=0;for(let d of i){if(l.length>=a)break;let g=d.kind.length+d.value.length+d.source.length+16;l.length>0&&f+g>p||(l.push(d),f+=g)}return{intent:r,facts:l,context:{project:{id:n.id,name:n.name},intent:r,selectedFacts:l.map(d=>({kind:d.kind,value:d.value,source:d.source}))},stats:{candidates:o.length,selected:l.length,chars:f}}}var J={previous_agent:"Agent tr\u01B0\u1EDBc",request:"Y\xEAu c\u1EA7u hi\u1EC7n t\u1EA1i",activity:"\u0110ang l\xE0m",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",rule:"Quy t\u1EAFc",architecture:"Ki\u1EBFn tr\xFAc",fix:"\u0110\xE3 s\u1EEDa",completed:"\u0110\xE3 ho\xE0n th\xE0nh",test:"Test",progress:"Ti\u1EBFn \u0111\u1ED9"},X=["previous_agent","request","activity","phase","task","file","todo","next_action","blocker","decision","rule","architecture","fix","completed","test","progress","goal"],nt={previous_agent:["previous_agent"],current_task:["request","activity","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:X};function rt(n){let e=n.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 ot(n){let e=n.filter(r=>r.source==="handoff");if(e.length===0)return n;let t=n.filter(r=>r.source==="checkpoint"&&["rule","architecture","fix"].includes(r.kind));return[...e,...t]}function st(n){let e=new Set,t=[];for(let r of n){let o=`${r.kind}:${r.value.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}`;e.has(o)||(e.add(o),t.push(r))}return t}function Z(n){switch(n){case"todo":case"blocker":case"decision":case"completed":return 3;default:return 1}}function it(n,e){let t=[];for(let r of e){let o=n.filter(i=>i.kind===r).slice(0,Z(r));for(let i of o)t.push(`${J[r]}: ${i.value}`)}return t.join(`
3
+ `)}function at(n,e){let t=nt[e]??X,r=[];for(let o of t){let i=n.filter(a=>a.kind===o).slice(0,Z(o));for(let a of i)r.push(`${J[o]}: ${a.value}`)}return r.join(`
4
+ `)}function M(n,e){let t=y(n,e,{maxFacts:32,maxChars:6400}),r=st(ot(t.facts)),o=rt(e)?it(r,["todo","task","next_action","file"]):at(r,String(t.intent));return o?{intent:String(t.intent),answer:o,source:r[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 ee=[{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 h(n){let e=ee.find(t=>t.id===n);if(!e)throw new Error(`Unsupported AI provider: ${n}`);return e}function x(n){return ee.some(e=>e.id===n)}function s(n){return process.env[n]?.trim()||void 0}function u(...n){return n.find(e=>!!e?.trim())}function te(){if(s("GROQ_API_KEY"))return"groq";if(s("DEEPSEEK_API_KEY"))return"deepseek";if(s("NVIDIA_API_KEY")||s("NVIDIA_NIM_API_KEY"))return"nvidia";if(s("OPENROUTER_API_KEY"))return"openrouter";if(s("ALIBABA_API_KEY")||s("DASHSCOPE_API_KEY"))return"alibaba";if(s("GEMINI_API_KEY")||s("GOOGLE_API_KEY"))return"gemini";if(s("CLOUDFLARE_API_TOKEN")&&s("CLOUDFLARE_ACCOUNT_ID"))return"cloudflare";if(s("HF_TOKEN"))return"huggingface";if(s("OLLAMA_MODEL")||s("OLLAMA_BASE_URL"))return"ollama"}function ne(){let n=s("TOOLNET_LLM_PROVIDER");return n&&x(n)?n:te()??"openai-compatible"}function T(n){let e=h(n);switch(n){case"alibaba":return{provider:n,apiKey:u(s("ALIBABA_API_KEY"),s("DASHSCOPE_API_KEY")),baseUrl:u(s("ALIBABA_BASE_URL"),s("DASHSCOPE_BASE_URL"),e.defaultBaseUrl),model:u(s("ALIBABA_MODEL"),s("DASHSCOPE_MODEL"))};case"openrouter":return{provider:n,apiKey:s("OPENROUTER_API_KEY"),baseUrl:u(s("OPENROUTER_BASE_URL"),e.defaultBaseUrl),model:s("OPENROUTER_MODEL")};case"groq":return{provider:n,apiKey:s("GROQ_API_KEY"),baseUrl:u(s("GROQ_BASE_URL"),e.defaultBaseUrl),model:s("GROQ_MODEL")};case"deepseek":return{provider:n,apiKey:s("DEEPSEEK_API_KEY"),baseUrl:u(s("DEEPSEEK_BASE_URL"),e.defaultBaseUrl),model:u(s("DEEPSEEK_MODEL"),e.defaultModel)};case"nvidia":return{provider:n,apiKey:u(s("NVIDIA_API_KEY"),s("NVIDIA_NIM_API_KEY")),baseUrl:u(s("NVIDIA_BASE_URL"),s("NVIDIA_NIM_BASE_URL"),e.defaultBaseUrl),model:u(s("NVIDIA_MODEL"),s("NVIDIA_NIM_MODEL"),e.defaultModel)};case"gemini":return{provider:n,apiKey:u(s("GEMINI_API_KEY"),s("GOOGLE_API_KEY")),baseUrl:u(s("GEMINI_BASE_URL"),e.defaultBaseUrl),model:s("GEMINI_MODEL")};case"huggingface":return{provider:n,apiKey:s("HF_TOKEN"),baseUrl:u(s("HF_INFERENCE_BASE_URL"),e.defaultBaseUrl),model:u(s("HF_LLM_MODEL"),s("HF_MODEL"))};case"ollama":return{provider:n,apiKey:s("OLLAMA_API_KEY"),baseUrl:u(s("OLLAMA_BASE_URL"),e.defaultBaseUrl),model:s("OLLAMA_MODEL")};case"cloudflare":return{provider:n,accountId:s("CLOUDFLARE_ACCOUNT_ID"),apiKey:s("CLOUDFLARE_API_TOKEN"),baseUrl:s("CLOUDFLARE_AI_BASE_URL"),model:s("CLOUDFLARE_MODEL")};case"custom":return{provider:n,apiKey:s("CUSTOM_AI_API_KEY"),baseUrl:s("CUSTOM_AI_BASE_URL"),model:s("CUSTOM_AI_MODEL")};default:return{provider:"openai-compatible",apiKey:u(s("OPENAI_API_KEY"),s("MODEL_API_KEY")),baseUrl:u(s("OPENAI_BASE_URL"),s("MODEL_BASE_URL")),model:u(s("OPENAI_MODEL"),s("MODEL_NAME"))}}}function ct(){let n=s("TOOLNET_LLM_PROVIDER"),e=n&&x(n)?n:ne(),t=h(e),r=T(e);return{provider:e,apiKey:u(s("TOOLNET_LLM_API_KEY"),r.apiKey),baseUrl:u(s("TOOLNET_LLM_BASE_URL"),r.baseUrl,t.defaultBaseUrl),model:u(s("TOOLNET_LLM_MODEL"),r.model,t.defaultModel),accountId:u(s("TOOLNET_LLM_ACCOUNT_ID"),r.accountId)}}function lt(){let n=s("TOOLNET_EMBEDDING_PROVIDER");return n==="local"?"local":n&&x(n)?n:s("HF_TOKEN")||s("HF_EMBEDDING_MODEL")?"huggingface":"local"}function ut(){let n=lt();if(n==="local")return{provider:"local",model:u(s("TOOLNET_EMBEDDING_MODEL"),s("LOCAL_EMBEDDING_MODEL"))};let e=h(n),t,r,o,i;switch(n){case"huggingface":t=s("HF_TOKEN"),r=s("HF_INFERENCE_BASE_URL"),o=s("HF_EMBEDDING_MODEL");break;case"openai-compatible":t=s("OPENAI_API_KEY"),r=s("OPENAI_BASE_URL"),o=s("OPENAI_EMBEDDING_MODEL");break;case"cloudflare":t=s("CLOUDFLARE_API_TOKEN"),r=s("CLOUDFLARE_AI_BASE_URL"),o=s("CLOUDFLARE_EMBEDDING_MODEL"),i=s("CLOUDFLARE_ACCOUNT_ID");break;default:t=T(n).apiKey,r=T(n).baseUrl,o=s(`${n.toUpperCase().replace(/-/g,"_")}_EMBEDDING_MODEL`)}let a=u(s("TOOLNET_EMBEDDING_ACCOUNT_ID"),i),p=u(s("TOOLNET_EMBEDDING_BASE_URL"),r,e.defaultBaseUrl),l=n==="cloudflare"&&!p&&a?`https://api.cloudflare.com/client/v4/accounts/${a}/ai/v1`:p;return{provider:n,apiKey:u(s("TOOLNET_EMBEDDING_API_KEY"),t),baseUrl:l,model:u(s("TOOLNET_EMBEDDING_MODEL"),o),accountId:a}}function z(){let n=ct(),e=ut();return{llm:n,embedding:e,legacy:{llm:!s("TOOLNET_LLM_PROVIDER")&&!!te(),embedding:!s("TOOLNET_EMBEDDING_PROVIDER")&&!!(s("HF_TOKEN")||s("HF_EMBEDDING_MODEL"))}}}function re(n=ne()){let e=z().llm;if(n===e.provider)return{id:n,apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,accountId:e.accountId};let t=T(n);return{id:n,apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,accountId:t.accountId}}function w(n,e){return s(`TOOLNET_LLM_FALLBACK_${n}_${e}`)}function oe(){let n=[];for(let e of[1,2]){let t=w(e,"PROVIDER");if(!t||!x(t))continue;let r=h(t);n.push({slot:e,config:{provider:t,apiKey:w(e,"API_KEY"),baseUrl:u(w(e,"BASE_URL"),r.defaultBaseUrl),model:u(w(e,"MODEL"),r.defaultModel),accountId:w(e,"ACCOUNT_ID")}})}return n}function se(){let n=s("TOOLNET_LLM_FALLBACK_COOLDOWN_MS"),e=s("TOOLNET_LLM_MAX_RETRIES"),t=Number(n??6e4),r=Number(e??1);return{cooldownMs:Number.isFinite(t)&&t>=0?t:6e4,maxRetries:Number.isFinite(r)&&r>=0?Math.floor(r):1}}var S=class extends Error{status;constructor(e,t){super(e),this.name="AiHttpError",this.status=t}};async function v(n,e,t=3e4){let r=new AbortController,o=setTimeout(()=>r.abort(),t);o.unref?.();try{let i=await fetch(n,{...e,signal:r.signal}),a=await i.text();if(!i.ok){let p=a;try{let l=JSON.parse(a);p=l.error?.message??l.message??a}catch{}throw new S(p||`HTTP ${i.status}`,i.status)}return a.trim()?JSON.parse(a):{}}finally{clearTimeout(o)}}function P(n,e){return`${n.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`}var O=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(),r=this.config.apiKey?.trim();if(!t)throw new Error("cloudflare: ACCOUNT ID is not configured");if(!r)throw new Error("cloudflare: API TOKEN is not configured");let o=this.model(),a=`${(this.config.baseUrl?.trim()||`https://api.cloudflare.com/client/v4/accounts/${t}/ai/run`).replace(/\/+$/,"")}/${o}`,p=await v(a,{method:"POST",headers:{authorization:`Bearer ${r}`,"content-type":"application/json",...this.config.headers},body:JSON.stringify({messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens})}),l=p.result?.response?.trim();if(!l)throw new Error(p.errors?.[0]?.message??"cloudflare: empty model response");return{text:l,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 R=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 r=this.config.baseUrl?.trim()||"https://generativelanguage.googleapis.com/v1beta",o=this.model(),i=e.messages.filter(d=>d.role==="system"),a=e.messages.filter(d=>d.role!=="system").map(d=>({role:d.role==="assistant"?"model":"user",parts:[{text:d.content}]})),p=`${P(r,`models/${encodeURIComponent(o)}:generateContent`)}?key=${encodeURIComponent(t)}`,l=await v(p,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({...i.length?{systemInstruction:{parts:[{text:i.map(d=>d.content).join(`
5
+
6
+ `)}]}}:{},contents:a,generationConfig:{temperature:e.temperature,maxOutputTokens:e.maxTokens}})}),f=l.candidates?.[0]?.content?.parts?.map(d=>d.text??"").join("").trim();if(!f)throw new Error("gemini: empty model response");return{text:f,provider:"gemini",model:o,usage:l.usageMetadata?{inputTokens:l.usageMetadata.promptTokenCount,outputTokens:l.usageMetadata.candidatesTokenCount,totalTokens:l.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 F=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(),r=await v(P(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=r.choices?.[0]?.message?.content?.trim();if(!o)throw new Error(`${this.id}: empty model response`);return{text:o,provider:this.id,model:t,usage:r.usage?{inputTokens:r.usage.prompt_tokens,outputTokens:r.usage.completion_tokens,totalTokens:r.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 ie(n=re()){switch(h(n.id).transport){case"gemini":return new R(n);case"cloudflare":return new O(n);default:return new F(n)}}function dt(n){return new Promise(e=>{setTimeout(e,n)})}function mt(n){if(n instanceof S){let t=n.status;return t===void 0||t===408||t===409||t===425||t===429?!0:t>=500}if(!(n instanceof Error))return!0;let e=n.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 N=class{id;config;providers;states=new Map;cooldownMs;maxRetries;constructor(e,t=[],r={}){this.id=e.config.id,this.config=e.config,this.cooldownMs=r.cooldownMs??6e4,this.maxRetries=Math.max(0,r.maxRetries??1),this.providers=[e,...t].map(o=>({provider:ie(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),r=this.states.get(t);return r||(r={failures:0,cooldownUntil:0},this.states.set(t,r)),r}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 r=this.state(e);r.failures+=1,r.lastError=t instanceof Error?t.message:String(t),r.cooldownUntil=Date.now()+this.cooldownMs}async generate(e){let t=[];for(let r=0;r<this.providers.length;r+=1){if(!this.available(r)){t.push(`${this.providers[r].label}: cooldown`);continue}let o=this.providers[r];for(let i=0;i<=this.maxRetries;i+=1)try{let a=await o.provider.generate(e);return this.success(r),a}catch(a){let p=a instanceof Error?a.message:String(a),l=mt(a);if(t.push(`${o.label}: ${p}`),!l)throw a;if(i<this.maxRetries){await dt(Math.min(250*2**i,2e3));continue}this.fail(r,a)}}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 r=this.state(t);return{position:t,provider:e.provider.id,model:e.provider.config.model,cooldown:r.cooldownUntil>Date.now(),cooldownUntil:r.cooldownUntil||void 0,failures:r.failures,lastError:r.lastError}})}};function ae(){let n=z(),e=oe();return new N({label:`Primary (${n.llm.provider})`,config:{id:n.llm.provider,apiKey:n.llm.apiKey,baseUrl:n.llm.baseUrl,model:n.llm.model,accountId:n.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}})),se())}var W=class{constructor(e=ae()){this.provider=e}provider;get activeProvider(){return this.provider}setProvider(e){this.provider=e}generate(e){return this.provider.generate(e)}};function ce(){return new W(ae())}function pt(n,e){let t=JSON.stringify(n,null,2);return t.length<=e?t:t.slice(0,e)+`
7
+ [truncated]`}function ft(n,e){let t=M(n,e),r=y(n,e,{maxFacts:12,maxChars:3200});return pt({project:r.context.project,intent:r.intent,directAnswer:t.answer,selectedMemory:r.context.selectedFacts,retrievalStats:r.stats},4200)}async function le(n,e,t={}){let r=M(n,e),o=ft(n,e),i=t.router??ce();try{let a=await i.generate({temperature:.1,maxTokens:450,messages:[{role:"system",content:`
8
+ You are ToolNet Memory Agent.
9
+
10
+ You are NOT the coding agent.
11
+
12
+ Your job is to help another coding AI continue work from previous sessions.
13
+
14
+ Rules:
15
+
16
+ 1. Answer only from the supplied ToolNet project memory.
17
+ 2. Never invent completed work, files, blockers, decisions, or test results.
18
+ 3. Prefer the latest session/work state over old information.
19
+ 4. Be concise.
20
+ 5. Do not dump raw JSON or full memory files.
21
+ 6. Tell the coding agent exactly:
22
+ - what the previous agent was doing,
23
+ - what is already completed,
24
+ - what remains unfinished,
25
+ - what file was last touched,
26
+ - what should be done next,
27
+ when those facts are available.
28
+ 7. If information is missing, say it is not recorded.
29
+ 8. Do not explain ToolNet internals unless asked.
30
+ 9. Do not repeat irrelevant history.
31
+ 10. Respond in the same language as the question.
32
+
33
+ Think of yourself as the project's memory coworker talking directly to another AI.
34
+ `.trim()},{role:"user",content:`
35
+ Question from coding agent:
36
+
37
+ ${e}
38
+
39
+ ToolNet selected memory:
40
+
41
+ ${o}
42
+
43
+ Answer the coding agent directly.
44
+ `.trim()}]}),p=a.text.trim();return p?{answer:p,provider:a.provider,model:a.model,usedAi:!0}:{answer:r.answer,usedAi:!1}}catch{return{answer:r.answer,usedAi:!1}}}import{existsSync as gt,mkdirSync as yt,readFileSync as ht,renameSync as kt,rmSync as bt,writeFileSync as vt}from"node:fs";import{join as ue}from"node:path";var At=720*60*1e3;function de(n){return ue(n.rootPath,".toolnet","context","memory-agent-conversation.json")}function E(n){return n.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}function Mt(n){let e=E(n);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 xt(n){if(Mt(n))return!1;let e=E(n),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(r=>r.test(e))?!0:t.length<=5&&/\b(?:nó|đó|vậy|thế|that|it|this)\b/u.test(e)}function wt(n,e,t){let r=de(n);if(!gt(r))return null;try{let o=JSON.parse(ht(r,"utf8"));if(o.schema!=="toolnet.memory-conversation.v1"||o.version!==1||o.projectId!==n.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 St(n,e=8){let t=new Set,r=[];for(let o of n){let i=o.value.replace(/\s+/gu," ").trim();if(!i)continue;let a=[o.kind,E(i)].join(":");if(!t.has(a)&&(t.add(a),r.push({...o,value:i}),r.length>=e))break}return r}function Et(n,e){let t=de(n),r=ue(n.rootPath,".toolnet","context");yt(r,{recursive:!0,mode:448});let o=`${t}.${process.pid}.tmp`;try{vt(o,JSON.stringify(e,null,2)+`
45
+ `,{encoding:"utf8",mode:384}),kt(o,t)}finally{bt(o,{force:!0})}}function Ct(n){return n.focus.slice(0,8).map(e=>`- ${e.kind}: ${e.value}`).join(`
46
+ `)}function _t(n,e){return n.priorFocus.find(t=>t.kind===e)?.value}function H(n){if(!n.usedPriorFocus||n.currentIntent!=="summary"||n.priorFocus.length===0)return null;let e=E(n.originalQuestion),t=/^(?:tại sao|vì sao|sao vậy|why|why so|why that)\b/u.test(e),r=/^(?: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"]:r?["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"},a=[];for(let p of o){let l=_t(n,p);if(l&&(a.push(`${i[p]??p}: ${l}.`),t&&a.length>=2||!t&&a.length>=3))break}return a.length===0?null:{answer:a.join(" "),intent:"summary",source:"conversation-focus"}}function It(n){let e=E(n);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 me(n,e,t={}){let r=t.now??Date.now(),o=t.maxAgeMs??At,i=wt(n,r,o),a=!!(i&&i.focus.length>0&&xt(e)&&!It(e)),p=a&&i?[e.trim(),"","Previous ToolNet Memory conversation focus:",Ct(i),"","Use the compact focus only to resolve the current follow-up question.","Do not reconstruct or request raw session transcripts."].join(`
47
+ `):e.trim(),l=C(e),d=y(n,p,{maxFacts:8,maxChars:1800}).facts.map(b=>({kind:b.kind,value:b.value,source:b.source})),g=St(a&&i?[...d,...i.focus]:d);return Et(n,{schema:"toolnet.memory-conversation.v1",version:1,projectId:n.id,updatedAt:new Date(r).toISOString(),lastIntent:l,focus:g}),{question:p,originalQuestion:e,usedPriorFocus:a,previousIntent:i?.lastIntent,currentIntent:l,priorFocus:i?.focus??[],focusCount:g.length}}var Dr={question:pe.string().min(2).max(4e3).describe("Question about previous project work, unfinished tasks, handoff, blockers, decisions, files or next actions."),mode:pe.enum(["ai","local"]).optional().describe("ai uses ToolNet Memory Agent with configured LLM fallback chain. local returns deterministic local memory only.")};async function fe(n,e){let t=me(n.project,e.question);if(e.mode==="local"){let o=H(t);if(o)return{answer:o.answer,mode:"local",usedAi:!1,source:o.source,intent:o.intent};let i=M(n.project,t.originalQuestion);return{answer:i.answer,mode:"local",usedAi:!1,source:i.source,intent:i.intent}}let r=await le(n.project,t.question);if(!r.usedAi){let o=H(t);if(o)return{answer:o.answer,mode:"ai",usedAi:!1,provider:r.provider,model:r.model}}return{answer:r.answer,mode:"ai",usedAi:r.usedAi,provider:r.provider,model:r.model}}var ge="RAW_TRANSCRIPT_MUST_NEVER_ENTER_CONTINUITY_CONTEXT_X1";function ye(n,e){he(n,JSON.stringify(e,null,2)+`
48
+ `,"utf8")}function Rt(n,e,t,r,o,i,a){return{continuity:{schema:"toolnet.handoff.v2",version:2,project:{id:n,name:e},source:{agent:t,nativeSessionId:`${t}-x1-source`,sessionKey:`${t}:${t}-x1-source`,sequence:42,reason:"checkpoint"},capturedAt:"2026-08-13T05:30:00.000Z",current:{phase:{id:"x1",title:"Cross-Agent Continuity E2E",status:"in_progress"},task:{id:"x1-cross-agent",title:r,status:"in_progress"},file:o},completed:{phases:["A1 OpenCode Adapter","A2 Agy Adapter","A3 Claude Code Adapter"],tasks:["Build canonical handoff schema","Build intelligent memory retrieval"]},remaining:{phases:["X1 Cross-Agent Continuity E2E"],tasks:[r],todos:[i]},nextAction:a,blockers:[],decisions:["Canonical handoff must override stale session state","Never replay raw transcripts during continuity recovery"],files:{current:o,recent:[o]},tests:{status:"passing",recent:["A1-A3 adapter tests passed"]},attention:[],progress:{phasesTotal:3,phasesCompleted:2,tasksTotal:4,tasksCompleted:3,blocked:0},stateDigest:"a".repeat(64)}}}async function L(n,e){return fe({project:n},{question:e,mode:"local"})}async function Ft(n,e){let t=Tt(k(Ot(),`toolnet-x1-${n}-${e}-`));try{let r=`x1-${n}-${e}`,o=`x1-${n}-to-${e}`,i={id:r,name:o,rootPath:t,createdAt:"2026-08-13T05:30:00.000Z",updatedAt:"2026-08-13T05:30:00.000Z",graphVersion:1,memoryVersion:1},a=k(t,".toolnet","work"),p=k(t,".toolnet","context"),l=k(t,".toolnet","sessions",n,"raw-session-x1");G(a,{recursive:!0}),G(p,{recursive:!0}),G(l,{recursive:!0});let f=`X1 ${n} to ${e} canonical continuity`,d=`src/x1/${n}-to-${e}.ts`,g=`Implement ${e} continuation step`,b=`Continue ${f} in ${d}`;ye(k(a,"handoff-latest.json"),Rt(r,o,n,f,d,g,b)),ye(k(p,"session-origin.json"),{version:1,projectId:r,agent:"stale-agent",nativeSessionId:"stale-session",updatedAt:"2026-01-01T00:00:00.000Z",currentTask:"STALE TASK MUST NOT WIN",currentPhase:"STALE PHASE",lastTouchedFile:"src/stale/old.ts",latestNextAction:"STALE NEXT ACTION"}),he(k(l,"events.jsonl"),JSON.stringify({text:ge})+`
49
+ `,"utf8");let be=y(i,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?",{maxFacts:12,maxChars:3200}),D=await L(i,"Task hi\u1EC7n t\u1EA1i \u0111ang l\xE0m l\xE0 g\xEC?"),K=await L(i,"File hi\u1EC7n t\u1EA1i l\xE0 file n\xE0o?"),$=await L(i,"TODO c\xF2n l\u1EA1i l\xE0 g\xEC?"),U=await L(i,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?"),ve=[D.answer,K.answer,$.answer,U.answer].join(`
50
+ `),j=[D,K,$,U],Y={canonicalHandoffPreferred:be.facts[0]?.source==="handoff",taskRecovered:D.answer.includes(f),fileRecovered:K.answer.includes(d),todoRecovered:$.answer.includes(g),nextActionRecovered:U.answer.includes(b),localModeOnly:j.every(A=>A.mode==="local"),noAiRequired:j.every(A=>A.usedAi===!1),rawTranscriptIsolated:!ve.includes(ge),compact:j.every(A=>A.answer.length<1600)};return{from:n,to:e,passed:Object.values(Y).every(Boolean),checks:Y}}finally{Pt(t,{recursive:!0,force:!0})}}async function ke(){let n=[["agy","codex"],["codex","opencode"],["opencode","claude"],["claude","agy"]],e=[];for(let[r,o]of n)e.push(await Ft(r,o));let t=e.filter(r=>r.passed).length;return{passed:t===e.length,total:e.length,passedCount:t,cases:e}}var m={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",cyan:"\x1B[36m",gray:"\x1B[90m",white:"\x1B[97m"};function Nt(n){return n?`${m.green}\u2713${m.reset}`:`${m.red}\u2717${m.reset}`}function Lt(n){let e=`${n.from.padEnd(8)} \u2192 ${n.to.padEnd(8)}`;if(console.log(`\u2502 ${Nt(n.passed)} ${e}`),!n.passed)for(let[t,r]of Object.entries(n.checks))r||console.log(`\u2502 ${m.red}\u2717${m.reset} ${t}`)}async function Dt(){console.log(""),console.log(`${m.cyan}\u25C7${m.reset} ${m.white}ToolNet Cross-Agent Continuity Certification${m.reset}`),console.log(`${m.gray}\u2502${m.reset}`);let n=await ke();for(let e of n.cases)Lt(e);if(console.log(`${m.gray}\u2502${m.reset}`),n.passed){console.log(`${m.green}\u25C6 PASS${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),console.log(`${m.green}\u2713${m.reset} canonical handoff`),console.log(`${m.green}\u2713${m.reset} memory_agent_ask`),console.log(`${m.green}\u2713${m.reset} task / file / TODO / next action`),console.log(`${m.green}\u2713${m.reset} raw transcripts isolated`),console.log(`${m.green}\u2713${m.reset} AI not required for direct continuity`);return}console.log(`${m.red}\u25C6 FAIL${m.reset} ${n.passedCount}/${n.total} cross-agent transitions`),process.exitCode=1}Dt().catch(n=>{console.error(n instanceof Error?n.message:String(n)),process.exitCode=1});
package/bundle/doctor.js CHANGED
@@ -1,3 +1,6 @@
1
- import"dotenv/config";import{existsSync as q,readFileSync as X}from"node:fs";import{homedir as ee}from"node:os";import{join as te}from"node:path";function re(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 oe(){let r=process.env.TOOLNET_GLOBAL_ENV??te(ee(),".config","toolnet-memory",".env");if(!q(r))return;let e=X(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 n=o.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)&&process.env[n]===void 0&&(process.env[n]=re(o.slice(s+1)))}}oe();function l(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function u(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function K(){return{memory:{autoCapture:l(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:l(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:l(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:l(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:u(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:u(process.env.MEMORY_RERANK_TOP,10),finalContext:u(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:u(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",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:l(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:u(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as se}from"node:crypto";import{existsSync as _,mkdirSync as ne,readFileSync as ie,renameSync as ae,writeFileSync as ce}from"node:fs";import{basename as pe,dirname as S,join as v,parse as V,resolve as f}from"node:path";var B=".toolnet",de="project.json";function me(r){return se("sha256").update(r).digest("hex").slice(0,16)}function T(r){return v(r,B,de)}function ge(r){return _(T(r))}function le(r,e){let t=f(r),o=V(t).root;for(;;){if(ge(t))return t;if(t===o||e&&t===f(e))break;let s=S(t);if(s===t)break;t=s}return null}function ue(r){let e=f(r),t=V(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(n=>_(v(e,n))))return e;if(e===t)break;let s=S(e);if(s===e)break;e=s}return f(r)}function fe(r){let e;try{e=JSON.parse(ie(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:S(S(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 N(r,e){let t=v(r,B);ne(t,{recursive:!0});let o=T(r),s=`${o}.tmp-${process.pid}`;ce(s,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),ae(s,o)}function D(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=f(e),o=ue(t),n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>_(v(o,d))),i=le(t,n?o:void 0);if(i){let d=T(i),m=fe(d);return m.rootPath!==i&&(m.rootPath=i,m.updatedAt=new Date().toISOString(),N(i,m)),D(m,i)}let a=new Date().toISOString(),c=pe(o),p={version:1,id:me(o),name:c,remote:c,rootPath:o,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return N(o,p),D(p,o)}};import{homedir as De}from"node:os";import{join as Ve}from"node:path";import{DeleteObjectCommand as ye,GetObjectCommand as he,HeadObjectCommand as Se,ListObjectsV2Command as be,PutObjectCommand as ve,S3Client as we}from"@aws-sdk/client-s3";import{getSignedUrl as je}from"@aws-sdk/s3-request-presigner";var w=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new we({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 ve({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await je(this.client,new he({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 Se({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 ye({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new be({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};import{access as z,mkdir as xe,readFile as ke,readdir as Pe,rm as Ee,stat as L,writeFile as Ae}from"node:fs/promises";import{dirname as Ce,join as Re,relative as U,resolve as Me}from"node:path";var y=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Me(this.root,t)}async put(e,t){let o=this.path(e);await xe(Ce(o),{recursive:!0}),await Ae(o,t)}async get(e){try{return await ke(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await z(this.path(e)),!0}catch{return!1}}async delete(e){await Ee(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await z(t)}catch{return o}let s=async i=>{let a=await Pe(i,{withFileTypes:!0});for(let c of a){let p=Re(i,c.name);if(c.isDirectory()){await s(p);continue}let d=await L(p);o.push({key:U(this.root,p),size:d.size,updatedAt:d.mtime.toISOString()})}},n=await L(t);return n.isDirectory()?await s(t):o.push({key:U(this.root,t),size:n.size,updatedAt:n.mtime.toISOString()}),o}};import{DeleteObjectCommand as _e,GetObjectCommand as Te,HeadObjectCommand as Oe,ListObjectsV2Command as Ie,PutObjectCommand as $e,S3Client as Ke}from"@aws-sdk/client-s3";import{getSignedUrl as Ne}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,o="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new $e({Bucket:this.bucket,Key:e,Body:s,ContentType:o}))}async get(e){let t=await Ne(this.client,new Te({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 Oe({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 _e({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let s=await this.client.send(new Ie({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let n of s.Contents??[])n.Key&&t.push({key:n.Key,size:n.Size,updatedAt:n.LastModified?.toISOString()});o=s.IsTruncated?s.NextContinuationToken:void 0}while(o);return t}};function O(r,e){return console.warn(e),new y(r)}function F(r){let e=r.localRoot??Ve(De(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.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}):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 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}):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 w({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new y(e)}var j=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 o=JSON.parse(t);if(!Array.isArray(o))throw new Error("Invalid memory snapshot");return o}async save(e,t){let o=JSON.stringify(t,null,2);await this.storage.put(this.key(e),o,"application/json")}};var x=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return null;let o=JSON.parse(t);if(o.version!==1||o.projectId!==e||!Array.isArray(o.records))throw new Error("Invalid vector snapshot");return o}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var k=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var P=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","chunks","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var E=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function Be(r){return new Promise(e=>setTimeout(e,r))}async function H(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,s=e.maxDelayMs??2e3,n;for(let i=1;i<=t;i++)try{return await r()}catch(a){if(n=a,i>=t)break;let c=Math.min(s,o*2**(i-1)),p=Math.floor(Math.random()*Math.max(1,c*.2));await Be(c+p)}throw n}var ze=new Set(["put","get","getText","delete","list"]);function J(r,e={}){return new Proxy(r,{get(t,o){let s=Reflect.get(t,o,t);return typeof s!="function"?s:ze.has(o)?(...n)=>H(()=>Promise.resolve(s.apply(t,n)),e):s.bind(t)}})}var A=class{constructor(e){this.options=e}options;name="huggingface";dimensions=384;async embed(e){let[t]=await this.embedMany([e]);return t}async embedMany(e){if(e.length===0)return[];let t=`https://router.huggingface.co/hf-inference/models/${this.options.model}/pipeline/feature-extraction`,o=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.options.token}`,"Content-Type":"application/json"},body:JSON.stringify({inputs:e,options:{wait_for_model:!0}})});if(!o.ok)throw new Error(`HF embedding failed: ${o.status} ${await o.text()}`);let s=await o.json();if(!Array.isArray(s))throw new Error("HF embedding returned invalid response");return s.map(n=>Le(n))}};function Le(r){if(!Array.isArray(r))throw new Error("Invalid embedding");if(r.length>0&&typeof r[0]=="number")return r;if(r.length>0&&Array.isArray(r[0])){let e=r;if(e.length===0)return[];let t=e[0].length,o=new Array(t).fill(0);for(let s of e)for(let n=0;n<t;n++)o[n]+=s[n]??0;for(let s=0;s<t;s++)o[s]/=e.length;return o}throw new Error("Unsupported embedding shape")}import{createHash as Ue}from"node:crypto";var C=class{name="hash-fallback";dimensions=128;async embed(e){let t=new Array(this.dimensions).fill(0),o=e.toLowerCase().split(/\s+/).filter(Boolean);for(let s of o){let i=Ue("sha256").update(s).digest().readUInt32BE(0)%this.dimensions;t[i]+=1}return Fe(t)}async embedMany(e){return Promise.all(e.map(t=>this.embed(t)))}};function Fe(r){let e=Math.sqrt(r.reduce((t,o)=>t+o*o,0));return e===0?r:r.map(t=>t/e)}var I=class{constructor(e,t=new C){this.primary=e;this.fallback=t}primary;fallback;name="resilient";async embed(e){if(this.primary)try{return await this.primary.embed(e)}catch(t){console.warn("[embedding] primary failed, fallback enabled:",t instanceof Error?t.message:t)}return this.fallback.embed(e)}async embedMany(e){if(this.primary)try{return await this.primary.embedMany(e)}catch(t){console.warn("[embedding] primary failed, fallback enabled:",t instanceof Error?t.message:t)}return this.fallback.embedMany(e)}};function Y(){let r=process.env.HF_TOKEN,e=process.env.HF_EMBEDDING_MODEL??"sentence-transformers/all-MiniLM-L6-v2",t=r?new A({token:r,model:e}):null;return new I(t)}var He=["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"],R=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",o){let s=new Date().toISOString(),n=s.replace(/[:.]/g,"-"),i=this.projectPrefix(e),a=this.snapshotPrefix(e,n),c=[];for(let d of He){let m=`${i}/${d}`,g=await this.storage.get(m);g&&(await this.storage.put(`${a}/${d}`,g,"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:o};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 o=this.snapshotPrefix(e,t),s=await this.storage.getText(`${o}/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(`${o}/${c}`);p&&(await this.storage.put(`${i}/${c}`,p,"application/json"),a.push(c))}return{restored:a}}async remove(e,t){let o=this.snapshotPrefix(e,t),s=await this.storage.list(o);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}};function $(r,e){for(let t of r)process.env[t]||e.push(`Missing ${t}`)}function G(){let r=[],e=[],t=process.env.MEMORY_STORAGE_PROVIDER??"local";return t==="r2"?$(["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"],r):t==="s3"?$(["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"],r):t==="huggingface"?$(["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"],r):t!=="local"&&r.push(`Unsupported MEMORY_STORAGE_PROVIDER: ${t}`),process.env.HF_TOKEN||e.push("HF_TOKEN missing: semantic embedding will use fallback"),process.env.HF_EMBEDDING_MODEL||e.push("HF_EMBEDDING_MODEL not set; default model will be used"),{ok:r.length===0,errors:r,warnings:e}}import{randomUUID as Je}from"node:crypto";var M=class{constructor(e,t){this.storage=e;this.embeddings=t}storage;embeddings;async run(){let e=`_health/production-${Je()}.txt`,t=`toolnet-memory:${Date.now()}`,o=!1;try{await this.storage.put(e,t,"text/plain"),o=await this.storage.getText(e)===t}finally{try{await this.storage.delete(e)}catch{}}let s=null;if(this.embeddings)try{let n=await this.embeddings.embed("ToolNet Memory production health check");s={ok:n.length>0,dimensions:n.length,mode:n.length===384?"huggingface":"fallback-or-custom"}}catch{s={ok:!1,dimensions:0,mode:"failed"}}return{ok:o&&(s===null||s.ok),storage:{ok:o,provider:this.storage.name},embedding:s}}};function Z(){return process.argv.includes("--json")}function Ye(r){if(console.log(""),console.log("ToolNet Memory Doctor"),console.log("====================="),console.log(""),r.project&&(console.log(`Project: ${r.project}`),console.log("")),r.storage)if(r.storage.ok){let s=typeof r.storage.provider=="string"?` (${r.storage.provider})`:"";console.log(`\u2713 Storage${s}`)}else console.log("\u2717 Storage");if(r.embedding)if(r.embedding.ok){let s=[];typeof r.embedding.mode=="string"&&s.push(r.embedding.mode),typeof r.embedding.dimensions=="number"&&s.push(`${r.embedding.dimensions} dimensions`),console.log(`\u2713 Embedding${s.length?` (${s.join(", ")})`:""}`)}else console.log("\u2717 Embedding");(r.memory!==void 0||r.graphSymbols!==void 0||r.codeChunks!==void 0)&&(console.log(""),console.log("Project data:"),r.memory!==void 0&&console.log(` Memories ${r.memory}`),r.graphSymbols!==void 0&&console.log(` Graph symbols ${r.graphSymbols}`),r.graphEdges!==void 0&&console.log(` Graph edges ${r.graphEdges}`),r.memoryVectors!==void 0&&console.log(` Memory vectors ${r.memoryVectors}`),r.codeChunks!==void 0&&console.log(` Code chunks ${r.codeChunks}`),r.codeVectors!==void 0&&console.log(` Code vectors ${r.codeVectors}`),r.snapshots!==void 0&&console.log(` Snapshots ${r.snapshots}`));let e=r.config?.errors??[];if(e.length>0){console.log(""),console.log("Configuration required:");for(let s of e)console.log(` - ${s}`)}let t=[...r.config?.warnings??[],...r.warnings??[]],o=[...new Set(t)];if(o.length>0){console.log(""),console.log("Warnings:");for(let s of o)console.log(` - ${s}`)}console.log(""),r.ok?console.log("\u2713 ToolNet Memory is ready"):(console.log("\u2717 ToolNet Memory requires attention"),e.length>0&&(console.log(""),console.log("Run:"),console.log(" toolnet-memory setup"))),console.log("")}function W(r){Z()?console.log(JSON.stringify(r,null,2)):Ye(r)}async function Ge(){let r=G();if(!r.ok){let Q={ok:!1,config:r,warnings:r.warnings};W(Q),process.exitCode=1;return}let e=K(),t=new b().detect(),o=J(F({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),s=Y(),n=await new M(o,s).run(),i=await new j(o).load(t.id),a=await new k(o).load(t.id),c=await new x(o).load(t.id),p=await new P(o).load(t.id),d=await new E(o).load(t.id),m=await new R(o).list(t.id),g={ok:n.ok,project:t.name,storage:n.storage,embedding:n.embedding,memory:i.length,graphSymbols:a?.symbols.length??0,graphEdges:a?.edges.length??0,memoryVectors:c?.records.length??0,codeChunks:p?.chunks.length??0,codeVectors:d?.records.length??0,snapshots:m.length,warnings:r.warnings};W(g),process.exitCode=g.ok?0:1}Ge().catch(r=>{let e=r instanceof Error?r.message:String(r);Z()?console.log(JSON.stringify({ok:!1,error:e},null,2)):(console.error(""),console.error("ToolNet Memory Doctor"),console.error("====================="),console.error(""),console.error(`\u2717 ${e}`),console.error("")),process.exitCode=1});
1
+ import"dotenv/config";import{existsSync as Re,readFileSync as Te}from"node:fs";import{homedir as $e}from"node:os";import{join as Le}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 Ne(){let r=process.env.TOOLNET_GLOBAL_ENV??Le($e(),".config","toolnet-memory",".env");if(!Re(r))return;let e=Te(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 n=o.indexOf("=");if(n<=0)continue;let s=o.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=De(o.slice(n+1)))}}Ne();function S(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function E(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function se(){return{memory:{autoCapture:S(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:S(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:S(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:S(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:E(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:E(process.env.MEMORY_RERANK_TOP,10),finalContext:E(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:E(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:S(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:E(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Ke}from"node:crypto";import{existsSync as Z,mkdirSync as Ue,readFileSync as Be,renameSync as Fe,writeFileSync as He}from"node:fs";import{basename as Ge,dirname as O,join as C,parse as ce,resolve as k}from"node:path";var de=".toolnet",Ve="project.json";function Ye(r){return Ke("sha256").update(r).digest("hex").slice(0,16)}function X(r){return C(r,de,Ve)}function ze(r){return Z(X(r))}function We(r,e){let t=k(r),o=ce(t).root;for(;;){if(ze(t))return t;if(t===o||e&&t===k(e))break;let n=O(t);if(n===t)break;t=n}return null}function Je(r){let e=k(r),t=ce(e).root,o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(o.some(s=>Z(C(e,s))))return e;if(e===t)break;let n=O(e);if(n===e)break;e=n}return k(r)}function qe(r){let e;try{e=JSON.parse(Be(r,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${r}: ${n instanceof Error?n.message:String(n)}`)}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:O(O(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 ie(r,e){let t=C(r,de);Ue(t,{recursive:!0});let o=X(r),n=`${o}.tmp-${process.pid}`;He(n,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Fe(n,o)}function ae(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),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>Z(C(o,u))),a=We(t,s?o:void 0);if(a){let u=X(a),l=qe(u);return l.rootPath!==a&&(l.rootPath=a,l.updatedAt=new Date().toISOString(),ie(a,l)),ae(l,a)}let c=new Date().toISOString(),d=Ge(o),m={version:1,id:Ye(o),name:d,remote:d,rootPath:o,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return ie(o,m),ae(m,o)}};import{homedir as vt}from"node:os";import{join as At}from"node:path";import{DeleteObjectCommand as Qe,GetObjectCommand as Ze,HeadObjectCommand as Xe,ListObjectsV2Command as et,PutObjectCommand as tt,S3Client as rt}from"@aws-sdk/client-s3";import{getSignedUrl as ot}from"@aws-sdk/s3-request-presigner";var M=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new rt({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 n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new tt({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await ot(this.client,new Ze({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 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 Qe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new et({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};import{access as le,mkdir as nt,readFile as st,readdir as it,rm as at,stat as pe,writeFile as ct}from"node:fs/promises";import{dirname as dt,join as lt,relative as ue,resolve as pt}from"node:path";var w=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return pt(this.root,t)}async put(e,t){let o=this.path(e);await nt(dt(o),{recursive:!0}),await ct(o,t)}async get(e){try{return await st(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 le(this.path(e)),!0}catch{return!1}}async delete(e){await at(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),o=[];try{await le(t)}catch{return o}let n=async a=>{let c=await it(a,{withFileTypes:!0});for(let d of c){let m=lt(a,d.name);if(d.isDirectory()){await n(m);continue}let u=await pe(m);o.push({key:ue(this.root,m),size:u.size,updatedAt:u.mtime.toISOString()})}},s=await pe(t);return s.isDirectory()?await n(t):o.push({key:ue(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),o}};import{DeleteObjectCommand as ut,GetObjectCommand as mt,HeadObjectCommand as gt,ListObjectsV2Command as ft,PutObjectCommand as yt,S3Client as ht}from"@aws-sdk/client-s3";import{getSignedUrl as bt}from"@aws-sdk/s3-request-presigner";var _=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new ht({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 n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new yt({Bucket:this.bucket,Key:e,Body:n,ContentType:o}))}async get(e){let t=await bt(this.client,new mt({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 gt({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 ut({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],o;do{let n=await this.client.send(new ft({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:o}));for(let s of n.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});o=n.IsTruncated?n.NextContinuationToken:void 0}while(o);return t}};function ee(r,e){return console.warn(e),new w(r)}function me(r){let e=r.localRoot??At(vt(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new _({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):ee(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 _({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):ee(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 M({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):ee(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new w(e)}var j=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 o=JSON.parse(t);if(!Array.isArray(o))throw new Error("Invalid memory snapshot");return o}async save(e,t){let o=JSON.stringify(t,null,2);await this.storage.put(this.key(e),o,"application/json")}};var R=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));if(!t)return null;let o=JSON.parse(t);if(o.version!==1||o.projectId!==e||!Array.isArray(o.records))throw new Error("Invalid vector snapshot");return o}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var T=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"graph","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var $=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","chunks","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};var L=class{constructor(e){this.storage=e}storage;key(e){return["projects",e,"code","vectors","current.json"].join("/")}async load(e){let t=await this.storage.getText(this.key(e));return t?JSON.parse(t):null}async save(e){await this.storage.put(this.key(e.projectId),JSON.stringify(e),"application/json")}};function St(r){return new Promise(e=>setTimeout(e,r))}async function ge(r,e={}){let t=Math.max(1,e.attempts??3),o=e.baseDelayMs??150,n=e.maxDelayMs??2e3,s;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(s=c,a>=t)break;let d=Math.min(n,o*2**(a-1)),m=Math.floor(Math.random()*Math.max(1,d*.2));await St(d+m)}throw s}var Et=new Set(["put","get","getText","delete","list"]);function fe(r,e={}){return new Proxy(r,{get(t,o){let n=Reflect.get(t,o,t);return typeof n!="function"?n:Et.has(o)?(...s)=>ge(()=>Promise.resolve(n.apply(t,s)),e):n.bind(t)}})}function ye(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 he(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 n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var D=class{constructor(e,t,o,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=o,this.folder=ye(n??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,n=await this.provider.getText(e);if(n){let a;try{a=JSON.parse(n)}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 s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:o,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=he(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))}};var be=[{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 b(r){let e=be.find(t=>t.id===r);if(!e)throw new Error(`Unsupported AI provider: ${r}`);return e}function N(r){return be.some(e=>e.id===r)}function i(r){return process.env[r]?.trim()||void 0}function g(...r){return r.find(e=>!!e?.trim())}function ve(){if(i("GROQ_API_KEY"))return"groq";if(i("DEEPSEEK_API_KEY"))return"deepseek";if(i("NVIDIA_API_KEY")||i("NVIDIA_NIM_API_KEY"))return"nvidia";if(i("OPENROUTER_API_KEY"))return"openrouter";if(i("ALIBABA_API_KEY")||i("DASHSCOPE_API_KEY"))return"alibaba";if(i("GEMINI_API_KEY")||i("GOOGLE_API_KEY"))return"gemini";if(i("CLOUDFLARE_API_TOKEN")&&i("CLOUDFLARE_ACCOUNT_ID"))return"cloudflare";if(i("HF_TOKEN"))return"huggingface";if(i("OLLAMA_MODEL")||i("OLLAMA_BASE_URL"))return"ollama"}function Ae(){let r=i("TOOLNET_LLM_PROVIDER");return r&&N(r)?r:ve()??"openai-compatible"}function K(r){let e=b(r);switch(r){case"alibaba":return{provider:r,apiKey:g(i("ALIBABA_API_KEY"),i("DASHSCOPE_API_KEY")),baseUrl:g(i("ALIBABA_BASE_URL"),i("DASHSCOPE_BASE_URL"),e.defaultBaseUrl),model:g(i("ALIBABA_MODEL"),i("DASHSCOPE_MODEL"))};case"openrouter":return{provider:r,apiKey:i("OPENROUTER_API_KEY"),baseUrl:g(i("OPENROUTER_BASE_URL"),e.defaultBaseUrl),model:i("OPENROUTER_MODEL")};case"groq":return{provider:r,apiKey:i("GROQ_API_KEY"),baseUrl:g(i("GROQ_BASE_URL"),e.defaultBaseUrl),model:i("GROQ_MODEL")};case"deepseek":return{provider:r,apiKey:i("DEEPSEEK_API_KEY"),baseUrl:g(i("DEEPSEEK_BASE_URL"),e.defaultBaseUrl),model:g(i("DEEPSEEK_MODEL"),e.defaultModel)};case"nvidia":return{provider:r,apiKey:g(i("NVIDIA_API_KEY"),i("NVIDIA_NIM_API_KEY")),baseUrl:g(i("NVIDIA_BASE_URL"),i("NVIDIA_NIM_BASE_URL"),e.defaultBaseUrl),model:g(i("NVIDIA_MODEL"),i("NVIDIA_NIM_MODEL"),e.defaultModel)};case"gemini":return{provider:r,apiKey:g(i("GEMINI_API_KEY"),i("GOOGLE_API_KEY")),baseUrl:g(i("GEMINI_BASE_URL"),e.defaultBaseUrl),model:i("GEMINI_MODEL")};case"huggingface":return{provider:r,apiKey:i("HF_TOKEN"),baseUrl:g(i("HF_INFERENCE_BASE_URL"),e.defaultBaseUrl),model:g(i("HF_LLM_MODEL"),i("HF_MODEL"))};case"ollama":return{provider:r,apiKey:i("OLLAMA_API_KEY"),baseUrl:g(i("OLLAMA_BASE_URL"),e.defaultBaseUrl),model:i("OLLAMA_MODEL")};case"cloudflare":return{provider:r,accountId:i("CLOUDFLARE_ACCOUNT_ID"),apiKey:i("CLOUDFLARE_API_TOKEN"),baseUrl:i("CLOUDFLARE_AI_BASE_URL"),model:i("CLOUDFLARE_MODEL")};case"custom":return{provider:r,apiKey:i("CUSTOM_AI_API_KEY"),baseUrl:i("CUSTOM_AI_BASE_URL"),model:i("CUSTOM_AI_MODEL")};default:return{provider:"openai-compatible",apiKey:g(i("OPENAI_API_KEY"),i("MODEL_API_KEY")),baseUrl:g(i("OPENAI_BASE_URL"),i("MODEL_BASE_URL")),model:g(i("OPENAI_MODEL"),i("MODEL_NAME"))}}}function kt(){let r=i("TOOLNET_LLM_PROVIDER"),e=r&&N(r)?r:Ae(),t=b(e),o=K(e);return{provider:e,apiKey:g(i("TOOLNET_LLM_API_KEY"),o.apiKey),baseUrl:g(i("TOOLNET_LLM_BASE_URL"),o.baseUrl,t.defaultBaseUrl),model:g(i("TOOLNET_LLM_MODEL"),o.model,t.defaultModel),accountId:g(i("TOOLNET_LLM_ACCOUNT_ID"),o.accountId)}}function wt(){let r=i("TOOLNET_EMBEDDING_PROVIDER");return r==="local"?"local":r&&N(r)?r:i("HF_TOKEN")||i("HF_EMBEDDING_MODEL")?"huggingface":"local"}function _t(){let r=wt();if(r==="local")return{provider:"local",model:g(i("TOOLNET_EMBEDDING_MODEL"),i("LOCAL_EMBEDDING_MODEL"))};let e=b(r),t,o,n,s;switch(r){case"huggingface":t=i("HF_TOKEN"),o=i("HF_INFERENCE_BASE_URL"),n=i("HF_EMBEDDING_MODEL");break;case"openai-compatible":t=i("OPENAI_API_KEY"),o=i("OPENAI_BASE_URL"),n=i("OPENAI_EMBEDDING_MODEL");break;case"cloudflare":t=i("CLOUDFLARE_API_TOKEN"),o=i("CLOUDFLARE_AI_BASE_URL"),n=i("CLOUDFLARE_EMBEDDING_MODEL"),s=i("CLOUDFLARE_ACCOUNT_ID");break;default:t=K(r).apiKey,o=K(r).baseUrl,n=i(`${r.toUpperCase().replace(/-/g,"_")}_EMBEDDING_MODEL`)}let a=g(i("TOOLNET_EMBEDDING_ACCOUNT_ID"),s),c=g(i("TOOLNET_EMBEDDING_BASE_URL"),o,e.defaultBaseUrl),d=r==="cloudflare"&&!c&&a?`https://api.cloudflare.com/client/v4/accounts/${a}/ai/v1`:c;return{provider:r,apiKey:g(i("TOOLNET_EMBEDDING_API_KEY"),t),baseUrl:d,model:g(i("TOOLNET_EMBEDDING_MODEL"),n),accountId:a}}function P(){let r=kt(),e=_t();return{llm:r,embedding:e,legacy:{llm:!i("TOOLNET_LLM_PROVIDER")&&!!ve(),embedding:!i("TOOLNET_EMBEDDING_PROVIDER")&&!!(i("HF_TOKEN")||i("HF_EMBEDDING_MODEL"))}}}function Se(r=Ae()){let e=P().llm;if(r===e.provider)return{id:r,apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,accountId:e.accountId};let t=K(r);return{id:r,apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,accountId:t.accountId}}var U=class{constructor(e){this.options=e}options;name="gemini";async embed(e){let t=this.options.model.replace(/^models\//,""),n=`${this.options.baseUrl?.replace(/\/+$/,"")||"https://generativelanguage.googleapis.com/v1beta"}/models/${encodeURIComponent(t)}:embedContent?key=${encodeURIComponent(this.options.apiKey)}`,s=await fetch(n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({model:`models/${t}`,content:{parts:[{text:e}]}})});if(!s.ok)throw new Error(`Gemini embedding failed: ${s.status} ${await s.text()}`);let c=(await s.json()).embedding?.values;if(!Array.isArray(c)||c.length===0)throw new Error("Gemini embedding returned invalid response");return c}async embedMany(e){let t=[];for(let o of e)t.push(await this.embed(o));return t}};import{createHash as Pt}from"node:crypto";var B=class{name="hash-fallback";dimensions=128;async embed(e){let t=new Array(this.dimensions).fill(0),o=e.toLowerCase().split(/\s+/).filter(Boolean);for(let n of o){let a=Pt("sha256").update(n).digest().readUInt32BE(0)%this.dimensions;t[a]+=1}return It(t)}async embedMany(e){return Promise.all(e.map(t=>this.embed(t)))}};function It(r){let e=Math.sqrt(r.reduce((t,o)=>t+o*o,0));return e===0?r:r.map(t=>t/e)}var F=class{constructor(e){this.options=e;this.dimensions=e.dimensions}options;name="openai-compatible";dimensions;async embed(e){let[t]=await this.embedMany([e]);return t}async embedMany(e){if(e.length===0)return[];let t=this.options.baseUrl.replace(/\/+$/,""),o=await fetch(`${t}/embeddings`,{method:"POST",headers:{"content-type":"application/json",...this.options.apiKey?{authorization:`Bearer ${this.options.apiKey}`}:{}},body:JSON.stringify({model:this.options.model,input:e})});if(!o.ok)throw new Error(`Embedding failed: ${o.status} ${await o.text()}`);let s=(await o.json()).data?.sort((a,c)=>(a.index??0)-(c.index??0)).map(a=>a.embedding);if(!s||s.length!==e.length||s.some(a=>!Array.isArray(a)))throw new Error("Embedding provider returned invalid response");return s}};var H=class{constructor(e){this.options=e}options;name="huggingface";dimensions=384;async embed(e){let[t]=await this.embedMany([e]);return t}async embedMany(e){if(e.length===0)return[];let t=`https://router.huggingface.co/hf-inference/models/${this.options.model}/pipeline/feature-extraction`,o=await fetch(t,{method:"POST",headers:{Authorization:`Bearer ${this.options.token}`,"Content-Type":"application/json"},body:JSON.stringify({inputs:e,options:{wait_for_model:!0}})});if(!o.ok)throw new Error(`HF embedding failed: ${o.status} ${await o.text()}`);let n=await o.json();if(!Array.isArray(n))throw new Error("HF embedding returned invalid response");return n.map(s=>Ot(s))}};function Ot(r){if(!Array.isArray(r))throw new Error("Invalid embedding");if(r.length>0&&typeof r[0]=="number")return r;if(r.length>0&&Array.isArray(r[0])){let e=r;if(e.length===0)return[];let t=e[0].length,o=new Array(t).fill(0);for(let n of e)for(let s=0;s<t;s++)o[s]+=n[s]??0;for(let n=0;n<t;n++)o[n]/=e.length;return o}throw new Error("Unsupported embedding shape")}var te=class{constructor(e,t=new B){this.primary=e;this.fallback=t}primary;fallback;name="resilient";async embed(e){if(this.primary)try{return await this.primary.embed(e)}catch(t){console.warn("[embedding] primary failed, fallback enabled:",t instanceof Error?t.message:t)}return this.fallback.embed(e)}async embedMany(e){if(this.primary)try{return await this.primary.embedMany(e)}catch(t){console.warn("[embedding] primary failed, fallback enabled:",t instanceof Error?t.message:t)}return this.fallback.embedMany(e)}};function xt(r){return`https://api.cloudflare.com/client/v4/accounts/${r}/ai/v1`}function Ct(){let r=P().embedding;if(r.provider==="local")return null;if(r.provider==="huggingface")return!r.apiKey||!r.model?null:new H({token:r.apiKey,model:r.model});if(r.provider==="gemini")return!r.apiKey||!r.model?null:new U({apiKey:r.apiKey,model:r.model,baseUrl:r.baseUrl});let e=r.baseUrl;return r.provider==="cloudflare"&&!e&&r.accountId&&(e=xt(r.accountId)),e&&r.model?new F({apiKey:r.apiKey,baseUrl:e,model:r.model}):null}function Ee(){return new te(Ct())}var re=class extends Error{status;constructor(e,t){super(e),this.name="AiHttpError",this.status=t}};async function v(r,e,t=3e4){let o=new AbortController,n=setTimeout(()=>o.abort(),t);n.unref?.();try{let s=await fetch(r,{...e,signal:o.signal}),a=await s.text();if(!s.ok){let c=a;try{let d=JSON.parse(a);c=d.error?.message??d.message??a}catch{}throw new re(c||`HTTP ${s.status}`,s.status)}return a.trim()?JSON.parse(a):{}}finally{clearTimeout(n)}}function G(r,e){return`${r.replace(/\/+$/,"")}/${e.replace(/^\/+/,"")}`}var V=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(),o=this.config.apiKey?.trim();if(!t)throw new Error("cloudflare: ACCOUNT ID is not configured");if(!o)throw new Error("cloudflare: API TOKEN is not configured");let n=this.model(),a=`${(this.config.baseUrl?.trim()||`https://api.cloudflare.com/client/v4/accounts/${t}/ai/run`).replace(/\/+$/,"")}/${n}`,c=await v(a,{method:"POST",headers:{authorization:`Bearer ${o}`,"content-type":"application/json",...this.config.headers},body:JSON.stringify({messages:e.messages,temperature:e.temperature,max_tokens:e.maxTokens})}),d=c.result?.response?.trim();if(!d)throw new Error(c.errors?.[0]?.message??"cloudflare: empty model response");return{text:d,provider:"cloudflare",model:n}}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 Y=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 o=this.config.baseUrl?.trim()||"https://generativelanguage.googleapis.com/v1beta",n=this.model(),s=e.messages.filter(u=>u.role==="system"),a=e.messages.filter(u=>u.role!=="system").map(u=>({role:u.role==="assistant"?"model":"user",parts:[{text:u.content}]})),c=`${G(o,`models/${encodeURIComponent(n)}:generateContent`)}?key=${encodeURIComponent(t)}`,d=await v(c,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({...s.length?{systemInstruction:{parts:[{text:s.map(u=>u.content).join(`
5
+
6
+ `)}]}}:{},contents:a,generationConfig:{temperature:e.temperature,maxOutputTokens:e.maxTokens}})}),m=d.candidates?.[0]?.content?.parts?.map(u=>u.text??"").join("").trim();if(!m)throw new Error("gemini: empty model response");return{text:m,provider:"gemini",model:n,usage:d.usageMetadata?{inputTokens:d.usageMetadata.promptTokenCount,outputTokens:d.usageMetadata.candidatesTokenCount,totalTokens:d.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 z=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(),o=await v(G(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}:{}})}),n=o.choices?.[0]?.message?.content?.trim();if(!n)throw new Error(`${this.id}: empty model response`);return{text:n,provider:this.id,model:t,usage:o.usage?{inputTokens:o.usage.prompt_tokens,outputTokens:o.usage.completion_tokens,totalTokens:o.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 ke(r=Se()){switch(b(r.id).transport){case"gemini":return new Y(r);case"cloudflare":return new V(r);default:return new z(r)}}var Mt=["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"],W=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",o){let n=new Date().toISOString(),s=n.replace(/[:.]/g,"-"),a=this.projectPrefix(e),c=this.snapshotPrefix(e,s),d=[];for(let u of Mt){let l=`${a}/${u}`,f=await this.storage.get(l);f&&(await this.storage.put(`${c}/${u}`,f,"application/json"),d.push(u))}if(d.length===0)return null;let m={version:1,id:s,projectId:e,createdAt:n,reason:t,objects:d,metadata:o};return await this.storage.put(`${c}/snapshot.json`,JSON.stringify(m,null,2),"application/json"),m}async list(e){let t=[this.projectPrefix(e),"snapshots"].join("/"),n=(await this.storage.list(t)).filter(a=>a.key.endsWith("/snapshot.json")),s=[];for(let a of n){let c=await this.storage.getText(a.key);if(c)try{s.push(JSON.parse(c))}catch{}}return s.sort((a,c)=>c.createdAt.localeCompare(a.createdAt))}async restore(e,t){let o=this.snapshotPrefix(e,t),n=await this.storage.getText(`${o}/snapshot.json`);if(!n)throw new Error(`Snapshot not found: ${t}`);let s=JSON.parse(n);if(s.projectId!==e)throw new Error("Snapshot project mismatch");let a=this.projectPrefix(e),c=[];for(let d of s.objects){let m=await this.storage.get(`${o}/${d}`);m&&(await this.storage.put(`${a}/${d}`,m,"application/json"),c.push(d))}return{restored:c}}async remove(e,t){let o=this.snapshotPrefix(e,t),n=await this.storage.list(o);for(let s of n)await this.storage.delete(s.key);return n.length}async prune(e,t=10){let n=(await this.list(e)).slice(t),s=0;for(let a of n)await this.remove(e,a.id),s++;return s}};function oe(r,e){for(let t of r)process.env[t]||e.push(`Missing ${t}`)}function we(){let r=[],e=[],t=process.env.MEMORY_STORAGE_PROVIDER??"local";return t==="r2"?oe(["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"],r):t==="s3"?oe(["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"],r):t==="huggingface"?oe(["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"],r):t!=="local"&&r.push(`Unsupported MEMORY_STORAGE_PROVIDER: ${t}`),process.env.HF_TOKEN||e.push("HF_TOKEN missing: semantic embedding will use fallback"),process.env.HF_EMBEDDING_MODEL||e.push("HF_EMBEDDING_MODEL not set; default model will be used"),{ok:r.length===0,errors:r,warnings:e}}import{randomUUID as jt}from"node:crypto";var J=class{constructor(e,t){this.storage=e;this.embeddings=t}storage;embeddings;async run(){let e=`_health/production-${jt()}.txt`,t=`toolnet-memory:${Date.now()}`,o=!1;try{await this.storage.put(e,t,"text/plain"),o=await this.storage.getText(e)===t}finally{try{await this.storage.delete(e)}catch{}}let n=null;if(this.embeddings)try{let s=await this.embeddings.embed("ToolNet Memory production health check");n={ok:s.length>0,dimensions:s.length,mode:s.length===384?"huggingface":"fallback-or-custom"}}catch{n={ok:!1,dimensions:0,mode:"failed"}}return{ok:o&&(n===null||n.ok),storage:{ok:o,provider:this.storage.name},embedding:n}}};import{existsSync as Oe,readFileSync as Dt,readdirSync as Pe}from"node:fs";import{join as q}from"node:path";import{existsSync as Rt,mkdirSync as In,readFileSync as Tt,renameSync as On,writeFileSync as xn}from"node:fs";import{dirname as Mn,join as $t}from"node:path";function Lt(r){return $t(r.rootPath,".toolnet","work","current.json")}function _e(r){let e=Lt(r);if(!Rt(e))return null;try{let t=JSON.parse(Tt(e,"utf8"));return t.version!==1||t.projectId!==r.id?null:t}catch{return null}}function xe(r){if(!Oe(r))return null;try{return JSON.parse(Dt(r,"utf8"))}catch{return null}}function Nt(r){let e=q(r.rootPath,".toolnet","sessions");if(!Oe(e))return[];let t=[];for(let o of Pe(e,{withFileTypes:!0})){if(!o.isDirectory())continue;let n=q(e,o.name);for(let s of Pe(n,{withFileTypes:!0})){if(!s.isDirectory())continue;let a=xe(q(n,s.name,"state.json"));!a||a.version!==1||a.projectId!==r.id||t.push(a)}}return t}function Ie(r){return r.filter(e=>!!e).sort().at(-1)}function Kt(r){let e=xe(q(r.rootPath,".toolnet","runtime","opencode-status.json"));return!e||e.projectRoot&&e.projectRoot!==r.rootPath?null:e}function Ce(r){let e=Nt(r),o=[...e].sort((l,f)=>(l.lastLocalEventAt??l.updatedAt).localeCompare(f.lastLocalEventAt??f.updatedAt)).at(-1),n=Array.from(new Set(e.map(l=>l.agent))).sort(),s=e.reduce((l,f)=>l+Math.max(0,f.lastSequence-f.lastRemoteSequence),0),a=Kt(r),c=a?.state==="capture-failed",d=a?.state==="sync-failed",m;c?m="degraded":s>0||d?m="pending":e.length>0?m="healthy":m="unknown";let u=_e(r);return{ok:m!=="degraded",agents:n,sessions:e.length,latestAgent:o?.agent,latestSessionId:o?.nativeSessionId,lastCaptureAt:Ie(e.map(l=>l.lastLocalEventAt??l.updatedAt)),lastFlushAt:Ie(e.map(l=>l.lastRemoteAt)),pendingWal:s,currentTask:u?.currentTask?.title,currentFile:u?.activeFiles?.at(-1)??u?.filesTouched.at(-1),syncHealth:m,opencode:a?{state:a.state,reason:a.reason,timestamp:a.timestamp,error:a.error}:void 0}}function je(){return process.argv.includes("--json")}function Ut(r){let e=process.stdout.isTTY===!0&&process.env.TERM!=="dumb"&&process.env.NO_COLOR===void 0,t=(p,y)=>e?`\x1B[${p}m${y}\x1B[0m`:y,o=p=>t("38;5;82",p),n=p=>t("38;5;51",p),s=p=>t("38;5;214",p),a=p=>t("38;5;196",p),c=p=>t("38;5;255",p),d=p=>t("2",p),m=p=>t("1",p),u=d("\u2502"),l=d("\u251C");if(console.log(""),console.log(`${n("\u25C7")} ${m(c("ToolNet Memory Doctor"))}`),console.log(""),r.project&&console.log(`${o("\u25C6")} Project ${c(r.project)}`),console.log(u),r.storage){let p=typeof r.storage.provider=="string"?` \u2014 ${r.storage.provider}`:"";r.storage.ok?console.log(`${l} ${o("\u25C6")} Storage${p}`):console.log(`${l} ${a("\u2717")} Storage${p}`)}if(r.llm){let p=[];if(r.llm.provider&&p.push(r.llm.provider),r.llm.model&&p.push(r.llm.model),typeof r.llm.latencyMs=="number"&&p.push(`${r.llm.latencyMs}ms`),r.llm.ok)console.log(`${l} ${o("\u25C6")} LLM${p.length?` \u2014 ${p.join(" \xB7 ")}`:""}`);else{let y=r.llm.error?` \u2014 ${r.llm.error}`:"";console.log(`${l} ${a("\u2717")} LLM${y}`)}}if(r.embedding){let p=[];typeof r.embedding.mode=="string"&&p.push(r.embedding.mode),typeof r.embedding.dimensions=="number"&&p.push(`${r.embedding.dimensions}d`),r.embedding.ok?console.log(`${l} ${o("\u25C6")} Embedding${p.length?` \u2014 ${p.join(" \xB7 ")}`:""}`):console.log(`${l} ${a("\u2717")} Embedding`)}if((r.memory!==void 0||r.graphSymbols!==void 0||r.codeChunks!==void 0)&&(console.log(u),console.log(`${n("\u25C7")} ${c("Project data")}`),r.memory!==void 0&&console.log(`${l} ${d("Memories")} ${c(String(r.memory))}`),r.graphSymbols!==void 0&&console.log(`${l} ${d("Graph symbols")} ${c(String(r.graphSymbols))}`),r.graphEdges!==void 0&&console.log(`${l} ${d("Graph edges")} ${c(String(r.graphEdges))}`),r.memoryVectors!==void 0&&console.log(`${l} ${d("Memory vectors")} ${c(String(r.memoryVectors))}`),r.codeChunks!==void 0&&console.log(`${l} ${d("Code chunks")} ${c(String(r.codeChunks))}`),r.codeVectors!==void 0&&console.log(`${l} ${d("Code vectors")} ${c(String(r.codeVectors))}`),r.snapshots!==void 0&&console.log(`${l} ${d("Snapshots")} ${c(String(r.snapshots))}`)),r.capture){console.log(u),console.log(`${n("\u25C7")} ${c("Session continuity")}`),console.log(`${l} ${d("Agents")} ${c(r.capture.agents.length?r.capture.agents.join(", "):"none")}`),r.capture.latestAgent&&console.log(`${l} ${d("Latest agent")} ${c(String(r.capture.latestAgent))}`),r.capture.currentTask&&console.log(`${l} ${d("Current task")} ${c(r.capture.currentTask)}`),r.capture.currentFile&&console.log(`${l} ${d("Current file")} ${c(r.capture.currentFile)}`),r.capture.lastCaptureAt&&console.log(`${l} ${d("Last capture")} ${c(r.capture.lastCaptureAt)}`),r.capture.lastFlushAt&&console.log(`${l} ${d("Last flush")} ${c(r.capture.lastFlushAt)}`),console.log(`${l} ${d("Pending WAL")} ${c(String(r.capture.pendingWal))}`);let p=r.capture.syncHealth==="healthy"?o(r.capture.syncHealth):r.capture.syncHealth==="degraded"?a(r.capture.syncHealth):s(r.capture.syncHealth);console.log(`${l} ${d("Sync health")} ${p}`)}let f=r.config?.errors??[];if(f.length>0){console.log(u),console.log(`${a("\u2717")} ${c("Configuration required")}`);for(let p of f)console.log(`${l} ${a("\u2022")} ${p}`)}let Q=[...r.config?.warnings??[],...r.warnings??[]],I=[...new Set(Q)];if(I.length>0){console.log(u),console.log(`${s("\u25C7")} ${c("Warnings")}`);for(let p of I)console.log(`${l} ${s("!")} ${p}`)}console.log(u),r.ok?console.log(`${o("\u2514 \u25C6")} ${m(c("ToolNet Memory is ready"))}`):(console.log(`${a("\u2514 \u2717")} ${m(c("ToolNet Memory requires attention"))}`),f.length>0&&console.log(d(" Run: toolnet-memory setup"))),console.log("")}function Me(r){je()?console.log(JSON.stringify(r,null,2)):Ut(r)}async function Bt(){let r=we();if(!r.ok){let h={ok:!1,config:r,warnings:r.warnings};Me(h),process.exitCode=1;return}let e=se(),t=new x().detect(),o=fe(me({provider:e.storage.provider,r2:e.storage.r2,s3:e.storage.s3,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3}),n=new D(o,t.id,t.name,t.remote??t.name),s=Ee(),a=P(),c;try{let A=await ke({id:a.llm.provider,apiKey:a.llm.apiKey,baseUrl:a.llm.baseUrl,model:a.llm.model,accountId:a.llm.accountId}).healthCheck();c={ok:A.ok,provider:a.llm.provider,model:A.model??a.llm.model,latencyMs:A.latencyMs,error:A.ok?void 0:A.message}}catch(h){c={ok:!1,provider:a.llm.provider,model:a.llm.model,error:h instanceof Error?h.message:String(h)}}let d=await new J(o,s).run(),m=await new j(n).load(t.id),u=await new T(n).load(t.id),l=await new R(n).load(t.id),f=await new $(n).load(t.id),Q=await new L(n).load(t.id),I=await new W(n).list(t.id),p=Ce(t),y=p.syncHealth==="degraded"?[`Session capture degraded${p.opencode?.error?`: ${p.opencode.error}`:""}`]:[],ne={ok:d.ok&&!!c?.ok&&p.ok,project:t.name,storage:d.storage,llm:c,embedding:d.embedding,memory:m.length,graphSymbols:u?.symbols.length??0,graphEdges:u?.edges.length??0,memoryVectors:l?.records.length??0,codeChunks:f?.chunks.length??0,codeVectors:Q?.records.length??0,snapshots:I.length,capture:p,warnings:[...r.warnings,...y]};Me(ne),process.exitCode=ne.ok?0:1}Bt().catch(r=>{let e=r instanceof Error?r.message:String(r);je()?console.log(JSON.stringify({ok:!1,error:e},null,2)):(console.error(""),console.error("ToolNet Memory Doctor"),console.error("====================="),console.error(""),console.error(`\u2717 ${e}`),console.error("")),process.exitCode=1});