toolnet-memory 0.3.14 → 0.3.15

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 +0 -1
  2. package/.release-target +1 -1
  3. package/README.md +25 -1
  4. package/bin/toolnet-memory +5 -0
  5. package/bundle/agy-hook.js +67 -67
  6. package/bundle/agy.js +61 -61
  7. package/bundle/api.js +15 -15
  8. package/bundle/auto-integrate.js +28 -28
  9. package/bundle/background-refresh.js +7 -7
  10. package/bundle/claude-hook.js +51 -50
  11. package/bundle/codex-context.js +25 -24
  12. package/bundle/codex.js +71 -71
  13. package/bundle/context-runtime.js +45 -44
  14. package/bundle/context.js +15 -15
  15. package/bundle/continuity-certify.js +13 -13
  16. package/bundle/copilot-hook.js +87 -85
  17. package/bundle/copilot.js +4 -4
  18. package/bundle/cursor-hook.js +87 -85
  19. package/bundle/cursor.js +4 -4
  20. package/bundle/doctor.js +4 -4
  21. package/bundle/full-index.js +20 -20
  22. package/bundle/gc.js +3 -0
  23. package/bundle/graph-index.js +4 -4
  24. package/bundle/graph-ui.js +3 -3
  25. package/bundle/grok-hook.js +60 -59
  26. package/bundle/grok.js +5 -5
  27. package/bundle/handoff-refresh.js +10 -10
  28. package/bundle/help-cli.js +4 -4
  29. package/bundle/impact.js +4 -4
  30. package/bundle/incremental.js +5 -4
  31. package/bundle/init.js +37 -37
  32. package/bundle/integration-status.js +4 -4
  33. package/bundle/kiro-hook.js +82 -81
  34. package/bundle/learner.js +6 -6
  35. package/bundle/mcp.js +17 -17
  36. package/bundle/memory-agent.js +7 -7
  37. package/bundle/memory-query.js +7 -7
  38. package/bundle/opencode.js +80 -80
  39. package/bundle/project-manual.js +7 -7
  40. package/bundle/recovery-certify.js +15 -15
  41. package/bundle/runtime.js +13 -12
  42. package/bundle/semantic.js +5 -5
  43. package/bundle/snapshot.js +4 -4
  44. package/bundle/status-cli.js +4 -4
  45. package/bundle/toolnet-cli.js +60 -60
  46. package/bundle/work-continuity.js +7 -7
  47. package/package.json +9 -3
  48. package/release-manifest.json +54 -3
