toolnet-memory 0.3.3 → 0.3.4

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.
@@ -1,69 +1,59 @@
1
- import*as f from"fs";import*as d from"path";function C(e){return e?Math.ceil(e.length/3.5):0}function Q(e,n){if(!e)return"";if(C(e)<=n)return e;let r=Math.floor(n*3.5),o=e.slice(0,r),s=o.lastIndexOf("."),i=o.lastIndexOf(`
2
- `),a=Math.max(s,i);return a>r*.7?o.slice(0,a+1):o}function R(e,n){if(!e)return"";let t=e.split(`
3
- `).filter(s=>s.trim());if(t.length<=n)return e;let r=t.slice(0,n),o=t.length-n;return[...r,`... (${o} more items omitted)`].join(`
4
- `)}function Z(e,n){let{maxTokens:t,trimMarker:r="[Context trimmed by ToolNet Memory token budget]"}=n;if(e.length===0)return"";let o=[...e].sort((c,u)=>u.priority-c.priority),s=[],i=0,a=!1;for(let c of o){let u=`# ${c.title}
1
+ import{existsSync as ce,readFileSync as ue}from"node:fs";import{homedir as pe}from"node:os";import{join as le}from"node:path";function de(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function fe(){let r=process.env.TOOLNET_GLOBAL_ENV??le(pe(),".config","toolnet-memory",".env");if(!ce(r))return;let e=ue(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 s=n.indexOf("=");if(s<=0)continue;let o=n.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=de(n.slice(s+1)))}}fe();function y(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 _(){return{memory:{autoCapture:y(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:y(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:y(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:y(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:y(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 me}from"node:crypto";import{existsSync as T,mkdirSync as ge,readFileSync as he,renameSync as ye,writeFileSync as Se}from"node:fs";import{basename as ke,dirname as b,join as x,parse as B,resolve as k}from"node:path";var D=".toolnet",je="project.json";function ve(r){return me("sha256").update(r).digest("hex").slice(0,16)}function E(r){return x(r,D,je)}function be(r){return T(E(r))}function we(r,e){let t=k(r),n=B(t).root;for(;;){if(be(t))return t;if(t===n||e&&t===k(e))break;let s=b(t);if(s===t)break;t=s}return null}function xe(r){let e=k(r),t=B(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(o=>T(x(e,o))))return e;if(e===t)break;let s=b(e);if(s===e)break;e=s}return k(r)}function Pe(r){let e;try{e=JSON.parse(he(r,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${r}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let 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:b(b(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 K(r,e){let t=x(r,D);ge(t,{recursive:!0});let n=E(r),s=`${n}.tmp-${process.pid}`;Se(s,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),ye(s,n)}function W(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 w=class{detect(e=process.cwd()){let t=k(e),n=xe(t),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(m=>T(x(n,m))),c=we(t,o?n:void 0);if(c){let m=E(c),i=Pe(m);return i.rootPath!==c&&(i.rootPath=c,i.updatedAt=new Date().toISOString(),K(c,i)),W(i,c)}let l=new Date().toISOString(),a=ke(n),p={version:1,id:ve(n),name:a,remote:a,rootPath:n,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return K(n,p),W(p,n)}};import{homedir as qe}from"node:os";import{join as Ye}from"node:path";import{DeleteObjectCommand as Oe,GetObjectCommand as Ie,HeadObjectCommand as Ae,ListObjectsV2Command as Te,PutObjectCommand as Ee,S3Client as Me}from"@aws-sdk/client-s3";import{getSignedUrl as Ce}from"@aws-sdk/s3-request-presigner";var P=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Me({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,n="application/octet-stream"){let s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Ee({Bucket:this.bucket,Key:e,Body:s,ContentType:n}))}async get(e){let t=await Ce(this.client,new Ie({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 Ae({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 Oe({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let s=await this.client.send(new Te({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&t.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return t}};import{access as L,mkdir as Re,readFile as Ne,readdir as $e,rm as _e,stat as z,writeFile as Ke}from"node:fs/promises";import{dirname as We,join as Be,relative as F,resolve as De}from"node:path";var j=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return De(this.root,t)}async put(e,t){let n=this.path(e);await Re(We(n),{recursive:!0}),await Ke(n,t)}async get(e){try{return await Ne(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await L(this.path(e)),!0}catch{return!1}}async delete(e){await _e(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await L(t)}catch{return n}let s=async c=>{let l=await $e(c,{withFileTypes:!0});for(let a of l){let p=Be(c,a.name);if(a.isDirectory()){await s(p);continue}let m=await z(p);n.push({key:F(this.root,p),size:m.size,updatedAt:m.mtime.toISOString()})}},o=await z(t);return o.isDirectory()?await s(t):n.push({key:F(this.root,t),size:o.size,updatedAt:o.mtime.toISOString()}),n}};import{DeleteObjectCommand as Le,GetObjectCommand as ze,HeadObjectCommand as Fe,ListObjectsV2Command as Ve,PutObjectCommand as Je,S3Client as Ue}from"@aws-sdk/client-s3";import{getSignedUrl as He}from"@aws-sdk/s3-request-presigner";var v=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Ue({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 s=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Je({Bucket:this.bucket,Key:e,Body:s,ContentType:n}))}async get(e){let t=await He(this.client,new ze({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 Fe({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 Le({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let s=await this.client.send(new Ve({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&t.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return t}};function M(r,e){return console.warn(e),new j(r)}function V(r){let e=r.localRoot??Ye(qe(),".toolnet-memory","storage");if(r.provider==="r2"){let t=r.r2;return t?.accountId&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new v({name:"r2",endpoint:`https://${t.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:t.bucket,forcePathStyle:!0,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):M(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 v({name:"s3",endpoint:t.endpoint,region:t.region??"us-east-1",bucket:t.bucket,forcePathStyle:t.forcePathStyle??!1,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):M(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 P({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):M(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new j(e)}function Ge(r){return new Promise(e=>setTimeout(e,r))}async function J(r,e={}){let t=Math.max(1,e.attempts??3),n=e.baseDelayMs??150,s=e.maxDelayMs??2e3,o;for(let c=1;c<=t;c++)try{return await r()}catch(l){if(o=l,c>=t)break;let a=Math.min(s,n*2**(c-1)),p=Math.floor(Math.random()*Math.max(1,a*.2));await Ge(a+p)}throw o}var Qe=new Set(["put","get","getText","delete","list"]);function U(r,e={}){return new Proxy(r,{get(t,n){let s=Reflect.get(t,n,t);return typeof s!="function"?s:Qe.has(n)?(...o)=>J(()=>Promise.resolve(s.apply(t,o)),e):s.bind(t)}})}function H(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 q(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 s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var O=class{constructor(e,t,n,s){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=H(s??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,s=await this.provider.getText(e);if(s){let c;try{c=JSON.parse(s)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${l instanceof Error?l.message:String(l)}`)}if(typeof c.id=="string"&&c.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${c.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof c.createdAt=="string"&&(n=c.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:t};await this.provider.put(e,JSON.stringify(o,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=q(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{createHash as Xe}from"node:crypto";import{dirname as Ze}from"node:path";import{mkdirSync as et,readFileSync as wn,renameSync as tt,writeFileSync as rt}from"node:fs";function f(r){return Xe("sha256").update(r).digest("hex")}function h(r,e){et(Ze(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;rt(t,JSON.stringify(e,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),tt(t,r)}async function C(r,e){let t=await e.getText(`projects/${r.id}/work/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}import{existsSync as Q,mkdirSync as st,readFileSync as ot,statSync as Y,writeFileSync as it}from"node:fs";import{dirname as at,join as ct}from"node:path";var G=64*1024,ut=`# ToolNet Project Operating Manual
5
6
 
6
- `,p=C(u),l=C(c.content),h=p+l;if(i+h<=t)s.push(u+c.content),i+=h;else{let g=t-i-p;if(g>50){let b=Q(c.content,g);s.push(u+b),i=t,a=!0}else a=!0;break}}return a&&s.push(`
7
- ${r}
8
- `),s.join(`
7
+ This file contains persistent instructions for AI agents working on this project.
9
8
 
10
- ---
9
+ ## Critical Rules
11
10
 
12
- `)}function T(e,n){let t=[{title:"Profile",content:R(e,10),priority:100},{title:"Current Work",content:R(n,15),priority:90}];return Z(t,{maxTokens:800})}import{chmodSync as Fe,existsSync as X,mkdirSync as $e,readFileSync as q,renameSync as Le,writeFileSync as ze}from"node:fs";import{dirname as Ve,join as _}from"node:path";var I="toolnet.context-offload.v1";function ee(e){return _(e,".toolnet","offload")}function te(e){return _(ee(e),"graph.json")}function E(){return{schema:I,version:1,updatedAt:new Date(0).toISOString(),nodes:[]}}function ne(e){let n=te(e);if(!X(n))return E();try{let t=JSON.parse(q(n,"utf8"));return t.schema!==I||t.version!==1||!Array.isArray(t.nodes)?E():t}catch{return E()}}function O(e,n){let t=e.replace(/\s+/gu," ").trim();return t.length<=n?t:t.slice(0,n-1).trimEnd()+"\u2026"}function j(e,n={}){let t=ne(e);if(t.nodes.length===0)return"";let r=Math.max(1,Math.min(12,n.maxAssets??6)),o=Math.max(320,Math.min(2400,n.maxChars??900)),s=["[TOOLNET CONTEXT OFFLOAD GRAPH]","Large tool/file payloads stay outside prompt context.","Read only a needed asset with MCP context_offload_read."];for(let i of t.nodes.slice(-r).reverse()){let a=O(i.sourceRefs.at(-1)??"unknown",72),c=i.files.length>0?` files=${O(i.files.join(","),120)}`:"",u=`event:${a} --offloads--> asset:${i.id.slice(0,12)} kind=${i.kind} bytes=${i.bytes}${c}`;if([...s,u].join(`
13
- `).length>o)break;s.push(u)}return s.join(`
14
- `)}import{existsSync as P,mkdirSync as Je,readFileSync as D,renameSync as Ue,writeFileSync as We}from"node:fs";import{dirname as Ze,join as F}from"node:path";import{createHash as re}from"node:crypto";function N(e){return re("sha256").update(e).digest("hex")}function oe(e){return F(e.rootPath,".toolnet","context","handoff.md")}function se(e){return F(e.rootPath,".toolnet","context","handoff.json")}function ie(e){let n=oe(e);if(!P(n))return null;try{let t=D(n,"utf8").trim();if(!t)return null;let r=new Date(0).toISOString(),o=se(e);if(P(o))try{let s=JSON.parse(D(o,"utf8"));typeof s.generatedAt=="string"&&(r=s.generatedAt)}catch{}return{version:1,projectId:e.id,projectName:e.name,text:t,digest:N(t),generatedAt:r}}catch{return null}}function $(e,n=1800){let t=ie(e);if(!t?.text)return null;let r=t.text;return r.length>n&&(r=`${r.slice(0,n)}
11
+ <!--
12
+ Examples:
15
13
 
16
- [Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${e.name}`,`Updated: ${t.generatedAt}`,"",r].join(`
17
- `)}var k="memory_agent_ask";function L(){return`
18
- [TOOLNET MEMORY AGENT]
14
+ - [enforce] Never edit production files directly.
15
+ - [enforce] Edit source only in /path/to/source.
16
+ - [enforce] Deploy only with ./deploy.sh --apply
17
+ -->
19
18
 
20
- Tool:
21
- - ${k}
19
+ ## Workflow
22
20
 
23
- For resume/continue requests:
21
+ <!--
22
+ Describe the correct working process.
23
+ -->
24
24
 
25
- 1. Use the injected ToolNet continuity handoff FIRST.
26
- 2. If the handoff is missing or ambiguous, invoke
27
- ${k} directly BEFORE repository/history exploration.
28
- 3. NEVER reconstruct prior work from:
29
- - .toolnet/sessions/**
30
- - state.json
31
- - events.jsonl
32
- - raw transcripts
33
- 4. NEVER search for the implementation/schema of
34
- ${k}; invoke the MCP tool directly.
35
- 5. Inspect git/source only AFTER continuity context is known.
25
+ ## Architecture
36
26
 
37
- Use:
38
- - mode="local" for current task, last file, blocker or next action.
39
- - mode="ai" for ambiguous or combined continuity questions.
27
+ <!--
28
+ Important architecture conventions.
29
+ -->
40
30
 
41
- Current repository evidence overrides stale memory.
42
- `.trim()}function ae(e){let n=d.resolve(e),t=d.parse(n).root;for(;n!==t;){let r=d.join(n,".toolnet");if(f.existsSync(r)&&f.statSync(r).isDirectory())return n;n=d.dirname(n)}return null}function z(e){try{return f.existsSync(e)?f.readFileSync(e,"utf-8").trim():null}catch{return null}}function G(e){return e.split(`
43
- `).filter(r=>{let o=r.toUpperCase();return!(o.includes("SECRET")||o.includes("TOKEN")||o.includes("API_KEY")||o.includes("APIKEY")||o.includes("PASSWORD")||o.includes("PASS="))}).join(`
44
- `)}function V(e={}){let n=e.projectPath||process.cwd(),t=ae(n);if(!t)return null;let r=d.join(t,".toolnet","project.json"),o="Unknown";try{if(f.existsSync(r)){let A=JSON.parse(f.readFileSync(r,"utf-8"));o=A.name||A.projectName||"Unknown"}}catch{}let s=d.join(t,".toolnet","profile.md"),i=d.join(t,".toolnet","current.md"),a=z(s)||"",c=z(i)||"";a=G(a),c=G(c);let u=`[TOOLNET PROJECT CONTEXT]
31
+ ## Verification
45
32
 
46
- Project: ${o}
47
- Root: ${t}
33
+ <!--
34
+ Tests / QA required after changes.
35
+ -->
48
36
 
49
- `,p=T(a,c),l=$({id:"",name:o,rootPath:t},1600),h=l?`
37
+ ## Known Gotchas
50
38
 
51
- ${l}
52
- `:"",g=j(t,{maxAssets:6,maxChars:900}),b=g?`
39
+ <!--
40
+ Things an AI agent should always remember.
41
+ -->
53
42
 
54
- ${g}
55
- `:"",W=`
43
+ ## Preferences
56
44
 
57
- ${L()}
45
+ <!--
46
+ - [advisory] Prefer small focused files.
47
+ -->
48
+ `;function I(r){return ct(r.rootPath,".toolnet","PROJECT.md")}function pt(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function lt(r){let e=[],t=new Set,n=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,s;for(;s=n.exec(r);){let o=s[1].toLowerCase(),c=pt(s[2]);if(!c)continue;let l=`${o}:${c.toLowerCase()}`;t.has(l)||(t.add(l),e.push({id:f(l).slice(0,24),mode:o,text:c,source:"manual"}))}return e}function dt(r){let e=I(r);return Q(e)||(st(at(e),{recursive:!0}),it(e,ut,{encoding:"utf8",mode:384})),e}function R(r,e=!1){let t=e?dt(r):I(r);if(!Q(t))return null;if(Y(t).size>G)throw new Error(`PROJECT.md exceeds ${G} bytes`);let s=ot(t,"utf8");return{path:t,content:s,digest:f(s),rules:lt(s),bytes:Buffer.byteLength(s,"utf8"),updatedAt:new Date(Y(t).mtimeMs).toISOString()}}async function X(r,e){let t=await e.getText(`projects/${r.id}/work/handoff-latest.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}async function Z(r,e){let t=await e.getText(`projects/${r.id}/work/semantic/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function N(r){if(!r)return 0;let e=Array.from(r).length,t=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,t*1.3))}function d(r,e){let t=r.replace(/\s+/g," ").trim();return t.length<=e?t:t.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function ft(r){let e=[],t=!1;for(let n of r.split(/\r?\n/u)){let s=n.trim();if(s.includes("<!--")&&(t=!0),t){s.includes("-->")&&(t=!1);continue}let o=s.toLowerCase();if(!(!s||s.startsWith("#")||s==="```"||o.startsWith("- [enforce]")||o.startsWith("* [enforce]")||o.startsWith("- [advisory]")||o.startsWith("* [advisory]"))&&(s=s.replace(/^[-*]\s+/u,""),s&&e.push(d(s,280)),e.length>=16))break}return e}function mt(r){let e=[],t=[];for(let n of r.split(/\\r?\\n/u)){let s=n.trim(),o=s.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(p=>o.startsWith(p));if(!l)continue;let a=s.slice(l.length).trim();a&&(l.includes("enforce")?e.push(a):t.push(a))}return{enforce:e,advisory:t}}function gt(r,e){let t=[];for(let n of r){let s=[...t,n].join(`
49
+ `);if(N(s)<=e){t.push(n);continue}let o=N(t.join(`
50
+ `)),c=Math.max(0,e-o);if(c>=16){let l=Math.floor(c*3.2),a=d(n,l);a&&t.push(a)}break}return t.join(`
51
+ `).trim()}async function ee(r){let e=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),t=R(r.project,!1),n=t?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let s=mt(n),o=t?t.rules.filter(u=>u.mode==="enforce").map(u=>u.text):s.enforce,c=t?t.rules.filter(u=>u.mode==="advisory").map(u=>u.text):s.advisory,l=n?ft(n):[],a=await C(r.project,r.storage),p=await Z(r.project,r.storage),m=await X(r.project,r.storage),i=[];if(i.push("[TOOLNET PROJECT CONTEXT]"),i.push(`Project: ${r.project.name}`),i.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&i.push(`Full operating manual: ${I(r.project)}`),o.length){i.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of o.slice(0,24))i.push(`- [ENFORCE] ${d(u,240)}`)}if(c.length){i.push("","PROJECT PREFERENCES");for(let u of c.slice(0,10))i.push(`- ${d(u,220)}`)}if(p&&(p.mission&&i.push("","MISSION",d(p.mission.value,420)),p.activeObjective&&i.push("","CURRENT OBJECTIVE",d(p.activeObjective.value,420)),p.why&&i.push("","WHY THIS WORK MATTERS",d(p.why.value,420)),p.desiredOutcome&&i.push("","DESIRED OUTCOME",d(p.desiredOutcome.value,420)),p.planRationale&&i.push("","WHY THIS APPROACH",d(p.planRationale.value,420))),a){if(i.push("","ACTIVE WORK"),a.goal&&i.push(`Goal: ${d(a.goal,320)}`),a.plan&&i.push(`Plan: ${d(a.plan,320)}`),i.push(`Progress: phases ${a.progress.phasesCompleted}/${a.progress.phasesTotal}; tasks ${a.progress.tasksCompleted}/${a.progress.tasksTotal}; blocked ${a.progress.blocked}`),a.currentPhase&&i.push(`Current phase: ${a.currentPhase.title} [${a.currentPhase.status}]`),a.currentPhase&&p){let u=p.phases.find(g=>g.order===a.currentPhase?.order);u&&(u.objective&&i.push(`Phase objective: ${d(u.objective.value,340)}`),u.why?i.push(`Why this phase: ${d(u.why.value,340)}`):i.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&i.push(`Deliverable: ${d(u.deliverable.value,340)}`),u.dependencies.length&&i.push(`Depends on: ${u.dependencies.slice(0,4).map(g=>d(g.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(i.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(g=>{i.push(`- ${d(g.value,260)}`)})),u.openQuestions.length&&(i.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(g=>{i.push(`- ${d(g.value,260)}`)})))}a.currentTask&&i.push(`Current task: ${a.currentTask.title} [${a.currentTask.status}]`),a.nextActions.length&&(i.push("","NEXT ACTIONS"),a.nextActions.slice(0,6).forEach((u,g)=>{i.push(`${g+1}. ${d(u,260)}`)})),a.blockers.length&&(i.push("","BLOCKERS"),a.blockers.slice(0,5).forEach(u=>{i.push(`- ${d(u,260)}`)})),a.warnings.length&&(i.push("","ATTENTION"),a.warnings.slice(-5).forEach(u=>{i.push(`- ${d(u,260)}`)})),a.decisions.length&&(i.push("","RECENT DECISIONS"),a.decisions.slice(-5).forEach(u=>{i.push(`- ${d(u,260)}`)})),a.lastSession&&i.push("",`Last work session: ${a.lastSession.agent} / ${a.lastSession.nativeSessionId}`)}if(p&&p.openQuestions.length&&(i.push("","UNRESOLVED QUESTIONS"),p.openQuestions.slice(0,5).forEach(u=>{i.push(`- ${d(u.value,260)}`)})),m&&i.push(`Latest handoff: ${m.reason} / ${m.sourceSession.agent}`),l.length){i.push("","OPERATING NOTES");for(let u of l)i.push(`- ${u}`)}i.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let $=gt(i,e);return{version:1,projectId:r.project.id,projectName:r.project.name,text:$,estimatedTokens:N($),maxTokens:e,hasManual:!!n,hasWorkState:!!a,hasHandoff:!!m,generatedAt:new Date().toISOString()}}import{existsSync as ht,mkdirSync as yt,readFileSync as St,writeFileSync as kt}from"node:fs";import{dirname as jt,join as re}from"node:path";function A(r){return r?Math.ceil(r.length/3.5):0}function te(r,e){if(!r)return"";if(A(r)<=e)return r;let n=Math.floor(e*3.5),s=r.slice(0,n),o=s.lastIndexOf("."),c=s.lastIndexOf(`
52
+ `),l=Math.max(o,c);return l>n*.7?s.slice(0,l+1):s}function ne(r){return re(r.rootPath,".toolnet","context","startup.md")}function vt(r){return re(r.rootPath,".toolnet","context","startup.json")}function se(r,e){let t=ne(r);yt(jt(t),{recursive:!0}),kt(t,e.text.endsWith(`
53
+ `)?e.text:e.text+`
54
+ `,{encoding:"utf8",mode:384}),h(vt(r),e)}async function bt(r,e,t=800){let s=(await ee({project:r,storage:e,maxTokens:t})).text;A(s)>t&&(s=te(s,t),s+=`
58
55
 
59
- Forbidden At Startup:
60
- - Do not run session:agy-recover, handoff:latest, or brief automatically.
61
- - Do not perform deep recovery merely because an agent starts.
62
- - If the user asks to resume previous work and fast context is insufficient,
63
- use memory_agent_ask before guessing.
64
- `;return u+p+h+b+W}import{existsSync as ve}from"node:fs";import{dirname as Me,join as Re,parse as Te,resolve as Oe}from"node:path";import{existsSync as ce,readFileSync as ue}from"node:fs";import{homedir as de}from"node:os";import{join as le}from"node:path";function fe(e){let n=e.trim();return n.length>=2&&n.startsWith('"')&&n.endsWith('"')?(n=n.slice(1,-1),n.replace(/\\n/g,`
65
- `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):n.length>=2&&n.startsWith("'")&&n.endsWith("'")?n.slice(1,-1):n}function pe(){let e=process.env.TOOLNET_GLOBAL_ENV??le(de(),".config","toolnet-memory",".env");if(!ce(e))return;let n=ue(e,"utf8");for(let t of n.split(/\r?\n/)){let r=t.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=fe(r.slice(o+1)))}}pe();import{createHash as me}from"node:crypto";import{existsSync as v,mkdirSync as ge,readFileSync as ye,renameSync as he,writeFileSync as Se}from"node:fs";import{basename as xe,dirname as S,join as w,parse as K,resolve as y}from"node:path";var Y=".toolnet",we="project.json";function be(e){return me("sha256").update(e).digest("hex").slice(0,16)}function M(e){return w(e,Y,we)}function Ae(e){return v(M(e))}function Ce(e,n){let t=y(e),r=K(t).root;for(;;){if(Ae(t))return t;if(t===r||n&&t===y(n))break;let o=S(t);if(o===t)break;t=o}return null}function Ee(e){let n=y(e),t=K(n).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(s=>v(w(n,s))))return n;if(n===t)break;let o=S(n);if(o===n)break;n=o}return y(e)}function ke(e){let n;try{n=JSON.parse(ye(e,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${e}: ${o instanceof Error?o.message:String(o)}`)}if(!n||typeof n!="object")throw new Error(`Invalid ToolNet project manifest: ${e}`);let t=n;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${e}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${e}`);let r=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:S(S(e)),createdAt:typeof t.createdAt=="string"?t.createdAt:r,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:r,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function H(e,n){let t=w(e,Y);ge(t,{recursive:!0});let r=M(e),o=`${r}.tmp-${process.pid}`;Se(o,JSON.stringify(n,null,2)+`
66
- `,{encoding:"utf8",mode:384}),he(o,r)}function B(e,n){return{id:e.id,name:e.name,remote:e.remote,rootPath:n,createdAt:e.createdAt,updatedAt:e.updatedAt,graphVersion:e.graphVersion,memoryVersion:e.memoryVersion,metadata:e.metadata}}var x=class{detect(n=process.cwd()){let t=y(n),r=Ee(t),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>v(w(r,p))),i=Ce(t,s?r:void 0);if(i){let p=M(i),l=ke(p);return l.rootPath!==i&&(l.rootPath=i,l.updatedAt=new Date().toISOString(),H(i,l)),B(l,i)}let a=new Date().toISOString(),c=xe(r),u={version:1,id:be(r),name:c,remote:c,rootPath:r,createdAt:a,updatedAt:a,graphVersion:0,memoryVersion:0};return H(r,u),B(u,r)}};function J(e){let n=Oe(e),t=Te(n).root;for(;;){if(ve(Re(n,".toolnet","project.json")))return new x().detect(n);if(n===t)break;let r=Me(n);if(r===n)break;n=r}return null}var U=3200;async function _e(){let e="";for await(let n of process.stdin)e+=n.toString();if(!e.trim())return{};try{return JSON.parse(e)}catch{return{}}}function m(){process.stdout.write("{}")}function Ie(e){return e.length<=U?e:`${e.slice(0,U)}
67
-
68
- [ToolNet startup context truncated]`}function je(e,n,t){if(process.env.TOOLNET_CODEX_STARTUP_DEBUG!=="1")return;let r=Date.now()-e;process.stderr.write(`[toolnet-memory] codex SessionStart ${r}ms cwd=${n} chars=${t}
69
- `)}async function Ne(){let e=Date.now(),n=await _e();if(n.hook_event_name!=="SessionStart"){m();return}let t=typeof n.cwd=="string"?n.cwd:"";if(!t){m();return}if(!J(t)){m();return}try{let o=V({projectPath:t});if(!o?.trim()){m();return}let s=Ie(o),i={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:s}};je(e,t,s.length),process.stdout.write(JSON.stringify(i))}catch{m()}}Ne().catch(()=>{m(),process.exitCode=0});
56
+ [Context trimmed by ToolNet Memory token budget]
57
+ `);let c={version:1,projectId:r.id,projectName:r.name,text:s,digest:f(s),estimatedTokens:A(s),generatedAt:new Date().toISOString()};return se(r,c),await e.put(`projects/${r.id}/context/startup.md`,c.text+`
58
+ `,"text/markdown"),await e.put(`projects/${r.id}/context/startup.json`,JSON.stringify(c,null,2)+`
59
+ `,"application/json"),c}async function oe(r,e,t=900){try{let s=await e.getText(`projects/${r.id}/context/startup.md`);if(s&&s.trim()){let o=s.trim(),c={version:1,projectId:r.id,projectName:r.name,text:o,digest:f(o),estimatedTokens:Math.ceil(o.length/3.5),generatedAt:new Date().toISOString()};return se(r,c),c}}catch{}let n=ne(r);if(ht(n))try{let s=St(n,"utf8").trim();if(s)return{version:1,projectId:r.id,projectName:r.name,text:s,digest:f(s),estimatedTokens:Math.ceil(s.length/3.5),generatedAt:new Date().toISOString()}}catch{}try{return await bt(r,e,t)}catch{return null}}async function ie(r){let e=await oe(r.project,r.storage,900);return e?.text?{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.text}}:{}}import{existsSync as wt}from"node:fs";import{dirname as xt,join as Pt,parse as Ot,resolve as It}from"node:path";function ae(r){let e=It(r),t=Ot(e).root;for(;;){if(wt(Pt(e,".toolnet","project.json")))return new w().detect(e);if(e===t)break;let n=xt(e);if(n===e)break;e=n}return null}async function At(){let r="";for await(let e of process.stdin)r+=e.toString();if(!r.trim())return{};try{return JSON.parse(r)}catch{return{}}}async function Tt(){let r=await At();if(r.hook_event_name!=="SessionStart"){process.stdout.write("{}");return}let e=typeof r.cwd=="string"?r.cwd:"";if(!e){process.stdout.write("{}");return}let t=ae(e);if(!t){process.stdout.write("{}");return}try{let n=_(),s=U(V({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:2}),o=new O(s,t.id,t.name,t.remote??t.name),c=await ie({project:t,storage:o});process.stdout.write(JSON.stringify(c))}catch{process.stdout.write("{}")}}Tt().catch(()=>{process.stdout.write("{}"),process.exitCode=0});