toolnet-memory 0.3.16 → 0.3.18

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.
Files changed (45) hide show
  1. package/.env.example +74 -0
  2. package/.release-target +1 -1
  3. package/README.md +157 -7
  4. package/bin/toolnet-memory +12 -0
  5. package/bundle/agy-hook.js +59 -59
  6. package/bundle/agy.js +58 -58
  7. package/bundle/api.js +15 -15
  8. package/bundle/audit.js +2 -0
  9. package/bundle/background-refresh.js +7 -7
  10. package/bundle/code-capabilities.js +2 -0
  11. package/bundle/codex.js +63 -63
  12. package/bundle/context-runtime.js +41 -41
  13. package/bundle/context.js +15 -15
  14. package/bundle/copilot-hook.js +73 -73
  15. package/bundle/cursor-hook.js +73 -73
  16. package/bundle/docker-healthcheck.js +3 -0
  17. package/bundle/doctor.js +4 -4
  18. package/bundle/full-index.js +20 -19
  19. package/bundle/gc.js +5 -3
  20. package/bundle/graph-cli.js +1 -1
  21. package/bundle/graph-index.js +5 -4
  22. package/bundle/graph-ui.js +4 -3
  23. package/bundle/grok-hook.js +52 -52
  24. package/bundle/handoff-refresh.js +2 -2
  25. package/bundle/help-cli.js +4 -4
  26. package/bundle/impact.js +5 -4
  27. package/bundle/incremental.js +5 -5
  28. package/bundle/init.js +35 -35
  29. package/bundle/kiro-hook.js +74 -74
  30. package/bundle/learner.js +8 -6
  31. package/bundle/mcp.js +19 -17
  32. package/bundle/memory-agent.js +4 -4
  33. package/bundle/memory-query.js +3 -3
  34. package/bundle/opencode.js +73 -73
  35. package/bundle/project-manual.js +7 -7
  36. package/bundle/public/index.html +1089 -2306
  37. package/bundle/runtime.js +16 -13
  38. package/bundle/semantic.js +6 -5
  39. package/bundle/service.js +11 -6
  40. package/bundle/snapshot.js +6 -4
  41. package/bundle/status-cli.js +4 -4
  42. package/bundle/toolnet-cli.js +49 -49
  43. package/bundle/work-continuity.js +7 -7
  44. package/package.json +26 -2
  45. package/release-manifest.json +57 -1
