toolnet-memory 0.3.15 → 0.3.17

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 (48) hide show
  1. package/.env.example +74 -0
  2. package/.release-target +1 -1
  3. package/README.md +316 -6
  4. package/bin/toolnet-memory +12 -0
  5. package/bundle/agy-hook.js +66 -66
  6. package/bundle/agy.js +56 -56
  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/claude-hook.js +49 -49
  11. package/bundle/code-capabilities.js +2 -0
  12. package/bundle/codex-context.js +29 -29
  13. package/bundle/codex.js +71 -71
  14. package/bundle/context-runtime.js +39 -39
  15. package/bundle/context.js +15 -15
  16. package/bundle/copilot-hook.js +78 -78
  17. package/bundle/cursor-hook.js +78 -78
  18. package/bundle/docker-healthcheck.js +3 -0
  19. package/bundle/doctor.js +4 -4
  20. package/bundle/full-index.js +22 -21
  21. package/bundle/gc.js +5 -3
  22. package/bundle/graph-cli.js +1 -1
  23. package/bundle/graph-index.js +5 -4
  24. package/bundle/graph-ui.js +4 -3
  25. package/bundle/grok-hook.js +56 -56
  26. package/bundle/handoff-refresh.js +10 -10
  27. package/bundle/help-cli.js +4 -4
  28. package/bundle/impact.js +5 -4
  29. package/bundle/incremental.js +5 -5
  30. package/bundle/init.js +36 -35
  31. package/bundle/kiro-hook.js +85 -85
  32. package/bundle/learner.js +8 -6
  33. package/bundle/mcp.js +19 -17
  34. package/bundle/memory-agent.js +6 -6
  35. package/bundle/memory-query.js +7 -7
  36. package/bundle/opencode.js +77 -77
  37. package/bundle/project-manual.js +7 -7
  38. package/bundle/public/index.html +51 -2
  39. package/bundle/recovery-certify.js +9 -9
  40. package/bundle/runtime.js +16 -13
  41. package/bundle/semantic.js +6 -5
  42. package/bundle/service.js +11 -6
  43. package/bundle/snapshot.js +6 -4
  44. package/bundle/status-cli.js +4 -4
  45. package/bundle/toolnet-cli.js +46 -46
  46. package/bundle/work-continuity.js +7 -7
  47. package/package.json +38 -2
  48. package/release-manifest.json +143 -4
