@x-otto/memory 0.0.1-alpha.2 → 0.0.1-alpha.3

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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{createLogger as e}from"@x-otto/shared";import{normalizeEnvFraction as t}from"@x-otto/env";import{CONVERSATION_SUMMARY_PREFIX as n}from"@x-otto/interchange";import{existsSync as r,statSync as i}from"node:fs";import{basename as a,dirname as o,extname as s,join as c,relative as l}from"node:path";import{readdir as u,stat as d}from"node:fs/promises";import{createPersistence as f,saveDocument as p}from"@x-otto/persistence";const m=`You are a conversation summarizer for an AI coding assistant. Your task is to create a structured summary of the conversation that preserves all information needed to continue the work.
1
+ import{createLogger as e}from"@x-otto/shared";import{normalizeEnvFraction as t}from"@x-otto/env";import{CONVERSATION_SUMMARY_PREFIX as n}from"@x-otto/interchange";import{existsSync as r,statSync as i}from"node:fs";import{basename as a,dirname as o,extname as s,join as c,relative as l}from"node:path";import{readdir as u,stat as d}from"node:fs/promises";import{FileAppendLog as f,createPersistence as p,saveDocument as m}from"@x-otto/persistence";const h=`You are a conversation summarizer for an AI coding assistant. Your task is to create a structured summary of the conversation that preserves all information needed to continue the work.
2
2
 
3
3
  Create the summary in the following format:
4
4
 
@@ -36,7 +36,7 @@ IMPORTANT:
36
36
  - Preserve exact file paths, function names, error messages, and code snippets
37
37
  - Keep technical details precise — do not generalize
38
38
  - Include all tool call results that affect the current state
39
- - Note any pending or failed operations`,h=`You are updating an existing conversation summary with new information. The existing summary and new messages are provided below.
39
+ - Note any pending or failed operations`,g=`You are updating an existing conversation summary with new information. The existing summary and new messages are provided below.
40
40
 
41
41
  Produce a single UPDATED summary that stays BOUNDED — a summary that grows without limit across rounds eventually gets hard-truncated by the output cap, silently losing whatever fell past the limit. Keep it tight by actively triaging, not by appending.
42
42
 
@@ -55,7 +55,7 @@ Rules:
55
55
  - Move completed "In Progress" items to "Done", then compress "Done" per the above
56
56
  - Merge new work items, decisions, and context; do not simply append
57
57
  - Keep the same structured format (including "All User Requests")
58
- - If any information conflicts, use the newer version`,g=`You are summarizing the FIRST PART of an assistant turn that was split during conversation compaction. The remaining part of this turn is still in the conversation.
58
+ - If any information conflicts, use the newer version`,_=`You are summarizing the FIRST PART of an assistant turn that was split during conversation compaction. The remaining part of this turn is still in the conversation.
59
59
 
60
60
  Create a brief summary in this format:
61
61
 
@@ -68,7 +68,7 @@ Create a brief summary in this format:
68
68
  ## Context for Remaining Messages
69
69
  - [Information needed to understand the remaining suffix messages]
70
70
 
71
- Keep it concise — this will be prepended to the remaining messages of this turn.`,_=`## Goal
71
+ Keep it concise — this will be prepended to the remaining messages of this turn.`,v=`## Goal
72
72
  [What is the user trying to accomplish?]
73
73
 
74
74
  ## Constraints & Preferences
@@ -96,60 +96,50 @@ Keep it concise — this will be prepended to the remaining messages of this tur
96
96
  - [Files that were modified]
97
97
 
98
98
  ## Critical Context
99
- - [Any critical context needed to continue the work]`,v=`IMPORTANT:
99
+ - [Any critical context needed to continue the work]`,y=`IMPORTANT:
100
100
  - Preserve exact file paths, function names, error messages, and code snippets
101
101
  - Keep technical details precise — do not generalize
102
102
  - Include all tool call results that affect the current state
103
- - Note any pending or failed operations`,y=`You are a conversation summarizer for an AI coding assistant. You are summarizing the EARLIER portion of a conversation. The most recent messages are kept verbatim and will follow your summary unchanged — they are shown to you below in <preserved_tail_context> FOR REFERENCE ONLY.
103
+ - Note any pending or failed operations`,b=`You are a conversation summarizer for an AI coding assistant. You are summarizing the EARLIER portion of a conversation. The most recent messages are kept verbatim and will follow your summary unchanged — they are shown to you below in <preserved_tail_context> FOR REFERENCE ONLY.
104
104
 
105
105
  Your summary and those preserved recent messages together become the assistant's full context going forward.
106
106
 
107
107
  Create the summary in the following format:
108
108
 
109
- ${_}
109
+ ${v}
110
110
 
111
111
  ## Context the Preserved Tail Depends On
112
112
  - [Facts a reader MUST know to correctly understand the preserved recent messages shown in <preserved_tail_context>: decisions already made, file paths and their current state, conventions/constraints agreed earlier, mistakes already corrected, and any partially-done work those recent messages continue. Read the preserved tail, then look BACK through the earlier conversation for what it silently relies on. This section is the whole point of this summary — be specific.]
113
113
 
114
- ${v}
114
+ ${y}
115
115
  - Do NOT summarize the content of <preserved_tail_context> — it is kept verbatim after your summary and re-describing it wastes your budget. Use it ONLY to work out which parts of the EARLIER conversation it depends on.
116
- - Summarize ONLY the conversation inside <conversation>.`,b=`You are summarizing ONE SEGMENT of a longer conversation for an AI coding assistant. This is a partial view — you are NOT seeing the whole conversation, and other segments are being summarized separately. Your segment summary will later be merged with the others.
116
+ - Summarize ONLY the conversation inside <conversation>.`,x=`You are summarizing ONE SEGMENT of a longer conversation for an AI coding assistant. This is a partial view — you are NOT seeing the whole conversation, and other segments are being summarized separately. Your segment summary will later be merged with the others.
117
117
 
118
118
  Create a concise summary of THIS SEGMENT in the following format:
119
119
 
120
- ${_}
121
-
122
120
  ${v}
121
+
122
+ ${y}
123
123
  - Do NOT emit conclusions that require seeing the whole conversation (e.g. a single global "next step"). Report only what THIS segment establishes; the merge step will reconcile across segments.
124
- - If <preserved_tail_context> is present, it holds the most recent messages, kept verbatim outside this compaction. Do NOT summarize it — use it ONLY to judge which parts of THIS segment it depends on, and make sure those parts survive into your summary. Summarize ONLY the content inside <conversation>.`,x=`You are merging several segment summaries of one conversation into a single coherent summary for an AI coding assistant. Each input below is a summary of a different (chronological) segment of the same conversation.
124
+ - If <preserved_tail_context> is present, it holds the most recent messages, kept verbatim outside this compaction. Do NOT summarize it — use it ONLY to judge which parts of THIS segment it depends on, and make sure those parts survive into your summary. Summarize ONLY the content inside <conversation>.`,S=`You are merging several segment summaries of one conversation into a single coherent summary for an AI coding assistant. Each input below is a summary of a different (chronological) segment of the same conversation.
125
125
 
126
126
  Reconcile them into one summary in the following format:
127
127
 
128
- ${_}
129
-
130
128
  ${v}
129
+
130
+ ${y}
131
131
  - Resolve conflicts in favour of the LATER segment.
132
- - Merge duplicate items; do not simply concatenate.`;function S(e,t,n,r){let i=[];return r?.variant===`segment`?i.push(b):t?(i.push(h),i.push(`\n<existing_summary>\n${t}\n</existing_summary>`),r?.preservedCount&&r.preservedCount>0&&i.push(C(r.preservedCount))):r?.variant===`preserved-tail`?i.push(y):i.push(m),n&&i.push(`\n<additional_instructions>\n${n}\n</additional_instructions>`),r?.preservedTailReference&&i.push(`\n<preserved_tail_context>\n${r.preservedTailReference}\n</preserved_tail_context>`),i.push(`\n<conversation>\n${e}\n</conversation>`),i.join(`
133
- `)}function C(e){return`\nNOTE: The ${e} most recent message(s) are kept verbatim and will follow your summary — do NOT re-summarize them; focus on the earlier conversation and what those recent messages depend on.`}function ee(e,t,n){let r=[x];return t&&r.push(`\n<existing_summary>\n${t}\n</existing_summary>`),n&&r.push(`\n<additional_instructions>\n${n}\n</additional_instructions>`),r.push(`\n<segment_summaries>\n${e}\n</segment_summaries>`),r.join(`
134
- `)}function w(e){return`${g}\n\n<turn_prefix>\n${e}\n</turn_prefix>`}const T=8e3;function E(e){let t=0;for(let n of e)/[\u3000-\u9fff\uff00-\uffef]/.test(n)&&t++;return Math.ceil(t+(e.length-t)/4)}function te(e,t){if(t<=0)return``;if(E(e)<=t)return e;let n=Array.from(e),r=0,i=n.length,a=``;for(;r<=i;){let e=Math.floor((r+i)/2),o=n.slice(0,e).join(``);E(o)<=t?(a=o,r=e+1):i=e-1}return a}function ne(e,t=T){if(e.size===0)return``;let n=[`<agent_memory>`],r=0,i=0;for(let[a,o]of e){let e=o.trim();if(!e)continue;let s=t-r;if(s<=0){i++;continue}let c=E(e);if(c<=s)n.push(`# ${a}`),n.push(e),n.push(``),r+=c;else{let i=te(e,s);n.push(`# ${a}`),n.push(i),n.push(`\n> ⚠ [truncated — content exceeded the memory injection budget (${t} tokens); remainder omitted]`),n.push(``),r=t}}return i>0&&(n.push(`> ⚠ [${i} additional memory source(s) omitted — total injection budget (${t} tokens) reached]`),n.push(``)),n.push(`</agent_memory>`),n.push(``),n.push(`<memory_guidelines>
135
- When you learn something from this interaction that should be remembered, use the memory_record tool — never edit/write .otto state files directly (they are protected; see RFC-228). Choose scope:
136
- - 'workspace' (default): durable structured facts portable across machines (preferences, gotchas, decisions).
137
- - 'local': machine-specific facts that should never sync to other devices (local paths, installed plugins, environment quirks).
138
- - 'project-shared': team-visible project conventions that belong in AGENTS.md (build/test commands, code style, architecture decisions all contributors should see).
139
-
140
- **Should remember**:
141
- - User-specified preferences and constraints
142
- - Patterns learned from feedback (code style, architecture preferences)
143
- - Key tool parameters (API endpoints, non-sensitive config)
144
- - Project-specific build/test commands
145
-
146
- **Should NOT remember**:
147
- - Temporary task details
148
- - One-off Q&A
149
- - API keys, passwords, or sensitive information
150
- - Casual conversation
151
- </memory_guidelines>`),n.join(`
152
- `)}const re=`You are a learning extractor for an AI coding assistant. Read the conversation below and extract durable, cross-session "lessons" — operational knowledge that will help the assistant do better on FUTURE, unrelated sessions in this project.
132
+ - Merge duplicate items; do not simply concatenate.`;function C(e,t,n,r){let i=[];return r?.variant===`segment`?i.push(x):t?(i.push(g),i.push(`\n<existing_summary>\n${t}\n</existing_summary>`),r?.preservedCount&&r.preservedCount>0&&i.push(ee(r.preservedCount))):r?.variant===`preserved-tail`?i.push(b):i.push(h),n&&i.push(`\n<additional_instructions>\n${n}\n</additional_instructions>`),r?.preservedTailReference&&i.push(`\n<preserved_tail_context>\n${r.preservedTailReference}\n</preserved_tail_context>`),r?.omitConversation||i.push(`\n<conversation>\n${e}\n</conversation>`),i.join(`
133
+ `)}function ee(e){return`\nNOTE: The ${e} most recent message(s) are kept verbatim and will follow your summary — do NOT re-summarize them; focus on the earlier conversation and what those recent messages depend on.`}function te(e,t,n){let r=[S];return t&&r.push(`\n<existing_summary>\n${t}\n</existing_summary>`),n&&r.push(`\n<additional_instructions>\n${n}\n</additional_instructions>`),r.push(`\n<segment_summaries>\n${e}\n</segment_summaries>`),r.join(`
134
+ `)}function w(e){return`${_}\n\n<turn_prefix>\n${e}\n</turn_prefix>`}const T=8e3;function E(e){let t=0;for(let n of e)/[\u3000-\u9fff\uff00-\uffef]/.test(n)&&t++;return Math.ceil(t+(e.length-t)/4)}function ne(e,t){if(t<=0)return``;if(E(e)<=t)return e;let n=Array.from(e),r=0,i=n.length,a=``;for(;r<=i;){let e=Math.floor((r+i)/2),o=n.slice(0,e).join(``);E(o)<=t?(a=o,r=e+1):i=e-1}return a}function re(e){return e.endsWith(`/MEMORY.md`)}function ie(e,t,n){if(!t||t.size===0)return{text:e,folded:0};let r=[],i=0,a=n-12096e5;for(let n of e.split(`
135
+ `)){let e=n.match(/^-\s*\[[^\]]+\]\(memory\/([^)]+\.md)\)/);if(!e){r.push(n);continue}let o=t.get(`memory/${e[1]}`);o!==void 0&&o>=a?r.push(n):i++}return i>0&&r.push(`+ ${i} older entr${i>1?`ies`:`y`} use memory_read to list/fetch`),{text:r.join(`
136
+ `),folded:i}}function ae(e,t=T,n,r=Date.now()){if(e.size===0)return``;let i=[`<agent_memory>`],a=0,o=0;for(let[s,c]of e){let e=(re(s)?ie(c,n,r).text:c).trim();if(!e)continue;let l=t-a;if(l<=0){o++;continue}let u=E(e);if(u<=l)i.push(`# ${s}`),i.push(e),i.push(``),a+=u;else{let n=ne(e,l);i.push(`# ${s}`),i.push(n),i.push(`\n> [truncated — content exceeded the memory injection budget (${t} tokens); remainder omitted]`),i.push(``),a=t}}return o>0&&(i.push(`> ⚠ [${o} additional memory source(s) omitted — total injection budget (${t} tokens) reached]`),i.push(``)),i.push(`</agent_memory>`),i.push(``),i.push(`<memory_guidelines>
137
+ Persist durable facts via the memory_record tool — never edit/write .otto state files directly (protected; RFC-228). Scopes: 'workspace' (default, portable facts), 'local' (machine-specific, never synced), 'project-shared' (team-visible AGENTS.md conventions).
138
+
139
+ Remember: user preferences and constraints; patterns learned from feedback; key non-sensitive tool parameters; project-specific build/test commands.
140
+ Do NOT remember: temporary task details; one-off Q&A; API keys, passwords, or sensitive information; casual conversation.
141
+ </memory_guidelines>`),i.join(`
142
+ `)}const oe=`You are a learning extractor for an AI coding assistant. Read the conversation below and extract durable, cross-session "lessons" operational knowledge that will help the assistant do better on FUTURE, unrelated sessions in this project.
153
143
 
154
144
  A lesson captures a reusable cause→action:
155
145
  - "trigger": the situation/context that should recall this lesson (when it applies)
@@ -169,37 +159,41 @@ DO NOT save these (they pollute recall):
169
159
  - Secrets, API keys, passwords, tokens, or any sensitive value
170
160
 
171
161
  Output ONLY a JSON array, nothing else. Each element: {"trigger": string, "insight": string, "tags": string[]}.
172
- If there is nothing durable worth saving, output exactly: []`;function ie(e){return`${re}\n\n<conversation>\n${e}\n</conversation>`}function ae(e,t){return`Earlier conversation history has been compacted into the summary below. If you need details the summary does not cover, call the memory_archive_read tool with archive id "${e}" to pull back the original messages.
162
+ If there is nothing durable worth saving, output exactly: []`;function se(e){return`${oe}\n\n<conversation>\n${e}\n</conversation>`}function ce(e,t){return`Earlier conversation history has been compacted into the summary below. If you need details the summary does not cover, call the memory_archive_read tool with archive id "${e}" to pull back the original messages.
173
163
 
174
164
  <summary>
175
165
  ${t}
