toolnet-memory 0.2.15 → 0.2.17
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/README.md +74 -0
- package/bin/toolnet-memory +36 -0
- package/bundle/agy-hook.js +45 -29
- package/bundle/auto-integrate.js +64 -14
- package/bundle/codex-context.js +39 -45
- package/bundle/codex.js +60 -43
- package/bundle/context-runtime.js +95 -22
- package/bundle/doctor.js +5 -3
- package/bundle/full-index.js +8 -8
- package/bundle/handoff-refresh.js +12 -0
- package/bundle/mcp.js +50 -7
- package/bundle/memory-agent.js +49 -0
- package/bundle/memory-query.js +7 -0
- package/bundle/opencode.js +82 -34
- package/bundle/provider.js +3 -0
- package/bundle/runtime.js +11 -11
- package/bundle/semantic.js +6 -6
- package/bundle/setup.js +67 -17
- package/package.json +1 -2
package/bundle/auto-integrate.js
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
|
-
import{existsSync as
|
|
2
|
-
`,{encoding:"utf8",mode:384}),
|
|
1
|
+
import{existsSync as O}from"node:fs";import{homedir as C}from"node:os";import{join as w}from"node:path";import{spawnSync as _e}from"node:child_process";import{existsSync as K,mkdirSync as V,readFileSync as z,writeFileSync as Q}from"node:fs";import{homedir as Z}from"node:os";import{dirname as ee,join as te}from"node:path";function ne(e){return`'${e.replace(/'/g,"'\\''")}'`}function N(e={}){let n=e.hooksFile??te(Z(),".gemini","config","hooks.json");V(ee(n),{recursive:!0});let t={};if(K(n))try{t=JSON.parse(z(n,"utf8"))}catch(s){throw new Error(`Invalid existing Agy hooks.json: ${s instanceof Error?s.message:String(s)}`)}let o=e.binary??"toolnet-memory",r=`${ne(o)} session:agy-hook`;return t["toolnet-memory"]={enabled:!0,PreInvocation:[{type:"command",command:`${r} pre`,timeout:15}],PostInvocation:[{type:"command",command:`${r} post`,timeout:15}],Stop:[{type:"command",command:`${r} stop`,timeout:30}]},Q(n,JSON.stringify(t,null,2)+`
|
|
2
|
+
`,{encoding:"utf8",mode:384}),n}import{existsSync as oe,mkdirSync as re,readFileSync as se,renameSync as ie,writeFileSync as ce}from"node:fs";import{homedir as ae}from"node:os";import{dirname as le,join as de}from"node:path";function p(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function ue(e,n){re(le(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;ce(t,`${JSON.stringify(n,null,2)}
|
|
3
|
+
`,{encoding:"utf8",mode:384}),ie(t,e)}function I(e){if(!oe(e))return{};let n=se(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing Agy MCP config: ${o instanceof Error?o.message:String(o)}`)}if(!p(t))throw new Error("Invalid existing Agy MCP config: root must be a JSON object.");return t}function T(e,n){return p(e)?e.command===n&&Array.isArray(e.args)&&e.args.length===1&&e.args[0]==="mcp":!1}function k(e={}){let n=e.configFile??de(ae(),".gemini","config","mcp_config.json"),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",r=I(n),s=r.mcpServers;if(s!==void 0&&!p(s))throw new Error("Invalid existing Agy MCP config: mcpServers must be an object.");let i=p(s)?{...s}:{},c=i[o];if(T(c,t))return{installed:!0,changed:!1,configFile:n,serverName:o,command:t,args:["mcp"]};i[o]={command:t,args:["mcp"]};let d={...r,mcpServers:i};ue(n,d);let l=I(n).mcpServers;if(!p(l)||!T(l[o],t))throw new Error("Agy MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:t,args:["mcp"]}}import{existsSync as pe,mkdirSync as M,readFileSync as fe,writeFileSync as A}from"node:fs";import{homedir as x}from"node:os";import{join as g}from"node:path";var me="memory_agent_ask";function E(){return`
|
|
4
|
+
[TOOLNET MEMORY AGENT]
|
|
5
|
+
|
|
6
|
+
Tool available:
|
|
7
|
+
- ${me}
|
|
8
|
+
|
|
9
|
+
Use it automatically BEFORE guessing when:
|
|
10
|
+
|
|
11
|
+
- The user asks to continue, resume, or pick up previous work.
|
|
12
|
+
- The user says things like:
|
|
13
|
+
- "ti\u1EBFp t\u1EE5c task l\xFAc n\xE3y"
|
|
14
|
+
- "l\xE0m ti\u1EBFp ph\u1EA7n \u0111ang d\u1EDF"
|
|
15
|
+
- "agent tr\u01B0\u1EDBc \u0111ang l\xE0m g\xEC?"
|
|
16
|
+
- "d\u1EEBng \u1EDF \u0111\xE2u?"
|
|
17
|
+
- "todo n\xE0o ch\u01B0a xong?"
|
|
18
|
+
- "continue the previous task"
|
|
19
|
+
- "resume the last session"
|
|
20
|
+
- Previous-agent state, unfinished work, blockers,
|
|
21
|
+
decisions, touched files, or next actions are unclear.
|
|
22
|
+
- Fast startup context is not enough to safely continue.
|
|
23
|
+
|
|
24
|
+
Preferred mode:
|
|
25
|
+
|
|
26
|
+
- mode="local"
|
|
27
|
+
for direct factual questions such as:
|
|
28
|
+
current task, last file, blocker, completed TODOs.
|
|
29
|
+
|
|
30
|
+
- mode="ai"
|
|
31
|
+
for composite or ambiguous continuity questions
|
|
32
|
+
that benefit from ToolNet Memory Agent reasoning.
|
|
33
|
+
|
|
34
|
+
Do NOT call it automatically when:
|
|
35
|
+
|
|
36
|
+
- Normal startup context already gives enough information.
|
|
37
|
+
- The question is unrelated to previous project work.
|
|
38
|
+
- The answer is obvious from current repository evidence.
|
|
39
|
+
|
|
40
|
+
Rules:
|
|
41
|
+
|
|
42
|
+
- Never invent previous work.
|
|
43
|
+
- Current repository evidence overrides stale memory.
|
|
44
|
+
- Do not dump raw transcripts or full memory.
|
|
45
|
+
- After receiving the ToolNet answer, continue the task
|
|
46
|
+
instead of asking the user to repeat known context.
|
|
47
|
+
- If ToolNet says information is not recorded, say so.
|
|
48
|
+
`.trim()}var j="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",h="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function ge(){let e=g(x(),".config","opencode","AGENTS.md");M(g(x(),".config","opencode"),{recursive:!0});let n=`${j}
|
|
3
49
|
## ToolNet Memory
|
|
4
50
|
|
|
5
51
|
ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
|
|
@@ -14,10 +60,13 @@ Rules:
|
|
|
14
60
|
6. Raw transcripts must not be injected into prompts.
|
|
15
61
|
7. Current repository evidence has priority over stale memory when they conflict.
|
|
16
62
|
|
|
17
|
-
${f}`,e=B(o)?J(o,"utf8"):"",r=e.indexOf(C),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+=`
|
|
18
63
|
|
|
19
|
-
|
|
20
|
-
|
|
64
|
+
${E()}
|
|
65
|
+
|
|
66
|
+
${h}`,t=pe(e)?fe(e,"utf8"):"",o=t.indexOf(j),r=t.indexOf(h);return o>=0&&r>=o?t=t.slice(0,o)+n+t.slice(r+h.length):(t=t.trimEnd(),t&&(t+=`
|
|
67
|
+
|
|
68
|
+
`),t+=n),A(e,t.trimEnd()+`
|
|
69
|
+
`,{encoding:"utf8",mode:384}),e}function R(e={}){let n=e.directory??g(x(),".config","opencode","plugins");M(n,{recursive:!0}),ge();let t=g(n,"toolnet-memory.js"),o=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",r=`
|
|
21
70
|
// Generated by ToolNet Memory.
|
|
22
71
|
// OpenCode integration:
|
|
23
72
|
// - project gated
|
|
@@ -31,7 +80,7 @@ import path from "node:path"
|
|
|
31
80
|
import os from "node:os"
|
|
32
81
|
|
|
33
82
|
const TOOLNET_BINARY =
|
|
34
|
-
${JSON.stringify(
|
|
83
|
+
${JSON.stringify(o)}
|
|
35
84
|
|
|
36
85
|
const CONTEXT_MAX_TOKENS = 700
|
|
37
86
|
|
|
@@ -645,11 +694,12 @@ export const ToolNetMemoryPlugin =
|
|
|
645
694
|
},
|
|
646
695
|
}
|
|
647
696
|
}
|
|
648
|
-
`;return
|
|
649
|
-
|
|
650
|
-
`)
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
`)
|
|
654
|
-
`)
|
|
655
|
-
|
|
697
|
+
`;return A(t,r.trimStart(),{encoding:"utf8",mode:384}),t}import{existsSync as $,mkdirSync as ye,readFileSync as he,renameSync as xe,writeFileSync as ve}from"node:fs";import{homedir as Se}from"node:os";import{dirname as P,join as y}from"node:path";function f(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Oe(){let e=process.env.XDG_CONFIG_HOME?.trim();return e?y(e,"opencode"):y(Se(),".config","opencode")}function Ce(){return y(Oe(),"opencode.json")}function we(e,n){ye(P(e),{recursive:!0});let t=`${e}.tmp-${process.pid}-${Date.now()}`;ve(t,`${JSON.stringify(n,null,2)}
|
|
698
|
+
`,{encoding:"utf8",mode:384}),xe(t,e)}function F(e){if(!$(e))return{};let n=he(e,"utf8").trim();if(!n)return{};let t;try{t=JSON.parse(n)}catch(o){throw new Error(`Invalid existing OpenCode opencode.json: ${o instanceof Error?o.message:String(o)}`)}if(!f(t))throw new Error("Invalid existing OpenCode opencode.json: root must be a JSON object.");return t}function _(e,n){if(!f(e))return!1;let t=e.command;return e.type==="local"&&e.enabled!==!1&&Array.isArray(t)&&t.length===2&&t[0]===n&&t[1]==="mcp"}function J(e={}){let n=e.configFile??Ce(),t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=e.serverName??"toolnet-memory",r=y(P(n),"opencode.jsonc"),s=$(r)?r:void 0,i=F(n),c=i.mcp;if(c!==void 0&&!f(c))throw new Error("Invalid existing OpenCode config: mcp must be an object.");let d=f(c)?{...c}:{},u=d[o];if(_(u,t))return{installed:!0,changed:!1,configFile:n,serverName:o,command:[t,"mcp"],preservedJsonc:s};d[o]={type:"local",command:[t,"mcp"],enabled:!0};let l={...i,mcp:d};we(n,l);let m=F(n);if(!f(m.mcp)||!_(m.mcp[o],t))throw new Error("OpenCode MCP configuration was written but verification failed.");return{installed:!0,changed:!0,configFile:n,serverName:o,command:[t,"mcp"],preservedJsonc:s}}import{existsSync as be,mkdirSync as D,readFileSync as Ne,writeFileSync as B}from"node:fs";import{homedir as L}from"node:os";import{dirname as Y,join as v}from"node:path";function Ie(e){let n=[],t=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,o;for(;o=t.exec(e);){let r=o[1]??o[2]??"";try{n.push(o[1]!==void 0?JSON.parse(`"${r}"`):r)}catch{n.push(r)}}return n}function H(e={}){let n=e.configFile??v(process.env.CODEX_HOME??v(L(),".codex"),"config.toml"),t=e.previousFile??v(L(),".config","toolnet-memory","codex-notify-previous.json");D(Y(n),{recursive:!0}),D(Y(t),{recursive:!0});let o=be(n)?Ne(n,"utf8"):"",r=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(r)}, "session:codex-notify"]`,i=o.split(`
|
|
699
|
+
`),c=i.findIndex(a=>/^\s*\[/.test(a));c<0&&(c=i.length);let d=-1,u=-1;for(let a=0;a<c;a+=1)if(/^\s*notify\s*=/.test(i[a])){if(d=a,u=a,i[a].includes("[")&&!i[a].includes("]"))for(;u+1<c&&(u+=1,!i[u].includes("]")););break}let l=[];if(d>=0){let a=i.slice(d,u+1).join(`
|
|
700
|
+
`);l=Ie(a),i.splice(d,u-d+1,s)}else c=i.findIndex(a=>/^\s*\[/.test(a)),c<0&&(c=i.length),i.splice(c,0,s);let m=l.length>=2&&l[l.length-1]==="session:codex-notify";return l.length>0&&!m&&B(t,JSON.stringify(l,null,2)+`
|
|
701
|
+
`,{encoding:"utf8",mode:384}),o=i.join(`
|
|
702
|
+
`),o.endsWith(`
|
|
703
|
+
`)||(o+=`
|
|
704
|
+
`),B(n,o,{encoding:"utf8",mode:384}),{configFile:n,previousFile:t,preservedPrevious:l.length>0&&!m}}import{existsSync as Te,mkdirSync as ke,readFileSync as Ee,writeFileSync as je}from"node:fs";import{homedir as Me}from"node:os";import{dirname as Ae,join as q}from"node:path";function Re(e){return`'${e.replace(/'/g,"'\\''")}'`}function G(e={}){let n=e.hooksFile??q(process.env.CODEX_HOME??q(Me(),".codex"),"hooks.json");ke(Ae(n),{recursive:!0});let t={};if(Te(n))try{t=JSON.parse(Ee(n,"utf8"))}catch(c){throw new Error(`Invalid existing Codex hooks.json: ${c instanceof Error?c.message:String(c)}`)}let o=t.hooks&&typeof t.hooks=="object"&&!Array.isArray(t.hooks)?t.hooks:{};t.hooks=o;let s=(Array.isArray(o.SessionStart)?o.SessionStart:[]).filter(c=>{try{return!JSON.stringify(c).includes("session:codex-context")}catch{return!0}}),i=e.binary??"toolnet-memory";return s.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${Re(i)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),o.SessionStart=s,je(n,JSON.stringify(t,null,2)+`
|
|
705
|
+
`,{encoding:"utf8",mode:384}),n}import{spawnSync as Fe}from"node:child_process";function S(e,n){return Fe(e,n,{encoding:"utf8",stdio:["ignore","pipe","pipe"]})}function U(e,n){let t=S(e,["mcp","get",n,"--json"]);if(t.status!==0||!t.stdout)return null;try{return JSON.parse(t.stdout)}catch{return null}}function X(e,n){return e.enabled!==!1&&e.transport?.type==="stdio"&&e.transport?.command===n&&Array.isArray(e.transport?.args)&&e.transport?.args.length===1&&e.transport.args[0]==="mcp"}function W(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=e.codexBinary??"codex",o=e.serverName??"toolnet-memory",r=U(t,o);if(r&&X(r,n))return{installed:!0,changed:!1,serverName:o,command:n,args:["mcp"]};if(r){let c=S(t,["mcp","remove",o]);if(c.status!==0)return{installed:!1,changed:!1,serverName:o,command:n,args:["mcp"],error:(c.stderr||c.stdout||"Unable to remove old ToolNet MCP configuration.").trim()}}let s=S(t,["mcp","add",o,"--",n,"mcp"]);if(s.status!==0)return{installed:!1,changed:!1,serverName:o,command:n,args:["mcp"],error:(s.stderr||s.stdout||"Unable to register ToolNet MCP.").trim()};let i=U(t,o);return!i||!X(i,n)?{installed:!1,changed:!0,serverName:o,command:n,args:["mcp"],error:"Codex accepted MCP registration but verification did not match expected ToolNet command."}:{installed:!0,changed:!0,serverName:o,command:n,args:["mcp"]}}function b(e){return _e("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function $e(){return b("agy")||O(w(C(),".gemini"))}function Pe(){return b("opencode")||O(w(C(),".config","opencode"))}function Je(){return b("codex")||O(process.env.CODEX_HOME??w(C(),".codex"))}function De(e={}){let n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",t=[];if(!(e.force===!0||$e()))t.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let r=N({binary:n}),s=k({binary:n});t.push({agent:"agy",detected:!0,installed:!0,targets:[r,s.configFile]})}catch(r){t.push({agent:"agy",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||Pe()))t.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let r=R({binary:n}),s=J({binary:n});t.push({agent:"opencode",detected:!0,installed:!0,targets:[r,s.configFile,`mcp:${s.serverName}`]})}catch(r){t.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||Je()))t.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let r=H({binary:n}),s=G({binary:n}),i=W({binary:n});if(!i.installed)throw new Error(i.error??"Codex MCP registration failed");let c=[r.configFile,s,`mcp:${i.serverName}`];r.preservedPrevious&&c.push(r.previousFile),t.push({agent:"codex",detected:!0,installed:!0,targets:c})}catch(r){t.push({agent:"codex",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}return t}function Be(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let n of e){let t=n.agent==="agy"?"Agy / Antigravity":n.agent==="opencode"?"OpenCode":"Codex";if(!n.detected){console.log(`- ${t}: not detected`);continue}if(n.installed){console.log(`\u2713 ${t}: automatic memory enabled`);continue}console.log(`\u2717 ${t}: integration failed`),n.error&&console.log(` ${n.error}`)}console.log("")}async function Le(){let e=process.argv.slice(2),n=e.includes("--all"),t=e.includes("--json"),o=De({force:n});if(t){console.log(JSON.stringify(o,null,2));return}Be(o)}var Ye=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Ye&&Le().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});export{De as installAutoIntegrations};
|
package/bundle/codex-context.js
CHANGED
|
@@ -1,59 +1,53 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
`,{encoding:"utf8",mode:384}),tt(t,r)}async function C(r,e){let t=await e.getText(`projects/${r.id}/work/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}import{existsSync as Q,mkdirSync as st,readFileSync as ot,statSync as Y,writeFileSync as it}from"node:fs";import{dirname as at,join as ct}from"node:path";var G=64*1024,ut=`# ToolNet Project Operating Manual
|
|
1
|
+
import*as p from"fs";import*as u from"path";function w(e){return e?Math.ceil(e.length/3.5):0}function B(e,t){if(!e)return"";if(w(e)<=t)return e;let n=Math.floor(t*3.5),o=e.slice(0,n),s=o.lastIndexOf("."),i=o.lastIndexOf(`
|
|
2
|
+
`),c=Math.max(s,i);return c>n*.7?o.slice(0,c+1):o}function b(e,t){if(!e)return"";let r=e.split(`
|
|
3
|
+
`).filter(s=>s.trim());if(r.length<=t)return e;let n=r.slice(0,t),o=r.length-t;return[...n,`... (${o} more items omitted)`].join(`
|
|
4
|
+
`)}function Y(e,t){let{maxTokens:r,trimMarker:n="[Context trimmed by ToolNet Memory token budget]"}=t;if(e.length===0)return"";let o=[...e].sort((a,m)=>m.priority-a.priority),s=[],i=0,c=!1;for(let a of o){let m=`# ${a.title}
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
`,d=w(m),f=w(a.content),h=d+f;if(i+h<=r)s.push(m+a.content),i+=h;else{let S=r-i-d;if(S>50){let g=B(a.content,S);s.push(m+g),i=r,c=!0}else c=!0;break}}return c&&s.push(`
|
|
7
|
+
${n}
|
|
8
|
+
`),s.join(`
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
---
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
Examples:
|
|
12
|
+
`)}function C(e,t){let r=[{title:"Profile",content:b(e,10),priority:100},{title:"Current Work",content:b(t,15),priority:90}];return Y(r,{maxTokens:800})}import{existsSync as M,mkdirSync as ke,readFileSync as v,renameSync as Me,writeFileSync as ve}from"node:fs";import{dirname as je,join as _}from"node:path";import{createHash as V}from"node:crypto";function k(e){return V("sha256").update(e).digest("hex")}function U(e){return _(e.rootPath,".toolnet","context","handoff.md")}function G(e){return _(e.rootPath,".toolnet","context","handoff.json")}function J(e){let t=U(e);if(!M(t))return null;try{let r=v(t,"utf8").trim();if(!r)return null;let n=new Date(0).toISOString(),o=G(e);if(M(o))try{let s=JSON.parse(v(o,"utf8"));typeof s.generatedAt=="string"&&(n=s.generatedAt)}catch{}return{version:1,projectId:e.id,projectName:e.name,text:r,digest:k(r),generatedAt:n}}catch{return null}}function j(e,t=1800){let r=J(e);if(!r?.text)return null;let n=r.text;return n.length>t&&(n=`${n.slice(0,t)}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-->
|
|
14
|
+
[Fast handoff truncated]`),["[TOOLNET FAST HANDOFF]","",`Project: ${e.name}`,`Updated: ${r.generatedAt}`,"",n].join(`
|
|
15
|
+
`)}var O="memory_agent_ask";function I(){return`
|
|
16
|
+
[TOOLNET MEMORY AGENT]
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
Tool:
|
|
19
|
+
- ${O}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-->
|
|
21
|
+
If the user asks to resume previous work and the fast context
|
|
22
|
+
is insufficient, call ${O} before guessing.
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
Use:
|
|
25
|
+
- mode="local" for current task, last file, blocker or next action.
|
|
26
|
+
- mode="ai" for ambiguous or combined continuity questions.
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
Current repository evidence overrides stale memory.
|
|
29
|
+
`.trim()}function W(e){let t=u.resolve(e),r=u.parse(t).root;for(;t!==r;){let n=u.join(t,".toolnet");if(p.existsSync(n)&&p.statSync(n).isDirectory())return t;t=u.dirname(t)}return null}function P(e){try{return p.existsSync(e)?p.readFileSync(e,"utf-8").trim():null}catch{return null}}function N(e){return e.split(`
|
|
30
|
+
`).filter(n=>{let o=n.toUpperCase();return!(o.includes("SECRET")||o.includes("TOKEN")||o.includes("API_KEY")||o.includes("APIKEY")||o.includes("PASSWORD")||o.includes("PASS="))}).join(`
|
|
31
|
+
`)}function D(e={}){let t=e.projectPath||process.cwd(),r=W(t);if(!r)return null;let n=u.join(r,".toolnet","project.json"),o="Unknown";try{if(p.existsSync(n)){let g=JSON.parse(p.readFileSync(n,"utf-8"));o=g.name||g.projectName||"Unknown"}}catch{}let s=u.join(r,".toolnet","profile.md"),i=u.join(r,".toolnet","current.md"),c=P(s)||"",a=P(i)||"";c=N(c),a=N(a);let m=`[TOOLNET PROJECT CONTEXT]
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
Project: ${o}
|
|
34
|
+
Root: ${r}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
Tests / QA required after changes.
|
|
35
|
-
-->
|
|
36
|
+
`,d=C(c,a),f=j({id:"",name:o,rootPath:r},1600),h=f?`
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
${f}
|
|
39
|
+
`:"",S=`
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
Things an AI agent should always remember.
|
|
41
|
-
-->
|
|
41
|
+
${I()}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
Forbidden At Startup:
|
|
44
|
+
- Do not run session:agy-recover, handoff:latest, or brief automatically.
|
|
45
|
+
- Do not perform deep recovery merely because an agent starts.
|
|
46
|
+
- If the user asks to resume previous work and fast context is insufficient,
|
|
47
|
+
use memory_agent_ask before guessing.
|
|
48
|
+
`;return m+d+h+S}import{existsSync as le}from"node:fs";import{dirname as ge,join as ye,parse as he,resolve as Se}from"node:path";import{existsSync as Z,readFileSync as X}from"node:fs";import{homedir as Q}from"node:os";import{join as q}from"node:path";function ee(e){let t=e.trim();return t.length>=2&&t.startsWith('"')&&t.endsWith('"')?(t=t.slice(1,-1),t.replace(/\\n/g,`
|
|
49
|
+
`).replace(/\\r/g,"\r").replace(/\\t/g," ").replace(/\\"/g,'"').replace(/\\\\/g,"\\")):t.length>=2&&t.startsWith("'")&&t.endsWith("'")?t.slice(1,-1):t}function te(){let e=process.env.TOOLNET_GLOBAL_ENV??q(Q(),".config","toolnet-memory",".env");if(!Z(e))return;let t=X(e,"utf8");for(let r of t.split(/\r?\n/)){let n=r.trim();if(!n||n.startsWith("#"))continue;n.startsWith("export ")&&(n=n.slice(7));let o=n.indexOf("=");if(o<=0)continue;let s=n.slice(0,o).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(s)&&process.env[s]===void 0&&(process.env[s]=ee(n.slice(o+1)))}}te();import{createHash as re}from"node:crypto";import{existsSync as E,mkdirSync as ne,readFileSync as oe,renameSync as se,writeFileSync as ie}from"node:fs";import{basename as ce,dirname as x,join as A,parse as $,resolve as y}from"node:path";var z=".toolnet",ae="project.json";function ue(e){return re("sha256").update(e).digest("hex").slice(0,16)}function R(e){return A(e,z,ae)}function me(e){return E(R(e))}function fe(e,t){let r=y(e),n=$(r).root;for(;;){if(me(r))return r;if(r===n||t&&r===y(t))break;let o=x(r);if(o===r)break;r=o}return null}function pe(e){let t=y(e),r=$(t).root,n=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"];for(;;){if(n.some(s=>E(A(t,s))))return t;if(t===r)break;let o=x(t);if(o===t)break;t=o}return y(e)}function de(e){let t;try{t=JSON.parse(oe(e,"utf8"))}catch(o){throw new Error(`Invalid ToolNet project manifest: ${e}: ${o instanceof Error?o.message:String(o)}`)}if(!t||typeof t!="object")throw new Error(`Invalid ToolNet project manifest: ${e}`);let r=t;if(typeof r.id!="string"||!r.id.trim())throw new Error(`ToolNet project manifest is missing id: ${e}`);if(typeof r.name!="string"||!r.name.trim())throw new Error(`ToolNet project manifest is missing name: ${e}`);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:x(x(e)),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 F(e,t){let r=A(e,z);ne(r,{recursive:!0});let n=R(e),o=`${n}.tmp-${process.pid}`;ie(o,JSON.stringify(t,null,2)+`
|
|
50
|
+
`,{encoding:"utf8",mode:384}),se(o,n)}function L(e,t){return{id:e.id,name:e.name,remote:e.remote,rootPath:t,createdAt:e.createdAt,updatedAt:e.updatedAt,graphVersion:e.graphVersion,memoryVersion:e.memoryVersion,metadata:e.metadata}}var T=class{detect(t=process.cwd()){let r=y(t),n=pe(r),s=[".git","package.json","pyproject.toml","Cargo.toml","go.mod","composer.json"].some(d=>E(A(n,d))),i=fe(r,s?n:void 0);if(i){let d=R(i),f=de(d);return f.rootPath!==i&&(f.rootPath=i,f.updatedAt=new Date().toISOString(),F(i,f)),L(f,i)}let c=new Date().toISOString(),a=ce(n),m={version:1,id:ue(n),name:a,remote:a,rootPath:n,createdAt:c,updatedAt:c,graphVersion:0,memoryVersion:0};return F(n,m),L(m,n)}};function K(e){let t=Se(e),r=he(t).root;for(;;){if(le(ye(t,".toolnet","project.json")))return new T().detect(t);if(t===r)break;let n=ge(t);if(n===t)break;t=n}return null}var H=3200;async function xe(){let e="";for await(let t of process.stdin)e+=t.toString();if(!e.trim())return{};try{return JSON.parse(e)}catch{return{}}}function l(){process.stdout.write("{}")}function Te(e){return e.length<=H?e:`${e.slice(0,H)}
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-->
|
|
48
|
-
`;function I(r){return ct(r.rootPath,".toolnet","PROJECT.md")}function pt(r){return r.normalize("NFKC").replace(/\s+/g," ").trim()}function lt(r){let e=[],t=new Set,n=/^\s*[-*]\s+\[(enforce|advisory)\]\s+(.+?)\s*$/gimu,s;for(;s=n.exec(r);){let o=s[1].toLowerCase(),c=pt(s[2]);if(!c)continue;let l=`${o}:${c.toLowerCase()}`;t.has(l)||(t.add(l),e.push({id:f(l).slice(0,24),mode:o,text:c,source:"manual"}))}return e}function dt(r){let e=I(r);return Q(e)||(st(at(e),{recursive:!0}),it(e,ut,{encoding:"utf8",mode:384})),e}function R(r,e=!1){let t=e?dt(r):I(r);if(!Q(t))return null;if(Y(t).size>G)throw new Error(`PROJECT.md exceeds ${G} bytes`);let s=ot(t,"utf8");return{path:t,content:s,digest:f(s),rules:lt(s),bytes:Buffer.byteLength(s,"utf8"),updatedAt:new Date(Y(t).mtimeMs).toISOString()}}async function X(r,e){let t=await e.getText(`projects/${r.id}/work/handoff-latest.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}async function Z(r,e){let t=await e.getText(`projects/${r.id}/work/semantic/current.json`);if(!t)return null;try{return JSON.parse(t)}catch{return null}}function N(r){if(!r)return 0;let e=Array.from(r).length,t=r.trim().split(/\s+/u).filter(Boolean).length;return Math.ceil(Math.max(e/3.5,t*1.3))}function d(r,e){let t=r.replace(/\s+/g," ").trim();return t.length<=e?t:t.slice(0,Math.max(0,e-1)).trimEnd()+"\u2026"}function ft(r){let e=[],t=!1;for(let n of r.split(/\r?\n/u)){let s=n.trim();if(s.includes("<!--")&&(t=!0),t){s.includes("-->")&&(t=!1);continue}let o=s.toLowerCase();if(!(!s||s.startsWith("#")||s==="```"||o.startsWith("- [enforce]")||o.startsWith("* [enforce]")||o.startsWith("- [advisory]")||o.startsWith("* [advisory]"))&&(s=s.replace(/^[-*]\s+/u,""),s&&e.push(d(s,280)),e.length>=16))break}return e}function mt(r){let e=[],t=[];for(let n of r.split(/\\r?\\n/u)){let s=n.trim(),o=s.toLowerCase(),l=["- [enforce]","* [enforce]","- [advisory]","* [advisory]"].find(p=>o.startsWith(p));if(!l)continue;let a=s.slice(l.length).trim();a&&(l.includes("enforce")?e.push(a):t.push(a))}return{enforce:e,advisory:t}}function gt(r,e){let t=[];for(let n of r){let s=[...t,n].join(`
|
|
49
|
-
`);if(N(s)<=e){t.push(n);continue}let o=N(t.join(`
|
|
50
|
-
`)),c=Math.max(0,e-o);if(c>=16){let l=Math.floor(c*3.2),a=d(n,l);a&&t.push(a)}break}return t.join(`
|
|
51
|
-
`).trim()}async function ee(r){let e=Math.max(256,Math.min(2e3,r.maxTokens??1e3)),t=R(r.project,!1),n=t?.content??"";n||(n=await r.storage.getText(`projects/${r.project.id}/project/manual.md`)??"");let s=mt(n),o=t?t.rules.filter(u=>u.mode==="enforce").map(u=>u.text):s.enforce,c=t?t.rules.filter(u=>u.mode==="advisory").map(u=>u.text):s.advisory,l=n?ft(n):[],a=await C(r.project,r.storage),p=await Z(r.project,r.storage),m=await X(r.project,r.storage),i=[];if(i.push("[TOOLNET PROJECT CONTEXT]"),i.push(`Project: ${r.project.name}`),i.push("Continue existing project state. Do not restart completed work unless evidence shows it is necessary."),n&&i.push(`Full operating manual: ${I(r.project)}`),o.length){i.push("","PROJECT RULES \u2014 MUST FOLLOW");for(let u of o.slice(0,24))i.push(`- [ENFORCE] ${d(u,240)}`)}if(c.length){i.push("","PROJECT PREFERENCES");for(let u of c.slice(0,10))i.push(`- ${d(u,220)}`)}if(p&&(p.mission&&i.push("","MISSION",d(p.mission.value,420)),p.activeObjective&&i.push("","CURRENT OBJECTIVE",d(p.activeObjective.value,420)),p.why&&i.push("","WHY THIS WORK MATTERS",d(p.why.value,420)),p.desiredOutcome&&i.push("","DESIRED OUTCOME",d(p.desiredOutcome.value,420)),p.planRationale&&i.push("","WHY THIS APPROACH",d(p.planRationale.value,420))),a){if(i.push("","ACTIVE WORK"),a.goal&&i.push(`Goal: ${d(a.goal,320)}`),a.plan&&i.push(`Plan: ${d(a.plan,320)}`),i.push(`Progress: phases ${a.progress.phasesCompleted}/${a.progress.phasesTotal}; tasks ${a.progress.tasksCompleted}/${a.progress.tasksTotal}; blocked ${a.progress.blocked}`),a.currentPhase&&i.push(`Current phase: ${a.currentPhase.title} [${a.currentPhase.status}]`),a.currentPhase&&p){let u=p.phases.find(g=>g.order===a.currentPhase?.order);u&&(u.objective&&i.push(`Phase objective: ${d(u.objective.value,340)}`),u.why?i.push(`Why this phase: ${d(u.why.value,340)}`):i.push("Why this phase: not explicitly recorded. Inspect existing implementation before assuming intent."),u.deliverable&&i.push(`Deliverable: ${d(u.deliverable.value,340)}`),u.dependencies.length&&i.push(`Depends on: ${u.dependencies.slice(0,4).map(g=>d(g.value,180)).join("; ")}`),u.acceptanceCriteria.length&&(i.push("","DEFINITION OF DONE"),u.acceptanceCriteria.slice(0,6).forEach(g=>{i.push(`- ${d(g.value,260)}`)})),u.openQuestions.length&&(i.push("","OPEN QUESTIONS FOR CURRENT PHASE"),u.openQuestions.slice(0,4).forEach(g=>{i.push(`- ${d(g.value,260)}`)})))}a.currentTask&&i.push(`Current task: ${a.currentTask.title} [${a.currentTask.status}]`),a.nextActions.length&&(i.push("","NEXT ACTIONS"),a.nextActions.slice(0,6).forEach((u,g)=>{i.push(`${g+1}. ${d(u,260)}`)})),a.blockers.length&&(i.push("","BLOCKERS"),a.blockers.slice(0,5).forEach(u=>{i.push(`- ${d(u,260)}`)})),a.warnings.length&&(i.push("","ATTENTION"),a.warnings.slice(-5).forEach(u=>{i.push(`- ${d(u,260)}`)})),a.decisions.length&&(i.push("","RECENT DECISIONS"),a.decisions.slice(-5).forEach(u=>{i.push(`- ${d(u,260)}`)})),a.lastSession&&i.push("",`Last work session: ${a.lastSession.agent} / ${a.lastSession.nativeSessionId}`)}if(p&&p.openQuestions.length&&(i.push("","UNRESOLVED QUESTIONS"),p.openQuestions.slice(0,5).forEach(u=>{i.push(`- ${d(u.value,260)}`)})),m&&i.push(`Latest handoff: ${m.reason} / ${m.sourceSession.agent}`),l.length){i.push("","OPERATING NOTES");for(let u of l)i.push(`- ${u}`)}i.push("","Before changing anything: verify the current repository state and continue from the active phase/task above.");let $=gt(i,e);return{version:1,projectId:r.project.id,projectName:r.project.name,text:$,estimatedTokens:N($),maxTokens:e,hasManual:!!n,hasWorkState:!!a,hasHandoff:!!m,generatedAt:new Date().toISOString()}}import{existsSync as ht,mkdirSync as yt,readFileSync as St,writeFileSync as kt}from"node:fs";import{dirname as jt,join as re}from"node:path";function A(r){return r?Math.ceil(r.length/3.5):0}function te(r,e){if(!r)return"";if(A(r)<=e)return r;let n=Math.floor(e*3.5),s=r.slice(0,n),o=s.lastIndexOf("."),c=s.lastIndexOf(`
|
|
52
|
-
`),l=Math.max(o,c);return l>n*.7?s.slice(0,l+1):s}function ne(r){return re(r.rootPath,".toolnet","context","startup.md")}function vt(r){return re(r.rootPath,".toolnet","context","startup.json")}function se(r,e){let t=ne(r);yt(jt(t),{recursive:!0}),kt(t,e.text.endsWith(`
|
|
53
|
-
`)?e.text:e.text+`
|
|
54
|
-
`,{encoding:"utf8",mode:384}),h(vt(r),e)}async function bt(r,e,t=800){let s=(await ee({project:r,storage:e,maxTokens:t})).text;A(s)>t&&(s=te(s,t),s+=`
|
|
55
|
-
|
|
56
|
-
[Context trimmed by ToolNet Memory token budget]
|
|
57
|
-
`);let c={version:1,projectId:r.id,projectName:r.name,text:s,digest:f(s),estimatedTokens:A(s),generatedAt:new Date().toISOString()};return se(r,c),await e.put(`projects/${r.id}/context/startup.md`,c.text+`
|
|
58
|
-
`,"text/markdown"),await e.put(`projects/${r.id}/context/startup.json`,JSON.stringify(c,null,2)+`
|
|
59
|
-
`,"application/json"),c}async function oe(r,e,t=900){try{let s=await e.getText(`projects/${r.id}/context/startup.md`);if(s&&s.trim()){let o=s.trim(),c={version:1,projectId:r.id,projectName:r.name,text:o,digest:f(o),estimatedTokens:Math.ceil(o.length/3.5),generatedAt:new Date().toISOString()};return se(r,c),c}}catch{}let n=ne(r);if(ht(n))try{let s=St(n,"utf8").trim();if(s)return{version:1,projectId:r.id,projectName:r.name,text:s,digest:f(s),estimatedTokens:Math.ceil(s.length/3.5),generatedAt:new Date().toISOString()}}catch{}try{return await bt(r,e,t)}catch{return null}}async function ie(r){let e=await oe(r.project,r.storage,900);return e?.text?{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.text}}:{}}import{existsSync as wt}from"node:fs";import{dirname as xt,join as Pt,parse as Ot,resolve as It}from"node:path";function ae(r){let e=It(r),t=Ot(e).root;for(;;){if(wt(Pt(e,".toolnet","project.json")))return new w().detect(e);if(e===t)break;let n=xt(e);if(n===e)break;e=n}return null}async function At(){let r="";for await(let e of process.stdin)r+=e.toString();if(!r.trim())return{};try{return JSON.parse(r)}catch{return{}}}async function Tt(){let r=await At();if(r.hook_event_name!=="SessionStart"){process.stdout.write("{}");return}let e=typeof r.cwd=="string"?r.cwd:"";if(!e){process.stdout.write("{}");return}let t=ae(e);if(!t){process.stdout.write("{}");return}try{let n=_(),s=U(V({provider:n.storage.provider,huggingface:n.storage.huggingface,localRoot:n.storage.localRoot}),{attempts:2}),o=new O(s,t.id,t.name,t.remote??t.name),c=await ie({project:t,storage:o});process.stdout.write(JSON.stringify(c))}catch{process.stdout.write("{}")}}Tt().catch(()=>{process.stdout.write("{}"),process.exitCode=0});
|
|
52
|
+
[ToolNet startup context truncated]`}function Ae(e,t,r){if(process.env.TOOLNET_CODEX_STARTUP_DEBUG!=="1")return;let n=Date.now()-e;process.stderr.write(`[toolnet-memory] codex SessionStart ${n}ms cwd=${t} chars=${r}
|
|
53
|
+
`)}async function we(){let e=Date.now(),t=await xe();if(t.hook_event_name!=="SessionStart"){l();return}let r=typeof t.cwd=="string"?t.cwd:"";if(!r){l();return}if(!K(r)){l();return}try{let o=D({projectPath:r});if(!o?.trim()){l();return}let s=Te(o),i={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:s}};Ae(e,r,s.length),process.stdout.write(JSON.stringify(i))}catch{l()}}we().catch(()=>{l(),process.exitCode=0});
|