@@ -1,7 +1,7 @@
1
- import{readFileSync as tt}from"node:fs";import{existsSync as X,readFileSync as ee}from"node:fs";import{homedir as te}from"node:os";import{join as re}from"node:path";function ne(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 oe(){let n=process.env.TOOLNET_GLOBAL_ENV??re(te(),".config","toolnet-memory",".env");if(!X(n))return;let e=ee(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 s=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=ne(r.slice(o+1)))}}oe();function m(n,e){return n===void 0?e:["1","true","yes","on"].includes(n.toLowerCase())}function f(n,e){if(!n)return e;let t=Number(n);return Number.isFinite(t)?t:e}function T(){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:f(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:f(process.env.MEMORY_RERANK_TOP,10),finalContext:f(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:f(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:f(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as se}from"node:crypto";import{existsSync as S,mkdirSync as ie,readFileSync as ae,renameSync as ce,writeFileSync as ue}from"node:fs";import{basename as de,dirname as w,join as g,parse as $,resolve as p}from"node:path";var K=".toolnet",pe="project.json";function le(n){return se("sha256").update(n).digest("hex").slice(0,16)}function b(n){return g(n,K,pe)}function me(n){return S(b(n))}function I(n,e){let t=p(n),r=$(t).root;for(;;){if(me(t))return t;if(t===r||e&&t===p(e))break;let o=w(t);if(o===t)break;t=o}return null}function _(n){let e=p(n),t=$(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(s=>S(g(e,s))))return e;if(e===t)break;let o=w(e);if(o===e)break;e=o}return p(n)}function O(n){let e;try{e=JSON.parse(ae(n,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${n}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:w(w(n)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function N(n,e){let t=g(n,K);ie(t,{recursive:!0});let r=b(n),o=`${r}.tmp-${process.pid}`;ue(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),ce(o,r)}function R(n,e){return{id:n.id,name:n.name,remote:n.remote,rootPath:e,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}var v=class{findExisting(e=process.cwd()){let t=p(e),r=_(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>S(g(r,c))),i=I(t,s?r:void 0);if(!i)return null;let a=O(b(i));return R(a,i)}requireExisting(e=process.cwd()){let t=this.findExisting(e);if(!t)throw new Error("PROJECT_NOT_INITIALIZED");return t}detect(e=process.cwd()){let t=p(e),r=_(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>S(g(r,d))),i=I(t,s?r:void 0);if(i){let d=b(i),l=O(d);return l.rootPath!==i&&(l.rootPath=i,l.updatedAt=new Date().toISOString(),N(i,l)),R(l,i)}let a=new Date().toISOString(),c=de(r),u={version:1,id:le(r),name:c,remote:c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return N(r,u),R(u,r)}};var fe=[{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"}],ge=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function D(n){return n.normalize("NFKC").trim().toLowerCase()}function ye(n){if(n.length===0)return 0;let e=new Map;for(let r of n)e.set(r,(e.get(r)??0)+1);let t=0;for(let r of e.values()){let o=r/n.length;t-=o*Math.log2(o)}return t}function he(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function Se(n,e,t){let r=n.slice(Math.max(0,e-48),e),o=n.slice(t,Math.min(n.length,t+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${o}`)}function we(n,e){return n.start<e.end&&e.start<n.end}function z(n){return n.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var x=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let r=D(t);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let s of fe){let i=new RegExp(s.regex.source,s.regex.flags);for(let a of e.matchAll(i))a.index===void 0||!a[0]||this.allowed(a[0])||t.push({type:s.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let r=z(t),o=[];for(let s of r)o.some(i=>we(i,s))||o.push(s);return z(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=D(e);return ge.has(t)?!0:this.allowValues.has(t)}entropyMatches(e){let t=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of e.matchAll(r)){if(o.index===void 0||!o[0])continue;let s=o[0];this.allowed(s)||he(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||Se(e,o.index,o.index+s.length)&&(ye(s)<3.7||t.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return t}};var j=class{scanner;constructor(e={}){this.scanner=new x(e)}sanitize(e){let t=this.scanner.scan(e);if(t.length===0)return{text:e,redacted:0,secretTypes:[]};let r=e,o=[...t].sort((i,a)=>a.start-i.start),s=new Set;for(let i of o)s.add(i.type),r=r.slice(0,i.start)+`[REDACTED:${i.type}]`+r.slice(i.end);return{text:r,redacted:t.length,secretTypes:[...s].sort()}}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 s=r.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")){t[r]="[REDACTED]";continue}t[r]=this.sanitizeValue(o)}return t}return e}};var bt=new j;import{homedir as Be}from"node:os";import{join as Fe}from"node:path";import{DeleteObjectCommand as be,GetObjectCommand as ve,HeadObjectCommand as xe,ListObjectsV2Command as je,PutObjectCommand as ke,S3Client as Me}from"@aws-sdk/client-s3";import{getSignedUrl as Re}from"@aws-sdk/s3-request-presigner";var k=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Me({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ke({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Re(this.client,new ve({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new 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 be({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new je({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let s of o.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};import{access as L,mkdir as Ae,readFile as Pe,readdir as Ce,rm as Ee,stat as V,writeFile as Te}from"node:fs/promises";import{dirname as Ie,join as _e,relative as B,resolve as Oe}from"node:path";var y=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Oe(this.root,t)}async put(e,t){let r=this.path(e);await Ae(Ie(r),{recursive:!0}),await Te(r,t)}async get(e){try{return await Pe(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 L(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),r=[];try{await L(t)}catch{return r}let o=async i=>{let a=await Ce(i,{withFileTypes:!0});for(let c of a){let u=_e(i,c.name);if(c.isDirectory()){await o(u);continue}let d=await V(u);r.push({key:B(this.root,u),size:d.size,updatedAt:d.mtime.toISOString()})}},s=await V(t);return s.isDirectory()?await o(t):r.push({key:B(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),r}};import{DeleteObjectCommand as Ne,GetObjectCommand as $e,HeadObjectCommand as Ke,ListObjectsV2Command as De,PutObjectCommand as ze,S3Client as Le}from"@aws-sdk/client-s3";import{getSignedUrl as Ve}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 Le({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,r="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ze({Bucket:this.bucket,Key:e,Body:o,ContentType:r}))}async get(e){let t=await Ve(this.client,new $e({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(t,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ke({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Ne({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],r;do{let o=await this.client.send(new De({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let s of o.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return t}};function A(n,e){return console.warn(e),new y(n)}function F(n){let e=n.localRoot??Fe(Be(),".toolnet-memory","storage");if(n.provider==="r2"){let t=n.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}):A(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let t=n.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}):A(e,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let t=n.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new k({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):A(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new y(e)}function Je(n){return new Promise(e=>setTimeout(e,n))}async function J(n,e={}){let t=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,o=e.maxDelayMs??2e3,s;for(let i=1;i<=t;i++)try{return await n()}catch(a){if(s=a,i>=t)break;let c=Math.min(o,r*2**(i-1)),u=Math.floor(Math.random()*Math.max(1,c*.2));await Je(c+u)}throw s}var Ue=new Set(["put","get","getText","delete","list"]);function U(n,e={}){return new Proxy(n,{get(t,r){let o=Reflect.get(t,r,t);return typeof o!="function"?o:Ue.has(r)?(...s)=>J(()=>Promise.resolve(o.apply(t,s)),e):o.bind(t)}})}function H(n){let e=n.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 Y(n){let e=n.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var M=class{constructor(e,t,r,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=r,this.folder=H(o??r),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),r=t,o=await this.provider.getText(e);if(o){let i;try{i=JSON.parse(o)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${a instanceof Error?a.message:String(a)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:t};await this.provider.put(e,JSON.stringify(s,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=Y(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,r){return await this.ensureRegistered(),this.provider.put(this.key(e),t,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{existsSync as Z,mkdirSync as Ye,readFileSync as We,statSync as W,writeFileSync as Ge}from"node:fs";import{dirname as Ze,join as Qe}from"node:path";import{createHash as He}from"node:crypto";function P(n){return He("sha256").update(n).digest("hex")}var G=64*1024,qe=`# ToolNet Project Operating Manual
1
+ import{readFileSync as Gt}from"node:fs";import{existsSync as Te,readFileSync as Ie}from"node:fs";import{homedir as Ce}from"node:os";import{join as _e}from"node:path";function Oe(n){let t=n.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 Ne(){let n=process.env.TOOLNET_GLOBAL_ENV??_e(Ce(),".config","toolnet-memory",".env");if(!Te(n))return;let t=Ie(n,"utf8");for(let e of t.split(/\r?\n/)){let r=e.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]=Oe(r.slice(o+1)))}}Ne();function S(n,t){return n===void 0?t:["1","true","yes","on"].includes(n.toLowerCase())}function b(n,t){if(!n)return t;let e=Number(n);return Number.isFinite(e)?e:t}function J(){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:b(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:b(process.env.MEMORY_RERANK_TOP,10),finalContext:b(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:b(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:b(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Ye}from"node:crypto";import{existsSync as k,mkdirSync as Be,readFileSync as Ue,renameSync as Fe,writeFileSync as Ge}from"node:fs";import{basename as He,dirname as T,join as x,parse as re,resolve as f}from"node:path";import{createHash as q}from"node:crypto";import{spawnSync as Ke}from"node:child_process";var w="git-remote-v1",De=new Set(["github.com","gitlab.com","bitbucket.org"]);function Q(n,t){let e=t.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!e||e==="."||e===".."||e.split("/").some(r=>!r||r==="."||r==="..")?null:(De.has(n)&&(e=e.toLowerCase()),e)}function $e(n){let t;try{t=new URL(n)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(t.protocol))return null;let e=t.hostname.trim().toLowerCase();if(!e)return null;let r=t.protocol==="https:"&&t.port==="443"||t.protocol==="http:"&&t.port==="80"||t.protocol==="ssh:"&&t.port==="22",o=t.port&&!r?`${e}:${t.port}`:e,i=Q(e,t.pathname);return i?`${o}/${i}`:null}function Le(n){let t=n.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!t)return null;let e=t[1]?.trim().toLowerCase();if(!e||e.length===1)return null;let r=Q(e,t[2]??"");return r?`${e}/${r}`:null}function W(n){let t=n.trim();return t?t.includes("://")?$e(t):Le(t):null}function ze(n){return q("sha256").update(`${w}:${n}`).digest("hex")}function X(n){return q("sha256").update(`toolnet-project:${w}:${n}`).digest("hex").slice(0,16)}function Ve(n){return n.split("/").filter(Boolean).at(-1)?.trim()||null}function K(n,t){let e=Ke("git",["-C",n,...t],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return e.error||e.status!==0?null:e.stdout?.trim()||null}function Z(n,t){let e=Ve(n);return e?{scheme:w,canonicalRemote:n,fingerprint:ze(n),repositoryName:e,source:t}:null}function ee(n){let t=K(n,["remote","get-url","origin"]);if(t){let o=W(t);if(o)return Z(o,"origin")}let e=K(n,["remote"]);if(!e)return null;let r=new Set;for(let o of e.split(/\r?\n/u).map(i=>i.trim()).filter(Boolean)){let i=K(n,["remote","get-url",o]);if(!i)continue;let s=W(i);s&&r.add(s)}return r.size!==1?null:Z([...r][0],"unique-remote")}var ne=".toolnet",Je="project.json";function We(n){return Ye("sha256").update(n).digest("hex").slice(0,16)}function y(n){return x(n,ne,Je)}function oe(n){return k(y(n))}function te(n,t){let e=f(n),r=re(e).root;for(;;){if(oe(e))return e;if(e===r||t&&e===f(t))break;let o=T(e);if(o===e)break;e=o}return null}function D(n){let t=f(n),e=re(t).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(i=>k(x(t,i))))return t;if(t===e)break;let o=T(t);if(o===t)break;t=o}return f(n)}function j(n){let t;try{t=JSON.parse(Ue(n,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${n}: ${o instanceof Error?o.message:String(o)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${n}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${n}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${n}`);let r=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:T(T(n)),createdAt:typeof e.createdAt=="string"?e.createdAt:r,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:r,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 A(n,t){let e=x(n,ne);Be(e,{recursive:!0});let r=y(n),o=`${r}.tmp-${process.pid}`;Ge(o,JSON.stringify(t,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Fe(o,r)}function m(n,t){return{id:n.id,name:n.name,remote:n.remote,rootPath:t,createdAt:n.createdAt,updatedAt:n.updatedAt,graphVersion:n.graphVersion,memoryVersion:n.memoryVersion,metadata:n.metadata}}function $(n){return{version:1,scheme:w,canonicalRemote:n.canonicalRemote,fingerprint:n.fingerprint,repositoryName:n.repositoryName}}function Ze(n){let t=n.metadata?.toolnetIdentity;if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t;return typeof e.fingerprint=="string"?e.fingerprint:null}var I=class{adopt(t,e){let r=D(f(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(oe(r)){let a=j(y(r));if(a.id!==e.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${a.id}`,`requested=${e.id}`].join(" "));return m(a,r)}let o=new Date().toISOString(),i={...e.metadata};e.gitIdentity&&(i.toolnetIdentity=$(e.gitIdentity));let s={version:1,id:e.id.trim(),name:e.name.trim(),remote:e.remote.trim(),rootPath:r,createdAt:e.createdAt??o,updatedAt:o,graphVersion:e.graphVersion??0,memoryVersion:e.memoryVersion??0,metadata:Object.keys(i).length?i:void 0};return A(r,s),m(s,r)}recordGitIdentity(t,e,r={}){let o=this.requireExisting(t),i=y(o.rootPath),s=j(i),a=Ze(s);if(a&&a!==e.fingerprint&&!r.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${a}`,`current=${e.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let c=s.metadata?.toolnetIdentity;return c&&typeof c=="object"&&!Array.isArray(c)&&c.fingerprint===e.fingerprint||(s.metadata={...s.metadata,toolnetIdentity:$(e)},s.updatedAt=new Date().toISOString(),A(o.rootPath,s)),m(s,o.rootPath)}findExisting(t=process.cwd()){let e=f(t),r=D(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(c=>k(x(r,c))),s=te(e,i?r:void 0);if(!s)return null;let a=j(y(s));return m(a,s)}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=f(t),r=D(e),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(l=>k(x(r,l))),s=te(e,i?r:void 0);if(s){let l=y(s),h=j(l);return h.rootPath!==s&&(h.rootPath=s,h.updatedAt=new Date().toISOString(),A(s,h)),m(h,s)}let a=new Date().toISOString(),c=He(r),u=ee(r),p={version:1,id:u?X(u.canonicalRemote):We(r),name:c,remote:u?.repositoryName??c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0,metadata:u?{toolnetIdentity:$(u)}:void 0};return A(r,p),m(p,r)}};var qe=[{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"}],Qe=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function ie(n){return n.normalize("NFKC").trim().toLowerCase()}function Xe(n){if(n.length===0)return 0;let t=new Map;for(let r of n)t.set(r,(t.get(r)??0)+1);let e=0;for(let r of t.values()){let o=r/n.length;e-=o*Math.log2(o)}return e}function et(n){return/^[a-f0-9]{32}$/iu.test(n)||/^[a-f0-9]{40}$/iu.test(n)||/^[a-f0-9]{64}$/iu.test(n)}function tt(n,t,e){let r=n.slice(Math.max(0,t-48),t),o=n.slice(e,Math.min(n.length,e+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${o}`)}function rt(n,t){return n.start<t.end&&t.start<n.end}function se(n){return n.sort((t,e)=>t.start!==e.start?t.start-e.start:e.end-e.start-(t.end-t.start))}var C=class{allowValues=new Set;enableEntropyHeuristic;constructor(t={}){for(let e of t.allowValues??[]){let r=ie(e);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=t.enableEntropyHeuristic??!0}scan(t){let e=[];for(let i of qe){let s=new RegExp(i.regex.source,i.regex.flags);for(let a of t.matchAll(s))a.index===void 0||!a[0]||this.allowed(a[0])||e.push({type:i.type,value:a[0],start:a.index,end:a.index+a[0].length,confidence:i.confidence})}this.enableEntropyHeuristic&&e.push(...this.entropyMatches(t));let r=se(e),o=[];for(let i of r)o.some(s=>rt(s,i))||o.push(i);return se(o)}hasSecrets(t){return this.scan(t).length>0}allowed(t){let e=ie(t);return Qe.has(e)?!0:this.allowValues.has(e)}entropyMatches(t){let e=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of t.matchAll(r)){if(o.index===void 0||!o[0])continue;let i=o[0];this.allowed(i)||et(i)||!/[A-Za-z]/u.test(i)||!/[0-9]/u.test(i)||tt(t,o.index,o.index+i.length)&&(Xe(i)<3.7||e.push({type:"high_entropy_secret",value:i,start:o.index,end:o.index+i.length,confidence:"heuristic"}))}return e}};var _=class{scanner;constructor(t={}){this.scanner=new C(t)}sanitize(t){let e=this.scanner.scan(t);if(e.length===0)return{text:t,redacted:0,secretTypes:[]};let r=t,o=[...e].sort((s,a)=>a.start-s.start),i=new Set;for(let s of o)i.add(s.type),r=r.slice(0,s.start)+`[REDACTED:${s.type}]`+r.slice(s.end);return{text:r,redacted:e.length,secretTypes:[...i].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[r,o]of Object.entries(t)){let i=r.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(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")){e[r]="[REDACTED]";continue}e[r]=this.sanitizeValue(o)}return e}return t}};var gr=new _;import{homedir as _t}from"node:os";import{join as Ot}from"node:path";import{DeleteObjectCommand as nt,GetObjectCommand as ot,HeadObjectCommand as it,ListObjectsV2Command as st,PutObjectCommand as at,S3Client as ct}from"@aws-sdk/client-s3";import{getSignedUrl as ut}from"@aws-sdk/s3-request-presigner";var O=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new ct({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,r="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new at({Bucket:this.bucket,Key:t,Body:o,ContentType:r}))}async get(t){let e=await ut(this.client,new ot({Bucket:this.bucket,Key:t}),{expiresIn:60}),r=await fetch(e,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(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 nt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],r;do{let o=await this.client.send(new st({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return e}};import{access as ae,mkdir as dt,readFile as lt,readdir as pt,rm as mt,stat as ce,writeFile as ft}from"node:fs/promises";import{dirname as gt,join as yt,relative as ue,resolve as ht}from"node:path";var E=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return ht(this.root,e)}async put(t,e){let r=this.path(t);await dt(gt(r),{recursive:!0}),await ft(r,e)}async get(t){try{return await lt(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 ae(this.path(t)),!0}catch{return!1}}async delete(t){await mt(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),r=[];try{await ae(e)}catch{return r}let o=async s=>{let a=await pt(s,{withFileTypes:!0});for(let c of a){let u=yt(s,c.name);if(c.isDirectory()){await o(u);continue}let p=await ce(u);r.push({key:ue(this.root,u),size:p.size,updatedAt:p.mtime.toISOString()})}},i=await ce(e);return i.isDirectory()?await o(e):r.push({key:ue(this.root,e),size:i.size,updatedAt:i.mtime.toISOString()}),r}};import{DeleteObjectCommand as St,GetObjectCommand as bt,HeadObjectCommand as wt,ListObjectsV2Command as xt,PutObjectCommand as Et,S3Client as Rt}from"@aws-sdk/client-s3";import{getSignedUrl as vt}from"@aws-sdk/s3-request-presigner";var R=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new Rt({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,r="application/octet-stream"){let o=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Et({Bucket:this.bucket,Key:t,Body:o,ContentType:r}))}async get(t){let e=await vt(this.client,new bt({Bucket:this.bucket,Key:t}),{expiresIn:60}),r=await fetch(e,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(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 wt({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 St({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],r;do{let o=await this.client.send(new xt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:r}));for(let i of o.Contents??[])i.Key&&e.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});r=o.IsTruncated?o.NextContinuationToken:void 0}while(r);return e}};import{createCipheriv as Mt,createDecipheriv as Pt,createHash as jt,randomBytes as At,timingSafeEqual as kt}from"node:crypto";import{readFileSync as Tt}from"node:fs";var g=Buffer.from("TNMEME01","ascii"),le=1,v=8,M=12,L=16,pe=g.length+1+v+M+L,It="toolnet-memory:remote-encryption:v1:",me="aes-256-gcm",z=32,d=class extends Error{constructor(e,r){super(r);this.code=e;this.name="RemoteEncryptionError"}code};function Ct(n){return n?["1","true","yes","on","enabled"].includes(n.trim().toLowerCase()):!1}function V(n=process.env){return Ct(n.TOOLNET_REMOTE_ENCRYPTION)}function de(n){let t=n.trim();if(!t)throw new d("REMOTE_ENCRYPTION_KEY_EMPTY","Remote encryption key is empty.");let e;if(t.startsWith("hex:")){let r=t.slice(4);if(!/^[0-9a-f]{64}$/iu.test(r))throw new d("REMOTE_ENCRYPTION_KEY_INVALID","hex: remote encryption key must contain exactly 64 hexadecimal characters.");e=Buffer.from(r,"hex")}else if(/^[0-9a-f]{64}$/iu.test(t))e=Buffer.from(t,"hex");else{let r=t.startsWith("base64:")?t.slice(7):t;if(!/^[A-Za-z0-9+/_-]+={0,2}$/u.test(r))throw new d("REMOTE_ENCRYPTION_KEY_INVALID","Remote encryption key must be 32 raw bytes encoded as hexadecimal or base64.");e=Buffer.from(r,r.includes("-")||r.includes("_")?"base64url":"base64")}if(e.length!==z)throw new d("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${z} bytes.`);return e}function fe(n=process.env){let t=n.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),e=n.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(t&&e)throw new d("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(t)return de(t);if(e){let r;try{r=Tt(e,"utf8")}catch(o){throw new d("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${e}.`,o instanceof Error?o.message:String(o)].join(" "))}return de(r)}}function ge(n){return jt("sha256").update(n).digest().subarray(0,v)}function ye(n){return Buffer.from(`${It}${n}`,"utf8")}function Y(n){return n.byteLength<g.length?!1:Buffer.from(n).subarray(0,g.length).equals(g)}function he(n,t,e){if(e.byteLength!==z)throw new d("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH","AES-256-GCM requires a 32-byte key.");let r=typeof t=="string"?Buffer.from(t,"utf8"):Buffer.from(t),o=At(M),i=Mt(me,e,o);i.setAAD(ye(n));let s=Buffer.concat([i.update(r),i.final()]),a=i.getAuthTag(),c=Buffer.alloc(pe),u=0;return g.copy(c,u),u+=g.length,c.writeUInt8(le,u),u+=1,ge(e).copy(c,u),u+=v,o.copy(c,u),u+=M,a.copy(c,u),Buffer.concat([c,s])}function Se(n,t,e){let r=Buffer.from(t);if(!Y(r))throw new d("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(r.length<pe)throw new d("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let o=g.length,i=r.readUInt8(o);if(o+=1,i!==le)throw new d("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${i}.`);let s=r.subarray(o,o+v);o+=v;let a=ge(e);if(!kt(s,a))throw new d("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let c=r.subarray(o,o+M);o+=M;let u=r.subarray(o,o+L);o+=L;let p=r.subarray(o),l=Pt(me,e,c);l.setAAD(ye(n)),l.setAuthTag(u);try{return Buffer.concat([l.update(p),l.final()])}catch{throw new d("REMOTE_ENCRYPTION_AUTH_FAILED","Encrypted remote object failed AES-GCM authentication.")}}var B=class{constructor(t,e){this.inner=t;this.options=e;if(this.name=t.name,e.enabled&&!e.key)throw new d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote client-side encryption is enabled but no encryption key is configured.")}inner;options;name;async put(t,e,r){if(!this.options.enabled){await this.inner.put(t,e,r);return}let o=this.options.key;if(!o)throw new d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let i=he(t,e,o);await this.inner.put(t,i,"application/octet-stream")}async get(t){let e=await this.inner.get(t);if(!e)return null;if(!Y(e))return e;if(!this.options.enabled)throw new d("REMOTE_ENCRYPTION_REQUIRED",["Remote object is client-side encrypted.","Enable TOOLNET_REMOTE_ENCRYPTION and configure the matching key."].join(" "));let r=this.options.key;if(!r)throw new d("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return Se(t,e,r)}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 be(n,t=process.env){if(n.name==="local")return V(t)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),n;let e=V(t),r=e?fe(t):void 0;return new B(n,{enabled:e,key:r})}function P(n){return be(n)}function U(n,t){return console.warn(t),P(new E(n))}function we(n){let t=n.localRoot??Ot(_t(),".toolnet-memory","storage");if(n.provider==="r2"){let e=n.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?P(new R({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):U(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(n.provider==="s3"){let e=n.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?P(new R({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):U(t,"[storage] S3 credentials missing. Using local fallback.")}if(n.provider==="huggingface"){let e=n.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?P(new O({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey})):U(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return P(new E(t))}function Nt(n){return new Promise(t=>setTimeout(t,n))}async function xe(n,t={}){let e=Math.max(1,t.attempts??3),r=t.baseDelayMs??150,o=t.maxDelayMs??2e3,i;for(let s=1;s<=e;s++)try{return await n()}catch(a){if(i=a,s>=e)break;let c=Math.min(o,r*2**(s-1)),u=Math.floor(Math.random()*Math.max(1,c*.2));await Nt(c+u)}throw i}var Kt=new Set(["put","get","getText","delete","list"]);function Ee(n,t={}){return new Proxy(n,{get(e,r){let o=Reflect.get(e,r,e);return typeof o!="function"?o:Kt.has(r)?(...i)=>xe(()=>Promise.resolve(o.apply(e,i)),t):o.bind(e)}})}function Re(n){let t=n.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 ve(n){let t=n.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 r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[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 N=class{constructor(t,e,r,o){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=r,this.folder=Re(o??r),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(),r=e,o=await this.provider.getText(t);if(o){let s;try{s=JSON.parse(o)}catch(a){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${a instanceof Error?a.message:String(a)}`)}if(typeof s.id=="string"&&s.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${s.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof s.createdAt=="string"&&(r=s.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,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=ve(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,r){return await this.ensureRegistered(),this.provider.put(this.key(t),e,r)}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{existsSync as je,mkdirSync as $t,readFileSync as Lt,statSync as Me,writeFileSync as zt}from"node:fs";import{dirname as Vt,join as Yt}from"node:path";import{createHash as Dt}from"node:crypto";function F(n){return Dt("sha256").update(n).digest("hex")}var Pe=64*1024,Bt=`# ToolNet Project Operating Manual
5
5
 
6
6
  This file contains persistent instructions for AI agents working on this project.
7
7
 
@@ -44,11 +44,11 @@ Things an AI agent should always remember.
44
44
  <!--
45
45
  - [advisory] Prefer small focused files.
46
46
  -->
47
- `;function Q(n){return Qe(n.rootPath,".toolnet","PROJECT.md")}function Xe(n){return n.normalize("NFKC").replace(/\s+/g," ").trim()}function et(n){let e=[],t=new Set,r=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,o;for(;o=r.exec(n);){let s=o[1].toLowerCase(),i=Xe(o[2]);if(!i)continue;let a=`${s}:${i.toLowerCase()}`;t.has(a)||(t.add(a),e.push({id:P(a).slice(0,24),mode:s,text:i,source:"manual"}))}return e}function C(n){let e=Q(n);return Z(e)||(Ye(Ze(e),{recursive:!0}),Ge(e,qe,{encoding:"utf8",mode:384})),e}function E(n,e=!1){let t=e?C(n):Q(n);if(!Z(t))return null;if(W(t).size>G)throw new Error(`PROJECT.md exceeds ${G} bytes`);let o=We(t,"utf8");return{path:t,content:o,digest:P(o),rules:et(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(W(t).mtimeMs).toISOString()}}async function q(n,e){let t=E(n,!0);if(!t)throw new Error("Project manual unavailable");let r=`projects/${n.id}/project`;return await e.put(`${r}/manual.md`,t.content,"text/markdown"),await e.put(`${r}/manual.json`,JSON.stringify({version:1,projectId:n.id,projectName:n.name,digest:t.digest,bytes:t.bytes,ruleCount:t.rules.length,enforceRules:t.rules.filter(o=>o.mode==="enforce").length,advisoryRules:t.rules.filter(o=>o.mode==="advisory").length,updatedAt:t.updatedAt},null,2)+`
48
- `,"application/json"),t}function rt(n,e){let t=n.indexOf(e);return t>=0?n[t+1]:void 0}function nt(n){let e=T(),t=U(F({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new M(t,n.id,n.name,n.remote??n.name)}async function ot(){let[n="help",...e]=process.argv.slice(2),t=new v().detect(rt(e,"--project")??process.cwd());if(n==="init"){let r=C(t);console.log(`\u2705 Project manual: ${r}`);return}if(n==="show"){let r=E(t,!1);if(!r){console.log("No .toolnet/PROJECT.md yet.");return}process.stdout.write(tt(r.path,"utf8"));return}if(n==="sync"){let r=await q(t,nt(t));console.log(JSON.stringify({project:t.name,path:r.path,digest:r.digest,bytes:r.bytes,rules:r.rules.length,enforce:r.rules.filter(o=>o.mode==="enforce").length,advisory:r.rules.filter(o=>o.mode==="advisory").length},null,2));return}console.log(`Project Manual
47
+ `;function Ae(n){return Yt(n.rootPath,".toolnet","PROJECT.md")}function Ut(n){return n.normalize("NFKC").replace(/\s+/g," ").trim()}function Ft(n){let t=[],e=new Set,r=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,o;for(;o=r.exec(n);){let i=o[1].toLowerCase(),s=Ut(o[2]);if(!s)continue;let a=`${i}:${s.toLowerCase()}`;e.has(a)||(e.add(a),t.push({id:F(a).slice(0,24),mode:i,text:s,source:"manual"}))}return t}function G(n){let t=Ae(n);return je(t)||($t(Vt(t),{recursive:!0}),zt(t,Bt,{encoding:"utf8",mode:384})),t}function H(n,t=!1){let e=t?G(n):Ae(n);if(!je(e))return null;if(Me(e).size>Pe)throw new Error(`PROJECT.md exceeds ${Pe} bytes`);let o=Lt(e,"utf8");return{path:e,content:o,digest:F(o),rules:Ft(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(Me(e).mtimeMs).toISOString()}}async function ke(n,t){let e=H(n,!0);if(!e)throw new Error("Project manual unavailable");let r=`projects/${n.id}/project`;return await t.put(`${r}/manual.md`,e.content,"text/markdown"),await t.put(`${r}/manual.json`,JSON.stringify({version:1,projectId:n.id,projectName:n.name,digest:e.digest,bytes:e.bytes,ruleCount:e.rules.length,enforceRules:e.rules.filter(o=>o.mode==="enforce").length,advisoryRules:e.rules.filter(o=>o.mode==="advisory").length,updatedAt:e.updatedAt},null,2)+`
48
+ `,"application/json"),e}function Ht(n,t){let e=n.indexOf(t);return e>=0?n[e+1]:void 0}function Jt(n){let t=J(),e=Ee(we({provider:t.storage.provider,huggingface:t.storage.huggingface,localRoot:t.storage.localRoot}),{attempts:3});return new N(e,n.id,n.name,n.remote??n.name)}async function Wt(){let[n="help",...t]=process.argv.slice(2),e=new I().detect(Ht(t,"--project")??process.cwd());if(n==="init"){let r=G(e);console.log(`\u2705 Project manual: ${r}`);return}if(n==="show"){let r=H(e,!1);if(!r){console.log("No .toolnet/PROJECT.md yet.");return}process.stdout.write(Gt(r.path,"utf8"));return}if(n==="sync"){let r=await ke(e,Jt(e));console.log(JSON.stringify({project:e.name,path:r.path,digest:r.digest,bytes:r.bytes,rules:r.rules.length,enforce:r.rules.filter(o=>o.mode==="enforce").length,advisory:r.rules.filter(o=>o.mode==="advisory").length},null,2));return}console.log(`Project Manual
49
49
 
50
50
  Commands:
51
51
  init [--project PATH]
52
52
  show [--project PATH]
53
53
  sync [--project PATH]
54
- `)}ot().catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1)});
54
+ `)}Wt().catch(n=>{console.error(n instanceof Error?n.message:n),process.exit(1)});
@@ -799,6 +799,55 @@
799
799
  <script src="/vendor/3d-force-graph.min.js"></script>
800
800
 
801
801
  <script>
802
+ const GRAPH_TOKEN_SESSION_KEY = "toolnet.graph.access-token";
803
+ function readGraphToken() {
804
+ try {
805
+ return sessionStorage.getItem(GRAPH_TOKEN_SESSION_KEY) || "";
806
+ } catch {
807
+ return "";
808
+ }
809
+ }
810
+ function writeGraphToken(value) {
811
+ try {
812
+ if (value) {
813
+ sessionStorage.setItem(GRAPH_TOKEN_SESSION_KEY, value);
814
+ return;
815
+ }
816
+ sessionStorage.removeItem(GRAPH_TOKEN_SESSION_KEY);
817
+ } catch {
818
+ // Session storage is optional.
819
+ }
820
+ }
821
+ function graphApiRequest(input, init, token) {
822
+ const headers = new Headers(init?.headers);
823
+ if (token) {
824
+ headers.set("Authorization", `Bearer ${token}`);
825
+ }
826
+ return window.fetch(input, {
827
+ ...init,
828
+ headers,
829
+ cache: "no-store",
830
+ });
831
+ }
832
+ async function graphApiFetch(input, init = {}) {
833
+ let token = readGraphToken();
834
+ let response = await graphApiRequest(input, init, token);
835
+ if (response.status !== 401) {
836
+ return response;
837
+ }
838
+ // Existing token is invalid/stale.
839
+ writeGraphToken("");
840
+ token = window.prompt("ToolNet Graph access token")?.trim() || "";
841
+ if (!token) {
842
+ return response;
843
+ }
844
+ writeGraphToken(token);
845
+ response = await graphApiRequest(input, init, token);
846
+ if (response.status === 401) {
847
+ writeGraphToken("");
848
+ }
849
+ return response;
850
+ }
802
851
  const state = {
803
852
  raw:
804
853
  null,
@@ -2534,7 +2583,7 @@
2534
2583
  }
2535
2584
 
2536
2585
  const response =
2537
- await fetch(
2586
+ await graphApiFetch(
2538
2587
  `/api/graph?${params.toString()}`
2539
2588
  );
2540
2589
 
@@ -2750,7 +2799,7 @@
2750
2799
 
2751
2800
  async function main() {
2752
2801
  const response =
2753
- await fetch(
2802
+ await graphApiFetch(
2754
2803
  "/api/projects"
2755
2804
  );
2756
2805
 
@@ -1,17 +1,17 @@
1
1
  import{mkdirSync as W,mkdtempSync as h,readFileSync as J,readdirSync as Tn,rmSync as b,writeFileSync as T}from"node:fs";import{tmpdir as v}from"node:os";import{join as l}from"node:path";import{existsSync as qt,readFileSync as Bt}from"node:fs";import{join as Ht}from"node:path";import{randomUUID as Ke}from"node:crypto";import{closeSync as De,existsSync as se,fsyncSync as We,mkdirSync as ze,openSync as qe,readFileSync as Be,statSync as He,unlinkSync as ie,writeFileSync as Ve}from"node:fs";import{dirname as Ue,join as Je}from"node:path";var Qe=new Int32Array(new SharedArrayBuffer(4));function Ye(t){t<=0||Atomics.wait(Qe,0,0,t)}function Ze(t){return Je(t.rootPath,".toolnet","work",".current.lock")}function Xe(t){if(!Number.isInteger(t)||t<=0)return!1;try{return process.kill(t,0),!0}catch(e){return e?.code!=="ESRCH"}}function ce(t){if(!se(t))return null;try{let e=JSON.parse(Be(t,"utf8"));return e.version!==1||typeof e.token!="string"||typeof e.pid!="number"||typeof e.acquiredAt!="string"?null:{version:1,token:e.token,pid:e.pid,acquiredAt:e.acquiredAt}}catch{return null}}function Ge(t){try{return Date.now()-He(t).mtimeMs}catch{return 0}}function et(t,e){if(!se(t)||Ge(t)<e)return!1;let n=ce(t);return n?!Xe(n.pid):!0}function tt(t,e){if(!et(t,e))return!1;try{return ie(t),!0}catch{return!1}}function nt(t,e){let n={version:1,token:e,pid:process.pid,acquiredAt:new Date().toISOString()},r=qe(t,"wx",384);try{Ve(r,`${JSON.stringify(n,null,2)}
2
- `,{encoding:"utf8"}),We(r)}finally{De(r)}}function rt(t,e){if(ce(t)?.token===e)try{ie(t)}catch{}}function ot(t,e={}){let n=Math.max(100,e.timeoutMs??5e3),r=Math.max(5,e.retryMs??20),o=Math.max(n*2,e.staleMs??3e4),s=Ze(t);ze(Ue(s),{recursive:!0});let c=Ke(),u=Date.now()+n;for(;;)try{nt(s,c);let d=!1;return()=>{d||(d=!0,rt(s,c))}}catch(d){if(d?.code!=="EEXIST")throw d;if(tt(s,o))continue;if(Date.now()>=u)throw new Error(`Timed out acquiring project work lock: ${s}`);Ye(r)}}function ae(t,e,n={}){let r=ot(t,n);try{return e()}finally{r()}}import{closeSync as ft,existsSync as mt,fsyncSync as pt,mkdirSync as gt,openSync as kt,readFileSync as yt,renameSync as ht,writeFileSync as bt}from"node:fs";import{dirname as vt,join as St}from"node:path";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"}],it=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function ue(t){return t.normalize("NFKC").trim().toLowerCase()}function ct(t){if(t.length===0)return 0;let e=new Map;for(let r of t)e.set(r,(e.get(r)??0)+1);let n=0;for(let r of e.values()){let o=r/t.length;n-=o*Math.log2(o)}return n}function at(t){return/^[a-f0-9]{32}$/iu.test(t)||/^[a-f0-9]{40}$/iu.test(t)||/^[a-f0-9]{64}$/iu.test(t)}function ut(t,e,n){let r=t.slice(Math.max(0,e-48),e),o=t.slice(n,Math.min(t.length,n+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${o}`)}function lt(t,e){return t.start<e.end&&e.start<t.end}function le(t){return t.sort((e,n)=>e.start!==n.start?e.start-n.start:n.end-n.start-(e.end-e.start))}var O=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let n of e.allowValues??[]){let r=ue(n);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let n=[];for(let s of st){let c=new RegExp(s.regex.source,s.regex.flags);for(let u of e.matchAll(c))u.index===void 0||!u[0]||this.allowed(u[0])||n.push({type:s.type,value:u[0],start:u.index,end:u.index+u[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&n.push(...this.entropyMatches(e));let r=le(n),o=[];for(let s of r)o.some(c=>lt(c,s))||o.push(s);return le(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let n=ue(e);return it.has(n)?!0:this.allowValues.has(n)}entropyMatches(e){let n=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of e.matchAll(r)){if(o.index===void 0||!o[0])continue;let s=o[0];this.allowed(s)||at(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||ut(e,o.index,o.index+s.length)&&(ct(s)<3.7||n.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return n}};var F=class{scanner;constructor(e={}){this.scanner=new O(e)}sanitize(e){let n=this.scanner.scan(e);if(n.length===0)return{text:e,redacted:0,secretTypes:[]};let r=e,o=[...n].sort((c,u)=>u.start-c.start),s=new Set;for(let c of o)s.add(c.type),r=r.slice(0,c.start)+`[REDACTED:${c.type}]`+r.slice(c.end);return{text:r,redacted:n.length,secretTypes:[...s].sort()}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(n=>this.sanitizeValue(n));if(e&&typeof e=="object"){let n={};for(let[r,o]of Object.entries(e)){let s=r.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")){n[r]="[REDACTED]";continue}n[r]=this.sanitizeValue(o)}return n}return e}};var dt=new F;function de(t){return dt.sanitizeValue(t)}function xt(t,e){gt(vt(t),{recursive:!0});let n=`${t}.tmp-${process.pid}-${Date.now()}`,r=kt(n,"w",384);try{bt(r,`${JSON.stringify(e,null,2)}
3
- `,{encoding:"utf8"}),pt(r)}finally{ft(r)}ht(n,t)}function ye(t){return St(t.rootPath,".toolnet","work","current.json")}function N(t){let e=ye(t);if(!mt(e))return null;try{let n=JSON.parse(yt(e,"utf8"));return n.version!==1||n.projectId!==t.id?null:n}catch{return null}}function j(t){return t.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function p(t,e,n){let r=[],o=new Set;for(let s of[...t,...e].reverse()){let c=j(s);if(!(!c||o.has(c))&&(o.add(c),r.push(s),r.length>=n))break}return r.reverse()}function Mt(t,e,n=20){let r=new Map;for(let o of[...t,...e]){let s=`${o.kind}|${j(o.command)}`;r.delete(s),r.set(s,o)}return Array.from(r.values()).slice(-n)}function wt(t){return t.kind==="phase"?/^Phase\s+\d+$/iu.test(t.text):t.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(t.text):!1}function fe(t,e){let n=e.status??t?.status??"pending",r=n;t?.status==="completed"&&n!=="completed"&&(r="completed"),t&&n==="pending"&&(t.status==="in_progress"||t.status==="blocked")&&(r=t.status);let o=t&&wt(e)?t.title:e.text;return{id:t?.id??e.id,title:o,status:r,order:e.order??t?.order,confidence:Math.max(t?.confidence??0,e.confidence),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}function me(t){let e=new Map;for(let n of t){let r=n.order!==void 0?`order:${n.order}`:j(n.title);e.set(r,n)}return e}function pe(t){return t.order!==void 0?`order:${t.order}`:j(t.key||t.text)}function ge(t){return Array.from(t).sort((e,n)=>{let r=e.order??Number.MAX_SAFE_INTEGER,o=n.order??Number.MAX_SAFE_INTEGER;return r!==o?r-o:e.updatedAt.localeCompare(n.updatedAt)})}function ke(t){return t.find(e=>e.status==="in_progress")??t.find(e=>e.status==="blocked")??t.find(e=>e.status==="pending")}function Ct(t,e){let n=N(t),r=me(n?.phases??[]),o=me(n?.tasks??[]),s=n?.currentRequest,c=n?.currentActivity,u=n?.goal,d=n?.plan,w=n?.lastSession,f=[],g=[],A=[],Y=[],Z=[],_=[...n?.activeFiles??[]],X=[],G=[],ee=[],te=[],ne=[],re=[],P=[...e].sort((i,m)=>{let E=i.occurredAt.localeCompare(m.occurredAt);return E!==0?E:i.sequence-m.sequence});for(let i of P)switch(i.kind){case"request":s=i.text;break;case"activity":c=i.text;break;case"goal":u=i.text;break;case"plan":d=i.text;break;case"phase":{let m=pe(i);r.set(m,fe(r.get(m),i));break}case"task":{let m=pe(i);o.set(m,fe(o.get(m),i));break}case"decision":f.push(i.text);break;case"blocker":g.push(i.text);break;case"warning":A.push(i.text);break;case"next_action":Y.push(i.text);break;case"file":{Z.push(i.text);let m=i.fileAction??"active",E=_.indexOf(i.text);E>=0&&_.splice(E,1),m!=="deleted"&&_.push(i.text),m==="modified"?X.push(i.text):m==="created"?G.push(i.text):m==="deleted"&&ee.push(i.text);break}case"command":te.push(i.text);break;case"test":ne.push(i.text),i.checkKind&&re.push({kind:i.checkKind,command:i.text,status:i.checkStatus??"unknown",updatedAt:i.occurredAt,agent:i.agent,nativeSessionId:i.nativeSessionId});break;case"session":w={agent:i.agent,nativeSessionId:i.nativeSessionId,sessionKey:i.sessionKey,updatedAt:i.occurredAt};break}let I=ge(r.values()),C=ge(o.values()),z=ke(I),R=ke(C),Ne=p(n?.nextActions??[],[...Y,...R?[R.title]:[],...!R&&z?[z.title]:[],...C.filter(i=>i.status==="pending").slice(0,5).map(i=>i.title)],8),je=p(n?.blockers??[],[...g,...I.filter(i=>i.status==="blocked").map(i=>i.title),...C.filter(i=>i.status==="blocked").map(i=>i.title)],20),Le=P.length>0?P[P.length-1].occurredAt:n?.updatedAt??new Date().toISOString(),$e={version:1,projectId:t.id,projectName:t.name,currentRequest:s,currentActivity:c,goal:u,plan:d,phases:I,tasks:C,decisions:p(n?.decisions??[],f,20),blockers:je,warnings:p(n?.warnings??[],A,20),nextActions:Ne,filesTouched:p(n?.filesTouched??[],Z,30),activeFiles:p([],_,5),modifiedFiles:p(n?.modifiedFiles??[],X,30),createdFiles:p(n?.createdFiles??[],G,30),deletedFiles:p(n?.deletedFiles??[],ee,30),commands:p(n?.commands??[],te,20),tests:p(n?.tests??[],ne,20),checks:Mt(n?.checks??[],re,20),currentPhase:z,currentTask:R,progress:{phasesTotal:I.length,phasesCompleted:I.filter(i=>i.status==="completed").length,tasksTotal:C.length,tasksCompleted:C.filter(i=>i.status==="completed").length,blocked:I.filter(i=>i.status==="blocked").length+C.filter(i=>i.status==="blocked").length},lastSession:w,updatedAt:Le},oe=de($e);return xt(ye(t),oe),oe}function he(t,e){return ae(t,()=>Ct(t,e))}import{existsSync as Tt,mkdirSync as Bn,readFileSync as At,renameSync as Hn,writeFileSync as Vn}from"node:fs";import{dirname as Jn,join as It}from"node:path";function Ft(t){return It(t.rootPath,".toolnet","context","session-origin.json")}function q(t){let e=Ft(t);if(!Tt(e))return null;try{let n=JSON.parse(At(e,"utf8"));return n.version!==1||n.projectId!==t.id?null:n}catch{return null}}function Et(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function L(t){let e=Et(t),n=/(?: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 n&&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 $t,mkdirSync as Wr,readFileSync as Kt}from"node:fs";import{join as be}from"node:path";import{existsSync as Pt,readFileSync as Rt}from"node:fs";import{homedir as Ot}from"node:os";import{join as Nt}from"node:path";function jt(t){let e=t.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
4
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Lt(){let t=process.env.TOOLNET_GLOBAL_ENV??Nt(Ot(),".config","toolnet-memory",".env");if(!Pt(t))return;let e=Rt(t,"utf8");for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=jt(r.slice(o+1)))}}Lt();var Ar=new F;function Dt(t){return be(t.rootPath,".toolnet","memory","checkpoints")}function Wt(t){return be(Dt(t),"latest.json")}function zt(t){let e=Wt(t);if(!$t(e))return null;try{let n=JSON.parse(Kt(e,"utf8"));return n.schema!=="toolnet.memory.checkpoint.v1"||n.project.id!==t.id?null:n}catch{return null}}function ve(t){return zt(t)}var Vt={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}},Ut={checkpoint:20,handoff:18,"session-origin":14,"work-state":8};function Jt(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function a(t,e){let n=e.value.replace(/\s+/g," ").trim();n&&t.push({...e,value:n,score:0})}function Qt(t){let e=Ht(t.rootPath,".toolnet","work","handoff-latest.json");if(!qt(e))return null;try{let r=JSON.parse(Bt(e,"utf8")).continuity;return r?.schema!=="toolnet.handoff.v2"||r.project.id!==t.id?null:r}catch{return null}}function Yt(t){let e=ve(t);if(!e)return[];let n=[];e.request&&a(n,{kind:"request",value:e.request,source:"checkpoint"}),e.activity&&a(n,{kind:"activity",value:e.activity,source:"checkpoint"}),e.current.phase&&a(n,{kind:"phase",value:e.current.phase.title,source:"checkpoint"}),e.current.task&&a(n,{kind:"task",value:e.current.task.title,source:"checkpoint"});let r=e.files.active.at(-1);r&&a(n,{kind:"file",value:r,source:"checkpoint"});for(let o of e.nextActions)a(n,{kind:"next_action",value:o,source:"checkpoint"});for(let o of e.blockers)a(n,{kind:"blocker",value:o,source:"checkpoint"});for(let o of e.completed.tasks)a(n,{kind:"completed",value:o,source:"checkpoint"});for(let o of e.remaining.tasks)a(n,{kind:"todo",value:o,source:"checkpoint"});for(let o of e.durableFacts)a(n,{kind:o.kind,value:o.content,source:"checkpoint"});return n}function Zt(t){let e=Qt(t);if(!e)return[];let n=[];a(n,{kind:"previous_agent",value:e.source.agent,source:"handoff"}),e.request&&a(n,{kind:"request",value:e.request,source:"handoff"}),e.activity&&a(n,{kind:"activity",value:e.activity,source:"handoff"}),e.goal&&a(n,{kind:"goal",value:e.goal,source:"handoff"}),e.current.phase&&a(n,{kind:"phase",value:e.current.phase.title,source:"handoff"}),e.current.task&&a(n,{kind:"task",value:e.current.task.title,source:"handoff"}),e.files.current&&a(n,{kind:"file",value:e.files.current,source:"handoff"}),e.nextAction&&a(n,{kind:"next_action",value:e.nextAction,source:"handoff"});for(let r of e.blockers)a(n,{kind:"blocker",value:r,source:"handoff"});for(let r of e.decisions)a(n,{kind:"decision",value:r,source:"handoff"});for(let r of e.completed.tasks)a(n,{kind:"completed",value:r,source:"handoff"});for(let r of e.remaining.todos)a(n,{kind:"todo",value:r,source:"handoff"});return a(n,{kind:"test",value:`Test status: ${e.tests.status}`,source:"handoff"}),a(n,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"handoff"}),n}function Xt(t){let e=q(t);if(!e)return[];let n=[];return a(n,{kind:"previous_agent",value:e.agent,source:"session-origin"}),e.currentPhase&&a(n,{kind:"phase",value:e.currentPhase,source:"session-origin"}),e.currentTask&&a(n,{kind:"task",value:e.currentTask,source:"session-origin"}),e.lastTouchedFile&&a(n,{kind:"file",value:e.lastTouchedFile,source:"session-origin"}),e.latestNextAction&&a(n,{kind:"next_action",value:e.latestNextAction,source:"session-origin"}),e.latestBlocker&&a(n,{kind:"blocker",value:e.latestBlocker,source:"session-origin"}),e.latestDecision&&a(n,{kind:"decision",value:e.latestDecision,source:"session-origin"}),n}function Gt(t){let e=N(t);if(!e)return[];let n=[];e.currentRequest&&a(n,{kind:"request",value:e.currentRequest,source:"work-state"}),e.currentActivity&&a(n,{kind:"activity",value:e.currentActivity,source:"work-state"}),e.goal&&a(n,{kind:"goal",value:e.goal,source:"work-state"}),e.currentPhase&&a(n,{kind:"phase",value:e.currentPhase.title,source:"work-state"}),e.currentTask&&a(n,{kind:"task",value:e.currentTask.title,source:"work-state"});let r=e.activeFiles?.at(-1)??e.filesTouched.at(-1);r&&a(n,{kind:"file",value:r,source:"work-state"});for(let o of e.nextActions.slice(0,5))a(n,{kind:"next_action",value:o,source:"work-state"});for(let o of e.blockers.slice(-5))a(n,{kind:"blocker",value:o,source:"work-state"});for(let o of e.decisions.slice(-5))a(n,{kind:"decision",value:o,source:"work-state"});for(let o of e.tasks.filter(s=>s.status==="completed").slice(-8))a(n,{kind:"completed",value:o.title,source:"work-state"});for(let o of e.tasks.filter(s=>s.status!=="completed"&&s.status!=="cancelled").slice(0,8))a(n,{kind:"todo",value:o.title,source:"work-state"});for(let o of e.tests.slice(-5))a(n,{kind:"test",value:o,source:"work-state"});return a(n,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"work-state"}),n}function en(t,e){let n=Vt[t];return e.map((r,o)=>({...r,score:(n[r.kind]??10)+Ut[r.source]+Math.max(0,5-o*.01)}))}function tn(t){let e=new Map;for(let n of t){let r=`${n.kind}:${Jt(n.value)}`,o=e.get(r);(!o||n.score>o.score)&&e.set(r,n)}return[...e.values()]}function $(t,e,n={}){let r=L(e),o=[...Yt(t),...Zt(t),...Xt(t),...Gt(t)],s=tn(en(r,o)).sort((f,g)=>g.score-f.score),c=Math.max(1,n.maxFacts??12),u=Math.max(300,n.maxChars??3200),d=[],w=0;for(let f of s){if(d.length>=c)break;let g=f.kind.length+f.value.length+f.source.length+16;d.length>0&&w+g>u||(d.push(f),w+=g)}return{intent:r,facts:d,context:{project:{id:t.id,name:t.name},intent:r,selectedFacts:d.map(f=>({kind:f.kind,value:f.value,source:f.source}))},stats:{candidates:o.length,selected:d.length,chars:w}}}var Se={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"},xe=["previous_agent","request","activity","phase","task","file","todo","next_action","blocker","decision","rule","architecture","fix","completed","test","progress","goal"],nn={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:xe};function rn(t){let e=t.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 on(t){let e=t.filter(r=>r.source==="handoff");if(e.length===0)return t;let n=t.filter(r=>r.source==="checkpoint"&&["rule","architecture","fix"].includes(r.kind));return[...e,...n]}function sn(t){let e=new Set,n=[];for(let r of t){let o=`${r.kind}:${r.value.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}`;e.has(o)||(e.add(o),n.push(r))}return n}function Me(t){switch(t){case"todo":case"blocker":case"decision":case"completed":return 3;default:return 1}}function cn(t,e){let n=[];for(let r of e){let o=t.filter(s=>s.kind===r).slice(0,Me(r));for(let s of o)n.push(`${Se[r]}: ${s.value}`)}return n.join(`
5
- `)}function an(t,e){let n=nn[e]??xe,r=[];for(let o of n){let s=t.filter(c=>c.kind===o).slice(0,Me(o));for(let c of s)r.push(`${Se[o]}: ${c.value}`)}return r.join(`
2
+ `,{encoding:"utf8"}),We(r)}finally{De(r)}}function rt(t,e){if(ce(t)?.token===e)try{ie(t)}catch{}}function ot(t,e={}){let n=Math.max(100,e.timeoutMs??5e3),r=Math.max(5,e.retryMs??20),o=Math.max(n*2,e.staleMs??3e4),s=Ze(t);ze(Ue(s),{recursive:!0});let c=Ke(),u=Date.now()+n;for(;;)try{nt(s,c);let d=!1;return()=>{d||(d=!0,rt(s,c))}}catch(d){if(d?.code!=="EEXIST")throw d;if(tt(s,o))continue;if(Date.now()>=u)throw new Error(`Timed out acquiring project work lock: ${s}`);Ye(r)}}function ae(t,e,n={}){let r=ot(t,n);try{return e()}finally{r()}}import{closeSync as ft,existsSync as mt,fsyncSync as pt,mkdirSync as gt,openSync as kt,readFileSync as yt,renameSync as ht,writeFileSync as bt}from"node:fs";import{dirname as vt,join as xt}from"node:path";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"}],it=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function ue(t){return t.normalize("NFKC").trim().toLowerCase()}function ct(t){if(t.length===0)return 0;let e=new Map;for(let r of t)e.set(r,(e.get(r)??0)+1);let n=0;for(let r of e.values()){let o=r/t.length;n-=o*Math.log2(o)}return n}function at(t){return/^[a-f0-9]{32}$/iu.test(t)||/^[a-f0-9]{40}$/iu.test(t)||/^[a-f0-9]{64}$/iu.test(t)}function ut(t,e,n){let r=t.slice(Math.max(0,e-48),e),o=t.slice(n,Math.min(t.length,n+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${r} ${o}`)}function lt(t,e){return t.start<e.end&&e.start<t.end}function le(t){return t.sort((e,n)=>e.start!==n.start?e.start-n.start:n.end-n.start-(e.end-e.start))}var N=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let n of e.allowValues??[]){let r=ue(n);r&&this.allowValues.add(r)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let n=[];for(let s of st){let c=new RegExp(s.regex.source,s.regex.flags);for(let u of e.matchAll(c))u.index===void 0||!u[0]||this.allowed(u[0])||n.push({type:s.type,value:u[0],start:u.index,end:u.index+u[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&n.push(...this.entropyMatches(e));let r=le(n),o=[];for(let s of r)o.some(c=>lt(c,s))||o.push(s);return le(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let n=ue(e);return it.has(n)?!0:this.allowValues.has(n)}entropyMatches(e){let n=[],r=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of e.matchAll(r)){if(o.index===void 0||!o[0])continue;let s=o[0];this.allowed(s)||at(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||ut(e,o.index,o.index+s.length)&&(ct(s)<3.7||n.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return n}};var F=class{scanner;constructor(e={}){this.scanner=new N(e)}sanitize(e){let n=this.scanner.scan(e);if(n.length===0)return{text:e,redacted:0,secretTypes:[]};let r=e,o=[...n].sort((c,u)=>u.start-c.start),s=new Set;for(let c of o)s.add(c.type),r=r.slice(0,c.start)+`[REDACTED:${c.type}]`+r.slice(c.end);return{text:r,redacted:n.length,secretTypes:[...s].sort()}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(n=>this.sanitizeValue(n));if(e&&typeof e=="object"){let n={};for(let[r,o]of Object.entries(e)){let s=r.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")){n[r]="[REDACTED]";continue}n[r]=this.sanitizeValue(o)}return n}return e}};var dt=new F;function de(t){return dt.sanitizeValue(t)}function St(t,e){gt(vt(t),{recursive:!0});let n=`${t}.tmp-${process.pid}-${Date.now()}`,r=kt(n,"w",384);try{bt(r,`${JSON.stringify(e,null,2)}
3
+ `,{encoding:"utf8"}),pt(r)}finally{ft(r)}ht(n,t)}function ye(t){return xt(t.rootPath,".toolnet","work","current.json")}function O(t){let e=ye(t);if(!mt(e))return null;try{let n=JSON.parse(yt(e,"utf8"));return n.version!==1||n.projectId!==t.id?null:n}catch{return null}}function j(t){return t.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function p(t,e,n){let r=[],o=new Set;for(let s of[...t,...e].reverse()){let c=j(s);if(!(!c||o.has(c))&&(o.add(c),r.push(s),r.length>=n))break}return r.reverse()}function Mt(t,e,n=20){let r=new Map;for(let o of[...t,...e]){let s=`${o.kind}|${j(o.command)}`;r.delete(s),r.set(s,o)}return Array.from(r.values()).slice(-n)}function wt(t){return t.kind==="phase"?/^Phase\s+\d+$/iu.test(t.text):t.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(t.text):!1}function fe(t,e){let n=e.status??t?.status??"pending",r=n;t?.status==="completed"&&n!=="completed"&&(r="completed"),t&&n==="pending"&&(t.status==="in_progress"||t.status==="blocked")&&(r=t.status);let o=t&&wt(e)?t.title:e.text;return{id:t?.id??e.id,title:o,status:r,order:e.order??t?.order,confidence:Math.max(t?.confidence??0,e.confidence),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}function me(t){let e=new Map;for(let n of t){let r=n.order!==void 0?`order:${n.order}`:j(n.title);e.set(r,n)}return e}function pe(t){return t.order!==void 0?`order:${t.order}`:j(t.key||t.text)}function ge(t){return Array.from(t).sort((e,n)=>{let r=e.order??Number.MAX_SAFE_INTEGER,o=n.order??Number.MAX_SAFE_INTEGER;return r!==o?r-o:e.updatedAt.localeCompare(n.updatedAt)})}function ke(t){return t.find(e=>e.status==="in_progress")??t.find(e=>e.status==="blocked")??t.find(e=>e.status==="pending")}function Ct(t,e){let n=O(t),r=me(n?.phases??[]),o=me(n?.tasks??[]),s=n?.currentRequest,c=n?.currentActivity,u=n?.goal,d=n?.plan,w=n?.lastSession,f=[],g=[],A=[],Y=[],Z=[],_=[...n?.activeFiles??[]],X=[],G=[],ee=[],te=[],ne=[],re=[],P=[...e].sort((i,m)=>{let E=i.occurredAt.localeCompare(m.occurredAt);return E!==0?E:i.sequence-m.sequence});for(let i of P)switch(i.kind){case"request":s=i.text;break;case"activity":c=i.text;break;case"goal":u=i.text;break;case"plan":d=i.text;break;case"phase":{let m=pe(i);r.set(m,fe(r.get(m),i));break}case"task":{let m=pe(i);o.set(m,fe(o.get(m),i));break}case"decision":f.push(i.text);break;case"blocker":g.push(i.text);break;case"warning":A.push(i.text);break;case"next_action":Y.push(i.text);break;case"file":{Z.push(i.text);let m=i.fileAction??"active",E=_.indexOf(i.text);E>=0&&_.splice(E,1),m!=="deleted"&&_.push(i.text),m==="modified"?X.push(i.text):m==="created"?G.push(i.text):m==="deleted"&&ee.push(i.text);break}case"command":te.push(i.text);break;case"test":ne.push(i.text),i.checkKind&&re.push({kind:i.checkKind,command:i.text,status:i.checkStatus??"unknown",updatedAt:i.occurredAt,agent:i.agent,nativeSessionId:i.nativeSessionId});break;case"session":w={agent:i.agent,nativeSessionId:i.nativeSessionId,sessionKey:i.sessionKey,updatedAt:i.occurredAt};break}let I=ge(r.values()),C=ge(o.values()),z=ke(I),R=ke(C),Oe=p(n?.nextActions??[],[...Y,...R?[R.title]:[],...!R&&z?[z.title]:[],...C.filter(i=>i.status==="pending").slice(0,5).map(i=>i.title)],8),je=p(n?.blockers??[],[...g,...I.filter(i=>i.status==="blocked").map(i=>i.title),...C.filter(i=>i.status==="blocked").map(i=>i.title)],20),Le=P.length>0?P[P.length-1].occurredAt:n?.updatedAt??new Date().toISOString(),$e={version:1,projectId:t.id,projectName:t.name,currentRequest:s,currentActivity:c,goal:u,plan:d,phases:I,tasks:C,decisions:p(n?.decisions??[],f,20),blockers:je,warnings:p(n?.warnings??[],A,20),nextActions:Oe,filesTouched:p(n?.filesTouched??[],Z,30),activeFiles:p([],_,5),modifiedFiles:p(n?.modifiedFiles??[],X,30),createdFiles:p(n?.createdFiles??[],G,30),deletedFiles:p(n?.deletedFiles??[],ee,30),commands:p(n?.commands??[],te,20),tests:p(n?.tests??[],ne,20),checks:Mt(n?.checks??[],re,20),currentPhase:z,currentTask:R,progress:{phasesTotal:I.length,phasesCompleted:I.filter(i=>i.status==="completed").length,tasksTotal:C.length,tasksCompleted:C.filter(i=>i.status==="completed").length,blocked:I.filter(i=>i.status==="blocked").length+C.filter(i=>i.status==="blocked").length},lastSession:w,updatedAt:Le},oe=de($e);return St(ye(t),oe),oe}function he(t,e){return ae(t,()=>Ct(t,e))}import{existsSync as Tt,mkdirSync as Bn,readFileSync as At,renameSync as Hn,writeFileSync as Vn}from"node:fs";import{dirname as Jn,join as It}from"node:path";function Ft(t){return It(t.rootPath,".toolnet","context","session-origin.json")}function q(t){let e=Ft(t);if(!Tt(e))return null;try{let n=JSON.parse(At(e,"utf8"));return n.version!==1||n.projectId!==t.id?null:n}catch{return null}}function Et(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function L(t){let e=Et(t),n=/(?: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 n&&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 $t,mkdirSync as Wr,readFileSync as Kt}from"node:fs";import{join as be}from"node:path";import{existsSync as Pt,readFileSync as Rt}from"node:fs";import{homedir as Nt}from"node:os";import{join as Ot}from"node:path";function jt(t){let e=t.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
4
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Lt(){let t=process.env.TOOLNET_GLOBAL_ENV??Ot(Nt(),".config","toolnet-memory",".env");if(!Pt(t))return;let e=Rt(t,"utf8");for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=jt(r.slice(o+1)))}}Lt();var Ar=new F;function Dt(t){return be(t.rootPath,".toolnet","memory","checkpoints")}function Wt(t){return be(Dt(t),"latest.json")}function zt(t){let e=Wt(t);if(!$t(e))return null;try{let n=JSON.parse(Kt(e,"utf8"));return n.schema!=="toolnet.memory.checkpoint.v1"||n.project.id!==t.id?null:n}catch{return null}}function ve(t){return zt(t)}var Vt={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}},Ut={checkpoint:20,handoff:18,"session-origin":14,"work-state":8};function Jt(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function a(t,e){let n=e.value.replace(/\s+/g," ").trim();n&&t.push({...e,value:n,score:0})}function Qt(t){let e=Ht(t.rootPath,".toolnet","work","handoff-latest.json");if(!qt(e))return null;try{let r=JSON.parse(Bt(e,"utf8")).continuity;return r?.schema!=="toolnet.handoff.v2"||r.project.id!==t.id?null:r}catch{return null}}function Yt(t){let e=ve(t);if(!e)return[];let n=[];e.request&&a(n,{kind:"request",value:e.request,source:"checkpoint"}),e.activity&&a(n,{kind:"activity",value:e.activity,source:"checkpoint"}),e.current.phase&&a(n,{kind:"phase",value:e.current.phase.title,source:"checkpoint"}),e.current.task&&a(n,{kind:"task",value:e.current.task.title,source:"checkpoint"});let r=e.files.active.at(-1);r&&a(n,{kind:"file",value:r,source:"checkpoint"});for(let o of e.nextActions)a(n,{kind:"next_action",value:o,source:"checkpoint"});for(let o of e.blockers)a(n,{kind:"blocker",value:o,source:"checkpoint"});for(let o of e.completed.tasks)a(n,{kind:"completed",value:o,source:"checkpoint"});for(let o of e.remaining.tasks)a(n,{kind:"todo",value:o,source:"checkpoint"});for(let o of e.durableFacts)a(n,{kind:o.kind,value:o.content,source:"checkpoint"});return n}function Zt(t){let e=Qt(t);if(!e)return[];let n=[];a(n,{kind:"previous_agent",value:e.source.agent,source:"handoff"}),e.request&&a(n,{kind:"request",value:e.request,source:"handoff"}),e.activity&&a(n,{kind:"activity",value:e.activity,source:"handoff"}),e.goal&&a(n,{kind:"goal",value:e.goal,source:"handoff"}),e.current.phase&&a(n,{kind:"phase",value:e.current.phase.title,source:"handoff"}),e.current.task&&a(n,{kind:"task",value:e.current.task.title,source:"handoff"}),e.files.current&&a(n,{kind:"file",value:e.files.current,source:"handoff"}),e.nextAction&&a(n,{kind:"next_action",value:e.nextAction,source:"handoff"});for(let r of e.blockers)a(n,{kind:"blocker",value:r,source:"handoff"});for(let r of e.decisions)a(n,{kind:"decision",value:r,source:"handoff"});for(let r of e.completed.tasks)a(n,{kind:"completed",value:r,source:"handoff"});for(let r of e.remaining.todos)a(n,{kind:"todo",value:r,source:"handoff"});return a(n,{kind:"test",value:`Test status: ${e.tests.status}`,source:"handoff"}),a(n,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"handoff"}),n}function Xt(t){let e=q(t);if(!e)return[];let n=[];return a(n,{kind:"previous_agent",value:e.agent,source:"session-origin"}),e.currentPhase&&a(n,{kind:"phase",value:e.currentPhase,source:"session-origin"}),e.currentTask&&a(n,{kind:"task",value:e.currentTask,source:"session-origin"}),e.lastTouchedFile&&a(n,{kind:"file",value:e.lastTouchedFile,source:"session-origin"}),e.latestNextAction&&a(n,{kind:"next_action",value:e.latestNextAction,source:"session-origin"}),e.latestBlocker&&a(n,{kind:"blocker",value:e.latestBlocker,source:"session-origin"}),e.latestDecision&&a(n,{kind:"decision",value:e.latestDecision,source:"session-origin"}),n}function Gt(t){let e=O(t);if(!e)return[];let n=[];e.currentRequest&&a(n,{kind:"request",value:e.currentRequest,source:"work-state"}),e.currentActivity&&a(n,{kind:"activity",value:e.currentActivity,source:"work-state"}),e.goal&&a(n,{kind:"goal",value:e.goal,source:"work-state"}),e.currentPhase&&a(n,{kind:"phase",value:e.currentPhase.title,source:"work-state"}),e.currentTask&&a(n,{kind:"task",value:e.currentTask.title,source:"work-state"});let r=e.activeFiles?.at(-1)??e.filesTouched.at(-1);r&&a(n,{kind:"file",value:r,source:"work-state"});for(let o of e.nextActions.slice(0,5))a(n,{kind:"next_action",value:o,source:"work-state"});for(let o of e.blockers.slice(-5))a(n,{kind:"blocker",value:o,source:"work-state"});for(let o of e.decisions.slice(-5))a(n,{kind:"decision",value:o,source:"work-state"});for(let o of e.tasks.filter(s=>s.status==="completed").slice(-8))a(n,{kind:"completed",value:o.title,source:"work-state"});for(let o of e.tasks.filter(s=>s.status!=="completed"&&s.status!=="cancelled").slice(0,8))a(n,{kind:"todo",value:o.title,source:"work-state"});for(let o of e.tests.slice(-5))a(n,{kind:"test",value:o,source:"work-state"});return a(n,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"work-state"}),n}function en(t,e){let n=Vt[t];return e.map((r,o)=>({...r,score:(n[r.kind]??10)+Ut[r.source]+Math.max(0,5-o*.01)}))}function tn(t){let e=new Map;for(let n of t){let r=`${n.kind}:${Jt(n.value)}`,o=e.get(r);(!o||n.score>o.score)&&e.set(r,n)}return[...e.values()]}function $(t,e,n={}){let r=L(e),o=[...Yt(t),...Zt(t),...Xt(t),...Gt(t)],s=tn(en(r,o)).sort((f,g)=>g.score-f.score),c=Math.max(1,n.maxFacts??12),u=Math.max(300,n.maxChars??3200),d=[],w=0;for(let f of s){if(d.length>=c)break;let g=f.kind.length+f.value.length+f.source.length+16;d.length>0&&w+g>u||(d.push(f),w+=g)}return{intent:r,facts:d,context:{project:{id:t.id,name:t.name},intent:r,selectedFacts:d.map(f=>({kind:f.kind,value:f.value,source:f.source}))},stats:{candidates:o.length,selected:d.length,chars:w}}}var xe={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"},Se=["previous_agent","request","activity","phase","task","file","todo","next_action","blocker","decision","rule","architecture","fix","completed","test","progress","goal"],nn={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:Se};function rn(t){let e=t.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 on(t){let e=t.filter(r=>r.source==="handoff");if(e.length===0)return t;let n=t.filter(r=>r.source==="checkpoint"&&["rule","architecture","fix"].includes(r.kind));return[...e,...n]}function sn(t){let e=new Set,n=[];for(let r of t){let o=`${r.kind}:${r.value.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}`;e.has(o)||(e.add(o),n.push(r))}return n}function Me(t){switch(t){case"todo":case"blocker":case"decision":case"completed":return 3;default:return 1}}function cn(t,e){let n=[];for(let r of e){let o=t.filter(s=>s.kind===r).slice(0,Me(r));for(let s of o)n.push(`${xe[r]}: ${s.value}`)}return n.join(`
5
+ `)}function an(t,e){let n=nn[e]??Se,r=[];for(let o of n){let s=t.filter(c=>c.kind===o).slice(0,Me(o));for(let c of s)r.push(`${xe[o]}: ${c.value}`)}return r.join(`
6
6
  `)}function k(t,e){let n=$(t,e,{maxFacts:32,maxChars:6400}),r=sn(on(n.facts)),o=rn(e)?cn(r,["todo","task","next_action","file"]):an(r,String(n.intent));return o?{intent:String(n.intent),answer:o,source:r[0]?.source??"none"}:{intent:String(n.intent),answer:"ToolNet ch\u01B0a c\xF3 \u0111\u1EE7 memory cho c\xE2u h\u1ECFi n\xE0y.",source:"none"}}async function we(t,e){let n=k(t,e);return n.answer?{answer:n.answer,usedAi:!1}:{answer:"ToolNet ch\u01B0a c\xF3 \u0111\u1EE7 memory cho c\xE2u h\u1ECFi n\xE0y.",usedAi:!1}}import{existsSync as un,mkdirSync as ln,readFileSync as dn,renameSync as fn,rmSync as mn,writeFileSync as pn}from"node:fs";import{join as Ce}from"node:path";var Te=720*60*1e3;function Ae(t){return Ce(t.rootPath,".toolnet","context","memory-agent-conversation.json")}function K(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}function gn(t){let e=K(t);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(n=>n.test(e))}function kn(t){if(gn(t))return!1;let e=K(t),n=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:n.length<=5&&/\b(?:nó|đó|vậy|thế|that|it|this)\b/u.test(e)}function Ie(t,e,n){let r=Ae(t);if(!un(r))return null;try{let o=JSON.parse(dn(r,"utf8"));if(o.schema!=="toolnet.memory-conversation.v1"||o.version!==1||o.projectId!==t.id||!Array.isArray(o.focus))return null;let s=Date.parse(o.updatedAt);return!Number.isFinite(s)||e-s>n?null:o}catch{return null}}function yn(t,e=8){let n=new Set,r=[];for(let o of t){let s=o.value.replace(/\s+/gu," ").trim();if(!s)continue;let c=[o.kind,K(s)].join(":");if(!n.has(c)&&(n.add(c),r.push({...o,value:s}),r.length>=e))break}return r}function hn(t,e){let n=Ae(t),r=Ce(t.rootPath,".toolnet","context");ln(r,{recursive:!0,mode:448});let o=`${n}.${process.pid}.tmp`;try{pn(o,JSON.stringify(e,null,2)+`
7
7
  `,{encoding:"utf8",mode:384}),fn(o,n)}finally{mn(o,{force:!0})}}function bn(t){return t.focus.slice(0,8).map(e=>`- ${e.kind}: ${e.value}`).join(`
8
8
  `)}function vn(t){let e=K(t);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 B(t,e,n={}){let r=n.now??Date.now(),o=n.maxAgeMs??Te,s=Ie(t,r,o),c=!!(s&&s.focus.length>0&&kn(e)&&!vn(e)),u=c&&s?[e.trim(),"","Previous ToolNet Memory conversation focus:",bn(s),"","Use the compact focus only to resolve the current follow-up question.","Do not reconstruct or request raw session transcripts."].join(`
9
- `):e.trim(),d=L(e),f=$(t,u,{maxFacts:8,maxChars:1800}).facts.map(A=>({kind:A.kind,value:A.value,source:A.source})),g=yn(c&&s?[...f,...s.focus]:f);return hn(t,{schema:"toolnet.memory-conversation.v1",version:1,projectId:t.id,updatedAt:new Date(r).toISOString(),lastIntent:d,focus:g}),{question:u,originalQuestion:e,usedPriorFocus:c,previousIntent:s?.lastIntent,currentIntent:d,priorFocus:s?.focus??[],focusCount:g.length}}function Fe(t,e={}){return Ie(t,e.now??Date.now(),e.maxAgeMs??Te)}import{existsSync as Sn,readFileSync as xn,writeFileSync as Mn}from"node:fs";import{join as wn}from"node:path";var _e="<!-- TOOLNET:STABLE-WORK:BEGIN -->",H="<!-- TOOLNET:STABLE-WORK:END -->";function V(t){switch(t.status){case"completed":return"[x]";case"in_progress":return"[~]";case"blocked":return"[!]";case"cancelled":return"[-]";default:return"[ ]"}}function y(t,e){return e.length?["",`${t}:`,...e.map(n=>`- ${n}`)]:[]}function Ee(t,e){return e.length?["",`${t}:`,...e.map(n=>`- ${V(n)} ${n.title}`)]:[]}function Cn(t){let e=[_e,"# ToolNet Stable Work State","",`Updated: ${t.updatedAt}`];return t.lastSession&&e.push(`Last agent: ${t.lastSession.agent}`,`Last session: ${t.lastSession.nativeSessionId}`),t.currentRequest&&e.push("","Current request:",t.currentRequest),t.currentActivity&&e.push("","Current activity:",t.currentActivity),t.goal&&e.push("","Goal:",t.goal),t.plan&&e.push("","Plan:",t.plan),t.currentPhase&&e.push("","Current phase:",`${V(t.currentPhase)} ${t.currentPhase.title}`),t.currentTask&&e.push("","Current task:",`${V(t.currentTask)} ${t.currentTask.title}`),e.push(...Ee("Phases",t.phases)),e.push(...Ee("TODO / Tasks",t.tasks)),e.push(...y("Next actions",t.nextActions)),e.push(...y("Blockers",t.blockers)),e.push(...y("Important decisions",t.decisions)),e.push(...y("Active files",t.activeFiles??[])),e.push(...y("Modified files",t.modifiedFiles??[])),e.push(...y("Created files",t.createdFiles??[])),e.push(...y("Deleted files",t.deletedFiles??[])),e.push(...y("Files touched",t.filesTouched)),e.push(...y("Recent commands",t.commands??[])),e.push(...y("Checks",(t.checks??[]).map(n=>`[${n.status}] ${n.kind}: ${n.command}`))),e.push("","Progress:",`- Phases: ${t.progress.phasesCompleted}/${t.progress.phasesTotal}`,`- Tasks: ${t.progress.tasksCompleted}/${t.progress.tasksTotal}`,`- Blocked: ${t.progress.blocked}`,"","Continuation:","- Resume current unfinished task.","- Never redo completed TODO/Phase unless explicitly requested.","- Ask ToolNet Memory for deeper history only when necessary.",H),e.join(`
10
- `)}function Pe(t,e){let n=wn(t.rootPath,".toolnet","current.md"),r="";if(Sn(n))try{r=xn(n,"utf8")}catch{r=""}r=r.replace(/<!-- TOOLNET:AUTO-CURRENT:BEGIN -->[\s\S]*?<!-- TOOLNET:AUTO-CURRENT:END -->/gu,"");let o=Cn(e),s=r.indexOf(_e),c=r.indexOf(H),u;s>=0&&c>=s?u=[r.slice(0,s).trimEnd(),o,r.slice(c+H.length).trimStart()].filter(Boolean).join(`
9
+ `):e.trim(),d=L(e),f=$(t,u,{maxFacts:8,maxChars:1800}).facts.map(A=>({kind:A.kind,value:A.value,source:A.source})),g=yn(c&&s?[...f,...s.focus]:f);return hn(t,{schema:"toolnet.memory-conversation.v1",version:1,projectId:t.id,updatedAt:new Date(r).toISOString(),lastIntent:d,focus:g}),{question:u,originalQuestion:e,usedPriorFocus:c,previousIntent:s?.lastIntent,currentIntent:d,priorFocus:s?.focus??[],focusCount:g.length}}function Fe(t,e={}){return Ie(t,e.now??Date.now(),e.maxAgeMs??Te)}import{existsSync as xn,readFileSync as Sn,writeFileSync as Mn}from"node:fs";import{join as wn}from"node:path";var _e="<!-- TOOLNET:STABLE-WORK:BEGIN -->",H="<!-- TOOLNET:STABLE-WORK:END -->";function V(t){switch(t.status){case"completed":return"[x]";case"in_progress":return"[~]";case"blocked":return"[!]";case"cancelled":return"[-]";default:return"[ ]"}}function y(t,e){return e.length?["",`${t}:`,...e.map(n=>`- ${n}`)]:[]}function Ee(t,e){return e.length?["",`${t}:`,...e.map(n=>`- ${V(n)} ${n.title}`)]:[]}function Cn(t){let e=[_e,"# ToolNet Stable Work State","",`Updated: ${t.updatedAt}`];return t.lastSession&&e.push(`Last agent: ${t.lastSession.agent}`,`Last session: ${t.lastSession.nativeSessionId}`),t.currentRequest&&e.push("","Current request:",t.currentRequest),t.currentActivity&&e.push("","Current activity:",t.currentActivity),t.goal&&e.push("","Goal:",t.goal),t.plan&&e.push("","Plan:",t.plan),t.currentPhase&&e.push("","Current phase:",`${V(t.currentPhase)} ${t.currentPhase.title}`),t.currentTask&&e.push("","Current task:",`${V(t.currentTask)} ${t.currentTask.title}`),e.push(...Ee("Phases",t.phases)),e.push(...Ee("TODO / Tasks",t.tasks)),e.push(...y("Next actions",t.nextActions)),e.push(...y("Blockers",t.blockers)),e.push(...y("Important decisions",t.decisions)),e.push(...y("Active files",t.activeFiles??[])),e.push(...y("Modified files",t.modifiedFiles??[])),e.push(...y("Created files",t.createdFiles??[])),e.push(...y("Deleted files",t.deletedFiles??[])),e.push(...y("Files touched",t.filesTouched)),e.push(...y("Recent commands",t.commands??[])),e.push(...y("Checks",(t.checks??[]).map(n=>`[${n.status}] ${n.kind}: ${n.command}`))),e.push("","Progress:",`- Phases: ${t.progress.phasesCompleted}/${t.progress.phasesTotal}`,`- Tasks: ${t.progress.tasksCompleted}/${t.progress.tasksTotal}`,`- Blocked: ${t.progress.blocked}`,"","Continuation:","- Resume current unfinished task.","- Never redo completed TODO/Phase unless explicitly requested.","- Ask ToolNet Memory for deeper history only when necessary.",H),e.join(`
10
+ `)}function Pe(t,e){let n=wn(t.rootPath,".toolnet","current.md"),r="";if(xn(n))try{r=Sn(n,"utf8")}catch{r=""}r=r.replace(/<!-- TOOLNET:AUTO-CURRENT:BEGIN -->[\s\S]*?<!-- TOOLNET:AUTO-CURRENT:END -->/gu,"");let o=Cn(e),s=r.indexOf(_e),c=r.indexOf(H),u;s>=0&&c>=s?u=[r.slice(0,s).trimEnd(),o,r.slice(c+H.length).trimStart()].filter(Boolean).join(`
11
11
 
12
12
  `):u=r.trim()?`${r.trim()}
13
13
 
14
14
  ${o}`:o,Mn(n,`${u.trim()}
15
- `,{encoding:"utf8",mode:384})}function S(t,e){return{id:e,name:e,rootPath:t,createdAt:"2026-08-13T16:00:00.000Z",updatedAt:"2026-08-13T16:00:00.000Z",graphVersion:1,memoryVersion:1}}function x(t){W(l(t,".toolnet","work"),{recursive:!0,mode:448}),W(l(t,".toolnet","context"),{recursive:!0,mode:448}),W(l(t,".toolnet","runtime","sources"),{recursive:!0,mode:448})}function Q(t,e){W(l(t,".."),{recursive:!0}),T(t,JSON.stringify(e,null,2)+`
16
- `,{encoding:"utf8",mode:384})}function D(t,e,n,r){Q(l(t.rootPath,".toolnet","context","session-origin.json"),{version:1,projectId:t.id,agent:"codex",nativeSessionId:"x2-origin",updatedAt:"2026-08-13T16:00:00.000Z",currentTask:e,currentPhase:"X2 Recovery",lastTouchedFile:n,latestNextAction:r})}function U(t,e,n,r){let s={version:1,projectId:t.id,confidence:1,occurredAt:"2026-08-13T16:00:00.000Z",sequence:1,agent:"opencode",nativeSessionId:"x2-work",sessionKey:"opencode:x2-work"},c=he(t,[{...s,id:"x2-task-observation",kind:"task",key:"task:x2",text:e,status:"in_progress",order:1,eventId:"x2-task-event"},{...s,id:"x2-file-observation",kind:"file",key:"file:x2",text:n,eventId:"x2-file-event"},{...s,id:"x2-next-observation",kind:"next_action",key:"next:x2",text:r,eventId:"x2-next-event"}]);Pe(t,c)}function Re(t,e,n,r,o){return{continuity:{schema:"toolnet.handoff.v2",version:2,project:{id:t.id,name:t.name},source:{agent:"agy",nativeSessionId:"x2-handoff",sessionKey:"agy:x2-handoff",sequence:99,reason:"checkpoint"},capturedAt:"2026-08-13T16:00:00.000Z",current:{phase:{id:"x2",title:"X2 Recovery",status:"in_progress"},task:{id:"x2-task",title:e,status:"in_progress"},file:n},completed:{phases:["X1"],tasks:["X1 cross-agent continuity"]},remaining:{phases:["X2"],tasks:[e],todos:[r]},nextAction:o,blockers:[],decisions:["Canonical handoff must remain authoritative"],files:{current:n,recent:[n]},tests:{status:"passing",recent:["X1 PASS 4/4"]},attention:[],progress:{phasesTotal:3,phasesCompleted:1,tasksTotal:3,tasksCompleted:1,blocked:0},stateDigest:"a".repeat(64)}}}function An(t){let e=l(t,".toolnet","context");for(let n of Tn(e)){let r=l(e,n);if(n.endsWith(".json"))try{if(JSON.parse(J(r,"utf8")).schema==="toolnet.memory-conversation.v1")return r}catch{}}return null}async function M(t,e,n){try{let r=await n();return{id:t,label:e,passed:r,detail:r?void 0:"Certification condition returned false."}}catch(r){return{id:t,label:e,passed:!1,detail:r instanceof Error?r.message:String(r)}}}async function Oe(){let t=[];t.push(await M("corrupt-handoff","corrupt handoff \u2192 safe fallback",()=>{let n=h(l(v(),"toolnet-x2-corrupt-handoff-"));try{let r=S(n,"x2-corrupt-handoff");x(n),T(l(n,".toolnet","work","handoff-latest.json"),"{ definitely broken","utf8"),D(r,"Recover task from session origin","src/x2/session-origin.ts","Continue safely");let o=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.source==="session-origin"&&o.answer.includes("Recover task from session origin")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("missing-handoff","missing handoff \u2192 WorkState fallback",()=>{let n=h(l(v(),"toolnet-x2-missing-handoff-"));try{let r=S(n,"x2-missing-handoff");x(n),U(r,"Recover task from WorkState","src/x2/work-state.ts","Continue from WorkState");let o=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.source==="work-state"&&o.answer.includes("Recover task from WorkState")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("corrupt-session-origin","corrupt session-origin \u2192 WorkState fallback",()=>{let n=h(l(v(),"toolnet-x2-corrupt-origin-"));try{let r=S(n,"x2-corrupt-origin");x(n),T(l(n,".toolnet","context","session-origin.json"),"{ broken origin","utf8"),U(r,"WorkState survives corrupt origin","src/x2/origin-fallback.ts","Use current WorkState");let o=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.source==="work-state"&&o.answer.includes("WorkState survives corrupt origin")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("corrupt-conversation","corrupt conversation \u2192 fresh clean state",()=>{let n=h(l(v(),"toolnet-x2-conversation-"));try{let r=S(n,"x2-conversation");x(n),D(r,"Conversation recovery task","src/x2/conversation.ts","Recover conversation"),B(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");let o=An(n);if(!o)return!1;T(o,"{ broken conversation","utf8");let s=B(r,"R\u1ED3i sao?"),c=Fe(r);return s.usedPriorFocus===!1&&c?.schema==="toolnet.memory-conversation.v1"&&c.projectId===r.id}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("corrupt-work-state","corrupt WorkState \u2192 safe empty answer",()=>{let n=h(l(v(),"toolnet-x2-workstate-"));try{let r=S(n,"x2-corrupt-workstate");x(n),T(l(n,".toolnet","work","current.json"),"{ broken work state","utf8");let o=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.source==="none"&&o.answer.includes("ToolNet ch\u01B0a c\xF3 \u0111\u1EE7 memory")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("ai-failure","AI/provider failure \u2192 deterministic local continuity",async()=>{let n=h(l(v(),"toolnet-x2-ai-"));try{let r=S(n,"x2-ai");x(n),D(r,"Deterministic AI outage fallback","src/x2/ai-fallback.ts","Continue without provider");let o=await we(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.usedAi===!1&&o.answer.includes("Deterministic AI outage fallback")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("canonical-priority","valid canonical handoff \u2192 stale fallback rejected",()=>{let n=h(l(v(),"toolnet-x2-priority-"));try{let r=S(n,"x2-priority");x(n);let o=l(n,".toolnet","work","handoff-latest.json");Q(o,Re(r,"CANONICAL X2 TASK","src/x2/canonical.ts","Canonical TODO","Canonical next action")),D(r,"STALE ORIGIN TASK","src/x2/stale-origin.ts","Stale origin next"),U(r,"STALE WORKSTATE TASK","src/x2/stale-work.ts","Stale WorkState next");let s=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return s.source==="handoff"&&s.answer.includes("CANONICAL X2 TASK")&&!s.answer.includes("STALE ORIGIN TASK")&&!s.answer.includes("STALE WORKSTATE TASK")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("memory-preserved","recovery read \u2192 valid handoff never destroyed",()=>{let n=h(l(v(),"toolnet-x2-preserve-"));try{let r=S(n,"x2-preserve");x(n);let o=l(n,".toolnet","work","handoff-latest.json");Q(o,Re(r,"Preserve canonical state","src/x2/preserve.ts","Preserve TODO","Preserve next action"));let s=J(o,"utf8");k(r,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?");let c=J(o,"utf8");return s===c}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("raw-transcript-isolation","raw transcript \u2192 never used for recovery",()=>{let n=h(l(v(),"toolnet-x2-transcript-"));try{let r=S(n,"x2-transcript");x(n);let o="X2_SECRET_RAW_TRANSCRIPT_MUST_NEVER_ENTER_CONTINUITY";T(l(n,".toolnet","runtime","sources","raw-transcript.json"),JSON.stringify({transcript:o}),"utf8"),T(l(n,".toolnet","runtime","sources","events.jsonl"),JSON.stringify({text:o})+`
17
- `,"utf8");let s=k(r,"Agent tr\u01B0\u1EDBc \u0111ang l\xE0m g\xEC?");return s.source==="none"&&!s.answer.includes(o)}finally{b(n,{recursive:!0,force:!0})}}));let e=t.filter(n=>n.passed).length;return{passed:e===t.length,total:t.length,passedCount:e,checks:t}}async function In(){let t=await Oe();console.log(""),console.log("\u25C7 ToolNet Recovery & Resilience Certification"),console.log("\u2502");for(let e of t.checks)console.log(`\u2502 ${e.passed?"\u2713":"\u2717"} ${e.label}`),!e.passed&&e.detail&&console.log(`\u2502 \u21B3 ${e.detail}`);if(console.log("\u2502"),t.passed){console.log(`\u25C6 PASS ${t.passedCount}/${t.total} recovery checks`);return}console.log(`\u25C6 FAIL ${t.passedCount}/${t.total} recovery checks`),process.exitCode=1}await In();
15
+ `,{encoding:"utf8",mode:384})}function x(t,e){return{id:e,name:e,rootPath:t,createdAt:"2026-08-13T16:00:00.000Z",updatedAt:"2026-08-13T16:00:00.000Z",graphVersion:1,memoryVersion:1}}function S(t){W(l(t,".toolnet","work"),{recursive:!0,mode:448}),W(l(t,".toolnet","context"),{recursive:!0,mode:448}),W(l(t,".toolnet","runtime","sources"),{recursive:!0,mode:448})}function Q(t,e){W(l(t,".."),{recursive:!0}),T(t,JSON.stringify(e,null,2)+`
16
+ `,{encoding:"utf8",mode:384})}function D(t,e,n,r){Q(l(t.rootPath,".toolnet","context","session-origin.json"),{version:1,projectId:t.id,agent:"codex",nativeSessionId:"x2-origin",updatedAt:"2026-08-13T16:00:00.000Z",currentTask:e,currentPhase:"X2 Recovery",lastTouchedFile:n,latestNextAction:r})}function U(t,e,n,r){let s={version:1,projectId:t.id,confidence:1,occurredAt:"2026-08-13T16:00:00.000Z",sequence:1,agent:"opencode",nativeSessionId:"x2-work",sessionKey:"opencode:x2-work"},c=he(t,[{...s,id:"x2-task-observation",kind:"task",key:"task:x2",text:e,status:"in_progress",order:1,eventId:"x2-task-event"},{...s,id:"x2-file-observation",kind:"file",key:"file:x2",text:n,eventId:"x2-file-event"},{...s,id:"x2-next-observation",kind:"next_action",key:"next:x2",text:r,eventId:"x2-next-event"}]);Pe(t,c)}function Re(t,e,n,r,o){return{continuity:{schema:"toolnet.handoff.v2",version:2,project:{id:t.id,name:t.name},source:{agent:"agy",nativeSessionId:"x2-handoff",sessionKey:"agy:x2-handoff",sequence:99,reason:"checkpoint"},capturedAt:"2026-08-13T16:00:00.000Z",current:{phase:{id:"x2",title:"X2 Recovery",status:"in_progress"},task:{id:"x2-task",title:e,status:"in_progress"},file:n},completed:{phases:["X1"],tasks:["X1 cross-agent continuity"]},remaining:{phases:["X2"],tasks:[e],todos:[r]},nextAction:o,blockers:[],decisions:["Canonical handoff must remain authoritative"],files:{current:n,recent:[n]},tests:{status:"passing",recent:["X1 PASS 4/4"]},attention:[],progress:{phasesTotal:3,phasesCompleted:1,tasksTotal:3,tasksCompleted:1,blocked:0},stateDigest:"a".repeat(64)}}}function An(t){let e=l(t,".toolnet","context");for(let n of Tn(e)){let r=l(e,n);if(n.endsWith(".json"))try{if(JSON.parse(J(r,"utf8")).schema==="toolnet.memory-conversation.v1")return r}catch{}}return null}async function M(t,e,n){try{let r=await n();return{id:t,label:e,passed:r,detail:r?void 0:"Certification condition returned false."}}catch(r){return{id:t,label:e,passed:!1,detail:r instanceof Error?r.message:String(r)}}}async function Ne(){let t=[];t.push(await M("corrupt-handoff","corrupt handoff \u2192 safe fallback",()=>{let n=h(l(v(),"toolnet-x2-corrupt-handoff-"));try{let r=x(n,"x2-corrupt-handoff");S(n),T(l(n,".toolnet","work","handoff-latest.json"),"{ definitely broken","utf8"),D(r,"Recover task from session origin","src/x2/session-origin.ts","Continue safely");let o=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.source==="session-origin"&&o.answer.includes("Recover task from session origin")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("missing-handoff","missing handoff \u2192 WorkState fallback",()=>{let n=h(l(v(),"toolnet-x2-missing-handoff-"));try{let r=x(n,"x2-missing-handoff");S(n),U(r,"Recover task from WorkState","src/x2/work-state.ts","Continue from WorkState");let o=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.source==="work-state"&&o.answer.includes("Recover task from WorkState")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("corrupt-session-origin","corrupt session-origin \u2192 WorkState fallback",()=>{let n=h(l(v(),"toolnet-x2-corrupt-origin-"));try{let r=x(n,"x2-corrupt-origin");S(n),T(l(n,".toolnet","context","session-origin.json"),"{ broken origin","utf8"),U(r,"WorkState survives corrupt origin","src/x2/origin-fallback.ts","Use current WorkState");let o=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.source==="work-state"&&o.answer.includes("WorkState survives corrupt origin")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("corrupt-conversation","corrupt conversation \u2192 fresh clean state",()=>{let n=h(l(v(),"toolnet-x2-conversation-"));try{let r=x(n,"x2-conversation");S(n),D(r,"Conversation recovery task","src/x2/conversation.ts","Recover conversation"),B(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");let o=An(n);if(!o)return!1;T(o,"{ broken conversation","utf8");let s=B(r,"R\u1ED3i sao?"),c=Fe(r);return s.usedPriorFocus===!1&&c?.schema==="toolnet.memory-conversation.v1"&&c.projectId===r.id}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("corrupt-work-state","corrupt WorkState \u2192 safe empty answer",()=>{let n=h(l(v(),"toolnet-x2-workstate-"));try{let r=x(n,"x2-corrupt-workstate");S(n),T(l(n,".toolnet","work","current.json"),"{ broken work state","utf8");let o=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.source==="none"&&o.answer.includes("ToolNet ch\u01B0a c\xF3 \u0111\u1EE7 memory")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("ai-failure","AI/provider failure \u2192 deterministic local continuity",async()=>{let n=h(l(v(),"toolnet-x2-ai-"));try{let r=x(n,"x2-ai");S(n),D(r,"Deterministic AI outage fallback","src/x2/ai-fallback.ts","Continue without provider");let o=await we(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return o.usedAi===!1&&o.answer.includes("Deterministic AI outage fallback")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("canonical-priority","valid canonical handoff \u2192 stale fallback rejected",()=>{let n=h(l(v(),"toolnet-x2-priority-"));try{let r=x(n,"x2-priority");S(n);let o=l(n,".toolnet","work","handoff-latest.json");Q(o,Re(r,"CANONICAL X2 TASK","src/x2/canonical.ts","Canonical TODO","Canonical next action")),D(r,"STALE ORIGIN TASK","src/x2/stale-origin.ts","Stale origin next"),U(r,"STALE WORKSTATE TASK","src/x2/stale-work.ts","Stale WorkState next");let s=k(r,"Task hi\u1EC7n t\u1EA1i l\xE0 g\xEC?");return s.source==="handoff"&&s.answer.includes("CANONICAL X2 TASK")&&!s.answer.includes("STALE ORIGIN TASK")&&!s.answer.includes("STALE WORKSTATE TASK")}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("memory-preserved","recovery read \u2192 valid handoff never destroyed",()=>{let n=h(l(v(),"toolnet-x2-preserve-"));try{let r=x(n,"x2-preserve");S(n);let o=l(n,".toolnet","work","handoff-latest.json");Q(o,Re(r,"Preserve canonical state","src/x2/preserve.ts","Preserve TODO","Preserve next action"));let s=J(o,"utf8");k(r,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?");let c=J(o,"utf8");return s===c}finally{b(n,{recursive:!0,force:!0})}})),t.push(await M("raw-transcript-isolation","raw transcript \u2192 never used for recovery",()=>{let n=h(l(v(),"toolnet-x2-transcript-"));try{let r=x(n,"x2-transcript");S(n);let o="X2_SECRET_RAW_TRANSCRIPT_MUST_NEVER_ENTER_CONTINUITY";T(l(n,".toolnet","runtime","sources","raw-transcript.json"),JSON.stringify({transcript:o}),"utf8"),T(l(n,".toolnet","runtime","sources","events.jsonl"),JSON.stringify({text:o})+`
17
+ `,"utf8");let s=k(r,"Agent tr\u01B0\u1EDBc \u0111ang l\xE0m g\xEC?");return s.source==="none"&&!s.answer.includes(o)}finally{b(n,{recursive:!0,force:!0})}}));let e=t.filter(n=>n.passed).length;return{passed:e===t.length,total:t.length,passedCount:e,checks:t}}async function In(){let t=await Ne();console.log(""),console.log("\u25C7 ToolNet Recovery & Resilience Certification"),console.log("\u2502");for(let e of t.checks)console.log(`\u2502 ${e.passed?"\u2713":"\u2717"} ${e.label}`),!e.passed&&e.detail&&console.log(`\u2502 \u21B3 ${e.detail}`);if(console.log("\u2502"),t.passed){console.log(`\u25C6 PASS ${t.passedCount}/${t.total} recovery checks`);return}console.log(`\u25C6 FAIL ${t.passedCount}/${t.total} recovery checks`),process.exitCode=1}await In();