toolnet-memory 0.2.13 → 0.2.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.
@@ -1,26 +1,26 @@
1
- import{existsSync as Qt,readFileSync as Xt}from"node:fs";import{homedir as Zt}from"node:os";import{join as en}from"node:path";function tn(t){let e=t.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 nn(){let t=process.env.TOOLNET_GLOBAL_ENV??en(Zt(),".config","toolnet-memory",".env");if(!Qt(t))return;let e=Xt(t,"utf8");for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let s=r.indexOf("=");if(s<=0)continue;let o=r.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=tn(r.slice(s+1)))}}nn();function B(t,e){return t===void 0?e:["1","true","yes","on"].includes(t.toLowerCase())}function z(t,e){if(!t)return e;let n=Number(t);return Number.isFinite(n)?n:e}function H(){return{memory:{autoCapture:B(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:B(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:B(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:B(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:z(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:z(process.env.MEMORY_RERANK_TOP,10),finalContext:z(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:z(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:B(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:z(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as rn}from"node:crypto";import{existsSync as we,mkdirSync as sn,readFileSync as on,renameSync as an,writeFileSync as cn}from"node:fs";import{basename as un,dirname as Y,join as Q,parse as ze,resolve as q}from"node:path";var qe=".toolnet",dn="project.json";function ln(t){return rn("sha256").update(t).digest("hex").slice(0,16)}function je(t){return Q(t,qe,dn)}function pn(t){return we(je(t))}function fn(t,e){let n=q(t),r=ze(n).root;for(;;){if(pn(n))return n;if(n===r||e&&n===q(e))break;let s=Y(n);if(s===n)break;n=s}return null}function mn(t){let e=q(t),n=ze(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(o=>we(Q(e,o))))return e;if(e===n)break;let s=Y(e);if(s===e)break;e=s}return q(t)}function gn(t){let e;try{e=JSON.parse(on(t,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${t}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${t}`);let n=e;if(typeof n.id!="string"||!n.id.trim())throw new Error(`ToolNet project manifest is missing id: ${t}`);if(typeof n.name!="string"||!n.name.trim())throw new Error(`ToolNet project manifest is missing name: ${t}`);let r=new Date().toISOString();return{version:1,id:n.id,name:n.name,remote:typeof n.remote=="string"&&n.remote.trim()?n.remote:n.name,rootPath:typeof n.rootPath=="string"?n.rootPath:Y(Y(t)),createdAt:typeof n.createdAt=="string"?n.createdAt:r,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:r,graphVersion:typeof n.graphVersion=="number"?n.graphVersion:0,memoryVersion:typeof n.memoryVersion=="number"?n.memoryVersion:0,metadata:n.metadata&&typeof n.metadata=="object"?n.metadata:void 0}}function We(t,e){let n=Q(t,qe);sn(n,{recursive:!0});let r=je(t),s=`${r}.tmp-${process.pid}`;cn(s,JSON.stringify(e,null,2)+`
3
- `,{encoding:"utf8",mode:384}),an(s,r)}function Be(t,e){return{id:t.id,name:t.name,remote:t.remote,rootPath:e,createdAt:t.createdAt,updatedAt:t.updatedAt,graphVersion:t.graphVersion,memoryVersion:t.memoryVersion,metadata:t.metadata}}var G=class{detect(e=process.cwd()){let n=q(e),r=mn(n),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>we(Q(r,p))),i=fn(n,o?r:void 0);if(i){let p=je(i),u=gn(p);return u.rootPath!==i&&(u.rootPath=i,u.updatedAt=new Date().toISOString(),We(i,u)),Be(u,i)}let d=new Date().toISOString(),c=un(r),a={version:1,id:ln(r),name:c,remote:c,rootPath:r,createdAt:d,updatedAt:d,graphVersion:0,memoryVersion:0};return We(r,a),Be(a,r)}};var hn=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_\-]{20,}\b/g},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._\-]{16,}\b/gi},{type:"jwt",regex:/\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b/g},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g},{type:"password_assignment",regex:/\b(password|passwd|pwd)\s*[:=]\s*["']?[^"'\s]{6,}["']?/gi},{type:"secret_assignment",regex:/\b(secret|api[_-]?key|access[_-]?token|refresh[_-]?token)\s*[:=]\s*["']?[^"'\s]{8,}["']?/gi},{type:"cookie",regex:/\b(cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi}],X=class{scan(e){let n=[];for(let r of hn){let s=new RegExp(r.regex.source,r.regex.flags);for(let o of e.matchAll(s))n.push({type:r.type,value:o[0]})}return n}hasSecrets(e){return this.scan(e).length>0}};var J=class{scanner=new X;sanitize(e){let n=e,r=this.scanner.scan(e),s=new Set;for(let o of r)s.add(o.type),n=n.split(o.value).join(`[REDACTED:${o.type}]`);return{text:n,redacted:r.length,secretTypes:[...s]}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(n=>this.sanitizeValue(n));if(e&&typeof e=="object"){let n={};for(let[r,s]of Object.entries(e)){let o=r.toLowerCase();o.includes("password")||o.includes("secret")||o.includes("token")||o.includes("cookie")||o.includes("authorization")?n[r]="[REDACTED]":n[r]=this.sanitizeValue(s)}return n}return e}};import{homedir as Fn}from"node:os";import{join as Kn}from"node:path";import{DeleteObjectCommand as yn,GetObjectCommand as Sn,HeadObjectCommand as vn,ListObjectsV2Command as bn,PutObjectCommand as kn,S3Client as wn}from"@aws-sdk/client-s3";import{getSignedUrl as jn}from"@aws-sdk/s3-request-presigner";var Z=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new wn({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,n,r="application/octet-stream"){let s=typeof n=="string"?Buffer.from(n,"utf8"):n;await this.client.send(new kn({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let n=await jn(this.client,new Sn({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(n,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let n=await this.get(e);return n?Buffer.from(n).toString("utf8"):null}async exists(e){try{return await this.client.send(new vn({Bucket:this.bucket,Key:e})),!0}catch(n){if(typeof n=="object"&&n!==null&&"$metadata"in n&&n.$metadata?.httpStatusCode===404)return!1;throw n}}async delete(e){await this.client.send(new yn({Bucket:this.bucket,Key:e}))}async list(e=""){let n=[],r;do{let s=await this.client.send(new bn({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&n.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return n}};import{access as Je,mkdir as xn,readFile as In,readdir as En,rm as On,stat as Ve,writeFile as Pn}from"node:fs/promises";import{dirname as Cn,join as Tn,relative as Ue,resolve as Rn}from"node:path";var V=class{constructor(e){this.root=e}root;name="local";path(e){let n=e.replace(/^\/+/,"");return Rn(this.root,n)}async put(e,n){let r=this.path(e);await xn(Cn(r),{recursive:!0}),await Pn(r,n)}async get(e){try{return await In(this.path(e))}catch(n){if(typeof n=="object"&&n!==null&&"code"in n&&n.code==="ENOENT")return null;throw n}}async getText(e){let n=await this.get(e);return n?Buffer.from(n).toString("utf8"):null}async exists(e){try{return await Je(this.path(e)),!0}catch{return!1}}async delete(e){await On(this.path(e),{force:!0})}async list(e=""){let n=this.path(e),r=[];try{await Je(n)}catch{return r}let s=async i=>{let d=await En(i,{withFileTypes:!0});for(let c of d){let a=Tn(i,c.name);if(c.isDirectory()){await s(a);continue}let p=await Ve(a);r.push({key:Ue(this.root,a),size:p.size,updatedAt:p.mtime.toISOString()})}},o=await Ve(n);return o.isDirectory()?await s(n):r.push({key:Ue(this.root,n),size:o.size,updatedAt:o.mtime.toISOString()}),r}};import{DeleteObjectCommand as Mn,GetObjectCommand as An,HeadObjectCommand as Nn,ListObjectsV2Command as _n,PutObjectCommand as $n,S3Client as Ln}from"@aws-sdk/client-s3";import{getSignedUrl as Dn}from"@aws-sdk/s3-request-presigner";var U=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Ln({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,n,r="application/octet-stream"){let s=typeof n=="string"?Buffer.from(n,"utf8"):n;await this.client.send(new $n({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let n=await Dn(this.client,new An({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(n,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let n=await this.get(e);return n?Buffer.from(n).toString("utf8"):null}async exists(e){try{return await this.client.send(new Nn({Bucket:this.bucket,Key:e})),!0}catch(n){if(typeof n=="object"&&n!==null&&"$metadata"in n&&n.$metadata?.httpStatusCode===404)return!1;throw n}}async delete(e){await this.client.send(new Mn({Bucket:this.bucket,Key:e}))}async list(e=""){let n=[],r;do{let s=await this.client.send(new _n({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&n.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return n}};function xe(t,e){return console.warn(e),new V(t)}function He(t){let e=t.localRoot??Kn(Fn(),".toolnet-memory","storage");if(t.provider==="r2"){let n=t.r2;return n?.accountId&&n.bucket&&n.accessKeyId&&n.secretAccessKey?new U({name:"r2",endpoint:`https://${n.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:n.bucket,forcePathStyle:!0,accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey}):xe(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(t.provider==="s3"){let n=t.s3;return n?.bucket&&n.accessKeyId&&n.secretAccessKey?new U({name:"s3",endpoint:n.endpoint,region:n.region??"us-east-1",bucket:n.bucket,forcePathStyle:n.forcePathStyle??!1,accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey}):xe(e,"[storage] S3 credentials missing. Using local fallback.")}if(t.provider==="huggingface"){let n=t.huggingface;return n?.namespace&&n.bucket&&n.accessKeyId&&n.secretAccessKey?new Z({namespace:n.namespace,bucket:n.bucket,accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey}):xe(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new V(e)}function Wn(t){return new Promise(e=>setTimeout(e,t))}async function Ye(t,e={}){let n=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,s=e.maxDelayMs??2e3,o;for(let i=1;i<=n;i++)try{return await t()}catch(d){if(o=d,i>=n)break;let c=Math.min(s,r*2**(i-1)),a=Math.floor(Math.random()*Math.max(1,c*.2));await Wn(c+a)}throw o}var Bn=new Set(["put","get","getText","delete","list"]);function Ge(t,e={}){return new Proxy(t,{get(n,r){let s=Reflect.get(n,r,n);return typeof s!="function"?s:Bn.has(r)?(...o)=>Ye(()=>Promise.resolve(s.apply(n,o)),e):s.bind(n)}})}function Qe(t){let e=t.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 Xe(t){let e=t.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 n=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let 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 ee=class{constructor(e,n,r,s){this.provider=e;this.name=e.name,this.projectId=n,this.projectName=r,this.folder=Qe(s??r),this.sourcePrefix=`projects/${n}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,n=new Date().toISOString(),r=n,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}catch(d){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${d instanceof Error?d.message:String(d)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:n};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=Xe(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,n,r){return await this.ensureRegistered(),this.provider.put(this.key(e),n,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{existsSync as bs}from"node:fs";import{homedir as ks}from"node:os";import{isAbsolute as ws,join as js,relative as xs,resolve as _t}from"node:path";import{DatabaseSync as Is}from"node:sqlite";import{join as Yn}from"node:path";import{createHash as zn}from"node:crypto";import{dirname as qn}from"node:path";import{mkdirSync as Jn,readFileSync as Vn,renameSync as Un,writeFileSync as Hn}from"node:fs";function v(t){return zn("sha256").update(t).digest("hex")}function Ie(t){if(Array.isArray(t))return t.map(Ie);if(t&&typeof t=="object"){let e=t,n={};for(let r of Object.keys(e).sort())n[r]=Ie(e[r]);return n}return t}function Ze(t){return JSON.stringify(Ie(t))}function et(t){try{return JSON.parse(Vn(t,"utf8"))}catch{return null}}function I(t,e){Jn(qn(t),{recursive:!0});let n=`${t}.${process.pid}.tmp`;Hn(n,JSON.stringify(e,null,2)+`
5
- `,{encoding:"utf8",mode:384}),Un(n,t)}function tt(t,e){let n=t.trim(),r=n.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),s=v(n).slice(0,12);if(!r||r==="."||r==="..")return`${e}--${s}`;let o=r.slice(0,100);return o===n&&n.length<=100?o:`${o.slice(0,85)}--${s}`}function nt(t,e,n){let r=e.trim(),s=n.trim();if(!r)throw new Error("Session agent is required");if(!s)throw new Error("Native session ID is required");let o=tt(r.toLowerCase(),"agent"),i=tt(s,"session");return{projectId:t.id,projectName:t.name,projectRoot:t.rootPath,agent:r,nativeSessionId:s,sessionKey:`${r}:${s}`,remotePrefix:["projects",t.id,"sessions",o,i].join("/"),localDirectory:Yn(t.rootPath,".toolnet","sessions",o,i)}}function rt(t){return String(t).padStart(12,"0")}var te=class{constructor(e,n=100,r=512*1024){this.storage=e;this.maxEventsPerChunk=n;this.maxChunkBytes=r;if(n<1)throw new Error("maxEventsPerChunk must be positive");if(r<1024)throw new Error("maxChunkBytes is too small")}storage;maxEventsPerChunk;maxChunkBytes;async getJson(e){let n=await this.storage.getText(e);return n?JSON.parse(n):null}async putJson(e,n){await this.storage.put(e,JSON.stringify(n,null,2)+`
6
- `,"application/json")}async scan(e){let n=`${e.remotePrefix}/events/`,r=await this.storage.list(n),s=[],o=0;for(let i of r){let d=i.key.match(/\/events\/(\d+)-(\d+)-[a-f0-9]+\.jsonl$/);if(!d)continue;let c=Number(d[1]),a=Number(d[2]);!Number.isFinite(c)||!Number.isFinite(a)||(s.push({key:i.key,start:c,end:a}),o=Math.max(o,a))}return s.sort((i,d)=>i.start-d.start),{chunks:s,maxSequence:o}}split(e){let n=[],r=[],s=0;for(let o of e){let i=Buffer.byteLength(JSON.stringify(o)+`
7
- `,"utf8");r.length>0&&(r.length>=this.maxEventsPerChunk||s+i>this.maxChunkBytes)&&(n.push(r),r=[],s=0),r.push(o),s+=i}return r.length>0&&n.push(r),n}async loadManifest(e){return this.getJson(`${e.remotePrefix}/session.json`)}async loadCursor(e){return this.getJson(`${e.remotePrefix}/cursor.json`)}async recover(e){let n=await this.scan(e);return{maxSequence:n.maxSequence,chunkCount:n.chunks.length}}async append(e,n,r,s={}){let o=await this.loadManifest(e),i=await this.scan(e),d=n.filter(g=>g.sequence>i.maxSequence),c=0;for(let g of this.split(d)){let h=g[0],w=g[g.length-1],C=g.map(A=>JSON.stringify(A)).join(`
1
+ import{existsSync as Qt,readFileSync as Zt}from"node:fs";import{homedir as en}from"node:os";import{join as tn}from"node:path";function nn(t){let e=t.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 rn(){let t=process.env.TOOLNET_GLOBAL_ENV??tn(en(),".config","toolnet-memory",".env");if(!Qt(t))return;let e=Zt(t,"utf8");for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let s=r.indexOf("=");if(s<=0)continue;let o=r.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=nn(r.slice(s+1)))}}rn();function B(t,e){return t===void 0?e:["1","true","yes","on"].includes(t.toLowerCase())}function z(t,e){if(!t)return e;let n=Number(t);return Number.isFinite(n)?n:e}function H(){return{memory:{autoCapture:B(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:B(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:B(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:B(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:z(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:z(process.env.MEMORY_RERANK_TOP,10),finalContext:z(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:z(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:B(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:z(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as sn}from"node:crypto";import{existsSync as we,mkdirSync as on,readFileSync as an,renameSync as cn,writeFileSync as un}from"node:fs";import{basename as ln,dirname as Y,join as X,parse as ze,resolve as q}from"node:path";var qe=".toolnet",dn="project.json";function pn(t){return sn("sha256").update(t).digest("hex").slice(0,16)}function je(t){return X(t,qe,dn)}function fn(t){return we(je(t))}function mn(t,e){let n=q(t),r=ze(n).root;for(;;){if(fn(n))return n;if(n===r||e&&n===q(e))break;let s=Y(n);if(s===n)break;n=s}return null}function gn(t){let e=q(t),n=ze(e).root,r=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(r.some(o=>we(X(e,o))))return e;if(e===n)break;let s=Y(e);if(s===e)break;e=s}return q(t)}function yn(t){let e;try{e=JSON.parse(an(t,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${t}: ${s instanceof Error?s.message:String(s)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${t}`);let n=e;if(typeof n.id!="string"||!n.id.trim())throw new Error(`ToolNet project manifest is missing id: ${t}`);if(typeof n.name!="string"||!n.name.trim())throw new Error(`ToolNet project manifest is missing name: ${t}`);let r=new Date().toISOString();return{version:1,id:n.id,name:n.name,remote:typeof n.remote=="string"&&n.remote.trim()?n.remote:n.name,rootPath:typeof n.rootPath=="string"?n.rootPath:Y(Y(t)),createdAt:typeof n.createdAt=="string"?n.createdAt:r,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:r,graphVersion:typeof n.graphVersion=="number"?n.graphVersion:0,memoryVersion:typeof n.memoryVersion=="number"?n.memoryVersion:0,metadata:n.metadata&&typeof n.metadata=="object"?n.metadata:void 0}}function We(t,e){let n=X(t,qe);on(n,{recursive:!0});let r=je(t),s=`${r}.tmp-${process.pid}`;un(s,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),cn(s,r)}function Be(t,e){return{id:t.id,name:t.name,remote:t.remote,rootPath:e,createdAt:t.createdAt,updatedAt:t.updatedAt,graphVersion:t.graphVersion,memoryVersion:t.memoryVersion,metadata:t.metadata}}var G=class{detect(e=process.cwd()){let n=q(e),r=gn(n),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(p=>we(X(r,p))),i=mn(n,o?r:void 0);if(i){let p=je(i),u=yn(p);return u.rootPath!==i&&(u.rootPath=i,u.updatedAt=new Date().toISOString(),We(i,u)),Be(u,i)}let l=new Date().toISOString(),c=ln(r),a={version:1,id:pn(r),name:c,remote:c,rootPath:r,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return We(r,a),Be(a,r)}};var hn=[{type:"openai_key",regex:/\bsk-[A-Za-z0-9_\-]{20,}\b/g},{type:"huggingface_token",regex:/\bhf_[A-Za-z0-9]{20,}\b/g},{type:"hf_s3_access_key",regex:/\bHFAK[A-Za-z0-9]{8,}\b/g},{type:"bearer_token",regex:/\bBearer\s+[A-Za-z0-9._\-]{16,}\b/gi},{type:"jwt",regex:/\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b/g},{type:"private_key",regex:/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g},{type:"password_assignment",regex:/\b(password|passwd|pwd)\s*[:=]\s*["']?[^"'\s]{6,}["']?/gi},{type:"secret_assignment",regex:/\b(secret|api[_-]?key|access[_-]?token|refresh[_-]?token)\s*[:=]\s*["']?[^"'\s]{8,}["']?/gi},{type:"cookie",regex:/\b(cookie|set-cookie)\s*[:=]\s*[^;\n]{8,}/gi}],Q=class{scan(e){let n=[];for(let r of hn){let s=new RegExp(r.regex.source,r.regex.flags);for(let o of e.matchAll(s))n.push({type:r.type,value:o[0]})}return n}hasSecrets(e){return this.scan(e).length>0}};var J=class{scanner=new Q;sanitize(e){let n=e,r=this.scanner.scan(e),s=new Set;for(let o of r)s.add(o.type),n=n.split(o.value).join(`[REDACTED:${o.type}]`);return{text:n,redacted:r.length,secretTypes:[...s]}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(n=>this.sanitizeValue(n));if(e&&typeof e=="object"){let n={};for(let[r,s]of Object.entries(e)){let o=r.toLowerCase();o.includes("password")||o.includes("secret")||o.includes("token")||o.includes("cookie")||o.includes("authorization")?n[r]="[REDACTED]":n[r]=this.sanitizeValue(s)}return n}return e}};import{homedir as Kn}from"node:os";import{join as Wn}from"node:path";import{DeleteObjectCommand as Sn,GetObjectCommand as vn,HeadObjectCommand as bn,ListObjectsV2Command as kn,PutObjectCommand as wn,S3Client as jn}from"@aws-sdk/client-s3";import{getSignedUrl as xn}from"@aws-sdk/s3-request-presigner";var Z=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new jn({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,n,r="application/octet-stream"){let s=typeof n=="string"?Buffer.from(n,"utf8"):n;await this.client.send(new wn({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let n=await xn(this.client,new vn({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(n,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`HF download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let n=await this.get(e);return n?Buffer.from(n).toString("utf8"):null}async exists(e){try{return await this.client.send(new bn({Bucket:this.bucket,Key:e})),!0}catch(n){if(typeof n=="object"&&n!==null&&"$metadata"in n&&n.$metadata?.httpStatusCode===404)return!1;throw n}}async delete(e){await this.client.send(new Sn({Bucket:this.bucket,Key:e}))}async list(e=""){let n=[],r;do{let s=await this.client.send(new kn({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&n.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return n}};import{access as Je,mkdir as In,readFile as En,readdir as On,rm as Pn,stat as Ve,writeFile as Cn}from"node:fs/promises";import{dirname as Tn,join as Rn,relative as Ue,resolve as Mn}from"node:path";var V=class{constructor(e){this.root=e}root;name="local";path(e){let n=e.replace(/^\/+/,"");return Mn(this.root,n)}async put(e,n){let r=this.path(e);await In(Tn(r),{recursive:!0}),await Cn(r,n)}async get(e){try{return await En(this.path(e))}catch(n){if(typeof n=="object"&&n!==null&&"code"in n&&n.code==="ENOENT")return null;throw n}}async getText(e){let n=await this.get(e);return n?Buffer.from(n).toString("utf8"):null}async exists(e){try{return await Je(this.path(e)),!0}catch{return!1}}async delete(e){await Pn(this.path(e),{force:!0})}async list(e=""){let n=this.path(e),r=[];try{await Je(n)}catch{return r}let s=async i=>{let l=await On(i,{withFileTypes:!0});for(let c of l){let a=Rn(i,c.name);if(c.isDirectory()){await s(a);continue}let p=await Ve(a);r.push({key:Ue(this.root,a),size:p.size,updatedAt:p.mtime.toISOString()})}},o=await Ve(n);return o.isDirectory()?await s(n):r.push({key:Ue(this.root,n),size:o.size,updatedAt:o.mtime.toISOString()}),r}};import{DeleteObjectCommand as An,GetObjectCommand as Nn,HeadObjectCommand as _n,ListObjectsV2Command as $n,PutObjectCommand as Ln,S3Client as Dn}from"@aws-sdk/client-s3";import{getSignedUrl as Fn}from"@aws-sdk/s3-request-presigner";var U=class{name;client;bucket;constructor(e){this.name=e.name??"s3",this.bucket=e.bucket,this.client=new Dn({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,n,r="application/octet-stream"){let s=typeof n=="string"?Buffer.from(n,"utf8"):n;await this.client.send(new Ln({Bucket:this.bucket,Key:e,Body:s,ContentType:r}))}async get(e){let n=await Fn(this.client,new Nn({Bucket:this.bucket,Key:e}),{expiresIn:60}),r=await fetch(n,{redirect:"follow"});if(r.status===404)return null;if(!r.ok)throw new Error(`${this.name} download failed: ${r.status} ${r.statusText}`);return new Uint8Array(await r.arrayBuffer())}async getText(e){let n=await this.get(e);return n?Buffer.from(n).toString("utf8"):null}async exists(e){try{return await this.client.send(new _n({Bucket:this.bucket,Key:e})),!0}catch(n){if(typeof n=="object"&&n!==null&&"$metadata"in n&&n.$metadata?.httpStatusCode===404)return!1;throw n}}async delete(e){await this.client.send(new An({Bucket:this.bucket,Key:e}))}async list(e=""){let n=[],r;do{let s=await this.client.send(new $n({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:r}));for(let o of s.Contents??[])o.Key&&n.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});r=s.IsTruncated?s.NextContinuationToken:void 0}while(r);return n}};function xe(t,e){return console.warn(e),new V(t)}function He(t){let e=t.localRoot??Wn(Kn(),".toolnet-memory","storage");if(t.provider==="r2"){let n=t.r2;return n?.accountId&&n.bucket&&n.accessKeyId&&n.secretAccessKey?new U({name:"r2",endpoint:`https://${n.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:n.bucket,forcePathStyle:!0,accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey}):xe(e,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(t.provider==="s3"){let n=t.s3;return n?.bucket&&n.accessKeyId&&n.secretAccessKey?new U({name:"s3",endpoint:n.endpoint,region:n.region??"us-east-1",bucket:n.bucket,forcePathStyle:n.forcePathStyle??!1,accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey}):xe(e,"[storage] S3 credentials missing. Using local fallback.")}if(t.provider==="huggingface"){let n=t.huggingface;return n?.namespace&&n.bucket&&n.accessKeyId&&n.secretAccessKey?new Z({namespace:n.namespace,bucket:n.bucket,accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey}):xe(e,"[storage] Hugging Face credentials missing. Using local fallback.")}return new V(e)}function Bn(t){return new Promise(e=>setTimeout(e,t))}async function Ye(t,e={}){let n=Math.max(1,e.attempts??3),r=e.baseDelayMs??150,s=e.maxDelayMs??2e3,o;for(let i=1;i<=n;i++)try{return await t()}catch(l){if(o=l,i>=n)break;let c=Math.min(s,r*2**(i-1)),a=Math.floor(Math.random()*Math.max(1,c*.2));await Bn(c+a)}throw o}var zn=new Set(["put","get","getText","delete","list"]);function Ge(t,e={}){return new Proxy(t,{get(n,r){let s=Reflect.get(n,r,n);return typeof s!="function"?s:zn.has(r)?(...o)=>Ye(()=>Promise.resolve(s.apply(n,o)),e):s.bind(n)}})}function Xe(t){let e=t.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 Qe(t){let e=t.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 n=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let 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 ee=class{constructor(e,n,r,s){this.provider=e;this.name=e.name,this.projectId=n,this.projectName=r,this.folder=Xe(s??r),this.sourcePrefix=`projects/${n}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,n=new Date().toISOString(),r=n,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${l instanceof Error?l.message:String(l)}`)}if(typeof i.id=="string"&&i.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${i.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof i.createdAt=="string"&&(r=i.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:r,updatedAt:n};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=Qe(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,n,r){return await this.ensureRegistered(),this.provider.put(this.key(e),n,r)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{existsSync as ks}from"node:fs";import{execFileSync as ws}from"node:child_process";import{homedir as js}from"node:os";import{isAbsolute as xs,join as _t,relative as Is,resolve as $t}from"node:path";import{DatabaseSync as Es}from"node:sqlite";import{join as Gn}from"node:path";import{createHash as qn}from"node:crypto";import{dirname as Jn}from"node:path";import{mkdirSync as Vn,readFileSync as Un,renameSync as Hn,writeFileSync as Yn}from"node:fs";function v(t){return qn("sha256").update(t).digest("hex")}function Ie(t){if(Array.isArray(t))return t.map(Ie);if(t&&typeof t=="object"){let e=t,n={};for(let r of Object.keys(e).sort())n[r]=Ie(e[r]);return n}return t}function Ze(t){return JSON.stringify(Ie(t))}function et(t){try{return JSON.parse(Un(t,"utf8"))}catch{return null}}function I(t,e){Vn(Jn(t),{recursive:!0});let n=`${t}.${process.pid}.tmp`;Yn(n,JSON.stringify(e,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),Hn(n,t)}function tt(t,e){let n=t.trim(),r=n.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),s=v(n).slice(0,12);if(!r||r==="."||r==="..")return`${e}--${s}`;let o=r.slice(0,100);return o===n&&n.length<=100?o:`${o.slice(0,85)}--${s}`}function nt(t,e,n){let r=e.trim(),s=n.trim();if(!r)throw new Error("Session agent is required");if(!s)throw new Error("Native session ID is required");let o=tt(r.toLowerCase(),"agent"),i=tt(s,"session");return{projectId:t.id,projectName:t.name,projectRoot:t.rootPath,agent:r,nativeSessionId:s,sessionKey:`${r}:${s}`,remotePrefix:["projects",t.id,"sessions",o,i].join("/"),localDirectory:Gn(t.rootPath,".toolnet","sessions",o,i)}}function rt(t){return String(t).padStart(12,"0")}var te=class{constructor(e,n=100,r=512*1024){this.storage=e;this.maxEventsPerChunk=n;this.maxChunkBytes=r;if(n<1)throw new Error("maxEventsPerChunk must be positive");if(r<1024)throw new Error("maxChunkBytes is too small")}storage;maxEventsPerChunk;maxChunkBytes;async getJson(e){let n=await this.storage.getText(e);return n?JSON.parse(n):null}async putJson(e,n){await this.storage.put(e,JSON.stringify(n,null,2)+`
6
+ `,"application/json")}async scan(e){let n=`${e.remotePrefix}/events/`,r=await this.storage.list(n),s=[],o=0;for(let i of r){let l=i.key.match(/\/events\/(\d+)-(\d+)-[a-f0-9]+\.jsonl$/);if(!l)continue;let c=Number(l[1]),a=Number(l[2]);!Number.isFinite(c)||!Number.isFinite(a)||(s.push({key:i.key,start:c,end:a}),o=Math.max(o,a))}return s.sort((i,l)=>i.start-l.start),{chunks:s,maxSequence:o}}split(e){let n=[],r=[],s=0;for(let o of e){let i=Buffer.byteLength(JSON.stringify(o)+`
7
+ `,"utf8");r.length>0&&(r.length>=this.maxEventsPerChunk||s+i>this.maxChunkBytes)&&(n.push(r),r=[],s=0),r.push(o),s+=i}return r.length>0&&n.push(r),n}async loadManifest(e){return this.getJson(`${e.remotePrefix}/session.json`)}async loadCursor(e){return this.getJson(`${e.remotePrefix}/cursor.json`)}async recover(e){let n=await this.scan(e);return{maxSequence:n.maxSequence,chunkCount:n.chunks.length}}async append(e,n,r,s={}){let o=await this.loadManifest(e),i=await this.scan(e),l=n.filter(g=>g.sequence>i.maxSequence),c=0;for(let g of this.split(l)){let y=g[0],w=g[g.length-1],C=g.map(A=>JSON.stringify(A)).join(`
8
8
  `)+`
9
- `,$=v(C).slice(0,16),T=[e.remotePrefix,"events",`${rt(h.sequence)}-${rt(w.sequence)}-${$}.jsonl`].join("/");await this.storage.exists(T)||await this.storage.put(T,C,"application/x-ndjson"),c+=g.length}let a=await this.scan(e),p=n[n.length-1],u=o?.status??"active";p?.type==="session_end"||p?.type==="session_idle"?u="idle":p?.type==="error"?u="error":n.length>0&&(u="active");let m=new Date().toISOString(),l=n[0],S={version:1,projectId:e.projectId,projectName:e.projectName,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,status:u,createdAt:o?.createdAt??l?.timestamp??m,updatedAt:p?.timestamp??m,firstEventAt:o?.firstEventAt??l?.timestamp,lastEventAt:p?.timestamp??o?.lastEventAt,eventCount:a.maxSequence,chunkCount:a.chunks.length,metadata:{...o?.metadata,...s.metadata}};(s.title??o?.title)&&(S.title=s.title??o?.title);let y={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,lastLocalSequence:n.length>0?n[n.length-1].sequence:a.maxSequence,lastRemoteSequence:a.maxSequence,sourceCursors:r,updatedAt:m};return await this.putJson(`${e.remotePrefix}/cursor.json`,y),await this.putJson(`${e.remotePrefix}/session.json`,S),{uploadedEvents:c,lastRemoteSequence:a.maxSequence,eventCount:S.eventCount,chunkCount:S.chunkCount,status:u}}};import{closeSync as Ee,existsSync as Gn,fsyncSync as Qn,mkdirSync as Xn,openSync as Oe,readSync as Zn,rmSync as st,statSync as ot,writeSync as er}from"node:fs";import{join as Pe}from"node:path";var tr=12e4,nr=80,rr=2e3;function sr(t){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,t)}var ne=class{constructor(e){this.identity=e;Xn(e.localDirectory,{recursive:!0}),this.eventsFile=Pe(e.localDirectory,"events.jsonl"),this.stateFile=Pe(e.localDirectory,"state.json"),this.lockFile=Pe(e.localDirectory,"journal.lock")}identity;eventsFile;stateFile;lockFile;initialState(){let e=new Date().toISOString();return{version:1,projectId:this.identity.projectId,agent:this.identity.agent,nativeSessionId:this.identity.nativeSessionId,status:"idle",createdAt:e,updatedAt:e,lastSequence:0,lastRemoteSequence:0,remoteByteOffset:0,sourceCursors:{},recentEventIds:[]}}loadStateUnsafe(){return et(this.stateFile)??this.initialState()}loadState(){return this.withLock(()=>this.loadStateUnsafe())}saveStateUnsafe(e){I(this.stateFile,e)}acquireLock(){for(let e=0;e<nr;e+=1)try{return Oe(this.lockFile,"wx",384)}catch(n){if(n.code!=="EEXIST")throw n;try{if(Date.now()-ot(this.lockFile).mtimeMs>tr){st(this.lockFile,{force:!0});continue}}catch{}sr(25)}throw new Error(`Session journal is locked: ${this.lockFile}`)}withLock(e){let n=this.acquireLock();try{return e()}finally{Ee(n),st(this.lockFile,{force:!0})}}append(e){return e.length===0?[]:this.withLock(()=>{let n=this.loadStateUnsafe(),r=new Set(n.recentEventIds),s=n.lastSequence,o=[];for(let u of e){let m=u.timestamp??new Date().toISOString(),l=u.data??{},S=u.provenance?.rawDigest??v(Ze(l)),y=u.sourceEventId?[this.identity.projectId,this.identity.agent,this.identity.nativeSessionId,u.sourceEventId].join("|"):[this.identity.projectId,this.identity.agent,this.identity.nativeSessionId,s+1,u.type,m,S].join("|"),g=v(y).slice(0,32);if(r.has(g))continue;s+=1;let h={version:1,id:g,sequence:s,projectId:this.identity.projectId,agent:this.identity.agent,nativeSessionId:this.identity.nativeSessionId,type:u.type,timestamp:m,data:l,provenance:{...u.provenance,rawDigest:S}};u.role!==void 0&&(h.role=u.role),u.sourceEventId!==void 0&&(h.sourceEventId=u.sourceEventId),u.sourceSequence!==void 0&&(h.sourceSequence=u.sourceSequence),o.push(h),r.add(g)}if(o.length===0)return[];let i=o.map(u=>JSON.stringify(u)).join(`
9
+ `,$=v(C).slice(0,16),T=[e.remotePrefix,"events",`${rt(y.sequence)}-${rt(w.sequence)}-${$}.jsonl`].join("/");await this.storage.exists(T)||await this.storage.put(T,C,"application/x-ndjson"),c+=g.length}let a=await this.scan(e),p=n[n.length-1],u=o?.status??"active";p?.type==="session_end"||p?.type==="session_idle"?u="idle":p?.type==="error"?u="error":n.length>0&&(u="active");let m=new Date().toISOString(),d=n[0],S={version:1,projectId:e.projectId,projectName:e.projectName,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,status:u,createdAt:o?.createdAt??d?.timestamp??m,updatedAt:p?.timestamp??m,firstEventAt:o?.firstEventAt??d?.timestamp,lastEventAt:p?.timestamp??o?.lastEventAt,eventCount:a.maxSequence,chunkCount:a.chunks.length,metadata:{...o?.metadata,...s.metadata}};(s.title??o?.title)&&(S.title=s.title??o?.title);let h={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,lastLocalSequence:n.length>0?n[n.length-1].sequence:a.maxSequence,lastRemoteSequence:a.maxSequence,sourceCursors:r,updatedAt:m};return await this.putJson(`${e.remotePrefix}/cursor.json`,h),await this.putJson(`${e.remotePrefix}/session.json`,S),{uploadedEvents:c,lastRemoteSequence:a.maxSequence,eventCount:S.eventCount,chunkCount:S.chunkCount,status:u}}};import{closeSync as Ee,existsSync as Xn,fsyncSync as Qn,mkdirSync as Zn,openSync as Oe,readSync as er,rmSync as st,statSync as ot,writeSync as tr}from"node:fs";import{join as Pe}from"node:path";var nr=12e4,rr=80,sr=2e3;function or(t){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,t)}var ne=class{constructor(e){this.identity=e;Zn(e.localDirectory,{recursive:!0}),this.eventsFile=Pe(e.localDirectory,"events.jsonl"),this.stateFile=Pe(e.localDirectory,"state.json"),this.lockFile=Pe(e.localDirectory,"journal.lock")}identity;eventsFile;stateFile;lockFile;initialState(){let e=new Date().toISOString();return{version:1,projectId:this.identity.projectId,agent:this.identity.agent,nativeSessionId:this.identity.nativeSessionId,status:"idle",createdAt:e,updatedAt:e,lastSequence:0,lastRemoteSequence:0,remoteByteOffset:0,sourceCursors:{},recentEventIds:[]}}loadStateUnsafe(){return et(this.stateFile)??this.initialState()}loadState(){return this.withLock(()=>this.loadStateUnsafe())}saveStateUnsafe(e){I(this.stateFile,e)}acquireLock(){for(let e=0;e<rr;e+=1)try{return Oe(this.lockFile,"wx",384)}catch(n){if(n.code!=="EEXIST")throw n;try{if(Date.now()-ot(this.lockFile).mtimeMs>nr){st(this.lockFile,{force:!0});continue}}catch{}or(25)}throw new Error(`Session journal is locked: ${this.lockFile}`)}withLock(e){let n=this.acquireLock();try{return e()}finally{Ee(n),st(this.lockFile,{force:!0})}}append(e){return e.length===0?[]:this.withLock(()=>{let n=this.loadStateUnsafe(),r=new Set(n.recentEventIds),s=n.lastSequence,o=[];for(let u of e){let m=u.timestamp??new Date().toISOString(),d=u.data??{},S=u.provenance?.rawDigest??v(Ze(d)),h=u.sourceEventId?[this.identity.projectId,this.identity.agent,this.identity.nativeSessionId,u.sourceEventId].join("|"):[this.identity.projectId,this.identity.agent,this.identity.nativeSessionId,s+1,u.type,m,S].join("|"),g=v(h).slice(0,32);if(r.has(g))continue;s+=1;let y={version:1,id:g,sequence:s,projectId:this.identity.projectId,agent:this.identity.agent,nativeSessionId:this.identity.nativeSessionId,type:u.type,timestamp:m,data:d,provenance:{...u.provenance,rawDigest:S}};u.role!==void 0&&(y.role=u.role),u.sourceEventId!==void 0&&(y.sourceEventId=u.sourceEventId),u.sourceSequence!==void 0&&(y.sourceSequence=u.sourceSequence),o.push(y),r.add(g)}if(o.length===0)return[];let i=o.map(u=>JSON.stringify(u)).join(`
10
10
  `)+`
11
- `,d=Oe(this.eventsFile,"a",384);try{er(d,i,null,"utf8"),Qn(d)}finally{Ee(d)}let c=o[o.length-1],a="active";c.type==="session_end"||c.type==="session_idle"?a="idle":c.type==="error"&&(a="error");let p=Array.from(r).slice(-rr);return this.saveStateUnsafe({...n,status:a,updatedAt:c.timestamp,lastSequence:c.sequence,recentEventIds:p}),o})}readPending(){return this.withLock(()=>{let e=this.loadStateUnsafe();if(!Gn(this.eventsFile))return{events:[],startOffset:e.remoteByteOffset,endOffset:e.remoteByteOffset};let n=ot(this.eventsFile).size,r=Math.min(e.remoteByteOffset,n);if(n<=r)return{events:[],startOffset:r,endOffset:n};let s=n-r,o=Buffer.alloc(s),i=Oe(this.eventsFile,"r");try{Zn(i,o,0,s,r)}finally{Ee(i)}return{events:o.toString("utf8").split(`
12
- `).filter(Boolean).map(a=>JSON.parse(a)),startOffset:r,endOffset:n}})}markRemote(e,n){this.withLock(()=>{let r=this.loadStateUnsafe();this.saveStateUnsafe({...r,lastRemoteSequence:Math.max(r.lastRemoteSequence,e),remoteByteOffset:Math.max(r.remoteByteOffset,n),updatedAt:new Date().toISOString()})})}setSourceCursor(e,n){this.withLock(()=>{let r=this.loadStateUnsafe();this.saveStateUnsafe({...r,sourceCursors:{...r.sourceCursors,[e]:String(n)},updatedAt:new Date().toISOString()})})}};import{closeSync as br,existsSync as kr,openSync as wr,readSync as jr,statSync as xr}from"node:fs";function it(t,e){let n=e.toLowerCase();return n.includes("kh\xF4ng \u0111\u01B0\u1EE3c")||n.includes("tuy\u1EC7t \u0111\u1ED1i")||n.includes("must not")||n.includes("never ")?"critical":t==="rule"||t==="decision"?"high":t==="todo"||n.includes("error")||n.includes("failed")||n.includes("exception")?"normal":"temporary"}var dt=[/không được/iu,/tuyệt đối/iu,/bắt buộc/iu,/đừng\s+/iu,/must not/iu,/do not/iu,/don't/iu,/\bnever\b/iu],or=[/từ giờ/iu,/về sau/iu,/mỗi lần/iu,/luôn luôn/iu,/\bluôn\b/iu,/quy tắc/iu,/workflow/iu,/\balways\b/iu,/\brequired\b/iu,/\bmust\b/iu,/from now on/iu],ir=[/\bchốt\b/iu,/quyết định/iu,/sẽ dùng/iu,/chọn .+ thay/iu,/đổi sang/iu,/chuyển sang/iu,/\bdecided\b/iu,/\bchosen\b/iu,/we will use/iu,/use .+ instead/iu,/switch(?:ed)? to/iu],ar=[/\btodo\b/iu,/cần làm/iu,/cần thêm/iu,/cần sửa/iu,/cần kiểm tra/iu,/tiếp theo/iu,/bước tiếp theo/iu,/còn phải/iu,/còn cần/iu,/next step/iu,/\bneed to\b/iu,/\bremaining\b/iu,/follow[- ]?up/iu],cr=[/đã sửa/iu,/đã fix/iu,/đã khắc phục/iu,/đã xử lý/iu,/hoàn tất/iu,/hoàn thành/iu,/\bfixed\b/iu,/\bresolved\b/iu,/\bimplemented\b/iu,/\bcompleted\b/iu,/\bpasses?\b/iu],at=[/kiến trúc/iu,/pipeline/iu,/adapter/iu,/schema/iu,/runtime/iu,/namespace/iu,/storage/iu,/lưu trữ/iu,/workflow/iu,/session core/iu,/memory engine/iu,/retrieval/iu],ur=[/\bdùng\b/iu,/\btách\b/iu,/\bthay\b/iu,/\bchuyển\b/iu,/\blưu\b/iu,/\bmap\b/iu,/\buse\b/iu,/\bsplit\b/iu,/\bstore\b/iu,/\breplace\b/iu,/\bmove\b/iu],dr=[/đường dẫn/iu,/\bpath\b/iu,/\bport\b/iu,/\bendpoint\b/iu,/\bdomain\b/iu,/\bbucket\b/iu,/\brepository\b/iu,/\brepo\b/iu,/\bbranch\b/iu,/\/[A-Za-z0-9._/-]{4,}/u],lr=[/\blà\b/iu,/\bở\b/iu,/\bdùng\b/iu,/\bnằm\b/iu,/\bis\b/iu,/\buse\b/iu,/located/iu,/runs on/iu],ct=new Set(["content","text","message","prompt","summary","description","reason","title","last_assistant_message","lastAssistantMessage","input_messages","inputMessages"]),pr=new Set(["payload","data","content","message","messages","parts","summary"]);function E(t,e){return e.some(n=>n.test(t))}function lt(t){return t.normalize("NFKC").replace(/\r/g,"").replace(/^[\s>*#\-•]+/u,"").replace(/\s+/g," ").trim()}function fr(t){return lt(t).toLowerCase().replace(/[^\p{L}\p{N}:/._-]+/gu," ").replace(/\s+/g," ").trim()}function mr(t){return!(t.length<12||t.length>1e3||(t.match(new RegExp("\\p{L}","gu"))??[]).length<6||/^(?:https?:\/\/\S+|[A-Za-z0-9+/=]{80,})$/u.test(t))}function Ce(t,e,n,r=0){if(!(r>6)){if(typeof t=="string"){(!e||ct.has(e))&&n.push(t);return}if(Array.isArray(t)){for(let s of t.slice(0,50))Ce(s,e,n,r+1);return}if(!(!t||typeof t!="object"))for(let[s,o]of Object.entries(t))(ct.has(s)||pr.has(s))&&Ce(o,s,n,r+1)}}function gr(t){let e=[];Ce(t.data,void 0,e);let n=[],r=new Set;for(let s of e)for(let o of s.split(/\n+|(?<=[.!?])\s+/u)){let i=lt(o);if(mr(i)&&!r.has(i)&&(r.add(i),n.push(i),n.length>=50))return n}return n}function ut(t){return(t.role??(typeof t.data.role=="string"?t.data.role:"")).toLowerCase()}function hr(t,e,n){if(n.type==="decision")return{kind:"decision",confidence:1};if(n.type==="todo")return{kind:"todo",confidence:1};let r=e==="user"||n.type==="user_prompt",s=e==="assistant"||n.type==="assistant_message";return r&&E(t,dt)?{kind:"rule",confidence:.98}:r&&E(t,or)?{kind:"rule",confidence:.92}:E(t,ir)?{kind:E(t,at)?"architecture":"decision",confidence:r?.93:.86}:r&&E(t,ar)?{kind:"todo",confidence:.87}:E(t,at)&&E(t,ur)?{kind:"architecture",confidence:r?.88:.82}:s&&E(t,cr)?{kind:"fix",confidence:.8}:r&&E(t,dr)&&E(t,lr)?{kind:"context",confidence:.79}:null}function yr(t){switch(t){case"rule":return"rule";case"decision":case"architecture":return"decision";case"todo":return"todo";case"fix":case"context":return"code"}}function Sr(t,e,n){return t==="rule"&&E(n,dt)?"critical":t==="architecture"||t==="decision"||t==="rule"?"high":t==="fix"||t==="context"?"normal":it(e,n)}function pt(t,e){let n=[],r=new Set,s=new Map;for(let o of e){let i=typeof o.data.messageId=="string"?o.data.messageId:void 0,d=ut(o);i&&d&&s.set(i,d)}for(let o of e){let i=ut(o),d=typeof o.data.messageId=="string"?o.data.messageId:void 0;!i&&d&&(i=s.get(d)??"");for(let c of gr(o)){let a=hr(c,i,o);if(!a||a.confidence<.75)continue;let p=yr(a.kind),u=fr(c),m=v([t.projectId,a.kind,u].join("|"));if(r.has(m))continue;r.add(m);let l=o.provenance.sourcePath?[o.provenance.sourcePath]:[],S=o.sourceEventId?[o.sourceEventId]:[];n.push({version:1,fingerprint:m,projectId:t.projectId,agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,kind:a.kind,type:p,content:c,confidence:a.confidence,importance:Sr(a.kind,p,c),tags:[p],provenance:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventIds:[o.id],sourceEventIds:S,sourcePaths:l,firstSequence:o.sequence,lastSequence:o.sequence},createdAt:o.timestamp})}}return n}function ft(t){return String(t).padStart(12,"0")}function vr(t){return t.remotePrefix.replace("/sessions/","/memory/learned/")}var re=class{constructor(e){this.storage=e}storage;async write(e,n,r){if(r.length===0||n.length===0)return null;let s=Math.min(...n.map(p=>p.sequence)),o=Math.max(...n.map(p=>p.sequence)),i={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,createdAt:new Date().toISOString(),firstSequence:s,lastSequence:o,candidateCount:r.length,candidates:r},d=JSON.stringify(i,null,2)+`
13
- `,c=v(r.map(p=>p.fingerprint).sort().join("|")).slice(0,16),a=[vr(e),"batches",`${ft(s)}-${ft(o)}-${c}.json`].join("/");return await this.storage.exists(a)||await this.storage.put(a,d,"application/json"),a}};function Ir(t,e){if(!kr(t))return{events:[],nextOffset:e};let n=xr(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=n-r,o=Buffer.alloc(s),i=wr(t,"r");try{jr(i,o,0,s,r)}finally{br(i)}let d=o.toString("utf8"),c=d.lastIndexOf(`
14
- `);if(c<0)return{events:[],nextOffset:r};let a=d.slice(0,c+1);return{events:a.split(`
15
- `).filter(Boolean).flatMap(u=>{try{return[JSON.parse(u)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(a,"utf8")}}var se=class{constructor(e){this.options=e;this.journal=new re(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["memory.learner.offset"]??0),r=Number.isFinite(n)?n:0,s=Ir(this.options.wal.eventsFile,r);if(s.events.length===0)return{scannedEvents:0,candidates:0,journalWritten:!1,nextOffset:s.nextOffset};let o=pt(this.options.identity,s.events),i=!1;return o.length>0&&(i=!!await this.journal.write(this.options.identity,s.events,o)),this.options.wal.setSourceCursor("memory.learner.offset",s.nextOffset),{scannedEvents:s.events.length,candidates:o.length,journalWritten:i,nextOffset:s.nextOffset}}};import{closeSync as Mr,existsSync as Ar,openSync as Nr,readSync as _r,statSync as $r}from"node:fs";var Er=new Set(["content","text","message","prompt","summary","description","title","reason","last_assistant_message","lastAssistantMessage"]);function D(t){return t.normalize("NFKC").replace(/\s+/g," ").replace(/^[\s>*#•-]+/u,"").trim()}function gt(t){return D(t).toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function L(t,e,n=0){if(!(n>6)){if(typeof t=="string"){e.push(t);return}if(Array.isArray(t)){for(let r of t.slice(0,50))L(r,e,n+1);return}if(!(!t||typeof t!="object"))for(let[r,s]of Object.entries(t))(Er.has(r)||["data","payload","parts","messages"].includes(r))&&L(s,e,n+1)}}function oe(t){return/\b(cancelled|canceled)\b|đã hủy|bỏ qua/iu.test(t)?"cancelled":/\b(blocked|blocker)\b|đang vướng|bị vướng|đang kẹt|chưa thể/iu.test(t)?"blocked":/\b(completed|complete|done|finished|passed)\b|hoàn thành|hoàn tất|đã xong|đã làm xong|\bxong\b/iu.test(t)?"completed":/\bin[\s_-]*progress\b|working on|đang làm|đang thực hiện|đang xử lý|đang triển khai/iu.test(t)?"in_progress":"pending"}function mt(t){let e=D(t);return/^(?:đang\s+làm|đang\s+thực\s+hiện|đang\s+xử\s+lý|đang\s+triển\s+khai|hoàn\s+thành|hoàn\s+tất|đã\s+xong|đã\s+làm\s+xong|xong|pending|in[\s_-]*progress|completed|complete|done|finished|blocked|cancelled|canceled)[.!]*$/iu.test(e)}function j(t,e,n,r,s={}){let o=D(r),i=s.key??gt(o);return{version:1,id:v([t.projectId,n,i,e.id,o,s.status??"",s.order??""].join("|")).slice(0,32),projectId:t.projectId,kind:n,key:i,text:o,status:s.status,order:s.order,confidence:s.confidence??.85,occurredAt:e.timestamp,sequence:e.sequence,agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventId:e.id,sourceEventId:e.sourceEventId}}function Or(t,e,n){let r=D(n);if(r.length<5||r.length>1200)return[];let s=[],o=/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\b/iu.test(r),i=r.match(/^(?:mục tiêu|goal|objective)\s*(?::|-)\s*(.+)$/iu);i?.[1]&&s.push(j(t,e,"goal",i[1],{confidence:.97}));let d=r.match(/^(?:kế hoạch|plan)\s*(?::|-)\s*(.+)$/iu);d?.[1]&&s.push(j(t,e,"plan",d[1],{confidence:.95}));let c=/\b(?:phase|giai đoạn|giai doan|stage)\s*(\d+)\s*(?:[:\-–—]\s*)?([^.;\n]{0,220})/giu,a;for(;!o&&(a=c.exec(r));){let m=Number(a[1]),l=D(a[2]??""),S=l&&!mt(l)?`Phase ${m} - ${l}`:`Phase ${m}`;s.push(j(t,e,"phase",S,{key:`phase:${m}`,order:m,status:oe(r),confidence:.93}))}let p=n.match(/^\s*[-*]\s*\[([ xX])\]\s*(.+)$/u);p?.[2]&&s.push(j(t,e,"task",p[2],{status:p[1].trim()?"completed":oe(p[2]),confidence:.96}));let u=r.match(/^(?:todo|task|việc)\s*(\d+)?(?:\s*[:.\-–—]\s*|\s+)(.+)$/iu);if(u?.[2]){let m=u[1]?Number(u[1]):void 0,l=D(u[2]),S=mt(l);s.push(j(t,e,"task",S&&m!==void 0?`TODO ${m}`:l,{key:m!==void 0?`task:${m}`:gt(l),order:m,status:oe(r),confidence:.93}))}if(/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\b/iu.test(r)){let m=r.replace(/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\s*(?::|-)?\s*/iu,"");m&&s.push(j(t,e,"next_action",m,{confidence:.9}))}return/\b(blocker|blocked)\b|đang vướng|bị vướng|đang kẹt/iu.test(r)&&s.push(j(t,e,"blocker",r,{confidence:.9})),/\b(chú ý|lưu ý|warning|attention|cẩn thận)\b/iu.test(r)&&s.push(j(t,e,"warning",r,{confidence:.88})),/\b(chốt|quyết định|decided|decision)\b/iu.test(r)&&s.push(j(t,e,"decision",r,{confidence:.9})),s}function ht(t,e){if(e.length===0)return[];let n=[],r=new Set;function s(i){r.has(i.id)||(r.add(i.id),n.push(i))}for(let i of e){if(i.type==="decision"){let c=[];L(i.data,c);for(let a of c)s(j(t,i,"decision",a,{confidence:1}))}if(i.type==="todo"){let c=[];L(i.data,c);for(let a of c)s(j(t,i,"task",a,{status:oe(a),confidence:1}))}if(["file_write","file_edit"].includes(i.type))for(let c of["filePath","path","file"]){let a=i.data[c];typeof a=="string"&&a&&s(j(t,i,"file",a,{confidence:1}))}if(i.type==="test"){let c=[];L(i.data,c);for(let a of c)s(j(t,i,"test",a,{confidence:1}))}let d=[];L(i.data,d);for(let c of d)for(let a of c.split(/\n+/u))for(let p of Or(t,i,a))s(p)}let o=e[e.length-1];return s(j(t,o,"session",`${t.agent}:${t.nativeSessionId}`,{key:t.sessionKey,confidence:1})),n}function yt(t){return String(t).padStart(12,"0")}var ie=class{constructor(e){this.storage=e}storage;async write(e,n){if(n.length===0)return null;let r=Math.min(...n.map(a=>a.sequence)),s=Math.max(...n.map(a=>a.sequence)),o={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,createdAt:new Date().toISOString(),firstSequence:r,lastSequence:s,observations:n},i=JSON.stringify(o,null,2)+`
16
- `,d=v(n.map(a=>a.id).sort().join("|")).slice(0,16),c=[`projects/${e.projectId}`,"work","observations",e.agent,e.nativeSessionId,`${yt(r)}-${yt(s)}-${d}.json`].join("/");return await this.storage.exists(c)||await this.storage.put(c,i,"application/json"),c}};import{join as St}from"node:path";import{mkdirSync as Pr}from"node:fs";function Cr(t){return t.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function F(t,e=20){let n=[],r=new Set;for(let s of t.slice().reverse()){let o=Cr(s);if(!(!o||r.has(o))&&(r.add(o),n.push(s),n.length>=e))break}return n.reverse()}function Tr(t){return t.kind==="phase"?/^Phase\s+\d+$/iu.test(t.text):t.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(t.text):!1}function vt(t,e){let n=e.status??t?.status??"pending",r=n;t&&(t.status==="completed"&&n!=="completed"?r="completed":n==="pending"&&(t.status==="in_progress"||t.status==="blocked")&&(r=t.status));let s=t&&Tr(e)?t.title:e.text;return{id:t?.id??v(e.key).slice(0,24),title:s,status:r,order:e.order??t?.order,confidence:Math.max(e.confidence,t?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function Rr(t,e){let n=`projects/${t.id}/work/observations/`,r=await e.list(n),s=[];for(let o of r.filter(i=>i.key.endsWith(".json")).sort((i,d)=>i.key.localeCompare(d.key))){let i=await e.getText(o.key);if(i)try{let d=JSON.parse(i);d.version===1&&Array.isArray(d.observations)&&s.push(d)}catch{}}return s}async function ae(t,e){let r=(await Rr(t,e)).flatMap(f=>f.observations).sort((f,x)=>{let Fe=f.occurredAt.localeCompare(x.occurredAt);if(Fe!==0)return Fe;let Ke=f.sequence-x.sequence;return Ke!==0?Ke:f.id.localeCompare(x.id)}),s=new Map,o=new Map,i,d,c,a=[],p=[],u=[],m=[],l=[],S=[];for(let f of r)switch(f.kind){case"goal":i=f.text;break;case"plan":d=f.text;break;case"phase":s.set(f.key,vt(s.get(f.key),f));break;case"task":o.set(f.key,vt(o.get(f.key),f));break;case"decision":a.push(f.text);break;case"blocker":p.push(f.text);break;case"warning":u.push(f.text);break;case"next_action":m.push(f.text);break;case"file":l.push(f.text);break;case"test":S.push(f.text);break;case"session":c={agent:f.agent,nativeSessionId:f.nativeSessionId,sessionKey:f.sessionKey,updatedAt:f.occurredAt};break}let y=Array.from(s.values()).sort((f,x)=>(f.order??Number.MAX_SAFE_INTEGER)-(x.order??Number.MAX_SAFE_INTEGER)),g=Array.from(o.values()).sort((f,x)=>(f.order??Number.MAX_SAFE_INTEGER)-(x.order??Number.MAX_SAFE_INTEGER)),h=y.find(f=>f.status==="in_progress")??y.find(f=>f.status==="blocked")??y.find(f=>f.status==="pending"),w=g.find(f=>f.status==="in_progress")??g.find(f=>f.status==="blocked")??g.find(f=>f.status==="pending"),C=F([...m,...w?[w.title]:[],...!w&&h?[h.title]:[],...g.filter(f=>f.status==="pending").slice(0,5).map(f=>f.title)],8),$=F([...p,...y.filter(f=>f.status==="blocked").map(f=>f.title),...g.filter(f=>f.status==="blocked").map(f=>f.title)],20),T={version:1,projectId:t.id,projectName:t.name,goal:i,plan:d,phases:y,tasks:g,decisions:F(a,20),blockers:$,warnings:F(u,20),nextActions:C,filesTouched:F(l,30),tests:F(S,20),currentPhase:h,currentTask:w,progress:{phasesTotal:y.length,phasesCompleted:y.filter(f=>f.status==="completed").length,tasksTotal:g.length,tasksCompleted:g.filter(f=>f.status==="completed").length,blocked:y.filter(f=>f.status==="blocked").length+g.filter(f=>f.status==="blocked").length},lastSession:c,updatedAt:r.length?r[r.length-1].occurredAt:new Date().toISOString()},A=St(t.rootPath,".toolnet","work");return Pr(A,{recursive:!0}),I(St(A,"current.json"),T),await e.put(`projects/${t.id}/work/current.json`,JSON.stringify(T,null,2)+`
17
- `,"application/json"),T}async function ce(t,e){let n=await e.getText(`projects/${t.id}/work/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}function Lr(t,e){if(!Ar(t))return{events:[],nextOffset:e};let n=$r(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=n-r,o=Buffer.alloc(s),i=Nr(t,"r");try{_r(i,o,0,s,r)}finally{Mr(i)}let d=o.toString("utf8"),c=d.lastIndexOf(`
18
- `);if(c<0)return{events:[],nextOffset:r};let a=d.slice(0,c+1);return{events:a.split(`
19
- `).filter(Boolean).flatMap(u=>{try{return[JSON.parse(u)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(a,"utf8")}}var ue=class{constructor(e){this.options=e;this.journal=new ie(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["work.continuity.offset"]??0),r=Lr(this.options.wal.eventsFile,Number.isFinite(n)?n:0);if(r.events.length===0)return{scannedEvents:0,observations:0,journalWritten:!1,reconciled:!1,nextOffset:r.nextOffset};let s=ht(this.options.identity,r.events),o=!1,i=!1;return s.length>0&&(o=!!await this.journal.write(this.options.identity,s),o&&(await ae(this.options.project,this.options.storage),i=!0)),this.options.wal.setSourceCursor("work.continuity.offset",r.nextOffset),{scannedEvents:r.events.length,observations:s.length,journalWritten:o,reconciled:i,nextOffset:r.nextOffset}}};import{closeSync as Vr,existsSync as Ur,openSync as Hr,readSync as Yr,statSync as Gr}from"node:fs";var Dr=new Set(["content","text","message","prompt","summary","description","title","reason","last_assistant_message","lastAssistantMessage"]);function K(t){return t.normalize("NFKC").replace(/\s+/g," ").replace(/^[\s>*#•-]+/u,"").trim()}function Te(t,e,n=0){if(!(n>6)){if(typeof t=="string"){e.push(t);return}if(Array.isArray(t)){for(let r of t.slice(0,50))Te(r,e,n+1);return}if(!(!t||typeof t!="object"))for(let[r,s]of Object.entries(t))(Dr.has(r)||["data","payload","parts","messages"].includes(r))&&Te(s,e,n+1)}}function O(t,e,n,r,s,o=.95){let i=K(r);return{version:1,id:v([t.projectId,n,s.type,s.key??"",i.toLowerCase(),e.id].join("|")).slice(0,32),projectId:t.projectId,kind:n,value:i,scope:s.type,scopeKey:s.key,scopeOrder:s.order,confidence:o,evidence:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventId:e.id,sourceEventId:e.sourceEventId,sequence:e.sequence,occurredAt:e.timestamp}}}function P(t,e){let n=t.toLowerCase();for(let r of e){let s=r.toLowerCase();if(n.startsWith(`${s}:`)||n.startsWith(`${s} -`)||n.startsWith(`${s} \u2014`))return K(t.slice(r.length+1))}return null}function Fr(t){let e=t.trimStart();return e.startsWith("- ")||e.startsWith("* ")||/^\d+[.)]\s+/u.test(e)}function Kr(t){return K(t.trim().replace(/^[-*]\s+/u,"").replace(/^\d+[.)]\s+/u,""))}function bt(t,e){let n=[],r=new Set;function s(o){!o.value||o.value.length<3||r.has(o.id)||(r.add(o.id),n.push(o))}for(let o of e){let i=[];Te(o.data,i);for(let d of i){let c={type:"project"},a=null;for(let p of d.split(/\r?\n/u)){let u=K(p);if(!u){a=null;continue}let m=u.match(/^(?:phase|giai đoạn|giai doan|stage)\s*(\d+)\s*(?:[:\-–—]\s*)?(.*)$/iu);if(m){let x=Number(m[1]);c={type:"phase",key:`phase:${x}`,order:x,title:K(m[2]??"")},a=null;continue}let l=u.match(/^(?:todo|task|việc)\s*(\d+)\s*(?:[:\-–—]\s*)?(.*)$/iu);if(l){let x=Number(l[1]);c={type:"task",key:`task:${x}`,order:x,title:K(l[2]??"")},a=null;continue}let S=P(u,["mission","s\u1EE9 m\u1EC7nh","m\u1EE5c ti\xEAu t\u1ED5ng th\u1EC3","m\u1EE5c ti\xEAu project","project goal"]);if(S){s(O(t,o,"mission",S,{type:"project"},.99)),a=null;continue}let y=P(u,["current objective","active objective","m\u1EE5c ti\xEAu hi\u1EC7n t\u1EA1i","objective","m\u1EE5c ti\xEAu","m\u1EE5c \u0111\xEDch"]);if(y){s(O(t,o,c.type==="phase"?"phase_objective":"objective",y,c,.98)),a=null;continue}let g=P(u,["why","why this","why this phase","reason","rationale","v\xEC sao","l\xFD do","t\u1EA1i sao","\xFD ngh\u0129a"]);if(g){s(O(t,o,c.type==="phase"?"phase_why":"why",g,c,.98)),a=null;continue}let h=P(u,["desired outcome","final outcome","k\u1EBFt qu\u1EA3 cu\u1ED1i","k\u1EBFt qu\u1EA3 mong mu\u1ED1n","m\u1EE5c ti\xEAu cu\u1ED1i"]);if(h){s(O(t,o,"desired_outcome",h,{type:"project"},.98)),a=null;continue}let w=P(u,["plan rationale","approach rationale","why this approach","t\u1EA1i sao ch\u1ECDn h\u01B0\u1EDBng n\xE0y","l\xFD do ch\u1ECDn h\u01B0\u1EDBng n\xE0y","l\xFD do ch\u1ECDn ki\u1EBFn tr\xFAc"]);if(w){s(O(t,o,"plan_rationale",w,{type:"project"},.98)),a=null;continue}let C=P(u,["deliverable","output","k\u1EBFt qu\u1EA3 c\u1EA7n \u0111\u1EA1t","ph\u1EA3i t\u1EA1o ra","\u0111\u1EA7u ra"]);if(C){s(O(t,o,"phase_deliverable",C,c,.97)),a=null;continue}let $=P(u,["acceptance criteria","definition of done","done khi","ho\xE0n th\xE0nh khi","ti\xEAu ch\xED ho\xE0n th\xE0nh"]);if($){s(O(t,o,"acceptance_criterion",$,c,.98)),a="acceptance_criterion";continue}let T=P(u,["depends on","dependency","dependencies","ph\u1EE5 thu\u1ED9c","c\u1EA7n c\xF3 tr\u01B0\u1EDBc"]);if(T){s(O(t,o,"dependency",T,c,.97)),a="dependency";continue}let A=P(u,["open question","open questions","c\xE2u h\u1ECFi m\u1EDF","ch\u01B0a quy\u1EBFt \u0111\u1ECBnh","ch\u01B0a r\xF5"]);if(A){s(O(t,o,"open_question",A,c,.95)),a="open_question";continue}let f=P(u,["constraint","constraints","r\xE0ng bu\u1ED9c","gi\u1EDBi h\u1EA1n"]);if(f){s(O(t,o,"constraint",f,c,.97)),a="constraint";continue}if(/^(?:acceptance criteria|definition of done|done khi|tiêu chí hoàn thành)\s*:?\s*$/iu.test(u)){a="acceptance_criterion";continue}if(/^(?:dependencies|dependency|phụ thuộc)\s*:?\s*$/iu.test(u)){a="dependency";continue}if(/^(?:open questions|open question|câu hỏi mở)\s*:?\s*$/iu.test(u)){a="open_question";continue}if(/^(?:constraints|constraint|ràng buộc)\s*:?\s*$/iu.test(u)){a="constraint";continue}if(a&&Fr(p)){s(O(t,o,a,Kr(p),c,.96));continue}a=null}}}return n}function kt(t){return String(t).padStart(12,"0")}var de=class{constructor(e){this.storage=e}storage;async write(e,n){if(n.length===0)return null;let r=Math.min(...n.map(c=>c.evidence.sequence)),s=Math.max(...n.map(c=>c.evidence.sequence)),o={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,firstSequence:r,lastSequence:s,createdAt:new Date().toISOString(),observations:n},i=v(n.map(c=>c.id).sort().join("|")).slice(0,16),d=[`projects/${e.projectId}`,"work","semantic","observations",e.agent,e.nativeSessionId,`${kt(r)}-${kt(s)}-${i}.json`].join("/");return await this.storage.exists(d)||await this.storage.put(d,JSON.stringify(o,null,2)+`
20
- `,"application/json"),d}};import{mkdirSync as Wr}from"node:fs";import{join as wt}from"node:path";function Br(t){return{value:t.value,confidence:t.confidence,evidence:t.evidence}}function zr(t,e){if(!e)return!0;let n=t.evidence.occurredAt.localeCompare(e.evidence.occurredAt);return n!==0?n>0:t.evidence.sessionKey===e.evidence.sessionKey?t.evidence.sequence>=e.evidence.sequence:t.confidence>=e.confidence}function R(t,e){return zr(e,t)?e:t}function M(t,e=30){let n=new Set,r=[];for(let s of t){let o=s.value.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim();!o||n.has(o)||(n.add(o),r.push(s))}return r.slice(-e)}async function qr(t,e){let n=`projects/${t.id}/work/semantic/observations/`,r=await e.list(n),s=[];for(let o of r.filter(i=>i.key.endsWith(".json")).sort((i,d)=>i.key.localeCompare(d.key))){let i=await e.getText(o.key);if(i)try{let d=JSON.parse(i);d.version===1&&Array.isArray(d.observations)&&s.push(d)}catch{}}return s}function Jr(t){return{key:t.scopeKey??`phase:${t.scopeOrder??0}`,order:t.scopeOrder??0,acceptanceCriteria:[],dependencies:[],openQuestions:[],constraints:[],notes:[]}}async function jt(t,e){let r=(await qr(t,e)).flatMap(y=>y.observations).sort((y,g)=>{let h=y.evidence.occurredAt.localeCompare(g.evidence.occurredAt);return h!==0?h:y.evidence.sessionKey===g.evidence.sessionKey?y.evidence.sequence-g.evidence.sequence:y.id.localeCompare(g.id)}),s,o,i,d,c,a=new Map,p=[],u=[],m=[];for(let y of r){let g=Br(y);if(y.scope==="phase"&&y.scopeKey){let h=a.get(y.scopeKey)??Jr(y);switch(y.kind){case"phase_objective":h.objective=R(h.objective,g);break;case"phase_why":h.why=R(h.why,g);break;case"phase_deliverable":h.deliverable=R(h.deliverable,g);break;case"acceptance_criterion":h.acceptanceCriteria.push(g);break;case"dependency":h.dependencies.push(g);break;case"open_question":h.openQuestions.push(g);break;case"constraint":h.constraints.push(g);break;case"note":h.notes.push(g);break}a.set(h.key,h);continue}switch(y.kind){case"mission":s=R(s,g);break;case"objective":o=R(o,g);break;case"why":i=R(i,g);break;case"desired_outcome":d=R(d,g);break;case"plan_rationale":c=R(c,g);break;case"open_question":p.push(g);break;case"constraint":u.push(g);break;case"note":m.push(g);break}}for(let y of a.values())y.acceptanceCriteria=M(y.acceptanceCriteria,20),y.dependencies=M(y.dependencies,15),y.openQuestions=M(y.openQuestions,15),y.constraints=M(y.constraints,15),y.notes=M(y.notes,20);let l={version:1,projectId:t.id,projectName:t.name,mission:s,activeObjective:o,why:i,desiredOutcome:d,planRationale:c,phases:Array.from(a.values()).sort((y,g)=>y.order-g.order),openQuestions:M(p,20),constraints:M(u,20),notes:M(m,20),updatedAt:r.length?r[r.length-1].evidence.occurredAt:new Date().toISOString()},S=wt(t.rootPath,".toolnet","work");return Wr(S,{recursive:!0}),I(wt(S,"semantic-current.json"),l),await e.put(`projects/${t.id}/work/semantic/current.json`,JSON.stringify(l,null,2)+`
21
- `,"application/json"),l}async function xt(t,e){let n=await e.getText(`projects/${t.id}/work/semantic/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}function Qr(t,e){if(!Ur(t))return{events:[],nextOffset:e};let n=Gr(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=Buffer.alloc(n-r),o=Hr(t,"r");try{Yr(o,s,0,s.length,r)}finally{Vr(o)}let i=s.toString("utf8"),d=i.lastIndexOf(`
22
- `);if(d<0)return{events:[],nextOffset:r};let c=i.slice(0,d+1);return{events:c.split(`
23
- `).filter(Boolean).flatMap(a=>{try{return[JSON.parse(a)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(c,"utf8")}}var le=class{constructor(e){this.options=e;this.journal=new de(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["work.semantic.offset"]??0),r=Qr(this.options.wal.eventsFile,Number.isFinite(n)?n:0);if(r.events.length===0)return{scannedEvents:0,observations:0,journalWritten:!1,reconciled:!1,nextOffset:r.nextOffset};let s=bt(this.options.identity,r.events),o=!1,i=!1;return s.length>0&&(o=!!await this.journal.write(this.options.identity,s),o&&(await jt(this.options.project,this.options.storage),i=!0)),this.options.wal.setSourceCursor("work.semantic.offset",r.nextOffset),{scannedEvents:r.events.length,observations:s.length,journalWritten:o,reconciled:i,nextOffset:r.nextOffset}}};import{mkdirSync as as}from"node:fs";import{join as Re}from"node:path";import{existsSync as Ot,mkdirSync as Xr,readFileSync as Zr,statSync as It,writeFileSync as es}from"node:fs";import{dirname as ts,join as ns}from"node:path";var Et=64*1024,rs=`# ToolNet Project Operating Manual
11
+ `,l=Oe(this.eventsFile,"a",384);try{tr(l,i,null,"utf8"),Qn(l)}finally{Ee(l)}let c=o[o.length-1],a="active";c.type==="session_end"||c.type==="session_idle"?a="idle":c.type==="error"&&(a="error");let p=Array.from(r).slice(-sr);return this.saveStateUnsafe({...n,status:a,updatedAt:c.timestamp,lastSequence:c.sequence,recentEventIds:p}),o})}readPending(){return this.withLock(()=>{let e=this.loadStateUnsafe();if(!Xn(this.eventsFile))return{events:[],startOffset:e.remoteByteOffset,endOffset:e.remoteByteOffset};let n=ot(this.eventsFile).size,r=Math.min(e.remoteByteOffset,n);if(n<=r)return{events:[],startOffset:r,endOffset:n};let s=n-r,o=Buffer.alloc(s),i=Oe(this.eventsFile,"r");try{er(i,o,0,s,r)}finally{Ee(i)}return{events:o.toString("utf8").split(`
12
+ `).filter(Boolean).map(a=>JSON.parse(a)),startOffset:r,endOffset:n}})}markRemote(e,n){this.withLock(()=>{let r=this.loadStateUnsafe();this.saveStateUnsafe({...r,lastRemoteSequence:Math.max(r.lastRemoteSequence,e),remoteByteOffset:Math.max(r.remoteByteOffset,n),updatedAt:new Date().toISOString()})})}setSourceCursor(e,n){this.withLock(()=>{let r=this.loadStateUnsafe();this.saveStateUnsafe({...r,sourceCursors:{...r.sourceCursors,[e]:String(n)},updatedAt:new Date().toISOString()})})}};import{closeSync as kr,existsSync as wr,openSync as jr,readSync as xr,statSync as Ir}from"node:fs";function it(t,e){let n=e.toLowerCase();return n.includes("kh\xF4ng \u0111\u01B0\u1EE3c")||n.includes("tuy\u1EC7t \u0111\u1ED1i")||n.includes("must not")||n.includes("never ")?"critical":t==="rule"||t==="decision"?"high":t==="todo"||n.includes("error")||n.includes("failed")||n.includes("exception")?"normal":"temporary"}var lt=[/không được/iu,/tuyệt đối/iu,/bắt buộc/iu,/đừng\s+/iu,/must not/iu,/do not/iu,/don't/iu,/\bnever\b/iu],ir=[/từ giờ/iu,/về sau/iu,/mỗi lần/iu,/luôn luôn/iu,/\bluôn\b/iu,/quy tắc/iu,/workflow/iu,/\balways\b/iu,/\brequired\b/iu,/\bmust\b/iu,/from now on/iu],ar=[/\bchốt\b/iu,/quyết định/iu,/sẽ dùng/iu,/chọn .+ thay/iu,/đổi sang/iu,/chuyển sang/iu,/\bdecided\b/iu,/\bchosen\b/iu,/we will use/iu,/use .+ instead/iu,/switch(?:ed)? to/iu],cr=[/\btodo\b/iu,/cần làm/iu,/cần thêm/iu,/cần sửa/iu,/cần kiểm tra/iu,/tiếp theo/iu,/bước tiếp theo/iu,/còn phải/iu,/còn cần/iu,/next step/iu,/\bneed to\b/iu,/\bremaining\b/iu,/follow[- ]?up/iu],ur=[/đã sửa/iu,/đã fix/iu,/đã khắc phục/iu,/đã xử lý/iu,/hoàn tất/iu,/hoàn thành/iu,/\bfixed\b/iu,/\bresolved\b/iu,/\bimplemented\b/iu,/\bcompleted\b/iu,/\bpasses?\b/iu],at=[/kiến trúc/iu,/pipeline/iu,/adapter/iu,/schema/iu,/runtime/iu,/namespace/iu,/storage/iu,/lưu trữ/iu,/workflow/iu,/session core/iu,/memory engine/iu,/retrieval/iu],lr=[/\bdùng\b/iu,/\btách\b/iu,/\bthay\b/iu,/\bchuyển\b/iu,/\blưu\b/iu,/\bmap\b/iu,/\buse\b/iu,/\bsplit\b/iu,/\bstore\b/iu,/\breplace\b/iu,/\bmove\b/iu],dr=[/đường dẫn/iu,/\bpath\b/iu,/\bport\b/iu,/\bendpoint\b/iu,/\bdomain\b/iu,/\bbucket\b/iu,/\brepository\b/iu,/\brepo\b/iu,/\bbranch\b/iu,/\/[A-Za-z0-9._/-]{4,}/u],pr=[/\blà\b/iu,/\bở\b/iu,/\bdùng\b/iu,/\bnằm\b/iu,/\bis\b/iu,/\buse\b/iu,/located/iu,/runs on/iu],ct=new Set(["content","text","message","prompt","summary","description","reason","title","last_assistant_message","lastAssistantMessage","input_messages","inputMessages"]),fr=new Set(["payload","data","content","message","messages","parts","summary"]);function E(t,e){return e.some(n=>n.test(t))}function dt(t){return t.normalize("NFKC").replace(/\r/g,"").replace(/^[\s>*#\-•]+/u,"").replace(/\s+/g," ").trim()}function mr(t){return dt(t).toLowerCase().replace(/[^\p{L}\p{N}:/._-]+/gu," ").replace(/\s+/g," ").trim()}function gr(t){return!(t.length<12||t.length>1e3||(t.match(new RegExp("\\p{L}","gu"))??[]).length<6||/^(?:https?:\/\/\S+|[A-Za-z0-9+/=]{80,})$/u.test(t))}function Ce(t,e,n,r=0){if(!(r>6)){if(typeof t=="string"){(!e||ct.has(e))&&n.push(t);return}if(Array.isArray(t)){for(let s of t.slice(0,50))Ce(s,e,n,r+1);return}if(!(!t||typeof t!="object"))for(let[s,o]of Object.entries(t))(ct.has(s)||fr.has(s))&&Ce(o,s,n,r+1)}}function yr(t){let e=[];Ce(t.data,void 0,e);let n=[],r=new Set;for(let s of e)for(let o of s.split(/\n+|(?<=[.!?])\s+/u)){let i=dt(o);if(gr(i)&&!r.has(i)&&(r.add(i),n.push(i),n.length>=50))return n}return n}function ut(t){return(t.role??(typeof t.data.role=="string"?t.data.role:"")).toLowerCase()}function hr(t,e,n){if(n.type==="decision")return{kind:"decision",confidence:1};if(n.type==="todo")return{kind:"todo",confidence:1};let r=e==="user"||n.type==="user_prompt",s=e==="assistant"||n.type==="assistant_message";return r&&E(t,lt)?{kind:"rule",confidence:.98}:r&&E(t,ir)?{kind:"rule",confidence:.92}:E(t,ar)?{kind:E(t,at)?"architecture":"decision",confidence:r?.93:.86}:r&&E(t,cr)?{kind:"todo",confidence:.87}:E(t,at)&&E(t,lr)?{kind:"architecture",confidence:r?.88:.82}:s&&E(t,ur)?{kind:"fix",confidence:.8}:r&&E(t,dr)&&E(t,pr)?{kind:"context",confidence:.79}:null}function Sr(t){switch(t){case"rule":return"rule";case"decision":case"architecture":return"decision";case"todo":return"todo";case"fix":case"context":return"code"}}function vr(t,e,n){return t==="rule"&&E(n,lt)?"critical":t==="architecture"||t==="decision"||t==="rule"?"high":t==="fix"||t==="context"?"normal":it(e,n)}function pt(t,e){let n=[],r=new Set,s=new Map;for(let o of e){let i=typeof o.data.messageId=="string"?o.data.messageId:void 0,l=ut(o);i&&l&&s.set(i,l)}for(let o of e){let i=ut(o),l=typeof o.data.messageId=="string"?o.data.messageId:void 0;!i&&l&&(i=s.get(l)??"");for(let c of yr(o)){let a=hr(c,i,o);if(!a||a.confidence<.75)continue;let p=Sr(a.kind),u=mr(c),m=v([t.projectId,a.kind,u].join("|"));if(r.has(m))continue;r.add(m);let d=o.provenance.sourcePath?[o.provenance.sourcePath]:[],S=o.sourceEventId?[o.sourceEventId]:[];n.push({version:1,fingerprint:m,projectId:t.projectId,agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,kind:a.kind,type:p,content:c,confidence:a.confidence,importance:vr(a.kind,p,c),tags:[p],provenance:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventIds:[o.id],sourceEventIds:S,sourcePaths:d,firstSequence:o.sequence,lastSequence:o.sequence},createdAt:o.timestamp})}}return n}function ft(t){return String(t).padStart(12,"0")}function br(t){return t.remotePrefix.replace("/sessions/","/memory/learned/")}var re=class{constructor(e){this.storage=e}storage;async write(e,n,r){if(r.length===0||n.length===0)return null;let s=Math.min(...n.map(p=>p.sequence)),o=Math.max(...n.map(p=>p.sequence)),i={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,createdAt:new Date().toISOString(),firstSequence:s,lastSequence:o,candidateCount:r.length,candidates:r},l=JSON.stringify(i,null,2)+`
13
+ `,c=v(r.map(p=>p.fingerprint).sort().join("|")).slice(0,16),a=[br(e),"batches",`${ft(s)}-${ft(o)}-${c}.json`].join("/");return await this.storage.exists(a)||await this.storage.put(a,l,"application/json"),a}};function Er(t,e){if(!wr(t))return{events:[],nextOffset:e};let n=Ir(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=n-r,o=Buffer.alloc(s),i=jr(t,"r");try{xr(i,o,0,s,r)}finally{kr(i)}let l=o.toString("utf8"),c=l.lastIndexOf(`
14
+ `);if(c<0)return{events:[],nextOffset:r};let a=l.slice(0,c+1);return{events:a.split(`
15
+ `).filter(Boolean).flatMap(u=>{try{return[JSON.parse(u)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(a,"utf8")}}var se=class{constructor(e){this.options=e;this.journal=new re(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["memory.learner.offset"]??0),r=Number.isFinite(n)?n:0,s=Er(this.options.wal.eventsFile,r);if(s.events.length===0)return{scannedEvents:0,candidates:0,journalWritten:!1,nextOffset:s.nextOffset};let o=pt(this.options.identity,s.events),i=!1;return o.length>0&&(i=!!await this.journal.write(this.options.identity,s.events,o)),this.options.wal.setSourceCursor("memory.learner.offset",s.nextOffset),{scannedEvents:s.events.length,candidates:o.length,journalWritten:i,nextOffset:s.nextOffset}}};import{closeSync as Ar,existsSync as Nr,openSync as _r,readSync as $r,statSync as Lr}from"node:fs";var Or=new Set(["content","text","message","prompt","summary","description","title","reason","last_assistant_message","lastAssistantMessage"]);function D(t){return t.normalize("NFKC").replace(/\s+/g," ").replace(/^[\s>*#•-]+/u,"").trim()}function gt(t){return D(t).toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function L(t,e,n=0){if(!(n>6)){if(typeof t=="string"){e.push(t);return}if(Array.isArray(t)){for(let r of t.slice(0,50))L(r,e,n+1);return}if(!(!t||typeof t!="object"))for(let[r,s]of Object.entries(t))(Or.has(r)||["data","payload","parts","messages"].includes(r))&&L(s,e,n+1)}}function oe(t){return/\b(cancelled|canceled)\b|đã hủy|bỏ qua/iu.test(t)?"cancelled":/\b(blocked|blocker)\b|đang vướng|bị vướng|đang kẹt|chưa thể/iu.test(t)?"blocked":/\b(completed|complete|done|finished|passed)\b|hoàn thành|hoàn tất|đã xong|đã làm xong|\bxong\b/iu.test(t)?"completed":/\bin[\s_-]*progress\b|working on|đang làm|đang thực hiện|đang xử lý|đang triển khai/iu.test(t)?"in_progress":"pending"}function mt(t){let e=D(t);return/^(?:đang\s+làm|đang\s+thực\s+hiện|đang\s+xử\s+lý|đang\s+triển\s+khai|hoàn\s+thành|hoàn\s+tất|đã\s+xong|đã\s+làm\s+xong|xong|pending|in[\s_-]*progress|completed|complete|done|finished|blocked|cancelled|canceled)[.!]*$/iu.test(e)}function j(t,e,n,r,s={}){let o=D(r),i=s.key??gt(o);return{version:1,id:v([t.projectId,n,i,e.id,o,s.status??"",s.order??""].join("|")).slice(0,32),projectId:t.projectId,kind:n,key:i,text:o,status:s.status,order:s.order,confidence:s.confidence??.85,occurredAt:e.timestamp,sequence:e.sequence,agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventId:e.id,sourceEventId:e.sourceEventId}}function Pr(t,e,n){let r=D(n);if(r.length<5||r.length>1200)return[];let s=[],o=/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\b/iu.test(r),i=r.match(/^(?:mục tiêu|goal|objective)\s*(?::|-)\s*(.+)$/iu);i?.[1]&&s.push(j(t,e,"goal",i[1],{confidence:.97}));let l=r.match(/^(?:kế hoạch|plan)\s*(?::|-)\s*(.+)$/iu);l?.[1]&&s.push(j(t,e,"plan",l[1],{confidence:.95}));let c=/\b(?:phase|giai đoạn|giai doan|stage)\s*(\d+)\s*(?:[:\-–—]\s*)?([^.;\n]{0,220})/giu,a;for(;!o&&(a=c.exec(r));){let m=Number(a[1]),d=D(a[2]??""),S=d&&!mt(d)?`Phase ${m} - ${d}`:`Phase ${m}`;s.push(j(t,e,"phase",S,{key:`phase:${m}`,order:m,status:oe(r),confidence:.93}))}let p=n.match(/^\s*[-*]\s*\[([ xX])\]\s*(.+)$/u);p?.[2]&&s.push(j(t,e,"task",p[2],{status:p[1].trim()?"completed":oe(p[2]),confidence:.96}));let u=r.match(/^(?:todo|task|việc)\s*(\d+)?(?:\s*[:.\-–—]\s*|\s+)(.+)$/iu);if(u?.[2]){let m=u[1]?Number(u[1]):void 0,d=D(u[2]),S=mt(d);s.push(j(t,e,"task",S&&m!==void 0?`TODO ${m}`:d,{key:m!==void 0?`task:${m}`:gt(d),order:m,status:oe(r),confidence:.93}))}if(/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\b/iu.test(r)){let m=r.replace(/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\s*(?::|-)?\s*/iu,"");m&&s.push(j(t,e,"next_action",m,{confidence:.9}))}return/\b(blocker|blocked)\b|đang vướng|bị vướng|đang kẹt/iu.test(r)&&s.push(j(t,e,"blocker",r,{confidence:.9})),/\b(chú ý|lưu ý|warning|attention|cẩn thận)\b/iu.test(r)&&s.push(j(t,e,"warning",r,{confidence:.88})),/\b(chốt|quyết định|decided|decision)\b/iu.test(r)&&s.push(j(t,e,"decision",r,{confidence:.9})),s}function yt(t,e){if(e.length===0)return[];let n=[],r=new Set;function s(i){r.has(i.id)||(r.add(i.id),n.push(i))}for(let i of e){if(i.type==="decision"){let c=[];L(i.data,c);for(let a of c)s(j(t,i,"decision",a,{confidence:1}))}if(i.type==="todo"){let c=[];L(i.data,c);for(let a of c)s(j(t,i,"task",a,{status:oe(a),confidence:1}))}if(["file_write","file_edit"].includes(i.type))for(let c of["filePath","path","file"]){let a=i.data[c];typeof a=="string"&&a&&s(j(t,i,"file",a,{confidence:1}))}if(i.type==="test"){let c=[];L(i.data,c);for(let a of c)s(j(t,i,"test",a,{confidence:1}))}let l=[];L(i.data,l);for(let c of l)for(let a of c.split(/\n+/u))for(let p of Pr(t,i,a))s(p)}let o=e[e.length-1];return s(j(t,o,"session",`${t.agent}:${t.nativeSessionId}`,{key:t.sessionKey,confidence:1})),n}function ht(t){return String(t).padStart(12,"0")}var ie=class{constructor(e){this.storage=e}storage;async write(e,n){if(n.length===0)return null;let r=Math.min(...n.map(a=>a.sequence)),s=Math.max(...n.map(a=>a.sequence)),o={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,createdAt:new Date().toISOString(),firstSequence:r,lastSequence:s,observations:n},i=JSON.stringify(o,null,2)+`
16
+ `,l=v(n.map(a=>a.id).sort().join("|")).slice(0,16),c=[`projects/${e.projectId}`,"work","observations",e.agent,e.nativeSessionId,`${ht(r)}-${ht(s)}-${l}.json`].join("/");return await this.storage.exists(c)||await this.storage.put(c,i,"application/json"),c}};import{join as St}from"node:path";import{mkdirSync as Cr}from"node:fs";function Tr(t){return t.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function F(t,e=20){let n=[],r=new Set;for(let s of t.slice().reverse()){let o=Tr(s);if(!(!o||r.has(o))&&(r.add(o),n.push(s),n.length>=e))break}return n.reverse()}function Rr(t){return t.kind==="phase"?/^Phase\s+\d+$/iu.test(t.text):t.kind==="task"?/^(?:TODO|Task|Việc)\s+\d+$/iu.test(t.text):!1}function vt(t,e){let n=e.status??t?.status??"pending",r=n;t&&(t.status==="completed"&&n!=="completed"?r="completed":n==="pending"&&(t.status==="in_progress"||t.status==="blocked")&&(r=t.status));let s=t&&Rr(e)?t.title:e.text;return{id:t?.id??v(e.key).slice(0,24),title:s,status:r,order:e.order??t?.order,confidence:Math.max(e.confidence,t?.confidence??0),updatedAt:e.occurredAt,updatedBy:{agent:e.agent,nativeSessionId:e.nativeSessionId,eventId:e.eventId}}}async function Mr(t,e){let n=`projects/${t.id}/work/observations/`,r=await e.list(n),s=[];for(let o of r.filter(i=>i.key.endsWith(".json")).sort((i,l)=>i.key.localeCompare(l.key))){let i=await e.getText(o.key);if(i)try{let l=JSON.parse(i);l.version===1&&Array.isArray(l.observations)&&s.push(l)}catch{}}return s}async function ae(t,e){let r=(await Mr(t,e)).flatMap(f=>f.observations).sort((f,x)=>{let Fe=f.occurredAt.localeCompare(x.occurredAt);if(Fe!==0)return Fe;let Ke=f.sequence-x.sequence;return Ke!==0?Ke:f.id.localeCompare(x.id)}),s=new Map,o=new Map,i,l,c,a=[],p=[],u=[],m=[],d=[],S=[];for(let f of r)switch(f.kind){case"goal":i=f.text;break;case"plan":l=f.text;break;case"phase":s.set(f.key,vt(s.get(f.key),f));break;case"task":o.set(f.key,vt(o.get(f.key),f));break;case"decision":a.push(f.text);break;case"blocker":p.push(f.text);break;case"warning":u.push(f.text);break;case"next_action":m.push(f.text);break;case"file":d.push(f.text);break;case"test":S.push(f.text);break;case"session":c={agent:f.agent,nativeSessionId:f.nativeSessionId,sessionKey:f.sessionKey,updatedAt:f.occurredAt};break}let h=Array.from(s.values()).sort((f,x)=>(f.order??Number.MAX_SAFE_INTEGER)-(x.order??Number.MAX_SAFE_INTEGER)),g=Array.from(o.values()).sort((f,x)=>(f.order??Number.MAX_SAFE_INTEGER)-(x.order??Number.MAX_SAFE_INTEGER)),y=h.find(f=>f.status==="in_progress")??h.find(f=>f.status==="blocked")??h.find(f=>f.status==="pending"),w=g.find(f=>f.status==="in_progress")??g.find(f=>f.status==="blocked")??g.find(f=>f.status==="pending"),C=F([...m,...w?[w.title]:[],...!w&&y?[y.title]:[],...g.filter(f=>f.status==="pending").slice(0,5).map(f=>f.title)],8),$=F([...p,...h.filter(f=>f.status==="blocked").map(f=>f.title),...g.filter(f=>f.status==="blocked").map(f=>f.title)],20),T={version:1,projectId:t.id,projectName:t.name,goal:i,plan:l,phases:h,tasks:g,decisions:F(a,20),blockers:$,warnings:F(u,20),nextActions:C,filesTouched:F(d,30),tests:F(S,20),currentPhase:y,currentTask:w,progress:{phasesTotal:h.length,phasesCompleted:h.filter(f=>f.status==="completed").length,tasksTotal:g.length,tasksCompleted:g.filter(f=>f.status==="completed").length,blocked:h.filter(f=>f.status==="blocked").length+g.filter(f=>f.status==="blocked").length},lastSession:c,updatedAt:r.length?r[r.length-1].occurredAt:new Date().toISOString()},A=St(t.rootPath,".toolnet","work");return Cr(A,{recursive:!0}),I(St(A,"current.json"),T),await e.put(`projects/${t.id}/work/current.json`,JSON.stringify(T,null,2)+`
17
+ `,"application/json"),T}async function ce(t,e){let n=await e.getText(`projects/${t.id}/work/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}function Dr(t,e){if(!Nr(t))return{events:[],nextOffset:e};let n=Lr(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=n-r,o=Buffer.alloc(s),i=_r(t,"r");try{$r(i,o,0,s,r)}finally{Ar(i)}let l=o.toString("utf8"),c=l.lastIndexOf(`
18
+ `);if(c<0)return{events:[],nextOffset:r};let a=l.slice(0,c+1);return{events:a.split(`
19
+ `).filter(Boolean).flatMap(u=>{try{return[JSON.parse(u)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(a,"utf8")}}var ue=class{constructor(e){this.options=e;this.journal=new ie(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["work.continuity.offset"]??0),r=Dr(this.options.wal.eventsFile,Number.isFinite(n)?n:0);if(r.events.length===0)return{scannedEvents:0,observations:0,journalWritten:!1,reconciled:!1,nextOffset:r.nextOffset};let s=yt(this.options.identity,r.events),o=!1,i=!1;return s.length>0&&(o=!!await this.journal.write(this.options.identity,s),o&&(await ae(this.options.project,this.options.storage),i=!0)),this.options.wal.setSourceCursor("work.continuity.offset",r.nextOffset),{scannedEvents:r.events.length,observations:s.length,journalWritten:o,reconciled:i,nextOffset:r.nextOffset}}};import{closeSync as Ur,existsSync as Hr,openSync as Yr,readSync as Gr,statSync as Xr}from"node:fs";var Fr=new Set(["content","text","message","prompt","summary","description","title","reason","last_assistant_message","lastAssistantMessage"]);function K(t){return t.normalize("NFKC").replace(/\s+/g," ").replace(/^[\s>*#•-]+/u,"").trim()}function Te(t,e,n=0){if(!(n>6)){if(typeof t=="string"){e.push(t);return}if(Array.isArray(t)){for(let r of t.slice(0,50))Te(r,e,n+1);return}if(!(!t||typeof t!="object"))for(let[r,s]of Object.entries(t))(Fr.has(r)||["data","payload","parts","messages"].includes(r))&&Te(s,e,n+1)}}function O(t,e,n,r,s,o=.95){let i=K(r);return{version:1,id:v([t.projectId,n,s.type,s.key??"",i.toLowerCase(),e.id].join("|")).slice(0,32),projectId:t.projectId,kind:n,value:i,scope:s.type,scopeKey:s.key,scopeOrder:s.order,confidence:o,evidence:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventId:e.id,sourceEventId:e.sourceEventId,sequence:e.sequence,occurredAt:e.timestamp}}}function P(t,e){let n=t.toLowerCase();for(let r of e){let s=r.toLowerCase();if(n.startsWith(`${s}:`)||n.startsWith(`${s} -`)||n.startsWith(`${s} \u2014`))return K(t.slice(r.length+1))}return null}function Kr(t){let e=t.trimStart();return e.startsWith("- ")||e.startsWith("* ")||/^\d+[.)]\s+/u.test(e)}function Wr(t){return K(t.trim().replace(/^[-*]\s+/u,"").replace(/^\d+[.)]\s+/u,""))}function bt(t,e){let n=[],r=new Set;function s(o){!o.value||o.value.length<3||r.has(o.id)||(r.add(o.id),n.push(o))}for(let o of e){let i=[];Te(o.data,i);for(let l of i){let c={type:"project"},a=null;for(let p of l.split(/\r?\n/u)){let u=K(p);if(!u){a=null;continue}let m=u.match(/^(?:phase|giai đoạn|giai doan|stage)\s*(\d+)\s*(?:[:\-–—]\s*)?(.*)$/iu);if(m){let x=Number(m[1]);c={type:"phase",key:`phase:${x}`,order:x,title:K(m[2]??"")},a=null;continue}let d=u.match(/^(?:todo|task|việc)\s*(\d+)\s*(?:[:\-–—]\s*)?(.*)$/iu);if(d){let x=Number(d[1]);c={type:"task",key:`task:${x}`,order:x,title:K(d[2]??"")},a=null;continue}let S=P(u,["mission","s\u1EE9 m\u1EC7nh","m\u1EE5c ti\xEAu t\u1ED5ng th\u1EC3","m\u1EE5c ti\xEAu project","project goal"]);if(S){s(O(t,o,"mission",S,{type:"project"},.99)),a=null;continue}let h=P(u,["current objective","active objective","m\u1EE5c ti\xEAu hi\u1EC7n t\u1EA1i","objective","m\u1EE5c ti\xEAu","m\u1EE5c \u0111\xEDch"]);if(h){s(O(t,o,c.type==="phase"?"phase_objective":"objective",h,c,.98)),a=null;continue}let g=P(u,["why","why this","why this phase","reason","rationale","v\xEC sao","l\xFD do","t\u1EA1i sao","\xFD ngh\u0129a"]);if(g){s(O(t,o,c.type==="phase"?"phase_why":"why",g,c,.98)),a=null;continue}let y=P(u,["desired outcome","final outcome","k\u1EBFt qu\u1EA3 cu\u1ED1i","k\u1EBFt qu\u1EA3 mong mu\u1ED1n","m\u1EE5c ti\xEAu cu\u1ED1i"]);if(y){s(O(t,o,"desired_outcome",y,{type:"project"},.98)),a=null;continue}let w=P(u,["plan rationale","approach rationale","why this approach","t\u1EA1i sao ch\u1ECDn h\u01B0\u1EDBng n\xE0y","l\xFD do ch\u1ECDn h\u01B0\u1EDBng n\xE0y","l\xFD do ch\u1ECDn ki\u1EBFn tr\xFAc"]);if(w){s(O(t,o,"plan_rationale",w,{type:"project"},.98)),a=null;continue}let C=P(u,["deliverable","output","k\u1EBFt qu\u1EA3 c\u1EA7n \u0111\u1EA1t","ph\u1EA3i t\u1EA1o ra","\u0111\u1EA7u ra"]);if(C){s(O(t,o,"phase_deliverable",C,c,.97)),a=null;continue}let $=P(u,["acceptance criteria","definition of done","done khi","ho\xE0n th\xE0nh khi","ti\xEAu ch\xED ho\xE0n th\xE0nh"]);if($){s(O(t,o,"acceptance_criterion",$,c,.98)),a="acceptance_criterion";continue}let T=P(u,["depends on","dependency","dependencies","ph\u1EE5 thu\u1ED9c","c\u1EA7n c\xF3 tr\u01B0\u1EDBc"]);if(T){s(O(t,o,"dependency",T,c,.97)),a="dependency";continue}let A=P(u,["open question","open questions","c\xE2u h\u1ECFi m\u1EDF","ch\u01B0a quy\u1EBFt \u0111\u1ECBnh","ch\u01B0a r\xF5"]);if(A){s(O(t,o,"open_question",A,c,.95)),a="open_question";continue}let f=P(u,["constraint","constraints","r\xE0ng bu\u1ED9c","gi\u1EDBi h\u1EA1n"]);if(f){s(O(t,o,"constraint",f,c,.97)),a="constraint";continue}if(/^(?:acceptance criteria|definition of done|done khi|tiêu chí hoàn thành)\s*:?\s*$/iu.test(u)){a="acceptance_criterion";continue}if(/^(?:dependencies|dependency|phụ thuộc)\s*:?\s*$/iu.test(u)){a="dependency";continue}if(/^(?:open questions|open question|câu hỏi mở)\s*:?\s*$/iu.test(u)){a="open_question";continue}if(/^(?:constraints|constraint|ràng buộc)\s*:?\s*$/iu.test(u)){a="constraint";continue}if(a&&Kr(p)){s(O(t,o,a,Wr(p),c,.96));continue}a=null}}}return n}function kt(t){return String(t).padStart(12,"0")}var le=class{constructor(e){this.storage=e}storage;async write(e,n){if(n.length===0)return null;let r=Math.min(...n.map(c=>c.evidence.sequence)),s=Math.max(...n.map(c=>c.evidence.sequence)),o={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,firstSequence:r,lastSequence:s,createdAt:new Date().toISOString(),observations:n},i=v(n.map(c=>c.id).sort().join("|")).slice(0,16),l=[`projects/${e.projectId}`,"work","semantic","observations",e.agent,e.nativeSessionId,`${kt(r)}-${kt(s)}-${i}.json`].join("/");return await this.storage.exists(l)||await this.storage.put(l,JSON.stringify(o,null,2)+`
20
+ `,"application/json"),l}};import{mkdirSync as Br}from"node:fs";import{join as wt}from"node:path";function zr(t){return{value:t.value,confidence:t.confidence,evidence:t.evidence}}function qr(t,e){if(!e)return!0;let n=t.evidence.occurredAt.localeCompare(e.evidence.occurredAt);return n!==0?n>0:t.evidence.sessionKey===e.evidence.sessionKey?t.evidence.sequence>=e.evidence.sequence:t.confidence>=e.confidence}function R(t,e){return qr(e,t)?e:t}function M(t,e=30){let n=new Set,r=[];for(let s of t){let o=s.value.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim();!o||n.has(o)||(n.add(o),r.push(s))}return r.slice(-e)}async function Jr(t,e){let n=`projects/${t.id}/work/semantic/observations/`,r=await e.list(n),s=[];for(let o of r.filter(i=>i.key.endsWith(".json")).sort((i,l)=>i.key.localeCompare(l.key))){let i=await e.getText(o.key);if(i)try{let l=JSON.parse(i);l.version===1&&Array.isArray(l.observations)&&s.push(l)}catch{}}return s}function Vr(t){return{key:t.scopeKey??`phase:${t.scopeOrder??0}`,order:t.scopeOrder??0,acceptanceCriteria:[],dependencies:[],openQuestions:[],constraints:[],notes:[]}}async function jt(t,e){let r=(await Jr(t,e)).flatMap(h=>h.observations).sort((h,g)=>{let y=h.evidence.occurredAt.localeCompare(g.evidence.occurredAt);return y!==0?y:h.evidence.sessionKey===g.evidence.sessionKey?h.evidence.sequence-g.evidence.sequence:h.id.localeCompare(g.id)}),s,o,i,l,c,a=new Map,p=[],u=[],m=[];for(let h of r){let g=zr(h);if(h.scope==="phase"&&h.scopeKey){let y=a.get(h.scopeKey)??Vr(h);switch(h.kind){case"phase_objective":y.objective=R(y.objective,g);break;case"phase_why":y.why=R(y.why,g);break;case"phase_deliverable":y.deliverable=R(y.deliverable,g);break;case"acceptance_criterion":y.acceptanceCriteria.push(g);break;case"dependency":y.dependencies.push(g);break;case"open_question":y.openQuestions.push(g);break;case"constraint":y.constraints.push(g);break;case"note":y.notes.push(g);break}a.set(y.key,y);continue}switch(h.kind){case"mission":s=R(s,g);break;case"objective":o=R(o,g);break;case"why":i=R(i,g);break;case"desired_outcome":l=R(l,g);break;case"plan_rationale":c=R(c,g);break;case"open_question":p.push(g);break;case"constraint":u.push(g);break;case"note":m.push(g);break}}for(let h of a.values())h.acceptanceCriteria=M(h.acceptanceCriteria,20),h.dependencies=M(h.dependencies,15),h.openQuestions=M(h.openQuestions,15),h.constraints=M(h.constraints,15),h.notes=M(h.notes,20);let d={version:1,projectId:t.id,projectName:t.name,mission:s,activeObjective:o,why:i,desiredOutcome:l,planRationale:c,phases:Array.from(a.values()).sort((h,g)=>h.order-g.order),openQuestions:M(p,20),constraints:M(u,20),notes:M(m,20),updatedAt:r.length?r[r.length-1].evidence.occurredAt:new Date().toISOString()},S=wt(t.rootPath,".toolnet","work");return Br(S,{recursive:!0}),I(wt(S,"semantic-current.json"),d),await e.put(`projects/${t.id}/work/semantic/current.json`,JSON.stringify(d,null,2)+`
21
+ `,"application/json"),d}async function xt(t,e){let n=await e.getText(`projects/${t.id}/work/semantic/current.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}function Qr(t,e){if(!Hr(t))return{events:[],nextOffset:e};let n=Xr(t).size,r=Number.isFinite(e)?Math.max(0,e):0;if(r>n&&(r=0),r===n)return{events:[],nextOffset:n};let s=Buffer.alloc(n-r),o=Yr(t,"r");try{Gr(o,s,0,s.length,r)}finally{Ur(o)}let i=s.toString("utf8"),l=i.lastIndexOf(`
22
+ `);if(l<0)return{events:[],nextOffset:r};let c=i.slice(0,l+1);return{events:c.split(`
23
+ `).filter(Boolean).flatMap(a=>{try{return[JSON.parse(a)]}catch{return[]}}),nextOffset:r+Buffer.byteLength(c,"utf8")}}var de=class{constructor(e){this.options=e;this.journal=new le(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),n=Number(e.sourceCursors["work.semantic.offset"]??0),r=Qr(this.options.wal.eventsFile,Number.isFinite(n)?n:0);if(r.events.length===0)return{scannedEvents:0,observations:0,journalWritten:!1,reconciled:!1,nextOffset:r.nextOffset};let s=bt(this.options.identity,r.events),o=!1,i=!1;return s.length>0&&(o=!!await this.journal.write(this.options.identity,s),o&&(await jt(this.options.project,this.options.storage),i=!0)),this.options.wal.setSourceCursor("work.semantic.offset",r.nextOffset),{scannedEvents:r.events.length,observations:s.length,journalWritten:o,reconciled:i,nextOffset:r.nextOffset}}};import{mkdirSync as cs}from"node:fs";import{join as Re}from"node:path";import{existsSync as Ot,mkdirSync as Zr,readFileSync as es,statSync as It,writeFileSync as ts}from"node:fs";import{dirname as ns,join as rs}from"node:path";var Et=64*1024,ss=`# ToolNet Project Operating Manual
24
24
 
25
25
  This file contains persistent instructions for AI agents working on this project.
26
26
 
@@ -63,27 +63,27 @@ Things an AI agent should always remember.
63
63
  <!--
64
64
  - [advisory] Prefer small focused files.
65
65
  -->
66
- `;function pe(t){return ns(t.rootPath,".toolnet","PROJECT.md")}function ss(t){return t.normalize("NFKC").replace(/\s+/g," ").trim()}function os(t){let e=[],n=new Set,r=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,s;for(;s=r.exec(t);){let o=s[1].toLowerCase(),i=ss(s[2]);if(!i)continue;let d=`${o}:${i.toLowerCase()}`;n.has(d)||(n.add(d),e.push({id:v(d).slice(0,24),mode:o,text:i,source:"manual"}))}return e}function is(t){let e=pe(t);return Ot(e)||(Xr(ts(e),{recursive:!0}),es(e,rs,{encoding:"utf8",mode:384})),e}function fe(t,e=!1){let n=e?is(t):pe(t);if(!Ot(n))return null;if(It(n).size>Et)throw new Error(`PROJECT.md exceeds ${Et} bytes`);let s=Zr(n,"utf8");return{path:n,content:s,digest:v(s),rules:os(s),bytes:Buffer.byteLength(s,"utf8"),updatedAt:new Date(It(n).mtimeMs).toISOString()}}function cs(t){return{goal:t.goal,plan:t.plan,phases:t.phases.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),tasks:t.tasks.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),decisions:t.decisions,blockers:t.blockers,warnings:t.warnings,nextActions:t.nextActions,filesTouched:t.filesTouched,tests:t.tests}}function us(t){return t.phases.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||t.tasks.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||t.nextActions.length>0||t.blockers.length>0}var me=class{constructor(e){this.options=e}options;async capture(e,n){let r=await ce(this.options.project,this.options.storage);if(r||(r=await ae(this.options.project,this.options.storage)),!us(r))return null;let s=fe(this.options.project,!1),o=s?s.rules.filter(m=>m.mode==="enforce").map(m=>m.text):[],i=v(JSON.stringify(cs(r))),d=v([this.options.project.id,this.options.identity.sessionKey,i].join("|")).slice(0,24),c=new Date().toISOString(),a={version:1,id:d,projectId:this.options.project.id,projectName:this.options.project.name,createdAt:c,reason:e,sourceSession:{agent:this.options.identity.agent,nativeSessionId:this.options.identity.nativeSessionId,sessionKey:this.options.identity.sessionKey,sequence:n},goal:r.goal,plan:r.plan,progress:r.progress,currentPhase:r.currentPhase,currentTask:r.currentTask,incompletePhases:r.phases.filter(m=>m.status!=="completed"&&m.status!=="cancelled"),incompleteTasks:r.tasks.filter(m=>m.status!=="completed"&&m.status!=="cancelled"),nextActions:r.nextActions.slice(0,10),blockers:r.blockers.slice(0,10),decisions:r.decisions.slice(-10),warnings:r.warnings.slice(-10),attention:[...o,...r.warnings].slice(0,20),filesTouched:r.filesTouched.slice(-20),tests:r.tests.slice(-15),stateDigest:i},p=`projects/${this.options.project.id}/work/handoffs/${d}.json`;await this.options.storage.exists(p)||await this.options.storage.put(p,JSON.stringify(a,null,2)+`
66
+ `;function pe(t){return rs(t.rootPath,".toolnet","PROJECT.md")}function os(t){return t.normalize("NFKC").replace(/\s+/g," ").trim()}function is(t){let e=[],n=new Set,r=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,s;for(;s=r.exec(t);){let o=s[1].toLowerCase(),i=os(s[2]);if(!i)continue;let l=`${o}:${i.toLowerCase()}`;n.has(l)||(n.add(l),e.push({id:v(l).slice(0,24),mode:o,text:i,source:"manual"}))}return e}function as(t){let e=pe(t);return Ot(e)||(Zr(ns(e),{recursive:!0}),ts(e,ss,{encoding:"utf8",mode:384})),e}function fe(t,e=!1){let n=e?as(t):pe(t);if(!Ot(n))return null;if(It(n).size>Et)throw new Error(`PROJECT.md exceeds ${Et} bytes`);let s=es(n,"utf8");return{path:n,content:s,digest:v(s),rules:is(s),bytes:Buffer.byteLength(s,"utf8"),updatedAt:new Date(It(n).mtimeMs).toISOString()}}function us(t){return{goal:t.goal,plan:t.plan,phases:t.phases.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),tasks:t.tasks.map(e=>({id:e.id,title:e.title,status:e.status,order:e.order})),decisions:t.decisions,blockers:t.blockers,warnings:t.warnings,nextActions:t.nextActions,filesTouched:t.filesTouched,tests:t.tests}}function ls(t){return t.phases.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||t.tasks.some(e=>e.status==="pending"||e.status==="in_progress"||e.status==="blocked")||t.nextActions.length>0||t.blockers.length>0}var me=class{constructor(e){this.options=e}options;async capture(e,n){let r=await ce(this.options.project,this.options.storage);if(r||(r=await ae(this.options.project,this.options.storage)),!ls(r))return null;let s=fe(this.options.project,!1),o=s?s.rules.filter(m=>m.mode==="enforce").map(m=>m.text):[],i=v(JSON.stringify(us(r))),l=v([this.options.project.id,this.options.identity.sessionKey,i].join("|")).slice(0,24),c=new Date().toISOString(),a={version:1,id:l,projectId:this.options.project.id,projectName:this.options.project.name,createdAt:c,reason:e,sourceSession:{agent:this.options.identity.agent,nativeSessionId:this.options.identity.nativeSessionId,sessionKey:this.options.identity.sessionKey,sequence:n},goal:r.goal,plan:r.plan,progress:r.progress,currentPhase:r.currentPhase,currentTask:r.currentTask,incompletePhases:r.phases.filter(m=>m.status!=="completed"&&m.status!=="cancelled"),incompleteTasks:r.tasks.filter(m=>m.status!=="completed"&&m.status!=="cancelled"),nextActions:r.nextActions.slice(0,10),blockers:r.blockers.slice(0,10),decisions:r.decisions.slice(-10),warnings:r.warnings.slice(-10),attention:[...o,...r.warnings].slice(0,20),filesTouched:r.filesTouched.slice(-20),tests:r.tests.slice(-15),stateDigest:i},p=`projects/${this.options.project.id}/work/handoffs/${l}.json`;await this.options.storage.exists(p)||await this.options.storage.put(p,JSON.stringify(a,null,2)+`
67
67
  `,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(a,null,2)+`
68
- `,"application/json");let u=Re(this.options.project.rootPath,".toolnet","work","handoffs");return as(u,{recursive:!0}),I(Re(u,`${d}.json`),a),I(Re(this.options.project.rootPath,".toolnet","work","handoff-latest.json"),a),a}};async function Pt(t,e){let n=await e.getText(`projects/${t.id}/work/handoff-latest.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}var ge=class{identity;wal;remote;sanitizer=new J;learner;continuity;semantic;handoff;title;metadata;constructor(e){this.identity=nt(e.project,e.agent,e.nativeSessionId),this.title=e.title,this.metadata=this.sanitizer.sanitizeValue(e.metadata??{}),this.wal=new ne(this.identity),this.remote=new te(e.storage,e.maxEventsPerChunk??100,e.maxChunkBytes??512*1024),this.learner=new se({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.continuity=new ue({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.semantic=new le({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.handoff=new me({project:e.project,storage:e.storage,identity:this.identity})}sanitizeEvent(e){let n=e.provenance?{...e.provenance,metadata:this.sanitizer.sanitizeValue(e.provenance.metadata)}:void 0;return{...e,data:this.sanitizer.sanitizeValue(e.data??{}),provenance:n}}start(e={}){let n=this.wal.loadState();return this.record({type:n.lastSequence===0?"session_start":"session_resume",data:e,provenance:{source:this.identity.agent}})}record(e){return this.wal.append([this.sanitizeEvent(e)])[0]}recordMany(e){return this.wal.append(e.map(n=>this.sanitizeEvent(n)))}setSourceCursor(e,n){this.wal.setSourceCursor(e,n)}async flush(){let e=this.wal.readPending(),n=this.wal.loadState(),r=await this.remote.append(this.identity,e.events,n.sourceCursors,{title:this.title,metadata:this.metadata});if(e.events.length>0){let s=e.events[e.events.length-1];this.wal.markRemote(s.sequence,e.endOffset)}if(process.env.TOOLNET_SESSION_LEARNING!=="0")try{await this.learner.learnNew()}catch{}if(process.env.TOOLNET_WORK_CONTINUITY!=="0")try{await this.continuity.learnNew()}catch{}if(process.env.TOOLNET_SEMANTIC_CONTINUITY!=="0")try{await this.semantic.learnNew()}catch{}if(process.env.TOOLNET_SMART_HANDOFF!=="0"&&e.events.length>0)try{let s=e.events[e.events.length-1],o=["session_idle","session_end","session_compact"].includes(s.type)?s.type:"checkpoint";await this.handoff.capture(o,s.sequence)}catch{}return r}async idle(e={}){return this.record({type:"session_idle",data:e,provenance:{source:this.identity.agent}}),this.flush()}async end(e={}){return this.record({type:"session_end",data:e,provenance:{source:this.identity.agent}}),this.flush()}status(){return this.wal.loadState()}recoverRemote(){return this.remote.recover(this.identity)}};var ds=[/^<SYSTEM MESSAGE>/i,/^<EPHEMERAL MESSAGE>/i,/^<system>/i,/^<ephemeral>/i,/^ManageTask:/i,/^Task \d+ status:/i,/^Task \d+ killed/i,/^Task \d+ loading/i,/^Thought for \d+ tokens/i,/^Prioritizing Tool Usage/i,/^Tool call:/i,/^Tool response:/i,/^npm notice/i,/^npm WARN/i,/^added \d+ packages/i,/^removed \d+ packages/i,/^up to date/i,/^\d+ packages are looking for funding/i,/^run `npm fund` for details/i,/^[\d.]+%/,/^\[={10,}\]/,/^Loading\.\.\./i,/^Processing\.\.\./i,/^bash-\d+\.\d+\$/,/^\$ /,/^\s*$/],ls=[/api[_-]?key[:\s=]+[^\s]+/gi,/token[:\s=]+[^\s]+/gi,/secret[:\s=]+[^\s]+/gi,/password[:\s=]+[^\s]+/gi,/bearer\s+[^\s]+/gi,/authorization:\s*[^\s]+/gi],ps=["decision","decided","rule","convention","architecture","pattern","fixed","resolved","implemented","created","updated","deleted","deployed","blocker","blocked","issue","bug","error","next","todo","action","requirement","must","should","important"];function fs(t){let e=t.toLowerCase();return ps.some(n=>e.includes(n))}function ms(t){if(!t.trim())return!0;for(let e of ds)if(e.test(t))return!0;return fs(t),!1}function gs(t){let e=t;for(let n of ls)e=e.replace(n,r=>{let s=r.split(/[:\s=]+/);return s.length>1?`${s[0]}: [REDACTED]`:"[REDACTED]"});return e}function Me(t){let e=t.trim();return e?ms(e)?{content:"",filtered:!0,reason:"noise"}:{content:gs(e),filtered:!1}:{content:"",filtered:!0,reason:"empty"}}function he(t){let e={};for(let[n,r]of Object.entries(t))if(typeof r=="string"){let s=Me(r);s.filtered||(e[n]=s.content)}else r&&typeof r=="object"&&!Array.isArray(r)?e[n]=he(r):Array.isArray(r)?e[n]=r.map(s=>{if(typeof s=="string"){let o=Me(s);return o.filtered?null:o.content}return s&&typeof s=="object"?he(s):s}).filter(s=>s!==null):e[n]=r;return e}function Ct(t){let e=typeof t.type=="string"?t.type.toLowerCase():"";if(e.includes("system")||e.includes("ephemeral")||e==="tool_call"&&!t.result)return!0;if(t.data&&typeof t.data=="object"){let n=t.data,r=typeof n.content=="string"?n.content:"";if(r&&Me(r).filtered)return!0}return!1}function ye(t){return t?Math.ceil(t.length/3.5):0}function Se(t,e){if(!t)return"";if(ye(t)<=e)return t;let r=Math.floor(e*3.5),s=t.slice(0,r),o=s.lastIndexOf("."),i=s.lastIndexOf(`
69
- `),d=Math.max(o,i);return d>r*.7?s.slice(0,d+1):s}function N(){let t=H(),e=process.env.TOOLNET_SESSION_SAVE||"summary",n=process.env.TOOLNET_RAW_TRANSCRIPT==="on"||e==="archive"||e==="full",r=process.env.TOOLNET_MEMORY_PROMOTION||"conservative",s=parseFloat(process.env.TOOLNET_PROMOTE_MIN_SCORE||"0.65"),o=parseInt(process.env.TOOLNET_SESSION_SUMMARY_MAX_TOKENS||"700",10),i=parseInt(process.env.TOOLNET_DURABLE_MEMORY_MAX_ITEMS_PER_SESSION||"10",10),d=n,c=process.env.TOOLNET_RAW_TRANSCRIPT_REMOTE==="on"||e==="full";return{sessionSave:e,rawTranscript:n,memoryPromotion:r,promoteMinScore:s,sessionSummaryMaxTokens:o,durableMemoryMaxItemsPerSession:i,archiveLocal:d,archiveRemote:c}}function Tt(t){return(t||N()).rawTranscript}function Rt(t){return(t||N()).durableMemoryMaxItemsPerSession}function Mt(t){return(t||N()).sessionSummaryMaxTokens}function At(t){return(t||N()).archiveRemote}function hs(t){return t.replace(/\b[A-Za-z0-9_-]{20,}\b/g,"[REDACTED]").replace(/api[_-]?key[:\s=]+[^\s]+/gi,"api_key=[REDACTED]").replace(/token[:\s=]+[^\s]+/gi,"token=[REDACTED]").replace(/secret[:\s=]+[^\s]+/gi,"secret=[REDACTED]").replace(/password[:\s=]+[^\s]+/gi,"password=[REDACTED]")}function ys(t,e){let n=t.toLowerCase(),r=.5,s=["nh\u1EDB","remember","quy t\u1EAFc","rule","t\u1EEB gi\u1EDD","from now","kh\xF4ng \u0111\u01B0\u1EE3c","must not","lu\xF4n lu\xF4n","always","never","critical","important","blocker","deploy","production","architecture"];for(let i of s)n.includes(i)&&(r+=.15);e==="rule"||e==="architecture"||e==="blocker"?r+=.2:e==="decision"||e==="deploy"?r+=.15:(e==="fix"||e==="next_action")&&(r+=.1),t.length<20?r-=.3:t.length>500&&(r-=.1);let o=[/npm (notice|warn|ERR)/i,/\d+ packages? in \d+/i,/found \d+ vulnerabilities/i,/up to date/i,/added \d+ packages/i,/^(ok|done|success|error|warning)$/i];for(let i of o)i.test(t)&&(r-=.4);return Math.max(0,Math.min(1,r))}function Ss(t,e){let n=[],r=new Set;for(let i of t){let d=i.split(`
70
- `).filter(c=>c.trim());for(let c of d){let a=c.trim();if(a.length<15)continue;let p=a.toLowerCase().replace(/\s+/g," ");if(r.has(p))continue;r.add(p);let u="decision";/\b(rule|quy tắc|policy|standard|convention)\b/i.test(a)||/\b(always|never|must|should|từ giờ|không được)\b/i.test(a)?u="rule":/\b(fix|fixed|bug|issue|error|lỗi)\b/i.test(a)?u="fix":/\b(blocker|blocked|stuck|cannot|không thể)\b/i.test(a)?u="blocker":/\b(next|todo|action|task|cần làm)\b/i.test(a)?u="next_action":/\b(deploy|release|publish|ship)\b/i.test(a)?u="deploy":/\b(architecture|design|structure|pattern)\b/i.test(a)?u="architecture":/\.(ts|js|py|go|rs|java|cpp|c|h)\b/i.test(a)&&(u="file");let m=ys(a,u);if(m<.3)continue;let l=hs(a);n.push({category:u,text:l,importance:m,sourceSessionId:e})}}let s=N(),o=Rt(s);return n.sort((i,d)=>d.importance-i.importance).slice(0,o)}function vs(t){let e=N(),n=Mt(e),o=t.join(`
68
+ `,"application/json");let u=Re(this.options.project.rootPath,".toolnet","work","handoffs");return cs(u,{recursive:!0}),I(Re(u,`${l}.json`),a),I(Re(this.options.project.rootPath,".toolnet","work","handoff-latest.json"),a),a}};async function Pt(t,e){let n=await e.getText(`projects/${t.id}/work/handoff-latest.json`);if(!n)return null;try{return JSON.parse(n)}catch{return null}}var ge=class{identity;wal;remote;sanitizer=new J;learner;continuity;semantic;handoff;title;metadata;constructor(e){this.identity=nt(e.project,e.agent,e.nativeSessionId),this.title=e.title,this.metadata=this.sanitizer.sanitizeValue(e.metadata??{}),this.wal=new ne(this.identity),this.remote=new te(e.storage,e.maxEventsPerChunk??100,e.maxChunkBytes??512*1024),this.learner=new se({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.continuity=new ue({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.semantic=new de({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.handoff=new me({project:e.project,storage:e.storage,identity:this.identity})}sanitizeEvent(e){let n=e.provenance?{...e.provenance,metadata:this.sanitizer.sanitizeValue(e.provenance.metadata)}:void 0;return{...e,data:this.sanitizer.sanitizeValue(e.data??{}),provenance:n}}start(e={}){let n=this.wal.loadState();return this.record({type:n.lastSequence===0?"session_start":"session_resume",data:e,provenance:{source:this.identity.agent}})}record(e){return this.wal.append([this.sanitizeEvent(e)])[0]}recordMany(e){return this.wal.append(e.map(n=>this.sanitizeEvent(n)))}setSourceCursor(e,n){this.wal.setSourceCursor(e,n)}async flush(){let e=this.wal.readPending(),n=this.wal.loadState(),r=await this.remote.append(this.identity,e.events,n.sourceCursors,{title:this.title,metadata:this.metadata});if(e.events.length>0){let s=e.events[e.events.length-1];this.wal.markRemote(s.sequence,e.endOffset)}if(process.env.TOOLNET_SESSION_LEARNING!=="0")try{await this.learner.learnNew()}catch{}if(process.env.TOOLNET_WORK_CONTINUITY!=="0")try{await this.continuity.learnNew()}catch{}if(process.env.TOOLNET_SEMANTIC_CONTINUITY!=="0")try{await this.semantic.learnNew()}catch{}if(process.env.TOOLNET_SMART_HANDOFF!=="0"&&e.events.length>0)try{let s=e.events[e.events.length-1],o=["session_idle","session_end","session_compact"].includes(s.type)?s.type:"checkpoint";await this.handoff.capture(o,s.sequence)}catch{}return r}async idle(e={}){return this.record({type:"session_idle",data:e,provenance:{source:this.identity.agent}}),this.flush()}async end(e={}){return this.record({type:"session_end",data:e,provenance:{source:this.identity.agent}}),this.flush()}status(){return this.wal.loadState()}recoverRemote(){return this.remote.recover(this.identity)}};var ds=[/^<SYSTEM MESSAGE>/i,/^<EPHEMERAL MESSAGE>/i,/^<system>/i,/^<ephemeral>/i,/^ManageTask:/i,/^Task \d+ status:/i,/^Task \d+ killed/i,/^Task \d+ loading/i,/^Thought for \d+ tokens/i,/^Prioritizing Tool Usage/i,/^Tool call:/i,/^Tool response:/i,/^npm notice/i,/^npm WARN/i,/^added \d+ packages/i,/^removed \d+ packages/i,/^up to date/i,/^\d+ packages are looking for funding/i,/^run `npm fund` for details/i,/^[\d.]+%/,/^\[={10,}\]/,/^Loading\.\.\./i,/^Processing\.\.\./i,/^bash-\d+\.\d+\$/,/^\$ /,/^\s*$/],ps=[/api[_-]?key[:\s=]+[^\s]+/gi,/token[:\s=]+[^\s]+/gi,/secret[:\s=]+[^\s]+/gi,/password[:\s=]+[^\s]+/gi,/bearer\s+[^\s]+/gi,/authorization:\s*[^\s]+/gi],fs=["decision","decided","rule","convention","architecture","pattern","fixed","resolved","implemented","created","updated","deleted","deployed","blocker","blocked","issue","bug","error","next","todo","action","requirement","must","should","important"];function ms(t){let e=t.toLowerCase();return fs.some(n=>e.includes(n))}function gs(t){if(!t.trim())return!0;for(let e of ds)if(e.test(t))return!0;return ms(t),!1}function ys(t){let e=t;for(let n of ps)e=e.replace(n,r=>{let s=r.split(/[:\s=]+/);return s.length>1?`${s[0]}: [REDACTED]`:"[REDACTED]"});return e}function Me(t){let e=t.trim();return e?gs(e)?{content:"",filtered:!0,reason:"noise"}:{content:ys(e),filtered:!1}:{content:"",filtered:!0,reason:"empty"}}function ye(t){let e={};for(let[n,r]of Object.entries(t))if(typeof r=="string"){let s=Me(r);s.filtered||(e[n]=s.content)}else r&&typeof r=="object"&&!Array.isArray(r)?e[n]=ye(r):Array.isArray(r)?e[n]=r.map(s=>{if(typeof s=="string"){let o=Me(s);return o.filtered?null:o.content}return s&&typeof s=="object"?ye(s):s}).filter(s=>s!==null):e[n]=r;return e}function Ct(t){let e=typeof t.type=="string"?t.type.toLowerCase():"";if(e.includes("system")||e.includes("ephemeral")||e==="tool_call"&&!t.result)return!0;if(t.data&&typeof t.data=="object"){let n=t.data,r=typeof n.content=="string"?n.content:"";if(r&&Me(r).filtered)return!0}return!1}function he(t){return t?Math.ceil(t.length/3.5):0}function Se(t,e){if(!t)return"";if(he(t)<=e)return t;let r=Math.floor(e*3.5),s=t.slice(0,r),o=s.lastIndexOf("."),i=s.lastIndexOf(`
69
+ `),l=Math.max(o,i);return l>r*.7?s.slice(0,l+1):s}function N(){let t=H(),e=process.env.TOOLNET_SESSION_SAVE||"summary",n=process.env.TOOLNET_RAW_TRANSCRIPT==="on"||e==="archive"||e==="full",r=process.env.TOOLNET_MEMORY_PROMOTION||"conservative",s=parseFloat(process.env.TOOLNET_PROMOTE_MIN_SCORE||"0.65"),o=parseInt(process.env.TOOLNET_SESSION_SUMMARY_MAX_TOKENS||"700",10),i=parseInt(process.env.TOOLNET_DURABLE_MEMORY_MAX_ITEMS_PER_SESSION||"10",10),l=n,c=process.env.TOOLNET_RAW_TRANSCRIPT_REMOTE==="on"||e==="full";return{sessionSave:e,rawTranscript:n,memoryPromotion:r,promoteMinScore:s,sessionSummaryMaxTokens:o,durableMemoryMaxItemsPerSession:i,archiveLocal:l,archiveRemote:c}}function Tt(t){return(t||N()).rawTranscript}function Rt(t){return(t||N()).durableMemoryMaxItemsPerSession}function Mt(t){return(t||N()).sessionSummaryMaxTokens}function At(t){return(t||N()).archiveRemote}function hs(t){return t.replace(/\b[A-Za-z0-9_-]{20,}\b/g,"[REDACTED]").replace(/api[_-]?key[:\s=]+[^\s]+/gi,"api_key=[REDACTED]").replace(/token[:\s=]+[^\s]+/gi,"token=[REDACTED]").replace(/secret[:\s=]+[^\s]+/gi,"secret=[REDACTED]").replace(/password[:\s=]+[^\s]+/gi,"password=[REDACTED]")}function Ss(t,e){let n=t.toLowerCase(),r=.5,s=["nh\u1EDB","remember","quy t\u1EAFc","rule","t\u1EEB gi\u1EDD","from now","kh\xF4ng \u0111\u01B0\u1EE3c","must not","lu\xF4n lu\xF4n","always","never","critical","important","blocker","deploy","production","architecture"];for(let i of s)n.includes(i)&&(r+=.15);e==="rule"||e==="architecture"||e==="blocker"?r+=.2:e==="decision"||e==="deploy"?r+=.15:(e==="fix"||e==="next_action")&&(r+=.1),t.length<20?r-=.3:t.length>500&&(r-=.1);let o=[/npm (notice|warn|ERR)/i,/\d+ packages? in \d+/i,/found \d+ vulnerabilities/i,/up to date/i,/added \d+ packages/i,/^(ok|done|success|error|warning)$/i];for(let i of o)i.test(t)&&(r-=.4);return Math.max(0,Math.min(1,r))}function vs(t,e){let n=[],r=new Set;for(let i of t){let l=i.split(`
70
+ `).filter(c=>c.trim());for(let c of l){let a=c.trim();if(a.length<15)continue;let p=a.toLowerCase().replace(/\s+/g," ");if(r.has(p))continue;r.add(p);let u="decision";/\b(rule|quy tắc|policy|standard|convention)\b/i.test(a)||/\b(always|never|must|should|từ giờ|không được)\b/i.test(a)?u="rule":/\b(fix|fixed|bug|issue|error|lỗi)\b/i.test(a)?u="fix":/\b(blocker|blocked|stuck|cannot|không thể)\b/i.test(a)?u="blocker":/\b(next|todo|action|task|cần làm)\b/i.test(a)?u="next_action":/\b(deploy|release|publish|ship)\b/i.test(a)?u="deploy":/\b(architecture|design|structure|pattern)\b/i.test(a)?u="architecture":/\.(ts|js|py|go|rs|java|cpp|c|h)\b/i.test(a)&&(u="file");let m=Ss(a,u);if(m<.3)continue;let d=hs(a);n.push({category:u,text:d,importance:m,sourceSessionId:e})}}let s=N(),o=Rt(s);return n.sort((i,l)=>l.importance-i.importance).slice(0,o)}function bs(t){let e=N(),n=Mt(e),o=t.join(`
71
71
 
72
72
  `).split(`
73
- `).filter(i=>{let d=i.trim();return d.length>20&&!d.startsWith("npm")&&!d.startsWith("\u2713")&&!d.startsWith("Error:")}).slice(0,20).join(`
73
+ `).filter(i=>{let l=i.trim();return l.length>20&&!l.startsWith("npm")&&!l.startsWith("\u2713")&&!l.startsWith("Error:")}).slice(0,20).join(`
74
74
  `);return Se(o,n)}function Nt(t,e){let r=(Array.isArray(t)?t:t.split(`
75
75
 
76
- `).filter(l=>l.trim())).map(l=>typeof l=="string"?l:JSON.stringify(l)).filter(l=>l.trim()),s=Ss(r,e),o=s.filter(l=>l.category==="decision").map(l=>l.text),i=s.filter(l=>l.category==="rule").map(l=>l.text),d=s.filter(l=>l.category==="file").map(l=>l.text),c=s.filter(l=>l.category==="fix").map(l=>l.text),a=s.filter(l=>l.category==="blocker").map(l=>l.text),p=s.filter(l=>l.category==="next_action").map(l=>l.text),u=s.filter(l=>l.category==="deploy").map(l=>l.text);return{summary:vs(r),decisions:o,projectRules:i,filesChanged:d,bugsFixed:c,commands:u,blockers:a,nextActions:p,durableFacts:s}}function Kt(){return process.env.OPENCODE_DB_PATH??js(ks(),".local","share","opencode","opencode.db")}function k(t){return typeof t=="string"?t:""}function _(t){if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t=="bigint")return Number(t);if(typeof t=="string"){let e=Number(t);if(Number.isFinite(e))return e}return 0}function Ae(t){if(t&&typeof t=="object"&&!Buffer.isBuffer(t))return t;if(typeof t!="string")return{};try{let e=JSON.parse(t);if(e&&typeof e=="object"&&!Array.isArray(e))return e}catch{}return{}}function W(t){let e=_(t);if(e<=0)return new Date().toISOString();e<1e11&&(e*=1e3);let n=new Date(e);return Number.isNaN(n.getTime())?new Date().toISOString():n.toISOString()}function ve(t,e){if(!e)return!1;let n=_t(t),r=_t(e);if(n===r)return!0;let s=xs(n,r);return s!==""&&s!==".."&&!s.startsWith(`..${process.platform==="win32"?"\\":"/"}`)&&!ws(s)}function $t(t){if(!t)return{time:-1,id:""};try{let e=JSON.parse(t);return{time:typeof e.time=="number"?e.time:-1,id:typeof e.id=="string"?e.id:""}}catch{return{time:-1,id:""}}}function Lt(t){return JSON.stringify(t)}function Wt(t){if(!bs(t))throw new Error(`OpenCode database not found: ${t}`);let e=new Is(t,{readOnly:!0});return e.exec("PRAGMA query_only = ON"),e.exec("PRAGMA busy_timeout = 3000"),e}function Es(t,e){let n=t.prepare(`
76
+ `).filter(d=>d.trim())).map(d=>typeof d=="string"?d:JSON.stringify(d)).filter(d=>d.trim()),s=vs(r,e),o=s.filter(d=>d.category==="decision").map(d=>d.text),i=s.filter(d=>d.category==="rule").map(d=>d.text),l=s.filter(d=>d.category==="file").map(d=>d.text),c=s.filter(d=>d.category==="fix").map(d=>d.text),a=s.filter(d=>d.category==="blocker").map(d=>d.text),p=s.filter(d=>d.category==="next_action").map(d=>d.text),u=s.filter(d=>d.category==="deploy").map(d=>d.text);return{summary:bs(r),decisions:o,projectRules:i,filesChanged:l,bugsFixed:c,commands:u,blockers:a,nextActions:p,durableFacts:s}}function Wt(){try{let e=ws("opencode",["db","path"],{encoding:"utf8",stdio:["ignore","pipe","ignore"],timeout:5e3}).trim();if(e)return e}catch{}if(process.env.OPENCODE_DB)return process.env.OPENCODE_DB;let t=process.env.XDG_DATA_HOME??_t(js(),".local","share");return _t(t,"opencode","opencode.db")}function k(t){return typeof t=="string"?t:""}function _(t){if(typeof t=="number"&&Number.isFinite(t))return t;if(typeof t=="bigint")return Number(t);if(typeof t=="string"){let e=Number(t);if(Number.isFinite(e))return e}return 0}function Ae(t){if(t&&typeof t=="object"&&!Buffer.isBuffer(t))return t;if(typeof t!="string")return{};try{let e=JSON.parse(t);if(e&&typeof e=="object"&&!Array.isArray(e))return e}catch{}return{}}function W(t){let e=_(t);if(e<=0)return new Date().toISOString();e<1e11&&(e*=1e3);let n=new Date(e);return Number.isNaN(n.getTime())?new Date().toISOString():n.toISOString()}function ve(t,e){if(!e)return!1;let n=$t(t),r=$t(e);if(n===r)return!0;let s=Is(n,r);return s!==""&&s!==".."&&!s.startsWith(`..${process.platform==="win32"?"\\":"/"}`)&&!xs(s)}function Lt(t){if(!t)return{time:-1,id:""};try{let e=JSON.parse(t);return{time:typeof e.time=="number"?e.time:-1,id:typeof e.id=="string"?e.id:""}}catch{return{time:-1,id:""}}}function Dt(t){return JSON.stringify(t)}function Bt(t){if(!ks(t))throw new Error(`OpenCode database not found: ${t}`);let e=new Es(t,{readOnly:!0});return e.exec("PRAGMA query_only = ON"),e.exec("PRAGMA busy_timeout = 3000"),e}function Os(t,e){let n=t.prepare(`
77
77
  SELECT *
78
78
  FROM "session"
79
79
  WHERE id = ?
80
80
  LIMIT 1
81
- `).get(e);if(!n)throw new Error(`OpenCode session not found: ${e}`);return n}function Bt(t,e,n){let r=k(e.directory);if(r&&ve(n.rootPath,r))return!0;let s=k(e.project_id);if(s){try{let o=t.prepare(`
81
+ `).get(e);if(!n)throw new Error(`OpenCode session not found: ${e}`);return n}function zt(t,e,n){let r=k(e.directory);if(r&&ve(n.rootPath,r))return!0;let s=k(e.project_id);if(s){try{let o=t.prepare(`
82
82
  SELECT *
83
83
  FROM "project"
84
84
  WHERE id = ?
85
85
  LIMIT 1
86
- `).get(s);if(o)for(let i of["worktree","directory","path"]){let d=k(o[i]);if(d&&ve(n.rootPath,d))return!0}}catch{}try{if(t.prepare(`
86
+ `).get(s);if(o)for(let i of["worktree","directory","path"]){let l=k(o[i]);if(l&&ve(n.rootPath,l))return!0}}catch{}try{if(t.prepare(`
87
87
  SELECT directory
88
88
  FROM "project_directory"
89
89
  WHERE project_id = ?
@@ -93,7 +93,7 @@ Things an AI agent should always remember.
93
93
  WHERE session_id = ?
94
94
  ORDER BY time_created DESC
95
95
  LIMIT 20
96
- `).all(k(e.id));for(let i of o){let d=Ae(i.data),c=d.path&&typeof d.path=="object"?d.path:{};for(let a of[k(c.cwd),k(c.root)])if(a&&ve(n.rootPath,a))return!0}}catch{}return!1}function Dt(t,e,n,r){let s=`
96
+ `).all(k(e.id));for(let i of o){let l=Ae(i.data),c=l.path&&typeof l.path=="object"?l.path:{};for(let a of[k(c.cwd),k(c.root)])if(a&&ve(n.rootPath,a))return!0}}catch{}return!1}function Ft(t,e,n,r){let s=`
97
97
  SELECT *,
98
98
  COALESCE(
99
99
  time_updated,
@@ -124,7 +124,10 @@ Things an AI agent should always remember.
124
124
  0
125
125
  ) ASC,
126
126
  id ASC
127
- `;return t.prepare(s).all(n,r.time,r.time,r.id)}function Ft(t,e){let n=t[t.length-1];return n?{time:_(n.__clock),id:k(n.id)}:e}function Os(t,e){let n=Ae(e.data),r=k(n.role),s=_(e.__clock),o=k(e.id),i="message";return r==="user"?i="user_prompt":r==="assistant"&&(i="assistant_message"),{clock:s,order:0,event:{type:i,timestamp:W(s),role:r||void 0,sourceEventId:`message:${o}:${s}`,sourceSequence:`${s}:${o}`,data:{messageId:o,...n},provenance:{source:"opencode",sourcePath:t,sourceTable:"message",sourceRowId:o,sourceOffset:`${s}:${o}`}}}}function Ps(t,e){let n=Ae(e.data),r=k(n.type),s=_(e.__clock),o=k(e.id),i=k(e.message_id),d="message_part";return r==="tool"?d="tool_call":r==="snapshot"&&(d="artifact"),{clock:s,order:1,event:{type:d,timestamp:W(s),sourceEventId:`part:${o}:${s}`,sourceSequence:`${s}:${o}`,data:{partId:o,messageId:i,...n},provenance:{source:"opencode",sourcePath:t,sourceTable:"part",sourceRowId:o,sourceOffset:`${s}:${o}`}}}}async function Ne(t){let e=t.dbPath??Kt(),n=Wt(e);try{let r=Es(n,t.nativeSessionId);if(!Bt(n,r,t.project))throw new Error(["OpenCode session does not belong to current ToolNet project.",`Session: ${t.nativeSessionId}`,`Project: ${t.project.rootPath}`,`Session directory: ${k(r.directory)||"unknown"}`].join(" "));let s=new ge({project:t.project,storage:t.storage,agent:"opencode",nativeSessionId:t.nativeSessionId,title:k(r.title)||void 0,metadata:{source:"opencode.db",openCodeProjectId:k(r.project_id)||void 0,directory:k(r.directory)||void 0}}),o=s.status(),i=$t(o.sourceCursors["opencode.message"]),d=$t(o.sourceCursors["opencode.part"]),c=Dt(n,"message",t.nativeSessionId,i),a=Dt(n,"part",t.nativeSessionId,d),p=[];if(o.lastSequence===0){let h=_(r.time_created);p.push({clock:h,order:-1,event:{type:"session_start",timestamp:W(h),sourceEventId:`session:${t.nativeSessionId}:created:${h}`,data:{title:k(r.title)||void 0,directory:k(r.directory)||void 0,openCodeProjectId:k(r.project_id)||void 0},provenance:{source:"opencode",sourcePath:e,sourceTable:"session",sourceRowId:t.nativeSessionId}}})}p.push(...c.map(h=>Os(e,h))),p.push(...a.map(h=>Ps(e,h)));let u=_(r.time_updated)||_(r.time_created);t.compacted&&p.push({clock:u,order:98,event:{type:"session_compact",timestamp:W(u),sourceEventId:`session:${t.nativeSessionId}:compact:${u}`,data:{},provenance:{source:"opencode"}}}),t.error?p.push({clock:u,order:99,event:{type:"error",timestamp:W(u),sourceEventId:`session:${t.nativeSessionId}:error:${u}`,data:{source:"session.error"},provenance:{source:"opencode"}}}):t.idle&&p.push({clock:u,order:100,event:{type:"session_idle",timestamp:W(u),sourceEventId:`session:${t.nativeSessionId}:idle:${u}`,data:{},provenance:{source:"opencode"}}}),p.sort((h,w)=>h.clock-w.clock||h.order-w.order);let m=p.filter(h=>!Ct(h.event.data)).map(h=>({...h,event:{...h.event,data:he(h.event.data)}})),l=s.recordMany(m.map(h=>h.event)),S=Ft(c,i),y=Ft(a,d);if(s.setSourceCursor("opencode.message",Lt(S)),s.setSourceCursor("opencode.part",Lt(y)),t.idle&&m.length>0)try{let h=m.map(C=>JSON.stringify(C.event.data)),w=Nt(h,t.nativeSessionId);s.setSourceCursor("opencode.session.summary",w.summary),s.setSourceCursor("opencode.session.facts_count",w.durableFacts.length),Tt()&&!At()&&s.setSourceCursor("opencode.raw_transcript.archived","local")}catch{}let g=await s.flush();return{nativeSessionId:t.nativeSessionId,importedMessages:c.length,importedParts:a.length,recordedEvents:l.length,eventCount:g.eventCount,chunkCount:g.chunkCount,status:g.status}}finally{n.close()}}async function zt(t){let e=t.dbPath??Kt(),n=Wt(e),r=[];try{let o=n.prepare(`
127
+ `;return t.prepare(s).all(n,r.time,r.time,r.id)}function Kt(t,e){let n=t[t.length-1];return n?{time:_(n.__clock),id:k(n.id)}:e}function Ps(t,e){let n=Ae(e.data),r=k(n.role),s=_(e.__clock),o=k(e.id),i="message";return r==="user"?i="user_prompt":r==="assistant"&&(i="assistant_message"),{clock:s,order:0,event:{type:i,timestamp:W(s),role:r||void 0,sourceEventId:`message:${o}:${s}`,sourceSequence:`${s}:${o}`,data:{messageId:o,...n},provenance:{source:"opencode",sourcePath:t,sourceTable:"message",sourceRowId:o,sourceOffset:`${s}:${o}`}}}}function Cs(t){let e={...t},n=t.state&&typeof t.state=="object"&&!Array.isArray(t.state)?{...t.state}:void 0;if(n){let r=n.output;if(typeof r=="string"){let s=r.replace(/\r\n/g,`
128
+ `),o=500;n.outputSummary=s.length<=o?s:`${s.slice(0,350)}
129
+ ...[ToolNet truncated ${s.length-o} chars]...
130
+ ${s.slice(-150)}`,delete n.output}else r!==void 0&&(n.outputSummary="[non-text tool output omitted]",delete n.output);if(n.input&&typeof n.input=="object"&&!Array.isArray(n.input)){let s={...n.input};for(let[o,i]of Object.entries(s))typeof i=="string"&&i.length>1e3&&(s[o]=`${i.slice(0,1e3)}...[ToolNet truncated]`);n.input=s}e.state=n}return e}function Ts(t,e){let n=Ae(e.data),r=k(n.type),s=_(e.__clock),o=k(e.id),i=k(e.message_id),l="message_part";return r==="tool"?l="tool_call":r==="snapshot"&&(l="artifact"),{clock:s,order:1,event:{type:l,timestamp:W(s),sourceEventId:`part:${o}:${s}`,sourceSequence:`${s}:${o}`,data:{partId:o,messageId:i,...r==="tool"?Cs(n):n},provenance:{source:"opencode",sourcePath:t,sourceTable:"part",sourceRowId:o,sourceOffset:`${s}:${o}`}}}}async function Ne(t){let e=t.dbPath??Wt(),n=Bt(e);try{let r=Os(n,t.nativeSessionId);if(!zt(n,r,t.project))throw new Error(["OpenCode session does not belong to current ToolNet project.",`Session: ${t.nativeSessionId}`,`Project: ${t.project.rootPath}`,`Session directory: ${k(r.directory)||"unknown"}`].join(" "));let s=new ge({project:t.project,storage:t.storage,agent:"opencode",nativeSessionId:t.nativeSessionId,title:k(r.title)||void 0,metadata:{source:"opencode.db",openCodeProjectId:k(r.project_id)||void 0,directory:k(r.directory)||void 0}}),o=s.status(),i=Lt(o.sourceCursors["opencode.message"]),l=Lt(o.sourceCursors["opencode.part"]),c=Ft(n,"message",t.nativeSessionId,i),a=Ft(n,"part",t.nativeSessionId,l),p=[];if(o.lastSequence===0){let y=_(r.time_created);p.push({clock:y,order:-1,event:{type:"session_start",timestamp:W(y),sourceEventId:`session:${t.nativeSessionId}:created:${y}`,data:{title:k(r.title)||void 0,directory:k(r.directory)||void 0,openCodeProjectId:k(r.project_id)||void 0},provenance:{source:"opencode",sourcePath:e,sourceTable:"session",sourceRowId:t.nativeSessionId}}})}p.push(...c.map(y=>Ps(e,y))),p.push(...a.map(y=>Ts(e,y)));let u=_(r.time_updated)||_(r.time_created);t.compacted&&p.push({clock:u,order:98,event:{type:"session_compact",timestamp:W(u),sourceEventId:`session:${t.nativeSessionId}:compact:${u}`,data:{},provenance:{source:"opencode"}}}),t.error?p.push({clock:u,order:99,event:{type:"error",timestamp:W(u),sourceEventId:`session:${t.nativeSessionId}:error:${u}`,data:{source:"session.error"},provenance:{source:"opencode"}}}):t.idle&&p.push({clock:u,order:100,event:{type:"session_idle",timestamp:W(u),sourceEventId:`session:${t.nativeSessionId}:idle:${u}`,data:{},provenance:{source:"opencode"}}}),p.sort((y,w)=>y.clock-w.clock||y.order-w.order);let m=p.filter(y=>!Ct(y.event.data)).map(y=>({...y,event:{...y.event,data:ye(y.event.data)}})),d=s.recordMany(m.map(y=>y.event)),S=Kt(c,i),h=Kt(a,l);if(s.setSourceCursor("opencode.message",Dt(S)),s.setSourceCursor("opencode.part",Dt(h)),t.idle&&m.length>0)try{let y=m.map(C=>JSON.stringify(C.event.data)),w=Nt(y,t.nativeSessionId);s.setSourceCursor("opencode.session.summary",w.summary),s.setSourceCursor("opencode.session.facts_count",w.durableFacts.length),Tt()&&!At()&&s.setSourceCursor("opencode.raw_transcript.archived","local")}catch{}let g=await s.flush();return{nativeSessionId:t.nativeSessionId,importedMessages:c.length,importedParts:a.length,recordedEvents:d.length,eventCount:g.eventCount,chunkCount:g.chunkCount,status:g.status}}finally{n.close()}}async function qt(t){let e=t.dbPath??Wt(),n=Bt(e),r=[];try{let o=n.prepare(`
128
131
  SELECT *
129
132
  FROM "session"
130
133
  ORDER BY
@@ -133,27 +136,171 @@ Things an AI agent should always remember.
133
136
  time_created,
134
137
  0
135
138
  ) DESC
136
- `).all();for(let i of o){if(!Bt(n,i,t.project))continue;let d=k(i.id);if(d&&r.push(d),r.length>=(t.limit??100))break}}finally{n.close()}let s=[];for(let o of r)s.push(await Ne({project:t.project,storage:t.storage,nativeSessionId:o,dbPath:e}));return s}import{existsSync as Cs,mkdirSync as Jt,readFileSync as Ts,writeFileSync as Vt}from"node:fs";import{homedir as $e}from"node:os";import{join as be}from"node:path";var qt="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",_e="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function Rs(){let t=be($e(),".config","opencode","AGENTS.md");Jt(be($e(),".config","opencode"),{recursive:!0});let e=`${qt}
137
- ## ToolNet Memory \u2014 Project Continuity
139
+ `).all();for(let i of o){if(!zt(n,i,t.project))continue;let l=k(i.id);if(l&&r.push(l),r.length>=(t.limit??100))break}}finally{n.close()}let s=[];for(let o of r)s.push(await Ne({project:t.project,storage:t.storage,nativeSessionId:o,dbPath:e}));return s}import{existsSync as Rs,mkdirSync as Vt,readFileSync as Ms,writeFileSync as Ut}from"node:fs";import{homedir as $e}from"node:os";import{join as be}from"node:path";var Jt="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",_e="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function As(){let t=be($e(),".config","opencode","AGENTS.md");Vt(be($e(),".config","opencode"),{recursive:!0});let e=`${Jt}
140
+ ## ToolNet Memory
141
+
142
+ ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
143
+
144
+ Rules:
145
+
146
+ 1. Do not create a ToolNet project automatically.
147
+ 2. If no valid \`.toolnet/project.json\` exists in the current project or an ancestor, ignore ToolNet Memory.
148
+ 3. Normal startup context must stay small and selective.
149
+ 4. Do not automatically run deep recovery commands.
150
+ 5. Use ToolNet MCP or selective retrieval when older project knowledge is actually needed.
151
+ 6. Raw transcripts must not be injected into prompts.
152
+ 7. Current repository evidence has priority over stale memory when they conflict.
153
+
154
+ ${_e}`,n=Rs(t)?Ms(t,"utf8"):"",r=n.indexOf(Jt),s=n.indexOf(_e);return r>=0&&s>=r?n=n.slice(0,r)+e+n.slice(s+_e.length):(n=n.trimEnd(),n&&(n+=`
155
+
156
+ `),n+=e),Ut(t,n.trimEnd()+`
157
+ `,{encoding:"utf8",mode:384}),t}function Ht(t={}){let e=t.directory??be($e(),".config","opencode","plugins");Vt(e,{recursive:!0}),As();let n=be(e,"toolnet-memory.js"),r=t.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",s=`
158
+ // Generated by ToolNet Memory.
159
+ // OpenCode integration:
160
+ // - project gated
161
+ // - selective context injection
162
+ // - incremental session checkpoints
163
+ // - periodic durability sync
164
+ // - best-effort shutdown flush
165
+
166
+ import fs from "node:fs"
167
+ import path from "node:path"
168
+ import os from "node:os"
169
+
170
+ const TOOLNET_BINARY =
171
+ ${JSON.stringify(r)}
172
+
173
+ const CONTEXT_MAX_TOKENS = 700
174
+
175
+ const CONTEXT_CACHE_MS = 5000
176
+
177
+ const PERIODIC_SYNC_MS = 30000
178
+
179
+ const SYNC_TIMEOUT_MS = 15000
180
+
181
+ const STATUS_FILE = path.join(
182
+ os.homedir(),
183
+ ".config",
184
+ "toolnet-memory",
185
+ "opencode-sync-status.json"
186
+ )
187
+
188
+ function writeStatus(data) {
189
+ try {
190
+ fs.mkdirSync(
191
+ path.dirname(
192
+ STATUS_FILE
193
+ ),
194
+ {
195
+ recursive: true,
196
+ }
197
+ )
198
+
199
+ fs.writeFileSync(
200
+ STATUS_FILE,
201
+ JSON.stringify(
202
+ {
203
+ timestamp:
204
+ new Date()
205
+ .toISOString(),
206
+ ...data,
207
+ },
208
+ null,
209
+ 2
210
+ ) + "\\n"
211
+ )
212
+ } catch {
213
+ // Status reporting must
214
+ // never break OpenCode.
215
+ }
216
+ }
217
+
218
+ function validProjectManifest(
219
+ file
220
+ ) {
221
+ try {
222
+ const parsed =
223
+ JSON.parse(
224
+ fs.readFileSync(
225
+ file,
226
+ "utf8"
227
+ )
228
+ )
229
+
230
+ return Boolean(
231
+ parsed &&
232
+ typeof parsed ===
233
+ "object" &&
234
+ typeof parsed.id ===
235
+ "string" &&
236
+ parsed.id.length > 0
237
+ )
238
+ } catch {
239
+ return false
240
+ }
241
+ }
242
+
243
+ function findProjectRoot(
244
+ inputDirectory
245
+ ) {
246
+ let current =
247
+ path.resolve(
248
+ inputDirectory
249
+ )
250
+
251
+ const filesystemRoot =
252
+ path.parse(
253
+ current
254
+ ).root
255
+
256
+ while (true) {
257
+ const manifest =
258
+ path.join(
259
+ current,
260
+ ".toolnet",
261
+ "project.json"
262
+ )
263
+
264
+ if (
265
+ fs.existsSync(
266
+ manifest
267
+ ) &&
268
+ validProjectManifest(
269
+ manifest
270
+ )
271
+ ) {
272
+ return current
273
+ }
274
+
275
+ if (
276
+ current ===
277
+ filesystemRoot
278
+ ) {
279
+ break
280
+ }
138
281
 
139
- When the current workspace belongs to a ToolNet project, identified by a nearest ancestor containing \`.toolnet/project.json\`:
282
+ const parent =
283
+ path.dirname(
284
+ current
285
+ )
140
286
 
141
- 1. Before planning, editing, or running implementation commands, read that project root's \`.toolnet/context/startup.md\`.
142
- 2. Treat its PROJECT RULES, MISSION, CURRENT OBJECTIVE, rationale, current phase/task, Definition of Done, blockers and next actions as project continuity context.
143
- 3. Do not restart work already marked completed unless repository evidence proves it must be revisited.
144
- 4. Verify current repository state before continuing old work.
145
- 5. If \`.toolnet/context/startup.md\` is missing, run \`toolnet-memory context:sync --project <project-root>\`, then read it.
146
- 6. Never invent missing rationale. If ToolNet says a reason was not explicitly recorded, inspect the implementation or ask rather than guessing.
287
+ if (
288
+ parent === current
289
+ ) {
290
+ break
291
+ }
147
292
 
148
- This applies automatically to every ToolNet-managed project.
149
- ${_e}`,n=Cs(t)?Ts(t,"utf8"):"",r=n.indexOf(qt),s=n.indexOf(_e);return r>=0&&s>=r?n=n.slice(0,r)+e+n.slice(s+_e.length):(n=n.trimEnd(),n&&(n+=`
293
+ current = parent
294
+ }
150
295
 
151
- `),n+=e),Vt(t,n.trimEnd()+`
152
- `,{encoding:"utf8",mode:384}),t}function Ut(t={}){let e=t.directory??be($e(),".config","opencode","plugins");Jt(e,{recursive:!0}),Rs();let n=be(e,"toolnet-memory.js"),r=t.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",s=`// Generated by ToolNet Memory.
153
- // OpenCode session capture + continuity bootstrap.
296
+ return null
297
+ }
154
298
 
155
- function getSessionId(event) {
156
- const p = event?.properties ?? {}
299
+ function getSessionId(
300
+ event
301
+ ) {
302
+ const p =
303
+ event?.properties ?? {}
157
304
 
158
305
  const candidates = [
159
306
  p.sessionID,
@@ -162,164 +309,493 @@ function getSessionId(event) {
162
309
  p.session?.id,
163
310
  ]
164
311
 
165
- return candidates.find(
166
- value => typeof value === "string" && value.length > 0
167
- ) ?? null
312
+ return (
313
+ candidates.find(
314
+ value =>
315
+ typeof value ===
316
+ "string" &&
317
+ value.length > 0
318
+ ) ?? null
319
+ )
168
320
  }
169
321
 
170
- export const ToolNetMemoryPlugin = async ({ directory }) => {
171
- const binary = ${JSON.stringify(r)}
172
-
173
- async function refreshContext() {
174
- try {
175
- const child = Bun.spawn(
176
- [
177
- binary,
178
- "context:sync",
179
- "--project",
180
- directory,
181
- ],
182
- {
183
- stdin: "ignore",
184
- stdout: "ignore",
185
- stderr: "ignore",
186
- }
322
+ async function runWithTimeout(
323
+ args,
324
+ {
325
+ stdout = "ignore",
326
+ timeout =
327
+ SYNC_TIMEOUT_MS,
328
+ } = {}
329
+ ) {
330
+ const child =
331
+ Bun.spawn(
332
+ args,
333
+ {
334
+ stdin:
335
+ "ignore",
336
+ stdout,
337
+ stderr:
338
+ "ignore",
339
+ }
340
+ )
341
+
342
+ let timer
343
+
344
+ const timeoutPromise =
345
+ new Promise(
346
+ (_, reject) => {
347
+ timer =
348
+ setTimeout(
349
+ () => {
350
+ try {
351
+ child.kill()
352
+ } catch {}
353
+
354
+ reject(
355
+ new Error(
356
+ "ToolNet command timeout"
357
+ )
358
+ )
359
+ },
360
+ timeout
361
+ )
362
+ }
363
+ )
364
+
365
+ try {
366
+ const exitCode =
367
+ await Promise.race([
368
+ child.exited,
369
+ timeoutPromise,
370
+ ])
371
+
372
+ if (
373
+ typeof exitCode ===
374
+ "number" &&
375
+ exitCode !== 0
376
+ ) {
377
+ throw new Error(
378
+ \`ToolNet exited with code \${exitCode}\`
187
379
  )
380
+ }
188
381
 
189
- await child.exited
190
- } catch {
191
- // Context sync must never break OpenCode startup.
382
+ return child
383
+ } finally {
384
+ if (timer) {
385
+ clearTimeout(
386
+ timer
387
+ )
192
388
  }
193
389
  }
390
+ }
194
391
 
195
- async function readContext() {
196
- try {
197
- const child = Bun.spawn(
198
- [
199
- binary,
200
- "context:print",
201
- "--project",
202
- directory,
203
- "--tokens",
204
- "900",
205
- ],
206
- {
207
- stdin: "ignore",
208
- stdout: "pipe",
209
- stderr: "ignore",
210
- }
392
+ export const ToolNetMemoryPlugin =
393
+ async ({
394
+ directory,
395
+ }) => {
396
+ /*
397
+ * CRITICAL:
398
+ * Never auto-create a ToolNet
399
+ * project just because OpenCode
400
+ * happens to start in /root,
401
+ * /tmp, $HOME, etc.
402
+ */
403
+ const projectRoot =
404
+ findProjectRoot(
405
+ directory
211
406
  )
212
407
 
213
- const text =
214
- await new Response(
215
- child.stdout
216
- ).text()
217
-
218
- await child.exited
408
+ if (!projectRoot) {
409
+ writeStatus({
410
+ active: false,
411
+ reason:
412
+ "no-toolnet-project",
413
+ directory,
414
+ })
219
415
 
220
- return text.trim()
221
- } catch {
222
- return ""
416
+ return {}
223
417
  }
224
- }
225
418
 
226
- function sync(sessionId, flag) {
227
- if (!sessionId) return
419
+ writeStatus({
420
+ active: true,
421
+ projectRoot,
422
+ state:
423
+ "plugin-loaded",
424
+ })
228
425
 
229
- try {
230
- const args = [
231
- binary,
232
- "session:opencode-sync",
233
- sessionId,
234
- "--project",
235
- directory,
236
- ]
426
+ let lastSessionId =
427
+ null
237
428
 
238
- if (flag) {
239
- args.push(flag)
240
- }
429
+ let syncChain =
430
+ Promise.resolve()
241
431
 
242
- const child = Bun.spawn(args, {
243
- stdin: "ignore",
244
- stdout: "ignore",
245
- stderr: "ignore",
246
- })
432
+ let contextCache = {
433
+ value: "",
434
+ expiresAt: 0,
435
+ }
436
+
437
+ async function readContext() {
438
+ const now =
439
+ Date.now()
247
440
 
248
441
  if (
249
- typeof child.unref === "function"
442
+ contextCache.value &&
443
+ now <
444
+ contextCache.expiresAt
250
445
  ) {
251
- child.unref()
446
+ return (
447
+ contextCache.value
448
+ )
252
449
  }
253
- } catch {
254
- // Memory capture must never break OpenCode.
255
- }
256
- }
257
450
 
258
- /*
259
- * Pull the latest project brief from ToolNet remote storage
260
- * before the new OpenCode session starts doing work.
261
- */
262
- await refreshContext()
451
+ try {
452
+ const child =
453
+ Bun.spawn(
454
+ [
455
+ TOOLNET_BINARY,
456
+ "context:print",
457
+ "--project",
458
+ projectRoot,
459
+ "--tokens",
460
+ String(
461
+ CONTEXT_MAX_TOKENS
462
+ ),
463
+ ],
464
+ {
465
+ stdin:
466
+ "ignore",
467
+ stdout:
468
+ "pipe",
469
+ stderr:
470
+ "ignore",
471
+ }
472
+ )
473
+
474
+ const textPromise =
475
+ new Response(
476
+ child.stdout
477
+ ).text()
478
+
479
+ const exitCode =
480
+ await child.exited
481
+
482
+ if (
483
+ exitCode !== 0
484
+ ) {
485
+ return ""
486
+ }
487
+
488
+ const text =
489
+ (
490
+ await textPromise
491
+ ).trim()
263
492
 
264
- return {
265
- event: async ({ event }) => {
266
- const sessionId =
267
- getSessionId(event)
493
+ contextCache = {
494
+ value: text,
495
+ expiresAt:
496
+ now +
497
+ CONTEXT_CACHE_MS,
498
+ }
499
+
500
+ return text
501
+ } catch {
502
+ return ""
503
+ }
504
+ }
268
505
 
506
+ async function syncNow(
507
+ sessionId,
508
+ flag,
509
+ reason
510
+ ) {
269
511
  if (!sessionId) {
270
512
  return
271
513
  }
272
514
 
273
- if (event.type === "session.idle") {
274
- sync(sessionId, "--idle")
275
- return
515
+ const args = [
516
+ TOOLNET_BINARY,
517
+ "session:opencode-sync",
518
+ sessionId,
519
+ "--project",
520
+ projectRoot,
521
+ ]
522
+
523
+ if (flag) {
524
+ args.push(
525
+ flag
526
+ )
276
527
  }
277
528
 
278
- if (event.type === "session.compacted") {
279
- sync(sessionId, "--compacted")
280
- return
529
+ try {
530
+ await runWithTimeout(
531
+ args
532
+ )
533
+
534
+ writeStatus({
535
+ active: true,
536
+ projectRoot,
537
+ sessionId,
538
+ reason,
539
+ state:
540
+ "sync-success",
541
+ })
542
+ } catch (error) {
543
+ writeStatus({
544
+ active: true,
545
+ projectRoot,
546
+ sessionId,
547
+ reason,
548
+ state:
549
+ "sync-failed",
550
+ error:
551
+ error instanceof
552
+ Error
553
+ ? error.message
554
+ : String(
555
+ error
556
+ ),
557
+ })
558
+
559
+ throw error
281
560
  }
561
+ }
282
562
 
283
- if (event.type === "session.error") {
284
- sync(sessionId, "--error")
563
+ function queueSync(
564
+ sessionId,
565
+ flag,
566
+ reason
567
+ ) {
568
+ if (!sessionId) {
569
+ return syncChain
285
570
  }
286
- },
571
+
572
+ lastSessionId =
573
+ sessionId
574
+
575
+ syncChain =
576
+ syncChain
577
+ .catch(
578
+ () =>
579
+ undefined
580
+ )
581
+ .then(
582
+ () =>
583
+ syncNow(
584
+ sessionId,
585
+ flag,
586
+ reason
587
+ )
588
+ )
589
+
590
+ return syncChain
591
+ }
287
592
 
288
593
  /*
289
- * Official OpenCode compaction hook.
290
- * Preserve ToolNet's semantic state after context compaction.
594
+ * Periodic durability checkpoint.
595
+ *
596
+ * This is intentionally independent
597
+ * from session.idle/dispose because
598
+ * abrupt SSH/process termination may
599
+ * skip shutdown hooks.
600
+ *
601
+ * unref() prevents the timer itself
602
+ * from keeping OpenCode alive.
291
603
  */
292
- "experimental.session.compacting": async (_input, output) => {
293
- const context =
294
- await readContext()
604
+ const periodic =
605
+ setInterval(
606
+ () => {
607
+ if (
608
+ lastSessionId
609
+ ) {
610
+ void queueSync(
611
+ lastSessionId,
612
+ null,
613
+ "periodic"
614
+ )
615
+ }
616
+ },
617
+ PERIODIC_SYNC_MS
618
+ )
295
619
 
296
- if (
297
- context &&
298
- Array.isArray(
299
- output.context
300
- )
301
- ) {
302
- output.context.push(
303
- context
620
+ if (
621
+ typeof periodic.unref ===
622
+ "function"
623
+ ) {
624
+ periodic.unref()
625
+ }
626
+
627
+ return {
628
+ event: async ({
629
+ event,
630
+ }) => {
631
+ const sessionId =
632
+ getSessionId(
633
+ event
634
+ )
635
+
636
+ if (sessionId) {
637
+ lastSessionId =
638
+ sessionId
639
+ }
640
+
641
+ if (
642
+ event.type ===
643
+ "session.idle"
644
+ ) {
645
+ await queueSync(
646
+ sessionId,
647
+ "--idle",
648
+ "session.idle"
649
+ )
650
+
651
+ return
652
+ }
653
+
654
+ if (
655
+ event.type ===
656
+ "session.compacted"
657
+ ) {
658
+ await queueSync(
659
+ sessionId,
660
+ "--compacted",
661
+ "session.compacted"
662
+ )
663
+
664
+ contextCache = {
665
+ value: "",
666
+ expiresAt: 0,
667
+ }
668
+
669
+ return
670
+ }
671
+
672
+ if (
673
+ event.type ===
674
+ "session.error"
675
+ ) {
676
+ await queueSync(
677
+ sessionId,
678
+ "--error",
679
+ "session.error"
680
+ )
681
+
682
+ return
683
+ }
684
+ },
685
+
686
+ /*
687
+ * VERIFIED on OpenCode 1.18.14:
688
+ * this hook fires on real model turns.
689
+ *
690
+ * It remains experimental, therefore
691
+ * AGENTS.md + MCP stay as fallbacks.
692
+ */
693
+ "experimental.chat.system.transform":
694
+ async (
695
+ _input,
696
+ output
697
+ ) => {
698
+ const context =
699
+ await readContext()
700
+
701
+ if (!context) {
702
+ return
703
+ }
704
+
705
+ if (
706
+ Array.isArray(
707
+ output?.system
708
+ )
709
+ ) {
710
+ output.system.push(
711
+ context
712
+ )
713
+
714
+ return
715
+ }
716
+
717
+ if (
718
+ typeof output?.system ===
719
+ "string"
720
+ ) {
721
+ output.system =
722
+ output.system
723
+ ? \`\${output.system}\\n\\n\${context}\`
724
+ : context
725
+ }
726
+ },
727
+
728
+ /*
729
+ * Optional compaction survival hook.
730
+ * We keep it as a supplementary path,
731
+ * never as the only continuity path.
732
+ */
733
+ "experimental.session.compacting":
734
+ async (
735
+ _input,
736
+ output
737
+ ) => {
738
+ const context =
739
+ await readContext()
740
+
741
+ if (
742
+ context &&
743
+ Array.isArray(
744
+ output?.context
745
+ )
746
+ ) {
747
+ output.context.push(
748
+ context
749
+ )
750
+ }
751
+ },
752
+
753
+ /*
754
+ * VERIFIED for clean OpenCode exits.
755
+ * Best effort only: kill -9 / crash /
756
+ * machine loss cannot guarantee this.
757
+ */
758
+ dispose: async () => {
759
+ clearInterval(
760
+ periodic
304
761
  )
305
- }
306
- },
762
+
763
+ if (
764
+ lastSessionId
765
+ ) {
766
+ try {
767
+ await queueSync(
768
+ lastSessionId,
769
+ "--idle",
770
+ "dispose"
771
+ )
772
+ } catch {
773
+ // Status already recorded.
774
+ }
775
+ }
776
+
777
+ try {
778
+ await syncChain
779
+ } catch {
780
+ // Do not block OpenCode shutdown.
781
+ }
782
+ },
783
+ }
307
784
  }
308
- }
309
- `;return Vt(n,s,{encoding:"utf8",mode:384}),n}import{existsSync as Ic,mkdirSync as _s,readFileSync as Ec,writeFileSync as $s}from"node:fs";import{dirname as Ls,join as Yt}from"node:path";function Le(t){if(!t)return 0;let e=Array.from(t).length,n=t.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,n*1.3))}function b(t,e){let n=t.replace(/\s+/g," ").trim();return n.length<=e?n:n.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function Ms(t){let e=[],n=!1;for(let r of t.split(/\r?\n/u)){let s=r.trim();if(s.includes("<!--")&&(n=!0),n){s.includes("-->")&&(n=!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(b(s,280)),e.length>=16))break}return e}function As(t){let e=[],n=[];for(let r of t.split(/\\r?\\n/u)){let s=r.trim(),o=s.toLowerCase(),d=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(a=>o.startsWith(a));if(!d)continue;let c=s.slice(d.length).trim();c&&(d.includes("enforce")?e.push(c):n.push(c))}return{enforce:e,advisory:n}}function Ns(t,e){let n=[];for(let r of t){let s=[...n,r].join(`
785
+ `;return Ut(n,s.trimStart(),{encoding:"utf8",mode:384}),n}import{existsSync as Rc,mkdirSync as Ls,readFileSync as Mc,writeFileSync as Ds}from"node:fs";import{dirname as Fs,join as Gt}from"node:path";function Le(t){if(!t)return 0;let e=Array.from(t).length,n=t.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,n*1.3))}function b(t,e){let n=t.replace(/\s+/g," ").trim();return n.length<=e?n:n.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function Ns(t){let e=[],n=!1;for(let r of t.split(/\r?\n/u)){let s=r.trim();if(s.includes("<!--")&&(n=!0),n){s.includes("-->")&&(n=!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(b(s,280)),e.length>=16))break}return e}function _s(t){let e=[],n=[];for(let r of t.split(/\\r?\\n/u)){let s=r.trim(),o=s.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(a=>o.startsWith(a));if(!l)continue;let c=s.slice(l.length).trim();c&&(l.includes("enforce")?e.push(c):n.push(c))}return{enforce:e,advisory:n}}function $s(t,e){let n=[];for(let r of t){let s=[...n,r].join(`
310
786
  `);if(Le(s)<=e){n.push(r);continue}let o=Le(n.join(`
311
- `)),i=Math.max(0,e-o);if(i>=16){let d=Math.floor(i*3.2),c=b(r,d);c&&n.push(c)}break}return n.join(`
312
- `).trim()}async function Ht(t){let e=Math.max(256,Math.min(2e3,t.maxTokens??1e3)),n=fe(t.project,!1),r=n?.content??"";r||(r=await t.storage.getText(`projects/${t.project.id}/project/manual.md`)??"");let s=As(r),o=n?n.rules.filter(l=>l.mode==="enforce").map(l=>l.text):s.enforce,i=n?n.rules.filter(l=>l.mode==="advisory").map(l=>l.text):s.advisory,d=r?Ms(r):[],c=await ce(t.project,t.storage),a=await xt(t.project,t.storage),p=await Pt(t.project,t.storage),u=[];if(u.push("[TOOLNET PROJECT CONTEXT]"),u.push(`Project: ${t.project.name}`),u.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),r&&u.push(`Full operating manual: ${pe(t.project)}`),o.length){u.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let l of o.slice(0,24))u.push(`- [ENFORCE] ${b(l,240)}`)}if(i.length){u.push("","PROJECT PREFERENCES");for(let l of i.slice(0,10))u.push(`- ${b(l,220)}`)}if(a&&(a.mission&&u.push("","MISSION",b(a.mission.value,420)),a.activeObjective&&u.push("","CURRENT OBJECTIVE",b(a.activeObjective.value,420)),a.why&&u.push("","WHY THIS WORK MATTERS",b(a.why.value,420)),a.desiredOutcome&&u.push("","DESIRED OUTCOME",b(a.desiredOutcome.value,420)),a.planRationale&&u.push("","WHY THIS APPROACH",b(a.planRationale.value,420))),c){if(u.push("","ACTIVE WORK"),c.goal&&u.push(`Goal: ${b(c.goal,320)}`),c.plan&&u.push(`Plan: ${b(c.plan,320)}`),u.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&u.push(`Current phase: ${c.currentPhase.title} [${c.currentPhase.status}]`),c.currentPhase&&a){let l=a.phases.find(S=>S.order===c.currentPhase?.order);l&&(l.objective&&u.push(`Phase objective: ${b(l.objective.value,340)}`),l.why?u.push(`Why this phase: ${b(l.why.value,340)}`):u.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),l.deliverable&&u.push(`Deliverable: ${b(l.deliverable.value,340)}`),l.dependencies.length&&u.push(`Depends on: ${l.dependencies.slice(0,4).map(S=>b(S.value,180)).join("; ")}`),l.acceptanceCriteria.length&&(u.push("","DEFINITION OF DONE"),l.acceptanceCriteria.slice(0,6).forEach(S=>{u.push(`- ${b(S.value,260)}`)})),l.openQuestions.length&&(u.push("","OPEN QUESTIONS FOR CURRENT PHASE"),l.openQuestions.slice(0,4).forEach(S=>{u.push(`- ${b(S.value,260)}`)})))}c.currentTask&&u.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(u.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((l,S)=>{u.push(`${S+1}. ${b(l,260)}`)})),c.blockers.length&&(u.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(l=>{u.push(`- ${b(l,260)}`)})),c.warnings.length&&(u.push("","ATTENTION"),c.warnings.slice(-5).forEach(l=>{u.push(`- ${b(l,260)}`)})),c.decisions.length&&(u.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(l=>{u.push(`- ${b(l,260)}`)})),c.lastSession&&u.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(a&&a.openQuestions.length&&(u.push("","UNRESOLVED QUESTIONS"),a.openQuestions.slice(0,5).forEach(l=>{u.push(`- ${b(l.value,260)}`)})),p&&u.push(`Latest handoff: ${p.reason} / ${p.sourceSession.agent}`),d.length){u.push("","OPERATING NOTES");for(let l of d)u.push(`- ${l}`)}u.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let m=Ns(u,e);return{version:1,projectId:t.project.id,projectName:t.project.name,text:m,estimatedTokens:Le(m),maxTokens:e,hasManual:!!r,hasWorkState:!!c,hasHandoff:!!p,generatedAt:new Date().toISOString()}}function Ds(t){return Yt(t.rootPath,".toolnet","context","startup.md")}function Fs(t){return Yt(t.rootPath,".toolnet","context","startup.json")}function Ks(t,e){let n=Ds(t);_s(Ls(n),{recursive:!0}),$s(n,e.text.endsWith(`
787
+ `)),i=Math.max(0,e-o);if(i>=16){let l=Math.floor(i*3.2),c=b(r,l);c&&n.push(c)}break}return n.join(`
788
+ `).trim()}async function Yt(t){let e=Math.max(256,Math.min(2e3,t.maxTokens??1e3)),n=fe(t.project,!1),r=n?.content??"";r||(r=await t.storage.getText(`projects/${t.project.id}/project/manual.md`)??"");let s=_s(r),o=n?n.rules.filter(d=>d.mode==="enforce").map(d=>d.text):s.enforce,i=n?n.rules.filter(d=>d.mode==="advisory").map(d=>d.text):s.advisory,l=r?Ns(r):[],c=await ce(t.project,t.storage),a=await xt(t.project,t.storage),p=await Pt(t.project,t.storage),u=[];if(u.push("[TOOLNET PROJECT CONTEXT]"),u.push(`Project: ${t.project.name}`),u.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),r&&u.push(`Full operating manual: ${pe(t.project)}`),o.length){u.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let d of o.slice(0,24))u.push(`- [ENFORCE] ${b(d,240)}`)}if(i.length){u.push("","PROJECT PREFERENCES");for(let d of i.slice(0,10))u.push(`- ${b(d,220)}`)}if(a&&(a.mission&&u.push("","MISSION",b(a.mission.value,420)),a.activeObjective&&u.push("","CURRENT OBJECTIVE",b(a.activeObjective.value,420)),a.why&&u.push("","WHY THIS WORK MATTERS",b(a.why.value,420)),a.desiredOutcome&&u.push("","DESIRED OUTCOME",b(a.desiredOutcome.value,420)),a.planRationale&&u.push("","WHY THIS APPROACH",b(a.planRationale.value,420))),c){if(u.push("","ACTIVE WORK"),c.goal&&u.push(`Goal: ${b(c.goal,320)}`),c.plan&&u.push(`Plan: ${b(c.plan,320)}`),u.push(`Progress: phases ${c.progress.phasesCompleted}/${c.progress.phasesTotal}; tasks ${c.progress.tasksCompleted}/${c.progress.tasksTotal}; blocked ${c.progress.blocked}`),c.currentPhase&&u.push(`Current phase: ${c.currentPhase.title} [${c.currentPhase.status}]`),c.currentPhase&&a){let d=a.phases.find(S=>S.order===c.currentPhase?.order);d&&(d.objective&&u.push(`Phase objective: ${b(d.objective.value,340)}`),d.why?u.push(`Why this phase: ${b(d.why.value,340)}`):u.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),d.deliverable&&u.push(`Deliverable: ${b(d.deliverable.value,340)}`),d.dependencies.length&&u.push(`Depends on: ${d.dependencies.slice(0,4).map(S=>b(S.value,180)).join("; ")}`),d.acceptanceCriteria.length&&(u.push("","DEFINITION OF DONE"),d.acceptanceCriteria.slice(0,6).forEach(S=>{u.push(`- ${b(S.value,260)}`)})),d.openQuestions.length&&(u.push("","OPEN QUESTIONS FOR CURRENT PHASE"),d.openQuestions.slice(0,4).forEach(S=>{u.push(`- ${b(S.value,260)}`)})))}c.currentTask&&u.push(`Current task: ${c.currentTask.title} [${c.currentTask.status}]`),c.nextActions.length&&(u.push("","NEXT ACTIONS"),c.nextActions.slice(0,6).forEach((d,S)=>{u.push(`${S+1}. ${b(d,260)}`)})),c.blockers.length&&(u.push("","BLOCKERS"),c.blockers.slice(0,5).forEach(d=>{u.push(`- ${b(d,260)}`)})),c.warnings.length&&(u.push("","ATTENTION"),c.warnings.slice(-5).forEach(d=>{u.push(`- ${b(d,260)}`)})),c.decisions.length&&(u.push("","RECENT DECISIONS"),c.decisions.slice(-5).forEach(d=>{u.push(`- ${b(d,260)}`)})),c.lastSession&&u.push("",`Last work session: ${c.lastSession.agent} / ${c.lastSession.nativeSessionId}`)}if(a&&a.openQuestions.length&&(u.push("","UNRESOLVED QUESTIONS"),a.openQuestions.slice(0,5).forEach(d=>{u.push(`- ${b(d.value,260)}`)})),p&&u.push(`Latest handoff: ${p.reason} / ${p.sourceSession.agent}`),l.length){u.push("","OPERATING NOTES");for(let d of l)u.push(`- ${d}`)}u.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let m=$s(u,e);return{version:1,projectId:t.project.id,projectName:t.project.name,text:m,estimatedTokens:Le(m),maxTokens:e,hasManual:!!r,hasWorkState:!!c,hasHandoff:!!p,generatedAt:new Date().toISOString()}}function Ks(t){return Gt(t.rootPath,".toolnet","context","startup.md")}function Ws(t){return Gt(t.rootPath,".toolnet","context","startup.json")}function Bs(t,e){let n=Ks(t);Ls(Fs(n),{recursive:!0}),Ds(n,e.text.endsWith(`
313
789
  `)?e.text:e.text+`
314
- `,{encoding:"utf8",mode:384}),I(Fs(t),e)}async function Gt(t,e,n=800){let s=(await Ht({project:t,storage:e,maxTokens:n})).text;ye(s)>n&&(s=Se(s,n),s+=`
790
+ `,{encoding:"utf8",mode:384}),I(Ws(t),e)}async function Xt(t,e,n=800){let s=(await Yt({project:t,storage:e,maxTokens:n})).text;he(s)>n&&(s=Se(s,n),s+=`
315
791
 
316
792
  [Context trimmed by ToolNet Memory token budget]
317
- `);let i={version:1,projectId:t.id,projectName:t.name,text:s,digest:v(s),estimatedTokens:ye(s),generatedAt:new Date().toISOString()};return Ks(t,i),await e.put(`projects/${t.id}/context/startup.md`,i.text+`
793
+ `);let i={version:1,projectId:t.id,projectName:t.name,text:s,digest:v(s),estimatedTokens:he(s),generatedAt:new Date().toISOString()};return Bs(t,i),await e.put(`projects/${t.id}/context/startup.md`,i.text+`
318
794
  `,"text/markdown"),await e.put(`projects/${t.id}/context/startup.json`,JSON.stringify(i,null,2)+`
319
- `,"application/json"),i}function ke(t,e){let n=t.indexOf(e);if(!(n<0))return t[n+1]}function De(t,e){return t.includes(e)}function Ws(t){let e=H(),n=Ge(He({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new ee(n,t.id,t.name,t.remote??t.name)}async function Bs(){let[t="help",...e]=process.argv.slice(2);if(t==="install-plugin"){let i=Ut({binary:ke(e,"--bin")});console.log(`\u2705 OpenCode plugin installed: ${i}`),console.log("OpenCode will load it automatically on next start.");return}let n=ke(e,"--project")??process.cwd(),r=new G().detect(n),s=Ws(r),o=ke(e,"--db");if(t==="sync"){let i=e.find(u=>!u.startsWith("--")&&u!==n&&u!==o);if(!i)throw new Error("Usage: session:opencode-sync <session-id>");let d=De(e,"--idle"),c=De(e,"--error"),a=De(e,"--compacted"),p=await Ne({project:r,storage:s,nativeSessionId:i,dbPath:o,idle:d,error:c,compacted:a});if(d||a||c)try{await Gt(r,s,800)}catch{}console.log(JSON.stringify(p,null,2));return}if(t==="recover"){let i=ke(e,"--limit"),d=i?Number(i):100,c=await zt({project:r,storage:s,dbPath:o,limit:Number.isFinite(d)?d:100});console.log(JSON.stringify({project:r.name,sessions:c.length,importedMessages:c.reduce((a,p)=>a+p.importedMessages,0),importedParts:c.reduce((a,p)=>a+p.importedParts,0)},null,2));return}console.log(`OpenCode Session Adapter
795
+ `,"application/json"),i}function ke(t,e){let n=t.indexOf(e);if(!(n<0))return t[n+1]}function De(t,e){return t.includes(e)}function zs(t){let e=H(),n=Ge(He({provider:e.storage.provider,huggingface:e.storage.huggingface,localRoot:e.storage.localRoot}),{attempts:3});return new ee(n,t.id,t.name,t.remote??t.name)}async function qs(){let[t="help",...e]=process.argv.slice(2);if(t==="install-plugin"){let i=Ht({binary:ke(e,"--bin")});console.log(`\u2705 OpenCode plugin installed: ${i}`),console.log("OpenCode will load it automatically on next start.");return}let n=ke(e,"--project")??process.cwd(),r=new G().detect(n),s=zs(r),o=ke(e,"--db");if(t==="sync"){let i=e.find(u=>!u.startsWith("--")&&u!==n&&u!==o);if(!i)throw new Error("Usage: session:opencode-sync <session-id>");let l=De(e,"--idle"),c=De(e,"--error"),a=De(e,"--compacted"),p=await Ne({project:r,storage:s,nativeSessionId:i,dbPath:o,idle:l,error:c,compacted:a});if(l||a||c)try{await Xt(r,s,800)}catch{}console.log(JSON.stringify(p,null,2));return}if(t==="recover"){let i=ke(e,"--limit"),l=i?Number(i):100,c=await qt({project:r,storage:s,dbPath:o,limit:Number.isFinite(l)?l:100});console.log(JSON.stringify({project:r.name,sessions:c.length,importedMessages:c.reduce((a,p)=>a+p.importedMessages,0),importedParts:c.reduce((a,p)=>a+p.importedParts,0)},null,2));return}console.log(`OpenCode Session Adapter
320
796
 
321
797
  Commands:
322
798
  sync <session-id> [--project PATH] [--idle]
323
799
  recover [--project PATH] [--limit N]
324
800
  install-plugin [--bin PATH]
325
- `)}Bs().catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1)});
801
+ `)}qs().catch(t=>{console.error(t instanceof Error?t.message:t),process.exit(1)});