package/bundle/context.js CHANGED
@@ -1,8 +1,8 @@
1
- import{existsSync as Ze,readFileSync as Xe}from"node:fs";import{homedir as et}from"node:os";import{join as tt}from"node:path";function rt(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 nt(){let r=process.env.TOOLNET_GLOBAL_ENV??tt(et(),".config","toolnet-memory",".env");if(!Ze(r))return;let t=Xe(r,"utf8");for(let e of t.split(/\r?\n/)){let n=e.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let s=n.indexOf("=");if(s<=0)continue;let o=n.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=rt(n.slice(s+1)))}}nt();function I(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function R(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function ye(){return{memory:{autoCapture:I(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:I(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:I(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:I(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:R(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:R(process.env.MEMORY_RERANK_TOP,10),finalContext:R(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:R(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",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:I(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:R(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as dt}from"node:crypto";import{existsSync as L,mkdirSync as lt,readFileSync as pt,renameSync as ft,writeFileSync as mt}from"node:fs";import{basename as gt,dirname as F,join as T,parse as je,resolve as A}from"node:path";import{createHash as Se}from"node:crypto";import{spawnSync as st}from"node:child_process";var C="git-remote-v1",ot=new Set(["github.com","gitlab.com","bitbucket.org"]);function be(r,t){let e=t.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!e||e==="."||e===".."||e.split("/").some(n=>!n||n==="."||n==="..")?null:(ot.has(r)&&(e=e.toLowerCase()),e)}function it(r){let t;try{t=new URL(r)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(t.protocol))return null;let e=t.hostname.trim().toLowerCase();if(!e)return null;let n=t.protocol==="https:"&&t.port==="443"||t.protocol==="http:"&&t.port==="80"||t.protocol==="ssh:"&&t.port==="22",s=t.port&&!n?`${e}:${t.port}`:e,o=be(e,t.pathname);return o?`${s}/${o}`:null}function at(r){let t=r.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!t)return null;let e=t[1]?.trim().toLowerCase();if(!e||e.length===1)return null;let n=be(e,t[2]??"");return n?`${e}/${n}`:null}function he(r){let t=r.trim();return t?t.includes("://")?it(t):at(t):null}function ct(r){return Se("sha256").update(`${C}:${r}`).digest("hex")}function ve(r){return Se("sha256").update(`toolnet-project:${C}:${r}`).digest("hex").slice(0,16)}function ut(r){return r.split("/").filter(Boolean).at(-1)?.trim()||null}function te(r,t){let e=st("git",["-C",r,...t],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return e.error||e.status!==0?null:e.stdout?.trim()||null}function ke(r,t){let e=ut(r);return e?{scheme:C,canonicalRemote:r,fingerprint:ct(r),repositoryName:e,source:t}:null}function xe(r){let t=te(r,["remote","get-url","origin"]);if(t){let s=he(t);if(s)return ke(s,"origin")}let e=te(r,["remote"]);if(!e)return null;let n=new Set;for(let s of e.split(/\r?\n/u).map(o=>o.trim()).filter(Boolean)){let o=te(r,["remote","get-url",s]);if(!o)continue;let i=he(o);i&&n.add(i)}return n.size!==1?null:ke([...n][0],"unique-remote")}var Ae=".toolnet",yt="project.json";function ht(r){return dt("sha256").update(r).digest("hex").slice(0,16)}function M(r){return T(r,Ae,yt)}function Me(r){return L(M(r))}function we(r,t){let e=A(r),n=je(e).root;for(;;){if(Me(e))return e;if(e===n||t&&e===A(t))break;let s=F(e);if(s===e)break;e=s}return null}function re(r){let t=A(r),e=je(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(o=>L(T(t,o))))return t;if(t===e)break;let s=F(t);if(s===t)break;t=s}return A(r)}function W(r){let t;try{t=JSON.parse(pt(r,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${r}: ${s instanceof Error?s.message:String(s)}`)}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 n=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:F(F(r)),createdAt:typeof e.createdAt=="string"?e.createdAt:n,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:n,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 D(r,t){let e=T(r,Ae);lt(e,{recursive:!0});let n=M(r),s=`${n}.tmp-${process.pid}`;mt(s,JSON.stringify(t,null,2)+`
3
- `,{encoding:"utf8",mode:384}),ft(s,n)}function j(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}}function ne(r){return{version:1,scheme:C,canonicalRemote:r.canonicalRemote,fingerprint:r.fingerprint,repositoryName:r.repositoryName}}function kt(r){let t=r.metadata?.toolnetIdentity;if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.fingerprint=="string"?e.fingerprint:null}var V=class{adopt(t,e){let n=re(A(t));if(!e.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!e.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!e.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(Me(n)){let d=W(M(n));if(d.id!==e.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${d.id}`,`requested=${e.id}`].join(" "));return j(d,n)}let s=new Date().toISOString(),o={...e.metadata};e.gitIdentity&&(o.toolnetIdentity=ne(e.gitIdentity));let i={version:1,id:e.id.trim(),name:e.name.trim(),remote:e.remote.trim(),rootPath:n,createdAt:e.createdAt??s,updatedAt:s,graphVersion:e.graphVersion??0,memoryVersion:e.memoryVersion??0,metadata:Object.keys(o).length?o:void 0};return D(n,i),j(i,n)}recordGitIdentity(t,e,n={}){let s=this.requireExisting(t),o=M(s.rootPath),i=W(o),d=kt(i);if(d&&d!==e.fingerprint&&!n.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${d}`,`current=${e.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let c=i.metadata?.toolnetIdentity;return c&&typeof c=="object"&&!Array.isArray(c)&&c.fingerprint===e.fingerprint||(i.metadata={...i.metadata,toolnetIdentity:ne(e)},i.updatedAt=new Date().toISOString(),D(s.rootPath,i)),j(i,s.rootPath)}findExisting(t=process.cwd()){let e=A(t),n=re(e),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>L(T(n,c))),i=we(e,o?n:void 0);if(!i)return null;let d=W(M(i));return j(d,i)}requireExisting(t=process.cwd()){let e=this.findExisting(t);if(!e)throw new Error("PROJECT_NOT_INITIALIZED");return e}detect(t=process.cwd()){let e=A(t),n=re(e),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>L(T(n,u))),i=we(e,o?n:void 0);if(i){let u=M(i),k=W(u);return k.rootPath!==i&&(k.rootPath=i,k.updatedAt=new Date().toISOString(),D(i,k)),j(k,i)}let d=new Date().toISOString(),c=gt(n),p=xe(n),g={version:1,id:p?ve(p.canonicalRemote):ht(n),name:c,remote:p?.repositoryName??c,rootPath:n,createdAt:d,updatedAt:d,graphVersion:0,memoryVersion:0,metadata:p?{toolnetIdentity:ne(p)}:void 0};return D(n,g),j(g,n)}};var St=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_-]{20,}\b/g,confidence:"exact"},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g,confidence:"exact"},{type:"aws_access_key",regex:/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,confidence:"exact"},{type:"github_token",regex:/\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{40,255})\b/g,confidence:"exact"},{type:"stripe_secret_key",regex:/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g,confidence:"exact"},{type:"google_api_key",regex:/\bAIza[A-Za-z0-9_-]{30,}\b/g,confidence:"exact"},{type:"slack_token",regex:/\bxox[baprs]-[A-Za-z0-9-]{16,}\b/g,confidence:"exact"},{type:"npm_token",regex:/\bnpm_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/gi,confidence:"high"},{type:"jwt",regex:/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,confidence:"exact"},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,confidence:"exact"},{type:"password_assignment",regex:/\b(?:password|passwd|pwd)\s*[:=]\s*["']?[^"' \t\r\n]{6,}["']?/gi,confidence:"high"},{type:"secret_assignment",regex:/\b(?:secret|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key)\s*[:=]\s*["']?[^"' \t\r\n]{8,}["']?/gi,confidence:"high"},{type:"cookie",regex:/\b(?:cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi,confidence:"high"},{type:"url_credentials",regex:/\bhttps?:\/\/[^:/@\s]+:[^/@\s]{4,}@[^/\s]+/gi,confidence:"high"}],bt=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function Pe(r){return r.normalize("NFKC").trim().toLowerCase()}function vt(r){if(r.length===0)return 0;let t=new Map;for(let n of r)t.set(n,(t.get(n)??0)+1);let e=0;for(let n of t.values()){let s=n/r.length;e-=s*Math.log2(s)}return e}function xt(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function wt(r,t,e){let n=r.slice(Math.max(0,t-48),t),s=r.slice(e,Math.min(r.length,e+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${n} ${s}`)}function jt(r,t){return r.start<t.end&&t.start<r.end}function Ie(r){return r.sort((t,e)=>t.start!==e.start?t.start-e.start:e.end-e.start-(t.end-t.start))}var z=class{allowValues=new Set;enableEntropyHeuristic;constructor(t={}){for(let e of t.allowValues??[]){let n=Pe(e);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=t.enableEntropyHeuristic??!0}scan(t){let e=[];for(let o of St){let i=new RegExp(o.regex.source,o.regex.flags);for(let d of t.matchAll(i))d.index===void 0||!d[0]||this.allowed(d[0])||e.push({type:o.type,value:d[0],start:d.index,end:d.index+d[0].length,confidence:o.confidence})}this.enableEntropyHeuristic&&e.push(...this.entropyMatches(t));let n=Ie(e),s=[];for(let o of n)s.some(i=>jt(i,o))||s.push(o);return Ie(s)}hasSecrets(t){return this.scan(t).length>0}allowed(t){let e=Pe(t);return bt.has(e)?!0:this.allowValues.has(e)}entropyMatches(t){let e=[],n=/[A-Za-z0-9_+/=-]{32,160}/g;for(let s of t.matchAll(n)){if(s.index===void 0||!s[0])continue;let o=s[0];this.allowed(o)||xt(o)||!/[A-Za-z]/u.test(o)||!/[0-9]/u.test(o)||wt(t,s.index,s.index+o.length)&&(vt(o)<3.7||e.push({type:"high_entropy_secret",value:o,start:s.index,end:s.index+o.length,confidence:"heuristic"}))}return e}};var H=class{scanner;constructor(t={}){this.scanner=new z(t)}sanitize(t){let e=this.scanner.scan(t);if(e.length===0)return{text:t,redacted:0,secretTypes:[]};let n=t,s=[...e].sort((i,d)=>d.start-i.start),o=new Set;for(let i of s)o.add(i.type),n=n.slice(0,i.start)+`[REDACTED:${i.type}]`+n.slice(i.end);return{text:n,redacted:e.length,secretTypes:[...o].sort()}}sanitizeValue(t){if(typeof t=="string")return this.sanitize(t).text;if(Array.isArray(t))return t.map(e=>this.sanitizeValue(e));if(t&&typeof t=="object"){let e={};for(let[n,s]of Object.entries(t)){let o=n.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(o.includes("password")||o.includes("passwd")||o==="pwd"||o.includes("secret")||o.includes("token")||o.includes("cookie")||o.includes("authorization")||o.includes("apikey")||o.includes("accesskey")||o.includes("privatekey")||o.includes("clientsecret")||o.includes("credential")){e[n]="[REDACTED]";continue}e[n]=this.sanitizeValue(s)}return e}return t}};var Zr=new H;import{homedir as Gt}from"node:os";import{join as Ut}from"node:path";import{DeleteObjectCommand as Mt,GetObjectCommand as Pt,HeadObjectCommand as It,ListObjectsV2Command as Rt,PutObjectCommand as Ct,S3Client as Tt}from"@aws-sdk/client-s3";import{getSignedUrl as Et}from"@aws-sdk/s3-request-presigner";var B=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Tt({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,n="application/octet-stream"){let s=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Ct({Bucket:this.bucket,Key:t,Body:s,ContentType:n}))}async get(t){let e=await Et(this.client,new Pt({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(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 It({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=[],n;do{let s=await this.client.send(new Rt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&e.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return e}};import{access as Re,mkdir as Ot,readFile as _t,readdir as Nt,rm as $t,stat as Ce,writeFile as Kt}from"node:fs/promises";import{dirname as Wt,join as Dt,relative as Te,resolve as Lt}from"node:path";var E=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return Lt(this.root,e)}async put(t,e){let n=this.path(t);await Ot(Wt(n),{recursive:!0}),await Kt(n,e)}async get(t){try{return await _t(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 Re(this.path(t)),!0}catch{return!1}}async delete(t){await $t(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),n=[];try{await Re(e)}catch{return n}let s=async i=>{let d=await Nt(i,{withFileTypes:!0});for(let c of d){let p=Dt(i,c.name);if(c.isDirectory()){await s(p);continue}let g=await Ce(p);n.push({key:Te(this.root,p),size:g.size,updatedAt:g.mtime.toISOString()})}},o=await Ce(e);return o.isDirectory()?await s(e):n.push({key:Te(this.root,e),size:o.size,updatedAt:o.mtime.toISOString()}),n}};import{DeleteObjectCommand as Ft,GetObjectCommand as Vt,HeadObjectCommand as zt,ListObjectsV2Command as Ht,PutObjectCommand as Bt,S3Client as Jt}from"@aws-sdk/client-s3";import{getSignedUrl as qt}from"@aws-sdk/s3-request-presigner";var O=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new Jt({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,n="application/octet-stream"){let s=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Bt({Bucket:this.bucket,Key:t,Body:s,ContentType:n}))}async get(t){let e=await qt(this.client,new Vt({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`${this.name} download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(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 zt({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 Ft({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let s=await this.client.send(new Ht({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&e.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return e}};function se(r,t){return console.warn(t),new E(r)}function Ee(r){let t=r.localRoot??Ut(Gt(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new O({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):se(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 O({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):se(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 B({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):se(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new E(t)}function Yt(r){return new Promise(t=>setTimeout(t,r))}async function Oe(r,t={}){let e=Math.max(1,t.attempts??3),n=t.baseDelayMs??150,s=t.maxDelayMs??2e3,o;for(let i=1;i<=e;i++)try{return await r()}catch(d){if(o=d,i>=e)break;let c=Math.min(s,n*2**(i-1)),p=Math.floor(Math.random()*Math.max(1,c*.2));await Yt(c+p)}throw o}var Qt=new Set(["put","get","getText","delete","list"]);function _e(r,t={}){return new Proxy(r,{get(e,n){let s=Reflect.get(e,n,e);return typeof s!="function"?s:Qt.has(n)?(...o)=>Oe(()=>Promise.resolve(s.apply(e,o)),t):s.bind(e)}})}function Ne(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 $e(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 n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let s=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:t))}var J=class{constructor(t,e,n,s){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=n,this.folder=Ne(s??n),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(),n=e,s=await this.provider.getText(t);if(s){let i;try{i=JSON.parse(s)}catch(d){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${d instanceof Error?d.message:String(d)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(n=i.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:e};await this.provider.put(t,JSON.stringify(o,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=$e(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,n){return await this.ensureRegistered(),this.provider.put(this.key(t),e,n)}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 nr}from"node:path";import{createHash as Zt}from"node:crypto";import{dirname as Xt}from"node:path";import{mkdirSync as er,readFileSync as Vs,renameSync as tr,writeFileSync as rr}from"node:fs";function b(r){return Zt("sha256").update(r).digest("hex")}function P(r,t){er(Xt(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;rr(e,JSON.stringify(t,null,2)+`
5
- `,{encoding:"utf8",mode:384}),tr(e,r)}function Ke(r,t){let e=r.trim(),n=e.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),s=b(e).slice(0,12);if(!n||n==="."||n==="..")return`${t}--${s}`;let o=n.slice(0,100);return o===e&&e.length<=100?o:`${o.slice(0,85)}--${s}`}function We(r,t,e){let n=t.trim(),s=e.trim();if(!n)throw new Error("Session agent is required");if(!s)throw new Error("Native session ID is required");let o=Ke(n.toLowerCase(),"agent"),i=Ke(s,"session");return{projectId:r.id,projectName:r.name,projectRoot:r.rootPath,agent:n,nativeSessionId:s,sessionKey:`${n}:${s}`,remotePrefix:["projects",r.id,"runtime","sources",o,i].join("/"),localDirectory:nr(r.rootPath,".toolnet","runtime","sources",o,i)}}import{existsSync as Fe,mkdirSync as sr,readFileSync as or,statSync as De,writeFileSync as ir}from"node:fs";import{dirname as ar,join as cr}from"node:path";var Le=64*1024,ur=`# ToolNet Project Operating Manual
1
+ import{existsSync as kt,readFileSync as St}from"node:fs";import{homedir as bt}from"node:os";import{join as wt}from"node:path";function xt(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 vt(){let r=process.env.TOOLNET_GLOBAL_ENV??wt(bt(),".config","toolnet-memory",".env");if(!kt(r))return;let t=St(r,"utf8");for(let e of t.split(/\r?\n/)){let n=e.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let s=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=xt(n.slice(o+1)))}}vt();function M(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function T(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function Ae(){return{memory:{autoCapture:M(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:M(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:M(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:M(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:T(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:T(process.env.MEMORY_RERANK_TOP,10),finalContext:T(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:T(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:M(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:T(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Mt}from"node:crypto";import{existsSync as H,mkdirSync as Tt,readFileSync as Ct,renameSync as Ot,writeFileSync as _t}from"node:fs";import{basename as Nt,dirname as U,join as O,parse as _e,resolve as A}from"node:path";import{createHash as Ie}from"node:crypto";import{spawnSync as Et}from"node:child_process";var C="git-remote-v1",jt=new Set(["github.com","gitlab.com","bitbucket.org"]);function Me(r,t){let e=t.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!e||e==="."||e===".."||e.split("/").some(n=>!n||n==="."||n==="..")?null:(jt.has(r)&&(e=e.toLowerCase()),e)}function At(r){let t;try{t=new URL(r)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(t.protocol))return null;let e=t.hostname.trim().toLowerCase();if(!e)return null;let n=t.protocol==="https:"&&t.port==="443"||t.protocol==="http:"&&t.port==="80"||t.protocol==="ssh:"&&t.port==="22",o=t.port&&!n?`${e}:${t.port}`:e,s=Me(e,t.pathname);return s?`${o}/${s}`:null}function Pt(r){let t=r.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!t)return null;let e=t[1]?.trim().toLowerCase();if(!e||e.length===1)return null;let n=Me(e,t[2]??"");return n?`${e}/${n}`:null}function Pe(r){let t=r.trim();return t?t.includes("://")?At(t):Pt(t):null}function Rt(r){return Ie("sha256").update(`${C}:${r}`).digest("hex")}function Te(r){return Ie("sha256").update(`toolnet-project:${C}:${r}`).digest("hex").slice(0,16)}function It(r){return r.split("/").filter(Boolean).at(-1)?.trim()||null}function ie(r,t){let e=Et("git",["-C",r,...t],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return e.error||e.status!==0?null:e.stdout?.trim()||null}function Re(r,t){let e=It(r);return e?{scheme:C,canonicalRemote:r,fingerprint:Rt(r),repositoryName:e,source:t}:null}function Ce(r){let t=ie(r,["remote","get-url","origin"]);if(t){let o=Pe(t);if(o)return Re(o,"origin")}let e=ie(r,["remote"]);if(!e)return null;let n=new Set;for(let o of e.split(/\r?\n/u).map(s=>s.trim()).filter(Boolean)){let s=ie(r,["remote","get-url",o]);if(!s)continue;let i=Pe(s);i&&n.add(i)}return n.size!==1?null:Re([...n][0],"unique-remote")}var Ne=".toolnet",$t="project.json";function Kt(r){return Mt("sha256").update(r).digest("hex").slice(0,16)}function R(r){return O(r,Ne,$t)}function $e(r){return H(R(r))}function Oe(r,t){let e=A(r),n=_e(e).root;for(;;){if($e(e))return e;if(e===n||t&&e===A(t))break;let o=U(e);if(o===e)break;e=o}return null}function ae(r){let t=A(r),e=_e(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>H(O(t,s))))return t;if(t===e)break;let o=U(t);if(o===t)break;t=o}return A(r)}function B(r){let t;try{t=JSON.parse(Ct(r,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${r}: ${o instanceof Error?o.message:String(o)}`)}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 n=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:U(U(r)),createdAt:typeof e.createdAt=="string"?e.createdAt:n,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:n,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 z(r,t){let e=O(r,Ne);Tt(e,{recursive:!0});let n=R(r),o=`${n}.tmp-${process.pid}`;_t(o,JSON.stringify(t,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Ot(o,n)}function j(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}}function ce(r){return{version:1,scheme:C,canonicalRemote:r.canonicalRemote,fingerprint:r.fingerprint,repositoryName:r.repositoryName}}function Lt(r){let t=r.metadata?.toolnetIdentity;if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.fingerprint=="string"?e.fingerprint:null}var Y=class{adopt(t,e){let n=ae(A(t));if(!e.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!e.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!e.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if($e(n)){let d=B(R(n));if(d.id!==e.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${d.id}`,`requested=${e.id}`].join(" "));return j(d,n)}let o=new Date().toISOString(),s={...e.metadata};e.gitIdentity&&(s.toolnetIdentity=ce(e.gitIdentity));let i={version:1,id:e.id.trim(),name:e.name.trim(),remote:e.remote.trim(),rootPath:n,createdAt:e.createdAt??o,updatedAt:o,graphVersion:e.graphVersion??0,memoryVersion:e.memoryVersion??0,metadata:Object.keys(s).length?s:void 0};return z(n,i),j(i,n)}recordGitIdentity(t,e,n={}){let o=this.requireExisting(t),s=R(o.rootPath),i=B(s),d=Lt(i);if(d&&d!==e.fingerprint&&!n.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${d}`,`current=${e.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let c=i.metadata?.toolnetIdentity;return c&&typeof c=="object"&&!Array.isArray(c)&&c.fingerprint===e.fingerprint||(i.metadata={...i.metadata,toolnetIdentity:ce(e)},i.updatedAt=new Date().toISOString(),z(o.rootPath,i)),j(i,o.rootPath)}findExisting(t=process.cwd()){let e=A(t),n=ae(e),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>H(O(n,c))),i=Oe(e,s?n:void 0);if(!i)return null;let d=B(R(i));return j(d,i)}requireExisting(t=process.cwd()){let e=this.findExisting(t);if(!e)throw new Error("PROJECT_NOT_INITIALIZED");return e}detect(t=process.cwd()){let e=A(t),n=ae(e),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>H(O(n,u))),i=Oe(e,s?n:void 0);if(i){let u=R(i),S=B(u);return S.rootPath!==i&&(S.rootPath=i,S.updatedAt=new Date().toISOString(),z(i,S)),j(S,i)}let d=new Date().toISOString(),c=Nt(n),l=Ce(n),g={version:1,id:l?Te(l.canonicalRemote):Kt(n),name:c,remote:l?.repositoryName??c,rootPath:n,createdAt:d,updatedAt:d,graphVersion:0,memoryVersion:0,metadata:l?{toolnetIdentity:ce(l)}:void 0};return z(n,g),j(g,n)}};var Dt=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_-]{20,}\b/g,confidence:"exact"},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g,confidence:"exact"},{type:"aws_access_key",regex:/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,confidence:"exact"},{type:"github_token",regex:/\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{40,255})\b/g,confidence:"exact"},{type:"stripe_secret_key",regex:/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g,confidence:"exact"},{type:"google_api_key",regex:/\bAIza[A-Za-z0-9_-]{30,}\b/g,confidence:"exact"},{type:"slack_token",regex:/\bxox[baprs]-[A-Za-z0-9-]{16,}\b/g,confidence:"exact"},{type:"npm_token",regex:/\bnpm_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/gi,confidence:"high"},{type:"jwt",regex:/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,confidence:"exact"},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,confidence:"exact"},{type:"password_assignment",regex:/\b(?:password|passwd|pwd)\s*[:=]\s*["']?[^"' \t\r\n]{6,}["']?/gi,confidence:"high"},{type:"secret_assignment",regex:/\b(?:secret|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key)\s*[:=]\s*["']?[^"' \t\r\n]{8,}["']?/gi,confidence:"high"},{type:"cookie",regex:/\b(?:cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi,confidence:"high"},{type:"url_credentials",regex:/\bhttps?:\/\/[^:/@\s]+:[^/@\s]{4,}@[^/\s]+/gi,confidence:"high"}],Wt=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function Ke(r){return r.normalize("NFKC").trim().toLowerCase()}function Ft(r){if(r.length===0)return 0;let t=new Map;for(let n of r)t.set(n,(t.get(n)??0)+1);let e=0;for(let n of t.values()){let o=n/r.length;e-=o*Math.log2(o)}return e}function Vt(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function Bt(r,t,e){let n=r.slice(Math.max(0,t-48),t),o=r.slice(e,Math.min(r.length,e+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${n} ${o}`)}function zt(r,t){return r.start<t.end&&t.start<r.end}function Le(r){return r.sort((t,e)=>t.start!==e.start?t.start-e.start:e.end-e.start-(t.end-t.start))}var J=class{allowValues=new Set;enableEntropyHeuristic;constructor(t={}){for(let e of t.allowValues??[]){let n=Ke(e);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=t.enableEntropyHeuristic??!0}scan(t){let e=[];for(let s of Dt){let i=new RegExp(s.regex.source,s.regex.flags);for(let d of t.matchAll(i))d.index===void 0||!d[0]||this.allowed(d[0])||e.push({type:s.type,value:d[0],start:d.index,end:d.index+d[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&e.push(...this.entropyMatches(t));let n=Le(e),o=[];for(let s of n)o.some(i=>zt(i,s))||o.push(s);return Le(o)}hasSecrets(t){return this.scan(t).length>0}allowed(t){let e=Ke(t);return Wt.has(e)?!0:this.allowValues.has(e)}entropyMatches(t){let e=[],n=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of t.matchAll(n)){if(o.index===void 0||!o[0])continue;let s=o[0];this.allowed(s)||Vt(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||Bt(t,o.index,o.index+s.length)&&(Ft(s)<3.7||e.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return e}};var G=class{scanner;constructor(t={}){this.scanner=new J(t)}sanitize(t){let e=this.scanner.scan(t);if(e.length===0)return{text:t,redacted:0,secretTypes:[]};let n=t,o=[...e].sort((i,d)=>d.start-i.start),s=new Set;for(let i of o)s.add(i.type),n=n.slice(0,i.start)+`[REDACTED:${i.type}]`+n.slice(i.end);return{text:n,redacted:e.length,secretTypes:[...s].sort()}}sanitizeValue(t){if(typeof t=="string")return this.sanitize(t).text;if(Array.isArray(t))return t.map(e=>this.sanitizeValue(e));if(t&&typeof t=="object"){let e={};for(let[n,o]of Object.entries(t)){let s=n.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(s.includes("password")||s.includes("passwd")||s==="pwd"||s.includes("secret")||s.includes("token")||s.includes("cookie")||s.includes("authorization")||s.includes("apikey")||s.includes("accesskey")||s.includes("privatekey")||s.includes("clientsecret")||s.includes("credential")){e[n]="[REDACTED]";continue}e[n]=this.sanitizeValue(o)}return e}return t}};var Pn=new G;import{homedir as xr}from"node:os";import{join as vr}from"node:path";import{DeleteObjectCommand as Ut,GetObjectCommand as Yt,HeadObjectCommand as Jt,ListObjectsV2Command as Gt,PutObjectCommand as qt,S3Client as Qt}from"@aws-sdk/client-s3";import{getSignedUrl as Zt}from"@aws-sdk/s3-request-presigner";var q=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Qt({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,n="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new qt({Bucket:this.bucket,Key:t,Body:o,ContentType:n}))}async get(t){let e=await Zt(this.client,new Yt({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(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 Jt({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 Ut({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let o=await this.client.send(new Gt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let s of o.Contents??[])s.Key&&e.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return e}};import{access as De,mkdir as Xt,readFile as er,readdir as tr,rm as rr,stat as We,writeFile as nr}from"node:fs/promises";import{dirname as or,join as sr,relative as Fe,resolve as ir}from"node:path";var _=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return ir(this.root,e)}async put(t,e){let n=this.path(t);await Xt(or(n),{recursive:!0}),await nr(n,e)}async get(t){try{return await er(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 rr(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),n=[];try{await De(e)}catch{return n}let o=async i=>{let d=await tr(i,{withFileTypes:!0});for(let c of d){let l=sr(i,c.name);if(c.isDirectory()){await o(l);continue}let g=await We(l);n.push({key:Fe(this.root,l),size:g.size,updatedAt:g.mtime.toISOString()})}},s=await We(e);return s.isDirectory()?await o(e):n.push({key:Fe(this.root,e),size:s.size,updatedAt:s.mtime.toISOString()}),n}};import{DeleteObjectCommand as ar,GetObjectCommand as cr,HeadObjectCommand as ur,ListObjectsV2Command as dr,PutObjectCommand as lr,S3Client as pr}from"@aws-sdk/client-s3";import{getSignedUrl as fr}from"@aws-sdk/s3-request-presigner";var N=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new pr({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,n="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new lr({Bucket:this.bucket,Key:t,Body:o,ContentType:n}))}async get(t){let e=await fr(this.client,new cr({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`${this.name} download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(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 ur({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 ar({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let o=await this.client.send(new dr({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let s of o.Contents??[])s.Key&&e.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return e}};import{createCipheriv as mr,createDecipheriv as gr,createHash as yr,randomBytes as hr,timingSafeEqual as kr}from"node:crypto";import{readFileSync as Sr}from"node:fs";var P=Buffer.from("TNMEME01","ascii"),Be=1,$=8,K=12,ue=16,ze=P.length+1+$+K+ue,br="toolnet-memory:remote-encryption:v1:",He="aes-256-gcm",de=32,y=class extends Error{constructor(e,n){super(n);this.code=e;this.name="RemoteEncryptionError"}code};function wr(r){return r?["1","true","yes","on","enabled"].includes(r.trim().toLowerCase()):!1}function le(r=process.env){return wr(r.TOOLNET_REMOTE_ENCRYPTION)}function Ve(r){let t=r.trim();if(!t)throw new y("REMOTE_ENCRYPTION_KEY_EMPTY","Remote encryption key is empty.");let e;if(t.startsWith("hex:")){let n=t.slice(4);if(!/^[0-9a-f]{64}$/iu.test(n))throw new y("REMOTE_ENCRYPTION_KEY_INVALID","hex: remote encryption key must contain exactly 64 hexadecimal characters.");e=Buffer.from(n,"hex")}else if(/^[0-9a-f]{64}$/iu.test(t))e=Buffer.from(t,"hex");else{let n=t.startsWith("base64:")?t.slice(7):t;if(!/^[A-Za-z0-9+/_-]+={0,2}$/u.test(n))throw new y("REMOTE_ENCRYPTION_KEY_INVALID","Remote encryption key must be 32 raw bytes encoded as hexadecimal or base64.");e=Buffer.from(n,n.includes("-")||n.includes("_")?"base64url":"base64")}if(e.length!==de)throw new y("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${de} bytes.`);return e}function Ue(r=process.env){let t=r.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),e=r.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(t&&e)throw new y("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(t)return Ve(t);if(e){let n;try{n=Sr(e,"utf8")}catch(o){throw new y("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${e}.`,o instanceof Error?o.message:String(o)].join(" "))}return Ve(n)}}function Ye(r){return yr("sha256").update(r).digest().subarray(0,$)}function Je(r){return Buffer.from(`${br}${r}`,"utf8")}function pe(r){return r.byteLength<P.length?!1:Buffer.from(r).subarray(0,P.length).equals(P)}function Ge(r,t,e){if(e.byteLength!==de)throw new y("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH","AES-256-GCM requires a 32-byte key.");let n=typeof t=="string"?Buffer.from(t,"utf8"):Buffer.from(t),o=hr(K),s=mr(He,e,o);s.setAAD(Je(r));let i=Buffer.concat([s.update(n),s.final()]),d=s.getAuthTag(),c=Buffer.alloc(ze),l=0;return P.copy(c,l),l+=P.length,c.writeUInt8(Be,l),l+=1,Ye(e).copy(c,l),l+=$,o.copy(c,l),l+=K,d.copy(c,l),Buffer.concat([c,i])}function qe(r,t,e){let n=Buffer.from(t);if(!pe(n))throw new y("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(n.length<ze)throw new y("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let o=P.length,s=n.readUInt8(o);if(o+=1,s!==Be)throw new y("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${s}.`);let i=n.subarray(o,o+$);o+=$;let d=Ye(e);if(!kr(i,d))throw new y("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let c=n.subarray(o,o+K);o+=K;let l=n.subarray(o,o+ue);o+=ue;let g=n.subarray(o),u=gr(He,e,c);u.setAAD(Je(r)),u.setAuthTag(l);try{return Buffer.concat([u.update(g),u.final()])}catch{throw new y("REMOTE_ENCRYPTION_AUTH_FAILED","Encrypted remote object failed AES-GCM authentication.")}}var fe=class{constructor(t,e){this.inner=t;this.options=e;if(this.name=t.name,e.enabled&&!e.key)throw new y("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote client-side encryption is enabled but no encryption key is configured.")}inner;options;name;async put(t,e,n){if(!this.options.enabled){await this.inner.put(t,e,n);return}let o=this.options.key;if(!o)throw new y("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let s=Ge(t,e,o);await this.inner.put(t,s,"application/octet-stream")}async get(t){let e=await this.inner.get(t);if(!e)return null;if(!pe(e))return e;if(!this.options.enabled)throw new y("REMOTE_ENCRYPTION_REQUIRED",["Remote object is client-side encrypted.","Enable TOOLNET_REMOTE_ENCRYPTION and configure the matching key."].join(" "));let n=this.options.key;if(!n)throw new y("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return qe(t,e,n)}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){return this.inner.exists(t)}async delete(t){await this.inner.delete(t)}async list(t=""){return this.inner.list(t)}};function Qe(r,t=process.env){if(r.name==="local")return le(t)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),r;let e=le(t),n=e?Ue(t):void 0;return new fe(r,{enabled:e,key:n})}function L(r){return Qe(r)}function me(r,t){return console.warn(t),L(new _(r))}function Ze(r){let t=r.localRoot??vr(xr(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?L(new N({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):me(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let e=r.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?L(new N({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):me(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?L(new q({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):me(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return L(new _(t))}function Er(r){return new Promise(t=>setTimeout(t,r))}async function Xe(r,t={}){let e=Math.max(1,t.attempts??3),n=t.baseDelayMs??150,o=t.maxDelayMs??2e3,s;for(let i=1;i<=e;i++)try{return await r()}catch(d){if(s=d,i>=e)break;let c=Math.min(o,n*2**(i-1)),l=Math.floor(Math.random()*Math.max(1,c*.2));await Er(c+l)}throw s}var jr=new Set(["put","get","getText","delete","list"]);function et(r,t={}){return new Proxy(r,{get(e,n){let o=Reflect.get(e,n,e);return typeof o!="function"?o:jr.has(n)?(...s)=>Xe(()=>Promise.resolve(o.apply(e,s)),t):o.bind(e)}})}function tt(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 rt(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 n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let o=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:t))}var Q=class{constructor(t,e,n,o){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=n,this.folder=tt(o??n),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(),n=e,o=await this.provider.getText(t);if(o){let i;try{i=JSON.parse(o)}catch(d){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${d instanceof Error?d.message:String(d)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(n=i.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:e};await this.provider.put(t,JSON.stringify(s,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=rt(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,n){return await this.ensureRegistered(),this.provider.put(this.key(t),e,n)}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 Tr}from"node:path";import{createHash as Ar}from"node:crypto";import{dirname as Pr}from"node:path";import{mkdirSync as Rr,readFileSync as Es,renameSync as Ir,writeFileSync as Mr}from"node:fs";function w(r){return Ar("sha256").update(r).digest("hex")}function I(r,t){Rr(Pr(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;Mr(e,JSON.stringify(t,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),Ir(e,r)}function nt(r,t){let e=r.trim(),n=e.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),o=w(e).slice(0,12);if(!n||n==="."||n==="..")return`${t}--${o}`;let s=n.slice(0,100);return s===e&&e.length<=100?s:`${s.slice(0,85)}--${o}`}function ot(r,t,e){let n=t.trim(),o=e.trim();if(!n)throw new Error("Session agent is required");if(!o)throw new Error("Native session ID is required");let s=nt(n.toLowerCase(),"agent"),i=nt(o,"session");return{projectId:r.id,projectName:r.name,projectRoot:r.rootPath,agent:n,nativeSessionId:o,sessionKey:`${n}:${o}`,remotePrefix:["projects",r.id,"runtime","sources",s,i].join("/"),localDirectory:Tr(r.rootPath,".toolnet","runtime","sources",s,i)}}import{existsSync as at,mkdirSync as Cr,readFileSync as Or,statSync as st,writeFileSync as _r}from"node:fs";import{dirname as Nr,join as $r}from"node:path";var it=64*1024,Kr=`# ToolNet Project Operating Manual
6
6
 
7
7
  This file contains persistent instructions for AI agents working on this project.
8
8
 
@@ -45,19 +45,19 @@ Things an AI agent should always remember.
45
45
  <!--
46
46
  - [advisory] Prefer small focused files.
47
47
  -->
48
- `;function q(r){return cr(r.rootPath,".toolnet","PROJECT.md")}function dr(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function lr(r){let t=[],e=new Set,n=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,s;for(;s=n.exec(r);){let o=s[1].toLowerCase(),i=dr(s[2]);if(!i)continue;let d=`${o}:${i.toLowerCase()}`;e.has(d)||(e.add(d),t.push({id:b(d).slice(0,24),mode:o,text:i,source:"manual"}))}return t}function pr(r){let t=q(r);return Fe(t)||(sr(ar(t),{recursive:!0}),ir(t,ur,{encoding:"utf8",mode:384})),t}function G(r,t=!1){let e=t?pr(r):q(r);if(!Fe(e))return null;if(De(e).size>Le)throw new Error(`PROJECT.md exceeds ${Le} bytes`);let s=or(e,"utf8");return{path:e,content:s,digest:b(s),rules:lr(s),bytes:Buffer.byteLength(s,"utf8"),updatedAt:new Date(De(e).mtimeMs).toISOString()}}import{existsSync as wr,mkdirSync as jr}from"node:fs";import{join as ie}from"node:path";import{join as Ve}from"node:path";import{mkdirSync as fr}from"node:fs";function He(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function v(r,t=20){let e=[],n=new Set;for(let s of r.slice().reverse()){let o=He(s);if(!(!o||n.has(o))&&(n.add(o),e.push(s),e.length>=t))break}return e.reverse()}function mr(r,t=20){let e=new Map;for(let n of r){let s=`${n.kind}|${He(n.command)}`;e.delete(s),e.set(s,n)}return Array.from(e.values()).slice(-t)}function gr(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 ze(r,t){let e=t.status??r?.status??"pending",n=e;r&&(r.status==="completed"&&e!=="completed"?n="completed":e==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(n=r.status));let s=r&&gr(t)?r.title:t.text;return{id:r?.id??b(t.key).slice(0,24),title:s,status:n,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 Be(r,t){let e=`projects/${r.id}/work/observations/`,n=await t.list(e),s=[];for(let o of n.filter(i=>i.key.endsWith(".json")).sort((i,d)=>i.key.localeCompare(d.key))){let i=await t.getText(o.key);if(i)try{let d=JSON.parse(i);d.version===1&&Array.isArray(d.observations)&&s.push(d)}catch{}}return s}async function oe(r,t){let n=(await Be(r,t)).flatMap(a=>a.observations).sort((a,S)=>{let me=a.occurredAt.localeCompare(S.occurredAt);if(me!==0)return me;let ge=a.sequence-S.sequence;return ge!==0?ge:a.id.localeCompare(S.id)}),s=new Map,o=new Map,i,d,c,p,g,u=[],k=[],l=[],h=[],Z=[],N=new Map,$=[],f=[],ue=[],de=[],le=[],pe=[];for(let a of n)switch(a.kind){case"request":i=a.text;break;case"activity":d=a.text;break;case"goal":c=a.text;break;case"plan":p=a.text;break;case"phase":s.set(a.key,ze(s.get(a.key),a));break;case"task":o.set(a.key,ze(o.get(a.key),a));break;case"decision":u.push(a.text);break;case"blocker":k.push(a.text);break;case"warning":l.push(a.text);break;case"next_action":h.push(a.text);break;case"file":{Z.push(a.text);let S=a.fileAction??"active";N.delete(a.text),N.set(a.text,S),S==="modified"?$.push(a.text):S==="created"?f.push(a.text):S==="deleted"&&ue.push(a.text);break}case"command":de.push(a.text);break;case"test":le.push(a.text),a.checkKind&&pe.push({kind:a.checkKind,command:a.text,status:a.checkStatus??"unknown",updatedAt:a.occurredAt,agent:a.agent,nativeSessionId:a.nativeSessionId});break;case"session":g={agent:a.agent,nativeSessionId:a.nativeSessionId,sessionKey:a.sessionKey,updatedAt:a.occurredAt};break}let w=Array.from(s.values()).sort((a,S)=>(a.order??Number.MAX_SAFE_INTEGER)-(S.order??Number.MAX_SAFE_INTEGER)),x=Array.from(o.values()).sort((a,S)=>(a.order??Number.MAX_SAFE_INTEGER)-(S.order??Number.MAX_SAFE_INTEGER)),X=w.find(a=>a.status==="in_progress")??w.find(a=>a.status==="blocked")??w.find(a=>a.status==="pending"),K=x.find(a=>a.status==="in_progress")??x.find(a=>a.status==="blocked")??x.find(a=>a.status==="pending"),Ye=v([...h,...K?[K.title]:[],...!K&&X?[X.title]:[],...x.filter(a=>a.status==="pending").slice(0,5).map(a=>a.title)],8),Qe=v([...k,...w.filter(a=>a.status==="blocked").map(a=>a.title),...x.filter(a=>a.status==="blocked").map(a=>a.title)],20),ee={version:1,projectId:r.id,projectName:r.name,currentRequest:i,currentActivity:d,goal:c,plan:p,phases:w,tasks:x,decisions:v(u,20),blockers:Qe,warnings:v(l,20),nextActions:Ye,filesTouched:v(Z,30),activeFiles:Array.from(N.entries()).filter(([,a])=>a!=="deleted").map(([a])=>a).slice(-5),modifiedFiles:v($,30),createdFiles:v(f,30),deletedFiles:v(ue,30),commands:v(de,20),tests:v(le,20),checks:mr(pe,20),currentPhase:X,currentTask:K,progress:{phasesTotal:w.length,phasesCompleted:w.filter(a=>a.status==="completed").length,tasksTotal:x.length,tasksCompleted:x.filter(a=>a.status==="completed").length,blocked:w.filter(a=>a.status==="blocked").length+x.filter(a=>a.status==="blocked").length},lastSession:g,updatedAt:n.length?n[n.length-1].occurredAt:new Date().toISOString()},fe=Ve(r.rootPath,".toolnet","work");return fr(fe,{recursive:!0}),P(Ve(fe,"current.json"),ee),await t.put(`projects/${r.id}/work/current.json`,JSON.stringify(ee,null,2)+`
49
- `,"application/json"),ee}async function U(r,t){if((await Be(r,t)).length>0)return oe(r,t);let n=await t.getText(`projects/${r.id}/work/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}var to=new Int32Array(new SharedArrayBuffer(4));import{closeSync as io,existsSync as yr,fsyncSync as ao,mkdirSync as co,openSync as uo,readFileSync as hr,renameSync as lo,writeFileSync as po}from"node:fs";import{dirname as mo,join as kr}from"node:path";function Sr(r){return kr(r.rootPath,".toolnet","work","current.json")}function Je(r){let t=Sr(r);if(!yr(t))return null;try{let e=JSON.parse(hr(t,"utf8"));return e.version!==1||e.projectId!==r.id?null:e}catch{return null}}function y(r,t){let e=new Set,n=[];for(let s of r){let o=s.replace(/\s+/g," ").trim();if(!o)continue;let i=o.normalize("NFKC").toLowerCase();if(!e.has(i)&&(e.add(i),n.push(o),n.length>=t))break}return n}function qe(r){if(r)return{id:r.id,title:r.title,status:r.status}}function br(r,t=[]){let e=t.slice(-10);if(e.some(s=>s.status==="failed"))return"failing";if(e.some(s=>s.status==="passed"))return"passing";let n=r.slice(-10).join(`
50
- `).toLowerCase();return/(?:failed|failing|failure|error|✗|❌)/u.test(n)?"failing":/(?:passed|passing|green|success|✓|✅)/u.test(n)?"passing":"unknown"}function vr(r){return b(JSON.stringify(r))}function xr(r){let t=[];for(let e of r){if(!e)continue;let n=e.match(/https?:\/\/[^\s<>"'`)\]}]+/giu)??[];for(let s of n){let o=s.replace(/[.,;:!?]+$/gu,"").trim();o&&t.push(o)}}return y(t,30)}function Ge(r){let{project:t,identity:e,state:n}=r,s=n.activeFiles?.at(-1)??n.filesTouched.at(-1),o=n.phases.filter(f=>f.status==="completed").map(f=>f.title),i=n.tasks.filter(f=>f.status==="completed").map(f=>f.title),d=n.phases.filter(f=>f.status!=="completed"&&f.status!=="cancelled").map(f=>f.title),c=n.tasks.filter(f=>f.status!=="completed"&&f.status!=="cancelled").map(f=>f.title),p=new Set(n.tasks.filter(f=>f.status==="completed").map(f=>f.title.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim())),g=y(n.nextActions.filter(f=>!p.has(f.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim())),10),u=y([...c,...g],15),k=y(n.tests.slice().reverse(),10),l=y([...(n.activeFiles??[]).slice().reverse(),...n.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:n.goal,request:n.currentRequest,activity:n.currentActivity,current:{phase:qe(n.currentPhase),task:qe(n.currentTask),file:s},completed:{phases:y(o,20),tasks:y(i,30)},remaining:{phases:y(d,20),tasks:y(c,30),todos:u},nextAction:g[0],blockers:y(n.blockers.slice().reverse(),10),decisions:y(n.decisions.slice().reverse(),10),files:{current:s,recent:l,active:y(n.activeFiles??[],10),modified:y(n.modifiedFiles??[],20),created:y(n.createdFiles??[],20),deleted:y(n.deletedFiles??[],20)},tests:{status:br(n.tests,n.checks),recent:k,checks:(n.checks??[]).slice(-10).map(f=>({kind:f.kind,status:f.status,command:f.command}))},evidence:{commands:y((n.commands??[]).slice().reverse(),20),references:xr([n.currentRequest,n.currentActivity,n.goal,n.plan,...n.decisions,...n.blockers,...n.warnings,...n.nextActions,...n.filesTouched,...n.commands??[],...n.tests,...(n.checks??[]).map(f=>f.command)])},attention:y(r.attention??[],20),progress:n.progress},{capturedAt:Z,source:N,...$}=h;return{...h,stateDigest:vr($)}}function Ar(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 Mr(r,t,e,n,s){if(!Ar(e))return null;let o=G(r,!1),d=[...o?o.rules.filter(u=>u.mode==="enforce").map(u=>u.text):[],...e.warnings].slice(0,20),c=Ge({project:r,identity:t,state:e,reason:n,sequence:s,attention:d}),p=c.stateDigest;return{version:1,id:b([r.id,t.sessionKey,p].join("|")).slice(0,24),projectId:r.id,projectName:r.name,createdAt:new Date().toISOString(),reason:n,sourceSession:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,sequence:s},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(u=>u.status!=="completed"&&u.status!=="cancelled"),incompleteTasks:e.tasks.filter(u=>u.status!=="completed"&&u.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 Pr(r,t){let e=ie(r.rootPath,".toolnet","work","handoffs");jr(e,{recursive:!0});let n=ie(e,`${t.id}.json`);wr(n)||P(n,t),P(ie(r.rootPath,".toolnet","work","handoff-latest.json"),t)}var Y=class{constructor(t){this.options=t}options;async capture(t,e){let n=Je(this.options.project);n||(n=await U(this.options.project,this.options.storage)),n||(n=await oe(this.options.project,this.options.storage));let s=Mr(this.options.project,this.options.identity,n,t,e);if(!s)return null;Pr(this.options.project,s);let o=`projects/${this.options.project.id}/work/handoffs/${s.id}.json`;return await this.options.storage.exists(o)||await this.options.storage.put(o,JSON.stringify(s,null,2)+`
51
- `,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(s,null,2)+`
52
- `,"application/json"),s}};async function Q(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 Ue(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 ae(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 m(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 Ir(r){let t=[],e=!1;for(let n of r.split(/\r?\n/u)){let s=n.trim();if(s.includes("<!--")&&(e=!0),e){s.includes("-->")&&(e=!1);continue}let o=s.toLowerCase();if(!(!s||s.startsWith("#")||s==="```"||o.startsWith("- [enforce]")||o.startsWith("* [enforce]")||o.startsWith("- [advisory]")||o.startsWith("* [advisory]"))&&(s=s.replace(/^[-*]\s+/u,""),s&&t.push(m(s,280)),t.length>=16))break}return t}function Rr(r){let t=[],e=[];for(let n of r.split(/\\r?\\n/u)){let s=n.trim(),o=s.toLowerCase(),d=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(p=>o.startsWith(p));if(!d)continue;let c=s.slice(d.length).trim();c&&(d.includes("enforce")?t.push(c):e.push(c))}return{enforce:t,advisory:e}}function Cr(r,t){let e=[];for(let n of r){let s=[...e,n].join(`
53
- `);if(ae(s)<=t){e.push(n);continue}let o=ae(e.join(`
54
- `)),i=Math.max(0,t-o);if(i>=16){let d=Math.floor(i*3.2),c=m(n,d);c&&e.push(c)}break}return e.join(`
55
- `).trim()}async function ce(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=G(r.project,!1),n=e?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let s=Rr(n),o=e?e.rules.filter(l=>l.mode==="enforce").map(l=>l.text):s.enforce,i=e?e.rules.filter(l=>l.mode==="advisory").map(l=>l.text):s.advisory,d=n?Ir(n):[],c=await U(r.project,r.storage),p=await Ue(r.project,r.storage),g=await Q(r.project,r.storage),u=[];if(u.push("[TOOLNET PROJECT CONTEXT]"),u.push(`Project: ${r.project.name}`),u.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&u.push(`Full operating manual: ${q(r.project)}`),o.length){u.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let l of o.slice(0,24))u.push(`- [ENFORCE] ${m(l,240)}`)}if(i.length){u.push("","PROJECT PREFERENCES");for(let l of i.slice(0,10))u.push(`- ${m(l,220)}`)}if(p&&(p.mission&&u.push("","MISSION",m(p.mission.value,420)),p.activeObjective&&u.push("","CURRENT OBJECTIVE",m(p.activeObjective.value,420)),p.why&&u.push("","WHY THIS WORK MATTERS",m(p.why.value,420)),p.desiredOutcome&&u.push("","DESIRED OUTCOME",m(p.desiredOutcome.value,420)),p.planRationale&&u.push("","WHY THIS APPROACH",m(p.planRationale.value,420))),c){if(u.push("","ACTIVE WORK"),c.goal&&u.push(`Goal: ${m(c.goal,320)}`),c.plan&&u.push(`Plan: ${m(c.plan,320)}`),u.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&u.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&&u.push(`Phase objective: ${m(l.objective.value,340)}`),l.why?u.push(`Why this phase: ${m(l.why.value,340)}`):u.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),l.deliverable&&u.push(`Deliverable: ${m(l.deliverable.value,340)}`),l.dependencies.length&&u.push(`Depends on: ${l.dependencies.slice(0,4).map(h=>m(h.value,180)).join("; ")}`),l.acceptanceCriteria.length&&(u.push("","DEFINITION OF DONE"),l.acceptanceCriteria.slice(0,6).forEach(h=>{u.push(`- ${m(h.value,260)}`)})),l.openQuestions.length&&(u.push("","OPEN QUESTIONS FOR CURRENT PHASE"),l.openQuestions.slice(0,4).forEach(h=>{u.push(`- ${m(h.value,260)}`)})))}c.currentTask&&u.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(u.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((l,h)=>{u.push(`${h+1}. ${m(l,260)}`)})),c.blockers.length&&(u.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(l=>{u.push(`- ${m(l,260)}`)})),c.warnings.length&&(u.push("","ATTENTION"),c.warnings.slice(-5).forEach(l=>{u.push(`- ${m(l,260)}`)})),c.decisions.length&&(u.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(l=>{u.push(`- ${m(l,260)}`)})),c.lastSession&&u.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(p&&p.openQuestions.length&&(u.push("","UNRESOLVED QUESTIONS"),p.openQuestions.slice(0,5).forEach(l=>{u.push(`- ${m(l.value,260)}`)})),g&&u.push(`Latest handoff: ${g.reason} / ${g.sourceSession.agent}`),d.length){u.push("","OPERATING NOTES");for(let l of d)u.push(`- ${l}`)}u.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let k=Cr(u,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:k,estimatedTokens:ae(k),maxTokens:t,hasManual:!!n,hasWorkState:!!c,hasHandoff:!!g,generatedAt:new Date().toISOString()}}function _(r,t){let e=r.indexOf(t);return e>=0?r[e+1]:void 0}function Tr(r){let t=ye(),e=_e(Ee({provider:t.storage.provider,huggingface:t.storage.huggingface,localRoot:t.storage.localRoot}),{attempts:3});return new J(e,r.id,r.name,r.remote??r.name)}async function Er(){let[r="help",...t]=process.argv.slice(2),e=new V().detect(_(t,"--project")??process.cwd()),n=Tr(e);if(r==="brief"){let s=Number(_(t,"--tokens")??1e3),o=await ce({project:e,storage:n,maxTokens:Number.isFinite(s)?s:1e3});process.stdout.write(o.text+`
56
- `);return}if(r==="brief-json"){let s=await ce({project:e,storage:n,maxTokens:Number(_(t,"--tokens")??1e3)});console.log(JSON.stringify(s,null,2));return}if(r==="handoff-latest"){let s=await Q(e,n);console.log(JSON.stringify(s,null,2));return}if(r==="handoff-create"){let s=_(t,"--agent")??"manual";if(s==="manual")throw new Error("--agent must be opencode, agy, or codex");let o=_(t,"--session");if(!o)throw new Error("--session is required");let i=We(e,s,o),c=await new Y({project:e,storage:n,identity:i}).capture("manual",0);console.log(JSON.stringify(c,null,2));return}console.log(`Work Context
48
+ `;function Z(r){return $r(r.rootPath,".toolnet","PROJECT.md")}function Lr(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function Dr(r){let t=[],e=new Set,n=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,o;for(;o=n.exec(r);){let s=o[1].toLowerCase(),i=Lr(o[2]);if(!i)continue;let d=`${s}:${i.toLowerCase()}`;e.has(d)||(e.add(d),t.push({id:w(d).slice(0,24),mode:s,text:i,source:"manual"}))}return t}function Wr(r){let t=Z(r);return at(t)||(Cr(Nr(t),{recursive:!0}),_r(t,Kr,{encoding:"utf8",mode:384})),t}function X(r,t=!1){let e=t?Wr(r):Z(r);if(!at(e))return null;if(st(e).size>it)throw new Error(`PROJECT.md exceeds ${it} bytes`);let o=Or(e,"utf8");return{path:e,content:o,digest:w(o),rules:Dr(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(st(e).mtimeMs).toISOString()}}import{existsSync as Qr,mkdirSync as Zr}from"node:fs";import{join as ye}from"node:path";import{join as ct}from"node:path";import{mkdirSync as Fr}from"node:fs";function dt(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function x(r,t=20){let e=[],n=new Set;for(let o of r.slice().reverse()){let s=dt(o);if(!(!s||n.has(s))&&(n.add(s),e.push(o),e.length>=t))break}return e.reverse()}function Vr(r,t=20){let e=new Map;for(let n of r){let o=`${n.kind}|${dt(n.command)}`;e.delete(o),e.set(o,n)}return Array.from(e.values()).slice(-t)}function Br(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 ut(r,t){let e=t.status??r?.status??"pending",n=e;r&&(r.status==="completed"&&e!=="completed"?n="completed":e==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(n=r.status));let o=r&&Br(t)?r.title:t.text;return{id:r?.id??w(t.key).slice(0,24),title:o,status:n,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 lt(r,t){let e=`projects/${r.id}/work/observations/`,n=await t.list(e),o=[];for(let s of n.filter(i=>i.key.endsWith(".json")).sort((i,d)=>i.key.localeCompare(d.key))){let i=await t.getText(s.key);if(i)try{let d=JSON.parse(i);d.version===1&&Array.isArray(d.observations)&&o.push(d)}catch{}}return o}async function ge(r,t){let n=(await lt(r,t)).flatMap(a=>a.observations).sort((a,b)=>{let Ee=a.occurredAt.localeCompare(b.occurredAt);if(Ee!==0)return Ee;let je=a.sequence-b.sequence;return je!==0?je:a.id.localeCompare(b.id)}),o=new Map,s=new Map,i,d,c,l,g,u=[],S=[],p=[],k=[],ne=[],W=new Map,F=[],f=[],Se=[],be=[],we=[],xe=[];for(let a of n)switch(a.kind){case"request":i=a.text;break;case"activity":d=a.text;break;case"goal":c=a.text;break;case"plan":l=a.text;break;case"phase":o.set(a.key,ut(o.get(a.key),a));break;case"task":s.set(a.key,ut(s.get(a.key),a));break;case"decision":u.push(a.text);break;case"blocker":S.push(a.text);break;case"warning":p.push(a.text);break;case"next_action":k.push(a.text);break;case"file":{ne.push(a.text);let b=a.fileAction??"active";W.delete(a.text),W.set(a.text,b),b==="modified"?F.push(a.text):b==="created"?f.push(a.text):b==="deleted"&&Se.push(a.text);break}case"command":be.push(a.text);break;case"test":we.push(a.text),a.checkKind&&xe.push({kind:a.checkKind,command:a.text,status:a.checkStatus??"unknown",updatedAt:a.occurredAt,agent:a.agent,nativeSessionId:a.nativeSessionId});break;case"session":g={agent:a.agent,nativeSessionId:a.nativeSessionId,sessionKey:a.sessionKey,updatedAt:a.occurredAt};break}let E=Array.from(o.values()).sort((a,b)=>(a.order??Number.MAX_SAFE_INTEGER)-(b.order??Number.MAX_SAFE_INTEGER)),v=Array.from(s.values()).sort((a,b)=>(a.order??Number.MAX_SAFE_INTEGER)-(b.order??Number.MAX_SAFE_INTEGER)),oe=E.find(a=>a.status==="in_progress")??E.find(a=>a.status==="blocked")??E.find(a=>a.status==="pending"),V=v.find(a=>a.status==="in_progress")??v.find(a=>a.status==="blocked")??v.find(a=>a.status==="pending"),yt=x([...k,...V?[V.title]:[],...!V&&oe?[oe.title]:[],...v.filter(a=>a.status==="pending").slice(0,5).map(a=>a.title)],8),ht=x([...S,...E.filter(a=>a.status==="blocked").map(a=>a.title),...v.filter(a=>a.status==="blocked").map(a=>a.title)],20),se={version:1,projectId:r.id,projectName:r.name,currentRequest:i,currentActivity:d,goal:c,plan:l,phases:E,tasks:v,decisions:x(u,20),blockers:ht,warnings:x(p,20),nextActions:yt,filesTouched:x(ne,30),activeFiles:Array.from(W.entries()).filter(([,a])=>a!=="deleted").map(([a])=>a).slice(-5),modifiedFiles:x(F,30),createdFiles:x(f,30),deletedFiles:x(Se,30),commands:x(be,20),tests:x(we,20),checks:Vr(xe,20),currentPhase:oe,currentTask:V,progress:{phasesTotal:E.length,phasesCompleted:E.filter(a=>a.status==="completed").length,tasksTotal:v.length,tasksCompleted:v.filter(a=>a.status==="completed").length,blocked:E.filter(a=>a.status==="blocked").length+v.filter(a=>a.status==="blocked").length},lastSession:g,updatedAt:n.length?n[n.length-1].occurredAt:new Date().toISOString()},ve=ct(r.rootPath,".toolnet","work");return Fr(ve,{recursive:!0}),I(ct(ve,"current.json"),se),await t.put(`projects/${r.id}/work/current.json`,JSON.stringify(se,null,2)+`
49
+ `,"application/json"),se}async function ee(r,t){if((await lt(r,t)).length>0)return ge(r,t);let n=await t.getText(`projects/${r.id}/work/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}var Ks=new Int32Array(new SharedArrayBuffer(4));import{closeSync as Vs,existsSync as zr,fsyncSync as Bs,mkdirSync as zs,openSync as Hs,readFileSync as Hr,renameSync as Us,writeFileSync as Ys}from"node:fs";import{dirname as Gs,join as Ur}from"node:path";function Yr(r){return Ur(r.rootPath,".toolnet","work","current.json")}function pt(r){let t=Yr(r);if(!zr(t))return null;try{let e=JSON.parse(Hr(t,"utf8"));return e.version!==1||e.projectId!==r.id?null:e}catch{return null}}function h(r,t){let e=new Set,n=[];for(let o of r){let s=o.replace(/\s+/g," ").trim();if(!s)continue;let i=s.normalize("NFKC").toLowerCase();if(!e.has(i)&&(e.add(i),n.push(s),n.length>=t))break}return n}function ft(r){if(r)return{id:r.id,title:r.title,status:r.status}}function Jr(r,t=[]){let e=t.slice(-10);if(e.some(o=>o.status==="failed"))return"failing";if(e.some(o=>o.status==="passed"))return"passing";let n=r.slice(-10).join(`
50
+ `).toLowerCase();return/(?:failed|failing|failure|error|✗|❌)/u.test(n)?"failing":/(?:passed|passing|green|success|✓|✅)/u.test(n)?"passing":"unknown"}function Gr(r){return w(JSON.stringify(r))}function qr(r){let t=[];for(let e of r){if(!e)continue;let n=e.match(/https?:\/\/[^\s<>"'`)\]}]+/giu)??[];for(let o of n){let s=o.replace(/[.,;:!?]+$/gu,"").trim();s&&t.push(s)}}return h(t,30)}function mt(r){let{project:t,identity:e,state:n}=r,o=n.activeFiles?.at(-1)??n.filesTouched.at(-1),s=n.phases.filter(f=>f.status==="completed").map(f=>f.title),i=n.tasks.filter(f=>f.status==="completed").map(f=>f.title),d=n.phases.filter(f=>f.status!=="completed"&&f.status!=="cancelled").map(f=>f.title),c=n.tasks.filter(f=>f.status!=="completed"&&f.status!=="cancelled").map(f=>f.title),l=new Set(n.tasks.filter(f=>f.status==="completed").map(f=>f.title.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim())),g=h(n.nextActions.filter(f=>!l.has(f.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim())),10),u=h([...c,...g],15),S=h(n.tests.slice().reverse(),10),p=h([...(n.activeFiles??[]).slice().reverse(),...n.filesTouched.slice().reverse()],20),k={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:n.goal,request:n.currentRequest,activity:n.currentActivity,current:{phase:ft(n.currentPhase),task:ft(n.currentTask),file:o},completed:{phases:h(s,20),tasks:h(i,30)},remaining:{phases:h(d,20),tasks:h(c,30),todos:u},nextAction:g[0],blockers:h(n.blockers.slice().reverse(),10),decisions:h(n.decisions.slice().reverse(),10),files:{current:o,recent:p,active:h(n.activeFiles??[],10),modified:h(n.modifiedFiles??[],20),created:h(n.createdFiles??[],20),deleted:h(n.deletedFiles??[],20)},tests:{status:Jr(n.tests,n.checks),recent:S,checks:(n.checks??[]).slice(-10).map(f=>({kind:f.kind,status:f.status,command:f.command}))},evidence:{commands:h((n.commands??[]).slice().reverse(),20),references:qr([n.currentRequest,n.currentActivity,n.goal,n.plan,...n.decisions,...n.blockers,...n.warnings,...n.nextActions,...n.filesTouched,...n.commands??[],...n.tests,...(n.checks??[]).map(f=>f.command)])},attention:h(r.attention??[],20),progress:n.progress},{capturedAt:ne,source:W,...F}=k;return{...k,stateDigest:Gr(F)}}function Xr(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 en(r,t,e,n,o){if(!Xr(e))return null;let s=X(r,!1),d=[...s?s.rules.filter(u=>u.mode==="enforce").map(u=>u.text):[],...e.warnings].slice(0,20),c=mt({project:r,identity:t,state:e,reason:n,sequence:o,attention:d}),l=c.stateDigest;return{version:1,id:w([r.id,t.sessionKey,l].join("|")).slice(0,24),projectId:r.id,projectName:r.name,createdAt:new Date().toISOString(),reason:n,sourceSession:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,sequence:o},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(u=>u.status!=="completed"&&u.status!=="cancelled"),incompleteTasks:e.tasks.filter(u=>u.status!=="completed"&&u.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:l,continuity:c}}function tn(r,t){let e=ye(r.rootPath,".toolnet","work","handoffs");Zr(e,{recursive:!0});let n=ye(e,`${t.id}.json`);Qr(n)||I(n,t),I(ye(r.rootPath,".toolnet","work","handoff-latest.json"),t)}var te=class{constructor(t){this.options=t}options;async capture(t,e){let n=pt(this.options.project);n||(n=await ee(this.options.project,this.options.storage)),n||(n=await ge(this.options.project,this.options.storage));let o=en(this.options.project,this.options.identity,n,t,e);if(!o)return null;tn(this.options.project,o);let s=`projects/${this.options.project.id}/work/handoffs/${o.id}.json`;return await this.options.storage.exists(s)||await this.options.storage.put(s,JSON.stringify(o,null,2)+`
51
+ `,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(o,null,2)+`
52
+ `,"application/json"),o}};async function re(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 gt(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 he(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 m(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 rn(r){let t=[],e=!1;for(let n of r.split(/\r?\n/u)){let o=n.trim();if(o.includes("<!--")&&(e=!0),e){o.includes("-->")&&(e=!1);continue}let s=o.toLowerCase();if(!(!o||o.startsWith("#")||o==="```"||s.startsWith("- [enforce]")||s.startsWith("* [enforce]")||s.startsWith("- [advisory]")||s.startsWith("* [advisory]"))&&(o=o.replace(/^[-*]\s+/u,""),o&&t.push(m(o,280)),t.length>=16))break}return t}function nn(r){let t=[],e=[];for(let n of r.split(/\\r?\\n/u)){let o=n.trim(),s=o.toLowerCase(),d=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(l=>s.startsWith(l));if(!d)continue;let c=o.slice(d.length).trim();c&&(d.includes("enforce")?t.push(c):e.push(c))}return{enforce:t,advisory:e}}function on(r,t){let e=[];for(let n of r){let o=[...e,n].join(`
53
+ `);if(he(o)<=t){e.push(n);continue}let s=he(e.join(`
54
+ `)),i=Math.max(0,t-s);if(i>=16){let d=Math.floor(i*3.2),c=m(n,d);c&&e.push(c)}break}return e.join(`
55
+ `).trim()}async function ke(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=X(r.project,!1),n=e?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let o=nn(n),s=e?e.rules.filter(p=>p.mode==="enforce").map(p=>p.text):o.enforce,i=e?e.rules.filter(p=>p.mode==="advisory").map(p=>p.text):o.advisory,d=n?rn(n):[],c=await ee(r.project,r.storage),l=await gt(r.project,r.storage),g=await re(r.project,r.storage),u=[];if(u.push("[TOOLNET PROJECT CONTEXT]"),u.push(`Project: ${r.project.name}`),u.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&u.push(`Full operating manual: ${Z(r.project)}`),s.length){u.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let p of s.slice(0,24))u.push(`- [ENFORCE] ${m(p,240)}`)}if(i.length){u.push("","PROJECT PREFERENCES");for(let p of i.slice(0,10))u.push(`- ${m(p,220)}`)}if(l&&(l.mission&&u.push("","MISSION",m(l.mission.value,420)),l.activeObjective&&u.push("","CURRENT OBJECTIVE",m(l.activeObjective.value,420)),l.why&&u.push("","WHY THIS WORK MATTERS",m(l.why.value,420)),l.desiredOutcome&&u.push("","DESIRED OUTCOME",m(l.desiredOutcome.value,420)),l.planRationale&&u.push("","WHY THIS APPROACH",m(l.planRationale.value,420))),c){if(u.push("","ACTIVE WORK"),c.goal&&u.push(`Goal: ${m(c.goal,320)}`),c.plan&&u.push(`Plan: ${m(c.plan,320)}`),u.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&u.push(`Current phase: ${c.currentPhase.title} [${c.currentPhase.status}]`),c.currentPhase&&l){let p=l.phases.find(k=>k.order===c.currentPhase?.order);p&&(p.objective&&u.push(`Phase objective: ${m(p.objective.value,340)}`),p.why?u.push(`Why this phase: ${m(p.why.value,340)}`):u.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),p.deliverable&&u.push(`Deliverable: ${m(p.deliverable.value,340)}`),p.dependencies.length&&u.push(`Depends on: ${p.dependencies.slice(0,4).map(k=>m(k.value,180)).join("; ")}`),p.acceptanceCriteria.length&&(u.push("","DEFINITION OF DONE"),p.acceptanceCriteria.slice(0,6).forEach(k=>{u.push(`- ${m(k.value,260)}`)})),p.openQuestions.length&&(u.push("","OPEN QUESTIONS FOR CURRENT PHASE"),p.openQuestions.slice(0,4).forEach(k=>{u.push(`- ${m(k.value,260)}`)})))}c.currentTask&&u.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(u.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((p,k)=>{u.push(`${k+1}. ${m(p,260)}`)})),c.blockers.length&&(u.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(p=>{u.push(`- ${m(p,260)}`)})),c.warnings.length&&(u.push("","ATTENTION"),c.warnings.slice(-5).forEach(p=>{u.push(`- ${m(p,260)}`)})),c.decisions.length&&(u.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(p=>{u.push(`- ${m(p,260)}`)})),c.lastSession&&u.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(l&&l.openQuestions.length&&(u.push("","UNRESOLVED QUESTIONS"),l.openQuestions.slice(0,5).forEach(p=>{u.push(`- ${m(p.value,260)}`)})),g&&u.push(`Latest handoff: ${g.reason} / ${g.sourceSession.agent}`),d.length){u.push("","OPERATING NOTES");for(let p of d)u.push(`- ${p}`)}u.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let S=on(u,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:S,estimatedTokens:he(S),maxTokens:t,hasManual:!!n,hasWorkState:!!c,hasHandoff:!!g,generatedAt:new Date().toISOString()}}function D(r,t){let e=r.indexOf(t);return e>=0?r[e+1]:void 0}function sn(r){let t=Ae(),e=et(Ze({provider:t.storage.provider,huggingface:t.storage.huggingface,localRoot:t.storage.localRoot}),{attempts:3});return new Q(e,r.id,r.name,r.remote??r.name)}async function an(){let[r="help",...t]=process.argv.slice(2),e=new Y().detect(D(t,"--project")??process.cwd()),n=sn(e);if(r==="brief"){let o=Number(D(t,"--tokens")??1e3),s=await ke({project:e,storage:n,maxTokens:Number.isFinite(o)?o:1e3});process.stdout.write(s.text+`
56
+ `);return}if(r==="brief-json"){let o=await ke({project:e,storage:n,maxTokens:Number(D(t,"--tokens")??1e3)});console.log(JSON.stringify(o,null,2));return}if(r==="handoff-latest"){let o=await re(e,n);console.log(JSON.stringify(o,null,2));return}if(r==="handoff-create"){let o=D(t,"--agent")??"manual";if(o==="manual")throw new Error("--agent must be opencode, agy, or codex");let s=D(t,"--session");if(!s)throw new Error("--session is required");let i=ot(e,o,s),c=await new te({project:e,storage:n,identity:i}).capture("manual",0);console.log(JSON.stringify(c,null,2));return}console.log(`Work Context
57
57
 
58
58
  Commands:
59
59
  brief [--project PATH] [--tokens N]
60
60
  brief-json [--project PATH] [--tokens N]
61
61
  handoff-latest [--project PATH]
62
62
  handoff-create --agent <opencode|agy|codex> --session <id>
63
- `)}Er().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
63
+ `)}an().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});