toolnet-memory 0.2.4 → 0.2.6

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.
package/.env.example CHANGED
@@ -26,3 +26,12 @@ MEMORY_MAX_CANDIDATES=50
26
26
  MEMORY_RERANK_TOP=10
27
27
  MEMORY_FINAL_CONTEXT=5
28
28
  MEMORY_TOKEN_BUDGET=2000
29
+
30
+ # Automatic Session Memory
31
+ TOOLNET_SESSION_LEARNING=1
32
+ TOOLNET_WORK_CONTINUITY=1
33
+ TOOLNET_SEMANTIC_CONTINUITY=1
34
+ TOOLNET_SMART_HANDOFF=1
35
+
36
+ # Recommended embedding model
37
+ HF_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
@@ -146,6 +146,11 @@ case "$COMMAND" in
146
146
  exit 1
147
147
  ;;
148
148
 
149
+ integrate:auto)
150
+ shift
151
+ run bundle/auto-integrate.js src/production/auto-integrate.ts "$@"
152
+ ;;
153
+
149
154
  integrate:opencode)
150
155
  shift
151
156
 
@@ -164,11 +169,11 @@ case "$COMMAND" in
164
169
  session:agy-hook)
165
170
  shift
166
171
 
167
- if [ -f "$ROOT/dist/src/session/agy/hook.js" ]; then
168
- exec node "$ROOT/dist/src/session/agy/hook.js" "$@"
172
+ if [ -f "$ROOT/bundle/agy-hook.js" ]; then
173
+ exec node "$ROOT/bundle/agy-hook.js" "$@"
169
174
  fi
170
175
 
171
- if [ -x "${TSX:-}" ]; then
176
+ if [ -x "${TSX:-}" ] && [ -f "$ROOT/src/session/agy/hook.ts" ]; then
172
177
  exec "$TSX" "$ROOT/src/session/agy/hook.ts" "$@"
173
178
  fi
174
179
 
@@ -387,11 +392,11 @@ case "$COMMAND" in
387
392
  session:codex-context)
388
393
  shift
389
394
 
390
- if [ -f "$ROOT/dist/src/session/codex/context-hook.js" ]; then
391
- exec node "$ROOT/dist/src/session/codex/context-hook.js" "$@"
395
+ if [ -f "$ROOT/bundle/codex-context.js" ]; then
396
+ exec node "$ROOT/bundle/codex-context.js" "$@"
392
397
  fi
393
398
 
394
- if [ -x "${TSX:-}" ]; then
399
+ if [ -x "${TSX:-}" ] && [ -f "$ROOT/src/session/codex/context-hook.ts" ]; then
395
400
  exec "$TSX" "$ROOT/src/session/codex/context-hook.ts" "$@"
396
401
  fi
397
402
 
@@ -416,6 +421,7 @@ Commands:
416
421
  toolnet-memory semantic "query"
417
422
  toolnet-memory impact [file]
418
423
  toolnet-memory mcp
424
+ toolnet-memory integrate:auto
419
425
  toolnet-memory snapshot:list
420
426
  toolnet-memory snapshot:create "reason"
421
427
  toolnet-memory snapshot:restore <id>
