toolnet-memory 0.3.12 → 0.3.13
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 +0 -4
- package/README.md +5 -48
- package/bin/toolnet-memory +15 -42
- package/bundle/agy-hook.js +50 -50
- package/bundle/agy.js +17 -17
- package/bundle/api.js +15 -55
- package/bundle/auto-integrate.js +34 -33
- package/bundle/config.js +17 -12
- package/bundle/context-runtime.js +9 -9
- package/bundle/continuity-certify.js +13 -53
- package/bundle/doctor.js +4 -6
- package/bundle/full-index.js +21 -22
- package/bundle/graph-index.js +3 -3
- package/bundle/graph-ui.js +3 -3
- package/bundle/help-cli.js +4 -4
- package/bundle/impact.js +3 -3
- package/bundle/incremental.js +3 -3
- package/bundle/init.js +37 -36
- package/bundle/kilo.js +2 -0
- package/bundle/learner.js +4 -4
- package/bundle/mcp.js +18 -59
- package/bundle/memory-agent.js +7 -47
- package/bundle/project-manual.js +2 -2
- package/bundle/recovery-certify.js +13 -53
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +5 -6
- package/bundle/snapshot.js +3 -3
- package/bundle/status-cli.js +4 -4
- package/bundle/toolnet-cli.js +2 -0
- package/bundle/work-continuity.js +5 -5
- package/package.json +1 -3
- package/bundle/model-cli.js +0 -2
- package/bundle/provider.js +0 -6
- package/bundle/setup.js +0 -1159
|
@@ -10,9 +10,9 @@ ${n}
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
`)}function G(r,t){let e=[{title:"Profile",content:J(r,10),priority:100},{title:"Current Work",content:J(t,15),priority:90}];return Me(e,{maxTokens:800})}import{chmodSync as cr,existsSync as
|
|
13
|
+
`)}function G(r,t){let e=[{title:"Profile",content:J(r,10),priority:100},{title:"Current Work",content:J(t,15),priority:90}];return Me(e,{maxTokens:800})}import{chmodSync as cr,existsSync as Re,mkdirSync as ur,readFileSync as Ee,renameSync as lr,writeFileSync as dr}from"node:fs";import{dirname as fr,join as q}from"node:path";var Y="toolnet.context-offload.v1";function Ne(r){return q(r,".toolnet","offload")}function $e(r){return q(Ne(r),"graph.json")}function W(){return{schema:Y,version:1,updatedAt:new Date(0).toISOString(),nodes:[]}}function _e(r){let t=$e(r);if(!Re(t))return W();try{let e=JSON.parse(Ee(t,"utf8"));return e.schema!==Y||e.version!==1||!Array.isArray(e.nodes)?W():e}catch{return W()}}function U(r,t){let e=r.replace(/\s+/gu," ").trim();return e.length<=t?e:e.slice(0,t-1).trimEnd()+"\u2026"}function Q(r,t={}){let e=_e(r);if(e.nodes.length===0)return"";let n=Math.max(1,Math.min(12,t.maxAssets??6)),s=Math.max(320,Math.min(2400,t.maxChars??900)),o=["[TOOLNET CONTEXT OFFLOAD GRAPH]","Large tool/file payloads stay outside prompt context.","Read only a needed asset with MCP context_offload_read."];for(let a of e.nodes.slice(-n).reverse()){let d=U(a.sourceRefs.at(-1)??"unknown",72),i=a.files.length>0?` files=${U(a.files.join(","),120)}`:"",l=`event:${d} --offloads--> asset:${a.id.slice(0,12)} kind=${a.kind} bytes=${a.bytes}${i}`;if([...o,l].join(`
|
|
14
14
|
`).length>s)break;o.push(l)}return o.join(`
|
|
15
|
-
`)}import{existsSync as X,mkdirSync as
|
|
15
|
+
`)}import{existsSync as X,mkdirSync as vr,readFileSync as Z,renameSync as wr,writeFileSync as br}from"node:fs";import{dirname as Ar,join as ee}from"node:path";import{createHash as Fe}from"node:crypto";import{dirname as We}from"node:path";import{mkdirSync as De,readFileSync as kr,renameSync as Ke,writeFileSync as Le}from"node:fs";function y(r){return Fe("sha256").update(r).digest("hex")}function x(r,t){De(We(r),{recursive:!0});let e=`${r}.${process.pid}.tmp`;Le(e,JSON.stringify(t,null,2)+`
|
|
16
16
|
`,{encoding:"utf8",mode:384}),Ke(e,r)}function Be(r){return ee(r.rootPath,".toolnet","context","handoff.md")}function Ve(r){return ee(r.rootPath,".toolnet","context","handoff.json")}function He(r){let t=Be(r);if(!X(t))return null;try{let e=Z(t,"utf8").trim();if(!e)return null;let n=new Date(0).toISOString(),s=Ve(r);if(X(s))try{let o=JSON.parse(Z(s,"utf8"));typeof o.generatedAt=="string"&&(n=o.generatedAt)}catch{}return{version:1,projectId:r.id,projectName:r.name,text:e,digest:y(e),generatedAt:n}}catch{return null}}function te(r,t=1800){let e=He(r);if(!e?.text)return null;let n=e.text;return n.length>t&&(n=`${n.slice(0,t)}
|
|
17
17
|
|
|
18
18
|
[Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${r.name}`,`Updated: ${e.generatedAt}`,"",n].join(`
|
|
@@ -92,9 +92,9 @@ Use:
|
|
|
92
92
|
- mode="ai" for ambiguous or combined continuity questions.
|
|
93
93
|
|
|
94
94
|
Current repository evidence overrides stale memory.
|
|
95
|
-
`.trim()}function
|
|
95
|
+
`.trim()}function v(r){let t=m.resolve(r),e=m.parse(t).root;for(;t!==e;){let n=m.join(t,".toolnet");if(h.existsSync(n)&&h.statSync(n).isDirectory())return t;t=m.dirname(t)}return null}function T(r){try{return h.existsSync(r)?h.readFileSync(r,"utf-8").trim():null}catch{return null}}function se(r){return r.split(`
|
|
96
96
|
`).filter(n=>{let s=n.toUpperCase();return!(s.includes("SECRET")||s.includes("TOKEN")||s.includes("API_KEY")||s.includes("APIKEY")||s.includes("PASSWORD")||s.includes("PASS="))}).join(`
|
|
97
|
-
`)}function D(r={}){let t=r.projectPath||process.cwd(),e=
|
|
97
|
+
`)}function D(r={}){let t=r.projectPath||process.cwd(),e=v(t);if(!e)return null;let n=m.join(e,".toolnet","project.json"),s="Unknown";try{if(h.existsSync(n)){let _=JSON.parse(h.readFileSync(n,"utf-8"));s=_.name||_.projectName||"Unknown"}}catch{}let o=m.join(e,".toolnet","profile.md"),a=m.join(e,".toolnet","current.md"),d=T(o)||"",i=T(a)||"";d=se(d),i=se(i);let l=`[TOOLNET PROJECT CONTEXT]
|
|
98
98
|
|
|
99
99
|
Project: ${s}
|
|
100
100
|
Root: ${e}
|
|
@@ -114,7 +114,7 @@ Forbidden At Startup:
|
|
|
114
114
|
- Do not perform deep recovery merely because an agent starts.
|
|
115
115
|
- If the user asks to resume previous work and fast context is insufficient,
|
|
116
116
|
use memory_agent_ask before guessing.
|
|
117
|
-
`;return l+f+k+g+Oe}function ie(r={}){let t=r.projectPath||process.cwd(),e=
|
|
117
|
+
`;return l+f+k+g+Oe}function ie(r={}){let t=r.projectPath||process.cwd(),e=v(t);if(!e)throw new Error("Not in a ToolNet project (no .toolnet directory found)");let n=m.join(e,".toolnet","profile.md"),s=m.join(e,".toolnet","current.md"),o=T(n)||"",a=T(s)||"",d=`# AI Startup Instructions
|
|
118
118
|
|
|
119
119
|
Read and follow:
|
|
120
120
|
- .toolnet/profile.md
|
|
@@ -187,12 +187,12 @@ Things an AI agent should always remember.
|
|
|
187
187
|
`)),a=Math.max(0,t-o);if(a>=16){let d=Math.floor(a*3.2),i=p(n,d);i&&e.push(i)}break}return e.join(`
|
|
188
188
|
`).trim()}async function fe(r){let t=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),e=K(r.project,!1),n=e?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let s=tt(n),o=e?e.rules.filter(u=>u.mode==="enforce").map(u=>u.text):s.enforce,a=e?e.rules.filter(u=>u.mode==="advisory").map(u=>u.text):s.advisory,d=n?et(n):[],i=await L(r.project,r.storage),l=await pe(r.project,r.storage),f=await de(r.project,r.storage),c=[];if(c.push("[TOOLNET PROJECT CONTEXT]"),c.push(`Project: ${r.project.name}`),c.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&c.push(`Full operating manual: ${I(r.project)}`),o.length){c.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of o.slice(0,24))c.push(`- [ENFORCE] ${p(u,240)}`)}if(a.length){c.push("","PROJECT PREFERENCES");for(let u of a.slice(0,10))c.push(`- ${p(u,220)}`)}if(l&&(l.mission&&c.push("","MISSION",p(l.mission.value,420)),l.activeObjective&&c.push("","CURRENT OBJECTIVE",p(l.activeObjective.value,420)),l.why&&c.push("","WHY THIS WORK MATTERS",p(l.why.value,420)),l.desiredOutcome&&c.push("","DESIRED OUTCOME",p(l.desiredOutcome.value,420)),l.planRationale&&c.push("","WHY THIS APPROACH",p(l.planRationale.value,420))),i){if(c.push("","ACTIVE WORK"),i.goal&&c.push(`Goal: ${p(i.goal,320)}`),i.plan&&c.push(`Plan: ${p(i.plan,320)}`),c.push(`Progress: phases ${i.progress.phasesCompleted}/${i.progress.phasesTotal}; tasks ${i.progress.tasksCompleted}/${i.progress.tasksTotal}; blocked ${i.progress.blocked}`),i.currentPhase&&c.push(`Current phase: ${i.currentPhase.title} [${i.currentPhase.status}]`),i.currentPhase&&l){let u=l.phases.find(g=>g.order===i.currentPhase?.order);u&&(u.objective&&c.push(`Phase objective: ${p(u.objective.value,340)}`),u.why?c.push(`Why this phase: ${p(u.why.value,340)}`):c.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&c.push(`Deliverable: ${p(u.deliverable.value,340)}`),u.dependencies.length&&c.push(`Depends on: ${u.dependencies.slice(0,4).map(g=>p(g.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(c.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(g=>{c.push(`- ${p(g.value,260)}`)})),u.openQuestions.length&&(c.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(g=>{c.push(`- ${p(g.value,260)}`)})))}i.currentTask&&c.push(`Current task: ${i.currentTask.title} [${i.currentTask.status}]`),i.nextActions.length&&(c.push("","NEXT ACTIONS"),i.nextActions.slice(0,6).forEach((u,g)=>{c.push(`${g+1}. ${p(u,260)}`)})),i.blockers.length&&(c.push("","BLOCKERS"),i.blockers.slice(0,5).forEach(u=>{c.push(`- ${p(u,260)}`)})),i.warnings.length&&(c.push("","ATTENTION"),i.warnings.slice(-5).forEach(u=>{c.push(`- ${p(u,260)}`)})),i.decisions.length&&(c.push("","RECENT DECISIONS"),i.decisions.slice(-5).forEach(u=>{c.push(`- ${p(u,260)}`)})),i.lastSession&&c.push("",`Last work session: ${i.lastSession.agent} / ${i.lastSession.nativeSessionId}`)}if(l&&l.openQuestions.length&&(c.push("","UNRESOLVED QUESTIONS"),l.openQuestions.slice(0,5).forEach(u=>{c.push(`- ${p(u.value,260)}`)})),f&&c.push(`Latest handoff: ${f.reason} / ${f.sourceSession.agent}`),d.length){c.push("","OPERATING NOTES");for(let u of d)c.push(`- ${u}`)}c.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let k=rt(c,t);return{version:1,projectId:r.project.id,projectName:r.project.name,text:k,estimatedTokens:B(k),maxTokens:t,hasManual:!!n,hasWorkState:!!i,hasHandoff:!!f,generatedAt:new Date().toISOString()}}function it(r){return me(r.rootPath,".toolnet","context","startup.md")}function at(r){return me(r.rootPath,".toolnet","context","startup.json")}function ct(r,t){let e=it(r);nt(ot(e),{recursive:!0}),st(e,t.text.endsWith(`
|
|
189
189
|
`)?t.text:t.text+`
|
|
190
|
-
`,{encoding:"utf8",mode:384}),
|
|
190
|
+
`,{encoding:"utf8",mode:384}),x(at(r),t)}async function ge(r,t,e=800){let s=(await fe({project:r,storage:t,maxTokens:e})).text;S(s)>e&&(s=F(s,e),s+=`
|
|
191
191
|
|
|
192
192
|
[Context trimmed by ToolNet Memory token budget]
|
|
193
193
|
`);let a={version:1,projectId:r.id,projectName:r.name,text:s,digest:y(s),estimatedTokens:S(s),generatedAt:new Date().toISOString()};return ct(r,a),await t.put(`projects/${r.id}/context/startup.md`,a.text+`
|
|
194
194
|
`,"text/markdown"),await t.put(`projects/${r.id}/context/startup.json`,JSON.stringify(a,null,2)+`
|
|
195
195
|
`,"application/json"),a}import{existsSync as ut,readFileSync as lt}from"node:fs";import{homedir as dt}from"node:os";import{join as pt}from"node:path";function ft(r){let t=r.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
|
|
196
|
-
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function mt(){let r=process.env.TOOLNET_GLOBAL_ENV??pt(dt(),".config","toolnet-memory",".env");if(!ut(r))return;let t=lt(r,"utf8");for(let e of t.split(/\r?\n/)){let n=e.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]=ft(n.slice(s+1)))}}mt();function w(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function
|
|
197
|
-
`,{encoding:"utf8",mode:384}),kt(s,n)}function ke(r,t){return{id:r.id,name:r.name,remote:r.remote,rootPath:t,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var M=class{detect(t=process.cwd()){let e=
|
|
198
|
-
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=Te(t),t===this.sourcePrefix)return this.targetPrefix;if(t.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+t.slice(this.sourcePrefix.length);if(t===this.targetPrefix||t.startsWith(`${this.targetPrefix}/`))return t;if(t.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${t}`].join(" "));return t}async put(t,e,n){return await this.ensureRegistered(),this.provider.put(this.key(t),e,n)}async get(t){return await this.ensureRegistered(),this.provider.get(this.key(t))}async getText(t){return await this.ensureRegistered(),this.provider.getText(this.key(t))}async delete(t){return await this.ensureRegistered(),this.provider.delete(this.key(t))}async exists(t){return await this.ensureRegistered(),this.provider.exists(this.key(t))}async list(t){return await this.ensureRegistered(),this.provider.list(this.key(t))}};function Zt(){let r=process.argv.slice(2),t=r[0]||"print",e={mode:"minimal"};for(let n=1;n<r.length;n++)r[n]==="--project"&&r[n+1]?(e.project=r[n+1],n++):r[n]==="--limit"&&r[n+1]?(e.limit=parseInt(r[n+1],10),n++):r[n]==="--focused"&&r[n+1]?(e.mode="focused",e.query=r[n+1],n++):r[n]==="--deep"&&(e.mode="deep");return{command:t,options:e}}async function er(){let{command:r,options:t}=Zt();try{switch(r){case"print":await tr(t);break;case"sync":await rr(t);break;case"refresh":await nr(t);break;case"profile-show":await sr(t);break;case"profile-sync":await or(t);break;default:console.error(`Unknown command: ${r}`),console.error("Available commands: print, sync, refresh, profile-show, profile-sync"),process.exit(1)}}catch(e){console.error("Error:",e instanceof Error?e.message:String(e)),process.exit(1)}}async function tr(r){let t=r.mode||"minimal";if(t==="minimal"){let e=D({projectPath:r.project});e||(console.error("No ToolNet project found. Run toolnet-memory init first."),process.exit(1)),process.stdout.write(e)}else(t==="focused"||t==="deep")&&(console.error("Focused and deep modes require storage access."),console.error("Use: toolnet-memory brief --deep for deep context"),process.exit(1))}async function rr(r){let t=D({projectPath:r.project});t||(console.error("No ToolNet project found."),process.exit(1));let e=ae(t);console.log(`Context hash: ${e}`),console.log(`Context size: ${t.length} chars`)}async function nr(r){console.log("Refreshing deep startup brief cache...");let t=r.project||process.cwd(),e=
|
|
196
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function mt(){let r=process.env.TOOLNET_GLOBAL_ENV??pt(dt(),".config","toolnet-memory",".env");if(!ut(r))return;let t=lt(r,"utf8");for(let e of t.split(/\r?\n/)){let n=e.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]=ft(n.slice(s+1)))}}mt();function w(r,t){return r===void 0?t:["1","true","yes","on"].includes(r.toLowerCase())}function b(r,t){if(!r)return t;let e=Number(r);return Number.isFinite(e)?e:t}function he(){return{memory:{autoCapture:w(process.env.MEMORY_AUTO_CAPTURE,!0),autoRetrieve:w(process.env.MEMORY_AUTO_RETRIEVE,!0),autoSummarize:w(process.env.MEMORY_AUTO_SUMMARIZE,!0),autoSync:w(process.env.MEMORY_AUTO_SYNC,!0)},retrieval:{maxCandidates:b(process.env.MEMORY_MAX_CANDIDATES,50),rerankTop:b(process.env.MEMORY_RERANK_TOP,10),finalContext:b(process.env.MEMORY_FINAL_CONTEXT,5),tokenBudget:b(process.env.MEMORY_TOKEN_BUDGET,2e3)},storage:{provider:process.env.MEMORY_STORAGE_PROVIDER??"huggingface",r2:{accountId:process.env.R2_ACCOUNT_ID,bucket:process.env.R2_BUCKET,accessKeyId:process.env.R2_ACCESS_KEY_ID,secretAccessKey:process.env.R2_SECRET_ACCESS_KEY},s3:{endpoint:process.env.S3_ENDPOINT,region:process.env.S3_REGION,bucket:process.env.S3_BUCKET,accessKeyId:process.env.S3_ACCESS_KEY_ID,secretAccessKey:process.env.S3_SECRET_ACCESS_KEY,forcePathStyle:w(process.env.S3_FORCE_PATH_STYLE,!1)},huggingface:{namespace:process.env.HF_NAMESPACE,bucket:process.env.HF_BUCKET,accessKeyId:process.env.HF_S3_ACCESS_KEY_ID,secretAccessKey:process.env.HF_S3_SECRET_ACCESS_KEY},localRoot:process.env.MEMORY_LOCAL_STORAGE_PATH},cache:{maxMb:b(process.env.MEMORY_LOCAL_CACHE_MB,200)}}}import{createHash as gt}from"node:crypto";import{existsSync as V,mkdirSync as ht,readFileSync as yt,renameSync as kt,writeFileSync as St}from"node:fs";import{basename as xt,dirname as O,join as R,parse as Se,resolve as j}from"node:path";var xe=".toolnet",vt="project.json";function wt(r){return gt("sha256").update(r).digest("hex").slice(0,16)}function H(r){return R(r,xe,vt)}function bt(r){return V(H(r))}function jt(r,t){let e=j(r),n=Se(e).root;for(;;){if(bt(e))return e;if(e===n||t&&e===j(t))break;let s=O(e);if(s===e)break;e=s}return null}function At(r){let t=j(r),e=Se(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(o=>V(R(t,o))))return t;if(t===e)break;let s=O(t);if(s===t)break;t=s}return j(r)}function Pt(r){let t;try{t=JSON.parse(yt(r,"utf8"))}catch(s){throw new Error(`Invalid ToolNet project manifest: ${r}: ${s instanceof Error?s.message:String(s)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${r}`);let e=t;if(typeof e.id!="string"||!e.id.trim())throw new Error(`ToolNet project manifest is missing id: ${r}`);if(typeof e.name!="string"||!e.name.trim())throw new Error(`ToolNet project manifest is missing name: ${r}`);let n=new Date().toISOString();return{version:1,id:e.id,name:e.name,remote:typeof e.remote=="string"&&e.remote.trim()?e.remote:e.name,rootPath:typeof e.rootPath=="string"?e.rootPath:O(O(r)),createdAt:typeof e.createdAt=="string"?e.createdAt:n,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:n,graphVersion:typeof e.graphVersion=="number"?e.graphVersion:0,memoryVersion:typeof e.memoryVersion=="number"?e.memoryVersion:0,metadata:e.metadata&&typeof e.metadata=="object"?e.metadata:void 0}}function ye(r,t){let e=R(r,xe);ht(e,{recursive:!0});let n=H(r),s=`${n}.tmp-${process.pid}`;St(s,JSON.stringify(t,null,2)+`
|
|
197
|
+
`,{encoding:"utf8",mode:384}),kt(s,n)}function ke(r,t){return{id:r.id,name:r.name,remote:r.remote,rootPath:t,createdAt:r.createdAt,updatedAt:r.updatedAt,graphVersion:r.graphVersion,memoryVersion:r.memoryVersion,metadata:r.metadata}}var M=class{detect(t=process.cwd()){let e=j(t),n=At(e),o=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(f=>V(R(n,f))),a=jt(e,o?n:void 0);if(a){let f=H(a),c=Pt(f);return c.rootPath!==a&&(c.rootPath=a,c.updatedAt=new Date().toISOString(),ye(a,c)),ke(c,a)}let d=new Date().toISOString(),i=xt(n),l={version:1,id:wt(n),name:i,remote:i,rootPath:n,createdAt:d,updatedAt:d,graphVersion:0,memoryVersion:0};return ye(n,l),ke(l,n)}};import{homedir as qt}from"node:os";import{join as Yt}from"node:path";import{DeleteObjectCommand as Ct,GetObjectCommand as Tt,HeadObjectCommand as It,ListObjectsV2Command as Ot,PutObjectCommand as Mt,S3Client as Rt}from"@aws-sdk/client-s3";import{getSignedUrl as Et}from"@aws-sdk/s3-request-presigner";var E=class{name="huggingface";client;bucket;constructor(t){this.bucket=t.bucket,this.client=new Rt({region:"us-east-1",endpoint:`https://s3.hf.co/${t.namespace}`,forcePathStyle:!0,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,n="application/octet-stream"){let s=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Mt({Bucket:this.bucket,Key:t,Body:s,ContentType:n}))}async get(t){let e=await Et(this.client,new Tt({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{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(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new It({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Ct({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let s=await this.client.send(new Ot({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&e.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return e}};import{access as ve,mkdir as Nt,readFile as $t,readdir as _t,rm as Ft,stat as we,writeFile as Wt}from"node:fs/promises";import{dirname as Dt,join as Kt,relative as be,resolve as Lt}from"node:path";var A=class{constructor(t){this.root=t}root;name="local";path(t){let e=t.replace(/^\/+/,"");return Lt(this.root,e)}async put(t,e){let n=this.path(t);await Nt(Dt(n),{recursive:!0}),await Wt(n,e)}async get(t){try{return await $t(this.path(t))}catch(e){if(typeof e=="object"&&e!==null&&"code"in e&&e.code==="ENOENT")return null;throw e}}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await ve(this.path(t)),!0}catch{return!1}}async delete(t){await Ft(this.path(t),{force:!0})}async list(t=""){let e=this.path(t),n=[];try{await ve(e)}catch{return n}let s=async a=>{let d=await _t(a,{withFileTypes:!0});for(let i of d){let l=Kt(a,i.name);if(i.isDirectory()){await s(l);continue}let f=await we(l);n.push({key:be(this.root,l),size:f.size,updatedAt:f.mtime.toISOString()})}},o=await we(e);return o.isDirectory()?await s(e):n.push({key:be(this.root,e),size:o.size,updatedAt:o.mtime.toISOString()}),n}};import{DeleteObjectCommand as Bt,GetObjectCommand as Vt,HeadObjectCommand as Ht,ListObjectsV2Command as zt,PutObjectCommand as Jt,S3Client as Gt}from"@aws-sdk/client-s3";import{getSignedUrl as Ut}from"@aws-sdk/s3-request-presigner";var P=class{name;client;bucket;constructor(t){this.name=t.name??"s3",this.bucket=t.bucket,this.client=new Gt({region:t.region??"us-east-1",endpoint:t.endpoint||void 0,forcePathStyle:t.forcePathStyle??!1,requestChecksumCalculation:"WHEN_REQUIRED",responseChecksumValidation:"WHEN_REQUIRED",credentials:{accessKeyId:t.accessKeyId,secretAccessKey:t.secretAccessKey}})}async put(t,e,n="application/octet-stream"){let s=typeof e=="string"?Buffer.from(e,"utf8"):e;await this.client.send(new Jt({Bucket:this.bucket,Key:t,Body:s,ContentType:n}))}async get(t){let e=await Ut(this.client,new Vt({Bucket:this.bucket,Key:t}),{expiresIn:60}),n=await fetch(e,{redirect:"follow"});if(n.status===404)return null;if(!n.ok)throw new Error(`${this.name} download failed: ${n.status} ${n.statusText}`);return new Uint8Array(await n.arrayBuffer())}async getText(t){let e=await this.get(t);return e?Buffer.from(e).toString("utf8"):null}async exists(t){try{return await this.client.send(new Ht({Bucket:this.bucket,Key:t})),!0}catch(e){if(typeof e=="object"&&e!==null&&"$metadata"in e&&e.$metadata?.httpStatusCode===404)return!1;throw e}}async delete(t){await this.client.send(new Bt({Bucket:this.bucket,Key:t}))}async list(t=""){let e=[],n;do{let s=await this.client.send(new zt({Bucket:this.bucket,Prefix:t||void 0,ContinuationToken:n}));for(let o of s.Contents??[])o.Key&&e.push({key:o.Key,size:o.Size,updatedAt:o.LastModified?.toISOString()});n=s.IsTruncated?s.NextContinuationToken:void 0}while(n);return e}};function z(r,t){return console.warn(t),new A(r)}function je(r){let t=r.localRoot??Yt(qt(),".toolnet-memory","storage");if(r.provider==="r2"){let e=r.r2;return e?.accountId&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new P({name:"r2",endpoint:`https://${e.accountId}.r2.cloudflarestorage.com`,region:"auto",bucket:e.bucket,forcePathStyle:!0,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):z(t,"[storage] Cloudflare R2 credentials missing. Using local fallback.")}if(r.provider==="s3"){let e=r.s3;return e?.bucket&&e.accessKeyId&&e.secretAccessKey?new P({name:"s3",endpoint:e.endpoint,region:e.region??"us-east-1",bucket:e.bucket,forcePathStyle:e.forcePathStyle??!1,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):z(t,"[storage] S3 credentials missing. Using local fallback.")}if(r.provider==="huggingface"){let e=r.huggingface;return e?.namespace&&e.bucket&&e.accessKeyId&&e.secretAccessKey?new E({namespace:e.namespace,bucket:e.bucket,accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}):z(t,"[storage] Hugging Face credentials missing. Using local fallback.")}return new A(t)}function Qt(r){return new Promise(t=>setTimeout(t,r))}async function Ae(r,t={}){let e=Math.max(1,t.attempts??3),n=t.baseDelayMs??150,s=t.maxDelayMs??2e3,o;for(let a=1;a<=e;a++)try{return await r()}catch(d){if(o=d,a>=e)break;let i=Math.min(s,n*2**(a-1)),l=Math.floor(Math.random()*Math.max(1,i*.2));await Qt(i+l)}throw o}var Xt=new Set(["put","get","getText","delete","list"]);function Pe(r,t={}){return new Proxy(r,{get(e,n){let s=Reflect.get(e,n,e);return typeof s!="function"?s:Xt.has(n)?(...o)=>Ae(()=>Promise.resolve(s.apply(e,o)),t):s.bind(e)}})}function Ce(r){let t=r.trim().replace(/\s+/g,"_").replace(/[^A-Za-z0-9._-]/g,"_").replace(/_+/g,"_").replace(/^\.+|\.+$/g,"").slice(0,100);if(!t||t==="."||t==="..")throw new Error("Invalid project storage folder");return t}function Te(r){let t=r.replace(/^\/+/,"");if(t.startsWith("memories/"))return"memory/records/"+t.slice(9);if(t.startsWith("vectors/"))return"memory/vectors/"+t.slice(8);if(t.startsWith("graph/"))return"code/graph/"+t.slice(6);let e=t.match(/^(snapshots\/[^/]+\/)memories\/(.+)$/);if(e)return`${e[1]}memory/records/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)vectors\/(.+)$/),e)return`${e[1]}memory/vectors/${e[2]}`;if(e=t.match(/^(snapshots\/[^/]+\/)graph\/(.+)$/),e)return`${e[1]}code/graph/${e[2]}`;let n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)memories\/(.+)$/);if(n)return`${n[1]}memory/records/${n[2]}`;if(n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)vectors\/(.+)$/),n)return`${n[1]}memory/vectors/${n[2]}`;if(n=t.match(/^(projects\/[^/]+\/snapshots\/[^/]+\/)graph\/(.+)$/),n)return`${n[1]}code/graph/${n[2]}`;let s=t.match(/^(projects\/[^/]+\/)memories\/(.+)$/);return s?`${s[1]}memory/records/${s[2]}`:(s=t.match(/^(projects\/[^/]+\/)vectors\/(.+)$/),s?`${s[1]}memory/vectors/${s[2]}`:(s=t.match(/^(projects\/[^/]+\/)graph\/(.+)$/),s?`${s[1]}code/graph/${s[2]}`:t))}var N=class{constructor(t,e,n,s){this.provider=t;this.name=t.name,this.projectId=e,this.projectName=n,this.folder=Ce(s??n),this.sourcePrefix=`projects/${e}`,this.targetPrefix=`projects/${this.folder}`}provider;name;folder;sourcePrefix;targetPrefix;projectId;projectName;registryPromise;async registerProject(){let t=`${this.targetPrefix}/project.json`,e=new Date().toISOString(),n=e,s=await this.provider.getText(t);if(s){let a;try{a=JSON.parse(s)}catch(d){throw new Error(`Invalid remote ToolNet project manifest at ${t}: ${d instanceof Error?d.message:String(d)}`)}if(typeof a.id=="string"&&a.id!==this.projectId)throw new Error(["ToolNet remote project namespace collision.",`Remote folder: ${this.targetPrefix}`,`Existing project id: ${a.id}`,`Current project id: ${this.projectId}`,"Refusing to mix data from two projects."].join(" "));typeof a.createdAt=="string"&&(n=a.createdAt)}let o={version:1,id:this.projectId,name:this.projectName,remote:this.folder,createdAt:n,updatedAt:e};await this.provider.put(t,JSON.stringify(o,null,2)+`
|
|
198
|
+
`,"application/json")}async ensureRegistered(){return this.registryPromise||(this.registryPromise=this.registerProject()),this.registryPromise}key(t){if(t=Te(t),t===this.sourcePrefix)return this.targetPrefix;if(t.startsWith(`${this.sourcePrefix}/`))return this.targetPrefix+t.slice(this.sourcePrefix.length);if(t===this.targetPrefix||t.startsWith(`${this.targetPrefix}/`))return t;if(t.startsWith("projects/"))throw new Error(["Cross-project storage access denied.",`Current project: ${this.targetPrefix}`,`Requested key: ${t}`].join(" "));return t}async put(t,e,n){return await this.ensureRegistered(),this.provider.put(this.key(t),e,n)}async get(t){return await this.ensureRegistered(),this.provider.get(this.key(t))}async getText(t){return await this.ensureRegistered(),this.provider.getText(this.key(t))}async delete(t){return await this.ensureRegistered(),this.provider.delete(this.key(t))}async exists(t){return await this.ensureRegistered(),this.provider.exists(this.key(t))}async list(t){return await this.ensureRegistered(),this.provider.list(this.key(t))}};function Zt(){let r=process.argv.slice(2),t=r[0]||"print",e={mode:"minimal"};for(let n=1;n<r.length;n++)r[n]==="--project"&&r[n+1]?(e.project=r[n+1],n++):r[n]==="--limit"&&r[n+1]?(e.limit=parseInt(r[n+1],10),n++):r[n]==="--focused"&&r[n+1]?(e.mode="focused",e.query=r[n+1],n++):r[n]==="--deep"&&(e.mode="deep");return{command:t,options:e}}async function er(){let{command:r,options:t}=Zt();try{switch(r){case"print":await tr(t);break;case"sync":await rr(t);break;case"refresh":await nr(t);break;case"profile-show":await sr(t);break;case"profile-sync":await or(t);break;default:console.error(`Unknown command: ${r}`),console.error("Available commands: print, sync, refresh, profile-show, profile-sync"),process.exit(1)}}catch(e){console.error("Error:",e instanceof Error?e.message:String(e)),process.exit(1)}}async function tr(r){let t=r.mode||"minimal";if(t==="minimal"){let e=D({projectPath:r.project});e||(console.error("No ToolNet project found. Run toolnet-memory init first."),process.exit(1)),process.stdout.write(e)}else(t==="focused"||t==="deep")&&(console.error("Focused and deep modes require storage access."),console.error("Use: toolnet-memory brief --deep for deep context"),process.exit(1))}async function rr(r){let t=D({projectPath:r.project});t||(console.error("No ToolNet project found."),process.exit(1));let e=ae(t);console.log(`Context hash: ${e}`),console.log(`Context size: ${t.length} chars`)}async function nr(r){console.log("Refreshing deep startup brief cache...");let t=r.project||process.cwd(),e=v(t);e||(console.error("No ToolNet project found."),process.exit(1));let n=new M().detect(e),s=he(),o=Pe(je({provider:s.storage.provider,huggingface:s.storage.huggingface,localRoot:s.storage.localRoot}),{attempts:2}),a=new N(o,n.id,n.name,n.remote??n.name);await ge(n,a),console.log("Deep startup brief cache refreshed.")}async function sr(r){let t=r.project||process.cwd(),e=v(t);e||(console.error("No ToolNet project found."),process.exit(1));let n=Ie.join(e,".toolnet","profile.md");$.existsSync(n)||(console.error("No profile.md found in .toolnet directory."),process.exit(1));let s=$.readFileSync(n,"utf-8");process.stdout.write(s)}async function or(r){let t=ie({projectPath:r.project});console.log("Created/updated:");for(let e of t)console.log(`- ${e}`)}er();
|
|
@@ -1,53 +1,13 @@
|
|
|
1
|
-
import{mkdirSync as
|
|
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
|
|
3
|
-
`)}function
|
|
4
|
-
`)}function
|
|
5
|
-
|
|
6
|
-
`)}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Rules:
|
|
15
|
-
|
|
16
|
-
1. Answer only from the supplied ToolNet project memory.
|
|
17
|
-
2. Never invent completed work, files, blockers, decisions, or test results.
|
|
18
|
-
3. Prefer the latest session/work state over old information.
|
|
19
|
-
4. Be concise.
|
|
20
|
-
5. Do not dump raw JSON or full memory files.
|
|
21
|
-
6. Tell the coding agent exactly:
|
|
22
|
-
- what the previous agent was doing,
|
|
23
|
-
- what is already completed,
|
|
24
|
-
- what remains unfinished,
|
|
25
|
-
- what file was last touched,
|
|
26
|
-
- what should be done next,
|
|
27
|
-
when those facts are available.
|
|
28
|
-
7. If information is missing, say it is not recorded.
|
|
29
|
-
8. Do not explain ToolNet internals unless asked.
|
|
30
|
-
9. Do not repeat irrelevant history.
|
|
31
|
-
10. Respond in the same language as the question.
|
|
32
|
-
|
|
33
|
-
Think of yourself as the project's memory coworker talking directly to another AI.
|
|
34
|
-
`.trim()},{role:"user",content:`
|
|
35
|
-
Question from coding agent:
|
|
36
|
-
|
|
37
|
-
${e}
|
|
38
|
-
|
|
39
|
-
ToolNet selected memory:
|
|
40
|
-
|
|
41
|
-
${o}
|
|
42
|
-
|
|
43
|
-
Answer the coding agent directly.
|
|
44
|
-
`.trim()}]}),c=a.text.trim();return c?{answer:c,provider:a.provider,model:a.model,usedAi:!0}:{answer:r.answer,usedAi:!1}}catch{return{answer:r.answer,usedAi:!1}}}import{existsSync as _t,mkdirSync as It,readFileSync as Tt,renameSync as Ot,rmSync as Pt,writeFileSync as Rt}from"node:fs";import{join as ke}from"node:path";var Ft=720*60*1e3;function be(t){return ke(t.rootPath,".toolnet","context","memory-agent-conversation.json")}function O(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}function Nt(t){let e=O(t);return[/\btiếp tục\b/u,/\btiếp tục task\b/u,/\blúc nãy\b/u,/\btrước đó\b/u,/\btask trước\b/u,/\bprevious (?:task|session|work)\b/u,/\bcontinue\b/u,/\bresume\b/u,/\bcurrent task\b/u,/\bđang làm gì\b/u].some(n=>n.test(e))}function Lt(t){if(Nt(t))return!1;let e=O(t),n=e.split(/\s+/u).filter(Boolean);return[/^(?:tại sao|vì sao|sao vậy)\b/u,/^(?:why|why so|why that)\b/u,/^(?:còn|thế|vậy|rồi sao|sau đó)\b/u,/^(?:what about|and what|then what)\b/u,/^(?:cái đó|việc đó|nó|phần đó)\b/u].some(r=>r.test(e))?!0:n.length<=5&&/\b(?:nó|đó|vậy|thế|that|it|this)\b/u.test(e)}function Dt(t,e,n){let r=be(t);if(!_t(r))return null;try{let o=JSON.parse(Tt(r,"utf8"));if(o.schema!=="toolnet.memory-conversation.v1"||o.version!==1||o.projectId!==t.id||!Array.isArray(o.focus))return null;let s=Date.parse(o.updatedAt);return!Number.isFinite(s)||e-s>n?null:o}catch{return null}}function $t(t,e=8){let n=new Set,r=[];for(let o of t){let s=o.value.replace(/\s+/gu," ").trim();if(!s)continue;let a=[o.kind,O(s)].join(":");if(!n.has(a)&&(n.add(a),r.push({...o,value:s}),r.length>=e))break}return r}function Kt(t,e){let n=be(t),r=ke(t.rootPath,".toolnet","context");It(r,{recursive:!0,mode:448});let o=`${n}.${process.pid}.tmp`;try{Rt(o,JSON.stringify(e,null,2)+`
|
|
45
|
-
`,{encoding:"utf8",mode:384}),Ot(o,n)}finally{Pt(o,{force:!0})}}function Ut(t){return t.focus.slice(0,8).map(e=>`- ${e.kind}: ${e.value}`).join(`
|
|
46
|
-
`)}function jt(t,e){return t.priorFocus.find(n=>n.kind===e)?.value}function X(t){if(!t.usedPriorFocus||t.currentIntent!=="summary"||t.priorFocus.length===0)return null;let e=O(t.originalQuestion),n=/^(?:tại sao|vì sao|sao vậy|why|why so|why that)\b/u.test(e),r=/^(?:rồi sao|sau đó|tiếp thì sao|thế tiếp theo|then what|what next)\b/u.test(e),o=n?["decision","blocker","next_action","task","file"]:r?["next_action","todo","task","blocker","file","decision"]:["task","next_action","file","decision","blocker"],s={task:"Task li\xEAn quan",next_action:"Vi\u1EC7c ti\u1EBFp theo",file:"File li\xEAn quan",decision:"Quy\u1EBFt \u0111\u1ECBnh li\xEAn quan",blocker:"Blocker",todo:"TODO"},a=[];for(let c of o){let d=jt(t,c);if(d&&(a.push(`${s[c]??c}: ${d}.`),n&&a.length>=2||!n&&a.length>=3))break}return a.length===0?null:{answer:a.join(" "),intent:"summary",source:"conversation-focus"}}function Bt(t){let e=O(t);return/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(e)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(e)||/(?:còn|các)\s+(?:todo|việc)\b/u.test(e)}function ve(t,e,n={}){let r=n.now??Date.now(),o=n.maxAgeMs??Ft,s=Dt(t,r,o),a=!!(s&&s.focus.length>0&&Lt(e)&&!Bt(e)),c=a&&s?[e.trim(),"","Previous ToolNet Memory conversation focus:",Ut(s),"","Use the compact focus only to resolve the current follow-up question.","Do not reconstruct or request raw session transcripts."].join(`
|
|
47
|
-
`):e.trim(),d=P(e),m=h(t,c,{maxFacts:8,maxChars:1800}).facts.map(A=>({kind:A.kind,value:A.value,source:A.source})),y=$t(a&&s?[...m,...s.focus]:m);return Kt(t,{schema:"toolnet.memory-conversation.v1",version:1,projectId:t.id,updatedAt:new Date(r).toISOString(),lastIntent:d,focus:y}),{question:c,originalQuestion:e,usedPriorFocus:a,previousIntent:s?.lastIntent,currentIntent:d,priorFocus:s?.focus??[],focusCount:y.length}}import{existsSync as Ht,readFileSync as qt}from"node:fs";import{join as zt}from"node:path";function k(t){return!!t?.trim()}function Ae(t){let e=[],n=[],r=100;k(t.goal)||k(t.request)||(e.push("goal_or_request"),r-=20),k(t.activity)||k(t.current.task?.title)||k(t.current.phase?.title)||(e.push("current_work"),r-=25),k(t.nextAction)||k(t.remaining.todos[0])||(e.push("next_action"),r-=25),t.tests.status!=="unknown"||t.tests.recent.length>0||(t.tests.checks?.length??0)>0||(t.evidence?.commands.length??0)>0||(t.evidence?.references.length??0)>0||(e.push("evidence"),r-=20),k(t.files.current)||t.files.recent.length>0||(t.files.active?.length??0)>0||(t.files.modified?.length??0)>0||(t.files.created?.length??0)>0||(e.push("files"),r-=10),t.tests.status==="failing"&&n.push("tests_failing"),t.blockers.length>0&&n.push("active_blockers"),r=Math.max(0,Math.min(100,r));let l=e.includes("current_work")||e.includes("next_action"),m;return r>=80&&!l?m="high":r>=50?m="medium":m="low",{confidence:m,score:r,missingContext:e,warnings:n}}var xe={compact:{completed:3,evidence:3,files:5,blockers:2,maxChars:1800},normal:{completed:10,evidence:10,files:15,blockers:5,maxChars:4200},benchmark:{completed:30,evidence:30,files:40,blockers:10,maxChars:1e4}};function S(t){let e=new Set,n=[];for(let r of t){let o=r?.replace(/\s+/gu," ").trim();if(!o)continue;let s=o.normalize("NFKC").toLowerCase();e.has(s)||(e.add(s),n.push(o))}return n}function C(t){return t.length>0?t.map(e=>`- ${e}`).join(`
|
|
48
|
-
`):"- none"}function Wt(t,e){return t.length<=e?t:[t.slice(0,e).trimEnd(),"","[Structured handoff truncated]"].join(`
|
|
49
|
-
`)}function Me(t,e){if(e)return e;let n=t.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim();return/\bbenchmark\b/u.test(n)||/\bdeep handoff\b/u.test(n)||/\bfull handoff\b/u.test(n)||/\btakeover\b/u.test(n)||/\btake over\b/u.test(n)||/tiếp quản/u.test(n)||/bằng chứng đầy đủ/u.test(n)||/evidence.*(?:file|test|command)/u.test(n)?"benchmark":/\bcompact\b/u.test(n)||/\bbrief\b/u.test(n)||/ngắn gọn/u.test(n)||/tóm tắt ngắn/u.test(n)?"compact":"normal"}function Gt(t,e="normal"){let n=xe[e],r=S([`Source agent: ${t.source.agent}`,`Captured: ${t.capturedAt}`,t.goal?`Goal: ${t.goal}`:void 0,t.request?`Request: ${t.request}`:void 0,t.activity?`Activity: ${t.activity}`:void 0,t.current.phase?`Phase: ${t.current.phase.title} [${t.current.phase.status}]`:void 0,t.current.task?`Task: ${t.current.task.title} [${t.current.task.status}]`:void 0,t.current.file?`Current file: ${t.current.file}`:void 0,`Progress: ${JSON.stringify(t.progress)}`]),o=S([...t.completed.phases.map(l=>`Phase: ${l}`),...t.completed.tasks.map(l=>`Task: ${l}`)]).slice(0,n.completed),s=S([`Tests status: ${t.tests.status}`,...t.tests.recent.map(l=>`Test: ${l}`),...(t.tests.checks??[]).map(l=>`Check [${l.status}] ${l.kind}: ${l.command}`),...(t.evidence?.commands??[]).map(l=>`Command: ${l}`),...(t.evidence?.references??[]).map(l=>`Reference: ${l}`),...t.decisions.map(l=>`Decision: ${l}`),...t.attention.map(l=>`Attention: ${l}`)]).slice(0,n.evidence),a=S([t.files.current,...(t.files.active??[]).map(l=>`Active: ${l}`),...(t.files.modified??[]).map(l=>`Modified: ${l}`),...(t.files.created??[]).map(l=>`Created: ${l}`),...(t.files.deleted??[]).map(l=>`Deleted: ${l}`),...t.files.recent.map(l=>`Recent: ${l}`)]).slice(0,n.files),c=S(t.blockers).slice(0,n.blockers),d=S([t.nextAction,t.nextAction?void 0:t.remaining.todos[0]]).slice(0,1);return{currentState:r,completed:o,evidence:s,filesTouched:a,blockers:c,nextAction:d}}function Yt(t,e,n){return[`HANDOFF_DETAIL: ${e}`,`CONTINUITY_CONFIDENCE: ${n.confidence}`,`QUALITY_SCORE: ${n.score}`,`MISSING_CONTEXT: ${n.missingContext.length>0?n.missingContext.join(", "):"none"}`,`QUALITY_WARNINGS: ${n.warnings.length>0?n.warnings.join(", "):"none"}`,"","CURRENT_STATE",C(t.currentState),"","COMPLETED",C(t.completed),"","EVIDENCE",C(t.evidence),"","FILES_TOUCHED",C(t.filesTouched),"","BLOCKERS",C(t.blockers),"","NEXT_ACTION",C(t.nextAction)].join(`
|
|
50
|
-
`)}function Se(t,e="normal"){let n=zt(t.rootPath,".toolnet","work","handoff-latest.json");if(!Ht(n))return null;try{let o=JSON.parse(qt(n,"utf8")).continuity;if(!o||o.schema!=="toolnet.handoff.v2"||o.version!==2)return null;let s=Gt(o,e),a=Ae(o),c=Wt(Yt(s,e,a),xe[e].maxChars);return{detail:e,data:s,quality:a,text:c}}catch{return null}}function Qt(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}var Vt=[/\bhandoff\b/u,/\bcontinuity\b/u,/\bcontinue\b/u,/\bresume\b/u,/\btakeover\b/u,/\btake over\b/u,/tiếp tục/u,/tiếp quản/u,/bàn giao/u,/dừng ở đâu/u,/đang làm gì/u],Jt=[/\bcurrent state\b/u,/\bcurrent task\b/u,/\bevidence\b/u,/\breport locations?\b/u,/\bfiles?\b/u,/\burls?\b/u,/\bassets?\b/u,/\btests?\b/u,/\bcommands?\b/u,/\bblockers?\b/u,/\bnext actions?\b/u,/trạng thái hiện tại/u,/task hiện tại/u,/bằng chứng/u,/vị trí báo cáo/u,/file/u,/url/u,/blocker/u,/việc tiếp theo/u,/bước tiếp theo/u],Xt=[/\bsummarize\b/u,/\bsummary\b/u,/\bfull state\b/u,/\bfull context\b/u,/tóm tắt/u,/đầy đủ/u,/toàn bộ trạng thái/u];function Zt(t){let e=0;for(let n of Jt)n.test(t)&&(e+=1);return e}function Ce(t,e){let n=Qt(t);if(/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(n)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(n)||/(?:còn|các)\s+(?:todo|việc)\b/u.test(n))return!1;if(e==="summary"||e==="status")return!0;if(!n)return!1;let o=Vt.some(c=>c.test(n)),s=Xt.some(c=>c.test(n)),a=Zt(n);return!!(o&&a>=1||s&&a>=2||a>=4)}var yo={question:Z.string().min(2).max(4e3).describe("Question about previous project work, unfinished tasks, handoff, blockers, decisions, files or next actions."),mode:Z.enum(["ai","local"]).optional().describe("ai uses ToolNet Memory Agent with configured LLM fallback chain. local returns deterministic local memory only."),detail:Z.enum(["compact","normal","benchmark"]).optional().describe("Handoff detail level. compact=minimal, normal=standard, benchmark=deep evidence/files/tests for agent takeover.")};function j(t,e,n,r,o){if(!Ce(e,n))return{answer:r};let s=Me(e,o),a=Se(t.project,s);return a?{answer:a.text,handoff:a.data,detail:a.detail,confidence:a.quality.confidence,missingContext:a.quality.missingContext,quality:a.quality}:{answer:r}}async function ee(t,e){let n=ve(t.project,e.question);if(e.mode==="local"){let a=X(n);if(a)return{...j(t,n.originalQuestion,String(a.intent),a.answer,e.detail),mode:"local",usedAi:!1,source:a.source,intent:a.intent};let c=x(t.project,n.originalQuestion);return{...j(t,n.originalQuestion,String(c.intent),c.answer,e.detail),mode:"local",usedAi:!1,source:c.source,intent:c.intent}}let r=x(t.project,n.originalQuestion),o=j(t,n.originalQuestion,String(r.intent),r.answer,e.detail);if("handoff"in o)return{...o,mode:"ai",usedAi:!1,source:r.source,intent:r.intent,routing:"deterministic-handoff"};let s=await he(t.project,n.question);if(!s.usedAi){let a=X(n);if(a)return{...j(t,n.originalQuestion,String(a.intent),a.answer,e.detail),mode:"ai",usedAi:!1,provider:s.provider,model:s.model}}return{answer:s.answer,mode:"ai",usedAi:s.usedAi,provider:s.provider,model:s.model}}var we="RAW_TRANSCRIPT_MUST_NEVER_ENTER_CONTINUITY_CONTEXT_X1";function Ee(t,e){_e(t,JSON.stringify(e,null,2)+`
|
|
51
|
-
`,"utf8")}function rn(t,e,n,r,o,s,a,c){return{continuity:{schema:"toolnet.handoff.v2",version:2,project:{id:t,name:e},source:{agent:n,nativeSessionId:`${n}-x1-source`,sessionKey:`${n}:${n}-x1-source`,sequence:42,reason:"checkpoint"},capturedAt:"2026-08-13T05:30:00.000Z",goal:`Continue ${e} safely across agents`,request:`Recover ${o} with exact evidence and next action`,activity:`Preparing ${r} takeover from ${n}`,current:{phase:{id:"x1",title:"Cross-Agent Continuity E2E",status:"in_progress"},task:{id:"x1-cross-agent",title:o,status:"in_progress"},file:s},completed:{phases:["A1 OpenCode Adapter","A2 Agy Adapter","A3 Claude Code Adapter"],tasks:["Build canonical handoff schema","Build intelligent memory retrieval"]},remaining:{phases:["X1 Cross-Agent Continuity E2E"],tasks:[o],todos:[a]},nextAction:c,blockers:[],decisions:["Canonical handoff must override stale session state","Never replay raw transcripts during continuity recovery"],files:{current:s,recent:[s]},tests:{status:"passing",recent:["A1-A3 adapter tests passed"],checks:[{kind:"test",status:"passed",command:`node scripts/${n}-to-${r}-verify.mjs`}]},evidence:{commands:[`node scripts/${n}-to-${r}-verify.mjs`],references:[`https://example.test/${n}/${r}/continuity-report`]},attention:[],progress:{phasesTotal:3,phasesCompleted:2,tasksTotal:4,tasksCompleted:3,blocked:0},stateDigest:"a".repeat(64)}}}async function B(t,e){return ee({project:t},{question:e,mode:"local"})}async function on(t,e){let n=en(v(nn(),`toolnet-x1-${t}-${e}-`));try{let r=`x1-${t}-${e}`,o=`x1-${t}-to-${e}`,s={id:r,name:o,rootPath:n,createdAt:"2026-08-13T05:30:00.000Z",updatedAt:"2026-08-13T05:30:00.000Z",graphVersion:1,memoryVersion:1},a=v(n,".toolnet","work"),c=v(n,".toolnet","context"),d=v(n,".toolnet","sessions",t,"raw-session-x1");te(a,{recursive:!0}),te(c,{recursive:!0}),te(d,{recursive:!0});let l=`X1 ${t} to ${e} canonical continuity`,m=`src/x1/${t}-to-${e}.ts`,y=`Implement ${e} continuation step`,A=`Continue ${l} in ${m}`;Ee(v(a,"handoff-latest.json"),rn(r,o,t,e,l,m,y,A)),Ee(v(c,"session-origin.json"),{version:1,projectId:r,agent:"stale-agent",nativeSessionId:"stale-session",updatedAt:"2026-01-01T00:00:00.000Z",currentTask:"STALE TASK MUST NOT WIN",currentPhase:"STALE PHASE",lastTouchedFile:"src/stale/old.ts",latestNextAction:"STALE NEXT ACTION"}),_e(v(d,"events.jsonl"),JSON.stringify({text:we})+`
|
|
52
|
-
`,"utf8");let Te=h(s,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?",{maxFacts:12,maxChars:3200}),H=await B(s,"Task hi\u1EC7n t\u1EA1i \u0111ang l\xE0m l\xE0 g\xEC?"),q=await B(s,"File hi\u1EC7n t\u1EA1i l\xE0 file n\xE0o?"),z=await B(s,"TODO c\xF2n l\u1EA1i l\xE0 g\xEC?"),W=await B(s,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?"),g=await ee({project:s},{mode:"ai",detail:"benchmark",question:"Summarize current state, completed work, evidence, files, tests, blockers and exact next action for benchmark agent takeover."}),w=g,Oe=`node scripts/${t}-to-${e}-verify.mjs`,Pe=`https://example.test/${t}/${e}/continuity-report`,Re=[H.answer,q.answer,z.answer,W.answer,g.answer].join(`
|
|
53
|
-
`),G=[H,q,z,W],ne={canonicalHandoffPreferred:Te.facts[0]?.source==="handoff",taskRecovered:H.answer.includes(l),fileRecovered:q.answer.includes(m),todoRecovered:z.answer.includes(y),nextActionRecovered:W.answer.includes(A),localModeOnly:G.every(E=>E.mode==="local"),noAiRequired:G.every(E=>E.usedAi===!1),rawTranscriptIsolated:!Re.includes(we),compact:G.every(E=>E.answer.length<1600),structuredTakeoverRecovered:g.answer.includes("CURRENT_STATE")&&g.answer.includes("COMPLETED")&&g.answer.includes("EVIDENCE")&&g.answer.includes("FILES_TOUCHED")&&g.answer.includes("BLOCKERS")&&g.answer.includes("NEXT_ACTION"),benchmarkDetailRecovered:w.detail==="benchmark"&&g.answer.includes("HANDOFF_DETAIL: benchmark"),highConfidence:w.confidence==="high"&&Array.isArray(w.missingContext)&&w.missingContext.length===0,evidenceRecovered:g.answer.includes(Oe)&&g.answer.includes(Pe),deterministicTakeover:g.usedAi===!1&&w.routing==="deterministic-handoff"};return{from:t,to:e,passed:Object.values(ne).every(Boolean),checks:ne}}finally{tn(n,{recursive:!0,force:!0})}}async function Ie(){let t=[["agy","codex"],["codex","opencode"],["opencode","claude"],["claude","kiro"],["kiro","cursor"],["cursor","copilot"],["copilot","grok"],["grok","agy"]],e=[];for(let[r,o]of t)e.push(await on(r,o));let n=e.filter(r=>r.passed).length;return{passed:n===e.length,total:e.length,passedCount:n,cases:e}}var p={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",cyan:"\x1B[36m",gray:"\x1B[90m",white:"\x1B[97m"};function sn(t){return t?`${p.green}\u2713${p.reset}`:`${p.red}\u2717${p.reset}`}function an(t){let e=`${t.from.padEnd(8)} \u2192 ${t.to.padEnd(8)}`;if(console.log(`\u2502 ${sn(t.passed)} ${e}`),!t.passed)for(let[n,r]of Object.entries(t.checks))r||console.log(`\u2502 ${p.red}\u2717${p.reset} ${n}`)}async function cn(){console.log(""),console.log(`${p.cyan}\u25C7${p.reset} ${p.white}ToolNet Cross-Agent Continuity Certification${p.reset}`),console.log(`${p.gray}\u2502${p.reset}`);let t=await Ie();for(let e of t.cases)an(e);if(console.log(`${p.gray}\u2502${p.reset}`),t.passed){console.log(`${p.green}\u25C6 PASS${p.reset} ${t.passedCount}/${t.total} cross-agent transitions`),console.log(`${p.green}\u2713${p.reset} canonical handoff`),console.log(`${p.green}\u2713${p.reset} memory_agent_ask`),console.log(`${p.green}\u2713${p.reset} task / file / TODO / next action`),console.log(`${p.green}\u2713${p.reset} raw transcripts isolated`),console.log(`${p.green}\u2713${p.reset} AI not required for direct continuity`);return}console.log(`${p.red}\u25C6 FAIL${p.reset} ${t.passedCount}/${t.total} cross-agent transitions`),process.exitCode=1}cn().catch(t=>{console.error(t instanceof Error?t.message:String(t)),process.exitCode=1});
|
|
1
|
+
import{mkdirSync as j,mkdtempSync as St,rmSync as Ct,writeFileSync as re}from"node:fs";import{tmpdir as Mt}from"node:os";import{join as k}from"node:path";import{z as N}from"zod";import{existsSync as _e,readFileSync as Re}from"node:fs";import{join as $e}from"node:path";import{existsSync as ue,mkdirSync as _t,readFileSync as le,renameSync as Rt,writeFileSync as $t}from"node:fs";import{dirname as Nt,join as de}from"node:path";function fe(t){return de(t.rootPath,".toolnet","work","current.json")}function $(t){let e=fe(t);if(!ue(e))return null;try{let n=JSON.parse(le(e,"utf8"));return n.version!==1||n.projectId!==t.id?null:n}catch{return null}}import{existsSync as me,mkdirSync as Dt,readFileSync as pe,renameSync as Lt,writeFileSync as Kt}from"node:fs";import{dirname as qt,join as ge}from"node:path";function ke(t){return ge(t.rootPath,".toolnet","context","session-origin.json")}function P(t){let e=ke(t);if(!me(e))return null;try{let n=JSON.parse(pe(e,"utf8"));return n.version!==1||n.projectId!==t.id?null:n}catch{return null}}function ye(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function M(t){let e=ye(t),n=/(?:agent trước|previous agent|ai trước|phiên trước)/u.test(e),r=/(?:đang làm gì|làm tới đâu|dừng ở đâu|dở ở đâu|stopped where|working on|current task|file nào|last touched|làm gì tiếp|next action|next step)/u.test(e);return n&&r?"summary":/(?:agent trước là ai|previous agent|ai trước là ai|phiên trước là ai)/u.test(e)?"previous_agent":/(?:task hiện tại|current task|đang làm gì|đang làm task nào|dở ở đâu|dừng ở đâu|stopped where)/u.test(e)?"current_task":/(?:tiếp theo|next action|next step|làm gì tiếp|phải làm gì tiếp)/u.test(e)?"next_action":/(?:file cuối|last file|file nào|đang sửa file|last touched)/u.test(e)?"last_file":/(?:blocker|vướng|kẹt|lỗi gì|blocked)/u.test(e)?"blocker":/(?:decision|quyết định|đã chốt|important decision)/u.test(e)?"decision":/(?:đã xong|completed|hoàn thành|done|todo nào xong)/u.test(e)?"completed":/(?:status|trạng thái|tiến độ|progress)/u.test(e)?"status":"summary"}import{existsSync as Ae,mkdirSync as Pn,readFileSync as Te}from"node:fs";import{join as L}from"node:path";var be=[{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}],w=class{scan(e){let n=[];for(let r of be){let o=new RegExp(r.regex.source,r.regex.flags);for(let s of e.matchAll(o))n.push({type:r.type,value:s[0]})}return n}hasSecrets(e){return this.scan(e).length>0}};var A=class{scanner=new w;sanitize(e){let n=e,r=this.scanner.scan(e),o=new Set;for(let s of r)o.add(s.type),n=n.split(s.value).join(`[REDACTED:${s.type}]`);return{text:n,redacted:r.length,secretTypes:[...o]}}sanitizeValue(e){if(typeof e=="string")return this.sanitize(e).text;if(Array.isArray(e))return e.map(n=>this.sanitizeValue(n));if(e&&typeof e=="object"){let n={};for(let[r,o]of Object.entries(e)){let s=r.normalize("NFKC").toLowerCase().replace(/[^a-z0-9]+/g,"");s.includes("password")||s.includes("passwd")||s==="pwd"||s.includes("secret")||s.includes("token")||s.includes("cookie")||s.includes("authorization")||s.includes("apikey")||s.includes("accesskey")||s.includes("privatekey")||s.includes("clientsecret")||s.includes("credential")?n[r]="[REDACTED]":n[r]=this.sanitizeValue(o)}return n}return e}};import{existsSync as ve,readFileSync as xe}from"node:fs";import{homedir as Se}from"node:os";import{join as Ce}from"node:path";function Me(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 we(){let t=process.env.TOOLNET_GLOBAL_ENV??Ce(Se(),".config","toolnet-memory",".env");if(!ve(t))return;let e=xe(t,"utf8");for(let n of e.split(/\r?\n/)){let r=n.trim();if(!r||r.startsWith("#"))continue;r.startsWith("export ")&&(r=r.slice(7));let o=r.indexOf("=");if(o<=0)continue;let s=r.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=Me(r.slice(o+1)))}}we();var Sn=new A;function Fe(t){return L(t.rootPath,".toolnet","memory","checkpoints")}function Ee(t){return L(Fe(t),"latest.json")}function Ie(t){let e=Ee(t);if(!Ae(e))return null;try{let n=JSON.parse(Te(e,"utf8"));return n.schema!=="toolnet.memory.checkpoint.v1"||n.project.id!==t.id?null:n}catch{return null}}function K(t){return Ie(t)}var Pe={previous_agent:{previous_agent:100,task:35,phase:25},current_task:{task:100,request:95,activity:90,phase:75,file:65,next_action:55,blocker:45},next_action:{next_action:100,todo:80,task:65,blocker:60,file:40},last_file:{file:100,task:65,next_action:40},blocker:{blocker:100,task:60,next_action:50,file:35},decision:{decision:100,rule:85,architecture:80,task:45,file:30},completed:{completed:100,progress:70,task:30},status:{progress:100,task:85,phase:75,blocker:65,next_action:60},summary:{task:100,request:98,activity:96,next_action:95,file:90,blocker:85,previous_agent:80,phase:75,completed:60,todo:60,decision:55,rule:54,architecture:53,fix:52,progress:50,test:45,goal:35}},Ne={checkpoint:20,handoff:18,"session-origin":14,"work-state":8};function Oe(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/g," ").trim()}function i(t,e){let n=e.value.replace(/\s+/g," ").trim();n&&t.push({...e,value:n,score:0})}function je(t){let e=$e(t.rootPath,".toolnet","work","handoff-latest.json");if(!_e(e))return null;try{let r=JSON.parse(Re(e,"utf8")).continuity;return r?.schema!=="toolnet.handoff.v2"||r.project.id!==t.id?null:r}catch{return null}}function De(t){let e=K(t);if(!e)return[];let n=[];e.request&&i(n,{kind:"request",value:e.request,source:"checkpoint"}),e.activity&&i(n,{kind:"activity",value:e.activity,source:"checkpoint"}),e.current.phase&&i(n,{kind:"phase",value:e.current.phase.title,source:"checkpoint"}),e.current.task&&i(n,{kind:"task",value:e.current.task.title,source:"checkpoint"});let r=e.files.active.at(-1);r&&i(n,{kind:"file",value:r,source:"checkpoint"});for(let o of e.nextActions)i(n,{kind:"next_action",value:o,source:"checkpoint"});for(let o of e.blockers)i(n,{kind:"blocker",value:o,source:"checkpoint"});for(let o of e.completed.tasks)i(n,{kind:"completed",value:o,source:"checkpoint"});for(let o of e.remaining.tasks)i(n,{kind:"todo",value:o,source:"checkpoint"});for(let o of e.durableFacts)i(n,{kind:o.kind,value:o.content,source:"checkpoint"});return n}function Le(t){let e=je(t);if(!e)return[];let n=[];i(n,{kind:"previous_agent",value:e.source.agent,source:"handoff"}),e.request&&i(n,{kind:"request",value:e.request,source:"handoff"}),e.activity&&i(n,{kind:"activity",value:e.activity,source:"handoff"}),e.goal&&i(n,{kind:"goal",value:e.goal,source:"handoff"}),e.current.phase&&i(n,{kind:"phase",value:e.current.phase.title,source:"handoff"}),e.current.task&&i(n,{kind:"task",value:e.current.task.title,source:"handoff"}),e.files.current&&i(n,{kind:"file",value:e.files.current,source:"handoff"}),e.nextAction&&i(n,{kind:"next_action",value:e.nextAction,source:"handoff"});for(let r of e.blockers)i(n,{kind:"blocker",value:r,source:"handoff"});for(let r of e.decisions)i(n,{kind:"decision",value:r,source:"handoff"});for(let r of e.completed.tasks)i(n,{kind:"completed",value:r,source:"handoff"});for(let r of e.remaining.todos)i(n,{kind:"todo",value:r,source:"handoff"});return i(n,{kind:"test",value:`Test status: ${e.tests.status}`,source:"handoff"}),i(n,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"handoff"}),n}function Ke(t){let e=P(t);if(!e)return[];let n=[];return i(n,{kind:"previous_agent",value:e.agent,source:"session-origin"}),e.currentPhase&&i(n,{kind:"phase",value:e.currentPhase,source:"session-origin"}),e.currentTask&&i(n,{kind:"task",value:e.currentTask,source:"session-origin"}),e.lastTouchedFile&&i(n,{kind:"file",value:e.lastTouchedFile,source:"session-origin"}),e.latestNextAction&&i(n,{kind:"next_action",value:e.latestNextAction,source:"session-origin"}),e.latestBlocker&&i(n,{kind:"blocker",value:e.latestBlocker,source:"session-origin"}),e.latestDecision&&i(n,{kind:"decision",value:e.latestDecision,source:"session-origin"}),n}function He(t){let e=$(t);if(!e)return[];let n=[];e.currentRequest&&i(n,{kind:"request",value:e.currentRequest,source:"work-state"}),e.currentActivity&&i(n,{kind:"activity",value:e.currentActivity,source:"work-state"}),e.goal&&i(n,{kind:"goal",value:e.goal,source:"work-state"}),e.currentPhase&&i(n,{kind:"phase",value:e.currentPhase.title,source:"work-state"}),e.currentTask&&i(n,{kind:"task",value:e.currentTask.title,source:"work-state"});let r=e.activeFiles?.at(-1)??e.filesTouched.at(-1);r&&i(n,{kind:"file",value:r,source:"work-state"});for(let o of e.nextActions.slice(0,5))i(n,{kind:"next_action",value:o,source:"work-state"});for(let o of e.blockers.slice(-5))i(n,{kind:"blocker",value:o,source:"work-state"});for(let o of e.decisions.slice(-5))i(n,{kind:"decision",value:o,source:"work-state"});for(let o of e.tasks.filter(s=>s.status==="completed").slice(-8))i(n,{kind:"completed",value:o.title,source:"work-state"});for(let o of e.tasks.filter(s=>s.status!=="completed"&&s.status!=="cancelled").slice(0,8))i(n,{kind:"todo",value:o.title,source:"work-state"});for(let o of e.tests.slice(-5))i(n,{kind:"test",value:o,source:"work-state"});return i(n,{kind:"progress",value:`${e.progress.tasksCompleted}/${e.progress.tasksTotal} tasks completed; ${e.progress.blocked} blocked`,source:"work-state"}),n}function qe(t,e){let n=Pe[t];return e.map((r,o)=>({...r,score:(n[r.kind]??10)+Ne[r.source]+Math.max(0,5-o*.01)}))}function ze(t){let e=new Map;for(let n of t){let r=`${n.kind}:${Oe(n.value)}`,o=e.get(r);(!o||n.score>o.score)&&e.set(r,n)}return[...e.values()]}function h(t,e,n={}){let r=M(e),o=[...De(t),...Le(t),...Ke(t),...He(t)],s=ze(qe(r,o)).sort((d,p)=>p.score-d.score),c=Math.max(1,n.maxFacts??12),l=Math.max(300,n.maxChars??3200),f=[],a=0;for(let d of s){if(f.length>=c)break;let p=d.kind.length+d.value.length+d.source.length+16;f.length>0&&a+p>l||(f.push(d),a+=p)}return{intent:r,facts:f,context:{project:{id:t.id,name:t.name},intent:r,selectedFacts:f.map(d=>({kind:d.kind,value:d.value,source:d.source}))},stats:{candidates:o.length,selected:f.length,chars:a}}}var H={previous_agent:"Agent tr\u01B0\u1EDBc",request:"Y\xEAu c\u1EA7u hi\u1EC7n t\u1EA1i",activity:"\u0110ang l\xE0m",goal:"M\u1EE5c ti\xEAu",phase:"Phase",task:"Task hi\u1EC7n t\u1EA1i",file:"File hi\u1EC7n t\u1EA1i",todo:"TODO",next_action:"Ti\u1EBFp theo",blocker:"Blocker",decision:"Quy\u1EBFt \u0111\u1ECBnh",rule:"Quy t\u1EAFc",architecture:"Ki\u1EBFn tr\xFAc",fix:"\u0110\xE3 s\u1EEDa",completed:"\u0110\xE3 ho\xE0n th\xE0nh",test:"Test",progress:"Ti\u1EBFn \u0111\u1ED9"},q=["previous_agent","request","activity","phase","task","file","todo","next_action","blocker","decision","rule","architecture","fix","completed","test","progress","goal"],We={previous_agent:["previous_agent"],current_task:["request","activity","task","file","next_action"],next_action:["next_action","task","file","todo"],last_file:["file","task","next_action"],blocker:["blocker"],decision:["decision"],completed:["completed"],status:["progress","task","next_action","blocker"],summary:q};function Be(t){let e=t.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim();return/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(e)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(e)}function Qe(t){let e=t.filter(r=>r.source==="handoff");if(e.length===0)return t;let n=t.filter(r=>r.source==="checkpoint"&&["rule","architecture","fix"].includes(r.kind));return[...e,...n]}function Ue(t){let e=new Set,n=[];for(let r of t){let o=`${r.kind}:${r.value.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}`;e.has(o)||(e.add(o),n.push(r))}return n}function z(t){switch(t){case"todo":case"blocker":case"decision":case"completed":return 3;default:return 1}}function Ve(t,e){let n=[];for(let r of e){let o=t.filter(s=>s.kind===r).slice(0,z(r));for(let s of o)n.push(`${H[r]}: ${s.value}`)}return n.join(`
|
|
3
|
+
`)}function Ye(t,e){let n=We[e]??q,r=[];for(let o of n){let s=t.filter(c=>c.kind===o).slice(0,z(o));for(let c of s)r.push(`${H[o]}: ${c.value}`)}return r.join(`
|
|
4
|
+
`)}function W(t,e){let n=h(t,e,{maxFacts:32,maxChars:6400}),r=Ue(Qe(n.facts)),o=Be(e)?Ve(r,["todo","task","next_action","file"]):Ye(r,String(n.intent));return o?{intent:String(n.intent),answer:o,source:r[0]?.source??"none"}:{intent:String(n.intent),answer:"ToolNet ch\u01B0a c\xF3 \u0111\u1EE7 memory cho c\xE2u h\u1ECFi n\xE0y.",source:"none"}}import{existsSync as Je,mkdirSync as Ge,readFileSync as Xe,renameSync as Ze,rmSync as et,writeFileSync as tt}from"node:fs";import{join as B}from"node:path";var nt=720*60*1e3;function Q(t){return B(t.rootPath,".toolnet","context","memory-agent-conversation.json")}function C(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}function rt(t){let e=C(t);return[/\btiếp tục\b/u,/\btiếp tục task\b/u,/\blúc nãy\b/u,/\btrước đó\b/u,/\btask trước\b/u,/\bprevious (?:task|session|work)\b/u,/\bcontinue\b/u,/\bresume\b/u,/\bcurrent task\b/u,/\bđang làm gì\b/u].some(n=>n.test(e))}function ot(t){if(rt(t))return!1;let e=C(t),n=e.split(/\s+/u).filter(Boolean);return[/^(?:tại sao|vì sao|sao vậy)\b/u,/^(?:why|why so|why that)\b/u,/^(?:còn|thế|vậy|rồi sao|sau đó)\b/u,/^(?:what about|and what|then what)\b/u,/^(?:cái đó|việc đó|nó|phần đó)\b/u].some(r=>r.test(e))?!0:n.length<=5&&/\b(?:nó|đó|vậy|thế|that|it|this)\b/u.test(e)}function st(t,e,n){let r=Q(t);if(!Je(r))return null;try{let o=JSON.parse(Xe(r,"utf8"));if(o.schema!=="toolnet.memory-conversation.v1"||o.version!==1||o.projectId!==t.id||!Array.isArray(o.focus))return null;let s=Date.parse(o.updatedAt);return!Number.isFinite(s)||e-s>n?null:o}catch{return null}}function it(t,e=8){let n=new Set,r=[];for(let o of t){let s=o.value.replace(/\s+/gu," ").trim();if(!s)continue;let c=[o.kind,C(s)].join(":");if(!n.has(c)&&(n.add(c),r.push({...o,value:s}),r.length>=e))break}return r}function ct(t,e){let n=Q(t),r=B(t.rootPath,".toolnet","context");Ge(r,{recursive:!0,mode:448});let o=`${n}.${process.pid}.tmp`;try{tt(o,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{encoding:"utf8",mode:384}),Ze(o,n)}finally{et(o,{force:!0})}}function at(t){return t.focus.slice(0,8).map(e=>`- ${e.kind}: ${e.value}`).join(`
|
|
6
|
+
`)}function ut(t,e){return t.priorFocus.find(n=>n.kind===e)?.value}function U(t){if(!t.usedPriorFocus||t.currentIntent!=="summary"||t.priorFocus.length===0)return null;let e=C(t.originalQuestion),n=/^(?:tại sao|vì sao|sao vậy|why|why so|why that)\b/u.test(e),r=/^(?:rồi sao|sau đó|tiếp thì sao|thế tiếp theo|then what|what next)\b/u.test(e),o=n?["decision","blocker","next_action","task","file"]:r?["next_action","todo","task","blocker","file","decision"]:["task","next_action","file","decision","blocker"],s={task:"Task li\xEAn quan",next_action:"Vi\u1EC7c ti\u1EBFp theo",file:"File li\xEAn quan",decision:"Quy\u1EBFt \u0111\u1ECBnh li\xEAn quan",blocker:"Blocker",todo:"TODO"},c=[];for(let l of o){let f=ut(t,l);if(f&&(c.push(`${s[l]??l}: ${f}.`),n&&c.length>=2||!n&&c.length>=3))break}return c.length===0?null:{answer:c.join(" "),intent:"summary",source:"conversation-focus"}}function lt(t){let e=C(t);return/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(e)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(e)||/(?:còn|các)\s+(?:todo|việc)\b/u.test(e)}function V(t,e,n={}){let r=n.now??Date.now(),o=n.maxAgeMs??nt,s=st(t,r,o),c=!!(s&&s.focus.length>0&&ot(e)&&!lt(e)),l=c&&s?[e.trim(),"","Previous ToolNet Memory conversation focus:",at(s),"","Use the compact focus only to resolve the current follow-up question.","Do not reconstruct or request raw session transcripts."].join(`
|
|
7
|
+
`):e.trim(),f=M(e),d=h(t,l,{maxFacts:8,maxChars:1800}).facts.map(y=>({kind:y.kind,value:y.value,source:y.source})),p=it(c&&s?[...d,...s.focus]:d);return ct(t,{schema:"toolnet.memory-conversation.v1",version:1,projectId:t.id,updatedAt:new Date(r).toISOString(),lastIntent:f,focus:p}),{question:l,originalQuestion:e,usedPriorFocus:c,previousIntent:s?.lastIntent,currentIntent:f,priorFocus:s?.focus??[],focusCount:p.length}}import{existsSync as dt,readFileSync as ft}from"node:fs";import{join as mt}from"node:path";function g(t){return!!t?.trim()}function Y(t){let e=[],n=[],r=100;g(t.goal)||g(t.request)||(e.push("goal_or_request"),r-=20),g(t.activity)||g(t.current.task?.title)||g(t.current.phase?.title)||(e.push("current_work"),r-=25),g(t.nextAction)||g(t.remaining.todos[0])||(e.push("next_action"),r-=25),t.tests.status!=="unknown"||t.tests.recent.length>0||(t.tests.checks?.length??0)>0||(t.evidence?.commands.length??0)>0||(t.evidence?.references.length??0)>0||(e.push("evidence"),r-=20),g(t.files.current)||t.files.recent.length>0||(t.files.active?.length??0)>0||(t.files.modified?.length??0)>0||(t.files.created?.length??0)>0||(e.push("files"),r-=10),t.tests.status==="failing"&&n.push("tests_failing"),t.blockers.length>0&&n.push("active_blockers"),r=Math.max(0,Math.min(100,r));let a=e.includes("current_work")||e.includes("next_action"),d;return r>=80&&!a?d="high":r>=50?d="medium":d="low",{confidence:d,score:r,missingContext:e,warnings:n}}var J={compact:{completed:3,evidence:3,files:5,blockers:2,maxChars:1800},normal:{completed:10,evidence:10,files:15,blockers:5,maxChars:4200},benchmark:{completed:30,evidence:30,files:40,blockers:10,maxChars:1e4}};function b(t){let e=new Set,n=[];for(let r of t){let o=r?.replace(/\s+/gu," ").trim();if(!o)continue;let s=o.normalize("NFKC").toLowerCase();e.has(s)||(e.add(s),n.push(o))}return n}function v(t){return t.length>0?t.map(e=>`- ${e}`).join(`
|
|
8
|
+
`):"- none"}function pt(t,e){return t.length<=e?t:[t.slice(0,e).trimEnd(),"","[Structured handoff truncated]"].join(`
|
|
9
|
+
`)}function G(t,e){if(e)return e;let n=t.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim();return/\bbenchmark\b/u.test(n)||/\bdeep handoff\b/u.test(n)||/\bfull handoff\b/u.test(n)||/\btakeover\b/u.test(n)||/\btake over\b/u.test(n)||/tiếp quản/u.test(n)||/bằng chứng đầy đủ/u.test(n)||/evidence.*(?:file|test|command)/u.test(n)?"benchmark":/\bcompact\b/u.test(n)||/\bbrief\b/u.test(n)||/ngắn gọn/u.test(n)||/tóm tắt ngắn/u.test(n)?"compact":"normal"}function gt(t,e="normal"){let n=J[e],r=b([`Source agent: ${t.source.agent}`,`Captured: ${t.capturedAt}`,t.goal?`Goal: ${t.goal}`:void 0,t.request?`Request: ${t.request}`:void 0,t.activity?`Activity: ${t.activity}`:void 0,t.current.phase?`Phase: ${t.current.phase.title} [${t.current.phase.status}]`:void 0,t.current.task?`Task: ${t.current.task.title} [${t.current.task.status}]`:void 0,t.current.file?`Current file: ${t.current.file}`:void 0,`Progress: ${JSON.stringify(t.progress)}`]),o=b([...t.completed.phases.map(a=>`Phase: ${a}`),...t.completed.tasks.map(a=>`Task: ${a}`)]).slice(0,n.completed),s=b([`Tests status: ${t.tests.status}`,...t.tests.recent.map(a=>`Test: ${a}`),...(t.tests.checks??[]).map(a=>`Check [${a.status}] ${a.kind}: ${a.command}`),...(t.evidence?.commands??[]).map(a=>`Command: ${a}`),...(t.evidence?.references??[]).map(a=>`Reference: ${a}`),...t.decisions.map(a=>`Decision: ${a}`),...t.attention.map(a=>`Attention: ${a}`)]).slice(0,n.evidence),c=b([t.files.current,...(t.files.active??[]).map(a=>`Active: ${a}`),...(t.files.modified??[]).map(a=>`Modified: ${a}`),...(t.files.created??[]).map(a=>`Created: ${a}`),...(t.files.deleted??[]).map(a=>`Deleted: ${a}`),...t.files.recent.map(a=>`Recent: ${a}`)]).slice(0,n.files),l=b(t.blockers).slice(0,n.blockers),f=b([t.nextAction,t.nextAction?void 0:t.remaining.todos[0]]).slice(0,1);return{currentState:r,completed:o,evidence:s,filesTouched:c,blockers:l,nextAction:f}}function kt(t,e,n){return[`HANDOFF_DETAIL: ${e}`,`CONTINUITY_CONFIDENCE: ${n.confidence}`,`QUALITY_SCORE: ${n.score}`,`MISSING_CONTEXT: ${n.missingContext.length>0?n.missingContext.join(", "):"none"}`,`QUALITY_WARNINGS: ${n.warnings.length>0?n.warnings.join(", "):"none"}`,"","CURRENT_STATE",v(t.currentState),"","COMPLETED",v(t.completed),"","EVIDENCE",v(t.evidence),"","FILES_TOUCHED",v(t.filesTouched),"","BLOCKERS",v(t.blockers),"","NEXT_ACTION",v(t.nextAction)].join(`
|
|
10
|
+
`)}function X(t,e="normal"){let n=mt(t.rootPath,".toolnet","work","handoff-latest.json");if(!dt(n))return null;try{let o=JSON.parse(ft(n,"utf8")).continuity;if(!o||o.schema!=="toolnet.handoff.v2"||o.version!==2)return null;let s=gt(o,e),c=Y(o),l=pt(kt(s,e,c),J[e].maxChars);return{detail:e,data:s,quality:c,text:l}}catch{return null}}function yt(t){return t.normalize("NFKC").toLowerCase().replace(/\s+/gu," ").trim()}var ht=[/\bhandoff\b/u,/\bcontinuity\b/u,/\bcontinue\b/u,/\bresume\b/u,/\btakeover\b/u,/\btake over\b/u,/tiếp tục/u,/tiếp quản/u,/bàn giao/u,/dừng ở đâu/u,/đang làm gì/u],bt=[/\bcurrent state\b/u,/\bcurrent task\b/u,/\bevidence\b/u,/\breport locations?\b/u,/\bfiles?\b/u,/\burls?\b/u,/\bassets?\b/u,/\btests?\b/u,/\bcommands?\b/u,/\bblockers?\b/u,/\bnext actions?\b/u,/trạng thái hiện tại/u,/task hiện tại/u,/bằng chứng/u,/vị trí báo cáo/u,/file/u,/url/u,/blocker/u,/việc tiếp theo/u,/bước tiếp theo/u],vt=[/\bsummarize\b/u,/\bsummary\b/u,/\bfull state\b/u,/\bfull context\b/u,/tóm tắt/u,/đầy đủ/u,/toàn bộ trạng thái/u];function xt(t){let e=0;for(let n of bt)n.test(t)&&(e+=1);return e}function Z(t,e){let n=yt(t);if(/(?:^|\s)(?:todo|to-do)(?:\s|$)/u.test(n)||/(?:việc|task)\s+(?:còn lại|chưa làm|chưa xong|chưa hoàn thành)/u.test(n)||/(?:còn|các)\s+(?:todo|việc)\b/u.test(n))return!1;if(e==="summary"||e==="status")return!0;if(!n)return!1;let o=ht.some(l=>l.test(n)),s=vt.some(l=>l.test(n)),c=xt(n);return!!(o&&c>=1||s&&c>=2||c>=4)}var lr={question:N.string().min(2).max(4e3).describe("Question about previous project work, unfinished tasks, handoff, blockers, decisions, files or next actions."),mode:N.enum(["local"]).optional().describe("Returns deterministic local memory only. No external AI is used."),detail:N.enum(["compact","normal","benchmark"]).optional().describe("Handoff detail level. compact=minimal, normal=standard, benchmark=deep evidence/files/tests for agent takeover.")};function ee(t,e,n,r,o){if(!Z(e,n))return{answer:r};let s=G(e,o),c=X(t.project,s);return c?{answer:c.text,handoff:c.data,detail:c.detail,confidence:c.quality.confidence,missingContext:c.quality.missingContext,quality:c.quality}:{answer:r}}async function O(t,e){let n=V(t.project,e.question),r=U(n);if(r)return{...ee(t,n.originalQuestion,String(r.intent),r.answer,e.detail),mode:"local",usedAi:!1,source:r.source,intent:r.intent};let o=W(t.project,n.originalQuestion),s=ee(t,n.originalQuestion,String(o.intent),o.answer,e.detail);return"handoff"in s?{...s,mode:"local",usedAi:!1,source:o.source,intent:o.intent,routing:"deterministic-handoff"}:{...s,mode:"local",usedAi:!1,source:o.source,intent:o.intent}}var te="RAW_TRANSCRIPT_MUST_NEVER_ENTER_CONTINUITY_CONTEXT_X1";function ne(t,e){re(t,JSON.stringify(e,null,2)+`
|
|
11
|
+
`,"utf8")}function wt(t,e,n,r,o,s,c,l){return{continuity:{schema:"toolnet.handoff.v2",version:2,project:{id:t,name:e},source:{agent:n,nativeSessionId:`${n}-x1-source`,sessionKey:`${n}:${n}-x1-source`,sequence:42,reason:"checkpoint"},capturedAt:"2026-08-13T05:30:00.000Z",goal:`Continue ${e} safely across agents`,request:`Recover ${o} with exact evidence and next action`,activity:`Preparing ${r} takeover from ${n}`,current:{phase:{id:"x1",title:"Cross-Agent Continuity E2E",status:"in_progress"},task:{id:"x1-cross-agent",title:o,status:"in_progress"},file:s},completed:{phases:["A1 OpenCode Adapter","A2 Agy Adapter","A3 Claude Code Adapter"],tasks:["Build canonical handoff schema","Build intelligent memory retrieval"]},remaining:{phases:["X1 Cross-Agent Continuity E2E"],tasks:[o],todos:[c]},nextAction:l,blockers:[],decisions:["Canonical handoff must override stale session state","Never replay raw transcripts during continuity recovery"],files:{current:s,recent:[s]},tests:{status:"passing",recent:["A1-A3 adapter tests passed"],checks:[{kind:"test",status:"passed",command:`node scripts/${n}-to-${r}-verify.mjs`}]},evidence:{commands:[`node scripts/${n}-to-${r}-verify.mjs`],references:[`https://example.test/${n}/${r}/continuity-report`]},attention:[],progress:{phasesTotal:3,phasesCompleted:2,tasksTotal:4,tasksCompleted:3,blocked:0},stateDigest:"a".repeat(64)}}}async function T(t,e){return O({project:t},{question:e,mode:"local"})}async function At(t,e){let n=St(k(Mt(),`toolnet-x1-${t}-${e}-`));try{let r=`x1-${t}-${e}`,o=`x1-${t}-to-${e}`,s={id:r,name:o,rootPath:n,createdAt:"2026-08-13T05:30:00.000Z",updatedAt:"2026-08-13T05:30:00.000Z",graphVersion:1,memoryVersion:1},c=k(n,".toolnet","work"),l=k(n,".toolnet","context"),f=k(n,".toolnet","sessions",t,"raw-session-x1");j(c,{recursive:!0}),j(l,{recursive:!0}),j(f,{recursive:!0});let a=`X1 ${t} to ${e} canonical continuity`,d=`src/x1/${t}-to-${e}.ts`,p=`Implement ${e} continuation step`,y=`Continue ${a} in ${d}`;ne(k(c,"handoff-latest.json"),wt(r,o,t,e,a,d,p,y)),ne(k(l,"session-origin.json"),{version:1,projectId:r,agent:"stale-agent",nativeSessionId:"stale-session",updatedAt:"2026-01-01T00:00:00.000Z",currentTask:"STALE TASK MUST NOT WIN",currentPhase:"STALE PHASE",lastTouchedFile:"src/stale/old.ts",latestNextAction:"STALE NEXT ACTION"}),re(k(f,"events.jsonl"),JSON.stringify({text:te})+`
|
|
12
|
+
`,"utf8");let se=h(s,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?",{maxFacts:12,maxChars:3200}),F=await T(s,"Task hi\u1EC7n t\u1EA1i \u0111ang l\xE0m l\xE0 g\xEC?"),E=await T(s,"File hi\u1EC7n t\u1EA1i l\xE0 file n\xE0o?"),I=await T(s,"TODO c\xF2n l\u1EA1i l\xE0 g\xEC?"),_=await T(s,"T\xF4i ph\u1EA3i l\xE0m g\xEC ti\u1EBFp?"),m=await O({project:s},{mode:"local",detail:"benchmark",question:"Summarize current state, completed work, evidence, files, tests, blockers and exact next action for benchmark agent takeover."}),x=m,ie=`node scripts/${t}-to-${e}-verify.mjs`,ce=`https://example.test/${t}/${e}/continuity-report`,ae=[F.answer,E.answer,I.answer,_.answer,m.answer].join(`
|
|
13
|
+
`),R=[F,E,I,_],D={canonicalHandoffPreferred:se.facts[0]?.source==="handoff",taskRecovered:F.answer.includes(a),fileRecovered:E.answer.includes(d),todoRecovered:I.answer.includes(p),nextActionRecovered:_.answer.includes(y),localModeOnly:R.every(S=>S.mode==="local"),noAiRequired:R.every(S=>S.usedAi===!1),rawTranscriptIsolated:!ae.includes(te),compact:R.every(S=>S.answer.length<1600),structuredTakeoverRecovered:m.answer.includes("CURRENT_STATE")&&m.answer.includes("COMPLETED")&&m.answer.includes("EVIDENCE")&&m.answer.includes("FILES_TOUCHED")&&m.answer.includes("BLOCKERS")&&m.answer.includes("NEXT_ACTION"),benchmarkDetailRecovered:x.detail==="benchmark"&&m.answer.includes("HANDOFF_DETAIL: benchmark"),highConfidence:x.confidence==="high"&&Array.isArray(x.missingContext)&&x.missingContext.length===0,evidenceRecovered:m.answer.includes(ie)&&m.answer.includes(ce),deterministicTakeover:m.usedAi===!1&&x.routing==="deterministic-handoff"};return{from:t,to:e,passed:Object.values(D).every(Boolean),checks:D}}finally{Ct(n,{recursive:!0,force:!0})}}async function oe(){let t=[["agy","codex"],["codex","opencode"],["opencode","claude"],["claude","kiro"],["kiro","cursor"],["cursor","copilot"],["copilot","grok"],["grok","agy"]],e=[];for(let[r,o]of t)e.push(await At(r,o));let n=e.filter(r=>r.passed).length;return{passed:n===e.length,total:e.length,passedCount:n,cases:e}}var u={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",cyan:"\x1B[36m",gray:"\x1B[90m",white:"\x1B[97m"};function Tt(t){return t?`${u.green}\u2713${u.reset}`:`${u.red}\u2717${u.reset}`}function Ft(t){let e=`${t.from.padEnd(8)} \u2192 ${t.to.padEnd(8)}`;if(console.log(`\u2502 ${Tt(t.passed)} ${e}`),!t.passed)for(let[n,r]of Object.entries(t.checks))r||console.log(`\u2502 ${u.red}\u2717${u.reset} ${n}`)}async function Et(){console.log(""),console.log(`${u.cyan}\u25C7${u.reset} ${u.white}ToolNet Cross-Agent Continuity Certification${u.reset}`),console.log(`${u.gray}\u2502${u.reset}`);let t=await oe();for(let e of t.cases)Ft(e);if(console.log(`${u.gray}\u2502${u.reset}`),t.passed){console.log(`${u.green}\u25C6 PASS${u.reset} ${t.passedCount}/${t.total} cross-agent transitions`),console.log(`${u.green}\u2713${u.reset} canonical handoff`),console.log(`${u.green}\u2713${u.reset} memory_agent_ask`),console.log(`${u.green}\u2713${u.reset} task / file / TODO / next action`),console.log(`${u.green}\u2713${u.reset} raw transcripts isolated`),console.log(`${u.green}\u2713${u.reset} AI not required for direct continuity`);return}console.log(`${u.red}\u25C6 FAIL${u.reset} ${t.passedCount}/${t.total} cross-agent transitions`),process.exitCode=1}Et().catch(t=>{console.error(t instanceof Error?t.message:String(t)),process.exitCode=1});
|