@@ -1,12 +1,12 @@
1
- import{existsSync as he,readFileSync as ke}from"node:fs";import{homedir as Se}from"node:os";import{join as ve}from"node:path";function we(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 be(){let r=process.env.TOOLNET_GLOBAL_ENV??ve(Se(),".config","toolnet-memory",".env");if(!he(r))return;let e=ke(r,"utf8");for(let t of e.split(/\r?\n/)){let s=t.trim();if(!s||s.startsWith("#"))continue;s.startsWith("export ")&&(s=s.slice(7));let n=s.indexOf("=");if(n<=0)continue;let i=s.slice(0,n).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(i)&&process.env[i]===void 0&&(process.env[i]=we(s.slice(n+1)))}}be();function h(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function k(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function G(){return{memory:{autoCapture:h(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:h(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:h(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:h(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:k(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:k(process.env.MEMORY_RERANK_TOP,10),finalContext:k(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:k(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:h(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:k(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as xe}from"node:crypto";import{existsSync as T,mkdirSync as je,readFileSync as Ae,renameSync as Re,writeFileSync as Me}from"node:fs";import{basename as Pe,dirname as x,join as A,parse as q,resolve as S}from"node:path";var X=".toolnet",Ce="project.json";function Ie(r){return xe("sha256").update(r).digest("hex").slice(0,16)}function E(r){return A(r,X,Ce)}function Te(r){return T(E(r))}function Ee(r,e){let t=S(r),s=q(t).root;for(;;){if(Te(t))return t;if(t===s||e&&t===S(e))break;let n=x(t);if(n===t)break;t=n}return null}function Oe(r){let e=S(r),t=q(e).root,s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(s.some(i=>T(A(e,i))))return e;if(e===t)break;let n=x(e);if(n===e)break;e=n}return S(r)}function _e(r){let e;try{e=JSON.parse(Ae(r,"utf8"))}catch(n){throw new Error(`Invalid ToolNet project manifest: ${r}: ${n instanceof Error?n.message:String(n)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let s=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:x(x(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:s,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:s,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function Q(r,e){let t=A(r,X);je(t,{recursive:!0});let s=E(r),n=`${s}.tmp-${process.pid}`;Me(n,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),Re(n,s)}function Z(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 j=class{detect(e=process.cwd()){let t=S(e),s=Oe(t),i=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>T(A(s,m))),a=Ee(t,i?s:void 0);if(a){let m=E(a),g=_e(m);return g.rootPath!==a&&(g.rootPath=a,g.updatedAt=new Date().toISOString(),Q(a,g)),Z(g,a)}let c=new Date().toISOString(),d=Pe(s),p={version:1,id:Ie(s),name:d,remote:d,rootPath:s,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return Q(s,p),Z(p,s)}};import{homedir as st}from"node:os";import{join as ot}from"node:path";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 R=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Le({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,s="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new ze({Bucket:this.bucket,Key:e,Body:n,ContentType:s}))}async get(e){let t=await Ve(this.client,new $e({Bucket:this.bucket,Key:e}),{expiresIn:60}),s=await fetch(t,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`HF download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new 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=[],s;do{let n=await this.client.send(new De({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:s}));for(let i of n.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});s=n.IsTruncated?n.NextContinuationToken:void 0}while(s);return t}};import{access as ee,mkdir as We,readFile as Fe,readdir as Be,rm as Je,stat as te,writeFile as Ue}from"node:fs/promises";import{dirname as He,join as Ye,relative as re,resolve as Ge}from"node:path";var v=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return Ge(this.root,t)}async put(e,t){let s=this.path(e);await We(He(s),{recursive:!0}),await Ue(s,t)}async get(e){try{return await Fe(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 ee(this.path(e)),!0}catch{return!1}}async delete(e){await Je(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),s=[];try{await ee(t)}catch{return s}let n=async a=>{let c=await Be(a,{withFileTypes:!0});for(let d of c){let p=Ye(a,d.name);if(d.isDirectory()){await n(p);continue}let m=await te(p);s.push({key:re(this.root,p),size:m.size,updatedAt:m.mtime.toISOString()})}},i=await te(t);return i.isDirectory()?await n(t):s.push({key:re(this.root,t),size:i.size,updatedAt:i.mtime.toISOString()}),s}};import{DeleteObjectCommand as Qe,GetObjectCommand as Ze,HeadObjectCommand as qe,ListObjectsV2Command as Xe,PutObjectCommand as et,S3Client as tt}from"@aws-sdk/client-s3";import{getSignedUrl as rt}from"@aws-sdk/s3-request-presigner";var w=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new tt({region:e.region??"us-east-1",endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,s="application/octet-stream"){let n=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new et({Bucket:this.bucket,Key:e,Body:n,ContentType:s}))}async get(e){let t=await rt(this.client,new Ze({Bucket:this.bucket,Key:e}),{expiresIn:60}),s=await fetch(t,{redirect:"follow"});if(s.status===404)return null;if(!s.ok)throw new Error(`${this.name} download failed: ${s.status} ${s.statusText}`);return new Uint8Array(await s.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new qe({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new Qe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],s;do{let n=await this.client.send(new Xe({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:s}));for(let i of n.Contents??[])i.Key&&t.push({key:i.Key,size:i.Size,updatedAt:i.LastModified?.toISOString()});s=n.IsTruncated?n.NextContinuationToken:void 0}while(s);return t}};function O(r,e){return console.warn(e),new v(r)}function se(r){let e=r.localRoot??ot(st(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new w({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let t=r.s3;return t?.bucket&&t.accessKeyId&&t.secretAccessKey?new w({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new R({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):O(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new v(e)}function nt(r){return new Promise(e=>setTimeout(e,r))}async function oe(r,e={}){let t=Math.max(1,e.attempts??3),s=e.baseDelayMs??150,n=e.maxDelayMs??2e3,i;for(let a=1;a<=t;a++)try{return await r()}catch(c){if(i=c,a>=t)break;let d=Math.min(n,s*2**(a-1)),p=Math.floor(Math.random()*Math.max(1,d*.2));await nt(d+p)}throw i}var it=new Set(["put","get","getText","delete","list"]);function ne(r,e={}){return new Proxy(r,{get(t,s){let n=Reflect.get(t,s,t);return typeof n!="function"?n:it.has(s)?(...i)=>oe(()=>Promise.resolve(n.apply(t,i)),e):n.bind(t)}})}function ie(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 ae(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(s)return`${s[1]}memory/records/${s[2]}`;if(s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),s)return`${s[1]}memory/vectors/${s[2]}`;if(s=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),s)return`${s[1]}code/graph/${s[2]}`;let n=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return n?`${n[1]}memory/records/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),n?`${n[1]}memory/vectors/${n[2]}`:(n=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),n?`${n[1]}code/graph/${n[2]}`:e))}var M=class{constructor(e,t,s,n){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=s,this.folder=ie(n??s),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),s=t,n=await this.provider.getText(e);if(n){let a;try{a=JSON.parse(n)}catch(c){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${c instanceof Error?c.message:String(c)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(s=a.createdAt)}let i={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:s,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=ae(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,s){return await this.ensureRegistered(),this.provider.put(this.key(e),t,s)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{join as de}from"node:path";import{mkdirSync as lt}from"node:fs";import{createHash as at}from"node:crypto";import{dirname as ct}from"node:path";import{mkdirSync as ut,readFileSync as ys,renameSync as dt,writeFileSync as pt}from"node:fs";function ce(r){return at("sha256").update(r).digest("hex")}function ue(r,e){ut(ct(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;pt(t,JSON.stringify(e,null,2)+`
5
- `,{encoding:"utf8",mode:384}),dt(t,r)}function le(r){return r.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function l(r,e=20){let t=[],s=new Set;for(let n of r.slice().reverse()){let i=le(n);if(!(!i||s.has(i))&&(s.add(i),t.push(n),t.length>=e))break}return t.reverse()}function mt(r,e=20){let t=new Map;for(let s of r){let n=`${s.kind}|${le(s.command)}`;t.delete(n),t.set(n,s)}return Array.from(t.values()).slice(-e)}function ft(r){return r.kind==="phase"?/^Phase\s+\d+$/iu.test(r.text):r.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(r.text):!1}function pe(r,e){let t=e.status??r?.status??"pending",s=t;r&&(r.status==="completed"&&t!=="completed"?s="completed":t==="pending"&&(r.status==="in_progress"||r.status==="blocked")&&(s=r.status));let n=r&&ft(e)?r.title:e.text;return{id:r?.id??ce(e.key).slice(0,24),title:n,status:s,order:e.order??r?.order,confidence:Math.max(e.confidence,r?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function me(r,e){let t=`projects/${r.id}/work/observations/`,s=await e.list(t),n=[];for(let i of s.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)&&n.push(c)}catch{}}return n}async function _(r,e){let s=(await me(r,e)).flatMap(o=>o.observations).sort((o,u)=>{let H=o.occurredAt.localeCompare(u.occurredAt);if(H!==0)return H;let Y=o.sequence-u.sequence;return Y!==0?Y:o.id.localeCompare(u.id)}),n=new Map,i=new Map,a,c,d,p,m,g=[],$=[],K=[],D=[],z=[],P=new Map,L=[],V=[],W=[],F=[],B=[],J=[];for(let o of s)switch(o.kind){case"request":a=o.text;break;case"activity":c=o.text;break;case"goal":d=o.text;break;case"plan":p=o.text;break;case"phase":n.set(o.key,pe(n.get(o.key),o));break;case"task":i.set(o.key,pe(i.get(o.key),o));break;case"decision":g.push(o.text);break;case"blocker":$.push(o.text);break;case"warning":K.push(o.text);break;case"next_action":D.push(o.text);break;case"file":{z.push(o.text);let u=o.fileAction??"active";P.delete(o.text),P.set(o.text,u),u==="modified"?L.push(o.text):u==="created"?V.push(o.text):u==="deleted"&&W.push(o.text);break}case"command":F.push(o.text);break;case"test":B.push(o.text),o.checkKind&&J.push({kind:o.checkKind,command:o.text,status:o.checkStatus??"unknown",updatedAt:o.occurredAt,agent:o.agent,nativeSessionId:o.nativeSessionId});break;case"session":m={agent:o.agent,nativeSessionId:o.nativeSessionId,sessionKey:o.sessionKey,updatedAt:o.occurredAt};break}let y=Array.from(n.values()).sort((o,u)=>(o.order??Number.MAX_SAFE_INTEGER)-(u.order??Number.MAX_SAFE_INTEGER)),f=Array.from(i.values()).sort((o,u)=>(o.order??Number.MAX_SAFE_INTEGER)-(u.order??Number.MAX_SAFE_INTEGER)),C=y.find(o=>o.status==="in_progress")??y.find(o=>o.status==="blocked")??y.find(o=>o.status==="pending"),b=f.find(o=>o.status==="in_progress")??f.find(o=>o.status==="blocked")??f.find(o=>o.status==="pending"),ge=l([...D,...b?[b.title]:[],...!b&&C?[C.title]:[],...f.filter(o=>o.status==="pending").slice(0,5).map(o=>o.title)],8),ye=l([...$,...y.filter(o=>o.status==="blocked").map(o=>o.title),...f.filter(o=>o.status==="blocked").map(o=>o.title)],20),I={version:1,projectId:r.id,projectName:r.name,currentRequest:a,currentActivity:c,goal:d,plan:p,phases:y,tasks:f,decisions:l(g,20),blockers:ye,warnings:l(K,20),nextActions:ge,filesTouched:l(z,30),activeFiles:Array.from(P.entries()).filter(([,o])=>o!=="deleted").map(([o])=>o).slice(-5),modifiedFiles:l(L,30),createdFiles:l(V,30),deletedFiles:l(W,30),commands:l(F,20),tests:l(B,20),checks:mt(J,20),currentPhase:C,currentTask:b,progress:{phasesTotal:y.length,phasesCompleted:y.filter(o=>o.status==="completed").length,tasksTotal:f.length,tasksCompleted:f.filter(o=>o.status==="completed").length,blocked:y.filter(o=>o.status==="blocked").length+f.filter(o=>o.status==="blocked").length},lastSession:m,updatedAt:s.length?s[s.length-1].occurredAt:new Date().toISOString()},U=de(r.rootPath,".toolnet","work");return lt(U,{recursive:!0}),ue(de(U,"current.json"),I),await e.put(`projects/${r.id}/work/current.json`,JSON.stringify(I,null,2)+`
6
- `,"application/json"),I}async function N(r,e){if((await me(r,e)).length>0)return _(r,e);let s=await e.getText(`projects/${r.id}/work/current.json`);if(!s)return null;try{return JSON.parse(s)}catch{return null}}function gt(r,e){let t=r.indexOf(e);return t>=0?r[t+1]:void 0}function yt(r){let e=G(),t=ne(se({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new M(t,r.id,r.name,r.remote??r.name)}function fe(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 ht(){let[r="help",...e]=process.argv.slice(2),t=new j().detect(gt(e,"--project")??process.cwd()),s=yt(t);if(r==="reconcile"){let n=await _(t,s);fe(n);return}if(r==="status"){let n=await N(t,s);if(!n){console.log("No active work state yet.");return}fe(n);return}if(r==="json"){let n=await N(t,s);console.log(JSON.stringify(n,null,2));return}console.log(`Work Continuity
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
7
7
 
8
8
  Commands:
9
9
  status [--project PATH]
10
10
  json [--project PATH]
11
11
  reconcile [--project PATH]
12
- `)}ht().catch(r=>{console.error(r instanceof Error?r.message:r),process.exit(1)});
12
+ `)}Rt().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.14",
3
+ "version": "0.3.15",
4
4
  "description": "Persistent project memory, work continuity, and code intelligence for AI coding agents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,6 +54,10 @@
54
54
  "snapshot:test": "tsx src/snapshot/test-snapshot.ts",
55
55
  "code:semantic:test": "tsx src/code-intelligence/test-semantic.ts",
56
56
  "doctor": "tsx src/production/doctor.ts",
57
+ "gc": "tsx src/retention/cli.ts",
58
+ "benchmark:large-repo": "tsx src/code-intelligence/benchmark/large-repo-benchmark.ts",
59
+ "code:large-repo-hardening:test": "vitest run tests/code-intelligence/large-repo-hardening.test.ts",
60
+ "code:correctness:test": "vitest run tests/code-intelligence/code-intelligence-correctness.test.ts",
57
61
  "cli": "./bin/toolnet-memory",
58
62
  "prod:check": "npm run build && npm test && npm run doctor",
59
63
  "project:migrate": "tsx src/storage/project/migrate.ts",
@@ -94,7 +98,7 @@
94
98
  "session:new-agents:test": "vitest run tests/session/new-agent-event-mappers.test.ts tests/session/new-agent-hook-installers.test.ts tests/session/new-agent-runtime.test.ts",
95
99
  "session:new-agents-continuity:test": "vitest run tests/session/new-agent-continuity.test.ts tests/session/new-agent-continuity-installers.test.ts",
96
100
  "session:new-agents-integration:test": "vitest run tests/session/new-agent-integrations.test.ts tests/production/new-agent-auto-integrate.test.ts tests/production/new-agent-production-wiring.test.ts",
97
- "session:integration-scope:test": "vitest run tests/session/integration-scope.test.ts tests/session/hook-event-dedupe.test.ts",
101
+ "session:integration-scope:test": "vitest run tests/session/integration-scope.test.ts tests/session/hook-event-dedupe.test.ts tests/session/hook-event-dedupe-project.test.ts",
98
102
  "session:cursor-dual-scope:test": "vitest run tests/session/cursor-dual-scope.test.ts tests/session/cursor-dual-scope-wiring.test.ts",
99
103
  "session:copilot-dual-scope:test": "vitest run tests/session/copilot-dual-scope.test.ts tests/session/copilot-dual-scope-wiring.test.ts",
100
104
  "session:grok-dual-scope:test": "vitest run tests/session/grok-dual-scope.test.ts tests/session/grok-dual-scope-wiring.test.ts",
@@ -106,7 +110,9 @@
106
110
  "production:phase07-final:test": "vitest run tests/production/final-phase07-release.test.ts",
107
111
  "release:certify:10": "vitest run tests/integration/cross-agent-continuity.test.ts tests/production/ten-agent-release-contract.test.ts",
108
112
  "release:certify:final": "bash scripts/final-release-certify.sh",
109
- "release:verify:static": "node scripts/verify-release-contract.mjs"
113
+ "release:verify:static": "node scripts/verify-release-contract.mjs",
114
+ "security:isolation:test": "vitest run tests/security/security-isolation.test.ts",
115
+ "release:verify:v0315": "node scripts/verify-v0315-contract.mjs"
110
116
  },
111
117
  "dependencies": {
112
118
  "@aws-sdk/client-s3": "^3.1102.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "name": "toolnet-memory",
4
- "version": "0.3.14",
4
+ "version": "0.3.15",
5
5
  "releaseSeries": "0.3.x",
6
6
  "runtime": {
7
7
  "node": ">=22",
@@ -67,7 +67,58 @@
67
67
  "nativeCliE2ERequired": false,
68
68
  "nativeCliE2EReason": "Native host binaries are optional and may be unavailable on the certification host.",
69
69
  "fullRepositoryCertificationRequired": true,
70
- "npmPublishAutomatic": false,
71
- "gitTagAutomatic": false
70
+ "npmPublishAutomatic": true,
71
+ "gitTagAutomatic": false,
72
+ "npmPublishTrigger": "git-tag-workflow-trusted-publishing"
73
+ },
74
+ "hardening": {
75
+ "retention": {
76
+ "safeGc": true,
77
+ "defaultDryRun": true,
78
+ "realDeleteRequiresApply": true,
79
+ "protectSemanticMemory": true,
80
+ "protectWorkCurrent": true,
81
+ "protectSharedJournal": true,
82
+ "protectRemoteOperations": true,
83
+ "unflushedWalDeletion": false
84
+ },
85
+ "dedupe": {
86
+ "projectScoped": true,
87
+ "crossProcess": true,
88
+ "crossContainerSharedVolume": true,
89
+ "usesPidIdentity": false,
90
+ "atomicExclusiveClaim": true,
91
+ "ownershipToken": true,
92
+ "ttlRecovery": true,
93
+ "remoteDistributedLock": false
94
+ },
95
+ "largeRepository": {
96
+ "benchmarkProfiles": ["10k", "50k", "100k"],
97
+ "maxCodeFileBytes": 2097152,
98
+ "symlinkTraversal": false,
99
+ "boundedParseConcurrency": true,
100
+ "boundedHashConcurrency": true,
101
+ "cancellationBoundary": true,
102
+ "renameDetection": "unique-hash-1to1"
103
+ },
104
+ "codeIntelligence": {
105
+ "parserEngine": "typescript-compiler-api",
106
+ "treeSitterRuntime": false,
107
+ "supportedExtensions": [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"],
108
+ "tsconfigPaths": true,
109
+ "baseUrlResolution": true,
110
+ "projectReferences": true,
111
+ "workspacePackages": true,
112
+ "graphRepair": true,
113
+ "danglingEdgeProtection": true
114
+ },
115
+ "security": {
116
+ "secretScannerVersion": 2,
117
+ "durableSanitization": true,
118
+ "projectIsolation": true,
119
+ "projectDataTrusted": false,
120
+ "clientSideEncryption": false,
121
+ "requiresEncryptionKey": false
122
+ }
72
123
  }
73
124
  }