176
- </summary>`}function oe(e){return`[Low-fidelity summary — only about ${Math.max(0,Math.round(e*100))}% of the original history could be fed into summarization. Significant details are missing. Treat this summary as incomplete: if you need details it does not cover, call memory_archive_read with the archive id below to pull back the original messages, rather than guessing or asking the user to repeat themselves.]`}const D=e(`@x-otto/memory:persistent`),se=[{path:`~/.otto/AGENTS.md`,writable:!0},{path:`./.otto/AGENTS.md`,writable:!0},{path:`./AGENTS.md`,writable:!1}];var ce=class{memories=new Map;unwatch;constructor(e,t=se){this.store=e,this.sources=t}async load(){this.memories=await this.store.load(this.sources),D.info(`Loaded ${this.memories.size} memory source(s)`)}async reload(){await this.load()}getInjection(){return ne(this.memories)}getMemories(){return this.memories}async write(e,t){let n=this.sources.find(t=>t.path===e);if(!n)throw Error(`Unknown memory source: ${e}`);if(!n.writable)throw Error(`Memory source is read-only: ${e}`);await this.store.write(e,t),this.memories.set(e,t),D.info(`Memory source written: ${e}`)}watching(){this.unwatch||!this.store.watch||(this.unwatch=this.store.watch(this.sources,e=>{D.info(`Memory source changed: ${e}, reloading...`),this.reload()}))}stop(){this.unwatch?.(),this.unwatch=void 0}dispose(){this.stop(),this.memories.clear()}},le=class{data=new Map;set(e,t){this.data.set(e,t)}async load(e){let t=new Map;for(let n of e){let e=this.data.get(n.path);e!==void 0&&t.set(n.path,e)}return t}async write(e,t){this.data.set(e,t)}},O=class{cwd;homeDirOverride;constructor(e=process.cwd(),t){this.cwd=e,this.homeDirOverride=t}async load(e){let{readFile:t}=await import(`node:fs/promises`),{resolve:n,isAbsolute:r}=await import(`node:path`),{homedir:i}=await import(`node:os`),a=new Map;for(let o of e){let e=this.resolvePath(o.path,n,r,i);try{let n=await t(e,`utf-8`);a.set(o.path,n)}catch{}}return a}async write(e,t){let{mkdir:n}=await import(`node:fs/promises`),{resolve:r,dirname:i,isAbsolute:a}=await import(`node:path`),{homedir:o}=await import(`node:os`),{atomicWriteFile:s}=await import(`@x-otto/persistence`),c=this.resolvePath(e,r,a,o);await n(i(c),{recursive:!0}),await s(c,t)}resolvePath(e,t,n,r){let i=this.homeDirOverride??r(),a=e.startsWith(`~/`)?e.replace(`~/`,`${i}/`):e;return n(a)?a:t(this.cwd,a)}};const ue=e(`@x-otto/memory:token-estimator`);let k=null,A=null;async function de(){if(!k){if(A){await A;return}A=(async()=>{try{let{getEncoding:e}=await import(`js-tiktoken`),t=e(`cl100k_base`);k={encode:e=>typeof e!=`string`||e.length===0?[]:t.encode(e)},ue.info(`Tokenizer loaded: cl100k_base`)}catch(e){ue.warn(`Failed to load js-tiktoken, falling back to heuristic: %s`,e.message),k=null}finally{A=null}})(),await A}}const fe=2e3;function pe(e){return e>=33&&e<=47||e>=58&&e<=64||e>=91&&e<=96||e>=123&&e<=126}function me(e){return e>=12352&&e<=12543||e>=13312&&e<=19903||e>=19968&&e<=40959||e>=63744&&e<=64255||e>=44032&&e<=55203||e>=65280&&e<=65519||e>=131072&&e<=191471}function j(e){let t=0,n=0,r=0;for(let i of e){let e=i.codePointAt(0)??0;me(e)?t+=1:pe(e)?n+=1:r+=1}return Math.ceil(t*1+n*.5+r*.25)}function he(e,t){let n=1;for(let r=1;r<e.length;r++)if(e.charCodeAt(r)===e.charCodeAt(r-1)){if(n++,n>=t)return!0}else n=1;return!1}function M(e){if(k){if(e.length>1e5||he(e,64))return j(e);try{return k.encode(e).length}catch{return j(e)}}return A||de().catch(()=>{}),j(e)}function ge(e){return e?e.length<=6?e:e.slice(-6):`?`}function N(e){return typeof e.content==`string`?e.content:Array.isArray(e.content)?e.content.map(e=>{switch(e.type){case`text`:return e.text;case`thinking`:return e.text;case`tool_call`:return`[call#${ge(e.id)} ${e.name}](${JSON.stringify(e.arguments)})`;case`image`:return`[image]`;default:return``}}).filter(Boolean).join(`
177
- `):JSON.stringify(e)}const _e=new WeakMap;function P(e,t=M){let n=t===M&&k!==null&&typeof e==`object`&&!!e;if(n){let t=_e.get(e);if(t!==void 0)return t}let r;if(Array.isArray(e.content)){r=4;for(let n of e.content)switch(n.type){case`text`:case`thinking`:r+=t(n.text);break;case`tool_call`:r+=t(`${n.name}(${JSON.stringify(n.arguments)})`);break;case`image`:r+=fe;break;default:break}}else r=4+t(N(e));return n&&_e.set(e,r),r}function F(e,t=M){let n=0;for(let r of e)n+=P(r,t);return n}function ve(e){return e.role===`assistant`}function I(e,t=M){let n=-1,r=0;for(let t=e.length-1;t>=0;t--){let i=e[t];if(ve(i)&&i.usage){n=t,r=(i.usage.cacheReadTokens??0)+(i.usage.cacheWriteTokens??0)+i.usage.inputTokens+i.usage.outputTokens;break}}let i=0,a=0;if(n>=0){for(let r=0;r<=n;r++)a+=P(e[r],t);for(let r=n+1;r<e.length;r++)i+=P(e[r],t)}else i=F(e,t);let o=r+i,s=n>=0?Math.max(0,r-a):0,c=Math.max(0,o-s);return{total:o,fromUsage:r,fromEstimate:i,lastUsageIndex:n,derivedPrefix:s,bodyAfterPrefix:c}}const ye=.1,L=16384,be=t(process.env.OTTO_MEMORY_COMPACTION_HARD_CEILING_FRACTION,.9),xe=24e3,Se=.6,Ce=.15,we=2e4,Te=2e3,Ee=`write`,De=`edit`,Oe=`read`,ke=`grep`,Ae=`find`,je=`read_file`,Me=`grep_search`,Ne=`file_search`,Pe=t(process.env.OTTO_MEMORY_COVERAGE_WARN_THRESHOLD,.7),Fe=t(process.env.OTTO_MEMORY_COVERAGE_ERROR_THRESHOLD,.5),Ie=t(process.env.OTTO_MEMORY_COVERAGE_FLOOR_THRESHOLD,.3),Le=[Ee,De],R=[Oe,ke,Ae,je,Me,Ne,`bash`,`bash_output`,`web_search`,`web_fetch`];function z(e){return{compaction:{enabled:!0,trigger:[`headroom`,Math.min(L,e.maxOutput)+xe],keepRecent:[`fraction`,ye],reserveTokens:Math.min(L,e.maxOutput)},prune:{trigger:[`fraction`,Se],protect:[`fraction`,Ce],minimum:we,regenerableTools:[...R],protectedTools:[],truncateTools:[...Le],truncateMaxLength:Te}}}const B={enabled:!0,trigger:[`headroom`,L+xe],keepRecent:[`fraction`,ye],reserveTokens:L},V={trigger:[`fraction`,Se],protect:[`fraction`,Ce],minimum:we,regenerableTools:[...R],protectedTools:[],truncateTools:[...Le],truncateMaxLength:Te};function H(e,t){let[n,r]=e;switch(n){case`tokens`:return r;case`fraction`:return Math.floor(t*r);case`headroom`:{let e=Math.floor(t*.5);return Math.max(e,t-r)}}}const Re=e(`@x-otto/memory:prune`);function ze(e,t,n={},r=M,i={}){let a={...V,...n},o=H(a.trigger,t),s=F(e,r),c=i.currentTokens??s;if(!i.force&&c<o)return{messages:[...e],prunedCount:0,tokensSaved:0,changed:!1};let l=H(a.protect,t),u=new Set(a.truncateTools),d=new Set(a.regenerableTools),f=new Set(a.protectedTools),p=0,m=e.length;for(let t=e.length-1;t>=0;t--){let n=e[t],i=P(n,r);if(p+=i,p>=l){m=t+1;break}}let h=e.map((e,t)=>{if(t>=m)return e;if(e.role===`tool_result`){let t=e;return!d.has(t.toolName)||f.has(t.toolName)?e:Be(t,r)}return e.role===`assistant`&&t<m?He(e,u,a.truncateMaxLength):e}),g=s-F(h,r);if(g<a.minimum)return Re.debug(`Prune skipped: savings ${g} < minimum ${a.minimum}`),{messages:[...e],prunedCount:0,tokensSaved:0,changed:!1};let _=h.filter((t,n)=>t!==e[n]).length;return Re.info(`Pruned ${_} messages, saved ~${g} tokens`),{messages:h,prunedCount:_,tokensSaved:g,changed:!0}}function Be(e,t){let n=e.content.map(e=>e.type===`text`?e.text:``).join(``),r=t(n);if(r<100)return e;let i=n.split(`
178
- `).find(e=>e.startsWith(`[tool result stored to `)),a=i?` ${i}`:``;return{...e,content:[{type:`text`,text:`[output pruned — ${r} tokens]${a}`}]}}function Ve(e,t){let n=typeof t.path==`string`?t.path:void 0;if(n===void 0)return null;if(e===`write`)return{path:n,content:`[content omitted by context pruning — ${(typeof t.content==`string`?t.content:``).length} chars. The file was already written to disk; read it if needed.]`};if(e===`edit`){let e=typeof t.oldContent==`string`?t.oldContent:``,r=typeof t.newContent==`string`?t.newContent:``;return{path:n,oldContent:`[omitted by context pruning — ${e.length} chars]`,newContent:`[omitted by context pruning — ${r.length} chars. The edit was already applied to the file on disk; read it if needed.]`}}return null}function He(e,t,n){if(!Array.isArray(e.content))return e;let r=!1,i=e.content.map(e=>{if(e.type!==`tool_call`||!t.has(e.name)||JSON.stringify(e.arguments).length<=n)return e;let i=Ve(e.name,e.arguments);return i===null?e:(r=!0,{...e,arguments:i})});return r?{...e,content:i}:e}function U(e){return`${e.role===`user`?`Human`:e.role===`assistant`?`Assistant`:e.role===`tool_result`?`Tool[result#${ge(e.toolCallId)} ${e.toolName}]${e.isError?`{error}`:``}`:`System`}: ${N(e)}`}function W(e,t){if(e.length<=t)return e;let n=e=>`\n\n…[${e} chars omitted to fit the summarization window]…\n\n`,r=Math.max(0,t-n(e.length).length),i=Math.floor(r/2);if(i<=0)return n(e.length).slice(0,t);let a=e.length-i*2;return`${e.slice(0,i)}${n(a)}${e.slice(-i)}`}function Ue(e,t,n){if(n(e)<=t)return e;let r=0,i=e.length,a=``;for(;r<=i;){let o=Math.floor((r+i)/2),s=W(e,o);n(s)<=t?(a=s,r=o+1):i=o-1}return a}function We(e){return e.role===`user`?0:e.role===`assistant`?1:e.role===`tool_result`?Ge.has(e.toolName)?3:2:1}const Ge=new Set(R);function Ke(e,t){let n=e.map(e=>({msg:e,prio:We(e),text:U(e)})),r=n.reduce((e,t)=>e+t.text.length,0);if(r<=t)return{texts:n.map(e=>e.text),sourceChars:r,fedChars:r,truncated:!1};let i=!1,a=e=>n.filter(t=>t.prio===e),o=a(0),s=a(1),c=a(2),l=a(3),u=e=>e.reduce((e,t)=>e+t.text.length,0),d=[{msgs:l,share:.1},{msgs:c,share:.2},{msgs:s,share:.35}],f=t;for(let{msgs:e,share:n}of d){if(e.length===0)continue;let r=u(e),a=Math.floor(t*n),o=Math.min(r,a);Je(e,o),o<r&&(i=!0),f-=u(e)}o.length>0&&u(o)>f&&(Je(o,Math.max(0,f)),i=!0);let p=n.reduce((e,t)=>e+t.text.length,0);return{texts:n.map(e=>e.text),sourceChars:r,fedChars:p,truncated:i}}function qe(e,t){if(e.length===0||t<=0)return{text:``,chars:0,truncated:!1};let n=Ke(e,t),r=n.texts.filter(e=>e.length>0).join(`
166
+ </summary>`}function le(e){return`[Low-fidelity summary — only about ${Math.max(0,Math.round(e*100))}% of the original history could be fed into summarization. Significant details are missing. Treat this summary as incomplete: if you need details it does not cover, call memory_archive_read with the archive id below to pull back the original messages, rather than guessing or asking the user to repeat themselves.]`}const D=e(`@x-otto/memory:persistent`),ue=[{path:`~/.otto/AGENTS.md`,writable:!0},{path:`./.otto/AGENTS.md`,writable:!0},{path:`./AGENTS.md`,writable:!1}],de=new Set([`## 代码风格与命名约定`,`## 发布与包管理注意事项`,`## 安全与配置建议`]);function fe(e){let t=e.split(`
167
+ `),n=[],r=!1;for(let e of t){if(e.match(/^## (.+)/)){r=de.has(e.trim()),r||n.push(e);continue}r||n.push(e)}return n.join(`
168
+ `)}const pe=/<!-- otto-memory:start slug="([^"]+)" -->/g;function me(e){let t=new Set;for(let n of e.matchAll(pe))t.add(n[1]??``);return t}function he(e){let t=new Set;for(let n of e.matchAll(/<!-- otto-memory:start slug="[^"]+" -->\n## ([^\n]+)/g))t.add((n[1]??``).trim());return t}function ge(e){return e.replace(/[^a-z0-9一-鿿]+/gi,``).toLowerCase()}function _e(e,t){if(!t||!e)return e;let n=me(t),r=new Set([...he(t)].map(e=>ge(e)));if(n.size===0&&r.size===0)return e;let i=e.replace(/<!-- otto-memory:start slug="([^"]+)" -->[\s\S]*?<!-- otto-memory:end slug="\1" -->\n?/g,(e,t)=>n.has(t)?``:e).split(`
169
+ `),a=[],o=!1;for(let e of i){let t=e.match(/^\s*-\s+\*\*([^*]+)\*\*/);if(t&&r.has(ge(t[1]??``))){o=!0;continue}if(o){(e.trim()===``||/^\s*- /.test(e)||e.startsWith(`## `))&&(o=!1,a.push(e));continue}a.push(e)}return a.join(`
170
+ `)}var ve=class{memories=new Map;unwatch;constructor(e,t=ue,n){this.store=e,this.sources=t,this.entryMtimes=n}async load(){this.memories=await this.store.load(this.sources);let e=this.memories.get(`./.otto/AGENTS.md`),t=this.sources.find(e=>e.path===`./AGENTS.md`)?.path;if(t&&e&&this.memories.has(t)){let n=this.memories.get(t)??``,r=_e(n,e);r!==n&&this.memories.set(t,r)}if(t&&this.memories.has(t)){let e=this.memories.get(t)??``,n=fe(e);n!==e&&this.memories.set(t,n)}D.info(`Loaded ${this.memories.size} memory source(s)`)}async reload(){await this.load()}getInjection(){return ae(this.memories,T,this.entryMtimes)}getMemories(){return this.memories}async write(e,t){let n=this.sources.find(t=>t.path===e);if(!n)throw Error(`Unknown memory source: ${e}`);if(!n.writable)throw Error(`Memory source is read-only: ${e}`);await this.store.write(e,t),this.memories.set(e,t),D.info(`Memory source written: ${e}`)}watching(){this.unwatch||!this.store.watch||(this.unwatch=this.store.watch(this.sources,e=>{D.info(`Memory source changed: ${e}, reloading...`),this.reload()}))}stop(){this.unwatch?.(),this.unwatch=void 0}dispose(){this.stop(),this.memories.clear()}},ye=class{data=new Map;set(e,t){this.data.set(e,t)}async load(e){let t=new Map;for(let n of e){let e=this.data.get(n.path);e!==void 0&&t.set(n.path,e)}return t}async write(e,t){this.data.set(e,t)}},O=class{cwd;homeDirOverride;constructor(e=process.cwd(),t){this.cwd=e,this.homeDirOverride=t}async load(e){let{readFile:t}=await import(`node:fs/promises`),{resolve:n,isAbsolute:r}=await import(`node:path`),{homedir:i}=await import(`node:os`),a=new Map;for(let o of e){let e=this.resolvePath(o.path,n,r,i);try{let n=await t(e,`utf-8`);a.set(o.path,n)}catch{}}return a}async write(e,t){let{mkdir:n}=await import(`node:fs/promises`),{resolve:r,dirname:i,isAbsolute:a}=await import(`node:path`),{homedir:o}=await import(`node:os`),{atomicWriteFile:s}=await import(`@x-otto/persistence`),c=this.resolvePath(e,r,a,o);await n(i(c),{recursive:!0}),await s(c,t)}resolvePath(e,t,n,r){let i=this.homeDirOverride??r(),a=e.startsWith(`~/`)?e.replace(`~/`,`${i}/`):e;return n(a)?a:t(this.cwd,a)}};const be=e(`@x-otto/memory:token-estimator`);let k=null,A=null;async function xe(){if(!k){if(A){await A;return}A=(async()=>{try{let{getEncoding:e}=await import(`js-tiktoken`),t=e(`cl100k_base`);k={encode:e=>typeof e!=`string`||e.length===0?[]:t.encode(e)},be.info(`Tokenizer loaded: cl100k_base`)}catch(e){be.warn(`Failed to load js-tiktoken, falling back to heuristic: %s`,e.message),k=null}finally{A=null}})(),await A}}const Se=2e3;function Ce(e){return e>=33&&e<=47||e>=58&&e<=64||e>=91&&e<=96||e>=123&&e<=126}function we(e){return e>=12352&&e<=12543||e>=13312&&e<=19903||e>=19968&&e<=40959||e>=63744&&e<=64255||e>=44032&&e<=55203||e>=65280&&e<=65519||e>=131072&&e<=191471}function j(e){let t=0,n=0,r=0;for(let i of e){let e=i.codePointAt(0)??0;we(e)?t+=1:Ce(e)?n+=1:r+=1}return Math.ceil(t*1+n*.5+r*.25)}function Te(e,t){let n=1;for(let r=1;r<e.length;r++)if(e.charCodeAt(r)===e.charCodeAt(r-1)){if(n++,n>=t)return!0}else n=1;return!1}function M(e){if(k){if(e.length>1e5||Te(e,64))return j(e);try{return k.encode(e).length}catch{return j(e)}}return A||xe().catch(()=>{}),j(e)}function Ee(e){return e?e.length<=6?e:e.slice(-6):`?`}function N(e){return typeof e.content==`string`?e.content:Array.isArray(e.content)?e.content.map(e=>{switch(e.type){case`text`:return e.text;case`thinking`:return e.text;case`tool_call`:return`[call#${Ee(e.id)} ${e.name}](${JSON.stringify(e.arguments)})`;case`image`:return`[image]`;default:return``}}).filter(Boolean).join(`
171
+ `):JSON.stringify(e)}const De=new WeakMap;function P(e,t=M){let n=t===M&&k!==null&&typeof e==`object`&&!!e;if(n){let t=De.get(e);if(t!==void 0)return t}let r;if(Array.isArray(e.content)){r=4;for(let n of e.content)switch(n.type){case`text`:case`thinking`:r+=t(n.text);break;case`tool_call`:r+=t(`${n.name}(${JSON.stringify(n.arguments)})`);break;case`image`:r+=Se;break;default:break}}else r=4+t(N(e));return n&&De.set(e,r),r}function F(e,t=M){let n=0;for(let r of e)n+=P(r,t);return n}function Oe(e){return e.role===`assistant`}function I(e,t=M){let n=-1,r=0;for(let t=e.length-1;t>=0;t--){let i=e[t];if(Oe(i)&&i.usage){n=t,r=(i.usage.cacheReadTokens??0)+(i.usage.cacheWriteTokens??0)+i.usage.inputTokens+i.usage.outputTokens;break}}let i=0,a=0;if(n>=0){for(let r=0;r<=n;r++)a+=P(e[r],t);for(let r=n+1;r<e.length;r++)i+=P(e[r],t)}else i=F(e,t);let o=r+i,s=n>=0?Math.max(0,r-a):0,c=Math.max(0,o-s);return{total:o,fromUsage:r,fromEstimate:i,lastUsageIndex:n,derivedPrefix:s,bodyAfterPrefix:c}}const ke=.1,L=16384,Ae=t(process.env.OTTO_MEMORY_COMPACTION_HARD_CEILING_FRACTION,.9),je=24e3,Me=.6,Ne=.15,Pe=2e4,Fe=2e3,Ie=`write`,Le=`edit`,Re=`read`,ze=`grep`,Be=`find`,Ve=`read_file`,He=`grep_search`,Ue=`file_search`,We=t(process.env.OTTO_MEMORY_COVERAGE_WARN_THRESHOLD,.7),Ge=t(process.env.OTTO_MEMORY_COVERAGE_ERROR_THRESHOLD,.5),Ke=t(process.env.OTTO_MEMORY_COVERAGE_FLOOR_THRESHOLD,.3),qe=[Ie,Le],R=[Re,ze,Be,Ve,He,Ue,`bash`,`bash_output`,`web_search`,`web_fetch`];function z(e){return{compaction:{enabled:!0,trigger:[`headroom`,Math.min(L,e.maxOutput)+je],keepRecent:[`fraction`,ke],reserveTokens:Math.min(L,e.maxOutput)},prune:{trigger:[`fraction`,Me],protect:[`fraction`,Ne],minimum:Pe,regenerableTools:[...R],protectedTools:[],truncateTools:[...qe],truncateMaxLength:Fe}}}const B={enabled:!0,trigger:[`headroom`,L+je],keepRecent:[`fraction`,ke],reserveTokens:L},V={trigger:[`fraction`,Me],protect:[`fraction`,Ne],minimum:Pe,regenerableTools:[...R],protectedTools:[],truncateTools:[...qe],truncateMaxLength:Fe,timeBased:{enabled:!1,gapThresholdMs:60*6e4,keepRecent:5}};function H(e,t){let[n,r]=e;switch(n){case`tokens`:return r;case`fraction`:return Math.floor(t*r);case`headroom`:{let e=Math.floor(t*.5);return Math.max(e,t-r)}}}const Je=e(`@x-otto/memory:prune`);function Ye(e,t,n={},r=M,i={}){let a={...V,...n},o=H(a.trigger,t),s=F(e,r),c=i.currentTokens??s,l=a.timeBased,u=l?.enabled===!0&&i.timeGapMs!==void 0&&i.timeGapMs>(l.gapThresholdMs??36e5);if(!i.force&&!u&&c<o)return{messages:[...e],prunedCount:0,tokensSaved:0,changed:!1};let d=u?Xe(e,a,r,l.keepRecent??5):Ze(e,a,t,r),f=s-F(d,r);if(f<a.minimum)return Je.debug(`Prune skipped: savings ${f} < minimum ${a.minimum}`),{messages:[...e],prunedCount:0,tokensSaved:0,changed:!1};let p=d.filter((t,n)=>t!==e[n]).length;return Je.info(`Pruned ${p} messages, saved ~${f} tokens`),{messages:d,prunedCount:p,tokensSaved:f,changed:!0}}function Xe(e,t,n,r){let i=new Set(t.regenerableTools),a=new Set(t.protectedTools),o=e=>{if(e.role!==`tool_result`)return!1;let t=e;return i.has(t.toolName)&&!a.has(t.toolName)},s=0,c=0;for(let t=e.length-1;t>=0;t--){let n=e[t];if(n&&o(n)&&(s++,s===r)){c=t;break}}return e.map((e,t)=>t>=c||!o(e)?e:Qe(e,n))}function Ze(e,t,n,r){let i=H(t.protect,n),a=new Set(t.truncateTools),o=new Set(t.regenerableTools),s=new Set(t.protectedTools),c=0,l=e.length;for(let t=e.length-1;t>=0;t--){let n=e[t],a=P(n,r);if(c+=a,c>=i){l=t+1;break}}return e.map((e,n)=>{if(n>=l)return e;if(e.role===`tool_result`){let t=e;return!o.has(t.toolName)||s.has(t.toolName)?e:Qe(t,r)}return e.role===`assistant`&&n<l?et(e,a,t.truncateMaxLength):e})}function Qe(e,t){let n=e.content.map(e=>e.type===`text`?e.text:``).join(``),r=t(n);if(r<100)return e;let i=n.split(`
172
+ `).find(e=>e.startsWith(`[tool result stored to `)),a=i?` ${i}`:``;return{...e,content:[{type:`text`,text:`[output pruned — ${r} tokens]${a}`}]}}function $e(e,t){let n=typeof t.path==`string`?t.path:void 0;if(n===void 0)return null;if(e===`write`)return{path:n,content:`[content omitted by context pruning — ${(typeof t.content==`string`?t.content:``).length} chars. The file was already written to disk; read it if needed.]`};if(e===`edit`){let e=typeof t.oldContent==`string`?t.oldContent:``,r=typeof t.newContent==`string`?t.newContent:``;return{path:n,oldContent:`[omitted by context pruning — ${e.length} chars]`,newContent:`[omitted by context pruning — ${r.length} chars. The edit was already applied to the file on disk; read it if needed.]`}}return null}function et(e,t,n){if(!Array.isArray(e.content))return e;let r=!1,i=e.content.map(e=>{if(e.type!==`tool_call`||!t.has(e.name)||JSON.stringify(e.arguments).length<=n)return e;let i=$e(e.name,e.arguments);return i===null?e:(r=!0,{...e,arguments:i})});return r?{...e,content:i}:e}function U(e){return`${e.role===`user`?`Human`:e.role===`assistant`?`Assistant`:e.role===`tool_result`?`Tool[result#${Ee(e.toolCallId)} ${e.toolName}]${e.isError?`{error}`:``}`:`System`}: ${N(e)}`}function W(e,t){if(e.length<=t)return e;let n=e=>`\n\n…[${e} chars omitted to fit the summarization window]…\n\n`,r=Math.max(0,t-n(e.length).length),i=Math.floor(r/2);if(i<=0)return n(e.length).slice(0,t);let a=e.length-i*2;return`${e.slice(0,i)}${n(a)}${e.slice(-i)}`}function tt(e,t,n){if(n(e)<=t)return e;let r=0,i=e.length,a=``;for(;r<=i;){let o=Math.floor((r+i)/2),s=W(e,o);n(s)<=t?(a=s,r=o+1):i=o-1}return a}function G(e){return e.role===`user`?0:e.role===`assistant`?1:e.role===`tool_result`?nt.has(e.toolName)?3:2:1}const nt=new Set(R),rt=4096;async function it(e,t){if(!t)return[...e];let n=!1,r=[];for(let i of e){if(i.role!==`tool_result`||G(i)!==3){r.push(i);continue}let e=i,a=N(i);if(a.length<4096){r.push(i);continue}let o=await t(e.toolCallId??``,a).catch(()=>void 0);o?(n=!0,r.push({...e,content:[{type:`text`,text:`[tool result stored to ${o} (${a.length} chars). Use the read tool on that path to recover the full output.]`}]})):r.push(i)}return n?r:[...e]}function at(e,t){let n=e.map(e=>({msg:e,prio:G(e),text:U(e)})),r=n.reduce((e,t)=>e+t.text.length,0);if(r<=t)return{texts:n.map(e=>e.text),sourceChars:r,fedChars:r,truncated:!1};let i=!1,a=e=>n.filter(t=>t.prio===e),o=a(0),s=a(1),c=a(2),l=a(3),u=e=>e.reduce((e,t)=>e+t.text.length,0),d=[{msgs:l,share:.1},{msgs:c,share:.2},{msgs:s,share:.35}],f=t;for(let{msgs:e,share:n}of d){if(e.length===0)continue;let r=u(e),a=Math.floor(t*n),o=Math.min(r,a);st(e,o),o<r&&(i=!0),f-=u(e)}o.length>0&&u(o)>f&&(st(o,Math.max(0,f)),i=!0);let p=n.reduce((e,t)=>e+t.text.length,0);return{texts:n.map(e=>e.text),sourceChars:r,fedChars:p,truncated:i}}function ot(e,t){if(e.length===0||t<=0)return{text:``,chars:0,truncated:!1};let n=at(e,t),r=n.texts.filter(e=>e.length>0).join(`
179
173
 
180
- `);return{text:r,chars:r.length,truncated:n.truncated}}function Je(e,t){if(e.length===0)return;let n=Math.max(0,Math.floor(t/e.length));for(let t of e)t.text.length>n&&(t.text=n<=0?``:W(t.text,n))}const G=new Map;function Ye(e){return G.get(e)??0}function Xe(e,t){G.set(e,t)}function Ze(e){let t=G.get(e)??0;return t>0&&G.delete(e),t}function Qe(e){G.delete(e)}const K=e(`@x-otto/memory:compaction`);function $e(e){return e.role===`user`&&N(e).startsWith(n)}function et(e){return e.startsWith(n)?e.slice(n.length).trimStart():e}const tt=new Set([Oe,ke,Ae,je,Me,Ne]),nt=new Set([Ee,De,`write_file`,`edit_file`,`replace_string_in_file`]);function rt(e,t,n=M){let r=0,i=0;for(let a=e.length-1;a>=0;a--){let o=P(e[a],n);if(r+=o,r>=t){i=a+1;break}}i=Math.max(0,Math.min(i,e.length-1));let a=i;for(;a<e.length;){let t=e[a];if(t.role===`user`||t.role===`assistant`)break;a++}let o=!1,s=a;if(a>0&&a<e.length&&e[a].role===`assistant`){let t=a-1;for(;t>=0&&e[t].role===`tool_result`;)t--;t>=0&&e[t].role===`assistant`&&(o=!0,s=t)}return{firstKeptIndex:a,turnStartIndex:s,isSplitTurn:o}}function it(e,t,n={},r=M){let i=H({...B,...n}.trigger,t);return F(e,r)>=i*.5}function at(e,t,n={},r,i=M,a){let o=H({...B,...n}.keepRecent,t),s=F(e,i);if(e.length<=2)return K.debug(`Too few messages to compact`),null;let c=rt(e,a?.force===!0?Math.max(1,Math.min(o,Math.floor(s/2))):o,i);if(c.firstKeptIndex<=0)return K.debug(`Cut point at beginning, nothing to compact`),null;let l=e.slice(0,c.firstKeptIndex),u=e.slice(c.firstKeptIndex),d=l.filter(e=>!$e(e));if(d.length===0)return K.debug(`Compaction window only contains prior summaries, nothing new to summarize`),null;let f=r;if(!f){for(let e=l.length-1;e>=0;e--)if($e(l[e])){f=et(N(l[e]));break}}let p=[];c.isSplitTurn&&(p=e.slice(c.turnStartIndex,c.firstKeptIndex));let m=ct(d);return{messagesToSummarize:[...d],turnPrefixMessages:p,preservedMessages:[...u],isSplitTurn:c.isSplitTurn,tokensBefore:s,previousSummary:f,fileOps:m}}function ot(e,t,n){let r=[],i=[],a=0;for(let o of e){let e=P(o,n);i.length>0&&a+e>t&&(r.push(i),i=[],a=0),i.push(o),a+=e}return i.length>0&&r.push(i),r}async function st(e,t,n={},r,i=M,a){let o={...B,...n};K.info(`Compacting ${e.messagesToSummarize.length} messages`);let s=o.contextWindow==null?0:i(S(``,void 0,o.customInstructions,{variant:`segment`})),c=o.contextWindow==null?void 0:Math.max(1,Math.floor(o.contextWindow*.6)-s),l=c==null?0:Math.floor(c*.2),u=c==null?void 0:c-l,d=c==null?{text:``,chars:0,truncated:!1}:qe(e.preservedMessages??[],l*4),f,p=1,m=u==null?[e.messagesToSummarize]:ot(e.messagesToSummarize,u,i);if(m.length<=1){let n=e.messagesToSummarize.map(U).join(`
174
+ `);return{text:r,chars:r.length,truncated:n.truncated}}function st(e,t){if(e.length===0)return;let n=Math.max(0,Math.floor(t/e.length));for(let t of e)t.text.length>n&&(t.text=n<=0?``:W(t.text,n))}const K=new Map;function ct(e){return K.get(e)??0}function lt(e,t){K.set(e,t)}function ut(e){let t=K.get(e)??0;return t>0&&K.delete(e),t}function dt(e){K.delete(e)}const q=e(`@x-otto/memory:compaction`);function ft(e){return e.role===`user`&&N(e).startsWith(n)}function pt(e){return e.startsWith(n)?e.slice(n.length).trimStart():e}const mt=new Set([Re,ze,Be,Ve,He,Ue]),ht=new Set([Ie,Le,`write_file`,`edit_file`,`replace_string_in_file`]);function gt(e,t,n=M){let r=0,i=0;for(let a=e.length-1;a>=0;a--){let o=P(e[a],n);if(r+=o,r>=t){i=a+1;break}}i=Math.max(0,Math.min(i,e.length-1));let a=i;for(;a<e.length;){let t=e[a];if(t.role===`user`||t.role===`assistant`)break;a++}let o=!1,s=a;if(a>0&&a<e.length&&e[a].role===`assistant`){let t=a-1;for(;t>=0&&e[t].role===`tool_result`;)t--;t>=0&&e[t].role===`assistant`&&(o=!0,s=t)}return{firstKeptIndex:a,turnStartIndex:s,isSplitTurn:o}}function _t(e,t,n={},r=M){let i=H({...B,...n}.trigger,t);return F(e,r)>=i*.5}function vt(e,t,n={},r,i=M,a){let o=H({...B,...n}.keepRecent,t),s=F(e,i);if(e.length<=2)return q.debug(`Too few messages to compact`),null;let c=gt(e,a?.force===!0?Math.max(1,Math.min(o,Math.floor(s/2))):o,i);if(c.firstKeptIndex<=0)return q.debug(`Cut point at beginning, nothing to compact`),null;let l=e.slice(0,c.firstKeptIndex),u=e.slice(c.firstKeptIndex),d=l.filter(e=>!ft(e));if(d.length===0)return q.debug(`Compaction window only contains prior summaries, nothing new to summarize`),null;let f=r;if(!f){for(let e=l.length-1;e>=0;e--)if(ft(l[e])){f=pt(N(l[e]));break}}let p=[];c.isSplitTurn&&(p=e.slice(c.turnStartIndex,c.firstKeptIndex));let m=xt(d);return{messagesToSummarize:[...d],turnPrefixMessages:p,preservedMessages:[...u],isSplitTurn:c.isSplitTurn,tokensBefore:s,previousSummary:f,fileOps:m}}function yt(e,t,n){let r=[],i=[],a=0;for(let o of e){let e=P(o,n);i.length>0&&a+e>t&&(r.push(i),i=[],a=0),i.push(o),a+=e}return i.length>0&&r.push(i),r}async function bt(e,t,n={},r,i=M,a,o){let s={...B,...n};q.info(`Compacting ${e.messagesToSummarize.length} messages`);let c=s.contextWindow==null?0:i(C(``,void 0,s.customInstructions,{variant:`segment`})),l=s.contextWindow==null?void 0:Math.max(1,Math.floor(s.contextWindow*.6)-c),u=l==null?0:Math.floor(l*.2),d=l==null?void 0:l-u,f=l==null?{text:``,chars:0,truncated:!1}:ot(e.preservedMessages??[],u*4),p,m=1,h=o?await it(e.messagesToSummarize,o):e.messagesToSummarize,g=d==null?[h]:yt(h,d,i);if(g.length<=1){let n=h.map(U).join(`
181
175
 
182
- `),i=e.preservedMessages?.length??0;f=await t(S(n,e.previousSummary,o.customInstructions,{variant:i>0?`preserved-tail`:`full`,preservedCount:i,...d.text?{preservedTailReference:d.text}:{}}),{maxTokens:o.reserveTokens,signal:r}),K.info({batches:1,sourceChars:n.length,fedChars:n.length,coverage:1,truncated:!1,preservedTailChars:d.chars,preservedTailTruncated:d.truncated},`[compaction] summarization coverage`)}else{let n=a??`__default__`,s=Ye(n),c=s>0?s:8;s>0&&Ze(n);let l=m.length<=c?m:[...m.slice(0,c-1),m.slice(c-1).flat()];K.info(`RFC-321 M6: summarization input exceeds window budget; map-reduce over ${l.length} batches`);let h=u*4,g=[],_=0,v=0,y=!1,b=[0,0,0,0],x=[0,0,0,0];for(let e of l){if(r?.aborted)break;let n=Ke(e,h),a=n.texts.join(`
176
+ `),i=e.preservedMessages?.length??0,o={variant:i>0?`preserved-tail`:`full`,preservedCount:i,...f.text?{preservedTailReference:f.text}:{}};p=s.prefixReuse?await t(C(n,e.previousSummary,s.customInstructions,o),{maxTokens:s.reserveTokens,signal:r,prefix:{sessionId:a,messages:h}}):await t(C(n,e.previousSummary,s.customInstructions,o),{maxTokens:s.reserveTokens,signal:r}),q.info({batches:1,sourceChars:n.length,fedChars:n.length,coverage:1,truncated:!1,preservedTailChars:f.chars,preservedTailTruncated:f.truncated},`[compaction] summarization coverage`)}else{let n=a??`__default__`,o=ct(n),c=o>0?o:8;o>0&&ut(n);let l=g.length<=c?g:[...g.slice(0,c-1),g.slice(c-1).flat()];q.info(`RFC-321 M6: summarization input exceeds window budget; map-reduce over ${l.length} batches`);let u=d*4,h=[],_=0,v=0,y=!1,b=[0,0,0,0],x=[0,0,0,0];for(let e of l){if(r?.aborted)break;let n=at(e,u),o=n.texts.join(`
183
177
 
184
- `),s=a;s.length>h&&(s=W(s,h)),s=Ue(s,u,i),_+=n.sourceChars,v+=Math.min(n.fedChars,s.length),(n.truncated||s.length<a.length)&&(y=!0),e.forEach((e,t)=>{let r=We(e);b[r]=(b[r]??0)+U(e).length,x[r]=(x[r]??0)+(n.texts[t]?.length??0)});let c=S(s,void 0,o.customInstructions,{variant:`segment`,...d.text?{preservedTailReference:d.text}:{}});g.push(await t(c,{maxTokens:o.reserveTokens,signal:r}))}f=await t(ee(W(g.map((e,t)=>`### Segment ${t+1}\n\n${e}`).join(`
178
+ `),c=o;c.length>u&&(c=W(c,u)),c=tt(c,d,i),_+=n.sourceChars,v+=Math.min(n.fedChars,c.length),(n.truncated||c.length<o.length)&&(y=!0),e.forEach((e,t)=>{let r=G(e);b[r]=(b[r]??0)+U(e).length,x[r]=(x[r]??0)+(n.texts[t]?.length??0)});let l=C(c,void 0,s.customInstructions,{variant:`segment`,...f.text?{preservedTailReference:f.text}:{}});h.push(await t(l,{maxTokens:s.reserveTokens,signal:r,...s.prefixReuse?{prefix:{sessionId:a}}:{}}))}p=await t(te(W(h.map((e,t)=>`### Segment ${t+1}\n\n${e}`).join(`
185
179
 
186
- `),h),e.previousSummary,o.customInstructions),{maxTokens:o.reserveTokens,signal:r});let C=_>0?v/_:1;p=C,K.info({batches:l.length,sourceChars:_,fedChars:v,coverage:Number(C.toFixed(4)),truncated:y,preservedTailChars:d.chars,preservedTailTruncated:d.truncated,byPriority:{p0:{src:b[0],fed:x[0]},p1:{src:b[1],fed:x[1]},p2:{src:b[2],fed:x[2]},p3:{src:b[3],fed:x[3]}}},`[compaction] summarization coverage`);let w=o.coverageWarnThreshold??Pe,T=Math.min(o.coverageErrorThreshold??Fe,w);C<T&&K.warn({coverage:Number(C.toFixed(4)),sourceChars:_,fedChars:v,batches:l.length,errorThreshold:T},`[compaction] low summarization coverage — large portion of history not represented in summary (RFC-321 C1)`),ut(C,l.length,a??`__default__`,T,w)}if(e.fileOps.read.length>0||e.fileOps.modified.length>0){let t=lt(e.fileOps);f.includes(`## File Operations`)||(f+=`\n\n${t}`)}if(e.isSplitTurn&&e.turnPrefixMessages.length>0){let n=await t(w(e.turnPrefixMessages.map(U).join(`
180
+ `),u),e.previousSummary,s.customInstructions),{maxTokens:s.reserveTokens,signal:r,...s.prefixReuse?{prefix:{sessionId:a}}:{}});let S=_>0?v/_:1;m=S,q.info({batches:l.length,sourceChars:_,fedChars:v,coverage:Number(S.toFixed(4)),truncated:y,preservedTailChars:f.chars,preservedTailTruncated:f.truncated,byPriority:{p0:{src:b[0],fed:x[0]},p1:{src:b[1],fed:x[1]},p2:{src:b[2],fed:x[2]},p3:{src:b[3],fed:x[3]}}},`[compaction] summarization coverage`);let ee=s.coverageWarnThreshold??We,w=Math.min(s.coverageErrorThreshold??Ge,ee);S<w&&q.warn({coverage:Number(S.toFixed(4)),sourceChars:_,fedChars:v,batches:l.length,errorThreshold:w},`[compaction] low summarization coverage — large portion of history not represented in summary (RFC-321 C1)`),Ct(S,l.length,a??`__default__`,w,ee)}if(e.fileOps.read.length>0||e.fileOps.modified.length>0){let t=St(e.fileOps);p.includes(`## File Operations`)||(p+=`\n\n${t}`)}if(e.isSplitTurn&&e.turnPrefixMessages.length>0){let n=await t(w(e.turnPrefixMessages.map(U).join(`
187
181
 
188
- `)),{signal:r});f=`${f}\n\n---\n\n${n}`}return K.info(`Compaction complete: summary ${f.length} chars`),{summary:f,firstKeptIndex:e.messagesToSummarize.length,tokensBefore:e.tokensBefore,coverage:p}}function ct(e){let t=new Set,n=new Set;for(let r of e){if(r.role!==`tool_result`)continue;let e=r.toolName,i=N(r).match(/(?:^|\s)(\/[^\s]+\.[a-z]+)/i);i?.[1]&&(tt.has(e)?t.add(i[1]):nt.has(e)&&n.add(i[1]))}return{read:[...t],modified:[...n]}}function lt(e){let t=[`## File Operations`];if(e.read.length>0){t.push(`### Read`);for(let n of e.read)t.push(`- ${n}`)}if(e.modified.length>0){t.push(`### Modified`);for(let n of e.modified)t.push(`- ${n}`)}return t.join(`
189
- `)}function ut(e,t,n,r,i){e<r?Xe(n,Math.max(t*2,16)):e<i&&Xe(n,t+2)}const dt=e(`@x-otto/memory:archive`);var ft=class{type=`memory`;archives=new Map;sessionIndex=new Map;async archive(e,t,n){let r=Date.now(),i=`memory://archives/${e}/compaction-${r}.md`,a=q(t,n,r);this.archives.set(i,{content:a,entry:{path:i,timestamp:r,summary:n.slice(0,200),messageCount:t.length}});let o=this.sessionIndex.get(e)??[];return o.push(i),this.sessionIndex.set(e,o),dt.info(`Archived ${t.length} messages for session ${e}`),i}async read(e){let t=this.archives.get(e);if(!t)throw Error(`Archive not found: ${e}`);return t.content}async list(e){return(this.sessionIndex.get(e)??[]).map(e=>this.archives.get(e)?.entry).filter(e=>e!==void 0)}forgetSession(e){let t=this.sessionIndex.get(e);if(t){for(let e of t)this.archives.delete(e);this.sessionIndex.delete(e)}}};function q(e,t,n){let r=[`## Compacted at ${new Date(n).toISOString()}`,``,`### Summary`,t,``,`### Original Messages (${e.length} messages)`];for(let t of e){let e=t.role===`user`?`Human`:t.role===`assistant`?`Assistant`:t.role===`tool_result`?`Tool[${t.toolName}]`:`System`,n=N(t),i=n.length>2e3?`${n.slice(0,2e3)}\n...(truncated, ${n.length} chars total)`:n;r.push(`\n**${e}**: ${i}`)}return r.join(`
190
- `)}function pt(e){if(e.role===`assistant`&&e.usage){let{usage:t,...n}=e;return n}return e}const J=e(`@x-otto/memory`);function Y(e){return e?.trim()||`__default__`}var mt=class{persistent;archiveStorage;loadSessionEntries;messageFromEntry;summarize;estimateTokens;rawConfig;contextWindow;getContextWindow;compactionConfig;pruneConfig;maxContentBytes;getMaxContentBytes;getEstimatedBytes;previousSummaries=new Map;archiveIdCounter=new Map;compactionInFlight=new Set;constructor(e){let t=e.model?z(e.model):{compaction:B,prune:V};this.compactionConfig={...t.compaction,...e.compaction},this.pruneConfig={...t.prune,...e.prune},this.contextWindow=e.model?.contextWindow??2e5,this.getContextWindow=e.getContextWindow,this.maxContentBytes=e.maxContentBytes,this.getMaxContentBytes=e.getMaxContentBytes,this.getEstimatedBytes=e.getEstimatedBytes,this.rawConfig=e,this.archiveStorage=e.archiveStorage??new ft,this.loadSessionEntries=e.loadSessionEntries,this.messageFromEntry=e.messageFromEntry,this.summarize=e.summarize,this.estimateTokens=e.estimateTokens??M,this.persistent=new ce(e.memoryStore??new O,e.sources)}clearSession(e){this.previousSummaries.delete(Y(e))}forgetSession(e){this.previousSummaries.delete(Y(e)),this.archiveIdCounter.delete(e),Qe(e),this.archiveStorage.forgetSession?.(e)}reconfigure(e){let t=z(e);this.contextWindow=e.contextWindow,this.compactionConfig={...t.compaction,...this.rawConfig.compaction},this.pruneConfig={...t.prune,...this.rawConfig.prune}}effectiveContextWindow(){return this.getContextWindow?.()??this.contextWindow}async ensureFitsWindow(e,t,n){if(!this.needsCompaction(e))return{messages:[...e],compacted:!1};let r=await this.process(e,t,n,{force:!0});return{messages:r.messages,compacted:r.compacted,summary:r.summary,replacement:r.replacement}}async loadMemory(){await this.persistent.load()}getMemoryPrompt(){return this.persistent.getInjection()}async reloadMemory(){await this.persistent.reload()}getMemories(){return this.persistent.getMemories()}needsPrune(e){return this.needsPruneWith(I(e,this.estimateTokens))}needsPruneWith(e){let t=H(this.pruneConfig.trigger,this.effectiveContextWindow());return e.bodyAfterPrefix>=t}prune(e,t=!1){return this.pruneWith(e,I(e,this.estimateTokens),t)}pruneWith(e,t,n){return ze(e,this.effectiveContextWindow(),this.pruneConfig,this.estimateTokens,{currentTokens:t.total,force:n})}needsCompaction(e){if(this.compactionConfig.enabled===!1)return!1;let t=this.effectiveContextWindow(),n=I(e,this.estimateTokens),r=H(this.compactionConfig.trigger,t),i=this.compactionConfig.hardCeilingFraction??be,a=Math.max(r,Math.floor(t*i));return!!(n.bodyAfterPrefix>=r||n.total>=a||this.residencyPressure())}residencyPressure(){if(this.getEstimatedBytes==null)return!1;let e=this.getMaxContentBytes?.()??this.maxContentBytes;if(e==null)return!1;let t=this.getEstimatedBytes();return t!=null&&t>=e*.9}isEligibleForManualCompact(e){return it(e,this.effectiveContextWindow(),this.compactionConfig,this.estimateTokens)}prepareCompaction(e,t,n){return at(e,this.effectiveContextWindow(),this.compactionConfig,this.previousSummaries.get(Y(t)),this.estimateTokens,n)}async compact(e,t,n){let r=await st(e,this.summarize,{...this.compactionConfig,contextWindow:this.effectiveContextWindow(),...(()=>{let e=this.rawConfig.getCoverageThresholds?.();return{...e?.warn===void 0?{}:{coverageWarnThreshold:e.warn},...e?.error===void 0?{}:{coverageErrorThreshold:e.error},...e?.floor===void 0?{}:{coverageFloorThreshold:e.floor}}})()},n,this.estimateTokens,t),i=Math.min(this.compactionConfig.coverageFloorThreshold??this.rawConfig.getCoverageThresholds?.()?.floor??Ie,this.compactionConfig.coverageErrorThreshold??Fe);if(r.coverage<i&&(J.warn({sessionId:t,coverage:Number(r.coverage.toFixed(4)),floorThreshold:i},`[compaction] coverage below floor — boundary established WITH an explicit low-fidelity notice; a large portion of history is not represented in this summary (RFC-340 D1)`),r.summary=`${oe(r.coverage)}\n\n${r.summary}`),t)try{await this.recordCompactionArchive(t,r,e.messagesToSummarize),J.info(`Compaction recorded at ${r.archivePath}`)}catch(e){J.warn({error:e},`Failed to record compaction archive`)}return this.previousSummaries.set(Y(t),r.summary),r}async recordCompactionArchive(e,t,n){let r=(this.archiveIdCounter.get(e)??0)+1;this.archiveIdCounter.set(e,r),t.archivePath=`archive:${e}:${r}`,this.loadSessionEntries||await this.archiveStorage.archive(e,n,t.summary)}async process(e,t,r,i){let a=[...e],o=!1,s=!1,c,l,u,d,f=i?.force===!0,p=i?.skipPrune===!0,m=I(a,this.estimateTokens);if(!p&&(f||this.needsPruneWith(m))){let e=a.length,t=this.pruneWith(a,m,f);t.changed&&(a=t.messages,o=!0,d={prunedCount:t.prunedCount,tokensSaved:t.tokensSaved,messagesBefore:e,messagesAfter:a.length},J.info(`Prune: removed ${t.prunedCount} tool outputs, saved ~${t.tokensSaved} tokens`))}let h=this.compactionConfig.enabled!==!1;if(f?h:this.needsCompaction(a)){let e=Y(t);if(this.compactionInFlight.has(e))J.info({sessionId:t},`Compaction already in flight for this session, skipping this round (will retry next round)`);else{this.compactionInFlight.add(e);try{let e=this.prepareCompaction(a,t,{force:f||this.residencyPressure()});if(e)try{let i=await this.compact(e,t,r);s=!0,c=i.summary,u=i.archivePath;let o={role:`user`,content:[{type:`text`,text:`${n}\n\n${u?ae(u,i.summary):i.summary}`}],timestamp:Date.now()};l=e.preservedMessages.map(pt),a=[o,...l],J.info(`Compaction: ${e.messagesToSummarize.length} messages → summary, kept ${e.preservedMessages.length}`)}catch(e){J.warn({error:e},`Compaction failed (LLM/summarizer error?), skipping compaction for this turn`)}}finally{this.compactionInFlight.delete(e)}}}return{messages:a,summary:c,replacement:l,archivePath:u,pruned:o,compacted:s,pruneMetrics:d}}async listArchives(e){if(this.loadSessionEntries&&this.messageFromEntry)try{return await this.listArchivesFromEntries(e)}catch(e){J.warn({error:e},`listArchives from session entries failed, falling back`)}return this.archiveStorage.list(e)}async listArchivesFromEntries(e){let t=await this.loadSessionEntries(e),n=this.messageFromEntry,r=[],i=0;for(let a=0;a<t.length;a++){let o=t[a];if(o.type!==`compaction`)continue;let s=o.data;i++;let c=0,l=0;for(let e=a-1;e>=0;e--){let r=t[e];if(r.type===`compaction`)break;let i=n(r);i&&(c++,l=i.timestamp??l)}r.push({path:`archive:${e}:${i}`,timestamp:l||o.seq||0,messageCount:s.compactedCount,summary:s.summary.slice(0,200)})}return r}async readArchive(e){if(this.loadSessionEntries&&this.messageFromEntry)try{let t=await this.readArchiveFromEntries(e);if(t)return t}catch(e){J.warn({error:e},`readArchive from session entries failed, falling back`)}return this.archiveStorage.read(e)}async readArchiveFromEntries(e){let t=e.match(/^archive:(.+):(\d+)$/);if(!t)return null;let n=t[1],r=parseInt(t[2],10),i=await this.loadSessionEntries(n),a=this.messageFromEntry,o=0,s=-1;for(let e=0;e<i.length;e++)if(i[e].type===`compaction`&&(o++,o===r)){s=e;break}if(s<0)return null;let c=i[s].data,l=-1;for(let e=s-1;e>=0;e--)if(i[e].type===`compaction`){l=e;break}let u=l+1,d=[];for(let e=u;e<s;e++){let t=a(i[e]);t&&d.push(t)}let f=Date.now();return q(d.filter(e=>e!==null),c.summary,f)}dispose(){this.persistent.dispose()}};function ht(e){return new mt(e)}var gt=class{baseUrl;userId;workspaceKeyGetter;isWorkspaceSyncEnabled;getAuth;fetchImpl;timeoutMs;local;onConflict;conflictCounters=new Map;knownVersions=new Map;constructor(e){this.baseUrl=e.baseUrl.replace(/\/+$/,``),this.userId=e.userId,this.workspaceKeyGetter=e.workspaceKey,this.isWorkspaceSyncEnabled=e.isWorkspaceSyncEnabled??(()=>!1),this.getAuth=e.getAuth,this.fetchImpl=e.fetch,this.timeoutMs=e.timeoutMs,this.local=new O(e.localDir,e.homeDirOverride),this.onConflict=e.onConflict}async load(e){let t=new Map,n=[];for(let r of e)if(this.userId&&_t(r.path)){let e=await this.loadRemoteUser(r.path);e===null?n.push(r):(t.set(r.path,e),this.local.write(r.path,e).catch(()=>{}))}else if(this.isWorkspaceSourceEnabled(r.path)){let e=await this.loadRemoteWorkspace(r.path);e===null?n.push(r):(t.set(r.path,e),this.local.write(r.path,e).catch(()=>{}))}else n.push(r);if(n.length>0){let e=await this.local.load(n);for(let[n,r]of e)t.set(n,r)}return t}async write(e,t){await this.local.write(e,t),this.userId&&_t(e)?this.writeRemoteUser(e,t).catch(()=>{}):this.isWorkspaceSourceEnabled(e)&&await this.writeRemoteWorkspace(e,t)}watch(e,t){return()=>{}}isWorkspaceSourceEnabled(e){return vt(e)&&this.isWorkspaceSyncEnabled()&&!!this.workspaceKeyGetter?.()}remoteKey(e){let t=e.startsWith(`~/.otto/`)?e.slice(8):e;return`user:${this.userId}:${t}`}remoteResourceId(e){let t=e.startsWith(`~/.otto/`)?e.slice(8):e;return encodeURIComponent(t)}workspaceResourceId(e){let t=e.startsWith(`./.otto/`)?e.slice(8):e;return encodeURIComponent(t)}async loadRemoteUser(e){try{let t={Authorization:`Bearer ${(await this.getAuth()).token}`,Accept:`application/json`,"X-Otto-User-Id":this.userId},n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs);try{let r=await this.fetchImpl(`${this.baseUrl}/memory/user/${this.remoteResourceId(e)}`,{method:`GET`,headers:t,signal:n.signal});if(r.ok){let e=await r.json();if(typeof e.content==`string`)return e.content}if(r.status===404){let r=await this.fetchImpl(`${this.baseUrl}/sessions/${encodeURIComponent(this.remoteKey(e))}`,{method:`GET`,headers:t,signal:n.signal});if(r.ok){let e=await r.json();if(typeof e.content==`string`)return e.content}}}finally{clearTimeout(r)}}catch{}return null}async writeRemoteUser(e,t){try{let n={Authorization:`Bearer ${(await this.getAuth()).token}`,"Content-Type":`application/json`,"X-Otto-User-Id":this.userId},r=new AbortController,i=setTimeout(()=>r.abort(),this.timeoutMs);try{await this.fetchImpl(`${this.baseUrl}/memory/user/${this.remoteResourceId(e)}`,{method:`PUT`,headers:n,body:JSON.stringify({content:t}),signal:r.signal})}finally{clearTimeout(i)}}catch{}}async loadRemoteWorkspace(e){let t=this.workspaceKeyGetter?.();if(!t)return null;try{let n={Authorization:`Bearer ${(await this.getAuth()).token}`,Accept:`application/json`},r=new AbortController,i=setTimeout(()=>r.abort(),this.timeoutMs);try{let i=await this.fetchImpl(`${this.baseUrl}/memory/workspace/${encodeURIComponent(t)}/${this.workspaceResourceId(e)}`,{method:`GET`,headers:n,signal:r.signal});if(i.ok){let t=await i.json();if(typeof t.content==`string`)return typeof t.version==`number`&&this.knownVersions.set(e,t.version),t.content}}finally{clearTimeout(i)}}catch{}return null}async writeRemoteWorkspace(e,t){let n=this.workspaceKeyGetter?.();if(n)try{let r={Authorization:`Bearer ${(await this.getAuth()).token}`,"Content-Type":`application/json`},i=new AbortController,a=setTimeout(()=>i.abort(),this.timeoutMs),o=this.knownVersions.get(e)??0,s=o+1;try{let a=await this.fetchImpl(`${this.baseUrl}/memory/workspace/${encodeURIComponent(n)}/${this.workspaceResourceId(e)}`,{method:`PUT`,headers:r,body:JSON.stringify({content:t,version:s,expectedVersion:o}),signal:i.signal});if(a.status===409){await this.writeConflictSidecar(e,t);return}a.ok&&this.knownVersions.set(e,s)}finally{clearTimeout(a)}}catch{}}async writeConflictSidecar(e,t){let n=this.conflictCounters.get(e)??0,r=n%5;this.conflictCounters.set(e,n+1);let i=`${e}.conflict-${r}`;try{await this.local.write(i,t),this.onConflict?.(e,i)}catch{}}};function _t(e){return e===`~/.otto/AGENTS.md`||e===`~/.otto/MEMORY.md`||e.startsWith(`~/.otto/memory/`)}function vt(e){return e===`./.otto/MEMORY.md`||e.startsWith(`./.otto/memory/`)&&!e.startsWith(`./.otto/local/`)||e===`./.otto/lessons.json`}function yt(e){let t=new Map;return{async load(n){let r=n.filter(e=>!t.has(e.path)),i=r.length>0?await e.load(r):new Map;for(let e of n){let n=t.get(e.path);n!==void 0&&i.set(e.path,n)}return i},async write(e,n){t.set(e,n)}}}function bt(e,t={}){let n=t.fileName??`AGENTS.md`,a=t.rootMarkers??[`.git`],s=t.maxDepth??25,l=t.maxFiles??10,u=t.maxFileBytes??64*1024,d=[],f=e;for(let e=0;e<s;e++){let e=c(f,n);try{r(e)&&i(e).size<=u&&d.push(e)}catch{}let t=a.some(e=>r(c(f,e))),s=o(f);if(t||s===f)break;f=s}return d.reverse().slice(-l).map(e=>({path:e,writable:!1}))}const xt=e(`@x-otto/memory:auto`),X=`# MEMORY`,St=/^- \[(.+?)\]\(memory\/(.+?)\.md\)/,Z=`./.otto/AGENTS.md`;function Ct(e,t,n){return`<!-- otto-memory:start slug="${e}" -->\n## ${t.replace(/[\r\n]+/g,` `).trim()}\n\n${n.trim()}\n<!-- otto-memory:end slug="${e}" -->`}function wt(e){return RegExp(`<!-- otto-memory:start slug="${e}" -->[\\s\\S]*?<!-- otto-memory:end slug="${e}" -->`)}var Tt=class{store;baseDir;localBaseDir;index=``;localIndex=``;loaded=!1;localLoaded=!1;constructor(e){this.store=e.store,this.baseDir=(e.baseDir??`./.otto`).replace(/\/+$/,``),this.localBaseDir=(e.localBaseDir??`${this.baseDir}/local`).replace(/\/+$/,``)}get indexPath(){return`${this.baseDir}/MEMORY.md`}entryPathOf(e){return`${this.baseDir}/memory/${e}.md`}get localIndexPath(){return`${this.localBaseDir}/MEMORY.md`}localEntryPathOf(e){return`${this.localBaseDir}/memory/${e}.md`}asSource(){return{path:this.indexPath,writable:!0}}asLocalSource(){return{path:this.localIndexPath,writable:!0}}async load(){this.index=(await this.store.load([this.asSource()])).get(this.indexPath)??``,this.loaded=!0}async loadLocal(){this.localIndex=(await this.store.load([this.asLocalSource()])).get(this.localIndexPath)??``,this.localLoaded=!0}getInjection(){let e=this.index.trim();return e?`${e}\n\n> Auto-memory entries live in the memory store (\`${this.baseDir}/memory/\`), not workspace files — read them with the memory_read tool; record new durable facts with memory_record.`:``}getLocalInjection(){let e=this.localIndex.trim();return e?`${e}\n\n> Machine-specific memory entries live in \`${this.localBaseDir}/memory/\` — never synced to other devices. Read with memory_read; record with memory_record (scope: 'local').`:``}async record(e,t=`workspace`){if(t===`local`){await this.recordLocal(e);return}if(t===`project-shared`){await this.recordProjectShared(e);return}await this.ensureLoaded();let n=this.resolveSlug(e.name,this.indexLines());await this.store.write(this.entryPathOf(n),`# ${e.name}\n\n${e.content}\n`);let r=`- [${e.name}](memory/${n}.md) — ${e.description}`,i=this.indexLines().filter(e=>!e.includes(`(memory/${n}.md)`));i.some(e=>e.startsWith(X))||i.unshift(X),i.push(r),this.index=`${i.join(`
191
- `)}\n`,await this.store.write(this.indexPath,this.index),xt.info(`Auto-memory recorded: ${n}`)}async recordLocal(e){await this.ensureLocalLoaded();let t=this.resolveSlug(e.name,this.localIndexLines());await this.store.write(this.localEntryPathOf(t),`# ${e.name}\n\n${e.content}\n`);let n=`- [${e.name}](memory/${t}.md) — ${e.description}`,r=this.localIndexLines().filter(e=>!e.includes(`(memory/${t}.md)`));r.some(e=>e.startsWith(X))||r.unshift(X),r.push(n),this.localIndex=`${r.join(`
192
- `)}\n`,await this.store.write(this.localIndexPath,this.localIndex),xt.info(`Auto-memory recorded (local scope): ${t}`)}async recordProjectShared(e){let t=(await this.store.load([{path:Z,writable:!0}])).get(Z)??``,n=e.name.replace(/[\r\n]+/g,` `).trim(),r=RegExp(`<!-- otto-memory:start slug="([^"]+)" -->\\n## ${Et(n)}\\n`).exec(t)?.[1]??Q(e.name),i=Ct(r,e.name,e.content),a=wt(r),o;o=a.test(t)?t.replace(a,i):t.trim()===``?`# 项目记忆(模型经 memory_record(scope: "project-shared") 写入,人工可编辑本文件任意内容)
193
- \n${i}\n`:`${t.trimEnd()}\n\n${i}\n`,await this.store.write(Z,o),xt.info(`Auto-memory recorded (project-shared scope): ${r}`)}async read(e){await this.ensureLoaded();let t=this.slugByName(e,this.indexLines())??Q(e),n=this.entryPathOf(t),r=await this.readEntry(n);if(r!==null)return await this.withConflictLabel(n,r);await this.ensureLocalLoaded();let i=this.slugByName(e,this.localIndexLines())??Q(e),a=this.localEntryPathOf(i),o=await this.readEntry(a);return o===null?null:await this.withConflictLabel(a,o)}async withConflictLabel(e,t){for(let n=0;n<5;n++){let r=`${e}.conflict-${n}`;if(await this.readEntry(r)!==null)return`[CONFLICT] Concurrent edit detected for "${e}" — review "${r}" and reconcile manually.\n\n${t}`}return t}async readEntry(e){let t=(await this.store.load([{path:e,writable:!1}])).get(e);return t&&t.trim()!==``?t:null}async forget(e){await this.ensureLoaded(),await this.ensureLocalLoaded();let t=this.slugByName(e,this.indexLines())??Q(e);this.index=`${this.indexLines().filter(e=>!e.includes(`(memory/${t}.md)`)).join(`
182
+ `)),{signal:r,...s.prefixReuse?{prefix:{sessionId:a}}:{}});p=`${p}\n\n---\n\n${n}`}return q.info(`Compaction complete: summary ${p.length} chars`),{summary:p,firstKeptIndex:e.messagesToSummarize.length,tokensBefore:e.tokensBefore,coverage:m}}function xt(e){let t=new Set,n=new Set;for(let r of e){if(r.role!==`tool_result`)continue;let e=r.toolName,i=N(r).match(/(?:^|\s)(\/[^\s]+\.[a-z]+)/i);i?.[1]&&(mt.has(e)?t.add(i[1]):ht.has(e)&&n.add(i[1]))}return{read:[...t],modified:[...n]}}function St(e){let t=[`## File Operations`];if(e.read.length>0){t.push(`### Read`);for(let n of e.read)t.push(`- ${n}`)}if(e.modified.length>0){t.push(`### Modified`);for(let n of e.modified)t.push(`- ${n}`)}return t.join(`
183
+ `)}function Ct(e,t,n,r,i){e<r?lt(n,Math.max(t*2,16)):e<i&&lt(n,t+2)}const wt=e(`@x-otto/memory:archive`);var Tt=class{type=`memory`;archives=new Map;sessionIndex=new Map;async archive(e,t,n){let r=Date.now(),i=`memory://archives/${e}/compaction-${r}.md`,a=J(t,n,r);this.archives.set(i,{content:a,entry:{path:i,timestamp:r,summary:n.slice(0,200),messageCount:t.length}});let o=this.sessionIndex.get(e)??[];return o.push(i),this.sessionIndex.set(e,o),wt.info(`Archived ${t.length} messages for session ${e}`),i}async read(e){let t=this.archives.get(e);if(!t)throw Error(`Archive not found: ${e}`);return t.content}async list(e){return(this.sessionIndex.get(e)??[]).map(e=>this.archives.get(e)?.entry).filter(e=>e!==void 0)}forgetSession(e){let t=this.sessionIndex.get(e);if(t){for(let e of t)this.archives.delete(e);this.sessionIndex.delete(e)}}};function J(e,t,n){let r=[`## Compacted at ${new Date(n).toISOString()}`,``,`### Summary`,t,``,`### Original Messages (${e.length} messages)`];for(let t of e){let e=t.role===`user`?`Human`:t.role===`assistant`?`Assistant`:t.role===`tool_result`?`Tool[${t.toolName}]`:`System`,n=N(t),i=n.length>2e3?`${n.slice(0,2e3)}\n...(truncated, ${n.length} chars total)`:n;r.push(`\n**${e}**: ${i}`)}return r.join(`
184
+ `)}function Et(e){if(e.role===`assistant`&&e.usage){let{usage:t,...n}=e;return n}return e}const Y=e(`@x-otto/memory`),Dt=`__default__`;function X(e){return e?.trim()||Dt}var Ot=class{persistent;archiveStorage;loadSessionEntries;messageFromEntry;summarize;estimateTokens;rawConfig;contextWindow;getContextWindow;compactionConfig;pruneConfig;maxContentBytes;getMaxContentBytes;getEstimatedBytes;previousSummaries=new Map;archiveIdCounter=new Map;compactionInFlight=new Set;compactionLock;compactionSpill;clock;constructor(e){let t=e.model?z(e.model):{compaction:B,prune:V};this.compactionConfig={...t.compaction,...e.compaction},this.pruneConfig={...t.prune,...e.prune},this.contextWindow=e.model?.contextWindow??2e5,this.getContextWindow=e.getContextWindow,this.maxContentBytes=e.maxContentBytes,this.getMaxContentBytes=e.getMaxContentBytes,this.getEstimatedBytes=e.getEstimatedBytes,this.rawConfig=e,this.archiveStorage=e.archiveStorage??new Tt,this.loadSessionEntries=e.loadSessionEntries,this.messageFromEntry=e.messageFromEntry,this.summarize=e.summarize,this.estimateTokens=e.estimateTokens??M,this.compactionLock=e.compactionLock,this.compactionSpill=e.compactionSpill,this.clock=e.clock??{now:()=>Date.now()},this.persistent=new ve(e.memoryStore??new O,e.sources,e.entryMtimes)}clearSession(e){this.previousSummaries.delete(X(e))}forgetSession(e){this.previousSummaries.delete(X(e)),this.archiveIdCounter.delete(e),dt(e),this.archiveStorage.forgetSession?.(e)}reconfigure(e){let t=z(e);this.contextWindow=e.contextWindow,this.compactionConfig={...t.compaction,...this.rawConfig.compaction},this.pruneConfig={...t.prune,...this.rawConfig.prune}}effectiveContextWindow(){return this.getContextWindow?.()??this.contextWindow}async ensureFitsWindow(e,t,n){if(!this.needsCompaction(e))return{messages:[...e],compacted:!1};let r=await this.process(e,t,n,{force:!0});return{messages:r.messages,compacted:r.compacted,summary:r.summary,replacement:r.replacement}}async loadMemory(){await this.persistent.load()}getMemoryPrompt(){return this.persistent.getInjection()}async reloadMemory(){await this.persistent.reload()}getMemories(){return this.persistent.getMemories()}needsPrune(e){return this.needsPruneWith(I(e,this.estimateTokens),e)}needsPruneWith(e,t){let n=H(this.pruneConfig.trigger,this.effectiveContextWindow());return e.bodyAfterPrefix>=n?!0:t!==void 0&&this.timeGapTriggered(t)}timeGapTriggered(e){let t=this.pruneConfig.timeBased;if(t?.enabled!==!0)return!1;let n;for(let t=e.length-1;t>=0;t--){let r=e[t];if(r&&`timestamp`in r&&typeof r.timestamp==`number`){n=r.timestamp;break}}return n===void 0?!1:this.clock.now()-n>(t.gapThresholdMs??60*6e4)}prune(e,t=!1){return this.pruneWith(e,I(e,this.estimateTokens),t)}pruneWith(e,t,n){let r;if(this.pruneConfig.timeBased?.enabled===!0){let t;for(let n=e.length-1;n>=0;n--){let r=e[n];if(r&&`timestamp`in r&&typeof r.timestamp==`number`){t=r.timestamp;break}}t!==void 0&&(r=this.clock.now()-t)}return Ye(e,this.effectiveContextWindow(),this.pruneConfig,this.estimateTokens,{currentTokens:t.total,force:n,timeGapMs:r})}needsCompaction(e){if(this.compactionConfig.enabled===!1)return!1;let t=this.effectiveContextWindow(),n=I(e,this.estimateTokens),r=H(this.compactionConfig.trigger,t),i=this.compactionConfig.hardCeilingFraction??Ae,a=Math.max(r,Math.floor(t*i));return!!(n.bodyAfterPrefix>=r||n.total>=a||this.residencyPressure())}residencyPressure(){if(this.getEstimatedBytes==null)return!1;let e=this.getMaxContentBytes?.()??this.maxContentBytes;if(e==null)return!1;let t=this.getEstimatedBytes();return t!=null&&t>=e*.9}isEligibleForManualCompact(e){return _t(e,this.effectiveContextWindow(),this.compactionConfig,this.estimateTokens)}prepareCompaction(e,t,n){return vt(e,this.effectiveContextWindow(),this.compactionConfig,this.previousSummaries.get(X(t)),this.estimateTokens,n)}async compact(e,t,n){let r=await bt(e,this.summarize,{...this.compactionConfig,contextWindow:this.effectiveContextWindow(),...(()=>{let e=this.rawConfig.getCoverageThresholds?.();return{...e?.warn===void 0?{}:{coverageWarnThreshold:e.warn},...e?.error===void 0?{}:{coverageErrorThreshold:e.error},...e?.floor===void 0?{}:{coverageFloorThreshold:e.floor}}})()},n,this.estimateTokens,t,this.compactionSpill),i=Math.min(this.compactionConfig.coverageFloorThreshold??this.rawConfig.getCoverageThresholds?.()?.floor??Ke,this.compactionConfig.coverageErrorThreshold??Ge);if(r.coverage<i&&(Y.warn({sessionId:t,coverage:Number(r.coverage.toFixed(4)),floorThreshold:i},`[compaction] coverage below floor — boundary established WITH an explicit low-fidelity notice; a large portion of history is not represented in this summary (RFC-340 D1)`),r.summary=`${le(r.coverage)}\n\n${r.summary}`),t)try{await this.recordCompactionArchive(t,r,e.messagesToSummarize),Y.info(`Compaction recorded at ${r.archivePath}`)}catch(e){Y.warn({error:e},`Failed to record compaction archive`)}return this.previousSummaries.set(X(t),r.summary),r}async recordCompactionArchive(e,t,n){let r=(this.archiveIdCounter.get(e)??0)+1;this.archiveIdCounter.set(e,r),t.archivePath=`archive:${e}:${r}`,this.loadSessionEntries||await this.archiveStorage.archive(e,n,t.summary)}async process(e,t,r,i){let a=[...e],o=!1,s=!1,c,l,u,d,f=i?.force===!0,p=i?.skipPrune===!0,m=I(a,this.estimateTokens);if(!p&&(f||this.needsPruneWith(m,a))){let e=a.length,t=this.pruneWith(a,m,f);t.changed&&(a=t.messages,o=!0,d={prunedCount:t.prunedCount,tokensSaved:t.tokensSaved,messagesBefore:e,messagesAfter:a.length},Y.info(`Prune: removed ${t.prunedCount} tool outputs, saved ~${t.tokensSaved} tokens`))}let h=this.compactionConfig.enabled!==!1;if(f?h:this.needsCompaction(a)){let e=X(t),i=`${process.pid}:${this.clock.now()}`;if(!(this.compactionLock?await this.compactionLock.acquire(t??Dt,i):!this.compactionInFlight.has(e)))Y.info({sessionId:t},`Compaction already in flight for this session, skipping this round (will retry next round)`);else{this.compactionLock||this.compactionInFlight.add(e);try{let e=this.prepareCompaction(a,t,{force:f||this.residencyPressure()});if(e)try{let i=await this.compact(e,t,r);s=!0,c=i.summary,u=i.archivePath;let o={role:`user`,content:[{type:`text`,text:`${n}\n\n${u?ce(u,i.summary):i.summary}`}],timestamp:this.clock.now()};l=e.preservedMessages.map(Et),a=[o,...l],Y.info({sessionId:t,trigger:f?`force`:this.residencyPressure()?`residency`:`token`},`Compaction: ${e.messagesToSummarize.length} messages → summary, kept ${e.preservedMessages.length}`)}catch(e){Y.warn({error:e},`Compaction failed (LLM/summarizer error?), skipping compaction for this turn`)}}finally{this.compactionLock?await this.compactionLock.release(t??Dt,i).catch(()=>{}):this.compactionInFlight.delete(e)}}}return{messages:a,summary:c,replacement:l,archivePath:u,pruned:o,compacted:s,pruneMetrics:d}}async listArchives(e){if(this.loadSessionEntries&&this.messageFromEntry)try{return await this.listArchivesFromEntries(e)}catch(e){Y.warn({error:e},`listArchives from session entries failed, falling back`)}return this.archiveStorage.list(e)}async listArchivesFromEntries(e){let t=await this.loadSessionEntries(e),n=this.messageFromEntry,r=[],i=0;for(let a=0;a<t.length;a++){let o=t[a];if(o.type!==`compaction`)continue;let s=o.data;i++;let c=0,l=0;for(let e=a-1;e>=0;e--){let r=t[e];if(r.type===`compaction`)break;let i=n(r);i&&(c++,l=i.timestamp??l)}r.push({path:`archive:${e}:${i}`,timestamp:l||o.seq||0,messageCount:s.compactedCount,summary:s.summary.slice(0,200)})}return r}async readArchive(e){if(this.loadSessionEntries&&this.messageFromEntry)try{let t=await this.readArchiveFromEntries(e);if(t)return t}catch(e){Y.warn({error:e},`readArchive from session entries failed, falling back`)}return this.archiveStorage.read(e)}async readArchiveFromEntries(e){let t=e.match(/^archive:(.+):(\d+)$/);if(!t)return null;let n=t[1],r=parseInt(t[2],10),i=await this.loadSessionEntries(n),a=this.messageFromEntry,o=0,s=-1;for(let e=0;e<i.length;e++)if(i[e].type===`compaction`&&(o++,o===r)){s=e;break}if(s<0)return null;let c=i[s].data,l=-1;for(let e=s-1;e>=0;e--)if(i[e].type===`compaction`){l=e;break}let u=l+1,d=[];for(let e=u;e<s;e++){let t=a(i[e]);t&&d.push(t)}let f=this.clock.now();return J(d.filter(e=>e!==null),c.summary,f)}dispose(){this.persistent.dispose()}};function kt(e){return new Ot(e)}var At=class{baseUrl;userId;workspaceKeyGetter;isWorkspaceSyncEnabled;getAuth;fetchImpl;timeoutMs;local;onConflict;conflictCounters=new Map;knownVersions=new Map;constructor(e){this.baseUrl=e.baseUrl.replace(/\/+$/,``),this.userId=e.userId,this.workspaceKeyGetter=e.workspaceKey,this.isWorkspaceSyncEnabled=e.isWorkspaceSyncEnabled??(()=>!1),this.getAuth=e.getAuth,this.fetchImpl=e.fetch,this.timeoutMs=e.timeoutMs,this.local=new O(e.localDir,e.homeDirOverride),this.onConflict=e.onConflict}async load(e){let t=new Map,n=[];for(let r of e)if(this.userId&&jt(r.path)){let e=await this.loadRemoteUser(r.path);e===null?n.push(r):(t.set(r.path,e),this.local.write(r.path,e).catch(()=>{}))}else if(this.isWorkspaceSourceEnabled(r.path)){let e=await this.loadRemoteWorkspace(r.path);e===null?n.push(r):(t.set(r.path,e),this.local.write(r.path,e).catch(()=>{}))}else n.push(r);if(n.length>0){let e=await this.local.load(n);for(let[n,r]of e)t.set(n,r)}return t}async write(e,t){await this.local.write(e,t),this.userId&&jt(e)?this.writeRemoteUser(e,t).catch(()=>{}):this.isWorkspaceSourceEnabled(e)&&await this.writeRemoteWorkspace(e,t)}watch(e,t){return()=>{}}isWorkspaceSourceEnabled(e){return Mt(e)&&this.isWorkspaceSyncEnabled()&&!!this.workspaceKeyGetter?.()}remoteKey(e){let t=e.startsWith(`~/.otto/`)?e.slice(8):e;return`user:${this.userId}:${t}`}remoteResourceId(e){let t=e.startsWith(`~/.otto/`)?e.slice(8):e;return encodeURIComponent(t)}workspaceResourceId(e){let t=e.startsWith(`./.otto/`)?e.slice(8):e;return encodeURIComponent(t)}async loadRemoteUser(e){try{let t={Authorization:`Bearer ${(await this.getAuth()).token}`,Accept:`application/json`,"X-Otto-User-Id":this.userId},n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs);try{let r=await this.fetchImpl(`${this.baseUrl}/memory/user/${this.remoteResourceId(e)}`,{method:`GET`,headers:t,signal:n.signal});if(r.ok){let e=await r.json();if(typeof e.content==`string`)return e.content}if(r.status===404){let r=await this.fetchImpl(`${this.baseUrl}/sessions/${encodeURIComponent(this.remoteKey(e))}`,{method:`GET`,headers:t,signal:n.signal});if(r.ok){let e=await r.json();if(typeof e.content==`string`)return e.content}}}finally{clearTimeout(r)}}catch{}return null}async writeRemoteUser(e,t){try{let n={Authorization:`Bearer ${(await this.getAuth()).token}`,"Content-Type":`application/json`,"X-Otto-User-Id":this.userId},r=new AbortController,i=setTimeout(()=>r.abort(),this.timeoutMs);try{await this.fetchImpl(`${this.baseUrl}/memory/user/${this.remoteResourceId(e)}`,{method:`PUT`,headers:n,body:JSON.stringify({content:t}),signal:r.signal})}finally{clearTimeout(i)}}catch{}}async loadRemoteWorkspace(e){let t=this.workspaceKeyGetter?.();if(!t)return null;try{let n={Authorization:`Bearer ${(await this.getAuth()).token}`,Accept:`application/json`},r=new AbortController,i=setTimeout(()=>r.abort(),this.timeoutMs);try{let i=await this.fetchImpl(`${this.baseUrl}/memory/workspace/${encodeURIComponent(t)}/${this.workspaceResourceId(e)}`,{method:`GET`,headers:n,signal:r.signal});if(i.ok){let t=await i.json();if(typeof t.content==`string`)return typeof t.version==`number`&&this.knownVersions.set(e,t.version),t.content}}finally{clearTimeout(i)}}catch{}return null}async writeRemoteWorkspace(e,t){let n=this.workspaceKeyGetter?.();if(n)try{let r={Authorization:`Bearer ${(await this.getAuth()).token}`,"Content-Type":`application/json`},i=new AbortController,a=setTimeout(()=>i.abort(),this.timeoutMs),o=this.knownVersions.get(e)??0,s=o+1;try{let a=await this.fetchImpl(`${this.baseUrl}/memory/workspace/${encodeURIComponent(n)}/${this.workspaceResourceId(e)}`,{method:`PUT`,headers:r,body:JSON.stringify({content:t,version:s,expectedVersion:o}),signal:i.signal});if(a.status===409){await this.writeConflictSidecar(e,t);return}a.ok&&this.knownVersions.set(e,s)}finally{clearTimeout(a)}}catch{}}async writeConflictSidecar(e,t){let n=this.conflictCounters.get(e)??0,r=n%5;this.conflictCounters.set(e,n+1);let i=`${e}.conflict-${r}`;try{await this.local.write(i,t),this.onConflict?.(e,i)}catch{}}};function jt(e){return e===`~/.otto/AGENTS.md`||e===`~/.otto/MEMORY.md`||e.startsWith(`~/.otto/memory/`)}function Mt(e){return e===`./.otto/MEMORY.md`||e.startsWith(`./.otto/memory/`)&&!e.startsWith(`./.otto/local/`)||e===`./.otto/lessons.json`}function Nt(e){let t=new Map;return{async load(n){let r=n.filter(e=>!t.has(e.path)),i=r.length>0?await e.load(r):new Map;for(let e of n){let n=t.get(e.path);n!==void 0&&i.set(e.path,n)}return i},async write(e,n){t.set(e,n)}}}function Pt(e,t={}){let n=t.fileName??`AGENTS.md`,a=t.rootMarkers??[`.git`],s=t.maxDepth??25,l=t.maxFiles??10,u=t.maxFileBytes??64*1024,d=[],f=e;for(let e=0;e<s;e++){let e=c(f,n);try{r(e)&&i(e).size<=u&&d.push(e)}catch{}let t=a.some(e=>r(c(f,e))),s=o(f);if(t||s===f)break;f=s}return d.reverse().slice(-l).map(e=>({path:e,writable:!1}))}const Ft=e(`@x-otto/memory:auto`),Z=`# MEMORY`,It=/^- \[(.+?)\]\(memory\/(.+?)\.md\)/,Lt=`./.otto/AGENTS.md`;function Rt(e,t,n){return`<!-- otto-memory:start slug="${e}" -->\n## ${t.replace(/[\r\n]+/g,` `).trim()}\n\n${n.trim()}\n<!-- otto-memory:end slug="${e}" -->`}function zt(e){return RegExp(`<!-- otto-memory:start slug="${e}" -->[\\s\\S]*?<!-- otto-memory:end slug="${e}" -->`)}var Bt=class{store;baseDir;localBaseDir;index=``;localIndex=``;loaded=!1;localLoaded=!1;constructor(e){this.store=e.store,this.baseDir=(e.baseDir??`./.otto`).replace(/\/+$/,``),this.localBaseDir=(e.localBaseDir??`${this.baseDir}/local`).replace(/\/+$/,``)}get indexPath(){return`${this.baseDir}/MEMORY.md`}entryPathOf(e){return`${this.baseDir}/memory/${e}.md`}get localIndexPath(){return`${this.localBaseDir}/MEMORY.md`}localEntryPathOf(e){return`${this.localBaseDir}/memory/${e}.md`}asSource(){return{path:this.indexPath,writable:!0}}asLocalSource(){return{path:this.localIndexPath,writable:!0}}async load(){this.index=(await this.store.load([this.asSource()])).get(this.indexPath)??``,this.loaded=!0}async loadLocal(){this.localIndex=(await this.store.load([this.asLocalSource()])).get(this.localIndexPath)??``,this.localLoaded=!0}getInjection(){let e=this.index.trim();return e?`${e}\n\n> Auto-memory entries live in the memory store (\`${this.baseDir}/memory/\`), not workspace files — read them with the memory_read tool; record new durable facts with memory_record.`:``}getLocalInjection(){let e=this.localIndex.trim();return e?`${e}\n\n> Machine-specific memory entries live in \`${this.localBaseDir}/memory/\` — never synced to other devices. Read with memory_read; record with memory_record (scope: 'local').`:``}async record(e,t=`workspace`){if(t===`local`){await this.recordLocal(e);return}if(t===`project-shared`){await this.recordProjectShared(e);return}await this.ensureLoaded();let n=this.resolveSlug(e.name,this.indexLines());await this.store.write(this.entryPathOf(n),`# ${e.name}\n\n${e.content}\n`);let r=`- [${e.name}](memory/${n}.md) — ${e.description}`,i=this.indexLines().filter(e=>!e.includes(`(memory/${n}.md)`));i.some(e=>e.startsWith(Z))||i.unshift(Z),i.push(r),this.index=`${i.join(`
185
+ `)}\n`,await this.store.write(this.indexPath,this.index),Ft.info(`Auto-memory recorded: ${n}`)}async recordLocal(e){await this.ensureLocalLoaded();let t=this.resolveSlug(e.name,this.localIndexLines());await this.store.write(this.localEntryPathOf(t),`# ${e.name}\n\n${e.content}\n`);let n=`- [${e.name}](memory/${t}.md) — ${e.description}`,r=this.localIndexLines().filter(e=>!e.includes(`(memory/${t}.md)`));r.some(e=>e.startsWith(Z))||r.unshift(Z),r.push(n),this.localIndex=`${r.join(`
186
+ `)}\n`,await this.store.write(this.localIndexPath,this.localIndex),Ft.info(`Auto-memory recorded (local scope): ${t}`)}async recordProjectShared(e){let t=(await this.store.load([{path:Lt,writable:!0}])).get(Lt)??``,n=e.name.replace(/[\r\n]+/g,` `).trim(),r=RegExp(`<!-- otto-memory:start slug="([^"]+)" -->\\n## ${Vt(n)}\\n`).exec(t)?.[1]??Q(e.name),i=Rt(r,e.name,e.content),a=zt(r),o;o=a.test(t)?t.replace(a,i):t.trim()===``?`# 项目记忆(模型经 memory_record(scope: "project-shared") 写入,人工可编辑本文件任意内容)
187
+ \n${i}\n`:`${t.trimEnd()}\n\n${i}\n`,await this.store.write(Lt,o),Ft.info(`Auto-memory recorded (project-shared scope): ${r}`)}async read(e){await this.ensureLoaded();let t=this.slugByName(e,this.indexLines())??Q(e),n=this.entryPathOf(t),r=await this.readEntry(n);if(r!==null)return await this.withConflictLabel(n,r);await this.ensureLocalLoaded();let i=this.slugByName(e,this.localIndexLines())??Q(e),a=this.localEntryPathOf(i),o=await this.readEntry(a);return o===null?null:await this.withConflictLabel(a,o)}async withConflictLabel(e,t){for(let n=0;n<5;n++){let r=`${e}.conflict-${n}`;if(await this.readEntry(r)!==null)return`[CONFLICT] Concurrent edit detected for "${e}" — review "${r}" and reconcile manually.\n\n${t}`}return t}async readEntry(e){let t=(await this.store.load([{path:e,writable:!1}])).get(e);return t&&t.trim()!==``?t:null}async forget(e){await this.ensureLoaded(),await this.ensureLocalLoaded();let t=this.slugByName(e,this.indexLines())??Q(e);this.index=`${this.indexLines().filter(e=>!e.includes(`(memory/${t}.md)`)).join(`
194
188
  `)}\n`,await this.store.write(this.indexPath,this.index),await this.store.write(this.entryPathOf(t),``);let n=this.slugByName(e,this.localIndexLines());n&&(this.localIndex=`${this.localIndexLines().filter(e=>!e.includes(`(memory/${n}.md)`)).join(`
195
189
  `)}\n`,await this.store.write(this.localIndexPath,this.localIndex),await this.store.write(this.localEntryPathOf(n),``))}async ensureLoaded(){this.loaded||await this.load()}async ensureLocalLoaded(){this.localLoaded||await this.loadLocal()}indexLines(){return this.index.split(`
196
190
  `).filter(e=>e.trim()!==``)}localIndexLines(){return this.localIndex.split(`
197
- `).filter(e=>e.trim()!==``)}slugByName(e,t){for(let n of t){let t=St.exec(n);if(t&&t[1]===e)return t[2]??null}return null}resolveSlug(e,t){let n=this.slugByName(e,t);if(n)return n;let r=new Map;for(let e of t){let t=St.exec(e);t&&t[1]!==void 0&&t[2]!==void 0&&r.set(t[2],t[1])}let i=Q(e),a=i,o=2;for(;r.has(a)&&r.get(a)!==e;)a=`${i}-${o}`,o+=1;return a}};function Q(e){return e.toLowerCase().replace(/[^a-z0-9一-鿿]+/g,`-`).replace(/^-+|-+$/g,``)}function Et(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Dt=e(`@x-otto/memory:extractor`);async function Ot(e,t,n,r={}){let i=At(e);if(!i.trim())return[];let a;try{a=await n(ie(i),{maxTokens:r.maxTokens??2048,signal:r.signal})}catch(e){return Dt.debug(`extraction LLM call failed (fail-closed → []): ${String(e)}`),[]}let o=jt(a);if(!o)return[];let s=r.maxLessons??5,c=[];for(let e of o){let n=Mt(e,t);if(n&&c.push(n),c.length>=s)break}return c}async function kt(e,t,n={}){let r=[],i=0;for(let a of t){let{lesson:t,deduped:o}=await e.saveDeduped(a,n.similarityThreshold);o?i++:r.push(t)}return{saved:r,skippedSimilar:i}}function At(e){return e.map(e=>`${e.role===`user`?`Human`:e.role===`assistant`?`Assistant`:e.role===`tool_result`?`Tool[${e.toolName??`unknown`}]`:`System`}: ${N(e)}`).join(`
191
+ `).filter(e=>e.trim()!==``)}slugByName(e,t){for(let n of t){let t=It.exec(n);if(t&&t[1]===e)return t[2]??null}return null}resolveSlug(e,t){let n=this.slugByName(e,t);if(n)return n;let r=new Map;for(let e of t){let t=It.exec(e);t&&t[1]!==void 0&&t[2]!==void 0&&r.set(t[2],t[1])}let i=Q(e),a=i,o=2;for(;r.has(a)&&r.get(a)!==e;)a=`${i}-${o}`,o+=1;return a}};function Q(e){return e.toLowerCase().replace(/[^a-z0-9一-鿿]+/g,`-`).replace(/^-+|-+$/g,``)}function Vt(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const Ht=e(`@x-otto/memory:extractor`);async function Ut(e,t,n,r={}){let i=Gt(e);if(!i.trim())return[];let a;try{a=await n(se(i),{maxTokens:r.maxTokens??2048,signal:r.signal})}catch(e){return Ht.debug(`extraction LLM call failed (fail-closed → []): ${String(e)}`),[]}let o=Kt(a);if(!o)return[];let s=r.maxLessons??5,c=[];for(let e of o){let n=qt(e,t);if(n&&c.push(n),c.length>=s)break}return c}async function Wt(e,t,n={}){let r=[],i=0;for(let a of t){let{lesson:t,deduped:o}=await e.saveDeduped(a,n.similarityThreshold);o?i++:r.push(t)}return{saved:r,skippedSimilar:i}}function Gt(e){return e.map(e=>`${e.role===`user`?`Human`:e.role===`assistant`?`Assistant`:e.role===`tool_result`?`Tool[${e.toolName??`unknown`}]`:`System`}: ${N(e)}`).join(`
198
192
 
199
- `)}function jt(e){let t=e.indexOf(`[`),n=e.lastIndexOf(`]`);if(t===-1||n===-1||n<t)return null;try{let r=JSON.parse(e.slice(t,n+1));return Array.isArray(r)?r:null}catch{return null}}function Mt(e,t){if(!e||typeof e!=`object`)return null;let n=typeof e.trigger==`string`?e.trigger.trim():``,r=typeof e.insight==`string`?e.insight.trim():``;if(!n||!r)return null;let i=Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`&&e.trim().length>0).map(e=>e.trim().toLowerCase()).slice(0,5):[];return{trigger:n.slice(0,500),insight:r.slice(0,2e3),tags:i,sourceSessionId:t}}const Nt=e(`@x-otto/memory:auto-extract`);function Pt(e){return e===`learn`||e===`memory_record`}function Ft(e,t){for(let n of e)if(n.role===`assistant`){for(let e of n.content)if(e.type===`tool_call`&&t(e.name))return!0}return!1}function It(e){let t=new Map,n=e.isMemoryWriteTool??Pt,r=()=>Math.max(1,e.config.everyNTurns??1);function i(e){let n=t.get(e);return n||(n={lastExtractedIndex:0,idleCount:0},t.set(e,n)),n}let a=(e=0,t=0,n)=>({ran:n===void 0,reason:n,saved:e,skippedSimilar:t});return{async onIdle(t,o,s){if(!e.config.enabled)return a(0,0,`disabled`);let c=i(t),l=o.slice(c.lastExtractedIndex);if(l.length===0)return a(0,0,`no-new-messages`);if(c.idleCount+=1,c.idleCount<r())return a(0,0,`throttled`);let u=()=>{c.lastExtractedIndex=o.length,c.idleCount=0};if(Ft(l,n))return u(),a(0,0,`main-agent-wrote`);try{let n=await Ot(l,t,e.llm,{maxTokens:e.config.maxTokens,maxLessons:e.config.maxLessons,signal:s?.signal}),r=await kt(e.store,n,{similarityThreshold:e.config.similarityThreshold});return u(),n.length>0&&Nt.info({sessionId:t,extracted:n.length,saved:r.saved.length,deduped:r.skippedSimilar},`memory-auto-extract`),a(r.saved.length,r.skippedSimilar)}catch(e){return Nt.warn(`auto-extract failed (fail-closed): ${String(e)}`),a(0,0,`error`)}},reset(e){t.delete(e)}}}const Lt=new Set([`node_modules`,`.git`,`dist`,`.next`,`.turbo`,`coverage`,`.cache`,`__pycache__`,`.vscode`]);var Rt=class{lastSnapshot=null;async capture(e){let[t,n]=await Promise.all([this.buildDirectoryTree(e.workspaceDir,0),this.checkRecentFiles(e.workspaceDir,e.recentFiles??[])]),r=[];n.length>0&&r.push(`${n.length} recently modified file(s) detected`);let i={timestamp:Date.now(),directoryTree:t,modifiedFiles:n,planStatus:e.planStatus,findings:r};return this.lastSnapshot=i,i}getLastSnapshot(){return this.lastSnapshot}formatSnapshot(e){let t=[`[File State Snapshot @ ${new Date(e.timestamp).toISOString()}]`];if(e.directoryTree&&t.push(`Directory:\n${e.directoryTree}`),e.modifiedFiles.length>0){let n=e.modifiedFiles.map(e=>` ${e.action}: ${e.path} — ${e.summary}`).join(`
193
+ `)}function Kt(e){let t=e.indexOf(`[`),n=e.lastIndexOf(`]`);if(t===-1||n===-1||n<t)return null;try{let r=JSON.parse(e.slice(t,n+1));return Array.isArray(r)?r:null}catch{return null}}function qt(e,t){if(!e||typeof e!=`object`)return null;let n=typeof e.trigger==`string`?e.trigger.trim():``,r=typeof e.insight==`string`?e.insight.trim():``;if(!n||!r)return null;let i=Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`&&e.trim().length>0).map(e=>e.trim().toLowerCase()).slice(0,5):[];return{trigger:n.slice(0,500),insight:r.slice(0,2e3),tags:i,sourceSessionId:t}}const $=e(`@x-otto/memory:auto-extract`);function Jt(e){return e===`learn`||e===`memory_record`}function Yt(e,t){for(let n of e)if(n.role===`assistant`){for(let e of n.content)if(e.type===`tool_call`&&t(e.name))return!0}return!1}function Xt(e){let t=new Map,n=e.isMemoryWriteTool??Jt,r=()=>Math.max(1,e.config.everyNTurns??1);function i(e){let n=t.get(e);return n||(n={lastExtractedIndex:0,idleCount:0},t.set(e,n)),n}let a=(e=0,t=0,n)=>({ran:n===void 0,reason:n,saved:e,skippedSimilar:t});return{async onIdle(t,o,s){if(!e.config.enabled)return a(0,0,`disabled`);let c=i(t),l=o.slice(c.lastExtractedIndex);if(l.length===0)return a(0,0,`no-new-messages`);if(c.idleCount+=1,c.idleCount<r())return a(0,0,`throttled`);let u=e.config.minRelevantHits;if(u!==void 0&&(e.readRelevantHits?await e.readRelevantHits():-1)<u)return e.readRelevantHits||$.warn(`auto-extract minRelevantHits=${u} set but readRelevantHits port missing — failing closed (no extraction) to avoid bypassing the configured threshold`),a(0,0,`below-relevant-hits-low-water`);let d=()=>{c.lastExtractedIndex=o.length,c.idleCount=0};if(Yt(l,n))return d(),a(0,0,`main-agent-wrote`);try{let n=await Ut(l,t,e.llm,{maxTokens:e.config.maxTokens,maxLessons:e.config.maxLessons,signal:s?.signal}),r=await Wt(e.store,n,{similarityThreshold:e.config.similarityThreshold});return d(),n.length>0&&$.info({sessionId:t,extracted:n.length,saved:r.saved.length,deduped:r.skippedSimilar},`memory-auto-extract`),a(r.saved.length,r.skippedSimilar)}catch(e){return $.warn(`auto-extract failed (fail-closed): ${String(e)}`),a(0,0,`error`)}},reset(e){t.delete(e)}}}const Zt=new Set([`node_modules`,`.git`,`dist`,`.next`,`.turbo`,`coverage`,`.cache`,`__pycache__`,`.vscode`]);var Qt=class{lastSnapshot=null;async capture(e){let[t,n]=await Promise.all([this.buildDirectoryTree(e.workspaceDir,0),this.checkRecentFiles(e.workspaceDir,e.recentFiles??[])]),r=[];n.length>0&&r.push(`${n.length} recently modified file(s) detected`);let i={timestamp:Date.now(),directoryTree:t,modifiedFiles:n,planStatus:e.planStatus,findings:r};return this.lastSnapshot=i,i}getLastSnapshot(){return this.lastSnapshot}formatSnapshot(e){let t=[`[File State Snapshot @ ${new Date(e.timestamp).toISOString()}]`];if(e.directoryTree&&t.push(`Directory:\n${e.directoryTree}`),e.modifiedFiles.length>0){let n=e.modifiedFiles.map(e=>` ${e.action}: ${e.path} — ${e.summary}`).join(`
200
194
  `);t.push(`Modified Files:\n${n}`)}return e.planStatus&&t.push(`Plan Status: ${e.planStatus}`),e.findings.length>0&&t.push(`Findings:\n${e.findings.map(e=>` - ${e}`).join(`
201
195
  `)}`),t.join(`
202
196
 
203
- `)}async buildDirectoryTree(e,t,n=``){if(t>=4)return n?`${n}...\n`:``;let r;try{r=(await u(e,{withFileTypes:!0})).filter(e=>!Lt.has(e.name)&&!e.name.startsWith(`.`)).sort((e,t)=>e.isDirectory()===t.isDirectory()?e.name.localeCompare(t.name):e.isDirectory()?-1:1).slice(0,50).map(e=>e.name+(e.isDirectory()?`/`:``))}catch{return``}let i=[];for(let a of r){let r=a.endsWith(`/`);if(i.push(`${n}${a}`),r){let r=await this.buildDirectoryTree(c(e,a.slice(0,-1)),t+1,n+` `);r&&i.push(r.trimEnd())}}return i.join(`
204
- `)}async checkRecentFiles(e,t){let n=[],r=Date.now();for(let i of t){let t=i.startsWith(`/`)?i:c(e,i),a=l(e,t);try{let e=await d(t),i=r-e.mtimeMs,o=e.birthtimeMs>0&&e.mtimeMs-e.birthtimeMs<1e3?`created`:`modified`,s=`${(e.size/1024).toFixed(1)}KB, modified ${zt(i)} ago`;n.push({path:a,action:o,summary:s})}catch{n.push({path:a,action:`deleted`,summary:`file not found`})}}return n}};function zt(e){let t=Math.floor(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);return n<60?`${n}m`:`${Math.floor(n/60)}h`}const Bt=.6;function Vt(e){return new Set(e.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(e=>e.length>2))}function Ht(e,t){if(e.size===0||t.size===0)return 0;let n=0;for(let r of e)t.has(r)&&n++;return n/(e.size+t.size-n)}const Ut=new Set([1,2]);function Wt(e){let t=`[${e.tags.join(`, `)}] ${e.trigger} → ${e.insight}`,n=0;for(let e of t)/[\u3000-\u9fff\uff00-\uffef]/.test(e)&&n++;return Math.ceil(n+(t.length-n)/4)}function Gt(e){let t=JSON.stringify({tags:[...e.tags].sort(),trigger:e.trigger,insight:e.insight}),n=2166136261;for(let e=0;e<t.length;e++)n^=t.charCodeAt(e),n=Math.imul(n,16777619)>>>0;return n.toString(16).padStart(8,`0`)}function $(e,t){let n=e.relevantHitCount??e.appliedCount??0;return{...e,scope:e.scope??t,state:e.state??`active`,contentHash:e.contentHash??Gt(e),estimatedTokens:e.estimatedTokens??Wt(e),relevantHitCount:n,coreInjectedCount:e.coreInjectedCount??0,hitCountTotal:e.hitCountTotal??n,appliedCount:n}}function Kt(e){if(!e||typeof e!=`object`)return;let t=e;if(t.version!==void 0&&Ut.has(t.version)&&t.data)return t.data;let n=e;if(n.version===void 0&&Array.isArray(n.lessons))return e}var qt=class{lessons=new Map;nextId=1;loaded=!1;snapshotId;persistence;store;storePath;logger;scope;ephemeral;constructor(e){if(this.logger=e?.logger,this.scope=e?.scope,this.ephemeral=e?.ephemeral??!1,e?.store)this.store=e.store,this.storePath=e.storePath??e.path;else if(e?.path){let t=s(e.path)||`.json`;this.snapshotId=a(e.path,t),this.persistence=f({type:`file`,baseDir:o(e.path),extension:t})}}async save(e){await this.ensureLoaded();let t=`lesson_${this.nextId++}`,n=$({...e,id:t,createdAt:Date.now(),appliedCount:0},this.scope);return this.lessons.set(t,n),await this.persist(),n}async search(e,t=5){await this.ensureLoaded();let n=e.toLowerCase(),r=[];for(let e of this.lessons.values()){let t=0;e.trigger.toLowerCase().includes(n)&&(t+=3),e.insight.toLowerCase().includes(n)&&(t+=2);for(let r of e.tags)r.toLowerCase().includes(n)&&(t+=1);t>0&&r.push({lesson:e,score:t})}let i=r.sort((e,t)=>t.score-e.score).slice(0,t).map(e=>(e.lesson.relevantHitCount=(e.lesson.relevantHitCount??0)+1,e.lesson.hitCountTotal=(e.lesson.hitCountTotal??0)+1,e.lesson.lastAccessedAt=Date.now(),e.lesson.appliedCount=e.lesson.relevantHitCount,e.lesson));return await this.persist(),i}async findSimilar(e,t,n=Bt){await this.ensureLoaded();let r=Vt(`${e} ${t}`);if(r.size===0)return;let i;for(let e of this.lessons.values()){let t=Ht(r,Vt(`${e.trigger} ${e.insight}`));t>=n&&(!i||t>i.similarity)&&(i={lesson:e,similarity:t})}return i}async saveDeduped(e,t=Bt){let n=await this.findSimilar(e.trigger,e.insight,t);return n?{lesson:n.lesson,deduped:!0}:{lesson:await this.save(e),deduped:!1}}async list(e){await this.ensureLoaded();let t=[...this.lessons.values()];if(e?.tags&&e.tags.length>0){let n=new Set(e.tags);t=t.filter(e=>e.tags.some(e=>n.has(e)))}if(e?.query){let n=e.query.toLowerCase();t=t.filter(e=>e.trigger.toLowerCase().includes(n)||e.insight.toLowerCase().includes(n))}return t}async get(e){return await this.ensureLoaded(),this.lessons.get(e)}async recordRelevantHits(e,t=Date.now()){await this.ensureLoaded();let n=!1;for(let r of e){let e=this.lessons.get(r);e&&(e.relevantHitCount=(e.relevantHitCount??0)+1,e.hitCountTotal=(e.hitCountTotal??0)+1,e.lastAccessedAt=t,e.appliedCount=e.relevantHitCount,n=!0)}n&&await this.persist()}async delete(e){await this.ensureLoaded();let t=this.lessons.delete(e);return t&&await this.persist(),t}get size(){return this.lessons.size}async ensureLoaded(){if(!this.loaded){if(this.store&&this.storePath){await this.loadFromStore(),this.loaded=!0;return}if(!this.persistence||!this.snapshotId){this.loaded=!0;return}try{let e=Kt(await this.persistence.load(this.snapshotId));if(e){for(let t of e.lessons)this.lessons.set(t.id,$(t,this.scope));this.nextId=e.nextId??this.lessons.size+1}}catch(e){this.logger?.warn(`Failed to load lessons from file persistence (snapshotId=${this.snapshotId}): ${String(e)}. Falling back to empty lessons — all previously learned lessons will be lost for this session.`)}this.loaded=!0}}async refresh(){!this.store||!this.storePath||(await this.loadFromStore(),this.loaded=!0)}async loadFromStore(){try{let e=(await this.store.load([{path:this.storePath,writable:!0}])).get(this.storePath);if(!e)return;let t=Kt(JSON.parse(e));if(t){for(let e of t.lessons)this.lessons.set(e.id,$(e,this.scope));this.nextId=t.nextId??this.lessons.size+1}}catch(e){this.logger?.warn(`Failed to load lessons from MemoryStore (storePath=${this.storePath}): ${String(e)}. Falling back to empty lessons — all previously learned lessons will be lost for this session.`)}}async persist(){if(this.ephemeral)return;if(this.store&&this.storePath){let e={lessons:[...this.lessons.values()],nextId:this.nextId};await this.store.write(this.storePath,JSON.stringify({version:2,data:e}));return}if(!this.persistence||!this.snapshotId)return;let e=Date.now();await p(this.persistence,this.snapshotId,{lessons:[...this.lessons.values()],nextId:this.nextId},{version:2,tags:[`operational-learning`],now:e,preserveCreatedAt:!1})}};export{Tt as AutoMemory,B as DEFAULT_COMPACTION_CONFIG,T as DEFAULT_MEMORY_INJECTION_BUDGET_TOKENS,se as DEFAULT_MEMORY_SOURCES,V as DEFAULT_PRUNE_CONFIG,Bt as DEFAULT_SIMILARITY_THRESHOLD,Rt as FileStateManager,O as FileSystemMemoryStore,gt as HttpMemoryStore,fe as IMAGE_BLOCK_TOKEN_ESTIMATE,ft as InMemoryArchiveStorage,le as InMemoryMemoryStore,re as LESSON_EXTRACTION_PROMPT,mt as MemoryManager,qt as OperationalLearningStore,ce as PersistentMemory,x as SEGMENT_MERGE_PROMPT,b as SEGMENT_SUMMARIZATION_PROMPT,m as SUMMARIZATION_PROMPT,g as TURN_PREFIX_SUMMARIZATION_PROMPT,h as UPDATE_SUMMARIZATION_PROMPT,y as WITH_PRESERVED_TAIL_PROMPT,ae as buildArchiveReference,ie as buildExtractionPrompt,oe as buildLowCoverageNotice,ne as buildMemoryInjection,ee as buildSegmentMergePrompt,S as buildSummarizationPrompt,w as buildTurnPrefixPrompt,Gt as computeLessonContentHash,z as computeMemoryDefaults,It as createMemoryAutoExtractor,ht as createMemoryManager,yt as createOverlayMemoryStore,bt as discoverAgentsSources,de as ensureTokenizer,Wt as estimateLessonTokens,P as estimateMessageTokens,F as estimateMessagesTokens,M as estimateTokens,j as estimateTokensHeuristic,Ot as extractLessons,q as formatArchive,N as messageToText,ze as prune,H as resolveContextSize,kt as saveExtractedLessons};
197
+ `)}async buildDirectoryTree(e,t,n=``){if(t>=4)return n?`${n}...\n`:``;let r;try{r=(await u(e,{withFileTypes:!0})).filter(e=>!Zt.has(e.name)&&!e.name.startsWith(`.`)).sort((e,t)=>e.isDirectory()===t.isDirectory()?e.name.localeCompare(t.name):e.isDirectory()?-1:1).slice(0,50).map(e=>e.name+(e.isDirectory()?`/`:``))}catch{return``}let i=[];for(let a of r){let r=a.endsWith(`/`);if(i.push(`${n}${a}`),r){let r=await this.buildDirectoryTree(c(e,a.slice(0,-1)),t+1,n+` `);r&&i.push(r.trimEnd())}}return i.join(`
198
+ `)}async checkRecentFiles(e,t){let n=[],r=Date.now();for(let i of t){let t=i.startsWith(`/`)?i:c(e,i),a=l(e,t);try{let e=await d(t),i=r-e.mtimeMs,o=e.birthtimeMs>0&&e.mtimeMs-e.birthtimeMs<1e3?`created`:`modified`,s=`${(e.size/1024).toFixed(1)}KB, modified ${$t(i)} ago`;n.push({path:a,action:o,summary:s})}catch{n.push({path:a,action:`deleted`,summary:`file not found`})}}return n}};function $t(e){let t=Math.floor(e/1e3);if(t<60)return`${t}s`;let n=Math.floor(t/60);return n<60?`${n}m`:`${Math.floor(n/60)}h`}var en=class{records=[];now;persist;logWarn;constructor(e={}){this.now=e.now??(()=>Date.now()),this.persist=e.persist,this.logWarn=e.logWarn}append(e){let t={...e,at:this.now()};this.records.push(t),this.persist&&this.persist.append(t).catch(e=>{this.logWarn?.(`Failed to persist evolution signal`,e)})}projectRelevantHits(e){return e.map(e=>({kind:`relevant.hits`,key:e.id,value:e.relevantHitCount??0}))}appendRelevantHits(e){for(let t of this.projectRelevantHits(e))this.append(t)}list(){return this.records}counts(){let e=new Map;for(let t of this.records){let n=`${t.kind}:${t.key}`,r=e.get(n)??{kind:t.kind,key:t.key,total:0,records:0,lastAt:t.at};r.total+=t.value,r.records+=1,r.lastAt=t.at,e.set(n,r)}return[...e.values()]}async restoreFromLog(){if(!this.persist)return 0;try{let e=await this.persist.load();return this.records.length=0,this.records.push(...e),e.length}catch(e){return this.logWarn?.(`Failed to restore evolution signal log; starting empty`,e),0}}async close(){await this.persist?.close?.()}get size(){return this.records.length}};const tn=`signals`,nn={encode:e=>JSON.stringify(e),decode:e=>JSON.parse(e)};function rn(e){let t=new f({baseDir:e,extension:`.jsonl`,codec:nn});return{async append(e){return t.append(tn,e)},load:async()=>{let e=[];for await(let{entry:n}of t.read(tn))e.push(n);return e},async close(){await t.close?.()}}}const an=.6;function on(e){return new Set(e.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(e=>e.length>2))}function sn(e,t){if(e.size===0||t.size===0)return 0;let n=0;for(let r of e)t.has(r)&&n++;return n/(e.size+t.size-n)}const cn=new Set([1,2]);function ln(e){let t=`[${e.tags.join(`, `)}] ${e.trigger} → ${e.insight}`,n=0;for(let e of t)/[\u3000-\u9fff\uff00-\uffef]/.test(e)&&n++;return Math.ceil(n+(t.length-n)/4)}function un(e){let t=JSON.stringify({tags:[...e.tags].sort(),trigger:e.trigger,insight:e.insight}),n=2166136261;for(let e=0;e<t.length;e++)n^=t.charCodeAt(e),n=Math.imul(n,16777619)>>>0;return n.toString(16).padStart(8,`0`)}function dn(e,t){let n=e.relevantHitCount??e.appliedCount??0;return{...e,scope:e.scope??t,state:e.state??`active`,contentHash:e.contentHash??un(e),estimatedTokens:e.estimatedTokens??ln(e),relevantHitCount:n,coreInjectedCount:e.coreInjectedCount??0,hitCountTotal:e.hitCountTotal??n,appliedCount:n}}function fn(e){if(!e||typeof e!=`object`)return;let t=e;if(t.version!==void 0&&cn.has(t.version)&&t.data)return t.data;let n=e;if(n.version===void 0&&Array.isArray(n.lessons))return e}var pn=class{lessons=new Map;nextId=1;loaded=!1;snapshotId;persistence;store;storePath;logger;scope;ephemeral;constructor(e){if(this.logger=e?.logger,this.scope=e?.scope,this.ephemeral=e?.ephemeral??!1,e?.store)this.store=e.store,this.storePath=e.storePath??e.path;else if(e?.path){let t=s(e.path)||`.json`;this.snapshotId=a(e.path,t),this.persistence=p({type:`file`,baseDir:o(e.path),extension:t})}}async save(e){await this.ensureLoaded();let t=`lesson_${this.nextId++}`,n=dn({...e,id:t,createdAt:Date.now(),appliedCount:0},this.scope);return this.lessons.set(t,n),await this.persist(),n}async search(e,t=5){await this.ensureLoaded();let n=e.toLowerCase(),r=[];for(let e of this.lessons.values()){let t=0;e.trigger.toLowerCase().includes(n)&&(t+=3),e.insight.toLowerCase().includes(n)&&(t+=2);for(let r of e.tags)r.toLowerCase().includes(n)&&(t+=1);t>0&&r.push({lesson:e,score:t})}let i=r.sort((e,t)=>t.score-e.score).slice(0,t).map(e=>(e.lesson.relevantHitCount=(e.lesson.relevantHitCount??0)+1,e.lesson.hitCountTotal=(e.lesson.hitCountTotal??0)+1,e.lesson.lastAccessedAt=Date.now(),e.lesson.appliedCount=e.lesson.relevantHitCount,e.lesson));return await this.persist(),i}async findSimilar(e,t,n=an){await this.ensureLoaded();let r=on(`${e} ${t}`);if(r.size===0)return;let i;for(let e of this.lessons.values()){let t=sn(r,on(`${e.trigger} ${e.insight}`));t>=n&&(!i||t>i.similarity)&&(i={lesson:e,similarity:t})}return i}async saveDeduped(e,t=an){let n=await this.findSimilar(e.trigger,e.insight,t);return n?{lesson:n.lesson,deduped:!0}:{lesson:await this.save(e),deduped:!1}}async list(e){await this.ensureLoaded();let t=[...this.lessons.values()];if(e?.tags&&e.tags.length>0){let n=new Set(e.tags);t=t.filter(e=>e.tags.some(e=>n.has(e)))}if(e?.query){let n=e.query.toLowerCase();t=t.filter(e=>e.trigger.toLowerCase().includes(n)||e.insight.toLowerCase().includes(n))}return t}async get(e){return await this.ensureLoaded(),this.lessons.get(e)}async recordRelevantHits(e,t=Date.now()){await this.ensureLoaded();let n=!1;for(let r of e){let e=this.lessons.get(r);e&&(e.relevantHitCount=(e.relevantHitCount??0)+1,e.hitCountTotal=(e.hitCountTotal??0)+1,e.lastAccessedAt=t,e.appliedCount=e.relevantHitCount,n=!0)}n&&await this.persist()}async delete(e){await this.ensureLoaded();let t=this.lessons.delete(e);return t&&await this.persist(),t}get size(){return this.lessons.size}async ensureLoaded(){if(!this.loaded){if(this.store&&this.storePath){await this.loadFromStore(),this.loaded=!0;return}if(!this.persistence||!this.snapshotId){this.loaded=!0;return}try{let e=fn(await this.persistence.load(this.snapshotId));if(e){for(let t of e.lessons)this.lessons.set(t.id,dn(t,this.scope));this.nextId=e.nextId??this.lessons.size+1}}catch(e){this.logger?.warn(`Failed to load lessons from file persistence (snapshotId=${this.snapshotId}): ${String(e)}. Falling back to empty lessons — all previously learned lessons will be lost for this session.`)}this.loaded=!0}}async refresh(){!this.store||!this.storePath||(await this.loadFromStore(),this.loaded=!0)}async loadFromStore(){try{let e=(await this.store.load([{path:this.storePath,writable:!0}])).get(this.storePath);if(!e)return;let t=fn(JSON.parse(e));if(t){for(let e of t.lessons)this.lessons.set(e.id,dn(e,this.scope));this.nextId=t.nextId??this.lessons.size+1}}catch(e){this.logger?.warn(`Failed to load lessons from MemoryStore (storePath=${this.storePath}): ${String(e)}. Falling back to empty lessons — all previously learned lessons will be lost for this session.`)}}async persist(){if(this.ephemeral)return;if(this.store&&this.storePath){let e={lessons:[...this.lessons.values()],nextId:this.nextId};await this.store.write(this.storePath,JSON.stringify({version:2,data:e}));return}if(!this.persistence||!this.snapshotId)return;let e=Date.now();await m(this.persistence,this.snapshotId,{lessons:[...this.lessons.values()],nextId:this.nextId},{version:2,tags:[`operational-learning`],now:e,preserveCreatedAt:!1})}};export{Bt as AutoMemory,B as DEFAULT_COMPACTION_CONFIG,T as DEFAULT_MEMORY_INJECTION_BUDGET_TOKENS,ue as DEFAULT_MEMORY_SOURCES,V as DEFAULT_PRUNE_CONFIG,an as DEFAULT_SIMILARITY_THRESHOLD,en as EvolutionSignalStore,Qt as FileStateManager,O as FileSystemMemoryStore,At as HttpMemoryStore,Se as IMAGE_BLOCK_TOKEN_ESTIMATE,Tt as InMemoryArchiveStorage,ye as InMemoryMemoryStore,oe as LESSON_EXTRACTION_PROMPT,Ot as MemoryManager,pn as OperationalLearningStore,ve as PersistentMemory,S as SEGMENT_MERGE_PROMPT,x as SEGMENT_SUMMARIZATION_PROMPT,rt as SPILL_MIN_CHARS,h as SUMMARIZATION_PROMPT,_ as TURN_PREFIX_SUMMARIZATION_PROMPT,g as UPDATE_SUMMARIZATION_PROMPT,b as WITH_PRESERVED_TAIL_PROMPT,ce as buildArchiveReference,se as buildExtractionPrompt,le as buildLowCoverageNotice,ae as buildMemoryInjection,te as buildSegmentMergePrompt,C as buildSummarizationPrompt,w as buildTurnPrefixPrompt,un as computeLessonContentHash,z as computeMemoryDefaults,Xt as createMemoryAutoExtractor,kt as createMemoryManager,Nt as createOverlayMemoryStore,rn as createSignalLog,Pt as discoverAgentsSources,xe as ensureTokenizer,ln as estimateLessonTokens,P as estimateMessageTokens,F as estimateMessagesTokens,M as estimateTokens,j as estimateTokensHeuristic,Ut as extractLessons,J as formatArchive,N as messageToText,Ye as prune,H as resolveContextSize,Wt as saveExtractedLessons,it as spillBatchP3};
205
199
  //# sourceMappingURL=index.js.map