toolnet-memory 0.2.13 → 0.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bundle/setup.js CHANGED
@@ -1,25 +1,171 @@
1
- import m from"node:fs";import Te from"node:os";import q from"node:path";import Ae from"node:readline/promises";import{stdin as d,stdout as S}from"node:process";import{existsSync as A}from"node:fs";import{homedir as R}from"node:os";import{join as N}from"node:path";import{spawnSync as me}from"node:child_process";import{existsSync as V,mkdirSync as W,readFileSync as X,writeFileSync as Z}from"node:fs";import{homedir as Q}from"node:os";import{dirname as z,join as ee}from"node:path";function te(e){return`'${e.replace(/'/g,"'\\''")}'`}function Y(e={}){let t=e.hooksFile??ee(Q(),".gemini","config","hooks.json");W(z(t),{recursive:!0});let o={};if(V(t))try{o=JSON.parse(X(t,"utf8"))}catch(s){throw new Error(`Invalid existing Agy hooks.json: ${s instanceof Error?s.message:String(s)}`)}let r=e.binary??"toolnet-memory",n=`${te(r)} session:agy-hook`;return o["toolnet-memory"]={enabled:!0,PreInvocation:[{type:"command",command:`${n} pre`,timeout:15}],PostInvocation:[{type:"command",command:`${n} post`,timeout:15}],Stop:[{type:"command",command:`${n} stop`,timeout:30}]},Z(t,JSON.stringify(o,null,2)+`
2
- `,{encoding:"utf8",mode:384}),t}import{existsSync as oe,mkdirSync as K,readFileSync as ne,writeFileSync as D}from"node:fs";import{homedir as O}from"node:os";import{join as p}from"node:path";var $="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",C="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function re(){let e=p(O(),".config","opencode","AGENTS.md");K(p(O(),".config","opencode"),{recursive:!0});let t=`${$}
3
- ## ToolNet Memory \u2014 Project Continuity
1
+ import m from"node:fs";import j from"node:os";import R from"node:path";import ze from"node:readline/promises";import{stdin as f,stdout as I}from"node:process";import{HeadBucketCommand as Ze,S3Client as Qe}from"@aws-sdk/client-s3";var W=[{id:"openai-compatible",label:"OpenAI-compatible",requiresApiKey:!0,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"alibaba",label:"Alibaba / DashScope",requiresApiKey:!0,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"openrouter",label:"OpenRouter",defaultBaseUrl:"https://openrouter.ai/api/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"groq",label:"Groq",defaultBaseUrl:"https://api.groq.com/openai/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"deepseek",label:"DeepSeek",defaultBaseUrl:"https://api.deepseek.com",defaultModel:"deepseek-v4-flash",requiresApiKey:!0,transport:"openai-compatible"},{id:"nvidia",label:"NVIDIA NIM",defaultBaseUrl:"https://integrate.api.nvidia.com/v1",defaultModel:"deepseek-ai/deepseek-v4-pro",requiresApiKey:!0,transport:"openai-compatible"},{id:"gemini",label:"Gemini",defaultBaseUrl:"https://generativelanguage.googleapis.com/v1beta",requiresApiKey:!0,transport:"gemini"},{id:"huggingface",label:"Hugging Face",defaultBaseUrl:"https://router.huggingface.co/v1",requiresApiKey:!0,transport:"openai-compatible"},{id:"ollama",label:"Ollama / Local",defaultBaseUrl:"http://127.0.0.1:11434/v1",requiresApiKey:!1,transport:"openai-compatible"},{id:"custom",label:"Custom endpoint",requiresApiKey:!1,requiresBaseUrl:!0,transport:"openai-compatible"},{id:"cloudflare",label:"Cloudflare Workers AI",requiresApiKey:!0,requiresAccountId:!0,transport:"cloudflare"}];function A(e){let t=W.find(o=>o.id===e);if(!t)throw new Error(`Unsupported AI provider: ${e}`);return t}function N(e){return W.some(t=>t.id===e)}function i(e){return process.env[e]?.trim()||void 0}function g(...e){return e.find(t=>!!t?.trim())}function X(){if(i("GROQ_API_KEY"))return"groq";if(i("DEEPSEEK_API_KEY"))return"deepseek";if(i("NVIDIA_API_KEY")||i("NVIDIA_NIM_API_KEY"))return"nvidia";if(i("OPENROUTER_API_KEY"))return"openrouter";if(i("ALIBABA_API_KEY")||i("DASHSCOPE_API_KEY"))return"alibaba";if(i("GEMINI_API_KEY")||i("GOOGLE_API_KEY"))return"gemini";if(i("CLOUDFLARE_API_TOKEN")&&i("CLOUDFLARE_ACCOUNT_ID"))return"cloudflare";if(i("HF_TOKEN"))return"huggingface";if(i("OLLAMA_MODEL")||i("OLLAMA_BASE_URL"))return"ollama"}function z(){let e=i("TOOLNET_LLM_PROVIDER");return e&&N(e)?e:X()??"openai-compatible"}function h(e){let t=A(e);switch(e){case"alibaba":return{provider:e,apiKey:g(i("ALIBABA_API_KEY"),i("DASHSCOPE_API_KEY")),baseUrl:g(i("ALIBABA_BASE_URL"),i("DASHSCOPE_BASE_URL"),t.defaultBaseUrl),model:g(i("ALIBABA_MODEL"),i("DASHSCOPE_MODEL"))};case"openrouter":return{provider:e,apiKey:i("OPENROUTER_API_KEY"),baseUrl:g(i("OPENROUTER_BASE_URL"),t.defaultBaseUrl),model:i("OPENROUTER_MODEL")};case"groq":return{provider:e,apiKey:i("GROQ_API_KEY"),baseUrl:g(i("GROQ_BASE_URL"),t.defaultBaseUrl),model:i("GROQ_MODEL")};case"deepseek":return{provider:e,apiKey:i("DEEPSEEK_API_KEY"),baseUrl:g(i("DEEPSEEK_BASE_URL"),t.defaultBaseUrl),model:g(i("DEEPSEEK_MODEL"),t.defaultModel)};case"nvidia":return{provider:e,apiKey:g(i("NVIDIA_API_KEY"),i("NVIDIA_NIM_API_KEY")),baseUrl:g(i("NVIDIA_BASE_URL"),i("NVIDIA_NIM_BASE_URL"),t.defaultBaseUrl),model:g(i("NVIDIA_MODEL"),i("NVIDIA_NIM_MODEL"),t.defaultModel)};case"gemini":return{provider:e,apiKey:g(i("GEMINI_API_KEY"),i("GOOGLE_API_KEY")),baseUrl:g(i("GEMINI_BASE_URL"),t.defaultBaseUrl),model:i("GEMINI_MODEL")};case"huggingface":return{provider:e,apiKey:i("HF_TOKEN"),baseUrl:g(i("HF_INFERENCE_BASE_URL"),t.defaultBaseUrl),model:g(i("HF_LLM_MODEL"),i("HF_MODEL"))};case"ollama":return{provider:e,apiKey:i("OLLAMA_API_KEY"),baseUrl:g(i("OLLAMA_BASE_URL"),t.defaultBaseUrl),model:i("OLLAMA_MODEL")};case"cloudflare":return{provider:e,accountId:i("CLOUDFLARE_ACCOUNT_ID"),apiKey:i("CLOUDFLARE_API_TOKEN"),baseUrl:i("CLOUDFLARE_AI_BASE_URL"),model:i("CLOUDFLARE_MODEL")};case"custom":return{provider:e,apiKey:i("CUSTOM_AI_API_KEY"),baseUrl:i("CUSTOM_AI_BASE_URL"),model:i("CUSTOM_AI_MODEL")};default:return{provider:"openai-compatible",apiKey:g(i("OPENAI_API_KEY"),i("MODEL_API_KEY")),baseUrl:g(i("OPENAI_BASE_URL"),i("MODEL_BASE_URL")),model:g(i("OPENAI_MODEL"),i("MODEL_NAME"))}}}function Te(){let e=i("TOOLNET_LLM_PROVIDER"),t=e&&N(e)?e:z(),o=A(t),n=h(t);return{provider:t,apiKey:g(i("TOOLNET_LLM_API_KEY"),n.apiKey),baseUrl:g(i("TOOLNET_LLM_BASE_URL"),n.baseUrl,o.defaultBaseUrl),model:g(i("TOOLNET_LLM_MODEL"),n.model,o.defaultModel),accountId:g(i("TOOLNET_LLM_ACCOUNT_ID"),n.accountId)}}function Ae(){let e=i("TOOLNET_EMBEDDING_PROVIDER");return e==="local"?"local":e&&N(e)?e:i("HF_TOKEN")||i("HF_EMBEDDING_MODEL")?"huggingface":"local"}function Se(){let e=Ae();if(e==="local")return{provider:"local",model:g(i("TOOLNET_EMBEDDING_MODEL"),i("LOCAL_EMBEDDING_MODEL"))};let t=A(e),o,n,r,s;switch(e){case"huggingface":o=i("HF_TOKEN"),n=i("HF_INFERENCE_BASE_URL"),r=i("HF_EMBEDDING_MODEL");break;case"openai-compatible":o=i("OPENAI_API_KEY"),n=i("OPENAI_BASE_URL"),r=i("OPENAI_EMBEDDING_MODEL");break;case"cloudflare":o=i("CLOUDFLARE_API_TOKEN"),n=i("CLOUDFLARE_AI_BASE_URL"),r=i("CLOUDFLARE_EMBEDDING_MODEL"),s=i("CLOUDFLARE_ACCOUNT_ID");break;default:o=h(e).apiKey,n=h(e).baseUrl,r=i(`${e.toUpperCase().replace(/-/g,"_")}_EMBEDDING_MODEL`)}return{provider:e,apiKey:g(i("TOOLNET_EMBEDDING_API_KEY"),o),baseUrl:g(i("TOOLNET_EMBEDDING_BASE_URL"),n,t.defaultBaseUrl),model:g(i("TOOLNET_EMBEDDING_MODEL"),r),accountId:g(i("TOOLNET_EMBEDDING_ACCOUNT_ID"),s)}}function Ce(){let e=Te(),t=Se();return{llm:e,embedding:t,legacy:{llm:!i("TOOLNET_LLM_PROVIDER")&&!!X(),embedding:!i("TOOLNET_EMBEDDING_PROVIDER")&&!!(i("HF_TOKEN")||i("HF_EMBEDDING_MODEL"))}}}function Z(e=z()){let t=Ce().llm;if(e===t.provider)return{id:e,apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,accountId:t.accountId};let o=h(e);return{id:e,apiKey:o.apiKey,baseUrl:o.baseUrl,model:o.model,accountId:o.accountId}}var w=class extends Error{status;constructor(t,o){super(t),this.name="AiHttpError",this.status=o}};async function S(e,t,o=3e4){let n=new AbortController,r=setTimeout(()=>n.abort(),o);r.unref?.();try{let s=await fetch(e,{...t,signal:n.signal}),a=await s.text();if(!s.ok){let l=a;try{let c=JSON.parse(a);l=c.error?.message??c.message??a}catch{}throw new w(l||`HTTP ${s.status}`,s.status)}return a.trim()?JSON.parse(a):{}}finally{clearTimeout(r)}}function D(e,t){return`${e.replace(/\/+$/,"")}/${t.replace(/^\/+/,"")}`}var P=class{constructor(t){this.config=t}config;id="cloudflare";model(){let t=this.config.model?.trim();if(!t)throw new Error("cloudflare: MODEL is not configured");return t}async generate(t){let o=this.config.accountId?.trim(),n=this.config.apiKey?.trim();if(!o)throw new Error("cloudflare: ACCOUNT ID is not configured");if(!n)throw new Error("cloudflare: API TOKEN is not configured");let r=this.model(),a=`${(this.config.baseUrl?.trim()||`https://api.cloudflare.com/client/v4/accounts/${o}/ai/run`).replace(/\/+$/,"")}/${r}`,l=await S(a,{method:"POST",headers:{authorization:`Bearer ${n}`,"content-type":"application/json",...this.config.headers},body:JSON.stringify({messages:t.messages,temperature:t.temperature,max_tokens:t.maxTokens})}),c=l.result?.response?.trim();if(!c)throw new Error(l.errors?.[0]?.message??"cloudflare: empty model response");return{text:c,provider:"cloudflare",model:r}}async healthCheck(){let t=Date.now();try{return{ok:!0,provider:"cloudflare",model:(await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8})).model,message:"Provider reachable",latencyMs:Date.now()-t}}catch(o){return{ok:!1,provider:"cloudflare",model:this.config.model,message:o instanceof Error?o.message:String(o),latencyMs:Date.now()-t}}}};var U=class{constructor(t){this.config=t}config;id="gemini";model(){let t=this.config.model?.trim();if(!t)throw new Error("gemini: MODEL is not configured");return t.replace(/^models\//,"")}async generate(t){let o=this.config.apiKey?.trim();if(!o)throw new Error("gemini: API KEY is not configured");let n=this.config.baseUrl?.trim()||"https://generativelanguage.googleapis.com/v1beta",r=this.model(),s=t.messages.filter(E=>E.role==="system"),a=t.messages.filter(E=>E.role!=="system").map(E=>({role:E.role==="assistant"?"model":"user",parts:[{text:E.content}]})),l=`${D(n,`models/${encodeURIComponent(r)}:generateContent`)}?key=${encodeURIComponent(o)}`,c=await S(l,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({...s.length?{systemInstruction:{parts:[{text:s.map(E=>E.content).join(`
2
+
3
+ `)}]}}:{},contents:a,generationConfig:{temperature:t.temperature,maxOutputTokens:t.maxTokens}})}),d=c.candidates?.[0]?.content?.parts?.map(E=>E.text??"").join("").trim();if(!d)throw new Error("gemini: empty model response");return{text:d,provider:"gemini",model:r,usage:c.usageMetadata?{inputTokens:c.usageMetadata.promptTokenCount,outputTokens:c.usageMetadata.candidatesTokenCount,totalTokens:c.usageMetadata.totalTokenCount}:void 0}}async healthCheck(){let t=Date.now();try{return{ok:!0,provider:"gemini",model:(await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8})).model,message:"Provider reachable",latencyMs:Date.now()-t}}catch(o){return{ok:!1,provider:"gemini",model:this.config.model,message:o instanceof Error?o.message:String(o),latencyMs:Date.now()-t}}}};var K=class{constructor(t){this.config=t;this.id=t.id}config;id;baseUrl(){let t=this.config.baseUrl?.trim();if(!t)throw new Error(`${this.id}: BASE URL is not configured`);return t}model(){let t=this.config.model?.trim();if(!t)throw new Error(`${this.id}: MODEL is not configured`);return t}headers(){let t={"content-type":"application/json",...this.config.headers};return this.config.apiKey&&(t.authorization=`Bearer ${this.config.apiKey}`),t}async generate(t){let o=this.model(),n=await S(D(this.baseUrl(),"chat/completions"),{method:"POST",headers:this.headers(),body:JSON.stringify({model:o,messages:t.messages,temperature:t.temperature,max_tokens:t.maxTokens})}),r=n.choices?.[0]?.message?.content?.trim();if(!r)throw new Error(`${this.id}: empty model response`);return{text:r,provider:this.id,model:o,usage:n.usage?{inputTokens:n.usage.prompt_tokens,outputTokens:n.usage.completion_tokens,totalTokens:n.usage.total_tokens}:void 0}}async healthCheck(){let t=Date.now();try{let o=await this.generate({messages:[{role:"user",content:"Reply exactly: OK"}],temperature:0,maxTokens:8});return{ok:!0,provider:this.id,model:o.model,message:"Provider reachable",latencyMs:Date.now()-t}}catch(o){return{ok:!1,provider:this.id,model:this.config.model,message:o instanceof Error?o.message:String(o),latencyMs:Date.now()-t}}}};function Q(e=Z()){switch(A(e.id).transport){case"gemini":return new U(e);case"cloudflare":return new P(e);default:return new K(e)}}import{existsSync as $}from"node:fs";import{homedir as F}from"node:os";import{join as v}from"node:path";import{spawnSync as Ge}from"node:child_process";import{existsSync as ye,mkdirSync as Ie,readFileSync as Re,writeFileSync as Me}from"node:fs";import{homedir as Le}from"node:os";import{dirname as Ne,join as he}from"node:path";function De(e){return`'${e.replace(/'/g,"'\\''")}'`}function ee(e={}){let t=e.hooksFile??he(Le(),".gemini","config","hooks.json");Ie(Ne(t),{recursive:!0});let o={};if(ye(t))try{o=JSON.parse(Re(t,"utf8"))}catch(s){throw new Error(`Invalid existing Agy hooks.json: ${s instanceof Error?s.message:String(s)}`)}let n=e.binary??"toolnet-memory",r=`${De(n)} session:agy-hook`;return o["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}]},Me(t,JSON.stringify(o,null,2)+`
4
+ `,{encoding:"utf8",mode:384}),t}import{existsSync as Pe,mkdirSync as oe,readFileSync as Ue,writeFileSync as ne}from"node:fs";import{homedir as B}from"node:os";import{join as b}from"node:path";var te="<!-- TOOLNET_MEMORY_BOOTSTRAP_START -->",k="<!-- TOOLNET_MEMORY_BOOTSTRAP_END -->";function Ke(){let e=b(B(),".config","opencode","AGENTS.md");oe(b(B(),".config","opencode"),{recursive:!0});let t=`${te}
5
+ ## ToolNet Memory
6
+
7
+ ToolNet Memory is available for projects that already contain a valid \`.toolnet/project.json\`.
8
+
9
+ Rules:
10
+
11
+ 1. Do not create a ToolNet project automatically.
12
+ 2. If no valid \`.toolnet/project.json\` exists in the current project or an ancestor, ignore ToolNet Memory.
13
+ 3. Normal startup context must stay small and selective.
14
+ 4. Do not automatically run deep recovery commands.
15
+ 5. Use ToolNet MCP or selective retrieval when older project knowledge is actually needed.
16
+ 6. Raw transcripts must not be injected into prompts.
17
+ 7. Current repository evidence has priority over stale memory when they conflict.
18
+
19
+ ${k}`,o=Pe(e)?Ue(e,"utf8"):"",n=o.indexOf(te),r=o.indexOf(k);return n>=0&&r>=n?o=o.slice(0,n)+t+o.slice(r+k.length):(o=o.trimEnd(),o&&(o+=`
20
+
21
+ `),o+=t),ne(e,o.trimEnd()+`
22
+ `,{encoding:"utf8",mode:384}),e}function re(e={}){let t=e.directory??b(B(),".config","opencode","plugins");oe(t,{recursive:!0}),Ke();let o=b(t,"toolnet-memory.js"),n=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",r=`
23
+ // Generated by ToolNet Memory.
24
+ // OpenCode integration:
25
+ // - project gated
26
+ // - selective context injection
27
+ // - incremental session checkpoints
28
+ // - periodic durability sync
29
+ // - best-effort shutdown flush
30
+
31
+ import fs from "node:fs"
32
+ import path from "node:path"
33
+ import os from "node:os"
34
+
35
+ const TOOLNET_BINARY =
36
+ ${JSON.stringify(n)}
37
+
38
+ const CONTEXT_MAX_TOKENS = 700
39
+
40
+ const CONTEXT_CACHE_MS = 5000
41
+
42
+ const PERIODIC_SYNC_MS = 30000
43
+
44
+ const SYNC_TIMEOUT_MS = 15000
45
+
46
+ const STATUS_FILE = path.join(
47
+ os.homedir(),
48
+ ".config",
49
+ "toolnet-memory",
50
+ "opencode-sync-status.json"
51
+ )
52
+
53
+ function writeStatus(data) {
54
+ try {
55
+ fs.mkdirSync(
56
+ path.dirname(
57
+ STATUS_FILE
58
+ ),
59
+ {
60
+ recursive: true,
61
+ }
62
+ )
63
+
64
+ fs.writeFileSync(
65
+ STATUS_FILE,
66
+ JSON.stringify(
67
+ {
68
+ timestamp:
69
+ new Date()
70
+ .toISOString(),
71
+ ...data,
72
+ },
73
+ null,
74
+ 2
75
+ ) + "\\n"
76
+ )
77
+ } catch {
78
+ // Status reporting must
79
+ // never break OpenCode.
80
+ }
81
+ }
82
+
83
+ function validProjectManifest(
84
+ file
85
+ ) {
86
+ try {
87
+ const parsed =
88
+ JSON.parse(
89
+ fs.readFileSync(
90
+ file,
91
+ "utf8"
92
+ )
93
+ )
94
+
95
+ return Boolean(
96
+ parsed &&
97
+ typeof parsed ===
98
+ "object" &&
99
+ typeof parsed.id ===
100
+ "string" &&
101
+ parsed.id.length > 0
102
+ )
103
+ } catch {
104
+ return false
105
+ }
106
+ }
107
+
108
+ function findProjectRoot(
109
+ inputDirectory
110
+ ) {
111
+ let current =
112
+ path.resolve(
113
+ inputDirectory
114
+ )
115
+
116
+ const filesystemRoot =
117
+ path.parse(
118
+ current
119
+ ).root
120
+
121
+ while (true) {
122
+ const manifest =
123
+ path.join(
124
+ current,
125
+ ".toolnet",
126
+ "project.json"
127
+ )
128
+
129
+ if (
130
+ fs.existsSync(
131
+ manifest
132
+ ) &&
133
+ validProjectManifest(
134
+ manifest
135
+ )
136
+ ) {
137
+ return current
138
+ }
139
+
140
+ if (
141
+ current ===
142
+ filesystemRoot
143
+ ) {
144
+ break
145
+ }
4
146
 
5
- When the current workspace belongs to a ToolNet project, identified by a nearest ancestor containing \`.toolnet/project.json\`:
147
+ const parent =
148
+ path.dirname(
149
+ current
150
+ )
6
151
 
7
- 1. Before planning, editing, or running implementation commands, read that project root's \`.toolnet/context/startup.md\`.
8
- 2. Treat its PROJECT RULES, MISSION, CURRENT OBJECTIVE, rationale, current phase/task, Definition of Done, blockers and next actions as project continuity context.
9
- 3. Do not restart work already marked completed unless repository evidence proves it must be revisited.
10
- 4. Verify current repository state before continuing old work.
11
- 5. If \`.toolnet/context/startup.md\` is missing, run \`toolnet-memory context:sync --project <project-root>\`, then read it.
12
- 6. Never invent missing rationale. If ToolNet says a reason was not explicitly recorded, inspect the implementation or ask rather than guessing.
152
+ if (
153
+ parent === current
154
+ ) {
155
+ break
156
+ }
13
157
 
14
- This applies automatically to every ToolNet-managed project.
15
- ${C}`,o=oe(e)?ne(e,"utf8"):"",r=o.indexOf($),n=o.indexOf(C);return r>=0&&n>=r?o=o.slice(0,r)+t+o.slice(n+C.length):(o=o.trimEnd(),o&&(o+=`
158
+ current = parent
159
+ }
16
160
 
17
- `),o+=t),D(e,o.trimEnd()+`
18
- `,{encoding:"utf8",mode:384}),e}function w(e={}){let t=e.directory??p(O(),".config","opencode","plugins");K(t,{recursive:!0}),re();let o=p(t,"toolnet-memory.js"),r=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",n=`// Generated by ToolNet Memory.
19
- // OpenCode session capture + continuity bootstrap.
161
+ return null
162
+ }
20
163
 
21
- function getSessionId(event) {
22
- const p = event?.properties ?? {}
164
+ function getSessionId(
165
+ event
166
+ ) {
167
+ const p =
168
+ event?.properties ?? {}
23
169
 
24
170
  const candidates = [
25
171
  p.sessionID,
@@ -28,210 +174,490 @@ function getSessionId(event) {
28
174
  p.session?.id,
29
175
  ]
30
176
 
31
- return candidates.find(
32
- value => typeof value === "string" && value.length > 0
33
- ) ?? null
177
+ return (
178
+ candidates.find(
179
+ value =>
180
+ typeof value ===
181
+ "string" &&
182
+ value.length > 0
183
+ ) ?? null
184
+ )
34
185
  }
35
186
 
36
- export const ToolNetMemoryPlugin = async ({ directory }) => {
37
- const binary = ${JSON.stringify(r)}
38
-
39
- async function refreshContext() {
40
- try {
41
- const child = Bun.spawn(
42
- [
43
- binary,
44
- "context:sync",
45
- "--project",
46
- directory,
47
- ],
48
- {
49
- stdin: "ignore",
50
- stdout: "ignore",
51
- stderr: "ignore",
52
- }
187
+ async function runWithTimeout(
188
+ args,
189
+ {
190
+ stdout = "ignore",
191
+ timeout =
192
+ SYNC_TIMEOUT_MS,
193
+ } = {}
194
+ ) {
195
+ const child =
196
+ Bun.spawn(
197
+ args,
198
+ {
199
+ stdin:
200
+ "ignore",
201
+ stdout,
202
+ stderr:
203
+ "ignore",
204
+ }
205
+ )
206
+
207
+ let timer
208
+
209
+ const timeoutPromise =
210
+ new Promise(
211
+ (_, reject) => {
212
+ timer =
213
+ setTimeout(
214
+ () => {
215
+ try {
216
+ child.kill()
217
+ } catch {}
218
+
219
+ reject(
220
+ new Error(
221
+ "ToolNet command timeout"
222
+ )
223
+ )
224
+ },
225
+ timeout
226
+ )
227
+ }
228
+ )
229
+
230
+ try {
231
+ const exitCode =
232
+ await Promise.race([
233
+ child.exited,
234
+ timeoutPromise,
235
+ ])
236
+
237
+ if (
238
+ typeof exitCode ===
239
+ "number" &&
240
+ exitCode !== 0
241
+ ) {
242
+ throw new Error(
243
+ \`ToolNet exited with code \${exitCode}\`
53
244
  )
245
+ }
54
246
 
55
- await child.exited
56
- } catch {
57
- // Context sync must never break OpenCode startup.
247
+ return child
248
+ } finally {
249
+ if (timer) {
250
+ clearTimeout(
251
+ timer
252
+ )
58
253
  }
59
254
  }
255
+ }
60
256
 
61
- async function readContext() {
62
- try {
63
- const child = Bun.spawn(
64
- [
65
- binary,
66
- "context:print",
67
- "--project",
68
- directory,
69
- "--tokens",
70
- "900",
71
- ],
72
- {
73
- stdin: "ignore",
74
- stdout: "pipe",
75
- stderr: "ignore",
76
- }
257
+ export const ToolNetMemoryPlugin =
258
+ async ({
259
+ directory,
260
+ }) => {
261
+ /*
262
+ * CRITICAL:
263
+ * Never auto-create a ToolNet
264
+ * project just because OpenCode
265
+ * happens to start in /root,
266
+ * /tmp, $HOME, etc.
267
+ */
268
+ const projectRoot =
269
+ findProjectRoot(
270
+ directory
77
271
  )
78
272
 
79
- const text =
80
- await new Response(
81
- child.stdout
82
- ).text()
83
-
84
- await child.exited
273
+ if (!projectRoot) {
274
+ writeStatus({
275
+ active: false,
276
+ reason:
277
+ "no-toolnet-project",
278
+ directory,
279
+ })
85
280
 
86
- return text.trim()
87
- } catch {
88
- return ""
281
+ return {}
89
282
  }
90
- }
91
283
 
92
- function sync(sessionId, flag) {
93
- if (!sessionId) return
284
+ writeStatus({
285
+ active: true,
286
+ projectRoot,
287
+ state:
288
+ "plugin-loaded",
289
+ })
94
290
 
95
- try {
96
- const args = [
97
- binary,
98
- "session:opencode-sync",
99
- sessionId,
100
- "--project",
101
- directory,
102
- ]
291
+ let lastSessionId =
292
+ null
103
293
 
104
- if (flag) {
105
- args.push(flag)
106
- }
294
+ let syncChain =
295
+ Promise.resolve()
107
296
 
108
- const child = Bun.spawn(args, {
109
- stdin: "ignore",
110
- stdout: "ignore",
111
- stderr: "ignore",
112
- })
297
+ let contextCache = {
298
+ value: "",
299
+ expiresAt: 0,
300
+ }
301
+
302
+ async function readContext() {
303
+ const now =
304
+ Date.now()
113
305
 
114
306
  if (
115
- typeof child.unref === "function"
307
+ contextCache.value &&
308
+ now <
309
+ contextCache.expiresAt
116
310
  ) {
117
- child.unref()
311
+ return (
312
+ contextCache.value
313
+ )
118
314
  }
119
- } catch {
120
- // Memory capture must never break OpenCode.
121
- }
122
- }
123
315
 
124
- /*
125
- * Pull the latest project brief from ToolNet remote storage
126
- * before the new OpenCode session starts doing work.
127
- */
128
- await refreshContext()
316
+ try {
317
+ const child =
318
+ Bun.spawn(
319
+ [
320
+ TOOLNET_BINARY,
321
+ "context:print",
322
+ "--project",
323
+ projectRoot,
324
+ "--tokens",
325
+ String(
326
+ CONTEXT_MAX_TOKENS
327
+ ),
328
+ ],
329
+ {
330
+ stdin:
331
+ "ignore",
332
+ stdout:
333
+ "pipe",
334
+ stderr:
335
+ "ignore",
336
+ }
337
+ )
338
+
339
+ const textPromise =
340
+ new Response(
341
+ child.stdout
342
+ ).text()
343
+
344
+ const exitCode =
345
+ await child.exited
346
+
347
+ if (
348
+ exitCode !== 0
349
+ ) {
350
+ return ""
351
+ }
352
+
353
+ const text =
354
+ (
355
+ await textPromise
356
+ ).trim()
129
357
 
130
- return {
131
- event: async ({ event }) => {
132
- const sessionId =
133
- getSessionId(event)
358
+ contextCache = {
359
+ value: text,
360
+ expiresAt:
361
+ now +
362
+ CONTEXT_CACHE_MS,
363
+ }
364
+
365
+ return text
366
+ } catch {
367
+ return ""
368
+ }
369
+ }
134
370
 
371
+ async function syncNow(
372
+ sessionId,
373
+ flag,
374
+ reason
375
+ ) {
135
376
  if (!sessionId) {
136
377
  return
137
378
  }
138
379
 
139
- if (event.type === "session.idle") {
140
- sync(sessionId, "--idle")
141
- return
380
+ const args = [
381
+ TOOLNET_BINARY,
382
+ "session:opencode-sync",
383
+ sessionId,
384
+ "--project",
385
+ projectRoot,
386
+ ]
387
+
388
+ if (flag) {
389
+ args.push(
390
+ flag
391
+ )
142
392
  }
143
393
 
144
- if (event.type === "session.compacted") {
145
- sync(sessionId, "--compacted")
146
- return
394
+ try {
395
+ await runWithTimeout(
396
+ args
397
+ )
398
+
399
+ writeStatus({
400
+ active: true,
401
+ projectRoot,
402
+ sessionId,
403
+ reason,
404
+ state:
405
+ "sync-success",
406
+ })
407
+ } catch (error) {
408
+ writeStatus({
409
+ active: true,
410
+ projectRoot,
411
+ sessionId,
412
+ reason,
413
+ state:
414
+ "sync-failed",
415
+ error:
416
+ error instanceof
417
+ Error
418
+ ? error.message
419
+ : String(
420
+ error
421
+ ),
422
+ })
423
+
424
+ throw error
147
425
  }
426
+ }
148
427
 
149
- if (event.type === "session.error") {
150
- sync(sessionId, "--error")
428
+ function queueSync(
429
+ sessionId,
430
+ flag,
431
+ reason
432
+ ) {
433
+ if (!sessionId) {
434
+ return syncChain
151
435
  }
152
- },
436
+
437
+ lastSessionId =
438
+ sessionId
439
+
440
+ syncChain =
441
+ syncChain
442
+ .catch(
443
+ () =>
444
+ undefined
445
+ )
446
+ .then(
447
+ () =>
448
+ syncNow(
449
+ sessionId,
450
+ flag,
451
+ reason
452
+ )
453
+ )
454
+
455
+ return syncChain
456
+ }
153
457
 
154
458
  /*
155
- * Official OpenCode compaction hook.
156
- * Preserve ToolNet's semantic state after context compaction.
459
+ * Periodic durability checkpoint.
460
+ *
461
+ * This is intentionally independent
462
+ * from session.idle/dispose because
463
+ * abrupt SSH/process termination may
464
+ * skip shutdown hooks.
465
+ *
466
+ * unref() prevents the timer itself
467
+ * from keeping OpenCode alive.
157
468
  */
158
- "experimental.session.compacting": async (_input, output) => {
159
- const context =
160
- await readContext()
469
+ const periodic =
470
+ setInterval(
471
+ () => {
472
+ if (
473
+ lastSessionId
474
+ ) {
475
+ void queueSync(
476
+ lastSessionId,
477
+ null,
478
+ "periodic"
479
+ )
480
+ }
481
+ },
482
+ PERIODIC_SYNC_MS
483
+ )
161
484
 
162
- if (
163
- context &&
164
- Array.isArray(
165
- output.context
166
- )
167
- ) {
168
- output.context.push(
169
- context
485
+ if (
486
+ typeof periodic.unref ===
487
+ "function"
488
+ ) {
489
+ periodic.unref()
490
+ }
491
+
492
+ return {
493
+ event: async ({
494
+ event,
495
+ }) => {
496
+ const sessionId =
497
+ getSessionId(
498
+ event
499
+ )
500
+
501
+ if (sessionId) {
502
+ lastSessionId =
503
+ sessionId
504
+ }
505
+
506
+ if (
507
+ event.type ===
508
+ "session.idle"
509
+ ) {
510
+ await queueSync(
511
+ sessionId,
512
+ "--idle",
513
+ "session.idle"
514
+ )
515
+
516
+ return
517
+ }
518
+
519
+ if (
520
+ event.type ===
521
+ "session.compacted"
522
+ ) {
523
+ await queueSync(
524
+ sessionId,
525
+ "--compacted",
526
+ "session.compacted"
527
+ )
528
+
529
+ contextCache = {
530
+ value: "",
531
+ expiresAt: 0,
532
+ }
533
+
534
+ return
535
+ }
536
+
537
+ if (
538
+ event.type ===
539
+ "session.error"
540
+ ) {
541
+ await queueSync(
542
+ sessionId,
543
+ "--error",
544
+ "session.error"
545
+ )
546
+
547
+ return
548
+ }
549
+ },
550
+
551
+ /*
552
+ * VERIFIED on OpenCode 1.18.14:
553
+ * this hook fires on real model turns.
554
+ *
555
+ * It remains experimental, therefore
556
+ * AGENTS.md + MCP stay as fallbacks.
557
+ */
558
+ "experimental.chat.system.transform":
559
+ async (
560
+ _input,
561
+ output
562
+ ) => {
563
+ const context =
564
+ await readContext()
565
+
566
+ if (!context) {
567
+ return
568
+ }
569
+
570
+ if (
571
+ Array.isArray(
572
+ output?.system
573
+ )
574
+ ) {
575
+ output.system.push(
576
+ context
577
+ )
578
+
579
+ return
580
+ }
581
+
582
+ if (
583
+ typeof output?.system ===
584
+ "string"
585
+ ) {
586
+ output.system =
587
+ output.system
588
+ ? \`\${output.system}\\n\\n\${context}\`
589
+ : context
590
+ }
591
+ },
592
+
593
+ /*
594
+ * Optional compaction survival hook.
595
+ * We keep it as a supplementary path,
596
+ * never as the only continuity path.
597
+ */
598
+ "experimental.session.compacting":
599
+ async (
600
+ _input,
601
+ output
602
+ ) => {
603
+ const context =
604
+ await readContext()
605
+
606
+ if (
607
+ context &&
608
+ Array.isArray(
609
+ output?.context
610
+ )
611
+ ) {
612
+ output.context.push(
613
+ context
614
+ )
615
+ }
616
+ },
617
+
618
+ /*
619
+ * VERIFIED for clean OpenCode exits.
620
+ * Best effort only: kill -9 / crash /
621
+ * machine loss cannot guarantee this.
622
+ */
623
+ dispose: async () => {
624
+ clearInterval(
625
+ periodic
170
626
  )
171
- }
172
- },
627
+
628
+ if (
629
+ lastSessionId
630
+ ) {
631
+ try {
632
+ await queueSync(
633
+ lastSessionId,
634
+ "--idle",
635
+ "dispose"
636
+ )
637
+ } catch {
638
+ // Status already recorded.
639
+ }
640
+ }
641
+
642
+ try {
643
+ await syncChain
644
+ } catch {
645
+ // Do not block OpenCode shutdown.
646
+ }
647
+ },
648
+ }
173
649
  }
174
- }
175
- `;return D(o,n,{encoding:"utf8",mode:384}),o}import{existsSync as ie,mkdirSync as k,readFileSync as se,writeFileSync as b}from"node:fs";import{homedir as H}from"node:os";import{dirname as P,join as T}from"node:path";function ce(e){let t=[],o=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,r;for(;r=o.exec(e);){let n=r[1]??r[2]??"";try{t.push(r[1]!==void 0?JSON.parse(`"${n}"`):n)}catch{t.push(n)}}return t}function L(e={}){let t=e.configFile??T(process.env.CODEX_HOME??T(H(),".codex"),"config.toml"),o=e.previousFile??T(H(),".config","toolnet-memory","codex-notify-previous.json");k(P(t),{recursive:!0}),k(P(o),{recursive:!0});let r=ie(t)?se(t,"utf8"):"",n=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(n)}, "session:codex-notify"]`,i=r.split(`
176
- `),a=i.findIndex(l=>/^\s*\[/.test(l));a<0&&(a=i.length);let f=-1,c=-1;for(let l=0;l<a;l+=1)if(/^\s*notify\s*=/.test(i[l])){if(f=l,c=l,i[l].includes("[")&&!i[l].includes("]"))for(;c+1<a&&(c+=1,!i[c].includes("]")););break}let g=[];if(f>=0){let l=i.slice(f,c+1).join(`
177
- `);g=ce(l),i.splice(f,c-f+1,s)}else a=i.findIndex(l=>/^\s*\[/.test(l)),a<0&&(a=i.length),i.splice(a,0,s);let E=g.length>=2&&g[g.length-1]==="session:codex-notify";return g.length>0&&!E&&b(o,JSON.stringify(g,null,2)+`
178
- `,{encoding:"utf8",mode:384}),r=i.join(`
179
- `),r.endsWith(`
180
- `)||(r+=`
181
- `),b(t,r,{encoding:"utf8",mode:384}),{configFile:t,previousFile:o,preservedPrevious:g.length>0&&!E}}import{existsSync as ae,mkdirSync as le,readFileSync as ge,writeFileSync as de}from"node:fs";import{homedir as ue}from"node:os";import{dirname as fe,join as U}from"node:path";function Ee(e){return`'${e.replace(/'/g,"'\\''")}'`}function v(e={}){let t=e.hooksFile??U(process.env.CODEX_HOME??U(ue(),".codex"),"hooks.json");le(fe(t),{recursive:!0});let o={};if(ae(t))try{o=JSON.parse(ge(t,"utf8"))}catch(a){throw new Error(`Invalid existing Codex hooks.json: ${a instanceof Error?a.message:String(a)}`)}let r=o.hooks&&typeof o.hooks=="object"&&!Array.isArray(o.hooks)?o.hooks:{};o.hooks=r;let s=(Array.isArray(r.SessionStart)?r.SessionStart:[]).filter(a=>{try{return!JSON.stringify(a).includes("session:codex-context")}catch{return!0}}),i=e.binary??"toolnet-memory";return s.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${Ee(i)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),r.SessionStart=s,de(t,JSON.stringify(o,null,2)+`
182
- `,{encoding:"utf8",mode:384}),t}function h(e){return me("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function _e(){return h("agy")||A(N(R(),".gemini"))}function Se(){return h("opencode")||A(N(R(),".config","opencode"))}function pe(){return h("codex")||A(process.env.CODEX_HOME??N(R(),".codex"))}function I(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=[];if(!(e.force===!0||_e()))o.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let n=Y({binary:t});o.push({agent:"agy",detected:!0,installed:!0,targets:[n]})}catch(n){o.push({agent:"agy",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(e.force===!0||Se()))o.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let n=w({binary:t});o.push({agent:"opencode",detected:!0,installed:!0,targets:[n]})}catch(n){o.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}if(!(e.force===!0||pe()))o.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let n=L({binary:t}),s=v({binary:t}),i=[n.configFile,s];n.preservedPrevious&&i.push(n.previousFile),o.push({agent:"codex",detected:!0,installed:!0,targets:i})}catch(n){o.push({agent:"codex",detected:!0,installed:!1,targets:[],error:n instanceof Error?n.message:String(n)})}return o}function ye(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let t of e){let o=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":"Codex";if(!t.detected){console.log(`- ${o}: not detected`);continue}if(t.installed){console.log(`\u2713 ${o}: automatic memory enabled`);continue}console.log(`\u2717 ${o}: integration failed`),t.error&&console.log(` ${t.error}`)}console.log("")}async function Ce(){let e=process.argv.slice(2),t=e.includes("--all"),o=e.includes("--json"),r=I({force:t});if(o){console.log(JSON.stringify(r,null,2));return}ye(r)}var Oe=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Oe&&Ce().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});var x=q.join(Te.homedir(),".config","toolnet-memory"),_=q.join(x,".env");function Re(e){let t=new Map;for(let o of e.split(/\r?\n/)){let r=o.trim();if(!r||r.startsWith("#"))continue;let n=r.indexOf("=");n!==-1&&t.set(r.slice(0,n).trim(),r.slice(n+1).trim())}return t}function J(e){let t=e.get("MEMORY_STORAGE_PROVIDER")?.trim();return t==="r2"||t==="s3"||t==="local"||t==="huggingface"?t:e.get("R2_ACCOUNT_ID")&&e.get("R2_BUCKET")?"r2":e.get("S3_BUCKET")?"s3":e.get("HF_NAMESPACE")&&e.get("HF_BUCKET")?"huggingface":"r2"}function B(e){return e==="r2"?["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"]:e==="s3"?["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"]:e==="huggingface"?["HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"]:[]}function y(e){return e==="r2"?"Cloudflare R2":e==="s3"?"S3-compatible storage":e==="huggingface"?"Hugging Face S3 (legacy)":"Local storage"}function j(e){m.mkdirSync(x,{recursive:!0,mode:448});let o=`# ==========================================================
183
- # TOOLNET MEMORY
184
- # ==========================================================
185
-
186
- MEMORY_STORAGE_PROVIDER=${J(e)}
187
-
188
- # Cloudflare R2
189
- R2_ACCOUNT_ID=${e.get("R2_ACCOUNT_ID")??""}
190
- R2_BUCKET=${e.get("R2_BUCKET")??"toolnet-memory"}
191
- R2_ACCESS_KEY_ID=${e.get("R2_ACCESS_KEY_ID")??""}
192
- R2_SECRET_ACCESS_KEY=${e.get("R2_SECRET_ACCESS_KEY")??""}
193
-
194
- # Generic S3 / S3-compatible storage
195
- # Leave S3_ENDPOINT empty for AWS S3.
196
- S3_ENDPOINT=${e.get("S3_ENDPOINT")??""}
197
- S3_REGION=${e.get("S3_REGION")??"us-east-1"}
198
- S3_BUCKET=${e.get("S3_BUCKET")??"toolnet-memory"}
199
- S3_ACCESS_KEY_ID=${e.get("S3_ACCESS_KEY_ID")??""}
200
- S3_SECRET_ACCESS_KEY=${e.get("S3_SECRET_ACCESS_KEY")??""}
201
- S3_FORCE_PATH_STYLE=${e.get("S3_FORCE_PATH_STYLE")??"false"}
202
-
203
- # Hugging Face S3 (legacy compatibility)
204
- HF_NAMESPACE=${e.get("HF_NAMESPACE")??""}
205
- HF_BUCKET=${e.get("HF_BUCKET")??"toolnet-memory"}
206
- HF_S3_ACCESS_KEY_ID=${e.get("HF_S3_ACCESS_KEY_ID")??""}
207
- HF_S3_SECRET_ACCESS_KEY=${e.get("HF_S3_SECRET_ACCESS_KEY")??""}
208
-
209
- # Embedding
210
- HF_TOKEN=${e.get("HF_TOKEN")??""}
211
- HF_EMBEDDING_MODEL=${e.get("HF_EMBEDDING_MODEL")??"sentence-transformers/all-MiniLM-L6-v2"}
212
-
213
- # Local cache
214
- MEMORY_LOCAL_STORAGE_PATH=${e.get("MEMORY_LOCAL_STORAGE_PATH")??""}
215
- MEMORY_LOCAL_CACHE_MB=${e.get("MEMORY_LOCAL_CACHE_MB")??"200"}
216
-
217
- # Automation
218
- MEMORY_AUTO_CAPTURE=${e.get("MEMORY_AUTO_CAPTURE")??"true"}
219
- MEMORY_AUTO_RETRIEVE=${e.get("MEMORY_AUTO_RETRIEVE")??"true"}
220
- MEMORY_AUTO_SUMMARIZE=${e.get("MEMORY_AUTO_SUMMARIZE")??"true"}
221
- MEMORY_AUTO_SYNC=${e.get("MEMORY_AUTO_SYNC")??"true"}
222
-
223
- # Retrieval
224
- MEMORY_MAX_CANDIDATES=${e.get("MEMORY_MAX_CANDIDATES")??"50"}
225
- MEMORY_RERANK_TOP=${e.get("MEMORY_RERANK_TOP")??"10"}
226
- MEMORY_FINAL_CONTEXT=${e.get("MEMORY_FINAL_CONTEXT")??"5"}
227
- MEMORY_TOKEN_BUDGET=${e.get("MEMORY_TOKEN_BUDGET")??"2000"}
228
-
229
- # Automatic Session Memory
230
- TOOLNET_SESSION_LEARNING=${e.get("TOOLNET_SESSION_LEARNING")??"1"}
231
- TOOLNET_WORK_CONTINUITY=${e.get("TOOLNET_WORK_CONTINUITY")??"1"}
232
- TOOLNET_SEMANTIC_CONTINUITY=${e.get("TOOLNET_SEMANTIC_CONTINUITY")??"1"}
233
- TOOLNET_SMART_HANDOFF=${e.get("TOOLNET_SMART_HANDOFF")??"1"}
234
- `;m.writeFileSync(_,o,{encoding:"utf8",mode:384}),m.chmodSync(x,448),m.chmodSync(_,384)}function G(e,t=!0){let o=e.trim().toLowerCase();return o?o==="y"||o==="yes":t}async function Ne(e){return d.isTTY?(S.write(e),new Promise(t=>{let o="",r=()=>{d.off("data",n),d.setRawMode?.(!1),d.pause(),S.write(`
235
- `),t(o)},n=s=>{for(let i of s.toString("utf8")){if(i==="\r"||i===`
236
- `){r();return}if(i===""&&(d.setRawMode?.(!1),S.write(`
237
- `),process.exit(130)),i==="\x7F"){o=o.slice(0,-1);continue}o+=i}};d.resume(),d.setRawMode?.(!0),d.on("data",n)})):""}function M(){try{let t=I().filter(o=>o.installed);if(t.length>0){console.log(""),console.log("Automatic AI memory:");for(let o of t){let r=o.agent==="agy"?"Agy / Antigravity":o.agent==="opencode"?"OpenCode":"Codex";console.log(` \u2713 ${r}`)}}}catch{}}function u(e,t,o,r){o.trim()?e.set(t,o.trim()):!e.get(t)&&r!==void 0&&e.set(t,r)}async function he(e,t){console.log("Storage provider:"),console.log(" 1. Cloudflare R2 (recommended)"),console.log(" 2. S3 / S3-compatible"),console.log(" 3. Local"),console.log(" 4. Hugging Face S3 (legacy)"),console.log("");let o=t==="s3"?"2":t==="local"?"3":t==="huggingface"?"4":"1",n=(await e.question(`Choose [${o}]: `)).trim()||o;return n==="2"?"s3":n==="3"?"local":n==="4"?"huggingface":"r2"}async function Ie(){let e=m.existsSync(_),t=e?Re(m.readFileSync(_,"utf8")):new Map,o=J(t),r=B(o),n=r.every(c=>!!t.get(c)?.trim());if(console.log(""),console.log("TOOLNET MEMORY SETUP"),console.log("===================="),console.log(""),console.log(`Config: ${_}`),console.log(""),!d.isTTY||!S.isTTY){e||(t.set("MEMORY_STORAGE_PROVIDER",o),j(t)),n?console.log(`\u2713 ${y(o)} already configured`):(console.log("Configuration pending."),console.log("Run:"),console.log(" toolnet-memory setup")),M();return}let s=Ae.createInterface({input:d,output:S});if(n){console.log(`\u2713 ${y(o)} already configured`),console.log("");let c=await s.question("Use existing storage configuration? (Y/n) [Y]: ");if(G(c)){s.close(),console.log(""),console.log(`\u2713 Existing ${y(o)} configuration kept`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor");return}console.log("")}o=await he(s,o),t.set("MEMORY_STORAGE_PROVIDER",o),console.log("");let i="",a="";if(o==="r2"){let c=await s.question(t.get("R2_ACCOUNT_ID")?`Cloudflare Account ID [${t.get("R2_ACCOUNT_ID")}]: `:"Cloudflare Account ID: "),g=await s.question(`R2 Bucket [${t.get("R2_BUCKET")||"toolnet-memory"}]: `),E=await s.question(t.get("R2_ACCESS_KEY_ID")?"R2 Access Key ID [configured]: ":"R2 Access Key ID: ");u(t,"R2_ACCOUNT_ID",c),u(t,"R2_BUCKET",g,"toolnet-memory"),u(t,"R2_ACCESS_KEY_ID",E),i="R2_SECRET_ACCESS_KEY",a=t.get(i)?"R2 Secret Access Key [configured]: ":"R2 Secret Access Key: "}else if(o==="s3"){let c=await s.question(t.get("S3_ENDPOINT")?`S3 Endpoint [${t.get("S3_ENDPOINT")}]: `:"S3 Endpoint [blank = AWS S3]: "),g=await s.question(`S3 Region [${t.get("S3_REGION")||"us-east-1"}]: `),E=await s.question(`S3 Bucket [${t.get("S3_BUCKET")||"toolnet-memory"}]: `),l=await s.question(t.get("S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: "),F=await s.question(`Force path-style? (y/N) [${t.get("S3_FORCE_PATH_STYLE")==="true"?"Y":"N"}]: `);c.trim()&&t.set("S3_ENDPOINT",c.trim()),u(t,"S3_REGION",g,"us-east-1"),u(t,"S3_BUCKET",E,"toolnet-memory"),u(t,"S3_ACCESS_KEY_ID",l),F.trim()?t.set("S3_FORCE_PATH_STYLE",G(F,!1)?"true":"false"):t.get("S3_FORCE_PATH_STYLE")||t.set("S3_FORCE_PATH_STYLE","false"),i="S3_SECRET_ACCESS_KEY",a=t.get(i)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}else if(o==="huggingface"){let c=await s.question(t.get("HF_NAMESPACE")?`Hugging Face namespace [${t.get("HF_NAMESPACE")}]: `:"Hugging Face namespace: "),g=await s.question(`Bucket [${t.get("HF_BUCKET")||"toolnet-memory"}]: `),E=await s.question(t.get("HF_S3_ACCESS_KEY_ID")?"S3 Access Key ID [configured]: ":"S3 Access Key ID: ");u(t,"HF_NAMESPACE",c),u(t,"HF_BUCKET",g,"toolnet-memory"),u(t,"HF_S3_ACCESS_KEY_ID",E),i="HF_S3_SECRET_ACCESS_KEY",a=t.get(i)?"S3 Secret Access Key [configured]: ":"S3 Secret Access Key: "}if(s.close(),i){let c=await Ne(a);c.trim()&&t.set(i,c.trim())}j(t),r=B(o);let f=r.filter(c=>!t.get(c)?.trim());if(console.log(""),console.log("\u2713 Configuration saved"),console.log(` ${_}`),console.log(""),f.length){console.log("Missing configuration:");for(let c of f)console.log(` - ${c}`);console.log(""),console.log("Run setup again:"),console.log(" toolnet-memory setup");return}console.log(`\u2713 ${y(o)} configuration complete`),console.log(""),M(),console.log("Next:"),console.log(" toolnet-memory doctor")}Ie().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});
650
+ `;return ne(o,r.trimStart(),{encoding:"utf8",mode:384}),o}import{existsSync as be,mkdirSync as ie,readFileSync as Ye,writeFileSync as se}from"node:fs";import{homedir as ae}from"node:os";import{dirname as ce,join as x}from"node:path";function we(e){let t=[],o=/"((?:\\.|[^"\\])*)"|'([^']*)'/g,n;for(;n=o.exec(e);){let r=n[1]??n[2]??"";try{t.push(n[1]!==void 0?JSON.parse(`"${r}"`):r)}catch{t.push(r)}}return t}function le(e={}){let t=e.configFile??x(process.env.CODEX_HOME??x(ae(),".codex"),"config.toml"),o=e.previousFile??x(ae(),".config","toolnet-memory","codex-notify-previous.json");ie(ce(t),{recursive:!0}),ie(ce(o),{recursive:!0});let n=be(t)?Ye(t,"utf8"):"",r=e.binary??"toolnet-memory",s=`notify = [${JSON.stringify(r)}, "session:codex-notify"]`,a=n.split(`
651
+ `),l=a.findIndex(u=>/^\s*\[/.test(u));l<0&&(l=a.length);let c=-1,d=-1;for(let u=0;u<l;u+=1)if(/^\s*notify\s*=/.test(a[u])){if(c=u,d=u,a[u].includes("[")&&!a[u].includes("]"))for(;d+1<l&&(d+=1,!a[d].includes("]")););break}let E=[];if(c>=0){let u=a.slice(c,d+1).join(`
652
+ `);E=we(u),a.splice(c,d-c+1,s)}else l=a.findIndex(u=>/^\s*\[/.test(u)),l<0&&(l=a.length),a.splice(l,0,s);let y=E.length>=2&&E[E.length-1]==="session:codex-notify";return E.length>0&&!y&&se(o,JSON.stringify(E,null,2)+`
653
+ `,{encoding:"utf8",mode:384}),n=a.join(`
654
+ `),n.endsWith(`
655
+ `)||(n+=`
656
+ `),se(t,n,{encoding:"utf8",mode:384}),{configFile:t,previousFile:o,preservedPrevious:E.length>0&&!y}}import{existsSync as ke,mkdirSync as Be,readFileSync as xe,writeFileSync as $e}from"node:fs";import{homedir as Fe}from"node:os";import{dirname as ve,join as de}from"node:path";function He(e){return`'${e.replace(/'/g,"'\\''")}'`}function ge(e={}){let t=e.hooksFile??de(process.env.CODEX_HOME??de(Fe(),".codex"),"hooks.json");Be(ve(t),{recursive:!0});let o={};if(ke(t))try{o=JSON.parse(xe(t,"utf8"))}catch(l){throw new Error(`Invalid existing Codex hooks.json: ${l instanceof Error?l.message:String(l)}`)}let n=o.hooks&&typeof o.hooks=="object"&&!Array.isArray(o.hooks)?o.hooks:{};o.hooks=n;let s=(Array.isArray(n.SessionStart)?n.SessionStart:[]).filter(l=>{try{return!JSON.stringify(l).includes("session:codex-context")}catch{return!0}}),a=e.binary??"toolnet-memory";return s.push({matcher:"startup|resume|clear|compact",hooks:[{type:"command",command:`${He(a)} session:codex-context`,timeout:15,additionalContextLimit:1e3,statusMessage:"Loading ToolNet project continuity"}]}),n.SessionStart=s,$e(t,JSON.stringify(o,null,2)+`
657
+ `,{encoding:"utf8",mode:384}),t}function H(e){return Ge("sh",["-lc",`command -v ${JSON.stringify(e)} >/dev/null 2>&1`],{stdio:"ignore"}).status===0}function qe(){return H("agy")||$(v(F(),".gemini"))}function je(){return H("opencode")||$(v(F(),".config","opencode"))}function Ve(){return H("codex")||$(process.env.CODEX_HOME??v(F(),".codex"))}function G(e={}){let t=e.binary??process.env.TOOLNET_MEMORY_BIN??"toolnet-memory",o=[];if(!(e.force===!0||qe()))o.push({agent:"agy",detected:!1,installed:!1,targets:[]});else try{let r=ee({binary:t});o.push({agent:"agy",detected:!0,installed:!0,targets:[r]})}catch(r){o.push({agent:"agy",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||je()))o.push({agent:"opencode",detected:!1,installed:!1,targets:[]});else try{let r=re({binary:t});o.push({agent:"opencode",detected:!0,installed:!0,targets:[r]})}catch(r){o.push({agent:"opencode",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}if(!(e.force===!0||Ve()))o.push({agent:"codex",detected:!1,installed:!1,targets:[]});else try{let r=le({binary:t}),s=ge({binary:t}),a=[r.configFile,s];r.preservedPrevious&&a.push(r.previousFile),o.push({agent:"codex",detected:!0,installed:!0,targets:a})}catch(r){o.push({agent:"codex",detected:!0,installed:!1,targets:[],error:r instanceof Error?r.message:String(r)})}return o}function Je(e){console.log(""),console.log("ToolNet Memory AI Integrations"),console.log("=============================="),console.log("");for(let t of e){let o=t.agent==="agy"?"Agy / Antigravity":t.agent==="opencode"?"OpenCode":"Codex";if(!t.detected){console.log(`- ${o}: not detected`);continue}if(t.installed){console.log(`\u2713 ${o}: automatic memory enabled`);continue}console.log(`\u2717 ${o}: integration failed`),t.error&&console.log(` ${t.error}`)}console.log("")}async function We(){let e=process.argv.slice(2),t=e.includes("--all"),o=e.includes("--json"),n=G({force:t});if(o){console.log(JSON.stringify(n,null,2));return}Je(n)}var Xe=process.argv[1]&&(process.argv[1].endsWith("auto-integrate.js")||process.argv[1].endsWith("auto-integrate.ts"));Xe&&We().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exitCode=1});var q=R.join(j.homedir(),".config","toolnet-memory"),_=R.join(q,".env"),et=new Set(["MEMORY_STORAGE_PROVIDER","R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY","S3_ENDPOINT","S3_REGION","S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY","S3_FORCE_PATH_STYLE","HF_NAMESPACE","HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY","HF_URL","HF_TOKEN","HF_EMBEDDING_MODEL","TOOLNET_LLM_PROVIDER","TOOLNET_LLM_API_KEY","TOOLNET_LLM_BASE_URL","TOOLNET_LLM_MODEL","TOOLNET_LLM_ACCOUNT_ID","TOOLNET_EMBEDDING_PROVIDER","TOOLNET_EMBEDDING_API_KEY","TOOLNET_EMBEDDING_BASE_URL","TOOLNET_EMBEDDING_MODEL","TOOLNET_EMBEDDING_ACCOUNT_ID","MEMORY_LOCAL_STORAGE_PATH","MEMORY_LOCAL_CACHE_MB","MEMORY_AUTO_CAPTURE","MEMORY_AUTO_RETRIEVE","MEMORY_AUTO_SUMMARIZE","MEMORY_AUTO_SYNC","MEMORY_MAX_CANDIDATES","MEMORY_RERANK_TOP","MEMORY_FINAL_CONTEXT","MEMORY_TOKEN_BUDGET","TOOLNET_SESSION_LEARNING","TOOLNET_WORK_CONTINUITY","TOOLNET_SEMANTIC_CONTINUITY","TOOLNET_SMART_HANDOFF"]);function tt(e){let t=new Map;for(let o of e.split(/\r?\n/)){let n=o.trim();if(!n||n.startsWith("#"))continue;let r=n.indexOf("=");if(r===-1)continue;let s=n.slice(0,r).trim(),a=n.slice(r+1).trim();s&&t.set(s,a)}return t}function T(e){let t=e.get("MEMORY_STORAGE_PROVIDER")?.trim();return t==="r2"||t==="s3"||t==="local"||t==="huggingface"?t:e.get("R2_ACCOUNT_ID")&&e.get("R2_BUCKET")?"r2":e.get("S3_BUCKET")?"s3":e.get("HF_BUCKET")&&e.get("HF_S3_ACCESS_KEY_ID")?"huggingface":e.get("MEMORY_LOCAL_STORAGE_PATH")?"local":"r2"}function M(e){switch(e){case"r2":return"Cloudflare R2";case"s3":return"S3 / S3-compatible";case"huggingface":return"Hugging Face S3";case"local":return"Local";default:return e}}function Ee(e){switch(e){case"r2":return["R2_ACCOUNT_ID","R2_BUCKET","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY"];case"s3":return["S3_BUCKET","S3_ACCESS_KEY_ID","S3_SECRET_ACCESS_KEY"];case"huggingface":return["HF_BUCKET","HF_S3_ACCESS_KEY_ID","HF_S3_SECRET_ACCESS_KEY"];case"local":return[];default:return[]}}function ue(e){let t=T(e);return t==="local"?!0:Ee(t).every(o=>!!e.get(o)?.trim())}function V(e){return e?e.length<=8?"configured":`${e.slice(0,4)}\u2022\u2022\u2022\u2022${e.slice(-3)}`:"not configured"}function ot(e){let o=["# ==========================================================","# TOOLNET MEMORY","# Generated by: toolnet-memory setup","# Do not commit this file.","# ==========================================================","",`MEMORY_STORAGE_PROVIDER=${T(e)}`,"","# ----------------------------------------------------------","# Cloudflare R2","# ----------------------------------------------------------",`R2_ACCOUNT_ID=${e.get("R2_ACCOUNT_ID")??""}`,`R2_BUCKET=${e.get("R2_BUCKET")??"toolnet-memory"}`,`R2_ACCESS_KEY_ID=${e.get("R2_ACCESS_KEY_ID")??""}`,`R2_SECRET_ACCESS_KEY=${e.get("R2_SECRET_ACCESS_KEY")??""}`,"","# ----------------------------------------------------------","# Generic S3 / S3-compatible","# ----------------------------------------------------------",`S3_ENDPOINT=${e.get("S3_ENDPOINT")??""}`,`S3_REGION=${e.get("S3_REGION")??"us-east-1"}`,`S3_BUCKET=${e.get("S3_BUCKET")??"toolnet-memory"}`,`S3_ACCESS_KEY_ID=${e.get("S3_ACCESS_KEY_ID")??""}`,`S3_SECRET_ACCESS_KEY=${e.get("S3_SECRET_ACCESS_KEY")??""}`,`S3_FORCE_PATH_STYLE=${e.get("S3_FORCE_PATH_STYLE")??"false"}`,"","# ----------------------------------------------------------","# Hugging Face S3","# ----------------------------------------------------------",`HF_NAMESPACE=${e.get("HF_NAMESPACE")??""}`,`HF_BUCKET=${e.get("HF_BUCKET")??"toolnet-memory"}`,`HF_S3_ACCESS_KEY_ID=${e.get("HF_S3_ACCESS_KEY_ID")??""}`,`HF_S3_SECRET_ACCESS_KEY=${e.get("HF_S3_SECRET_ACCESS_KEY")??""}`,`HF_URL=${e.get("HF_URL")??""}`,"","# ----------------------------------------------------------","# AI / LLM - canonical ToolNet configuration","# ----------------------------------------------------------",`TOOLNET_LLM_PROVIDER=${e.get("TOOLNET_LLM_PROVIDER")??""}`,`TOOLNET_LLM_API_KEY=${e.get("TOOLNET_LLM_API_KEY")??""}`,`TOOLNET_LLM_BASE_URL=${e.get("TOOLNET_LLM_BASE_URL")??""}`,`TOOLNET_LLM_MODEL=${e.get("TOOLNET_LLM_MODEL")??""}`,`TOOLNET_LLM_ACCOUNT_ID=${e.get("TOOLNET_LLM_ACCOUNT_ID")??""}`,"","# ----------------------------------------------------------","# Embedding - canonical ToolNet configuration","# ----------------------------------------------------------",`TOOLNET_EMBEDDING_PROVIDER=${e.get("TOOLNET_EMBEDDING_PROVIDER")??""}`,`TOOLNET_EMBEDDING_API_KEY=${e.get("TOOLNET_EMBEDDING_API_KEY")??""}`,`TOOLNET_EMBEDDING_BASE_URL=${e.get("TOOLNET_EMBEDDING_BASE_URL")??""}`,`TOOLNET_EMBEDDING_MODEL=${e.get("TOOLNET_EMBEDDING_MODEL")??""}`,`TOOLNET_EMBEDDING_ACCOUNT_ID=${e.get("TOOLNET_EMBEDDING_ACCOUNT_ID")??""}`,"","# ----------------------------------------------------------","# Embedding - legacy/current compatibility","# ----------------------------------------------------------",`HF_TOKEN=${e.get("HF_TOKEN")??""}`,`HF_EMBEDDING_MODEL=${e.get("HF_EMBEDDING_MODEL")??"sentence-transformers/all-MiniLM-L6-v2"}`,"","# ----------------------------------------------------------","# Local storage/cache","# ----------------------------------------------------------",`MEMORY_LOCAL_STORAGE_PATH=${e.get("MEMORY_LOCAL_STORAGE_PATH")??""}`,`MEMORY_LOCAL_CACHE_MB=${e.get("MEMORY_LOCAL_CACHE_MB")??"200"}`,"","# ----------------------------------------------------------","# Automation","# ----------------------------------------------------------",`MEMORY_AUTO_CAPTURE=${e.get("MEMORY_AUTO_CAPTURE")??"true"}`,`MEMORY_AUTO_RETRIEVE=${e.get("MEMORY_AUTO_RETRIEVE")??"true"}`,`MEMORY_AUTO_SUMMARIZE=${e.get("MEMORY_AUTO_SUMMARIZE")??"true"}`,`MEMORY_AUTO_SYNC=${e.get("MEMORY_AUTO_SYNC")??"true"}`,"","# ----------------------------------------------------------","# Retrieval","# ----------------------------------------------------------",`MEMORY_MAX_CANDIDATES=${e.get("MEMORY_MAX_CANDIDATES")??"50"}`,`MEMORY_RERANK_TOP=${e.get("MEMORY_RERANK_TOP")??"10"}`,`MEMORY_FINAL_CONTEXT=${e.get("MEMORY_FINAL_CONTEXT")??"5"}`,`MEMORY_TOKEN_BUDGET=${e.get("MEMORY_TOKEN_BUDGET")??"2000"}`,"","# ----------------------------------------------------------","# Automatic Session Memory","# ----------------------------------------------------------",`TOOLNET_SESSION_LEARNING=${e.get("TOOLNET_SESSION_LEARNING")??"1"}`,`TOOLNET_WORK_CONTINUITY=${e.get("TOOLNET_WORK_CONTINUITY")??"1"}`,`TOOLNET_SEMANTIC_CONTINUITY=${e.get("TOOLNET_SEMANTIC_CONTINUITY")??"1"}`,`TOOLNET_SMART_HANDOFF=${e.get("TOOLNET_SMART_HANDOFF")??"1"}`],n=[...e.entries()].filter(([r])=>!et.has(r));if(n.length>0){o.push("","# ----------------------------------------------------------","# Preserved settings","# ----------------------------------------------------------");for(let[r,s]of n)o.push(`${r}=${s}`)}return`${o.join(`
658
+ `)}
659
+ `}function O(e){m.mkdirSync(q,{recursive:!0,mode:448});let t=`${_}.tmp-${process.pid}`;m.writeFileSync(t,ot(e),{encoding:"utf8",mode:384}),m.renameSync(t,_),m.chmodSync(q,448),m.chmodSync(_,384)}function p(e,t,o,n){let r=o.trim();if(r){e.set(t,r);return}!e.get(t)&&n!==void 0&&e.set(t,n)}async function Y(e,t){return f.isTTY?(e.pause(),I.write(t),new Promise(o=>{let n="",r=!1,s=()=>{r||(r=!0,f.off("data",a),f.setRawMode?.(!1),f.pause(),I.write(`
660
+ `),e.resume(),o(n))},a=l=>{for(let c of l.toString("utf8")){if(c==="\r"||c===`
661
+ `){s();return}if(c===""&&(f.off("data",a),f.setRawMode?.(!1),I.write(`
662
+ `),process.exit(130)),c==="\x7F"){n=n.slice(0,-1);continue}n+=c}};f.resume(),f.setRawMode?.(!0),f.on("data",a)})):""}function L(e){return e.trim().replace(/\/+$/,"")}function _e(e){return`https://${e}.r2.cloudflarestorage.com`}async function J(e){let t=new AbortController,o=setTimeout(()=>t.abort(),15e3);o.unref?.();let n=new Qe({region:e.region,endpoint:e.endpoint||void 0,forcePathStyle:e.forcePathStyle??!1,credentials:{accessKeyId:e.accessKeyId,secretAccessKey:e.secretAccessKey}});try{return await n.send(new Ze({Bucket:e.bucket}),{abortSignal:t.signal}),{ok:!0,message:`Bucket "${e.bucket}" reachable`}}catch(r){return{ok:!1,message:r instanceof Error?r.message:String(r)}}finally{clearTimeout(o),n.destroy()}}async function nt(e){let t=e.get("R2_ACCOUNT_ID")?.trim()??"",o=e.get("R2_BUCKET")?.trim()??"",n=e.get("R2_ACCESS_KEY_ID")?.trim()??"",r=e.get("R2_SECRET_ACCESS_KEY")?.trim()??"";return!t||!o||!n||!r?{ok:!1,message:"R2 configuration is incomplete"}:J({endpoint:_e(t),region:"auto",bucket:o,accessKeyId:n,secretAccessKey:r})}async function rt(e){let t=L(e.get("S3_ENDPOINT")??""),o=e.get("S3_REGION")?.trim()||"us-east-1",n=e.get("S3_BUCKET")?.trim()??"",r=e.get("S3_ACCESS_KEY_ID")?.trim()??"",s=e.get("S3_SECRET_ACCESS_KEY")?.trim()??"";return!n||!r||!s?{ok:!1,message:"S3 configuration is incomplete"}:J({endpoint:t||void 0,region:o,bucket:n,accessKeyId:r,secretAccessKey:s,forcePathStyle:e.get("S3_FORCE_PATH_STYLE")==="true"})}async function it(e){let t=L(e.get("HF_URL")??""),o=e.get("HF_BUCKET")?.trim()??"",n=e.get("HF_S3_ACCESS_KEY_ID")?.trim()??"",r=e.get("HF_S3_SECRET_ACCESS_KEY")?.trim()??"";return t?!o||!n||!r?{ok:!1,message:"Hugging Face S3 configuration is incomplete"}:J({endpoint:t,region:"us-east-1",bucket:o,accessKeyId:n,secretAccessKey:r,forcePathStyle:!0}):{ok:!1,message:"HF_URL / S3 endpoint is required for connection test"}}async function st(e){let t=e.get("MEMORY_LOCAL_STORAGE_PATH")?.trim()||R.join(j.homedir(),".local","share","toolnet-memory"),o=R.join(t,`.toolnet-test-${process.pid}-${Date.now()}`);try{return m.mkdirSync(t,{recursive:!0,mode:448}),m.writeFileSync(o,`toolnet-memory
663
+ `,{encoding:"utf8",mode:384}),m.unlinkSync(o),{ok:!0,message:`Writable: ${t}`}}catch(n){return{ok:!1,message:n instanceof Error?n.message:String(n)}}}async function at(e,t){switch(e){case"r2":return nt(t);case"s3":return rt(t);case"huggingface":return it(t);case"local":return st(t)}}async function ct(e,t){console.log(""),console.log(`Testing ${M(e)}...`);let o=await at(e,t);return o.ok?console.log(`\u2713 ${o.message}`):(console.log("\u2717 Connection test failed"),console.log(` ${o.message}`)),console.log(""),o}function lt(){console.log(""),console.log("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.log("\u2551 TOOLNET MEMORY SETUP \u2551"),console.log("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),console.log("")}function dt(e){let t=T(e);console.log("Current configuration"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(`Storage : ${M(t)} ${ue(e)?"\u2713":"\u26A0 incomplete"}`);let o=e.get("TOOLNET_LLM_PROVIDER"),n=e.get("TOOLNET_LLM_MODEL"),r=e.get("TOOLNET_EMBEDDING_PROVIDER");console.log(`LLM : ${o?`${fe(o)}${n?` / ${n}`:""}`:"not configured"}`),console.log(`Embedding: ${r||"legacy/default"}`),console.log("")}async function gt(e){console.log("Setup"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(" 1. Storage"),console.log(" 2. AI Model"),console.log(" 3. Finish & Save"),console.log(" 0. Exit without saving"),console.log("");let t=(await e.question("Choose [1-3]: ")).trim();return t==="1"?"storage":t==="2"?"ai":t==="0"?"exit":"finish"}async function Et(e,t){console.log(""),console.log("Storage"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(` 1. Cloudflare R2${t==="r2"?" \u2713 current":""}`),console.log(` 2. S3 / S3-compatible${t==="s3"?" \u2713 current":""}`),console.log(` 3. Hugging Face S3${t==="huggingface"?" \u2713 current":""}`),console.log(` 4. Local${t==="local"?" \u2713 current":""}`),console.log(""),console.log(" 0. Back"),console.log("");let o=(await e.question("Choose storage: ")).trim();return o==="0"?"back":o==="2"?"s3":o==="3"?"huggingface":o==="4"?"local":"r2"}async function ut(e,t){console.log(""),console.log("Cloudflare R2"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let o=await e.question(t.get("R2_ACCOUNT_ID")?`ACCOUNT ID [${t.get("R2_ACCOUNT_ID")}]: `:"ACCOUNT ID: "),n=await e.question(t.get("R2_ACCESS_KEY_ID")?`ACCESS KEY ID [${V(t.get("R2_ACCESS_KEY_ID"))}]: `:"ACCESS KEY ID: "),r=await e.question(`BUCKET [${t.get("R2_BUCKET")||"toolnet-memory"}]: `);p(t,"R2_ACCOUNT_ID",o),p(t,"R2_ACCESS_KEY_ID",n),p(t,"R2_BUCKET",r,"toolnet-memory");let s=t.get("R2_ACCOUNT_ID")?.trim();s&&console.log(`URL: ${_e(s)}`);let a=await Y(e,t.get("R2_SECRET_ACCESS_KEY")?"SECRET ACCESS KEY [configured, Enter = keep]: ":"SECRET ACCESS KEY: ");a.trim()&&t.set("R2_SECRET_ACCESS_KEY",a.trim())}async function _t(e,t){console.log(""),console.log("S3 / S3-compatible"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let o=await e.question(t.get("S3_ENDPOINT")?`URL / ENDPOINT [${t.get("S3_ENDPOINT")}]: `:"URL / ENDPOINT [blank = AWS S3]: "),n=await e.question(`REGION [${t.get("S3_REGION")||"us-east-1"}]: `),r=await e.question(t.get("S3_ACCESS_KEY_ID")?`ACCESS KEY ID [${V(t.get("S3_ACCESS_KEY_ID"))}]: `:"ACCESS KEY ID: "),s=await e.question(`BUCKET [${t.get("S3_BUCKET")||"toolnet-memory"}]: `),a=t.get("S3_FORCE_PATH_STYLE")==="true",l=await e.question(`FORCE PATH STYLE [${a?"Y":"N"}] (y/n): `);if(o.trim()&&t.set("S3_ENDPOINT",L(o)),p(t,"S3_REGION",n,"us-east-1"),p(t,"S3_ACCESS_KEY_ID",r),p(t,"S3_BUCKET",s,"toolnet-memory"),l.trim()){let d=l.trim().toLowerCase();t.set("S3_FORCE_PATH_STYLE",d==="y"||d==="yes"?"true":"false")}else t.has("S3_FORCE_PATH_STYLE")||t.set("S3_FORCE_PATH_STYLE","false");let c=await Y(e,t.get("S3_SECRET_ACCESS_KEY")?"SECRET ACCESS KEY [configured, Enter = keep]: ":"SECRET ACCESS KEY: ");c.trim()&&t.set("S3_SECRET_ACCESS_KEY",c.trim())}async function ft(e,t){console.log(""),console.log("Hugging Face S3"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let o=await e.question(t.get("HF_NAMESPACE")?`NAMESPACE [${t.get("HF_NAMESPACE")}]: `:"NAMESPACE [optional]: "),n=await e.question(t.get("HF_S3_ACCESS_KEY_ID")?`ACCESS KEY ID [${V(t.get("HF_S3_ACCESS_KEY_ID"))}]: `:"ACCESS KEY ID: "),r=await e.question(`BUCKET [${t.get("HF_BUCKET")||"toolnet-memory"}]: `),s=await e.question(t.get("HF_URL")?`URL [${t.get("HF_URL")}]: `:"URL / S3 ENDPOINT: ");p(t,"HF_NAMESPACE",o),p(t,"HF_S3_ACCESS_KEY_ID",n),p(t,"HF_BUCKET",r,"toolnet-memory"),s.trim()&&t.set("HF_URL",L(s));let a=await Y(e,t.get("HF_S3_SECRET_ACCESS_KEY")?"SECRET ACCESS KEY [configured, Enter = keep]: ":"SECRET ACCESS KEY: ");a.trim()&&t.set("HF_S3_SECRET_ACCESS_KEY",a.trim())}async function mt(e,t){console.log(""),console.log("Local Storage"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let o=t.get("MEMORY_LOCAL_STORAGE_PATH")??R.join(j.homedir(),".local","share","toolnet-memory"),n=await e.question(`LOCAL PATH [${o}]: `);p(t,"MEMORY_LOCAL_STORAGE_PATH",n,o)}async function pt(e,t){let o=new Map(t);for(;;){let n=await Et(e,T(t));if(n==="back")return;let r=new Map(t);t.set("MEMORY_STORAGE_PROVIDER",n),n==="r2"?await ut(e,t):n==="s3"?await _t(e,t):n==="huggingface"?await ft(e,t):await mt(e,t);let s=Ee(n).filter(c=>!t.get(c)?.trim());if(s.length){console.log(""),console.log("\u26A0 Missing required fields:");for(let d of s)console.log(` - ${d}`);console.log(""),console.log(" 1. Retry"),console.log(" 2. Save anyway"),console.log(" 3. Choose another provider"),console.log(" 4. Cancel"),console.log("");let c=(await e.question("Choose [1]: ")).trim()||"1";if(c==="2"){O(t),console.log(""),console.log("\u26A0 Saved with incomplete configuration"),console.log("");return}if(c==="3"){t.clear();for(let[d,E]of r)t.set(d,E);continue}if(c==="4"){t.clear();for(let[d,E]of o)t.set(d,E);console.log(""),console.log("Storage changes cancelled."),console.log("");return}continue}if((await ct(n,t)).ok){O(t),console.log(`\u2713 ${M(n)} configuration saved`),console.log(` ${_}`),console.log("");return}console.log(" 1. Retry"),console.log(" 2. Save anyway"),console.log(" 3. Choose another provider"),console.log(" 4. Cancel"),console.log("");let l=(await e.question("Choose [1]: ")).trim()||"1";if(l==="2"){O(t),console.log(""),console.log("\u26A0 Saved even though connection test failed"),console.log("");return}if(l==="3"){t.clear();for(let[c,d]of r)t.set(c,d);continue}if(l==="4"){t.clear();for(let[c,d]of o)t.set(c,d);console.log(""),console.log("Storage changes cancelled."),console.log("");return}}}var C=[{id:"openai-compatible",label:"OpenAI-compatible",apiKeyRequired:!0,baseUrlRequired:!0},{id:"alibaba",label:"Alibaba / DashScope",baseUrl:"https://dashscope-intl.aliyuncs.com/compatible-mode/v1",suggestedModel:"qwen3.6-flash",apiKeyRequired:!0},{id:"openrouter",label:"OpenRouter",baseUrl:"https://openrouter.ai/api/v1",apiKeyRequired:!0},{id:"groq",label:"Groq",baseUrl:"https://api.groq.com/openai/v1",apiKeyRequired:!0},{id:"deepseek",label:"DeepSeek",baseUrl:"https://api.deepseek.com",suggestedModel:"deepseek-v4-flash",apiKeyRequired:!0},{id:"nvidia",label:"NVIDIA NIM",baseUrl:"https://integrate.api.nvidia.com/v1",suggestedModel:"deepseek-ai/deepseek-v4-pro",apiKeyRequired:!0},{id:"gemini",label:"Gemini",baseUrl:"https://generativelanguage.googleapis.com/v1beta",suggestedModel:"gemini-3.6-flash",apiKeyRequired:!0},{id:"huggingface",label:"Hugging Face",baseUrl:"https://router.huggingface.co/v1",apiKeyRequired:!0},{id:"ollama",label:"Ollama / Local",baseUrl:"http://127.0.0.1:11434/v1",apiKeyRequired:!1},{id:"custom",label:"Custom endpoint",apiKeyRequired:!1,baseUrlRequired:!0},{id:"cloudflare",label:"Cloudflare Workers AI",suggestedModel:"@cf/meta/llama-3.1-8b-instruct",apiKeyRequired:!0,accountIdRequired:!0}];function Ot(e){return C.find(t=>t.id===e)}function fe(e){return Ot(e)?.label??e??"not configured"}function me(e){let t=e.get("TOOLNET_LLM_PROVIDER")?.trim();return C.some(o=>o.id===t)?t:void 0}async function pe(e,t){console.log(""),console.log("AI Model"),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),C.forEach((r,s)=>{console.log(` ${s+1}. ${r.label}${t===r.id?" \u2713 current":""}`)}),console.log(""),console.log(" 0. Back"),console.log("");let o=(await e.question("Choose provider: ")).trim();if(o==="0")return"back";let n=Number(o)-1;return Number.isInteger(n)&&n>=0&&n<C.length?C[n].id:(console.log(""),console.log("\u26A0 Invalid provider selection"),pe(e,t))}function Tt(e,t){return!(!e.get("TOOLNET_LLM_MODEL")?.trim()||t.apiKeyRequired&&!e.get("TOOLNET_LLM_API_KEY")?.trim()||t.accountIdRequired&&!e.get("TOOLNET_LLM_ACCOUNT_ID")?.trim()||t.baseUrlRequired&&!e.get("TOOLNET_LLM_BASE_URL")?.trim())}function At(e){e.delete("TOOLNET_LLM_API_KEY"),e.delete("TOOLNET_LLM_BASE_URL"),e.delete("TOOLNET_LLM_MODEL"),e.delete("TOOLNET_LLM_ACCOUNT_ID")}async function St(e,t,o){console.log(""),console.log(o.label),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");let n=t.get("TOOLNET_LLM_PROVIDER");if(n&&n!==o.id&&At(t),t.set("TOOLNET_LLM_PROVIDER",o.id),o.accountIdRequired){let c=t.get("TOOLNET_LLM_ACCOUNT_ID"),d=await e.question(c?`ACCOUNT ID [${c}]: `:"ACCOUNT ID: ");p(t,"TOOLNET_LLM_ACCOUNT_ID",d)}else t.delete("TOOLNET_LLM_ACCOUNT_ID");if(o.apiKeyRequired||o.id==="custom"){let c=t.get("TOOLNET_LLM_API_KEY"),d=await Y(e,c?"API KEY [configured, Enter = keep]: ":o.apiKeyRequired?"API KEY: ":"API KEY [optional]: ");d.trim()&&t.set("TOOLNET_LLM_API_KEY",d.trim())}else t.delete("TOOLNET_LLM_API_KEY");if(o.id!=="cloudflare"){let d=t.get("TOOLNET_LLM_BASE_URL")||o.baseUrl||"",y=(await e.question(d?`BASE URL [${d}]: `:o.baseUrlRequired?"BASE URL: ":"BASE URL [optional]: ")).trim()||d;y?t.set("TOOLNET_LLM_BASE_URL",L(y)):t.delete("TOOLNET_LLM_BASE_URL")}else t.delete("TOOLNET_LLM_BASE_URL");let s=t.get("TOOLNET_LLM_MODEL")||o.suggestedModel||"",l=(await e.question(s?`MODEL [${s}]: `:"MODEL: ")).trim()||s;l&&t.set("TOOLNET_LLM_MODEL",l)}function Ct(e){let t=me(e);if(!t)throw new Error("AI provider is not configured");return{id:t,apiKey:e.get("TOOLNET_LLM_API_KEY")?.trim()||void 0,baseUrl:e.get("TOOLNET_LLM_BASE_URL")?.trim()||void 0,model:e.get("TOOLNET_LLM_MODEL")?.trim()||void 0,accountId:e.get("TOOLNET_LLM_ACCOUNT_ID")?.trim()||void 0}}async function yt(e){let t=Ct(e);console.log(""),console.log(`Testing ${fe(t.id)}...`);try{let n=await Q(t).healthCheck();return n.ok?(console.log(`\u2713 Provider reachable${n.latencyMs?` (${n.latencyMs} ms)`:""}`),console.log(`\u2713 Model: ${n.model??t.model??"configured"}`),console.log(""),{ok:!0,message:n.message}):(console.log("\u2717 AI provider test failed"),console.log(` ${n.message}`),console.log(""),{ok:!1,message:n.message})}catch(o){let n=o instanceof Error?o.message:String(o);return console.log("\u2717 AI provider test failed"),console.log(` ${n}`),console.log(""),{ok:!1,message:n}}}async function It(e,t){let o=new Map(t);for(;;){let n=await pe(e,me(t));if(n==="back")return;let r=C.find(l=>l.id===n);if(!r)continue;if(await St(e,t,r),!Tt(t,r)){console.log(""),console.log("\u26A0 AI configuration is incomplete."),console.log(""),console.log(" 1. Retry"),console.log(" 2. Save anyway"),console.log(" 3. Choose another provider"),console.log(" 4. Cancel"),console.log("");let l=(await e.question("Choose [1]: ")).trim()||"1";if(l==="2"){O(t),console.log(""),console.log("\u26A0 AI configuration saved without validation"),console.log(` ${_}`),console.log("");return}if(l==="3")continue;if(l==="4"){t.clear();for(let[c,d]of o)t.set(c,d);console.log(""),console.log("AI changes cancelled."),console.log("");return}continue}if((await yt(t)).ok){O(t),console.log(`\u2713 ${r.label} configuration saved`),console.log(` ${_}`),console.log("");return}console.log(" 1. Retry"),console.log(" 2. Save anyway"),console.log(" 3. Choose another provider"),console.log(" 4. Cancel"),console.log("");let a=(await e.question("Choose [1]: ")).trim()||"1";if(a==="2"){O(t),console.log(""),console.log("\u26A0 AI configuration saved even though provider test failed"),console.log(` ${_}`),console.log("");return}if(a!=="3"&&a==="4"){t.clear();for(let[l,c]of o)t.set(l,c);console.log(""),console.log("AI changes cancelled."),console.log("");return}}}function Rt(){try{let t=G().filter(o=>o.installed);if(t.length===0)return;console.log(""),console.log("Automatic AI memory");for(let o of t){let n=o.agent==="agy"?"Agy / Antigravity":o.agent==="opencode"?"OpenCode":o.agent==="codex"?"Codex":o.agent;console.log(` \u2713 ${n}`)}}catch{}}function Oe(e){e.has("MEMORY_STORAGE_PROVIDER")||e.set("MEMORY_STORAGE_PROVIDER",T(e)),e.has("MEMORY_LOCAL_CACHE_MB")||e.set("MEMORY_LOCAL_CACHE_MB","200"),e.has("MEMORY_AUTO_CAPTURE")||e.set("MEMORY_AUTO_CAPTURE","true"),e.has("MEMORY_AUTO_RETRIEVE")||e.set("MEMORY_AUTO_RETRIEVE","true"),e.has("MEMORY_AUTO_SUMMARIZE")||e.set("MEMORY_AUTO_SUMMARIZE","true"),e.has("MEMORY_AUTO_SYNC")||e.set("MEMORY_AUTO_SYNC","true"),e.has("MEMORY_MAX_CANDIDATES")||e.set("MEMORY_MAX_CANDIDATES","50"),e.has("MEMORY_RERANK_TOP")||e.set("MEMORY_RERANK_TOP","10"),e.has("MEMORY_FINAL_CONTEXT")||e.set("MEMORY_FINAL_CONTEXT","5"),e.has("MEMORY_TOKEN_BUDGET")||e.set("MEMORY_TOKEN_BUDGET","2000"),e.has("TOOLNET_SESSION_LEARNING")||e.set("TOOLNET_SESSION_LEARNING","1"),e.has("TOOLNET_WORK_CONTINUITY")||e.set("TOOLNET_WORK_CONTINUITY","1"),e.has("TOOLNET_SEMANTIC_CONTINUITY")||e.set("TOOLNET_SEMANTIC_CONTINUITY","1"),e.has("TOOLNET_SMART_HANDOFF")||e.set("TOOLNET_SMART_HANDOFF","1")}async function Mt(e,t){Oe(e),t||O(e),console.log("TOOLNET MEMORY SETUP"),console.log(""),console.log("Interactive terminal not detected."),console.log(`Config: ${_}`),console.log(`Storage: ${M(T(e))}`),console.log(""),console.log("Run from an interactive terminal to configure providers:"),console.log(" toolnet-memory setup")}async function Lt(){let e=m.existsSync(_),t=e?tt(m.readFileSync(_,"utf8")):new Map,o=new Map(t);if(Oe(o),!f.isTTY||!I.isTTY){await Mt(o,e);return}let n=ze.createInterface({input:f,output:I}),r=!1;try{for(;;){lt(),dt(o);let s=await gt(n);if(s==="storage"){let a=JSON.stringify([...o.entries()]);await pt(n,o),r=r||a!==JSON.stringify([...o.entries()]);continue}if(s==="ai"){let a=JSON.stringify([...o.entries()]);await It(n,o),r=r||a!==JSON.stringify([...o.entries()]);continue}if(s==="exit"){if(!r){console.log(""),console.log("No changes made.");return}console.log("");let a=(await n.question("Discard unsaved changes? (y/N): ")).trim().toLowerCase();if(a==="y"||a==="yes"){console.log(""),console.log("Changes discarded.");return}continue}O(o),console.log(""),console.log("\u2713 Configuration saved"),console.log(` ${_}`),console.log(""),console.log(`\u2713 Permissions: ${m.statSync(_).mode.toString(8).slice(-3)}`),console.log(`\u2713 Storage: ${M(T(o))}`),ue(o)||console.log("\u26A0 Storage configuration is incomplete."),Rt(),console.log(""),console.log("Next:"),console.log(" toolnet-memory doctor"),console.log("");return}}finally{n.close()}}Lt().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1)});