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,12 +1,12 @@
1
- import{existsSync as je,readFileSync as Ae}from"node:fs";import{homedir as Me}from"node:os";import{join as Re}from"node:path";function Pe(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function Ce(){let r=process.env.TOOLNET_GLOBAL_ENV??Re(Me(),".config","toolnet-memory",".env");if(!je(r))return;let e=Ae(r,"utf8");for(let t of e.split(/\r?\n/)){let n=t.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let i=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=Pe(n.slice(o+1)))}}Ce();function k(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function S(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function q(){return{memory:{autoCapture:k(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:k(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:k(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:k(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:S(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:S(process.env.MEMORY_RERANK_TOP,10),finalContext:S(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:S(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:k(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:S(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as Ee}from"node:crypto";import{existsSync as j,mkdirSync as Ie,readFileSync as Te,renameSync as _e,writeFileSync as Oe}from"node:fs";import{basename as Ne,dirname as A,join as b,parse as ne,resolve as h}from"node:path";var oe=".toolnet",$e="project.json";function Ke(r){return Ee("sha256").update(r).digest("hex").slice(0,16)}function M(r){return b(r,oe,$e)}function De(r){return j(M(r))}function X(r,e){let t=h(r),n=ne(t).root;for(;;){if(De(t))return t;if(t===n||e&&t===h(e))break;let o=A(t);if(o===t)break;t=o}return null}function ee(r){let e=h(r),t=ne(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(i=>j(b(e,i))))return e;if(e===t)break;let o=A(e);if(o===e)break;e=o}return h(r)}function te(r){let e;try{e=JSON.parse(Te(r,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${r}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let n=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:A(A(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:n,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:n,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function re(r,e){let t=b(r,oe);Ie(t,{recursive:!0});let n=M(r),o=`${n}.tmp-${process.pid}`;Oe(o,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),_e(o,n)}function N(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var R=class{findExisting(e=process.cwd()){let t=h(e),n=ee(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>j(b(n,u))),a=X(t,i?n:void 0);if(!a)return null;let c=te(M(a));return N(c,a)}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=h(e),n=ee(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>j(b(n,m))),a=X(t,i?n:void 0);if(a){let m=M(a),g=te(m);return g.rootPath!==a&&(g.rootPath=a,g.updatedAt=new Date().toISOString(),re(a,g)),N(g,a)}let c=new Date().toISOString(),u=Ne(n),l={version:1,id:Ke(n),name:u,remote:u,rootPath:n,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return re(n,l),N(l,n)}};var ze=[{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"}],Le=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function se(r){return r.normalize("NFKC").trim().toLowerCase()}function Ve(r){if(r.length===0)return 0;let e=new Map;for(let n of r)e.set(n,(e.get(n)??0)+1);let t=0;for(let n of e.values()){let o=n/r.length;t-=o*Math.log2(o)}return t}function We(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function Fe(r,e,t){let n=r.slice(Math.max(0,e-48),e),o=r.slice(t,Math.min(r.length,t+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${n} ${o}`)}function Be(r,e){return r.start<e.end&&e.start<r.end}function ie(r){return r.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var P=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let n=se(t);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let i of ze){let a=new RegExp(i.regex.source,i.regex.flags);for(let c of e.matchAll(a))c.index===void 0||!c[0]||this.allowed(c[0])||t.push({type:i.type,value:c[0],start:c.index,end:c.index+c[0].length,confidence:i.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let n=ie(t),o=[];for(let i of n)o.some(a=>Be(a,i))||o.push(i);return ie(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=se(e);return Le.has(t)?!0:this.allowValues.has(t)}entropyMatches(e){let t=[],n=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of e.matchAll(n)){if(o.index===void 0||!o[0])continue;let i=o[0];this.allowed(i)||We(i)||!/[A-Za-z]/u.test(i)||!/[0-9]/u.test(i)||Fe(e,o.index,o.index+i.length)&&(Ve(i)<3.7||t.push({type:"high_entropy_secret",value:i,start:o.index,end:o.index+i.length,confidence:"heuristic"}))}return t}};var C=class{scanner;constructor(e={}){this.scanner=new P(e)}sanitize(e){let t=this.scanner.scan(e);if(t.length===0)return{text:e,redacted:0,secretTypes:[]};let n=e,o=[...t].sort((a,c)=>c.start-a.start),i=new Set;for(let a of o)i.add(a.type),n=n.slice(0,a.start)+`[REDACTED:${a.type}]`+n.slice(a.end);return{text:n,redacted:t.length,secretTypes:[...i].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[n,o]of Object.entries(e)){let i=n.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")){t[n]="[REDACTED]";continue}t[n]=this.sanitizeValue(o)}return t}return e}};var Ft=new C;import{homedir as mt}from"node:os";import{join as ft}from"node:path";import{DeleteObjectCommand as Je,GetObjectCommand as He,HeadObjectCommand as Ue,ListObjectsV2Command as Ye,PutObjectCommand as Ge,S3Client as Ze}from"@aws-sdk/client-s3";import{getSignedUrl as Qe}from"@aws-sdk/s3-request-presigner";var E=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ze({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ge({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Qe(this.client,new He({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ue({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Je({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Ye({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};import{access as ae,mkdir as qe,readFile as Xe,readdir as et,rm as tt,stat as ce,writeFile as rt}from"node:fs/promises";import{dirname as nt,join as ot,relative as ue,resolve as st}from"node:path";var w=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return st(this.root,t)}async put(e,t){let n=this.path(e);await qe(nt(n),{recursive:!0}),await rt(n,t)}async get(e){try{return await Xe(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 ae(this.path(e)),!0}catch{return!1}}async delete(e){await tt(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await ae(t)}catch{return n}let o=async a=>{let c=await et(a,{withFileTypes:!0});for(let u of c){let l=ot(a,u.name);if(u.isDirectory()){await o(l);continue}let m=await ce(l);n.push({key:ue(this.root,l),size:m.size,updatedAt:m.mtime.toISOString()})}},i=await ce(t);return i.isDirectory()?await o(t):n.push({key:ue(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),n}};import{DeleteObjectCommand as it,GetObjectCommand as at,HeadObjectCommand as ct,ListObjectsV2Command as ut,PutObjectCommand as dt,S3Client as lt}from"@aws-sdk/client-s3";import{getSignedUrl as pt}from"@aws-sdk/s3-request-presigner";var x=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new lt({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new dt({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await pt(this.client,new at({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`${this.name} download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new ct({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 it({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new ut({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let i of o.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};function $(r,e){return console.warn(e),new w(r)}function de(r){let e=r.localRoot??ft(mt(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new x({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):$(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new x({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):$(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new E({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):$(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new w(e)}function gt(r){return new Promise(e=>setTimeout(e,r))}async function le(r,e={}){let t=Math.max(1,e.attempts??3),n=e.baseDelayMs??150,o=e.maxDelayMs??2e3,i;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(i=c,a>=t)break;let u=Math.min(o,n*2**(a-1)),l=Math.floor(Math.random()*Math.max(1,u*.2));await gt(u+l)}throw i}var yt=new Set(["put","get","getText","delete","list"]);function pe(r,e={}){return new Proxy(r,{get(t,n){let o=Reflect.get(t,n,t);return typeof o!="function"?o:yt.has(n)?(...i)=>le(()=>Promise.resolve(o.apply(t,i)),e):o.bind(t)}})}function me(r){let e=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function fe(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var I=class{constructor(e,t,n,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=me(o??n),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),n=t,o=await this.provider.getText(e);if(o){let a;try{a=JSON.parse(o)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(n=a.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:t};await this.provider.put(e,JSON.stringify(i,null,2)+`
4
- `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=fe(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,n){return await this.ensureRegistered(),this.provider.put(this.key(e),t,n)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as he}from"node:path";import{mkdirSync as xt}from"node:fs";import{createHash as ht}from"node:crypto";import{dirname as kt}from"node:path";import{mkdirSync as St,readFileSync as En,renameSync as bt,writeFileSync as wt}from"node:fs";function ge(r){return ht("sha256").update(r).digest("hex")}function ye(r,e){St(kt(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;wt(t,JSON.stringify(e,null,2)+`
5
- `,{encoding:"utf8",mode:384}),bt(t,r)}function Se(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function p(r,e=20){let t=[],n=new Set;for(let o of r.slice().reverse()){let i=Se(o);if(!(!i||n.has(i))&&(n.add(i),t.push(o),t.length>=e))break}return t.reverse()}function vt(r,e=20){let t=new Map;for(let n of r){let o=`${n.kind}|${Se(n.command)}`;t.delete(o),t.set(o,n)}return Array.from(t.values()).slice(-e)}function jt(r){return r.kind==="phase"?/^Phase\s+\d+$/iu.test(r.text):r.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(r.text):!1}function ke(r,e){let t=e.status??r?.status??"pending",n=t;r&&(r.status==="completed"&&t!=="completed"?n="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(n=r.status));let o=r&&jt(e)?r.title:e.text;return{id:r?.id??ge(e.key).slice(0,24),title:o,status:n,order:e.order??r?.order,confidence:Math.max(e.confidence,r?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function be(r,e){let t=`projects/${r.id}/work/observations/`,n=await e.list(t),o=[];for(let i of n.filter(a=>a.key.endsWith(".json")).sort((a,c)=>a.key.localeCompare(c.key))){let a=await e.getText(i.key);if(a)try{let c=JSON.parse(a);c.version===1&&Array.isArray(c.observations)&&o.push(c)}catch{}}return o}async function K(r,e){let n=(await be(r,e)).flatMap(s=>s.observations).sort((s,d)=>{let Z=s.occurredAt.localeCompare(d.occurredAt);if(Z!==0)return Z;let Q=s.sequence-d.sequence;return Q!==0?Q:s.id.localeCompare(d.id)}),o=new Map,i=new Map,a,c,u,l,m,g=[],z=[],L=[],V=[],W=[],T=new Map,F=[],B=[],J=[],H=[],U=[],Y=[];for(let s of n)switch(s.kind){case"request":a=s.text;break;case"activity":c=s.text;break;case"goal":u=s.text;break;case"plan":l=s.text;break;case"phase":o.set(s.key,ke(o.get(s.key),s));break;case"task":i.set(s.key,ke(i.get(s.key),s));break;case"decision":g.push(s.text);break;case"blocker":z.push(s.text);break;case"warning":L.push(s.text);break;case"next_action":V.push(s.text);break;case"file":{W.push(s.text);let d=s.fileAction??"active";T.delete(s.text),T.set(s.text,d),d==="modified"?F.push(s.text):d==="created"?B.push(s.text):d==="deleted"&&J.push(s.text);break}case"command":H.push(s.text);break;case"test":U.push(s.text),s.checkKind&&Y.push({kind:s.checkKind,command:s.text,status:s.checkStatus??"unknown",updatedAt:s.occurredAt,agent:s.agent,nativeSessionId:s.nativeSessionId});break;case"session":m={agent:s.agent,nativeSessionId:s.nativeSessionId,sessionKey:s.sessionKey,updatedAt:s.occurredAt};break}let y=Array.from(o.values()).sort((s,d)=>(s.order??Number.MAX_SAFE_INTEGER)-(d.order??Number.MAX_SAFE_INTEGER)),f=Array.from(i.values()).sort((s,d)=>(s.order??Number.MAX_SAFE_INTEGER)-(d.order??Number.MAX_SAFE_INTEGER)),_=y.find(s=>s.status==="in_progress")??y.find(s=>s.status==="blocked")??y.find(s=>s.status==="pending"),v=f.find(s=>s.status==="in_progress")??f.find(s=>s.status==="blocked")??f.find(s=>s.status==="pending"),xe=p([...V,...v?[v.title]:[],...!v&&_?[_.title]:[],...f.filter(s=>s.status==="pending").slice(0,5).map(s=>s.title)],8),ve=p([...z,...y.filter(s=>s.status==="blocked").map(s=>s.title),...f.filter(s=>s.status==="blocked").map(s=>s.title)],20),O={version:1,projectId:r.id,projectName:r.name,currentRequest:a,currentActivity:c,goal:u,plan:l,phases:y,tasks:f,decisions:p(g,20),blockers:ve,warnings:p(L,20),nextActions:xe,filesTouched:p(W,30),activeFiles:Array.from(T.entries()).filter(([,s])=>s!=="deleted").map(([s])=>s).slice(-5),modifiedFiles:p(F,30),createdFiles:p(B,30),deletedFiles:p(J,30),commands:p(H,20),tests:p(U,20),checks:vt(Y,20),currentPhase:_,currentTask:v,progress:{phasesTotal:y.length,phasesCompleted:y.filter(s=>s.status==="completed").length,tasksTotal:f.length,tasksCompleted:f.filter(s=>s.status==="completed").length,blocked:y.filter(s=>s.status==="blocked").length+f.filter(s=>s.status==="blocked").length},lastSession:m,updatedAt:n.length?n[n.length-1].occurredAt:new Date().toISOString()},G=he(r.rootPath,".toolnet","work");return xt(G,{recursive:!0}),ye(he(G,"current.json"),O),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(O,null,2)+`
6
- `,"application/json"),O}async function D(r,e){if((await be(r,e)).length>0)return K(r,e);let n=await e.getText(`projects/${r.id}/work/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}function At(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function Mt(r){let e=q(),t=pe(de({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new I(t,r.id,r.name,r.remote??r.name)}function we(r){console.log(),console.log(`Project: ${r.projectName}`),r.goal&&console.log(`Goal: ${r.goal}`),r.currentPhase&&console.log(`Current phase: ${r.currentPhase.title} [${r.currentPhase.status}]`),r.currentTask&&console.log(`Current task: ${r.currentTask.title} [${r.currentTask.status}]`),console.log(`Phases: ${r.progress.phasesCompleted}/${r.progress.phasesTotal}`),console.log(`Tasks: ${r.progress.tasksCompleted}/${r.progress.tasksTotal}`),r.nextActions.length&&(console.log(),console.log("Next actions:"),r.nextActions.forEach((e,t)=>console.log(`${t+1}. ${e}`))),r.blockers.length&&(console.log(),console.log("Blockers:"),r.blockers.forEach(e=>console.log(`- ${e}`))),r.lastSession&&(console.log(),console.log(`Last session: ${r.lastSession.agent} / ${r.lastSession.nativeSessionId}`))}async function Rt(){let[r="help",...e]=process.argv.slice(2),t=new R().detect(At(e,"--project")??process.cwd()),n=Mt(t);if(r==="reconcile"){let o=await K(t,n);we(o);return}if(r==="status"){let o=await D(t,n);if(!o){console.log("No active work state yet.");return}we(o);return}if(r==="json"){let o=await D(t,n);console.log(JSON.stringify(o,null,2));return}console.log(`Work Continuity
1
+ import{existsSync as et,readFileSync as tt}from"node:fs";import{homedir as rt}from"node:os";import{join as nt}from"node:path";function ot(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function st(){let r=process.env.TOOLNET_GLOBAL_ENV??nt(rt(),".config","toolnet-memory",".env");if(!et(r))return;let e=tt(r,"utf8");for(let t of e.split(/\r?\n/)){let n=t.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let s=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=ot(n.slice(o+1)))}}st();function E(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function v(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function fe(){return{memory:{autoCapture:E(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:E(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:E(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:E(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:v(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:v(process.env.MEMORY_RERANK_TOP,10),finalContext:v(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:v(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:E(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:v(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as pt}from"node:crypto";import{existsSync as N,mkdirSync as mt,readFileSync as ft,renameSync as gt,writeFileSync as yt}from"node:fs";import{basename as ht,dirname as K,join as A,parse as we,resolve as x}from"node:path";import{createHash as he}from"node:crypto";import{spawnSync as it}from"node:child_process";var R="git-remote-v1",at=new Set(["github.com","gitlab.com","bitbucket.org"]);function Se(r,e){let t=e.replaceAll("\\","/").replace(/^\/+/u,"").replace(/\/+$/u,"").replace(/\.git$/iu,"").replace(/\/+/gu,"/");return!t||t==="."||t===".."||t.split("/").some(n=>!n||n==="."||n==="..")?null:(at.has(r)&&(t=t.toLowerCase()),t)}function ct(r){let e;try{e=new URL(r)}catch{return null}if(!["https:","http:","ssh:","git:"].includes(e.protocol))return null;let t=e.hostname.trim().toLowerCase();if(!t)return null;let n=e.protocol==="https:"&&e.port==="443"||e.protocol==="http:"&&e.port==="80"||e.protocol==="ssh:"&&e.port==="22",o=e.port&&!n?`${t}:${e.port}`:t,s=Se(t,e.pathname);return s?`${o}/${s}`:null}function ut(r){let e=r.match(/^(?:[^@\s/:]+@)?([^:/\s]+):(.+)$/u);if(!e)return null;let t=e[1]?.trim().toLowerCase();if(!t||t.length===1)return null;let n=Se(t,e[2]??"");return n?`${t}/${n}`:null}function ge(r){let e=r.trim();return e?e.includes("://")?ct(e):ut(e):null}function dt(r){return he("sha256").update(`${R}:${r}`).digest("hex")}function be(r){return he("sha256").update(`toolnet-project:${R}:${r}`).digest("hex").slice(0,16)}function lt(r){return r.split("/").filter(Boolean).at(-1)?.trim()||null}function U(r,e){let t=it("git",["-C",r,...e],{encoding:"utf8",windowsHide:!0,stdio:["ignore","pipe","ignore"]});return t.error||t.status!==0?null:t.stdout?.trim()||null}function ye(r,e){let t=lt(r);return t?{scheme:R,canonicalRemote:r,fingerprint:dt(r),repositoryName:t,source:e}:null}function xe(r){let e=U(r,["remote","get-url","origin"]);if(e){let o=ge(e);if(o)return ye(o,"origin")}let t=U(r,["remote"]);if(!t)return null;let n=new Set;for(let o of t.split(/\r?\n/u).map(s=>s.trim()).filter(Boolean)){let s=U(r,["remote","get-url",o]);if(!s)continue;let a=ge(s);a&&n.add(a)}return n.size!==1?null:ye([...n][0],"unique-remote")}var Ee=".toolnet",St="project.json";function bt(r){return pt("sha256").update(r).digest("hex").slice(0,16)}function w(r){return A(r,Ee,St)}function ve(r){return N(w(r))}function ke(r,e){let t=x(r),n=we(t).root;for(;;){if(ve(t))return t;if(t===n||e&&t===x(e))break;let o=K(t);if(o===t)break;t=o}return null}function G(r){let e=x(r),t=we(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>N(A(e,s))))return e;if(e===t)break;let o=K(e);if(o===e)break;e=o}return x(r)}function _(r){let e;try{e=JSON.parse(ft(r,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${r}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let n=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:K(K(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:n,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:n,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function O(r,e){let t=A(r,Ee);mt(t,{recursive:!0});let n=w(r),o=`${n}.tmp-${process.pid}`;yt(o,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),gt(o,n)}function b(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}function W(r){return{version:1,scheme:R,canonicalRemote:r.canonicalRemote,fingerprint:r.fingerprint,repositoryName:r.repositoryName}}function xt(r){let e=r.metadata?.toolnetIdentity;if(!e||typeof e!="object"||Array.isArray(e))return null;let t=e;return typeof t.fingerprint=="string"?t.fingerprint:null}var $=class{adopt(e,t){let n=G(x(e));if(!t.id.trim())throw new Error("PROJECT_ADOPTION_INVALID_ID");if(!t.name.trim())throw new Error("PROJECT_ADOPTION_INVALID_NAME");if(!t.remote.trim())throw new Error("PROJECT_ADOPTION_INVALID_REMOTE");if(ve(n)){let c=_(w(n));if(c.id!==t.id)throw new Error(["PROJECT_IDENTITY_ALREADY_EXISTS",`existing=${c.id}`,`requested=${t.id}`].join(" "));return b(c,n)}let o=new Date().toISOString(),s={...t.metadata};t.gitIdentity&&(s.toolnetIdentity=W(t.gitIdentity));let a={version:1,id:t.id.trim(),name:t.name.trim(),remote:t.remote.trim(),rootPath:n,createdAt:t.createdAt??o,updatedAt:o,graphVersion:t.graphVersion??0,memoryVersion:t.memoryVersion??0,metadata:Object.keys(s).length?s:void 0};return O(n,a),b(a,n)}recordGitIdentity(e,t,n={}){let o=this.requireExisting(e),s=w(o.rootPath),a=_(s),c=xt(a);if(c&&c!==t.fingerprint&&!n.allowRebind)throw new Error(["PROJECT_GIT_REMOTE_CHANGED",`existing=${c}`,`current=${t.fingerprint}`,"Use explicit rebind only when this repository identity change is intentional."].join(" "));let u=a.metadata?.toolnetIdentity;return u&&typeof u=="object"&&!Array.isArray(u)&&u.fingerprint===t.fingerprint||(a.metadata={...a.metadata,toolnetIdentity:W(t)},a.updatedAt=new Date().toISOString(),O(o.rootPath,a)),b(a,o.rootPath)}findExisting(e=process.cwd()){let t=x(e),n=G(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(u=>N(A(n,u))),a=ke(t,s?n:void 0);if(!a)return null;let c=_(w(a));return b(c,a)}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=x(e),n=G(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>N(A(n,m))),a=ke(t,s?n:void 0);if(a){let m=w(a),h=_(m);return h.rootPath!==a&&(h.rootPath=a,h.updatedAt=new Date().toISOString(),O(a,h)),b(h,a)}let c=new Date().toISOString(),u=ht(n),d=xe(n),f={version:1,id:d?be(d.canonicalRemote):bt(n),name:u,remote:d?.repositoryName??u,rootPath:n,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0,metadata:d?{toolnetIdentity:W(d)}:void 0};return O(n,f),b(f,n)}};var kt=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_-]{20,}\b/g,confidence:"exact"},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g,confidence:"exact"},{type:"aws_access_key",regex:/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,confidence:"exact"},{type:"github_token",regex:/\b(?:gh[pousr]_[A-Za-z0-9]{30,255}|github_pat_[A-Za-z0-9_]{40,255})\b/g,confidence:"exact"},{type:"stripe_secret_key",regex:/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g,confidence:"exact"},{type:"google_api_key",regex:/\bAIza[A-Za-z0-9_-]{30,}\b/g,confidence:"exact"},{type:"slack_token",regex:/\bxox[baprs]-[A-Za-z0-9-]{16,}\b/g,confidence:"exact"},{type:"npm_token",regex:/\bnpm_[A-Za-z0-9]{20,}\b/g,confidence:"exact"},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/gi,confidence:"high"},{type:"jwt",regex:/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g,confidence:"exact"},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/g,confidence:"exact"},{type:"password_assignment",regex:/\b(?:password|passwd|pwd)\s*[:=]\s*["']?[^"' \t\r\n]{6,}["']?/gi,confidence:"high"},{type:"secret_assignment",regex:/\b(?:secret|api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key)\s*[:=]\s*["']?[^"' \t\r\n]{8,}["']?/gi,confidence:"high"},{type:"cookie",regex:/\b(?:cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi,confidence:"high"},{type:"url_credentials",regex:/\bhttps?:\/\/[^:/@\s]+:[^/@\s]{4,}@[^/\s]+/gi,confidence:"high"}],wt=new Set(["example","example-key","example-token","changeme","change-me","password","secret","your-api-key","your-token","<token>","<secret>","<password>","[redacted]"]);function Re(r){return r.normalize("NFKC").trim().toLowerCase()}function Et(r){if(r.length===0)return 0;let e=new Map;for(let n of r)e.set(n,(e.get(n)??0)+1);let t=0;for(let n of e.values()){let o=n/r.length;t-=o*Math.log2(o)}return t}function vt(r){return/^[a-f0-9]{32}$/iu.test(r)||/^[a-f0-9]{40}$/iu.test(r)||/^[a-f0-9]{64}$/iu.test(r)}function Rt(r,e,t){let n=r.slice(Math.max(0,e-48),e),o=r.slice(t,Math.min(r.length,t+16));return/\b(?:token|secret|key|credential|authorization|password|passwd|apikey|api_key|access[_-]?key)\b/iu.test(`${n} ${o}`)}function At(r,e){return r.start<e.end&&e.start<r.end}function Ae(r){return r.sort((e,t)=>e.start!==t.start?e.start-t.start:t.end-t.start-(e.end-e.start))}var D=class{allowValues=new Set;enableEntropyHeuristic;constructor(e={}){for(let t of e.allowValues??[]){let n=Re(t);n&&this.allowValues.add(n)}this.enableEntropyHeuristic=e.enableEntropyHeuristic??!0}scan(e){let t=[];for(let s of kt){let a=new RegExp(s.regex.source,s.regex.flags);for(let c of e.matchAll(a))c.index===void 0||!c[0]||this.allowed(c[0])||t.push({type:s.type,value:c[0],start:c.index,end:c.index+c[0].length,confidence:s.confidence})}this.enableEntropyHeuristic&&t.push(...this.entropyMatches(e));let n=Ae(t),o=[];for(let s of n)o.some(a=>At(a,s))||o.push(s);return Ae(o)}hasSecrets(e){return this.scan(e).length>0}allowed(e){let t=Re(e);return wt.has(t)?!0:this.allowValues.has(t)}entropyMatches(e){let t=[],n=/[A-Za-z0-9_+/=-]{32,160}/g;for(let o of e.matchAll(n)){if(o.index===void 0||!o[0])continue;let s=o[0];this.allowed(s)||vt(s)||!/[A-Za-z]/u.test(s)||!/[0-9]/u.test(s)||Rt(e,o.index,o.index+s.length)&&(Et(s)<3.7||t.push({type:"high_entropy_secret",value:s,start:o.index,end:o.index+s.length,confidence:"heuristic"}))}return t}};var L=class{scanner;constructor(e={}){this.scanner=new D(e)}sanitize(e){let t=this.scanner.scan(e);if(t.length===0)return{text:e,redacted:0,secretTypes:[]};let n=e,o=[...t].sort((a,c)=>c.start-a.start),s=new Set;for(let a of o)s.add(a.type),n=n.slice(0,a.start)+`[REDACTED:${a.type}]`+n.slice(a.end);return{text:n,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[n,o]of Object.entries(e)){let s=n.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");if(s.includes("password")||s.includes("passwd")||s==="pwd"||s.includes("secret")||s.includes("token")||s.includes("cookie")||s.includes("authorization")||s.includes("apikey")||s.includes("accesskey")||s.includes("privatekey")||s.includes("clientsecret")||s.includes("credential")){t[n]="[REDACTED]";continue}t[n]=this.sanitizeValue(o)}return t}return e}};var $r=new L;import{homedir as nr}from"node:os";import{join as or}from"node:path";import{DeleteObjectCommand as Mt,GetObjectCommand as It,HeadObjectCommand as Pt,ListObjectsV2Command as Tt,PutObjectCommand as jt,S3Client as Ct}from"@aws-sdk/client-s3";import{getSignedUrl as _t}from"@aws-sdk/s3-request-presigner";var z=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ct({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new jt({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await _t(this.client,new It({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Pt({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 Mt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Tt({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let s of o.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};import{access as Me,mkdir as Ot,readFile as Nt,readdir as Kt,rm as $t,stat as Ie,writeFile as Dt}from"node:fs/promises";import{dirname as Lt,join as zt,relative as Pe,resolve as Vt}from"node:path";var M=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Vt(this.root,t)}async put(e,t){let n=this.path(e);await Ot(Lt(n),{recursive:!0}),await Dt(n,t)}async get(e){try{return await Nt(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 Me(this.path(e)),!0}catch{return!1}}async delete(e){await $t(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await Me(t)}catch{return n}let o=async a=>{let c=await Kt(a,{withFileTypes:!0});for(let u of c){let d=zt(a,u.name);if(u.isDirectory()){await o(d);continue}let f=await Ie(d);n.push({key:Pe(this.root,d),size:f.size,updatedAt:f.mtime.toISOString()})}},s=await Ie(t);return s.isDirectory()?await o(t):n.push({key:Pe(this.root,t),size:s.size,updatedAt:s.mtime.toISOString()}),n}};import{DeleteObjectCommand as Bt,GetObjectCommand as Yt,HeadObjectCommand as Ft,ListObjectsV2Command as Ut,PutObjectCommand as Gt,S3Client as Wt}from"@aws-sdk/client-s3";import{getSignedUrl as Ht}from"@aws-sdk/s3-request-presigner";var I=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Wt({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Gt({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Ht(this.client,new Yt({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`${this.name} download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new Ft({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 Bt({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Ut({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let s of o.Contents??[])s.Key&&t.push({key:s.Key,size:s.Size,updatedAt:s.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};import{createCipheriv as Jt,createDecipheriv as qt,createHash as Zt,randomBytes as Qt,timingSafeEqual as Xt}from"node:crypto";import{readFileSync as er}from"node:fs";var k=Buffer.from("TNMEME01","ascii"),je=1,P=8,T=12,H=16,Ce=k.length+1+P+T+H,tr="toolnet-memory:remote-encryption:v1:",_e="aes-256-gcm",J=32,l=class extends Error{constructor(t,n){super(n);this.code=t;this.name="RemoteEncryptionError"}code};function rr(r){return r?["1","true","yes","on","enabled"].includes(r.trim().toLowerCase()):!1}function q(r=process.env){return rr(r.TOOLNET_REMOTE_ENCRYPTION)}function Te(r){let e=r.trim();if(!e)throw new l("REMOTE_ENCRYPTION_KEY_EMPTY","Remote encryption key is empty.");let t;if(e.startsWith("hex:")){let n=e.slice(4);if(!/^[0-9a-f]{64}$/iu.test(n))throw new l("REMOTE_ENCRYPTION_KEY_INVALID","hex: remote encryption key must contain exactly 64 hexadecimal characters.");t=Buffer.from(n,"hex")}else if(/^[0-9a-f]{64}$/iu.test(e))t=Buffer.from(e,"hex");else{let n=e.startsWith("base64:")?e.slice(7):e;if(!/^[A-Za-z0-9+/_-]+={0,2}$/u.test(n))throw new l("REMOTE_ENCRYPTION_KEY_INVALID","Remote encryption key must be 32 raw bytes encoded as hexadecimal or base64.");t=Buffer.from(n,n.includes("-")||n.includes("_")?"base64url":"base64")}if(t.length!==J)throw new l("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH",`Remote encryption key must decode to exactly ${J} bytes.`);return t}function Oe(r=process.env){let e=r.TOOLNET_REMOTE_ENCRYPTION_KEY?.trim(),t=r.TOOLNET_REMOTE_ENCRYPTION_KEY_FILE?.trim();if(e&&t)throw new l("REMOTE_ENCRYPTION_KEY_AMBIGUOUS","Configure either TOOLNET_REMOTE_ENCRYPTION_KEY or TOOLNET_REMOTE_ENCRYPTION_KEY_FILE, not both.");if(e)return Te(e);if(t){let n;try{n=er(t,"utf8")}catch(o){throw new l("REMOTE_ENCRYPTION_KEY_FILE_READ_FAILED",[`Unable to read remote encryption key file: ${t}.`,o instanceof Error?o.message:String(o)].join(" "))}return Te(n)}}function Ne(r){return Zt("sha256").update(r).digest().subarray(0,P)}function Ke(r){return Buffer.from(`${tr}${r}`,"utf8")}function Z(r){return r.byteLength<k.length?!1:Buffer.from(r).subarray(0,k.length).equals(k)}function $e(r,e,t){if(t.byteLength!==J)throw new l("REMOTE_ENCRYPTION_KEY_INVALID_LENGTH","AES-256-GCM requires a 32-byte key.");let n=typeof e=="string"?Buffer.from(e,"utf8"):Buffer.from(e),o=Qt(T),s=Jt(_e,t,o);s.setAAD(Ke(r));let a=Buffer.concat([s.update(n),s.final()]),c=s.getAuthTag(),u=Buffer.alloc(Ce),d=0;return k.copy(u,d),d+=k.length,u.writeUInt8(je,d),d+=1,Ne(t).copy(u,d),d+=P,o.copy(u,d),d+=T,c.copy(u,d),Buffer.concat([u,a])}function De(r,e,t){let n=Buffer.from(e);if(!Z(n))throw new l("REMOTE_ENCRYPTION_ENVELOPE_REQUIRED","Payload is not a ToolNet encrypted remote object.");if(n.length<Ce)throw new l("REMOTE_ENCRYPTION_ENVELOPE_TRUNCATED","Encrypted remote payload is truncated.");let o=k.length,s=n.readUInt8(o);if(o+=1,s!==je)throw new l("REMOTE_ENCRYPTION_VERSION_UNSUPPORTED",`Unsupported remote encryption envelope version: ${s}.`);let a=n.subarray(o,o+P);o+=P;let c=Ne(t);if(!Xt(a,c))throw new l("REMOTE_ENCRYPTION_KEY_MISMATCH","Configured remote encryption key does not match this encrypted object.");let u=n.subarray(o,o+T);o+=T;let d=n.subarray(o,o+H);o+=H;let f=n.subarray(o),m=qt(_e,t,u);m.setAAD(Ke(r)),m.setAuthTag(d);try{return Buffer.concat([m.update(f),m.final()])}catch{throw new l("REMOTE_ENCRYPTION_AUTH_FAILED","Encrypted remote object failed AES-GCM authentication.")}}var Q=class{constructor(e,t){this.inner=e;this.options=t;if(this.name=e.name,t.enabled&&!t.key)throw new l("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote client-side encryption is enabled but no encryption key is configured.")}inner;options;name;async put(e,t,n){if(!this.options.enabled){await this.inner.put(e,t,n);return}let o=this.options.key;if(!o)throw new l("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote encryption key is unavailable.");let s=$e(e,t,o);await this.inner.put(e,s,"application/octet-stream")}async get(e){let t=await this.inner.get(e);if(!t)return null;if(!Z(t))return t;if(!this.options.enabled)throw new l("REMOTE_ENCRYPTION_REQUIRED",["Remote object is client-side encrypted.","Enable TOOLNET_REMOTE_ENCRYPTION and configure the matching key."].join(" "));let n=this.options.key;if(!n)throw new l("REMOTE_ENCRYPTION_KEY_REQUIRED","Remote object is encrypted but no decryption key is configured.");return De(e,t,n)}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){return this.inner.exists(e)}async delete(e){await this.inner.delete(e)}async list(e=""){return this.inner.list(e)}};function Le(r,e=process.env){if(r.name==="local")return q(e)&&console.warn("[storage] Remote encryption requested but active storage provider is local; local data remains unchanged."),r;let t=q(e),n=t?Oe(e):void 0;return new Q(r,{enabled:t,key:n})}function j(r){return Le(r)}function X(r,e){return console.warn(e),j(new M(r))}function ze(r){let e=r.localRoot??or(nr(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?j(new I({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):X(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?j(new I({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):X(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?j(new z({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey})):X(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return j(new M(e))}function sr(r){return new Promise(e=>setTimeout(e,r))}async function Ve(r,e={}){let t=Math.max(1,e.attempts??3),n=e.baseDelayMs??150,o=e.maxDelayMs??2e3,s;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(s=c,a>=t)break;let u=Math.min(o,n*2**(a-1)),d=Math.floor(Math.random()*Math.max(1,u*.2));await sr(u+d)}throw s}var ir=new Set(["put","get","getText","delete","list"]);function Be(r,e={}){return new Proxy(r,{get(t,n){let o=Reflect.get(t,n,t);return typeof o!="function"?o:ir.has(n)?(...s)=>Ve(()=>Promise.resolve(o.apply(t,s)),e):o.bind(t)}})}function Ye(r){let e=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function Fe(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var V=class{constructor(e,t,n,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=Ye(o??n),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),n=t,o=await this.provider.getText(e);if(o){let a;try{a=JSON.parse(o)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(n=a.createdAt)}let s={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,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=Fe(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,n){return await this.ensureRegistered(),this.provider.put(this.key(e),t,n)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as We}from"node:path";import{mkdirSync as pr}from"node:fs";import{createHash as ar}from"node:crypto";import{dirname as cr}from"node:path";import{mkdirSync as ur,readFileSync as Ko,renameSync as dr,writeFileSync as lr}from"node:fs";function Ue(r){return ar("sha256").update(r).digest("hex")}function Ge(r,e){ur(cr(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;lr(t,JSON.stringify(e,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),dr(t,r)}function Je(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function g(r,e=20){let t=[],n=new Set;for(let o of r.slice().reverse()){let s=Je(o);if(!(!s||n.has(s))&&(n.add(s),t.push(o),t.length>=e))break}return t.reverse()}function mr(r,e=20){let t=new Map;for(let n of r){let o=`${n.kind}|${Je(n.command)}`;t.delete(o),t.set(o,n)}return Array.from(t.values()).slice(-e)}function fr(r){return r.kind==="phase"?/^Phase\s+\d+$/iu.test(r.text):r.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(r.text):!1}function He(r,e){let t=e.status??r?.status??"pending",n=t;r&&(r.status==="completed"&&t!=="completed"?n="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(n=r.status));let o=r&&fr(e)?r.title:e.text;return{id:r?.id??Ue(e.key).slice(0,24),title:o,status:n,order:e.order??r?.order,confidence:Math.max(e.confidence,r?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function qe(r,e){let t=`projects/${r.id}/work/observations/`,n=await e.list(t),o=[];for(let s of n.filter(a=>a.key.endsWith(".json")).sort((a,c)=>a.key.localeCompare(c.key))){let a=await e.getText(s.key);if(a)try{let c=JSON.parse(a);c.version===1&&Array.isArray(c.observations)&&o.push(c)}catch{}}return o}async function ee(r,e){let n=(await qe(r,e)).flatMap(i=>i.observations).sort((i,p)=>{let pe=i.occurredAt.localeCompare(p.occurredAt);if(pe!==0)return pe;let me=i.sequence-p.sequence;return me!==0?me:i.id.localeCompare(p.id)}),o=new Map,s=new Map,a,c,u,d,f,m=[],h=[],re=[],ne=[],oe=[],B=new Map,se=[],ie=[],ae=[],ce=[],ue=[],de=[];for(let i of n)switch(i.kind){case"request":a=i.text;break;case"activity":c=i.text;break;case"goal":u=i.text;break;case"plan":d=i.text;break;case"phase":o.set(i.key,He(o.get(i.key),i));break;case"task":s.set(i.key,He(s.get(i.key),i));break;case"decision":m.push(i.text);break;case"blocker":h.push(i.text);break;case"warning":re.push(i.text);break;case"next_action":ne.push(i.text);break;case"file":{oe.push(i.text);let p=i.fileAction??"active";B.delete(i.text),B.set(i.text,p),p==="modified"?se.push(i.text):p==="created"?ie.push(i.text):p==="deleted"&&ae.push(i.text);break}case"command":ce.push(i.text);break;case"test":ue.push(i.text),i.checkKind&&de.push({kind:i.checkKind,command:i.text,status:i.checkStatus??"unknown",updatedAt:i.occurredAt,agent:i.agent,nativeSessionId:i.nativeSessionId});break;case"session":f={agent:i.agent,nativeSessionId:i.nativeSessionId,sessionKey:i.sessionKey,updatedAt:i.occurredAt};break}let S=Array.from(o.values()).sort((i,p)=>(i.order??Number.MAX_SAFE_INTEGER)-(p.order??Number.MAX_SAFE_INTEGER)),y=Array.from(s.values()).sort((i,p)=>(i.order??Number.MAX_SAFE_INTEGER)-(p.order??Number.MAX_SAFE_INTEGER)),Y=S.find(i=>i.status==="in_progress")??S.find(i=>i.status==="blocked")??S.find(i=>i.status==="pending"),C=y.find(i=>i.status==="in_progress")??y.find(i=>i.status==="blocked")??y.find(i=>i.status==="pending"),Qe=g([...ne,...C?[C.title]:[],...!C&&Y?[Y.title]:[],...y.filter(i=>i.status==="pending").slice(0,5).map(i=>i.title)],8),Xe=g([...h,...S.filter(i=>i.status==="blocked").map(i=>i.title),...y.filter(i=>i.status==="blocked").map(i=>i.title)],20),F={version:1,projectId:r.id,projectName:r.name,currentRequest:a,currentActivity:c,goal:u,plan:d,phases:S,tasks:y,decisions:g(m,20),blockers:Xe,warnings:g(re,20),nextActions:Qe,filesTouched:g(oe,30),activeFiles:Array.from(B.entries()).filter(([,i])=>i!=="deleted").map(([i])=>i).slice(-5),modifiedFiles:g(se,30),createdFiles:g(ie,30),deletedFiles:g(ae,30),commands:g(ce,20),tests:g(ue,20),checks:mr(de,20),currentPhase:Y,currentTask:C,progress:{phasesTotal:S.length,phasesCompleted:S.filter(i=>i.status==="completed").length,tasksTotal:y.length,tasksCompleted:y.filter(i=>i.status==="completed").length,blocked:S.filter(i=>i.status==="blocked").length+y.filter(i=>i.status==="blocked").length},lastSession:f,updatedAt:n.length?n[n.length-1].occurredAt:new Date().toISOString()},le=We(r.rootPath,".toolnet","work");return pr(le,{recursive:!0}),Ge(We(le,"current.json"),F),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(F,null,2)+`
6
+ `,"application/json"),F}async function te(r,e){if((await qe(r,e)).length>0)return ee(r,e);let n=await e.getText(`projects/${r.id}/work/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}function gr(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function yr(r){let e=fe(),t=Be(ze({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new V(t,r.id,r.name,r.remote??r.name)}function Ze(r){console.log(),console.log(`Project: ${r.projectName}`),r.goal&&console.log(`Goal: ${r.goal}`),r.currentPhase&&console.log(`Current phase: ${r.currentPhase.title} [${r.currentPhase.status}]`),r.currentTask&&console.log(`Current task: ${r.currentTask.title} [${r.currentTask.status}]`),console.log(`Phases: ${r.progress.phasesCompleted}/${r.progress.phasesTotal}`),console.log(`Tasks: ${r.progress.tasksCompleted}/${r.progress.tasksTotal}`),r.nextActions.length&&(console.log(),console.log("Next actions:"),r.nextActions.forEach((e,t)=>console.log(`${t+1}. ${e}`))),r.blockers.length&&(console.log(),console.log("Blockers:"),r.blockers.forEach(e=>console.log(`- ${e}`))),r.lastSession&&(console.log(),console.log(`Last session: ${r.lastSession.agent} / ${r.lastSession.nativeSessionId}`))}async function hr(){let[r="help",...e]=process.argv.slice(2),t=new $().detect(gr(e,"--project")??process.cwd()),n=yr(t);if(r==="reconcile"){let o=await ee(t,n);Ze(o);return}if(r==="status"){let o=await te(t,n);if(!o){console.log("No active work state yet.");return}Ze(o);return}if(r==="json"){let o=await te(t,n);console.log(JSON.stringify(o,null,2));return}console.log(`Work Continuity
7
7
 
8
8
  Commands:
9
9
  status [--project PATH]
10
10
  json [--project PATH]
11
11
  reconcile [--project PATH]
12
- `)}Rt().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
12
+ `)}hr().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolnet-memory",
3
- "version": "0.3.15",
3
+ "version": "0.3.17",
4
4
  "description": "Persistent project memory, work continuity, and code intelligence for AI coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -112,7 +112,42 @@
112
112
  "release:certify:final": "bash scripts/final-release-certify.sh",
113
113
  "release:verify:static": "node scripts/verify-release-contract.mjs",
114
114
  "security:isolation:test": "vitest run tests/security/security-isolation.test.ts",
115
- "release:verify:v0315": "node scripts/verify-v0315-contract.mjs"
115
+ "release:verify:v0315": "node scripts/verify-v0315-contract.mjs",
116
+ "project:cross-machine:test": "vitest run tests/core/project-cross-machine-identity.test.ts tests/core/project-identity.test.ts",
117
+ "repository:truth:cleanup": "node scripts/repository-truth-cleanup.mjs",
118
+ "repository:truth:audit": "node scripts/repository-truth-audit.mjs",
119
+ "repository:truth:test": "vitest run tests/repository-truth/repository-truth.test.ts",
120
+ "code:search:naming:audit": "node scripts/code-search-naming-audit.mjs",
121
+ "code:search:naming:test": "vitest run tests/code-intelligence/code-search-naming.test.ts",
122
+ "docs:truth:audit": "node scripts/documentation-truth-audit.mjs",
123
+ "docs:truth:test": "vitest run tests/documentation/documentation-truth.test.ts",
124
+ "memory:conflict-v2:audit": "node scripts/conflict-v2-audit.mjs",
125
+ "memory:conflict-v2:test": "vitest run tests/memory/conflict-v2.test.ts",
126
+ "release:verify:v0316": "node scripts/verify-v0316-contract.mjs",
127
+ "project:identity:audit": "node scripts/project-identity-audit.mjs",
128
+ "graph:security:audit": "node scripts/graph-security-audit.mjs",
129
+ "graph:security:test": "vitest run tests/visualization/graph-security.test.ts",
130
+ "docker:contract:audit": "node scripts/docker-contract-audit.mjs",
131
+ "docker:contract:test": "vitest run tests/production/docker-contract.test.ts",
132
+ "docker:build": "docker build -t toolnet-memory:local .",
133
+ "docker:smoke": "bash scripts/docker-smoke.sh",
134
+ "security:remote-encryption:audit": "node scripts/remote-encryption-audit.mjs",
135
+ "security:remote-encryption:test": "vitest run tests/security/remote-encryption.test.ts",
136
+ "storage:scope:audit": "node scripts/storage-scope-audit.mjs",
137
+ "standalone:build": "node scripts/build-standalone.mjs",
138
+ "standalone:smoke": "bash scripts/standalone-smoke.sh",
139
+ "standalone:routes:audit": "node scripts/standalone-route-audit.mjs",
140
+ "standalone:contract:test": "vitest run tests/production/standalone-contract.test.ts",
141
+ "standalone:contract:audit": "node scripts/standalone-contract-audit.mjs",
142
+ "audit:test": "vitest run tests/audit/audit-log.test.ts",
143
+ "audit:verify": "node bundle/audit.js verify",
144
+ "retention:auto-gc:test": "vitest run tests/retention/auto-gc-scheduler.test.ts",
145
+ "phase29:audit": "node scripts/phase29-audit.mjs",
146
+ "code:non-ts:test": "vitest run tests/code-intelligence/non-ts-foundation.test.ts",
147
+ "phase30:audit": "node scripts/phase30-audit.mjs",
148
+ "code:capabilities": "node bundle/code-capabilities.js",
149
+ "release:verify:v0317": "node scripts/verify-v0317-contract.mjs",
150
+ "release:certify:v0317": "bash scripts/final-release-certify-v0317.sh"
116
151
  },
117
152
  "dependencies": {
118
153
  "@aws-sdk/client-s3": "^3.1102.0",
@@ -126,6 +161,7 @@
126
161
  "@types/node": "^24.0.0",
127
162
  "@typescript-eslint/eslint-plugin": "^8.66.0",
128
163
  "@typescript-eslint/parser": "^8.66.0",
164
+ "@yao-pkg/pkg": "6.22.0",
129
165
  "3d-force-graph": "^1.80.0",
130
166
  "esbuild": "^0.28.1",
131
167
  "eslint": "^10.8.1",
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "name": "toolnet-memory",
4
- "version": "0.3.15",
4
+ "version": "0.3.17",
5
5
  "releaseSeries": "0.3.x",
6
6
  "runtime": {
7
7
  "node": ">=22",
8
8
  "localMemoryRuntime": true,
9
9
  "requiresLlm": false,
10
10
  "requiresEmbeddings": false,
11
- "retrieval": ["BM25", "SQLite FTS5"],
11
+ "retrieval": [
12
+ "BM25",
13
+ "SQLite FTS5"
14
+ ],
12
15
  "sharedProjectMemory": true,
13
16
  "multiHostConvergence": true,
14
17
  "storageImplementationFrozen": true
@@ -93,7 +96,11 @@
93
96
  "remoteDistributedLock": false
94
97
  },
95
98
  "largeRepository": {
96
- "benchmarkProfiles": ["10k", "50k", "100k"],
99
+ "benchmarkProfiles": [
100
+ "10k",
101
+ "50k",
102
+ "100k"
103
+ ],
97
104
  "maxCodeFileBytes": 2097152,
98
105
  "symlinkTraversal": false,
99
106
  "boundedParseConcurrency": true,
@@ -104,7 +111,16 @@
104
111
  "codeIntelligence": {
105
112
  "parserEngine": "typescript-compiler-api",
106
113
  "treeSitterRuntime": false,
107
- "supportedExtensions": [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"],
114
+ "supportedExtensions": [
115
+ ".ts",
116
+ ".tsx",
117
+ ".js",
118
+ ".jsx",
119
+ ".mts",
120
+ ".cts",
121
+ ".mjs",
122
+ ".cjs"
123
+ ],
108
124
  "tsconfigPaths": true,
109
125
  "baseUrlResolution": true,
110
126
  "projectReferences": true,
@@ -119,6 +135,129 @@
119
135
  "projectDataTrusted": false,
120
136
  "clientSideEncryption": false,
121
137
  "requiresEncryptionKey": false
138
+ },
139
+ "projectIdentity": {
140
+ "crossMachine": true,
141
+ "existingManifestCanonical": true,
142
+ "normalizedGitRemote": true,
143
+ "sshHttpsEquivalent": true,
144
+ "remoteIdentityRegistry": true,
145
+ "legacyExplicitAdoption": true,
146
+ "silentUnverifiedAdoption": false,
147
+ "pathHashPrimaryForGit": false,
148
+ "credentialLeakIntoFingerprint": false
149
+ },
150
+ "repositoryTruth": {
151
+ "deadScaffoldsRemoved": true,
152
+ "unsupportedCapabilitiesExplicit": true,
153
+ "legacySyncScaffoldRemoved": true,
154
+ "multiHostReplacement": "src/multi-host",
155
+ "unclassifiedZeroByteSourceFiles": 0,
156
+ "storagePlaceholdersFrozen": true
157
+ },
158
+ "codeSearch": {
159
+ "displayName": "Local Code Search — SQLite FTS5/BM25",
160
+ "engine": "sqlite-fts5-bm25",
161
+ "mode": "lexical",
162
+ "embeddings": false,
163
+ "vectorDatabase": false,
164
+ "networkRequired": false,
165
+ "legacySemanticAliasesPreserved": true
166
+ },
167
+ "documentationTruth": {
168
+ "runtimeCapabilityMatrix": true,
169
+ "unsupportedLanguagesExplicit": true,
170
+ "unsupportedStorageExplicit": true,
171
+ "crossMachineIdentityDocumented": true,
172
+ "multiHostArchitectureDocumented": true,
173
+ "clientEncryptionClaimed": false
174
+ },
175
+ "conflictEngineV2": {
176
+ "deterministic": true,
177
+ "llmMerge": false,
178
+ "embeddingMatching": false,
179
+ "kinds": [
180
+ "rule",
181
+ "decision",
182
+ "todo",
183
+ "next_action",
184
+ "fix",
185
+ "context",
186
+ "architecture"
187
+ ],
188
+ "topicMatching": true,
189
+ "entityMatching": true,
190
+ "lexicalSubjectMatching": true,
191
+ "verifiedFixCompletesTask": true,
192
+ "verifiedFixResolvesContext": true,
193
+ "conflictingMemoryActive": false,
194
+ "completedMemoryActive": false,
195
+ "resolvedMemoryActive": false
196
+ },
197
+ "graphExposure": {
198
+ "localhostDefault": true,
199
+ "optionalBearerAuthentication": true,
200
+ "hostHeaderProtection": true,
201
+ "sameOriginApiProtection": true,
202
+ "minimalUnauthenticatedHealth": true
203
+ },
204
+ "docker": {
205
+ "officialDockerfile": true,
206
+ "nodeRuntime": 22,
207
+ "multiStage": true,
208
+ "nonRoot": true,
209
+ "ipcHealthcheck": true,
210
+ "ghcr": true,
211
+ "multiArch": [
212
+ "linux/amd64",
213
+ "linux/arm64"
214
+ ]
215
+ },
216
+ "remoteEncryption": {
217
+ "supported": true,
218
+ "defaultEnabled": false,
219
+ "keyRequiredByDefault": false,
220
+ "algorithm": "AES-256-GCM",
221
+ "existingPlaintextReadable": true,
222
+ "automaticMigration": false
223
+ },
224
+ "standalone": {
225
+ "supported": true,
226
+ "nodeRequiredOnTarget": false,
227
+ "linuxX64": true,
228
+ "linuxArm64": true,
229
+ "macosX64": true,
230
+ "macosArm64": true,
231
+ "windowsX64": true
232
+ },
233
+ "auditLog": {
234
+ "projectScoped": true,
235
+ "appendOnly": true,
236
+ "sha256HashChain": true,
237
+ "durableSanitization": true,
238
+ "rawMemoryContent": false,
239
+ "rawGuardCommand": false
240
+ },
241
+ "autoGc": {
242
+ "supported": true,
243
+ "enabledByDefault": false,
244
+ "defaultIntervalHours": 168,
245
+ "remoteGcEnabledByDefault": false,
246
+ "implicitProjectInit": false,
247
+ "filesystemProjectScan": false
248
+ },
249
+ "nonTsIntelligence": {
250
+ "localCodeSearch": [
251
+ "python",
252
+ "go",
253
+ "rust",
254
+ "c",
255
+ "cpp"
256
+ ],
257
+ "engine": "sqlite-fts5-bm25",
258
+ "structuralParserSupport": false,
259
+ "lspFoundation": "detection-only",
260
+ "automaticLspDownload": false
122
261
  }
123
262
  }
124
263
  }