@@ -0,0 +1,78 @@
1
+ import{existsSync as kt,readFileSync as xt}from"node:fs";import{homedir as It}from"node:os";import{join as Pt}from"node:path";function Ot(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 Et(){let t=process.env.TOOLNET_GLOBAL_ENV??Pt(It(),".config","toolnet-memory",".env");if(!kt(t))return;let e=xt(t,"utf8");for(let r of e.split(/\r?\n/)){let n=r.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let s=n.indexOf("=");if(s<=0)continue;let o=n.slice(0,s).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)&&process.env[o]===void 0&&(process.env[o]=Ot(n.slice(s+1)))}}Et();function D(t,e){return t===void 0?e:["1","true","yes","on"].includes(t.toLowerCase())}function W(t,e){if(!t)return e;let r=Number(t);return Number.isFinite(r)?r:e}function ke(){return{memory:{autoCapture:D(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:D(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:D(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:D(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:W(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:W(process.env.MEMORY_RERANK_TOP,10),finalContext:W(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:W(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",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:W(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as At}from"node:crypto";import{existsSync as pe,mkdirSync as Mt,readFileSync as Nt,renameSync as Tt,writeFileSync as Rt}from"node:fs";import{basename as Ct,dirname as K,join as J,parse as Pe,resolve as z}from"node:path";var Oe=".toolnet",_t="project.json";function $t(t){return At("sha256").update(t).digest("hex").slice(0,16)}function le(t){return J(t,Oe,_t)}function Lt(t){return pe(le(t))}function Wt(t,e){let r=z(t),n=Pe(r).root;for(;;){if(Lt(r))return r;if(r===n||e&&r===z(e))break;let s=K(r);if(s===r)break;r=s}return null}function zt(t){let e=z(t),r=Pe(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(o=>pe(J(e,o))))return e;if(e===r)break;let s=K(e);if(s===e)break;e=s}return z(t)}function Ft(t){let e;try{e=JSON.parse(Nt(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 r=e;if(typeof r.id!="string"||!r.id.trim())throw new Error(`ToolNet project manifest is missing id: ${t}`);if(typeof r.name!="string"||!r.name.trim())throw new Error(`ToolNet project manifest is missing name: ${t}`);let n=new Date().toISOString();return{version:1,id:r.id,name:r.name,remote:typeof r.remote=="string"&&r.remote.trim()?r.remote:r.name,rootPath:typeof r.rootPath=="string"?r.rootPath:K(K(t)),createdAt:typeof r.createdAt=="string"?r.createdAt:n,updatedAt:typeof r.updatedAt=="string"?r.updatedAt:n,graphVersion:typeof r.graphVersion=="number"?r.graphVersion:0,memoryVersion:typeof r.memoryVersion=="number"?r.memoryVersion:0,metadata:r.metadata&&typeof r.metadata=="object"?r.metadata:void 0}}function xe(t,e){let r=J(t,Oe);Mt(r,{recursive:!0});let n=le(t),s=`${n}.tmp-${process.pid}`;Rt(s,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),Tt(s,n)}function Ie(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 q=class{detect(e=process.cwd()){let r=z(e),n=zt(r),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>pe(J(n,f))),i=Wt(r,o?n:void 0);if(i){let f=le(i),p=Ft(f);return p.rootPath!==i&&(p.rootPath=i,p.updatedAt=new Date().toISOString(),xe(i,p)),Ie(p,i)}let u=new Date().toISOString(),a=Ct(n),c={version:1,id:$t(n),name:a,remote:a,rootPath:n,createdAt:u,updatedAt:u,graphVersion:0,memoryVersion:0};return xe(n,c),Ie(c,n)}};var Bt=[{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}],V=class{scan(e){let r=[];for(let n of Bt){let s=new RegExp(n.regex.source,n.regex.flags);for(let o of e.matchAll(s))r.push({type:n.type,value:o[0]})}return r}hasSecrets(e){return this.scan(e).length>0}};var F=class{scanner=new V;sanitize(e){let r=e,n=this.scanner.scan(e),s=new Set;for(let o of n)s.add(o.type),r=r.split(o.value).join(`[REDACTED:${o.type}]`);return{text:r,redacted:n.length,secretTypes:[...s]}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(r=>this.sanitizeValue(r));if(e&&typeof e=="object"){let r={};for(let[n,s]of Object.entries(e)){let o=n.toLowerCase();o.includes("password")||o.includes("secret")||o.includes("token")||o.includes("cookie")||o.includes("authorization")?r[n]="[REDACTED]":r[n]=this.sanitizeValue(s)}return r}return e}};import{homedir as nr}from"node:os";import{join as sr}from"node:path";import{DeleteObjectCommand as Dt,GetObjectCommand as Kt,HeadObjectCommand as qt,ListObjectsV2Command as Jt,PutObjectCommand as Vt,S3Client as Ut}from"@aws-sdk/client-s3";import{getSignedUrl as Ht}from"@aws-sdk/s3-request-presigner";var U=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ut({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,r,n="application/octet-stream"){let s=typeof r=="string"?Buffer.from(r,"utf8"):r;await this.client.send(new Vt({Bucket:this.bucket,Key:e,Body:s,ContentType:n}))}async get(e){let r=await Ht(this.client,new Kt({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(r,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(e){let r=await this.get(e);return r?Buffer.from(r).toString("utf8"):null}async exists(e){try{return await this.client.send(new qt({Bucket:this.bucket,Key:e})),!0}catch(r){if(typeof r=="object"&&r!==null&&"$metadata"in r&&r.$metadata?.httpStatusCode===404)return!1;throw r}}async delete(e){await this.client.send(new Dt({Bucket:this.bucket,Key:e}))}async list(e=""){let r=[],n;do{let s=await this.client.send(new Jt({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&r.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return r}};import{access as Ee,mkdir as Yt,readFile as Gt,readdir as Qt,rm as Xt,stat as Ae,writeFile as Zt}from"node:fs/promises";import{dirname as er,join as tr,relative as Me,resolve as rr}from"node:path";var B=class{constructor(e){this.root=e}root;name="local";path(e){let r=e.replace(/^\/+/,"");return rr(this.root,r)}async put(e,r){let n=this.path(e);await Yt(er(n),{recursive:!0}),await Zt(n,r)}async get(e){try{return await Gt(this.path(e))}catch(r){if(typeof r=="object"&&r!==null&&"code"in r&&r.code==="ENOENT")return null;throw r}}async getText(e){let r=await this.get(e);return r?Buffer.from(r).toString("utf8"):null}async exists(e){try{return await Ee(this.path(e)),!0}catch{return!1}}async delete(e){await Xt(this.path(e),{force:!0})}async list(e=""){let r=this.path(e),n=[];try{await Ee(r)}catch{return n}let s=async i=>{let u=await Qt(i,{withFileTypes:!0});for(let a of u){let c=tr(i,a.name);if(a.isDirectory()){await s(c);continue}let f=await Ae(c);n.push({key:Me(this.root,c),size:f.size,updatedAt:f.mtime.toISOString()})}},o=await Ae(r);return o.isDirectory()?await s(r):n.push({key:Me(this.root,r),size:o.size,updatedAt:o.mtime.toISOString()}),n}};function Ne(t){let e=t.localRoot??sr(nr(),".toolnet-memory","storage");if(t.provider==="huggingface"){let r=t.huggingface;return r?.namespace&&r.bucket&&r.accessKeyId&&r.secretAccessKey?new U({namespace:r.namespace,bucket:r.bucket,accessKeyId:r.accessKeyId,secretAccessKey:r.secretAccessKey}):(console.warn("[storage] Hugging Face credentials missing. Using local fallback."),new B(e))}return new B(e)}function or(t){return new Promise(e=>setTimeout(e,t))}async function Te(t,e={}){let r=Math.max(1,e.attempts??3),n=e.baseDelayMs??150,s=e.maxDelayMs??2e3,o;for(let i=1;i<=r;i++)try{return await t()}catch(u){if(o=u,i>=r)break;let a=Math.min(s,n*2**(i-1)),c=Math.floor(Math.random()*Math.max(1,a*.2));await or(a+c)}throw o}var ir=new Set(["put","get","getText","delete","list"]);function Re(t,e={}){return new Proxy(t,{get(r,n){let s=Reflect.get(r,n,r);return typeof s!="function"?s:ir.has(n)?(...o)=>Te(()=>Promise.resolve(s.apply(r,o)),e):s.bind(r)}})}function Ce(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 _e(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 r=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(r)return`${r[1]}memory/records/${r[2]}`;if(r=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),r)return`${r[1]}memory/vectors/${r[2]}`;if(r=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),r)return`${r[1]}code/graph/${r[2]}`;let n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let s=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:e))}var H=class{constructor(e,r,n,s){this.provider=e;this.name=e.name,this.projectId=r,this.projectName=n,this.folder=Ce(s??n),this.sourcePrefix=`projects/${r}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,r=new Date().toISOString(),n=r,s=await this.provider.getText(e);if(s){let i;try{i=JSON.parse(s)}catch(u){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${u instanceof Error?u.message:String(u)}`)}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"&&(n=i.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:r};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=_e(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,r,n){return await this.ensureRegistered(),this.provider.put(this.key(e),r,n)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{createHash as ar}from"node:crypto";import{dirname as cr}from"node:path";import{mkdirSync as ur,readFileSync as pr,renameSync as lr,writeFileSync as dr}from"node:fs";function v(t){return ar("sha256").update(t).digest("hex")}function de(t){if(Array.isArray(t))return t.map(de);if(t&&typeof t=="object"){let e=t,r={};for(let n of Object.keys(e).sort())r[n]=de(e[n]);return r}return t}function $e(t){return JSON.stringify(de(t))}function Le(t){try{return JSON.parse(pr(t,"utf8"))}catch{return null}}function x(t,e){ur(cr(t),{recursive:!0});let r=`${t}.${process.pid}.tmp`;dr(r,JSON.stringify(e,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),lr(r,t)}var fr=new Set(["content","text","message","prompt","summary","description","title","reason","last_assistant_message","lastAssistantMessage"]);function _(t){return t.normalize("NFKC").replace(/\s+/g," ").replace(/^[\s>*#•-]+/u,"").trim()}function ze(t){return _(t).toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function C(t,e,r=0){if(!(r>6)){if(typeof t=="string"){e.push(t);return}if(Array.isArray(t)){for(let n of t.slice(0,50))C(n,e,r+1);return}if(!(!t||typeof t!="object"))for(let[n,s]of Object.entries(t))(fr.has(n)||["data","payload","parts","messages"].includes(n))&&C(s,e,r+1)}}function Y(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 We(t){let e=_(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 w(t,e,r,n,s={}){let o=_(n),i=s.key??ze(o);return{version:1,id:v([t.projectId,r,i,e.id,o,s.status??"",s.order??""].join("|")).slice(0,32),projectId:t.projectId,kind:r,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 mr(t,e,r){let n=_(r);if(n.length<5||n.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(n),i=n.match(/^(?:mục tiêu|goal|objective)\s*(?::|-)\s*(.+)$/iu);i?.[1]&&s.push(w(t,e,"goal",i[1],{confidence:.97}));let u=n.match(/^(?:kế hoạch|plan)\s*(?::|-)\s*(.+)$/iu);u?.[1]&&s.push(w(t,e,"plan",u[1],{confidence:.95}));let a=/\b(?:phase|giai đoạn|giai doan|stage)\s*(\d+)\s*(?:[:\-–—]\s*)?([^.;\n]{0,220})/giu,c;for(;!o&&(c=a.exec(n));){let g=Number(c[1]),l=_(c[2]??""),y=l&&!We(l)?`Phase ${g} - ${l}`:`Phase ${g}`;s.push(w(t,e,"phase",y,{key:`phase:${g}`,order:g,status:Y(n),confidence:.93}))}let f=r.match(/^\s*[-*]\s*\[([ xX])\]\s*(.+)$/u);f?.[2]&&s.push(w(t,e,"task",f[2],{status:f[1].trim()?"completed":Y(f[2]),confidence:.96}));let p=n.match(/^(?:todo|task|việc)\s*(\d+)?(?:\s*[:.\-–—]\s*|\s+)(.+)$/iu);if(p?.[2]){let g=p[1]?Number(p[1]):void 0,l=_(p[2]),y=We(l);s.push(w(t,e,"task",y&&g!==void 0?`TODO ${g}`:l,{key:g!==void 0?`task:${g}`:ze(l),order:g,status:Y(n),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(n)){let g=n.replace(/^(?:next|next action|next step|tiếp theo|bước tiếp theo|cần làm tiếp)\s*(?::|-)?\s*/iu,"");g&&s.push(w(t,e,"next_action",g,{confidence:.9}))}return/\b(blocker|blocked)\b|đang vướng|bị vướng|đang kẹt/iu.test(n)&&s.push(w(t,e,"blocker",n,{confidence:.9})),/\b(chú ý|lưu ý|warning|attention|cẩn thận)\b/iu.test(n)&&s.push(w(t,e,"warning",n,{confidence:.88})),/\b(chốt|quyết định|decided|decision)\b/iu.test(n)&&s.push(w(t,e,"decision",n,{confidence:.9})),s}function Fe(t,e){if(e.length===0)return[];let r=[],n=new Set;function s(i){n.has(i.id)||(n.add(i.id),r.push(i))}for(let i of e){if(i.type==="decision"){let a=[];C(i.data,a);for(let c of a)s(w(t,i,"decision",c,{confidence:1}))}if(i.type==="todo"){let a=[];C(i.data,a);for(let c of a)s(w(t,i,"task",c,{status:Y(c),confidence:1}))}if(["file_write","file_edit"].includes(i.type))for(let a of["filePath","path","file"]){let c=i.data[a];typeof c=="string"&&c&&s(w(t,i,"file",c,{confidence:1}))}if(i.type==="test"){let a=[];C(i.data,a);for(let c of a)s(w(t,i,"test",c,{confidence:1}))}let u=[];C(i.data,u);for(let a of u)for(let c of a.split(/\n+/u))for(let f of mr(t,i,c))s(f)}let o=e[e.length-1];return s(w(t,o,"session",`${t.agent}:${t.nativeSessionId}`,{key:t.sessionKey,confidence:1})),r}function Be(t){return String(t).padStart(12,"0")}var G=class{constructor(e){this.storage=e}storage;async write(e,r){if(r.length===0)return null;let n=Math.min(...r.map(c=>c.sequence)),s=Math.max(...r.map(c=>c.sequence)),o={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,createdAt:new Date().toISOString(),firstSequence:n,lastSequence:s,observations:r},i=JSON.stringify(o,null,2)+`
6
+ `,u=v(r.map(c=>c.id).sort().join("|")).slice(0,16),a=[`projects/${e.projectId}`,"work","observations",e.agent,e.nativeSessionId,`${Be(n)}-${Be(s)}-${u}.json`].join("/");return await this.storage.exists(a)||await this.storage.put(a,i,"application/json"),a}};import{join as De}from"node:path";import{mkdirSync as gr}from"node:fs";function hr(t){return t.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").replace(/\s+/g," ").trim()}function $(t,e=20){let r=[],n=new Set;for(let s of t.slice().reverse()){let o=hr(s);if(!(!o||n.has(o))&&(n.add(o),r.push(s),r.length>=e))break}return r.reverse()}function yr(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 Ke(t,e){let r=e.status??t?.status??"pending",n=r;t&&(t.status==="completed"&&r!=="completed"?n="completed":r==="pending"&&(t.status==="in_progress"||t.status==="blocked")&&(n=t.status));let s=t&&yr(e)?t.title:e.text;return{id:t?.id??v(e.key).slice(0,24),title:s,status:n,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 Sr(t,e){let r=`projects/${t.id}/work/observations/`,n=await e.list(r),s=[];for(let o of n.filter(i=>i.key.endsWith(".json")).sort((i,u)=>i.key.localeCompare(u.key))){let i=await e.getText(o.key);if(i)try{let u=JSON.parse(i);u.version===1&&Array.isArray(u.observations)&&s.push(u)}catch{}}return s}async function Q(t,e){let n=(await Sr(t,e)).flatMap(d=>d.observations).sort((d,k)=>{let be=d.occurredAt.localeCompare(k.occurredAt);if(be!==0)return be;let we=d.sequence-k.sequence;return we!==0?we:d.id.localeCompare(k.id)}),s=new Map,o=new Map,i,u,a,c=[],f=[],p=[],g=[],l=[],y=[];for(let d of n)switch(d.kind){case"goal":i=d.text;break;case"plan":u=d.text;break;case"phase":s.set(d.key,Ke(s.get(d.key),d));break;case"task":o.set(d.key,Ke(o.get(d.key),d));break;case"decision":c.push(d.text);break;case"blocker":f.push(d.text);break;case"warning":p.push(d.text);break;case"next_action":g.push(d.text);break;case"file":l.push(d.text);break;case"test":y.push(d.text);break;case"session":a={agent:d.agent,nativeSessionId:d.nativeSessionId,sessionKey:d.sessionKey,updatedAt:d.occurredAt};break}let h=Array.from(s.values()).sort((d,k)=>(d.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),m=Array.from(o.values()).sort((d,k)=>(d.order??Number.MAX_SAFE_INTEGER)-(k.order??Number.MAX_SAFE_INTEGER)),S=h.find(d=>d.status==="in_progress")??h.find(d=>d.status==="blocked")??h.find(d=>d.status==="pending"),b=m.find(d=>d.status==="in_progress")??m.find(d=>d.status==="blocked")??m.find(d=>d.status==="pending"),N=$([...g,...b?[b.title]:[],...!b&&S?[S.title]:[],...m.filter(d=>d.status==="pending").slice(0,5).map(d=>d.title)],8),R=$([...f,...h.filter(d=>d.status==="blocked").map(d=>d.title),...m.filter(d=>d.status==="blocked").map(d=>d.title)],20),E={version:1,projectId:t.id,projectName:t.name,goal:i,plan:u,phases:h,tasks:m,decisions:$(c,20),blockers:R,warnings:$(p,20),nextActions:N,filesTouched:$(l,30),tests:$(y,20),currentPhase:S,currentTask:b,progress:{phasesTotal:h.length,phasesCompleted:h.filter(d=>d.status==="completed").length,tasksTotal:m.length,tasksCompleted:m.filter(d=>d.status==="completed").length,blocked:h.filter(d=>d.status==="blocked").length+m.filter(d=>d.status==="blocked").length},lastSession:a,updatedAt:n.length?n[n.length-1].occurredAt:new Date().toISOString()},T=De(t.rootPath,".toolnet","work");return gr(T,{recursive:!0}),x(De(T,"current.json"),E),await e.put(`projects/${t.id}/work/current.json`,JSON.stringify(E,null,2)+`
7
+ `,"application/json"),E}async function X(t,e){let r=await e.getText(`projects/${t.id}/work/current.json`);if(!r)return null;try{return JSON.parse(r)}catch{return null}}import{closeSync as vr,existsSync as jr,openSync as br,readSync as wr,statSync as kr}from"node:fs";function xr(t,e){if(!jr(t))return{events:[],nextOffset:e};let r=kr(t).size,n=Number.isFinite(e)?Math.max(0,e):0;if(n>r&&(n=0),n===r)return{events:[],nextOffset:r};let s=r-n,o=Buffer.alloc(s),i=br(t,"r");try{wr(i,o,0,s,n)}finally{vr(i)}let u=o.toString("utf8"),a=u.lastIndexOf(`
8
+ `);if(a<0)return{events:[],nextOffset:n};let c=u.slice(0,a+1);return{events:c.split(`
9
+ `).filter(Boolean).flatMap(p=>{try{return[JSON.parse(p)]}catch{return[]}}),nextOffset:n+Buffer.byteLength(c,"utf8")}}var Z=class{constructor(e){this.options=e;this.journal=new G(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),r=Number(e.sourceCursors["work.continuity.offset"]??0),n=xr(this.options.wal.eventsFile,Number.isFinite(r)?r:0);if(n.events.length===0)return{scannedEvents:0,observations:0,journalWritten:!1,reconciled:!1,nextOffset:n.nextOffset};let s=Fe(this.options.identity,n.events),o=!1,i=!1;return s.length>0&&(o=!!await this.journal.write(this.options.identity,s),o&&(await Q(this.options.project,this.options.storage),i=!0)),this.options.wal.setSourceCursor("work.continuity.offset",n.nextOffset),{scannedEvents:n.events.length,observations:s.length,journalWritten:o,reconciled:i,nextOffset:n.nextOffset}}};import{mkdirSync as Cr}from"node:fs";import{join as fe}from"node:path";import{existsSync as Ve,mkdirSync as Ir,readFileSync as Pr,statSync as qe,writeFileSync as Or}from"node:fs";import{dirname as Er,join as Ar}from"node:path";var Je=64*1024,Mr=`# ToolNet Project Operating Manual
10
+
11
+ This file contains persistent instructions for AI agents working on this project.
12
+
13
+ ## Critical Rules
14
+
15
+ <!--
16
+ Examples:
17
+
18
+ - [enforce] Never edit production files directly.
19
+ - [enforce] Edit source only in /path/to/source.
20
+ - [enforce] Deploy only with ./deploy.sh --apply
21
+ -->
22
+
23
+ ## Workflow
24
+
25
+ <!--
26
+ Describe the correct working process.
27
+ -->
28
+
29
+ ## Architecture
30
+
31
+ <!--
32
+ Important architecture conventions.
33
+ -->
34
+
35
+ ## Verification
36
+
37
+ <!--
38
+ Tests / QA required after changes.
39
+ -->
40
+
41
+ ## Known Gotchas
42
+
43
+ <!--
44
+ Things an AI agent should always remember.
45
+ -->
46
+
47
+ ## Preferences
48
+
49
+ <!--
50
+ - [advisory] Prefer small focused files.
51
+ -->
52
+ `;function ee(t){return Ar(t.rootPath,".toolnet","PROJECT.md")}function Nr(t){return t.normalize("NFKC").replace(/\s+/g," ").trim()}function Tr(t){let e=[],r=new Set,n=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,s;for(;s=n.exec(t);){let o=s[1].toLowerCase(),i=Nr(s[2]);if(!i)continue;let u=`${o}:${i.toLowerCase()}`;r.has(u)||(r.add(u),e.push({id:v(u).slice(0,24),mode:o,text:i,source:"manual"}))}return e}function Rr(t){let e=ee(t);return Ve(e)||(Ir(Er(e),{recursive:!0}),Or(e,Mr,{encoding:"utf8",mode:384})),e}function te(t,e=!1){let r=e?Rr(t):ee(t);if(!Ve(r))return null;if(qe(r).size>Je)throw new Error(`PROJECT.md exceeds ${Je} bytes`);let s=Pr(r,"utf8");return{path:r,content:s,digest:v(s),rules:Tr(s),bytes:Buffer.byteLength(s,"utf8"),updatedAt:new Date(qe(r).mtimeMs).toISOString()}}function _r(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 $r(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 re=class{constructor(e){this.options=e}options;async capture(e,r){let n=await X(this.options.project,this.options.storage);if(n||(n=await Q(this.options.project,this.options.storage)),!$r(n))return null;let s=te(this.options.project,!1),o=s?s.rules.filter(g=>g.mode==="enforce").map(g=>g.text):[],i=v(JSON.stringify(_r(n))),u=v([this.options.project.id,this.options.identity.sessionKey,i].join("|")).slice(0,24),a=new Date().toISOString(),c={version:1,id:u,projectId:this.options.project.id,projectName:this.options.project.name,createdAt:a,reason:e,sourceSession:{agent:this.options.identity.agent,nativeSessionId:this.options.identity.nativeSessionId,sessionKey:this.options.identity.sessionKey,sequence:r},goal:n.goal,plan:n.plan,progress:n.progress,currentPhase:n.currentPhase,currentTask:n.currentTask,incompletePhases:n.phases.filter(g=>g.status!=="completed"&&g.status!=="cancelled"),incompleteTasks:n.tasks.filter(g=>g.status!=="completed"&&g.status!=="cancelled"),nextActions:n.nextActions.slice(0,10),blockers:n.blockers.slice(0,10),decisions:n.decisions.slice(-10),warnings:n.warnings.slice(-10),attention:[...o,...n.warnings].slice(0,20),filesTouched:n.filesTouched.slice(-20),tests:n.tests.slice(-15),stateDigest:i},f=`projects/${this.options.project.id}/work/handoffs/${u}.json`;await this.options.storage.exists(f)||await this.options.storage.put(f,JSON.stringify(c,null,2)+`
53
+ `,"application/json"),await this.options.storage.put(`projects/${this.options.project.id}/work/handoff-latest.json`,JSON.stringify(c,null,2)+`
54
+ `,"application/json");let p=fe(this.options.project.rootPath,".toolnet","work","handoffs");return Cr(p,{recursive:!0}),x(fe(p,`${u}.json`),c),x(fe(this.options.project.rootPath,".toolnet","work","handoff-latest.json"),c),c}};async function Ue(t,e){let r=await e.getText(`projects/${t.id}/work/handoff-latest.json`);if(!r)return null;try{return JSON.parse(r)}catch{return null}}import{mkdirSync as Lr}from"node:fs";import{join as He}from"node:path";function Wr(t){return{value:t.value,confidence:t.confidence,evidence:t.evidence}}function zr(t,e){if(!e)return!0;let r=t.evidence.occurredAt.localeCompare(e.evidence.occurredAt);return r!==0?r>0:t.evidence.sessionKey===e.evidence.sessionKey?t.evidence.sequence>=e.evidence.sequence:t.confidence>=e.confidence}function A(t,e){return zr(e,t)?e:t}function M(t,e=30){let r=new Set,n=[];for(let s of t){let o=s.value.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim();!o||r.has(o)||(r.add(o),n.push(s))}return n.slice(-e)}async function Fr(t,e){let r=`projects/${t.id}/work/semantic/observations/`,n=await e.list(r),s=[];for(let o of n.filter(i=>i.key.endsWith(".json")).sort((i,u)=>i.key.localeCompare(u.key))){let i=await e.getText(o.key);if(i)try{let u=JSON.parse(i);u.version===1&&Array.isArray(u.observations)&&s.push(u)}catch{}}return s}function Br(t){return{key:t.scopeKey??`phase:${t.scopeOrder??0}`,order:t.scopeOrder??0,acceptanceCriteria:[],dependencies:[],openQuestions:[],constraints:[],notes:[]}}async function Ye(t,e){let n=(await Fr(t,e)).flatMap(h=>h.observations).sort((h,m)=>{let S=h.evidence.occurredAt.localeCompare(m.evidence.occurredAt);return S!==0?S:h.evidence.sessionKey===m.evidence.sessionKey?h.evidence.sequence-m.evidence.sequence:h.id.localeCompare(m.id)}),s,o,i,u,a,c=new Map,f=[],p=[],g=[];for(let h of n){let m=Wr(h);if(h.scope==="phase"&&h.scopeKey){let S=c.get(h.scopeKey)??Br(h);switch(h.kind){case"phase_objective":S.objective=A(S.objective,m);break;case"phase_why":S.why=A(S.why,m);break;case"phase_deliverable":S.deliverable=A(S.deliverable,m);break;case"acceptance_criterion":S.acceptanceCriteria.push(m);break;case"dependency":S.dependencies.push(m);break;case"open_question":S.openQuestions.push(m);break;case"constraint":S.constraints.push(m);break;case"note":S.notes.push(m);break}c.set(S.key,S);continue}switch(h.kind){case"mission":s=A(s,m);break;case"objective":o=A(o,m);break;case"why":i=A(i,m);break;case"desired_outcome":u=A(u,m);break;case"plan_rationale":a=A(a,m);break;case"open_question":f.push(m);break;case"constraint":p.push(m);break;case"note":g.push(m);break}}for(let h of c.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 l={version:1,projectId:t.id,projectName:t.name,mission:s,activeObjective:o,why:i,desiredOutcome:u,planRationale:a,phases:Array.from(c.values()).sort((h,m)=>h.order-m.order),openQuestions:M(f,20),constraints:M(p,20),notes:M(g,20),updatedAt:n.length?n[n.length-1].evidence.occurredAt:new Date().toISOString()},y=He(t.rootPath,".toolnet","work");return Lr(y,{recursive:!0}),x(He(y,"semantic-current.json"),l),await e.put(`projects/${t.id}/work/semantic/current.json`,JSON.stringify(l,null,2)+`
55
+ `,"application/json"),l}async function Ge(t,e){let r=await e.getText(`projects/${t.id}/work/semantic/current.json`);if(!r)return null;try{return JSON.parse(r)}catch{return null}}function me(t){if(!t)return 0;let e=Array.from(t).length,r=t.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,r*1.3))}function j(t,e){let r=t.replace(/\s+/g," ").trim();return r.length<=e?r:r.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function Dr(t){let e=[],r=!1;for(let n of t.split(/\r?\n/u)){let s=n.trim();if(s.includes("<!--")&&(r=!0),r){s.includes("-->")&&(r=!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(j(s,280)),e.length>=16))break}return e}function Kr(t){let e=[],r=[];for(let n of t.split(/\\r?\\n/u)){let s=n.trim(),o=s.toLowerCase(),u=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(c=>o.startsWith(c));if(!u)continue;let a=s.slice(u.length).trim();a&&(u.includes("enforce")?e.push(a):r.push(a))}return{enforce:e,advisory:r}}function qr(t,e){let r=[];for(let n of t){let s=[...r,n].join(`
56
+ `);if(me(s)<=e){r.push(n);continue}let o=me(r.join(`
57
+ `)),i=Math.max(0,e-o);if(i>=16){let u=Math.floor(i*3.2),a=j(n,u);a&&r.push(a)}break}return r.join(`
58
+ `).trim()}async function Qe(t){let e=Math.max(256,Math.min(2e3,t.maxTokens??1e3)),r=te(t.project,!1),n=r?.content??"";n||(n=await t.storage.getText(`projects/${t.project.id}/project/manual.md`)??"");let s=Kr(n),o=r?r.rules.filter(l=>l.mode==="enforce").map(l=>l.text):s.enforce,i=r?r.rules.filter(l=>l.mode==="advisory").map(l=>l.text):s.advisory,u=n?Dr(n):[],a=await X(t.project,t.storage),c=await Ge(t.project,t.storage),f=await Ue(t.project,t.storage),p=[];if(p.push("[TOOLNET PROJECT CONTEXT]"),p.push(`Project: ${t.project.name}`),p.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&p.push(`Full operating manual: ${ee(t.project)}`),o.length){p.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let l of o.slice(0,24))p.push(`- [ENFORCE] ${j(l,240)}`)}if(i.length){p.push("","PROJECT PREFERENCES");for(let l of i.slice(0,10))p.push(`- ${j(l,220)}`)}if(c&&(c.mission&&p.push("","MISSION",j(c.mission.value,420)),c.activeObjective&&p.push("","CURRENT OBJECTIVE",j(c.activeObjective.value,420)),c.why&&p.push("","WHY THIS WORK MATTERS",j(c.why.value,420)),c.desiredOutcome&&p.push("","DESIRED OUTCOME",j(c.desiredOutcome.value,420)),c.planRationale&&p.push("","WHY THIS APPROACH",j(c.planRationale.value,420))),a){if(p.push("","ACTIVE WORK"),a.goal&&p.push(`Goal: ${j(a.goal,320)}`),a.plan&&p.push(`Plan: ${j(a.plan,320)}`),p.push(`Progress: phases ${a.progress.phasesCompleted}/${a.progress.phasesTotal}; tasks ${a.progress.tasksCompleted}/${a.progress.tasksTotal}; blocked ${a.progress.blocked}`),a.currentPhase&&p.push(`Current phase: ${a.currentPhase.title} [${a.currentPhase.status}]`),a.currentPhase&&c){let l=c.phases.find(y=>y.order===a.currentPhase?.order);l&&(l.objective&&p.push(`Phase objective: ${j(l.objective.value,340)}`),l.why?p.push(`Why this phase: ${j(l.why.value,340)}`):p.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),l.deliverable&&p.push(`Deliverable: ${j(l.deliverable.value,340)}`),l.dependencies.length&&p.push(`Depends on: ${l.dependencies.slice(0,4).map(y=>j(y.value,180)).join("; ")}`),l.acceptanceCriteria.length&&(p.push("","DEFINITION OF DONE"),l.acceptanceCriteria.slice(0,6).forEach(y=>{p.push(`- ${j(y.value,260)}`)})),l.openQuestions.length&&(p.push("","OPEN QUESTIONS FOR CURRENT PHASE"),l.openQuestions.slice(0,4).forEach(y=>{p.push(`- ${j(y.value,260)}`)})))}a.currentTask&&p.push(`Current task: ${a.currentTask.title} [${a.currentTask.status}]`),a.nextActions.length&&(p.push("","NEXT ACTIONS"),a.nextActions.slice(0,6).forEach((l,y)=>{p.push(`${y+1}. ${j(l,260)}`)})),a.blockers.length&&(p.push("","BLOCKERS"),a.blockers.slice(0,5).forEach(l=>{p.push(`- ${j(l,260)}`)})),a.warnings.length&&(p.push("","ATTENTION"),a.warnings.slice(-5).forEach(l=>{p.push(`- ${j(l,260)}`)})),a.decisions.length&&(p.push("","RECENT DECISIONS"),a.decisions.slice(-5).forEach(l=>{p.push(`- ${j(l,260)}`)})),a.lastSession&&p.push("",`Last work session: ${a.lastSession.agent} / ${a.lastSession.nativeSessionId}`)}if(c&&c.openQuestions.length&&(p.push("","UNRESOLVED QUESTIONS"),c.openQuestions.slice(0,5).forEach(l=>{p.push(`- ${j(l.value,260)}`)})),f&&p.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),u.length){p.push("","OPERATING NOTES");for(let l of u)p.push(`- ${l}`)}p.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let g=qr(p,e);return{version:1,projectId:t.project.id,projectName:t.project.name,text:g,estimatedTokens:me(g),maxTokens:e,hasManual:!!n,hasWorkState:!!a,hasHandoff:!!f,generatedAt:new Date().toISOString()}}var Jr=new Set(["content","text","message","prompt","summary","description","title","reason","last_assistant_message","lastAssistantMessage"]);function L(t){return t.normalize("NFKC").replace(/\s+/g," ").replace(/^[\s>*#•-]+/u,"").trim()}function ge(t,e,r=0){if(!(r>6)){if(typeof t=="string"){e.push(t);return}if(Array.isArray(t)){for(let n of t.slice(0,50))ge(n,e,r+1);return}if(!(!t||typeof t!="object"))for(let[n,s]of Object.entries(t))(Jr.has(n)||["data","payload","parts","messages"].includes(n))&&ge(s,e,r+1)}}function I(t,e,r,n,s,o=.95){let i=L(n);return{version:1,id:v([t.projectId,r,s.type,s.key??"",i.toLowerCase(),e.id].join("|")).slice(0,32),projectId:t.projectId,kind:r,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 O(t,e){let r=t.toLowerCase();for(let n of e){let s=n.toLowerCase();if(r.startsWith(`${s}:`)||r.startsWith(`${s} -`)||r.startsWith(`${s} \u2014`))return L(t.slice(n.length+1))}return null}function Vr(t){let e=t.trimStart();return e.startsWith("- ")||e.startsWith("* ")||/^\d+[.)]\s+/u.test(e)}function Ur(t){return L(t.trim().replace(/^[-*]\s+/u,"").replace(/^\d+[.)]\s+/u,""))}function Xe(t,e){let r=[],n=new Set;function s(o){!o.value||o.value.length<3||n.has(o.id)||(n.add(o.id),r.push(o))}for(let o of e){let i=[];ge(o.data,i);for(let u of i){let a={type:"project"},c=null;for(let f of u.split(/\r?\n/u)){let p=L(f);if(!p){c=null;continue}let g=p.match(/^(?:phase|giai đoạn|giai doan|stage)\s*(\d+)\s*(?:[:\-–—]\s*)?(.*)$/iu);if(g){let k=Number(g[1]);a={type:"phase",key:`phase:${k}`,order:k,title:L(g[2]??"")},c=null;continue}let l=p.match(/^(?:todo|task|việc)\s*(\d+)\s*(?:[:\-–—]\s*)?(.*)$/iu);if(l){let k=Number(l[1]);a={type:"task",key:`task:${k}`,order:k,title:L(l[2]??"")},c=null;continue}let y=O(p,["mission","s\u1EE9 m\u1EC7nh","m\u1EE5c ti\xEAu t\u1ED5ng th\u1EC3","m\u1EE5c ti\xEAu project","project goal"]);if(y){s(I(t,o,"mission",y,{type:"project"},.99)),c=null;continue}let h=O(p,["current objective","active objective","m\u1EE5c ti\xEAu hi\u1EC7n t\u1EA1i","objective","m\u1EE5c ti\xEAu","m\u1EE5c \u0111\xEDch"]);if(h){s(I(t,o,a.type==="phase"?"phase_objective":"objective",h,a,.98)),c=null;continue}let m=O(p,["why","why this","why this phase","reason","rationale","v\xEC sao","l\xFD do","t\u1EA1i sao","\xFD ngh\u0129a"]);if(m){s(I(t,o,a.type==="phase"?"phase_why":"why",m,a,.98)),c=null;continue}let S=O(p,["desired outcome","final outcome","k\u1EBFt qu\u1EA3 cu\u1ED1i","k\u1EBFt qu\u1EA3 mong mu\u1ED1n","m\u1EE5c ti\xEAu cu\u1ED1i"]);if(S){s(I(t,o,"desired_outcome",S,{type:"project"},.98)),c=null;continue}let b=O(p,["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(b){s(I(t,o,"plan_rationale",b,{type:"project"},.98)),c=null;continue}let N=O(p,["deliverable","output","k\u1EBFt qu\u1EA3 c\u1EA7n \u0111\u1EA1t","ph\u1EA3i t\u1EA1o ra","\u0111\u1EA7u ra"]);if(N){s(I(t,o,"phase_deliverable",N,a,.97)),c=null;continue}let R=O(p,["acceptance criteria","definition of done","done khi","ho\xE0n th\xE0nh khi","ti\xEAu ch\xED ho\xE0n th\xE0nh"]);if(R){s(I(t,o,"acceptance_criterion",R,a,.98)),c="acceptance_criterion";continue}let E=O(p,["depends on","dependency","dependencies","ph\u1EE5 thu\u1ED9c","c\u1EA7n c\xF3 tr\u01B0\u1EDBc"]);if(E){s(I(t,o,"dependency",E,a,.97)),c="dependency";continue}let T=O(p,["open question","open questions","c\xE2u h\u1ECFi m\u1EDF","ch\u01B0a quy\u1EBFt \u0111\u1ECBnh","ch\u01B0a r\xF5"]);if(T){s(I(t,o,"open_question",T,a,.95)),c="open_question";continue}let d=O(p,["constraint","constraints","r\xE0ng bu\u1ED9c","gi\u1EDBi h\u1EA1n"]);if(d){s(I(t,o,"constraint",d,a,.97)),c="constraint";continue}if(/^(?:acceptance criteria|definition of done|done khi|tiêu chí hoàn thành)\s*:?\s*$/iu.test(p)){c="acceptance_criterion";continue}if(/^(?:dependencies|dependency|phụ thuộc)\s*:?\s*$/iu.test(p)){c="dependency";continue}if(/^(?:open questions|open question|câu hỏi mở)\s*:?\s*$/iu.test(p)){c="open_question";continue}if(/^(?:constraints|constraint|ràng buộc)\s*:?\s*$/iu.test(p)){c="constraint";continue}if(c&&Vr(f)){s(I(t,o,c,Ur(f),a,.96));continue}c=null}}}return r}function Ze(t){return String(t).padStart(12,"0")}var ne=class{constructor(e){this.storage=e}storage;async write(e,r){if(r.length===0)return null;let n=Math.min(...r.map(a=>a.evidence.sequence)),s=Math.max(...r.map(a=>a.evidence.sequence)),o={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,firstSequence:n,lastSequence:s,createdAt:new Date().toISOString(),observations:r},i=v(r.map(a=>a.id).sort().join("|")).slice(0,16),u=[`projects/${e.projectId}`,"work","semantic","observations",e.agent,e.nativeSessionId,`${Ze(n)}-${Ze(s)}-${i}.json`].join("/");return await this.storage.exists(u)||await this.storage.put(u,JSON.stringify(o,null,2)+`
59
+ `,"application/json"),u}};import{closeSync as Hr,existsSync as Yr,openSync as Gr,readSync as Qr,statSync as Xr}from"node:fs";function Zr(t,e){if(!Yr(t))return{events:[],nextOffset:e};let r=Xr(t).size,n=Number.isFinite(e)?Math.max(0,e):0;if(n>r&&(n=0),n===r)return{events:[],nextOffset:r};let s=Buffer.alloc(r-n),o=Gr(t,"r");try{Qr(o,s,0,s.length,n)}finally{Hr(o)}let i=s.toString("utf8"),u=i.lastIndexOf(`
60
+ `);if(u<0)return{events:[],nextOffset:n};let a=i.slice(0,u+1);return{events:a.split(`
61
+ `).filter(Boolean).flatMap(c=>{try{return[JSON.parse(c)]}catch{return[]}}),nextOffset:n+Buffer.byteLength(a,"utf8")}}var se=class{constructor(e){this.options=e;this.journal=new ne(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),r=Number(e.sourceCursors["work.semantic.offset"]??0),n=Zr(this.options.wal.eventsFile,Number.isFinite(r)?r:0);if(n.events.length===0)return{scannedEvents:0,observations:0,journalWritten:!1,reconciled:!1,nextOffset:n.nextOffset};let s=Xe(this.options.identity,n.events),o=!1,i=!1;return s.length>0&&(o=!!await this.journal.write(this.options.identity,s),o&&(await Ye(this.options.project,this.options.storage),i=!0)),this.options.wal.setSourceCursor("work.semantic.offset",n.nextOffset),{scannedEvents:n.events.length,observations:s.length,journalWritten:o,reconciled:i,nextOffset:n.nextOffset}}};import{existsSync as en,mkdirSync as tn,readFileSync as rn,writeFileSync as nn}from"node:fs";import{dirname as sn,join as et}from"node:path";function tt(t){return et(t.rootPath,".toolnet","context","startup.md")}function on(t){return et(t.rootPath,".toolnet","context","startup.json")}function rt(t,e){let r=tt(t);tn(sn(r),{recursive:!0}),nn(r,e.text.endsWith(`
62
+ `)?e.text:e.text+`
63
+ `,{encoding:"utf8",mode:384}),x(on(t),e)}async function he(t,e,r=900){let n=await Qe({project:t,storage:e,maxTokens:r}),s={version:1,projectId:t.id,projectName:t.name,text:n.text,digest:v(n.text),estimatedTokens:n.estimatedTokens,generatedAt:new Date().toISOString()};return rt(t,s),await e.put(`projects/${t.id}/context/startup.md`,s.text+`
64
+ `,"text/markdown"),await e.put(`projects/${t.id}/context/startup.json`,JSON.stringify(s,null,2)+`
65
+ `,"application/json"),s}async function nt(t,e,r=900){try{let s=await e.getText(`projects/${t.id}/context/startup.md`);if(s&&s.trim()){let o=s.trim(),i={version:1,projectId:t.id,projectName:t.name,text:o,digest:v(o),estimatedTokens:Math.ceil(o.length/3.5),generatedAt:new Date().toISOString()};return rt(t,i),i}}catch{}let n=tt(t);if(en(n))try{let s=rn(n,"utf8").trim();if(s)return{version:1,projectId:t.id,projectName:t.name,text:s,digest:v(s),estimatedTokens:Math.ceil(s.length/3.5),generatedAt:new Date().toISOString()}}catch{}try{return await he(t,e,r)}catch{return null}}import{existsSync as an,mkdirSync as cn,readFileSync as un}from"node:fs";import{homedir as pn}from"node:os";import{dirname as ln,join as st}from"node:path";function dn(t,e,r){let n=r??st(pn(),".config","toolnet-memory","injections","agy");return st(n,`${t.id}-${e}.json`)}function fn(t){if(!an(t))return null;try{return JSON.parse(un(t,"utf8"))}catch{return null}}async function ot(t){let e=t.now??Date.now(),r=t.resumeAfterMs??360*60*1e3,n=dn(t.project,t.conversationId,t.markerDirectory),s=fn(n),o=s?Date.parse(s.injectedAt):0,i=o?e-o:Number.POSITIVE_INFINITY;if(s&&i<6e4)return{};let u=t.invocationNum===0,a=i>=r;if(s&&!u&&!a)return{};let c=await nt(t.project,t.storage,900);return c?.text?(cn(ln(n),{recursive:!0}),x(n,{digest:c.digest,injectedAt:new Date(e).toISOString()}),{injectSteps:[{ephemeralMessage:c.text}]}):{}}import{existsSync as mn}from"node:fs";import{dirname as gn,join as hn,parse as yn,resolve as Sn}from"node:path";function it(t){for(let e of t){let r=Sn(e),n=yn(r).root;for(;;){if(mn(hn(r,".toolnet","project.json")))return new q().detect(r);if(r===n)break;let s=gn(r);if(s===r)break;r=s}}return null}import{homedir as bt}from"node:os";import{join as ns,resolve as ss}from"node:path";import{join as vn}from"node:path";function at(t,e){let r=t.trim(),n=r.replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,""),s=v(r).slice(0,12);if(!n||n==="."||n==="..")return`${e}--${s}`;let o=n.slice(0,100);return o===r&&r.length<=100?o:`${o.slice(0,85)}--${s}`}function ct(t,e,r){let n=e.trim(),s=r.trim();if(!n)throw new Error("Session agent is required");if(!s)throw new Error("Native session ID is required");let o=at(n.toLowerCase(),"agent"),i=at(s,"session");return{projectId:t.id,projectName:t.name,projectRoot:t.rootPath,agent:n,nativeSessionId:s,sessionKey:`${n}:${s}`,remotePrefix:["projects",t.id,"sessions",o,i].join("/"),localDirectory:vn(t.rootPath,".toolnet","sessions",o,i)}}function ut(t){return String(t).padStart(12,"0")}var oe=class{constructor(e,r=100,n=512*1024){this.storage=e;this.maxEventsPerChunk=r;this.maxChunkBytes=n;if(r<1)throw new Error("maxEventsPerChunk must be positive");if(n<1024)throw new Error("maxChunkBytes is too small")}storage;maxEventsPerChunk;maxChunkBytes;async getJson(e){let r=await this.storage.getText(e);return r?JSON.parse(r):null}async putJson(e,r){await this.storage.put(e,JSON.stringify(r,null,2)+`
66
+ `,"application/json")}async scan(e){let r=`${e.remotePrefix}/events/`,n=await this.storage.list(r),s=[],o=0;for(let i of n){let u=i.key.match(/\/events\/(\d+)-(\d+)-[a-f0-9]+\.jsonl$/);if(!u)continue;let a=Number(u[1]),c=Number(u[2]);!Number.isFinite(a)||!Number.isFinite(c)||(s.push({key:i.key,start:a,end:c}),o=Math.max(o,c))}return s.sort((i,u)=>i.start-u.start),{chunks:s,maxSequence:o}}split(e){let r=[],n=[],s=0;for(let o of e){let i=Buffer.byteLength(JSON.stringify(o)+`
67
+ `,"utf8");n.length>0&&(n.length>=this.maxEventsPerChunk||s+i>this.maxChunkBytes)&&(r.push(n),n=[],s=0),n.push(o),s+=i}return n.length>0&&r.push(n),r}async loadManifest(e){return this.getJson(`${e.remotePrefix}/session.json`)}async loadCursor(e){return this.getJson(`${e.remotePrefix}/cursor.json`)}async recover(e){let r=await this.scan(e);return{maxSequence:r.maxSequence,chunkCount:r.chunks.length}}async append(e,r,n,s={}){let o=await this.loadManifest(e),i=await this.scan(e),u=r.filter(m=>m.sequence>i.maxSequence),a=0;for(let m of this.split(u)){let S=m[0],b=m[m.length-1],N=m.map(T=>JSON.stringify(T)).join(`
68
+ `)+`
69
+ `,R=v(N).slice(0,16),E=[e.remotePrefix,"events",`${ut(S.sequence)}-${ut(b.sequence)}-${R}.jsonl`].join("/");await this.storage.exists(E)||await this.storage.put(E,N,"application/x-ndjson"),a+=m.length}let c=await this.scan(e),f=r[r.length-1],p=o?.status??"active";f?.type==="session_end"||f?.type==="session_idle"?p="idle":f?.type==="error"?p="error":r.length>0&&(p="active");let g=new Date().toISOString(),l=r[0],y={version:1,projectId:e.projectId,projectName:e.projectName,agent:e.agent,nativeSessionId:e.nativeSessionId,sessionKey:e.sessionKey,status:p,createdAt:o?.createdAt??l?.timestamp??g,updatedAt:f?.timestamp??g,firstEventAt:o?.firstEventAt??l?.timestamp,lastEventAt:f?.timestamp??o?.lastEventAt,eventCount:c.maxSequence,chunkCount:c.chunks.length,metadata:{...o?.metadata,...s.metadata}};(s.title??o?.title)&&(y.title=s.title??o?.title);let h={version:1,projectId:e.projectId,agent:e.agent,nativeSessionId:e.nativeSessionId,lastLocalSequence:r.length>0?r[r.length-1].sequence:c.maxSequence,lastRemoteSequence:c.maxSequence,sourceCursors:n,updatedAt:g};return await this.putJson(`${e.remotePrefix}/cursor.json`,h),await this.putJson(`${e.remotePrefix}/session.json`,y),{uploadedEvents:a,lastRemoteSequence:c.maxSequence,eventCount:y.eventCount,chunkCount:y.chunkCount,status:p}}};import{closeSync as ye,existsSync as jn,fsyncSync as bn,mkdirSync as wn,openSync as Se,readSync as kn,rmSync as pt,statSync as lt,writeSync as xn}from"node:fs";import{join as ve}from"node:path";var In=12e4,Pn=80,On=2e3;function En(t){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,t)}var ie=class{constructor(e){this.identity=e;wn(e.localDirectory,{recursive:!0}),this.eventsFile=ve(e.localDirectory,"events.jsonl"),this.stateFile=ve(e.localDirectory,"state.json"),this.lockFile=ve(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 Le(this.stateFile)??this.initialState()}loadState(){return this.withLock(()=>this.loadStateUnsafe())}saveStateUnsafe(e){x(this.stateFile,e)}acquireLock(){for(let e=0;e<Pn;e+=1)try{return Se(this.lockFile,"wx",384)}catch(r){if(r.code!=="EEXIST")throw r;try{if(Date.now()-lt(this.lockFile).mtimeMs>In){pt(this.lockFile,{force:!0});continue}}catch{}En(25)}throw new Error(`Session journal is locked: ${this.lockFile}`)}withLock(e){let r=this.acquireLock();try{return e()}finally{ye(r),pt(this.lockFile,{force:!0})}}append(e){return e.length===0?[]:this.withLock(()=>{let r=this.loadStateUnsafe(),n=new Set(r.recentEventIds),s=r.lastSequence,o=[];for(let p of e){let g=p.timestamp??new Date().toISOString(),l=p.data??{},y=p.provenance?.rawDigest??v($e(l)),h=p.sourceEventId?[this.identity.projectId,this.identity.agent,this.identity.nativeSessionId,p.sourceEventId].join("|"):[this.identity.projectId,this.identity.agent,this.identity.nativeSessionId,s+1,p.type,g,y].join("|"),m=v(h).slice(0,32);if(n.has(m))continue;s+=1;let S={version:1,id:m,sequence:s,projectId:this.identity.projectId,agent:this.identity.agent,nativeSessionId:this.identity.nativeSessionId,type:p.type,timestamp:g,data:l,provenance:{...p.provenance,rawDigest:y}};p.role!==void 0&&(S.role=p.role),p.sourceEventId!==void 0&&(S.sourceEventId=p.sourceEventId),p.sourceSequence!==void 0&&(S.sourceSequence=p.sourceSequence),o.push(S),n.add(m)}if(o.length===0)return[];let i=o.map(p=>JSON.stringify(p)).join(`
70
+ `)+`
71
+ `,u=Se(this.eventsFile,"a",384);try{xn(u,i,null,"utf8"),bn(u)}finally{ye(u)}let a=o[o.length-1],c="active";a.type==="session_end"||a.type==="session_idle"?c="idle":a.type==="error"&&(c="error");let f=Array.from(n).slice(-On);return this.saveStateUnsafe({...r,status:c,updatedAt:a.timestamp,lastSequence:a.sequence,recentEventIds:f}),o})}readPending(){return this.withLock(()=>{let e=this.loadStateUnsafe();if(!jn(this.eventsFile))return{events:[],startOffset:e.remoteByteOffset,endOffset:e.remoteByteOffset};let r=lt(this.eventsFile).size,n=Math.min(e.remoteByteOffset,r);if(r<=n)return{events:[],startOffset:n,endOffset:r};let s=r-n,o=Buffer.alloc(s),i=Se(this.eventsFile,"r");try{kn(i,o,0,s,n)}finally{ye(i)}return{events:o.toString("utf8").split(`
72
+ `).filter(Boolean).map(c=>JSON.parse(c)),startOffset:n,endOffset:r}})}markRemote(e,r){this.withLock(()=>{let n=this.loadStateUnsafe();this.saveStateUnsafe({...n,lastRemoteSequence:Math.max(n.lastRemoteSequence,e),remoteByteOffset:Math.max(n.remoteByteOffset,r),updatedAt:new Date().toISOString()})})}setSourceCursor(e,r){this.withLock(()=>{let n=this.loadStateUnsafe();this.saveStateUnsafe({...n,sourceCursors:{...n.sourceCursors,[e]:String(r)},updatedAt:new Date().toISOString()})})}};import{closeSync as qn,existsSync as Jn,openSync as Vn,readSync as Un,statSync as Hn}from"node:fs";function dt(t,e){let r=e.toLowerCase();return r.includes("kh\xF4ng \u0111\u01B0\u1EE3c")||r.includes("tuy\u1EC7t \u0111\u1ED1i")||r.includes("must not")||r.includes("never ")?"critical":t==="rule"||t==="decision"?"high":t==="todo"||r.includes("error")||r.includes("failed")||r.includes("exception")?"normal":"temporary"}var ht=[/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],An=[/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],Mn=[/\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],Nn=[/\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],Tn=[/đã 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],ft=[/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],Rn=[/\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],Cn=[/đườ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],_n=[/\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],mt=new Set(["content","text","message","prompt","summary","description","reason","title","last_assistant_message","lastAssistantMessage","input_messages","inputMessages"]),$n=new Set(["payload","data","content","message","messages","parts","summary"]);function P(t,e){return e.some(r=>r.test(t))}function yt(t){return t.normalize("NFKC").replace(/\r/g,"").replace(/^[\s>*#\-•]+/u,"").replace(/\s+/g," ").trim()}function Ln(t){return yt(t).toLowerCase().replace(/[^\p{L}\p{N}:/._-]+/gu," ").replace(/\s+/g," ").trim()}function Wn(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 je(t,e,r,n=0){if(!(n>6)){if(typeof t=="string"){(!e||mt.has(e))&&r.push(t);return}if(Array.isArray(t)){for(let s of t.slice(0,50))je(s,e,r,n+1);return}if(!(!t||typeof t!="object"))for(let[s,o]of Object.entries(t))(mt.has(s)||$n.has(s))&&je(o,s,r,n+1)}}function zn(t){let e=[];je(t.data,void 0,e);let r=[],n=new Set;for(let s of e)for(let o of s.split(/\n+|(?<=[.!?])\s+/u)){let i=yt(o);if(Wn(i)&&!n.has(i)&&(n.add(i),r.push(i),r.length>=50))return r}return r}function gt(t){return(t.role??(typeof t.data.role=="string"?t.data.role:"")).toLowerCase()}function Fn(t,e,r){if(r.type==="decision")return{kind:"decision",confidence:1};if(r.type==="todo")return{kind:"todo",confidence:1};let n=e==="user"||r.type==="user_prompt",s=e==="assistant"||r.type==="assistant_message";return n&&P(t,ht)?{kind:"rule",confidence:.98}:n&&P(t,An)?{kind:"rule",confidence:.92}:P(t,Mn)?{kind:P(t,ft)?"architecture":"decision",confidence:n?.93:.86}:n&&P(t,Nn)?{kind:"todo",confidence:.87}:P(t,ft)&&P(t,Rn)?{kind:"architecture",confidence:n?.88:.82}:s&&P(t,Tn)?{kind:"fix",confidence:.8}:n&&P(t,Cn)&&P(t,_n)?{kind:"context",confidence:.79}:null}function Bn(t){switch(t){case"rule":return"rule";case"decision":case"architecture":return"decision";case"todo":return"todo";case"fix":case"context":return"code"}}function Dn(t,e,r){return t==="rule"&&P(r,ht)?"critical":t==="architecture"||t==="decision"||t==="rule"?"high":t==="fix"||t==="context"?"normal":dt(e,r)}function St(t,e){let r=[],n=new Set,s=new Map;for(let o of e){let i=typeof o.data.messageId=="string"?o.data.messageId:void 0,u=gt(o);i&&u&&s.set(i,u)}for(let o of e){let i=gt(o),u=typeof o.data.messageId=="string"?o.data.messageId:void 0;!i&&u&&(i=s.get(u)??"");for(let a of zn(o)){let c=Fn(a,i,o);if(!c||c.confidence<.75)continue;let f=Bn(c.kind),p=Ln(a),g=v([t.projectId,c.kind,p].join("|"));if(n.has(g))continue;n.add(g);let l=o.provenance.sourcePath?[o.provenance.sourcePath]:[],y=o.sourceEventId?[o.sourceEventId]:[];r.push({version:1,fingerprint:g,projectId:t.projectId,agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,kind:c.kind,type:f,content:a,confidence:c.confidence,importance:Dn(c.kind,f,a),tags:[f],provenance:{agent:t.agent,nativeSessionId:t.nativeSessionId,sessionKey:t.sessionKey,eventIds:[o.id],sourceEventIds:y,sourcePaths:l,firstSequence:o.sequence,lastSequence:o.sequence},createdAt:o.timestamp})}}return r}function vt(t){return String(t).padStart(12,"0")}function Kn(t){return t.remotePrefix.replace("/sessions/","/memory/learned/")}var ae=class{constructor(e){this.storage=e}storage;async write(e,r,n){if(n.length===0||r.length===0)return null;let s=Math.min(...r.map(f=>f.sequence)),o=Math.max(...r.map(f=>f.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:n.length,candidates:n},u=JSON.stringify(i,null,2)+`
73
+ `,a=v(n.map(f=>f.fingerprint).sort().join("|")).slice(0,16),c=[Kn(e),"batches",`${vt(s)}-${vt(o)}-${a}.json`].join("/");return await this.storage.exists(c)||await this.storage.put(c,u,"application/json"),c}};function Yn(t,e){if(!Jn(t))return{events:[],nextOffset:e};let r=Hn(t).size,n=Number.isFinite(e)?Math.max(0,e):0;if(n>r&&(n=0),n===r)return{events:[],nextOffset:r};let s=r-n,o=Buffer.alloc(s),i=Vn(t,"r");try{Un(i,o,0,s,n)}finally{qn(i)}let u=o.toString("utf8"),a=u.lastIndexOf(`
74
+ `);if(a<0)return{events:[],nextOffset:n};let c=u.slice(0,a+1);return{events:c.split(`
75
+ `).filter(Boolean).flatMap(p=>{try{return[JSON.parse(p)]}catch{return[]}}),nextOffset:n+Buffer.byteLength(c,"utf8")}}var ce=class{constructor(e){this.options=e;this.journal=new ae(e.storage)}options;journal;async learnNew(){let e=this.options.wal.loadState(),r=Number(e.sourceCursors["memory.learner.offset"]??0),n=Number.isFinite(r)?r:0,s=Yn(this.options.wal.eventsFile,n);if(s.events.length===0)return{scannedEvents:0,candidates:0,journalWritten:!1,nextOffset:s.nextOffset};let o=St(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}}};var ue=class{identity;wal;remote;sanitizer=new F;learner;continuity;semantic;handoff;title;metadata;constructor(e){this.identity=ct(e.project,e.agent,e.nativeSessionId),this.title=e.title,this.metadata=this.sanitizer.sanitizeValue(e.metadata??{}),this.wal=new ie(this.identity),this.remote=new oe(e.storage,e.maxEventsPerChunk??100,e.maxChunkBytes??512*1024),this.learner=new ce({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.continuity=new Z({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.semantic=new se({project:e.project,storage:e.storage,identity:this.identity,wal:this.wal}),this.handoff=new re({project:e.project,storage:e.storage,identity:this.identity})}sanitizeEvent(e){let r=e.provenance?{...e.provenance,metadata:this.sanitizer.sanitizeValue(e.provenance.metadata)}:void 0;return{...e,data:this.sanitizer.sanitizeValue(e.data??{}),provenance:r}}start(e={}){let r=this.wal.loadState();return this.record({type:r.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(r=>this.sanitizeEvent(r)))}setSourceCursor(e,r){this.wal.setSourceCursor(e,r)}async flush(){let e=this.wal.readPending(),r=this.wal.loadState(),n=await this.remote.append(this.identity,e.events,r.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 n}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)}};import{existsSync as Gn,openSync as Qn,closeSync as Xn,readSync as Zn,statSync as es}from"node:fs";function ts(t){let e=typeof t.role=="string"?t.role:"",r=typeof t.type=="string"?t.type.toLowerCase():"";return e==="user"||r.includes("user")?"user_prompt":e==="assistant"||r.includes("assistant")||r.includes("model")?"assistant_message":r.includes("tool")||"toolCall"in t||"tool_call"in t?"tool_call":r.includes("error")?"error":"message"}function rs(t){let e=[t.timestamp,t.createdAt,t.time,t.created_at];for(let r of e){if(typeof r=="string"){let n=new Date(r);if(!Number.isNaN(n.getTime()))return n.toISOString()}if(typeof r=="number"){let n=r;n<1e11&&(n*=1e3);let s=new Date(n);if(!Number.isNaN(s.getTime()))return s.toISOString()}}return new Date().toISOString()}function jt(t,e={offset:0}){if(!Gn(t))return{events:[],nextOffset:e.offset,reset:!1};let r=es(t).size,n=e.offset>r,s=n?0:e.offset;if(s===r)return{events:[],nextOffset:r,reset:n};let o=r-s,i=Buffer.alloc(o),u=Qn(t,"r");try{Zn(u,i,0,o,s)}finally{Xn(u)}let a=i.toString("utf8"),c=a.lastIndexOf(`
76
+ `);if(c<0)return{events:[],nextOffset:s,reset:n};let f=a.slice(0,c+1),p=Buffer.byteLength(f,"utf8"),g=[],l=0;for(let y of f.split(`
77
+ `)){if(!y)continue;let h=Buffer.byteLength(y+`
78
+ `,"utf8"),m=s+l;l+=h;let S;try{S=JSON.parse(y)}catch{g.push({type:"custom",sourceEventId:`transcript:${m}`,sourceSequence:m,data:{format:"raw-line",content:y},provenance:{source:"agy-transcript",sourcePath:t,sourceOffset:m}});continue}let b=S&&typeof S=="object"&&!Array.isArray(S)?S:{value:S};g.push({type:ts(b),timestamp:rs(b),role:typeof b.role=="string"?b.role:void 0,sourceEventId:`transcript:${m}`,sourceSequence:m,data:b,provenance:{source:"agy-transcript",sourcePath:t,sourceOffset:m}})}return{events:g,nextOffset:s+p,reset:n}}function os(t){return t==="~"?bt():t.startsWith("~/")?ns(bt(),t.slice(2)):t}async function wt(t){let e=t.conversationId.trim();if(!e)throw new Error("Agy conversationId is required");let r=ss(os(t.transcriptPath)),n=new ue({project:t.project,storage:t.storage,agent:"agy",nativeSessionId:e,metadata:{source:"antigravity-hook",transcriptPath:r,workspacePaths:t.workspacePaths??[],artifactDirectoryPath:t.artifactDirectoryPath,modelName:t.modelName}}),s=n.status(),o=s.sourceCursors["agy.transcript.offset"],i=o?Number(o):0,u=jt(r,{offset:Number.isFinite(i)?i:0}),a=[];s.lastSequence===0&&a.push({type:"session_start",sourceEventId:`agy:${e}:start`,data:{workspacePaths:t.workspacePaths??[],transcriptPath:r,modelName:t.modelName},provenance:{source:"antigravity-hook",sourcePath:r}}),u.reset&&a.push({type:"custom",sourceEventId:`agy:${e}:transcript-reset:${u.nextOffset}`,data:{event:"transcript_reset",transcriptPath:r},provenance:{source:"agy-transcript",sourcePath:r}}),a.push(...u.events),t.phase==="stop"&&(t.error&&a.push({type:"error",sourceEventId:["agy",e,"stop-error",t.terminationReason??"",u.nextOffset].join(":"),data:{error:t.error,terminationReason:t.terminationReason,fullyIdle:t.fullyIdle},provenance:{source:"antigravity-stop-hook"}}),t.fullyIdle&&a.push({type:"session_idle",sourceEventId:["agy",e,"idle",u.nextOffset].join(":"),data:{terminationReason:t.terminationReason,fullyIdle:!0},provenance:{source:"antigravity-stop-hook"}}));let c=n.recordMany(a);n.setSourceCursor("agy.transcript.offset",u.nextOffset),t.phase&&n.setSourceCursor("agy.last.phase",t.phase);let f=await n.flush();return{conversationId:e,imported:c.length,eventCount:f.eventCount,chunkCount:f.chunkCount,status:f.status,transcriptOffset:u.nextOffset,reset:u.reset}}async function is(){let t="";for await(let e of process.stdin)t+=e.toString();if(!t.trim())return{};try{return JSON.parse(t)}catch{return{}}}function as(t){return Array.isArray(t)?t.filter(e=>typeof e=="string"):[]}async function cs(){let t=process.argv[2]??"post",e=await is(),r=typeof e.conversationId=="string"?e.conversationId:"",n=typeof e.transcriptPath=="string"?e.transcriptPath:"",s=as(e.workspacePaths),o=it(s),i={};if(o&&r)try{let u=ke(),a=Re(Ne({provider:u.storage.provider,huggingface:u.storage.huggingface,localRoot:u.storage.localRoot}),{attempts:2}),c=new H(a,o.id,o.name,o.remote??o.name);if(n)try{await wt({project:o,storage:c,conversationId:r,transcriptPath:n,workspacePaths:s,artifactDirectoryPath:typeof e.artifactDirectoryPath=="string"?e.artifactDirectoryPath:void 0,modelName:typeof e.modelName=="string"?e.modelName:void 0,phase:t,fullyIdle:e.fullyIdle===!0,terminationReason:typeof e.terminationReason=="string"?e.terminationReason:void 0,error:typeof e.error=="string"&&e.error?e.error:void 0})}catch{}if(t==="pre")try{i=await ot({project:o,storage:c,conversationId:r,invocationNum:typeof e.invocationNum=="number"?e.invocationNum:void 0})}catch{i={}}if(t==="stop")try{await he(o,c,900)}catch{}}catch{}if(t==="stop"){process.stdout.write(JSON.stringify({decision:"stop"}));return}process.stdout.write(JSON.stringify(i))}cs().catch(()=>{process.argv[2]==="stop"?process.stdout.write('{"decision":"stop"}'):process.stdout.write("{}"),process.exitCode=0});
@@ -0,0 +1,182 @@
1
+ import{existsSync as y}from"node:fs";import{homedir as h}from"node:os";import{join as x}from"node:path";import{spawnSync as ee}from"node:child_process";import{existsSync as R,mkdirSync as $,readFileSync as M,writeFileSync as P}from"node:fs";import{homedir as _}from"node:os";import{dirname as J,join as B}from"node:path";function H(o){return`'${o.replace(/'/g,"'\\''")}'`}function S(o={}){let t=o.hooksFile??B(_(),".gemini","config","hooks.json");$(J(t),{recursive:!0});let e={};if(R(t))try{e=JSON.parse(M(t,"utf8"))}catch(a){throw new Error(`Invalid existing Agy hooks.json: ${a instanceof Error?a.message:String(a)}`)}let r=o.binary??"toolnet-memory",n=`${H(r)} session:agy-hook`;return e["toolnet-memory"]={enabled:!0,PreInvocation:[{type:"command",command:`${n} pre`,timeout:15}],PostInvocation:[{type:"command",command:`${n} post`,timeout:15}],Stop:[{type:"command",command:`${n} stop`,timeout:30}]},P(t,JSON.stringify(e,null,2)+`
2
+ `,{encoding:"utf8",mode:384}),t}import{existsSync as D,mkdirSync as b,readFileSync as L,writeFileSync as C}from"node:fs";import{homedir as p}from"node:os";import{join as g}from"node:path";var k="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",f="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function W(){let o=g(p(),".config","opencode","AGENTS.md");b(g(p(),".config","opencode"),{recursive:!0});let t=`${k}
3
+ ## ToolNet Memory \u2014 Project Continuity
4
+
5
+ When the current workspace belongs to a ToolNet project, identified by a nearest ancestor containing \`.toolnet/project.json\`:
6
+
7
+ 1. Before planning, editing, or running implementation commands, read that project root's \`.toolnet/context/startup.md\`.
8
+ 2. Treat its PROJECT RULES, MISSION, CURRENT OBJECTIVE, rationale, current phase/task, Definition of Done, blockers and next actions as project continuity context.
9
+ 3. Do not restart work already marked completed unless repository evidence proves it must be revisited.
10
+ 4. Verify current repository state before continuing old work.
11
+ 5. If \`.toolnet/context/startup.md\` is missing, run \`toolnet-memory context:sync --project <project-root>\`, then read it.
12
+ 6. Never invent missing rationale. If ToolNet says a reason was not explicitly recorded, inspect the implementation or ask rather than guessing.
13
+
14
+ This applies automatically to every ToolNet-managed project.
15
+ ${f}`,e=D(o)?L(o,"utf8"):"",r=e.indexOf(k),n=e.indexOf(f);return r>=0&&n>=r?e=e.slice(0,r)+t+e.slice(n+f.length):(e=e.trimEnd(),e&&(e+=`
16
+
17
+ `),e+=t),C(o,e.trimEnd()+`
18
+ `,{encoding:"utf8",mode:384}),o}function I(o={}){let t=o.directory??g(p(),".config","opencode","plugins");b(t,{recursive:!0}),W();let e=g(t,"toolnet-memory.js"),r=o.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=`// Generated by ToolNet Memory.
19
+ // OpenCode session capture + continuity bootstrap.
20
+
21
+ function getSessionId(event) {
22
+ const p = event?.properties ?? {}
23
+
24
+ const candidates = [
25
+ p.sessionID,
26
+ p.sessionId,
27
+ p.info?.id,
28
+ p.session?.id,
29
+ ]
30
+
31
+ return candidates.find(
32
+ value => typeof value === "string" && value.length > 0
33
+ ) ?? null
34
+ }
35
+
36
+ export const ToolNetMemoryPlugin = async ({ directory }) => {
37
+ const binary = ${JSON.stringify(r)}
38
+
39
+ async function refreshContext() {
40
+ try {
41
+ const child = Bun.spawn(
42
+ [
43
+ binary,
44
+ "context:sync",
45
+ "--project",
46
+ directory,
47
+ ],
48
+ {
49
+ stdin: "ignore",
50
+ stdout: "ignore",
51
+ stderr: "ignore",
52
+ }
53
+ )
54
+
55
+ await child.exited
56
+ } catch {
57
+ // Context sync must never break OpenCode startup.
58
+ }
59
+ }
60
+
61
+ async function readContext() {
62
+ try {
63
+ const child = Bun.spawn(
64
+ [
65
+ binary,
66
+ "context:print",
67
+ "--project",
68
+ directory,
69
+ "--tokens",
70
+ "900",
71
+ ],
72
+ {
73
+ stdin: "ignore",
74
+ stdout: "pipe",
75
+ stderr: "ignore",
76
+ }
77
+ )
78
+
79
+ const text =
80
+ await new Response(
81
+ child.stdout
82
+ ).text()
83
+
84
+ await child.exited
85
+
86
+ return text.trim()
87
+ } catch {
88
+ return ""
89
+ }
90
+ }
91
+
92
+ function sync(sessionId, flag) {
93
+ if (!sessionId) return
94
+
95
+ try {
96
+ const args = [
97
+ binary,
98
+ "session:opencode-sync",
99
+ sessionId,
100
+ "--project",
101
+ directory,
102
+ ]
103
+
104
+ if (flag) {
105
+ args.push(flag)
106
+ }
107
+
108
+ const child = Bun.spawn(args, {
109
+ stdin: "ignore",
110
+ stdout: "ignore",
111
+ stderr: "ignore",
112
+ })
113
+
114
+ if (
115
+ typeof child.unref === "function"
116
+ ) {
117
+ child.unref()
118
+ }
119
+ } catch {
120
+ // Memory capture must never break OpenCode.
121
+ }
122
+ }
123
+
124
+ /*
125
+ * Pull the latest project brief from ToolNet remote storage
126
+ * before the new OpenCode session starts doing work.
127
+ */
128
+ await refreshContext()
129
+
130
+ return {
131
+ event: async ({ event }) => {
132
+ const sessionId =
133
+ getSessionId(event)
134
+
135
+ if (!sessionId) {
136
+ return
137
+ }
138
+
139
+ if (event.type === "session.idle") {
140
+ sync(sessionId, "--idle")
141
+ return
142
+ }
143
+
144
+ if (event.type === "session.compacted") {
145
+ sync(sessionId, "--compacted")
146
+ return
147
+ }
148
+
149
+ if (event.type === "session.error") {
150
+ sync(sessionId, "--error")
151
+ }
152
+ },
153
+
154
+ /*
155
+ * Official OpenCode compaction hook.
156
+ * Preserve ToolNet's semantic state after context compaction.
157
+ */
158
+ "experimental.session.compacting": async (_input, output) => {
159
+ const context =
160
+ await readContext()
161
+
162
+ if (
163
+ context &&
164
+ Array.isArray(
165
+ output.context
166
+ )
167
+ ) {
168
+ output.context.push(
169
+ context
170
+ )
171
+ }
172
+ },
173
+ }
174
+ }
175
+ `;return C(e,n,{encoding:"utf8",mode:384}),e}import{existsSync as Y,mkdirSync as N,readFileSync as X,writeFileSync as T}from"node:fs";import{homedir as E}from"node:os";import{dirname as w,join as m}from"node:path";function q(o){let t=[],e=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,r;for(;r=e.exec(o);){let n=r[1]??r[2]??"";try{t.push(r[1]!==void 0?JSON.parse(`"${n}"`):n)}catch{t.push(n)}}return t}function j(o={}){let t=o.configFile??m(process.env.CODEX_HOME??m(E(),".codex"),"config.toml"),e=o.previousFile??m(E(),".config","toolnet-memory","codex-notify-previous.json");N(w(t),{recursive:!0}),N(w(e),{recursive:!0});let r=Y(t)?X(t,"utf8"):"",n=o.binary??"toolnet-memory",a=`notify = [${JSON.stringify(n)}, "session:codex-notify"]`,s=r.split(`
176
+ `),i=s.findIndex(c=>/^\s*\[/.test(c));i<0&&(i=s.length);let u=-1,d=-1;for(let c=0;c<i;c+=1)if(/^\s*notify\s*=/.test(s[c])){if(u=c,d=c,s[c].includes("[")&&!s[c].includes("]"))for(;d+1<i&&(d+=1,!s[d].includes("]")););break}let l=[];if(u>=0){let c=s.slice(u,d+1).join(`
177
+ `);l=q(c),s.splice(u,d-u+1,a)}else i=s.findIndex(c=>/^\s*\[/.test(c)),i<0&&(i=s.length),s.splice(i,0,a);let O=l.length>=2&&l[l.length-1]==="session:codex-notify";return l.length>0&&!O&&T(e,JSON.stringify(l,null,2)+`
178
+ `,{encoding:"utf8",mode:384}),r=s.join(`
179
+ `),r.endsWith(`
180
+ `)||(r+=`
181
+ `),T(t,r,{encoding:"utf8",mode:384}),{configFile:t,previousFile:e,preservedPrevious:l.length>0&&!O}}import{existsSync as G,mkdirSync as U,readFileSync as V,writeFileSync as z}from"node:fs";import{homedir as K}from"node:os";import{dirname as Q,join as A}from"node:path";function Z(o){return`'${o.replace(/'/g,"'\\''")}'`}function F(o={}){let t=o.hooksFile??A(process.env.CODEX_HOME??A(K(),".codex"),"hooks.json");U(Q(t),{recursive:!0});let e={};if(G(t))try{e=JSON.parse(V(t,"utf8"))}catch(i){throw new Error(`Invalid existing Codex hooks.json: ${i instanceof Error?i.message:String(i)}`)}let r=e.hooks&&typeof e.hooks=="object"&&!Array.isArray(e.hooks)?e.hooks:{};e.hooks=r;let a=(Array.isArray(r.SessionStart)?r.SessionStart:[]).filter(i=>{try{return!JSON.stringify(i).includes("session:codex-context")}catch{return!0}}),s=o.binary??"toolnet-memory";return a.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${Z(s)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),r.SessionStart=a,z(t,JSON.stringify(e,null,2)+`
182
+ `,{encoding:"utf8",mode:384}),t}function v(o){return ee("sh",["-lc",`command -v ${JSON.stringify(o)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function te(){return v("agy")||y(x(h(),".gemini"))}function oe(){return v("opencode")||y(x(h(),".config","opencode"))}function ne(){return v("codex")||y(process.env.CODEX_HOME??x(h(),".codex"))}function re(o={}){let t=o.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",e=[];if(!(o.force===!0||te()))e.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let n=S({binary:t});e.push({agent:"agy",detected:!0,installed:!0,targets:[n]})}catch(n){e.push({agent:"agy",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(o.force===!0||oe()))e.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let n=I({binary:t});e.push({agent:"opencode",detected:!0,installed:!0,targets:[n]})}catch(n){e.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(o.force===!0||ne()))e.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let n=j({binary:t}),a=F({binary:t}),s=[n.configFile,a];n.preservedPrevious&&s.push(n.previousFile),e.push({agent:"codex",detected:!0,installed:!0,targets:s})}catch(n){e.push({agent:"codex",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}return e}function se(o){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let t of o){let e=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":"Codex";if(!t.detected){console.log(`- ${e}: not detected`);continue}if(t.installed){console.log(`\u2713 ${e}: automatic memory enabled`);continue}console.log(`\u2717 ${e}: integration failed`),t.error&&console.log(` ${t.error}`)}console.log("")}async function ie(){let o=process.argv.slice(2),t=o.includes("--all"),e=o.includes("--json"),r=re({force:t});if(e){console.log(JSON.stringify(r,null,2));return}se(r)}var ce=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));ce&&ie().catch(o=>{console.error(o instanceof Error?o.message:String(o)),process.exitCode=1});export{re as installAutoIntegrations};
@@ -0,0 +1,55 @@
1
+ import{existsSync as oe,readFileSync as se}from"node:fs";import{homedir as ie}from"node:os";import{join as ae}from"node:path";function ce(r){let e=r.trim();return e.length>=2&&e.startsWith('"')&&e.endsWith('"')?(e=e.slice(1,-1),e.replace(/\\n/g,`
2
+ `).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):e.length>=2&&e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function pe(){let r=process.env.TOOLNET_GLOBAL_ENV??ae(ie(),".config","toolnet-memory",".env");if(!oe(r))return;let e=se(r,"utf8");for(let t of e.split(/\r?\n/)){let n=t.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let a=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(a)&&process.env[a]===void 0&&(process.env[a]=ce(n.slice(o+1)))}}pe();function v(r,e){return r===void 0?e:["1","true","yes","on"].includes(r.toLowerCase())}function y(r,e){if(!r)return e;let t=Number(r);return Number.isFinite(t)?t:e}function N(){return{memory:{autoCapture:v(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:v(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:v(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:v(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:y(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:y(process.env.MEMORY_RERANK_TOP,10),finalContext:y(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:y(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",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:y(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as ue}from"node:crypto";import{existsSync as I,mkdirSync as le,readFileSync as de,renameSync as me,writeFileSync as fe}from"node:fs";import{basename as ge,dirname as k,join as x,parse as _,resolve as S}from"node:path";var W=".toolnet",he="project.json";function ye(r){return ue("sha256").update(r).digest("hex").slice(0,16)}function A(r){return x(r,W,he)}function Se(r){return I(A(r))}function je(r,e){let t=S(r),n=_(t).root;for(;;){if(Se(t))return t;if(t===n||e&&t===S(e))break;let o=k(t);if(o===t)break;t=o}return null}function ve(r){let e=S(r),t=_(e).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(a=>I(x(e,a))))return e;if(e===t)break;let o=k(e);if(o===e)break;e=o}return S(r)}function ke(r){let e;try{e=JSON.parse(de(r,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${r}: ${o instanceof Error?o.message:String(o)}`)}if(!e||typeof e!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let t=e;if(typeof t.id!="string"||!t.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof t.name!="string"||!t.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let n=new Date().toISOString();return{version:1,id:t.id,name:t.name,remote:typeof t.remote=="string"&&t.remote.trim()?t.remote:t.name,rootPath:typeof t.rootPath=="string"?t.rootPath:k(k(r)),createdAt:typeof t.createdAt=="string"?t.createdAt:n,updatedAt:typeof t.updatedAt=="string"?t.updatedAt:n,graphVersion:typeof t.graphVersion=="number"?t.graphVersion:0,memoryVersion:typeof t.memoryVersion=="number"?t.memoryVersion:0,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0}}function C(r,e){let t=x(r,W);le(t,{recursive:!0});let n=A(r),o=`${n}.tmp-${process.pid}`;fe(o,JSON.stringify(e,null,2)+`
3
+ `,{encoding:"utf8",mode:384}),me(o,n)}function $(r,e){return{id:r.id,name:r.name,remote:r.remote,rootPath:e,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var w=class{detect(e=process.cwd()){let t=S(e),n=ve(t),a=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>I(x(n,f))),c=je(t,a?n:void 0);if(c){let f=A(c),s=ke(f);return s.rootPath!==c&&(s.rootPath=c,s.updatedAt=new Date().toISOString(),C(c,s)),$(s,c)}let l=new Date().toISOString(),i=ge(n),u={version:1,id:ye(n),name:i,remote:i,rootPath:n,createdAt:l,updatedAt:l,graphVersion:0,memoryVersion:0};return C(n,u),$(u,n)}};import{homedir as We}from"node:os";import{join as Be}from"node:path";import{DeleteObjectCommand as we,GetObjectCommand as xe,HeadObjectCommand as be,ListObjectsV2Command as Pe,PutObjectCommand as Oe,S3Client as Ie}from"@aws-sdk/client-s3";import{getSignedUrl as Ae}from"@aws-sdk/s3-request-presigner";var b=class{name="huggingface";client;bucket;constructor(e){this.bucket=e.bucket,this.client=new Ie({region:"us-east-1",endpoint:`https://s3.hf.co/${e.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}})}async put(e,t,n="application/octet-stream"){let o=typeof t=="string"?Buffer.from(t,"utf8"):t;await this.client.send(new Oe({Bucket:this.bucket,Key:e,Body:o,ContentType:n}))}async get(e){let t=await Ae(this.client,new xe({Bucket:this.bucket,Key:e}),{expiresIn:60}),n=await fetch(t,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`HF download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await this.client.send(new be({Bucket:this.bucket,Key:e})),!0}catch(t){if(typeof t=="object"&&t!==null&&"$metadata"in t&&t.$metadata?.httpStatusCode===404)return!1;throw t}}async delete(e){await this.client.send(new we({Bucket:this.bucket,Key:e}))}async list(e=""){let t=[],n;do{let o=await this.client.send(new Pe({Bucket:this.bucket,Prefix:e||void 0,ContinuationToken:n}));for(let a of o.Contents??[])a.Key&&t.push({key:a.Key,size:a.Size,updatedAt:a.LastModified?.toISOString()});n=o.IsTruncated?o.NextContinuationToken:void 0}while(n);return t}};import{access as B,mkdir as Me,readFile as Te,readdir as Ee,rm as Re,stat as D,writeFile as Ne}from"node:fs/promises";import{dirname as Ce,join as $e,relative as L,resolve as _e}from"node:path";var j=class{constructor(e){this.root=e}root;name="local";path(e){let t=e.replace(/^\/+/,"");return _e(this.root,t)}async put(e,t){let n=this.path(e);await Me(Ce(n),{recursive:!0}),await Ne(n,t)}async get(e){try{return await Te(this.path(e))}catch(t){if(typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT")return null;throw t}}async getText(e){let t=await this.get(e);return t?Buffer.from(t).toString("utf8"):null}async exists(e){try{return await B(this.path(e)),!0}catch{return!1}}async delete(e){await Re(this.path(e),{force:!0})}async list(e=""){let t=this.path(e),n=[];try{await B(t)}catch{return n}let o=async c=>{let l=await Ee(c,{withFileTypes:!0});for(let i of l){let u=$e(c,i.name);if(i.isDirectory()){await o(u);continue}let f=await D(u);n.push({key:L(this.root,u),size:f.size,updatedAt:f.mtime.toISOString()})}},a=await D(t);return a.isDirectory()?await o(t):n.push({key:L(this.root,t),size:a.size,updatedAt:a.mtime.toISOString()}),n}};function K(r){let e=r.localRoot??Be(We(),".toolnet-memory","storage");if(r.provider==="huggingface"){let t=r.huggingface;return t?.namespace&&t.bucket&&t.accessKeyId&&t.secretAccessKey?new b({namespace:t.namespace,bucket:t.bucket,accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}):(console.warn("[storage] Hugging Face credentials missing. Using local fallback."),new j(e))}return new j(e)}function De(r){return new Promise(e=>setTimeout(e,r))}async function z(r,e={}){let t=Math.max(1,e.attempts??3),n=e.baseDelayMs??150,o=e.maxDelayMs??2e3,a;for(let c=1;c<=t;c++)try{return await r()}catch(l){if(a=l,c>=t)break;let i=Math.min(o,n*2**(c-1)),u=Math.floor(Math.random()*Math.max(1,i*.2));await De(i+u)}throw a}var Le=new Set(["put","get","getText","delete","list"]);function F(r,e={}){return new Proxy(r,{get(t,n){let o=Reflect.get(t,n,t);return typeof o!="function"?o:Le.has(n)?(...a)=>z(()=>Promise.resolve(o.apply(t,a)),e):o.bind(t)}})}function V(r){let e=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!e||e==="."||e==="..")throw new Error("Invalid project storage folder");return e}function J(r){let e=r.replace(/^\/+/,"");if(e.startsWith("memories/"))return"memory/records/"+e.slice(9);if(e.startsWith("vectors/"))return"memory/vectors/"+e.slice(8);if(e.startsWith("graph/"))return"code/graph/"+e.slice(6);let t=e.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(t)return`${t[1]}memory/records/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),t)return`${t[1]}memory/vectors/${t[2]}`;if(t=e.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),t)return`${t[1]}code/graph/${t[2]}`;let n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=e.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let o=e.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return o?`${o[1]}memory/records/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),o?`${o[1]}memory/vectors/${o[2]}`:(o=e.match(/^(projects\/[^/]+\/)graph\/(.+)$/),o?`${o[1]}code/graph/${o[2]}`:e))}var P=class{constructor(e,t,n,o){this.provider=e;this.name=e.name,this.projectId=t,this.projectName=n,this.folder=V(o??n),this.sourcePrefix=`projects/${t}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let e=`${this.targetPrefix}/project.json`,t=new Date().toISOString(),n=t,o=await this.provider.getText(e);if(o){let c;try{c=JSON.parse(o)}catch(l){throw new Error(`Invalid remote ToolNet project manifest at ${e}: ${l instanceof Error?l.message:String(l)}`)}if(typeof c.id=="string"&&c.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${c.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof c.createdAt=="string"&&(n=c.createdAt)}let a={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:t};await this.provider.put(e,JSON.stringify(a,null,2)+`
4
+ `,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(e){if(e=J(e),e===this.sourcePrefix)return this.targetPrefix;if(e.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+e.slice(this.sourcePrefix.length);if(e===this.targetPrefix||e.startsWith(`${this.targetPrefix}/`))return e;if(e.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${e}`].join(" "));return e}async put(e,t,n){return await this.ensureRegistered(),this.provider.put(this.key(e),t,n)}async get(e){return await this.ensureRegistered(),this.provider.get(this.key(e))}async getText(e){return await this.ensureRegistered(),this.provider.getText(this.key(e))}async delete(e){return await this.ensureRegistered(),this.provider.delete(this.key(e))}async exists(e){return await this.ensureRegistered(),this.provider.exists(this.key(e))}async list(e){return await this.ensureRegistered(),this.provider.list(this.key(e))}};import{createHash as Ke}from"node:crypto";import{dirname as ze}from"node:path";import{mkdirSync as Fe,readFileSync as on,renameSync as Ve,writeFileSync as Je}from"node:fs";function m(r){return Ke("sha256").update(r).digest("hex")}function h(r,e){Fe(ze(r),{recursive:!0});let t=`${r}.${process.pid}.tmp`;Je(t,JSON.stringify(e,null,2)+`
5
+ `,{encoding:"utf8",mode:384}),Ve(t,r)}async function M(r,e){let t=await e.getText(`projects/${r.id}/work/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}import{existsSync as U,mkdirSync as He,readFileSync as Ue,statSync as q,writeFileSync as Ye}from"node:fs";import{dirname as Ge,join as Qe}from"node:path";var H=64*1024,Xe=`# ToolNet Project Operating Manual
6
+
7
+ This file contains persistent instructions for AI agents working on this project.
8
+
9
+ ## Critical Rules
10
+
11
+ <!--
12
+ Examples:
13
+
14
+ - [enforce] Never edit production files directly.
15
+ - [enforce] Edit source only in /path/to/source.
16
+ - [enforce] Deploy only with ./deploy.sh --apply
17
+ -->
18
+
19
+ ## Workflow
20
+
21
+ <!--
22
+ Describe the correct working process.
23
+ -->
24
+
25
+ ## Architecture
26
+
27
+ <!--
28
+ Important architecture conventions.
29
+ -->
30
+
31
+ ## Verification
32
+
33
+ <!--
34
+ Tests / QA required after changes.
35
+ -->
36
+
37
+ ## Known Gotchas
38
+
39
+ <!--
40
+ Things an AI agent should always remember.
41
+ -->
42
+
43
+ ## Preferences
44
+
45
+ <!--
46
+ - [advisory] Prefer small focused files.
47
+ -->
48
+ `;function O(r){return Qe(r.rootPath,".toolnet","PROJECT.md")}function Ze(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function et(r){let e=[],t=new Set,n=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,o;for(;o=n.exec(r);){let a=o[1].toLowerCase(),c=Ze(o[2]);if(!c)continue;let l=`${a}:${c.toLowerCase()}`;t.has(l)||(t.add(l),e.push({id:m(l).slice(0,24),mode:a,text:c,source:"manual"}))}return e}function tt(r){let e=O(r);return U(e)||(He(Ge(e),{recursive:!0}),Ye(e,Xe,{encoding:"utf8",mode:384})),e}function T(r,e=!1){let t=e?tt(r):O(r);if(!U(t))return null;if(q(t).size>H)throw new Error(`PROJECT.md exceeds ${H} bytes`);let o=Ue(t,"utf8");return{path:t,content:o,digest:m(o),rules:et(o),bytes:Buffer.byteLength(o,"utf8"),updatedAt:new Date(q(t).mtimeMs).toISOString()}}async function Y(r,e){let t=await e.getText(`projects/${r.id}/work/handoff-latest.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}async function G(r,e){let t=await e.getText(`projects/${r.id}/work/semantic/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function E(r){if(!r)return 0;let e=Array.from(r).length,t=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,t*1.3))}function d(r,e){let t=r.replace(/\s+/g," ").trim();return t.length<=e?t:t.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function rt(r){let e=[],t=!1;for(let n of r.split(/\r?\n/u)){let o=n.trim();if(o.includes("<!--")&&(t=!0),t){o.includes("-->")&&(t=!1);continue}let a=o.toLowerCase();if(!(!o||o.startsWith("#")||o==="```"||a.startsWith("- [enforce]")||a.startsWith("* [enforce]")||a.startsWith("- [advisory]")||a.startsWith("* [advisory]"))&&(o=o.replace(/^[-*]\s+/u,""),o&&e.push(d(o,280)),e.length>=16))break}return e}function nt(r){let e=[],t=[];for(let n of r.split(/\\r?\\n/u)){let o=n.trim(),a=o.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(u=>a.startsWith(u));if(!l)continue;let i=o.slice(l.length).trim();i&&(l.includes("enforce")?e.push(i):t.push(i))}return{enforce:e,advisory:t}}function ot(r,e){let t=[];for(let n of r){let o=[...t,n].join(`
49
+ `);if(E(o)<=e){t.push(n);continue}let a=E(t.join(`
50
+ `)),c=Math.max(0,e-a);if(c>=16){let l=Math.floor(c*3.2),i=d(n,l);i&&t.push(i)}break}return t.join(`
51
+ `).trim()}async function Q(r){let e=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),t=T(r.project,!1),n=t?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let o=nt(n),a=t?t.rules.filter(p=>p.mode==="enforce").map(p=>p.text):o.enforce,c=t?t.rules.filter(p=>p.mode==="advisory").map(p=>p.text):o.advisory,l=n?rt(n):[],i=await M(r.project,r.storage),u=await G(r.project,r.storage),f=await Y(r.project,r.storage),s=[];if(s.push("[TOOLNET PROJECT CONTEXT]"),s.push(`Project: ${r.project.name}`),s.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&s.push(`Full operating manual: ${O(r.project)}`),a.length){s.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let p of a.slice(0,24))s.push(`- [ENFORCE] ${d(p,240)}`)}if(c.length){s.push("","PROJECT PREFERENCES");for(let p of c.slice(0,10))s.push(`- ${d(p,220)}`)}if(u&&(u.mission&&s.push("","MISSION",d(u.mission.value,420)),u.activeObjective&&s.push("","CURRENT OBJECTIVE",d(u.activeObjective.value,420)),u.why&&s.push("","WHY THIS WORK MATTERS",d(u.why.value,420)),u.desiredOutcome&&s.push("","DESIRED OUTCOME",d(u.desiredOutcome.value,420)),u.planRationale&&s.push("","WHY THIS APPROACH",d(u.planRationale.value,420))),i){if(s.push("","ACTIVE WORK"),i.goal&&s.push(`Goal: ${d(i.goal,320)}`),i.plan&&s.push(`Plan: ${d(i.plan,320)}`),s.push(`Progress: phases ${i.progress.phasesCompleted}/${i.progress.phasesTotal}; tasks ${i.progress.tasksCompleted}/${i.progress.tasksTotal}; blocked ${i.progress.blocked}`),i.currentPhase&&s.push(`Current phase: ${i.currentPhase.title} [${i.currentPhase.status}]`),i.currentPhase&&u){let p=u.phases.find(g=>g.order===i.currentPhase?.order);p&&(p.objective&&s.push(`Phase objective: ${d(p.objective.value,340)}`),p.why?s.push(`Why this phase: ${d(p.why.value,340)}`):s.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),p.deliverable&&s.push(`Deliverable: ${d(p.deliverable.value,340)}`),p.dependencies.length&&s.push(`Depends on: ${p.dependencies.slice(0,4).map(g=>d(g.value,180)).join("; ")}`),p.acceptanceCriteria.length&&(s.push("","DEFINITION OF DONE"),p.acceptanceCriteria.slice(0,6).forEach(g=>{s.push(`- ${d(g.value,260)}`)})),p.openQuestions.length&&(s.push("","OPEN QUESTIONS FOR CURRENT PHASE"),p.openQuestions.slice(0,4).forEach(g=>{s.push(`- ${d(g.value,260)}`)})))}i.currentTask&&s.push(`Current task: ${i.currentTask.title} [${i.currentTask.status}]`),i.nextActions.length&&(s.push("","NEXT ACTIONS"),i.nextActions.slice(0,6).forEach((p,g)=>{s.push(`${g+1}. ${d(p,260)}`)})),i.blockers.length&&(s.push("","BLOCKERS"),i.blockers.slice(0,5).forEach(p=>{s.push(`- ${d(p,260)}`)})),i.warnings.length&&(s.push("","ATTENTION"),i.warnings.slice(-5).forEach(p=>{s.push(`- ${d(p,260)}`)})),i.decisions.length&&(s.push("","RECENT DECISIONS"),i.decisions.slice(-5).forEach(p=>{s.push(`- ${d(p,260)}`)})),i.lastSession&&s.push("",`Last work session: ${i.lastSession.agent} / ${i.lastSession.nativeSessionId}`)}if(u&&u.openQuestions.length&&(s.push("","UNRESOLVED QUESTIONS"),u.openQuestions.slice(0,5).forEach(p=>{s.push(`- ${d(p.value,260)}`)})),f&&s.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),l.length){s.push("","OPERATING NOTES");for(let p of l)s.push(`- ${p}`)}s.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let R=ot(s,e);return{version:1,projectId:r.project.id,projectName:r.project.name,text:R,estimatedTokens:E(R),maxTokens:e,hasManual:!!n,hasWorkState:!!i,hasHandoff:!!f,generatedAt:new Date().toISOString()}}import{existsSync as st,mkdirSync as it,readFileSync as at,writeFileSync as ct}from"node:fs";import{dirname as pt,join as X}from"node:path";function Z(r){return X(r.rootPath,".toolnet","context","startup.md")}function ut(r){return X(r.rootPath,".toolnet","context","startup.json")}function ee(r,e){let t=Z(r);it(pt(t),{recursive:!0}),ct(t,e.text.endsWith(`
52
+ `)?e.text:e.text+`
53
+ `,{encoding:"utf8",mode:384}),h(ut(r),e)}async function lt(r,e,t=900){let n=await Q({project:r,storage:e,maxTokens:t}),o={version:1,projectId:r.id,projectName:r.name,text:n.text,digest:m(n.text),estimatedTokens:n.estimatedTokens,generatedAt:new Date().toISOString()};return ee(r,o),await e.put(`projects/${r.id}/context/startup.md`,o.text+`
54
+ `,"text/markdown"),await e.put(`projects/${r.id}/context/startup.json`,JSON.stringify(o,null,2)+`
55
+ `,"application/json"),o}async function te(r,e,t=900){try{let o=await e.getText(`projects/${r.id}/context/startup.md`);if(o&&o.trim()){let a=o.trim(),c={version:1,projectId:r.id,projectName:r.name,text:a,digest:m(a),estimatedTokens:Math.ceil(a.length/3.5),generatedAt:new Date().toISOString()};return ee(r,c),c}}catch{}let n=Z(r);if(st(n))try{let o=at(n,"utf8").trim();if(o)return{version:1,projectId:r.id,projectName:r.name,text:o,digest:m(o),estimatedTokens:Math.ceil(o.length/3.5),generatedAt:new Date().toISOString()}}catch{}try{return await lt(r,e,t)}catch{return null}}async function re(r){let e=await te(r.project,r.storage,900);return e?.text?{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.text}}:{}}import{existsSync as dt}from"node:fs";import{dirname as mt,join as ft,parse as gt,resolve as ht}from"node:path";function ne(r){let e=ht(r),t=gt(e).root;for(;;){if(dt(ft(e,".toolnet","project.json")))return new w().detect(e);if(e===t)break;let n=mt(e);if(n===e)break;e=n}return null}async function yt(){let r="";for await(let e of process.stdin)r+=e.toString();if(!r.trim())return{};try{return JSON.parse(r)}catch{return{}}}async function St(){let r=await yt();if(r.hook_event_name!=="SessionStart"){process.stdout.write("{}");return}let e=typeof r.cwd=="string"?r.cwd:"";if(!e){process.stdout.write("{}");return}let t=ne(e);if(!t){process.stdout.write("{}");return}try{let n=N(),o=F(K({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:2}),a=new P(o,t.id,t.name,t.remote??t.name),c=await re({project:t,storage:a});process.stdout.write(JSON.stringify(c))}catch{process.stdout.write("{}")}}St().catch(()=>{process.stdout.write("{}"),process.exitCode=0});