@alvin0/ai-agent-sdk-core 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/dist/agent/accounting/ledger.js +1 -1
- package/dist/agent/accounting/ledger.js.map +1 -1
- package/dist/agent/accounting/report.d.ts +1 -1
- package/dist/agent/define/definition.d.ts +27 -6
- package/dist/agent/define/definition.d.ts.map +1 -1
- package/dist/agent/define/definition.js +1 -1
- package/dist/agent/define/definition.js.map +1 -1
- package/dist/agent/define/session/accounting.js +1 -1
- package/dist/agent/define/session/accounting.js.map +1 -1
- package/dist/agent/define/session/model-config.js +1 -1
- package/dist/agent/define/session/model-config.js.map +1 -1
- package/dist/agent/define/session/types.d.ts +6 -3
- package/dist/agent/define/session/types.d.ts.map +1 -1
- package/dist/agent/define/session.d.ts +25 -2
- package/dist/agent/define/session.d.ts.map +1 -1
- package/dist/agent/define/session.js +1 -1
- package/dist/agent/define/session.js.map +1 -1
- package/dist/agent/loop/admission.js +2 -0
- package/dist/agent/loop/admission.js.map +1 -0
- package/dist/agent/loop/events.d.ts +4 -0
- package/dist/agent/loop/events.d.ts.map +1 -1
- package/dist/agent/loop/program.js +2 -0
- package/dist/agent/loop/program.js.map +1 -0
- package/dist/agent/loop/run-turn.d.ts.map +1 -1
- package/dist/agent/loop/run-turn.js +1 -1
- package/dist/agent/loop/run-turn.js.map +1 -1
- package/dist/agent/loop/schedule.d.ts.map +1 -1
- package/dist/agent/loop/schedule.js +2 -2
- package/dist/agent/loop/schedule.js.map +1 -1
- package/dist/agent/loop/tool-call-support.js +2 -0
- package/dist/agent/loop/tool-call-support.js.map +1 -0
- package/dist/agent/loop/turn/config.js +1 -1
- package/dist/agent/loop/turn/config.js.map +1 -1
- package/dist/agent/loop/turn/hooks.js +1 -1
- package/dist/agent/loop/turn/hooks.js.map +1 -1
- package/dist/agent/loop/turn/model-request-boundary.js +2 -0
- package/dist/agent/loop/turn/model-request-boundary.js.map +1 -0
- package/dist/agent/loop/turn/model-round.js +1 -1
- package/dist/agent/loop/turn/model-round.js.map +1 -1
- package/dist/agent/loop/turn/step-projection.js +2 -0
- package/dist/agent/loop/turn/step-projection.js.map +1 -0
- package/dist/agent/loop/turn/types.d.ts +6 -0
- package/dist/agent/loop/turn/types.d.ts.map +1 -1
- package/dist/agent/memory/compaction.d.ts.map +1 -1
- package/dist/agent/memory/compaction.js +1 -1
- package/dist/agent/memory/compaction.js.map +1 -1
- package/dist/agent/memory/context-optimizer.d.ts +61 -0
- package/dist/agent/memory/context-optimizer.d.ts.map +1 -0
- package/dist/agent/memory/context-optimizer.js +4 -0
- package/dist/agent/memory/context-optimizer.js.map +1 -0
- package/dist/agent/memory/memory.js +1 -1
- package/dist/agent/memory/memory.js.map +1 -1
- package/dist/agent/mode/run-agent.d.ts +15 -1
- package/dist/agent/mode/run-agent.d.ts.map +1 -1
- package/dist/agent/mode/run-agent.js +2 -2
- package/dist/agent/mode/run-agent.js.map +1 -1
- package/dist/agent/skill/provider/snapshot.js +1 -1
- package/dist/agent/team/managed.d.ts +33 -64
- package/dist/agent/team/managed.d.ts.map +1 -1
- package/dist/agent/team/managed.js +6 -3
- package/dist/agent/team/managed.js.map +1 -1
- package/dist/agent/team/team.d.ts +2 -0
- package/dist/agent/team/team.d.ts.map +1 -1
- package/dist/agent/team/team.js +1 -1
- package/dist/agent/team/team.js.map +1 -1
- package/dist/agent/team/types.d.ts +1 -0
- package/dist/agent/team/types.d.ts.map +1 -1
- package/dist/agent/tool/action-fusion.d.ts +30 -0
- package/dist/agent/tool/action-fusion.d.ts.map +1 -0
- package/dist/agent/tool/action-fusion.js +2 -0
- package/dist/agent/tool/action-fusion.js.map +1 -0
- package/dist/agent/tool/capture.js +1 -1
- package/dist/agent/tool/capture.js.map +1 -1
- package/dist/agent/tool/definition.d.ts +8 -0
- package/dist/agent/tool/definition.d.ts.map +1 -1
- package/dist/agent/tool/definition.js.map +1 -1
- package/dist/agent/tool/evidence-reducer.d.ts +41 -0
- package/dist/agent/tool/evidence-reducer.d.ts.map +1 -0
- package/dist/agent/tool/evidence-reducer.js +7 -0
- package/dist/agent/tool/evidence-reducer.js.map +1 -0
- package/dist/agent/tool/nested.d.ts +117 -0
- package/dist/agent/tool/nested.d.ts.map +1 -0
- package/dist/agent/tool/nested.js +2 -0
- package/dist/agent/tool/nested.js.map +1 -0
- package/dist/agent/tool/output-budget.d.ts.map +1 -1
- package/dist/agent/tool/output-budget.js +2 -2
- package/dist/agent/tool/output-budget.js.map +1 -1
- package/dist/agent/tool/output-schema.js +2 -0
- package/dist/agent/tool/output-schema.js.map +1 -0
- package/dist/agent/tool/pipeline.d.ts +7 -0
- package/dist/agent/tool/pipeline.d.ts.map +1 -1
- package/dist/agent/tool/pipeline.js +2 -2
- package/dist/agent/tool/pipeline.js.map +1 -1
- package/dist/agent/tool/program-results.js +2 -0
- package/dist/agent/tool/program-results.js.map +1 -0
- package/dist/agent.d.ts +6 -2
- package/dist/agent.js +1 -1
- package/dist/composition/agent/definition.js +1 -1
- package/dist/composition/agent/definition.js.map +1 -1
- package/dist/composition/agent/options.js +1 -1
- package/dist/composition/agent/options.js.map +1 -1
- package/dist/composition/agent/session.js +1 -1
- package/dist/composition/agent/session.js.map +1 -1
- package/dist/composition/agent/types.d.ts +17 -10
- package/dist/composition/agent/types.d.ts.map +1 -1
- package/dist/composition/runtime/owner.js +1 -1
- package/dist/composition/runtime/owner.js.map +1 -1
- package/dist/composition/runtime/types.d.ts +7 -1
- package/dist/composition/runtime/types.d.ts.map +1 -1
- package/dist/composition/tool-source/snapshot.js +1 -1
- package/dist/contract/adapter.d.ts +1 -1
- package/dist/contract/call-config.d.ts +8 -1
- package/dist/contract/call-config.d.ts.map +1 -1
- package/dist/contract/call-config.js +1 -1
- package/dist/contract/call-config.js.map +1 -1
- package/dist/contract/generate-options.d.ts +5 -0
- package/dist/contract/generate-options.d.ts.map +1 -1
- package/dist/contract/model-info.d.ts +26 -7
- package/dist/contract/model-info.d.ts.map +1 -1
- package/dist/errors/model-error.d.ts +0 -1
- package/dist/errors/model-error.d.ts.map +1 -1
- package/dist/errors/model-error.js +1 -1
- package/dist/errors/model-error.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/memory.d.ts +2 -1
- package/dist/memory.js +1 -1
- package/dist/observability/bus.js +1 -1
- package/dist/observation/report.d.ts +11 -0
- package/dist/observation/report.d.ts.map +1 -1
- package/dist/observation/report.js.map +1 -1
- package/dist/primitives/version.d.ts +1 -1
- package/dist/primitives/version.js +1 -1
- package/dist/primitives/version.js.map +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/runtime/model-call-handle.js +1 -1
- package/dist/runtime/model-call-handle.js.map +1 -1
- package/dist/runtime/model-metadata.js +1 -1
- package/dist/runtime/model-metadata.js.map +1 -1
- package/dist/runtime/model-stream.js +1 -1
- package/dist/runtime/model-stream.js.map +1 -1
- package/dist/runtime/registry.d.ts +7 -1
- package/dist/runtime/registry.d.ts.map +1 -1
- package/dist/runtime/registry.js +1 -1
- package/dist/runtime/registry.js.map +1 -1
- package/dist/tools.d.ts +4 -1
- package/dist/tools.js +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{ReasoningEffortId as e}from"../../primitives/brand.js";import{waitForSettlement as t}from"../../async/settlement.js";import{systemRandomId as n}from"../../platform/adapter.js";import{createMessage as r,createUserMessage as i}from"../../message/message.js";import{BlockAssembler as a}from"../../stream/assembler.js";import{normalizeToolPairing as o}from"../history/normalize.js";import{compactionAccounting as s}from"./accounting-binding.js";import{estimateContextTokens as c,estimateMessageTokens as l}from"./token-estimator.js";import{pruneToolResults as u,selectCompactablePrefix as d}from"./surface-compaction.js";const f=[`You are performing a CONTEXT CHECKPOINT COMPACTION for a long-running AI agent task.`,`Condense the conversation above into a handoff that lets another model continue without forgetting the original purpose.`,``,`Output exactly these Markdown sections, in order, using terse bullets. Write "(none)" for an empty section.`,`## Primary Request and Intent`,`## Progress and Completed Work`,`## Decisions and Rationale`,`## Constraints and User Preferences`,`## Important Files, Symbols, and Evidence`,`## Errors and Failed Approaches`,`## Pending Work`,`## Current Work`,`## Next Step`,`## Critical Context`,``,`Preserve exact paths, identifiers, commands, error strings, numeric limits, and user corrections when they matter.`,`Under Important Files, mark relevant files as inspected or modified and preserve the exact declarations or findings needed next.`,`Under Current Work, record the last successful tool action, the latest verification result, and whether any edit is still partial.`,`Under Next Step, name one direct edit or command. Do not recommend rereading an unchanged file whose relevant contents are already recorded.`,`Do not reveal hidden chain-of-thought. Record only public progress, observed evidence, decisions, and actionable context.`,`If an earlier <compacted-summary> exists, merge still-valid facts with newer information and drop stale facts.`,`Output only the checkpoint. Do not call tools and do not acknowledge the compaction.`].join(`
|
|
2
|
-
`);var p=class{input;active=!1;overflowTurn=-1;overflowRetries=0;recoveringOverflow=!1;modelBudget;pressureCooldown=0;constructor(e){this.input=e}async beforeStep(e){if(!(!this.input.policy.auto||e.signal.aborted)){if(this.pressureCooldown>0){this.pressureCooldown--;return}this.overflowTurn===e.turn?this.recoveringOverflow||(this.overflowRetries=0):(this.overflowTurn=e.turn,this.overflowRetries=0),this.recoveringOverflow=!1;try{let t;for(let n=0;n<=this.input.policy.compactionRetries;n++){let n=await this.compactIfNeeded(`pressure`,e.signal,e.emit);if(n===null)break;if(t=n,n.backoffReason!==void 0){this.pressureCooldown=n.cooldownSteps??4;break}}t!==void 0&&t.thresholdTokens!==void 0&&t.estimatedTokensAfter>=t.thresholdTokens&&(this.pressureCooldown=Math.max(this.pressureCooldown,4))}catch{this.pressureCooldown=2}}}async onRequestError(e){if(!(e.failure.code!==`CONTEXT_WINDOW_EXCEEDED`||e.signal.aborted)&&(this.overflowTurn!==e.turn&&(this.overflowTurn=e.turn,this.overflowRetries=0),!(this.overflowRetries>=this.input.policy.maxOverflowRetries)))try{return await this.compactIfNeeded(`context-overflow`,e.signal,e.emit)===null||e.signal.aborted?void 0:(this.overflowRetries++,this.recoveringOverflow=!0,`retry`)}catch{return}}async compactNow(e){let t=e===void 0?new AbortController:void 0,n=e??t?.signal;if(n===void 0)throw Error(`failed to create compaction signal`);return this.compactIfNeeded(`manual`,n)}async compactIfNeeded(e,t,n){if(this.active)return null;let r=AbortSignal.timeout(this.input.policy.summaryTimeoutMs),a=AbortSignal.any([t,r]);a.throwIfAborted(),this.active=!0;try{let o=this.input.history(),s=o.surface();if(s.length<2)return null;let f=this.input.tools(),p=this.input.system(),m=this.input.pinnedMessages?.()??[],h=c({system:p,messages:[...m,...s.map(e=>e.message)],tools:f}),g=await this.resolveBudget(h,a);if(a.throwIfAborted(),e===`pressure`&&(g===null||h<g.thresholdTokens))return null;if(u(o,s,this.input.policy.maxToolResultChars)>0){if(s=o.surface(),h=c({system:p,messages:[...m,...s.map(e=>e.message)],tools:f}),e===`pressure`&&g!==null&&h<g.thresholdTokens)return null;g=await this.resolveBudget(h,a),a.throwIfAborted()}let _=e===`context-overflow`?Math.max(1,l(s.at(-1)?.message)):g?.retainTokens??Math.max(1,Math.floor(h*.25)),v=d(s,_);if(v.length===0)return null;let y=new Set(v.map(e=>e.seq)),C=c({system:p,messages:[...m,...s.filter(e=>!y.has(e.seq)).map(e=>e.message)],tools:f}),w=b(),T=new Date().toISOString();o.append({kind:`compaction-start`,compactionId:w,trigger:e,at:T});try{await this.emitObserver(n,{type:`compaction-start`,compactionId:w,trigger:e,estimatedInputTokens:h});let t=await this.summarize([...m,...v.map(e=>e.message)],p,f,a);a.throwIfAborted();let r=i({source:{kind:`app`,producer:`agent-compaction:${w}`},content:[{type:`text`,text:`This is an automatically generated checkpoint of earlier conversation context. Treat it as established background, preserve the original objective and constraints, and continue directly from the messages that follow.\n\n<compacted-summary>\n${t.summary}\n</compacted-summary>`}]}),s=v.reduce((e,t)=>e+l(t.message),0),c=l(r);if(c>=s)throw Error(`compaction summary did not shrink selected context (${c} >= ${s} estimated tokens)`);let u=Object.freeze(v.map(e=>e.seq)),d=Math.max(0,h-s+c),_=g?.thresholdTokens,y=O({trigger:e,thresholdTokens:_,estimatedTokensBefore:h,estimatedTokensAfter:d,estimatedNonCompactableTokens:C});o.appendBatch([{event:{kind:`compaction-summary`,compactionId:w,summary:t.summary,shadowedSeqs:u,estimatedTokensBefore:h,estimatedTokensAfter:d,provider:t.provider,model:t.model,...t.usage===void 0?{}:{usage:t.usage}}},{event:{kind:`user`,message:r},surfaceOp:{op:`replace`,from:Math.min(...u),to:Math.max(...u),targets:u}},{event:{kind:`compaction-end`,compactionId:w,status:`completed`,at:new Date().toISOString(),..._===void 0?{}:{thresholdTokens:_},estimatedNonCompactableTokens:C,...y===void 0?{}:{backoffReason:y,cooldownSteps:4}}}]);let b=Object.freeze({compactionId:w,trigger:e,summary:t.summary,shadowedSeqs:u,estimatedTokensBefore:h,estimatedTokensAfter:d,..._===void 0?{}:{thresholdTokens:_},estimatedNonCompactableTokens:C,...y===void 0?{}:{backoffReason:y,cooldownSteps:4},provider:t.provider,model:t.model,...t.usage===void 0?{}:{usage:t.usage}});return await this.emitObserver(n,{type:`compaction-end`,compactionId:w,trigger:e,status:`completed`,shadowedSeqs:u,estimatedTokensBefore:h,estimatedTokensAfter:d,summary:t.summary,..._===void 0?{}:{thresholdTokens:_},estimatedNonCompactableTokens:C,...y===void 0?{}:{backoffReason:y,cooldownSteps:4},...t.usage===void 0?{}:{usage:t.usage}}),b}catch(i){let a=r.aborted&&!t.aborted&&S(i)!==`MODEL_TEARDOWN_TIMEOUT`?E(this.input.policy.summaryTimeoutMs,i):i,s=x(a);try{o.append({kind:`compaction-end`,compactionId:w,status:`failed`,at:new Date().toISOString(),error:s})}catch{}throw await this.emitObserver(n,{type:`compaction-end`,compactionId:w,trigger:e,status:`failed`,shadowedSeqs:[],estimatedTokensBefore:h,estimatedTokensAfter:h,error:s}),a}}finally{this.active=!1}}async resolveBudget(e,t){let n=this.input.policy,r=this.input.config(),i=`${r.provider}\u0000${r.model}\u0000${String(r.maxTokens??``)}`;if(this.modelBudget?.key!==i)try{let e=await D(this.input.registry.resolveModelInfo(r.provider,r.model,t),t),n=e.context?.contextWindow;this.modelBudget={key:i,budget:n===void 0?null:{contextWindow:n,outputReserve:r.maxTokens??e.defaultMaxTokens??e.maxOutputTokens??0}}}catch{if(n.maxInputTokens===void 0)return null;this.modelBudget={key:i,budget:null}}let a=this.modelBudget?.budget?.contextWindow,o=a===void 0?void 0:Math.max(1,a-(this.modelBudget?.budget?.outputReserve??0)),s=a===void 0?void 0:Math.floor(a*n.thresholdRatio),c=o===void 0||s===void 0?void 0:Math.min(o,s),l=n.maxInputTokens===void 0?c:o===void 0?n.maxInputTokens:Math.min(n.maxInputTokens,o);if(l===void 0)return null;let u=o??Math.max(e,Math.ceil(l/n.thresholdRatio)),d=n.retainTokens??Math.floor(u*(n.retainRatio??.2));return{thresholdTokens:l,retainTokens:Math.max(1,d)}}async emitObserver(e,n){if(e===void 0)return;let r=Promise.resolve().then(()=>e(n));await t(r,this.input.policy.teardownTimeoutMs)}async summarize(n,r,c,l){let u=this.input.policy,d=this.input.config(),p=u.summarizationProvider??d.provider,h=u.summarizationModel??d.model,g=s(this);w(g);let _=await D(this.input.registry.resolveModelInfo(p,h,l),l),v=Math.min(u.maxSummaryTokens,_.maxOutputTokens??2**53-1,_.context===void 0?2**53-1:Math.max(1,_.context.contextWindow-1)),y=await D(this.input.registry.prepareCall({provider:p,model:h,...u.summarizationEffort===void 0?{}:{reasoningEffort:e(u.summarizationEffort)},maxTokens:v},l,g?.modelInvocation),l),b={remaining:u.maxSummaryRequestChars},x=o(n).map(e=>m(e,u.maxSummaryInputChars,b)),S=i({source:{kind:`app`,producer:`agent-compaction`},content:[{type:`text`,text:f}]}),E=new a,O={...y.config,messages:[...x,S],...r.length===0?{}:{system:r},...c.length===0?{}:{tools:c},toolChoice:`none`,signal:l},{signal:k,...A}=O;if(C(A)>u.maxSummaryRequestBytes)throw T(`compaction summary request exceeds the ${u.maxSummaryRequestBytes}-byte limit`,`MODEL_REQUEST_TOO_LARGE`);w(g);let j=y.stream(O,g?.modelInvocation),M=j[Symbol.asyncIterator](),N=!1,P=0,F=0,I=!1,L;try{for(;;){let e=await D(M.next(),l);if(e.done===!0){N=!0;break}if(P++,F+=C(e.value),P>u.maxSummaryStreamEvents||F>u.maxSummaryResponseBytes)throw T(`compaction summary response exceeded its resource limit`,`MODEL_RESPONSE_TOO_LARGE`);if(I)throw T(`compaction summarizer emitted data after its terminal finish chunk`,`INVALID_MODEL_STREAM`);E.push(e.value),e.value.type===`finish`&&(I=!0)}}catch(e){L=e}finally{if(!N){let e=M.return?.bind(M);if(e!==void 0)try{let n=Promise.resolve().then(async()=>{await e()});await t(n,u.teardownTimeoutMs)||(L=T(`compaction model stream ignored cancellation for more than ${u.teardownTimeoutMs}ms`,`MODEL_TEARDOWN_TIMEOUT`))}catch(e){L??=e}}}try{let e=await j.report,t=await g?.recordModelCall(e,O);t?.usageRequired===!0&&(L??=T(`compaction model usage is required by the configured run policy`,`USAGE_REQUIRED`)),t?.usageUnavailable===!0&&(L??=T(`compaction model usage is unavailable for the configured budget`,`USAGE_UNAVAILABLE`))}catch(e){L??=e}if(L!==void 0)throw L;let R=E.finish;if(R.kind===`error`||R.kind===`aborted`){let e=Error(R.failure.message);throw e.code=R.failure.code,e}if(R.kind===`max-tokens`)throw Error(`compaction summary was truncated at maxSummaryTokens`);if(R.kind===`tool-calls`)throw Error(`compaction summarizer attempted a tool call`);let z=E.blocks().flatMap(e=>e.type===`text`?[e.text]:[]).join(`
|
|
2
|
+
`);var p=class{input;active=!1;overflowTurn=-1;overflowRetries=0;recoveringOverflow=!1;modelBudget;pressureCooldown=0;constructor(e){this.input=e}async beforeStep(e){if(!(!this.input.policy.auto||e.signal.aborted)){if(this.pressureCooldown>0){this.pressureCooldown--;return}this.overflowTurn===e.turn?this.recoveringOverflow||(this.overflowRetries=0):(this.overflowTurn=e.turn,this.overflowRetries=0),this.recoveringOverflow=!1;try{let t;for(let n=0;n<=this.input.policy.compactionRetries;n++){let n=await this.compactIfNeeded(`pressure`,e.signal,e.emit);if(n===null)break;if(t=n,n.backoffReason!==void 0){this.pressureCooldown=n.cooldownSteps??4;break}}t!==void 0&&t.thresholdTokens!==void 0&&t.estimatedTokensAfter>=t.thresholdTokens&&(this.pressureCooldown=Math.max(this.pressureCooldown,4))}catch{this.pressureCooldown=2}}}async onRequestError(e){if(!(e.failure.code!==`CONTEXT_WINDOW_EXCEEDED`||e.signal.aborted)&&(this.overflowTurn!==e.turn&&(this.overflowTurn=e.turn,this.overflowRetries=0),!(this.overflowRetries>=this.input.policy.maxOverflowRetries)))try{return await this.compactIfNeeded(`context-overflow`,e.signal,e.emit)===null||e.signal.aborted?void 0:(this.overflowRetries++,this.recoveringOverflow=!0,`retry`)}catch{return}}async compactNow(e){let t=e===void 0?new AbortController:void 0,n=e??t?.signal;if(n===void 0)throw Error(`failed to create compaction signal`);return this.compactIfNeeded(`manual`,n)}async compactIfNeeded(e,t,n){if(this.active)return null;let r=AbortSignal.timeout(this.input.policy.summaryTimeoutMs),a=AbortSignal.any([t,r]);a.throwIfAborted(),this.active=!0;try{let o=this.input.history(),s=o.surface();if(s.length<2)return null;let f=this.input.tools(),p=this.input.system(),m=this.input.pinnedMessages?.()??[],h=c({system:p,messages:[...m,...s.map(e=>e.message)],tools:f}),g=await this.resolveBudget(h,a);if(a.throwIfAborted(),e===`pressure`&&(g===null||h<g.thresholdTokens))return null;if(u(o,s,this.input.policy.maxToolResultChars)>0){if(s=o.surface(),h=c({system:p,messages:[...m,...s.map(e=>e.message)],tools:f}),e===`pressure`&&g!==null&&h<g.thresholdTokens)return null;g=await this.resolveBudget(h,a),a.throwIfAborted()}let _=e===`context-overflow`?Math.max(1,l(s.at(-1)?.message)):g?.retainTokens??Math.max(1,Math.floor(h*.25)),v=d(s,_);if(v.length===0)return null;let y=new Set(v.map(e=>e.seq)),C=c({system:p,messages:[...m,...s.filter(e=>!y.has(e.seq)).map(e=>e.message)],tools:f}),w=b(),T=new Date().toISOString();o.append({kind:`compaction-start`,compactionId:w,trigger:e,at:T});try{await this.emitObserver(n,{type:`compaction-start`,compactionId:w,trigger:e,estimatedInputTokens:h});let t=await this.summarize([...m,...v.map(e=>e.message)],p,f,a);a.throwIfAborted();let r=i({source:{kind:`app`,producer:`agent-compaction:${w}`},content:[{type:`text`,text:`This is an automatically generated checkpoint of earlier conversation context. Treat it as established background, preserve the original objective and constraints, and continue directly from the messages that follow.\n\n<compacted-summary>\n${t.summary}\n</compacted-summary>`}]}),s=v.reduce((e,t)=>e+l(t.message),0),c=l(r);if(c>=s)throw Error(`compaction summary did not shrink selected context (${c} >= ${s} estimated tokens)`);let u=Object.freeze(v.map(e=>e.seq)),d=Math.max(0,h-s+c),_=g?.thresholdTokens,y=O({trigger:e,thresholdTokens:_,estimatedTokensBefore:h,estimatedTokensAfter:d,estimatedNonCompactableTokens:C});o.appendBatch([{event:{kind:`compaction-summary`,compactionId:w,summary:t.summary,shadowedSeqs:u,estimatedTokensBefore:h,estimatedTokensAfter:d,provider:t.provider,model:t.model,...t.usage===void 0?{}:{usage:t.usage}}},{event:{kind:`user`,message:r},surfaceOp:{op:`replace`,from:Math.min(...u),to:Math.max(...u),targets:u}},{event:{kind:`compaction-end`,compactionId:w,status:`completed`,at:new Date().toISOString(),..._===void 0?{}:{thresholdTokens:_},estimatedNonCompactableTokens:C,...y===void 0?{}:{backoffReason:y,cooldownSteps:4}}}]);let b=Object.freeze({compactionId:w,trigger:e,summary:t.summary,shadowedSeqs:u,estimatedTokensBefore:h,estimatedTokensAfter:d,..._===void 0?{}:{thresholdTokens:_},estimatedNonCompactableTokens:C,...y===void 0?{}:{backoffReason:y,cooldownSteps:4},provider:t.provider,model:t.model,...t.usage===void 0?{}:{usage:t.usage}});return await this.emitObserver(n,{type:`compaction-end`,compactionId:w,trigger:e,status:`completed`,shadowedSeqs:u,estimatedTokensBefore:h,estimatedTokensAfter:d,summary:t.summary,..._===void 0?{}:{thresholdTokens:_},estimatedNonCompactableTokens:C,...y===void 0?{}:{backoffReason:y,cooldownSteps:4},...t.usage===void 0?{}:{usage:t.usage}}),b}catch(i){let a=r.aborted&&!t.aborted&&S(i)!==`MODEL_TEARDOWN_TIMEOUT`?E(this.input.policy.summaryTimeoutMs,i):i,s=x(a);try{o.append({kind:`compaction-end`,compactionId:w,status:`failed`,at:new Date().toISOString(),error:s})}catch{}throw await this.emitObserver(n,{type:`compaction-end`,compactionId:w,trigger:e,status:`failed`,shadowedSeqs:[],estimatedTokensBefore:h,estimatedTokensAfter:h,error:s}),a}}finally{this.active=!1}}async resolveBudget(e,t){let n=this.input.policy,r=this.input.config(),i=`${r.provider}\u0000${r.model}\u0000${String(r.maxTokens??``)}`;if(this.modelBudget?.key!==i)try{let e=await D(this.input.registry.resolveModelInfo(r.provider,r.model,t),t),n=r.contextWindow??e.context?.contextWindow;this.modelBudget={key:i,budget:n===void 0?null:{contextWindow:n,outputReserve:r.maxTokens??e.defaultMaxTokens??e.maxOutputTokens??0}}}catch{if(n.maxInputTokens===void 0)return null;this.modelBudget={key:i,budget:null}}let a=this.modelBudget?.budget?.contextWindow,o=a===void 0?void 0:Math.max(1,a-(this.modelBudget?.budget?.outputReserve??0)),s=a===void 0?void 0:Math.floor(a*n.thresholdRatio),c=o===void 0||s===void 0?void 0:Math.min(o,s),l=n.maxInputTokens===void 0?c:o===void 0?n.maxInputTokens:Math.min(n.maxInputTokens,o);if(l===void 0)return null;let u=o??Math.max(e,Math.ceil(l/n.thresholdRatio)),d=n.retainTokens??Math.floor(u*(n.retainRatio??.2));return{thresholdTokens:l,retainTokens:Math.max(1,d)}}async emitObserver(e,n){if(e===void 0)return;let r=Promise.resolve().then(()=>e(n));await t(r,this.input.policy.teardownTimeoutMs)}async summarize(n,r,c,l){let u=this.input.policy,d=this.input.config(),p=u.summarizationProvider??d.provider,h=u.summarizationModel??d.model,g=s(this);w(g);let _=await D(this.input.registry.resolveModelInfo(p,h,l),l),v=Math.min(u.maxSummaryTokens,_.maxOutputTokens??2**53-1,_.context===void 0?2**53-1:Math.max(1,_.context.contextWindow-1)),y=await D(this.input.registry.prepareCall({provider:p,model:h,...u.summarizationEffort===void 0?{}:{reasoningEffort:e(u.summarizationEffort)},maxTokens:v},l,g?.modelInvocation),l),b={remaining:u.maxSummaryRequestChars},x=o(n).map(e=>m(e,u.maxSummaryInputChars,b)),S=i({source:{kind:`app`,producer:`agent-compaction`},content:[{type:`text`,text:f}]}),E=new a,O={...y.config,messages:[...x,S],...r.length===0?{}:{system:r},...c.length===0?{}:{tools:c},toolChoice:`none`,signal:l},{signal:k,...A}=O;if(C(A)>u.maxSummaryRequestBytes)throw T(`compaction summary request exceeds the ${u.maxSummaryRequestBytes}-byte limit`,`MODEL_REQUEST_TOO_LARGE`);w(g);let j=y.stream(O,g?.modelInvocation),M=j[Symbol.asyncIterator](),N=!1,P=0,F=0,I=!1,L;try{for(;;){let e=await D(M.next(),l);if(e.done===!0){N=!0;break}if(P++,F+=C(e.value),P>u.maxSummaryStreamEvents||F>u.maxSummaryResponseBytes)throw T(`compaction summary response exceeded its resource limit`,`MODEL_RESPONSE_TOO_LARGE`);if(I)throw T(`compaction summarizer emitted data after its terminal finish chunk`,`INVALID_MODEL_STREAM`);E.push(e.value),e.value.type===`finish`&&(I=!0)}}catch(e){L=e}finally{if(!N){let e=M.return?.bind(M);if(e!==void 0)try{let n=Promise.resolve().then(async()=>{await e()});await t(n,u.teardownTimeoutMs)||(L=T(`compaction model stream ignored cancellation for more than ${u.teardownTimeoutMs}ms`,`MODEL_TEARDOWN_TIMEOUT`))}catch(e){L??=e}}}try{let e=await j.report,t=await g?.recordModelCall(e,O);t?.usageRequired===!0&&(L??=T(`compaction model usage is required by the configured run policy`,`USAGE_REQUIRED`)),t?.usageUnavailable===!0&&(L??=T(`compaction model usage is unavailable for the configured budget`,`USAGE_UNAVAILABLE`))}catch(e){L??=e}if(L!==void 0)throw L;let R=E.finish;if(R.kind===`error`||R.kind===`aborted`){let e=Error(R.failure.message);throw e.code=R.failure.code,e}if(R.kind===`max-tokens`)throw Error(`compaction summary was truncated at maxSummaryTokens`);if(R.kind===`tool-calls`)throw Error(`compaction summarizer attempted a tool call`);let z=E.blocks().flatMap(e=>e.type===`text`?[e.text]:[]).join(`
|
|
3
3
|
`).trim();if(z.length===0)throw Error(`compaction summarizer produced no text`);return{summary:z,provider:p,model:h,...E.usage===void 0?{}:{usage:E.usage}}}};function m(e,t,n){let i=e.content.flatMap(e=>h(e,t,n)),a=e.source.kind===`model`?{kind:`model`,provider:e.source.provider,model:e.source.model}:e.source;return r({role:e.role,source:a,content:i})}function h(e,t,n){switch(e.type){case`text`:return[{type:`text`,text:g(e.text,t,n)}];case`reasoning`:return e.text.length===0?[]:[{type:`text`,text:g(`[Reasoning summary] ${e.text}`,t,n)}];case`image`:return[{type:`text`,text:g(`[Image input: ${e.source.kind}]`,t,n)}];case`document`:return[{type:`text`,text:g(`[Document input: ${e.filename??e.source.kind}]`,t,n)}];case`tool-call`:return[{...e,arguments:_(e.arguments,t,n)}];case`tool-result`:return[{...e,content:e.content.flatMap(e=>h(e,t,n))}];case`native-tool-call`:return[{type:`text`,text:g(`[Native tool ${e.name} (${e.status??`unknown`})] ${v(e.arguments??{})}`,t,n)}];default:return[{type:`text`,text:g(`[Unsupported content block: ${v(e)}]`,t,n)}]}}function g(e,t,n){let r=y(e,Math.max(0,Math.min(t,n.remaining)));return n.remaining-=r.length,r}function _(e,t,n){let r=Math.max(0,Math.min(t,n.remaining));return e.length<=r?(n.remaining-=e.length,e):19<=r?(n.remaining-=19,`{"_compacted":true}`):`{}`}function v(e){try{return JSON.stringify(e)??``}catch{return String(e)}}function y(e,t){if(e.length<=t)return e;if(t<=30)return e.slice(0,t);let n=Math.ceil((t-30)/2),r=Math.floor((t-30)/2);return e.slice(0,n)+`
|
|
4
4
|
…[truncated for compaction]…
|
|
5
5
|
`+e.slice(e.length-r)}function b(){return n()}function x(e){return e instanceof Error?e.message:String(e)}function S(e){return typeof e==`object`&&e&&`code`in e&&typeof e.code==`string`?e.code:void 0}function C(e){let t=JSON.stringify(e);if(t===void 0)throw TypeError(`compaction model payload is not JSON serializable`);return new TextEncoder().encode(t).byteLength}function w(e){let t=e?.usageStop;if(t!==void 0)throw T(`compaction cannot dispatch after a mandatory usage stop`,t.usageRequired?`USAGE_REQUIRED`:`USAGE_UNAVAILABLE`)}function T(e,t,n){let r=Error(e,n===void 0?void 0:{cause:n});return r.code=t,r}function E(e,t){return T(`compaction model operation exceeded ${e}ms`,`MODEL_TIMEOUT`,t)}async function D(e,t){if(t.aborted)throw e.catch(()=>void 0),t.reason??Error(`operation aborted`);return await new Promise((n,r)=>{let i=()=>{t.removeEventListener(`abort`,i),r(t.reason??Error(`operation aborted`))};t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{t.removeEventListener(`abort`,i),n(e)},e=>{t.removeEventListener(`abort`,i),r(e)})})}function O(e){if(!(e.trigger!==`pressure`||e.thresholdTokens===void 0||e.estimatedTokensAfter<e.thresholdTokens))return e.estimatedNonCompactableTokens>=e.thresholdTokens?`unreachable-threshold`:e.estimatedTokensBefore-e.estimatedTokensAfter<Math.max(256,Math.ceil(e.estimatedTokensBefore*.01))?`low-savings`:void 0}export{f as COMPACTION_INSTRUCTION,p as ContextCompactor};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction.js","names":[],"sources":["../../../src/agent/memory/compaction.ts"],"sourcesContent":["import { systemRandomId } from '../../platform/adapter.ts'\n/** Context-pressure compaction inspired by Codex checkpoints and deepseek-harness surface replacement. */\n\nimport type { CallConfig } from '../../contract/index.ts'\nimport type { ModelToolSchema } from '../../contract/index.ts'\nimport type { ContentBlock, ToolCallBlock, ToolResultBlock } from '../../message/index.ts'\nimport { createMessage, createUserMessage, type Message } from '../../message/index.ts'\nimport { ReasoningEffortId } from '../../primitives/index.ts'\nimport type { ModelRegistry } from '../../runtime/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport { BlockAssembler } from '../../stream/index.ts'\nimport type { TokenUsage } from '../../stream/index.ts'\nimport type { History } from '../history/history.ts'\nimport { normalizeToolPairing } from '../history/normalize.ts'\nimport type {\n AgentMaintenanceEvent, BeforeStepContext, CompactionBackoffReason, CompactionTrigger, RequestErrorContext,\n} from '../loop/events.ts'\nimport type { AgentCompactionConfig } from './compaction-config.ts'\nimport { compactionAccounting } from './accounting-binding.ts'\nimport type { RunReport } from '../accounting/delivery-types.ts'\nimport { pruneToolResults, selectCompactablePrefix } from './surface-compaction.ts'\nimport { estimateContextTokens, estimateMessageTokens } from './token-estimator.ts'\n\nexport interface CompactionResult {\n readonly compactionId: string\n readonly trigger: CompactionTrigger\n readonly summary: string\n readonly shadowedSeqs: readonly number[]\n readonly estimatedTokensBefore: number\n readonly estimatedTokensAfter: number\n readonly thresholdTokens?: number\n readonly estimatedNonCompactableTokens: number\n readonly backoffReason?: CompactionBackoffReason\n readonly cooldownSteps?: number\n readonly provider: string\n readonly model: string\n readonly usage?: TokenUsage\n /** Additive runtime projection; absent on the preserved low-level compactor path. */\n readonly status?: 'completed'\n readonly report?: RunReport\n}\n\nexport interface ContextCompactorOptions {\n readonly registry: ModelRegistry\n /**\n * The call configuration in force RIGHT NOW, not when the compactor was built.\n *\n * A supplier rather than a value because a run may target a different model\n * than the session it belongs to: the context window a checkpoint is budgeted\n * against, and the model a summary is written by, both have to follow that\n * override or compaction would reason about a window nobody is calling.\n */\n readonly config: () => CallConfig\n readonly history: () => History\n readonly system: () => string\n readonly pinnedMessages?: () => readonly Message[]\n readonly tools: () => readonly ModelToolSchema[]\n readonly policy: AgentCompactionConfig\n}\n\ninterface ResolvedBudget {\n readonly thresholdTokens: number\n readonly retainTokens: number\n}\n\ninterface CharacterBudget { remaining: number }\n\nconst CHECKPOINT_PREAMBLE =\n 'This is an automatically generated checkpoint of earlier conversation context. Treat it as established background, preserve the original objective and constraints, and continue directly from the messages that follow.'\n\nconst MIN_PRESSURE_SAVINGS_TOKENS = 256\nconst MIN_PRESSURE_SAVINGS_RATIO = 0.01\nconst PRESSURE_BACKOFF_STEPS = 4\n\nexport const COMPACTION_INSTRUCTION = [\n 'You are performing a CONTEXT CHECKPOINT COMPACTION for a long-running AI agent task.',\n 'Condense the conversation above into a handoff that lets another model continue without forgetting the original purpose.',\n '',\n 'Output exactly these Markdown sections, in order, using terse bullets. Write \"(none)\" for an empty section.',\n '## Primary Request and Intent',\n '## Progress and Completed Work',\n '## Decisions and Rationale',\n '## Constraints and User Preferences',\n '## Important Files, Symbols, and Evidence',\n '## Errors and Failed Approaches',\n '## Pending Work',\n '## Current Work',\n '## Next Step',\n '## Critical Context',\n '',\n 'Preserve exact paths, identifiers, commands, error strings, numeric limits, and user corrections when they matter.',\n 'Under Important Files, mark relevant files as inspected or modified and preserve the exact declarations or findings needed next.',\n 'Under Current Work, record the last successful tool action, the latest verification result, and whether any edit is still partial.',\n 'Under Next Step, name one direct edit or command. Do not recommend rereading an unchanged file whose relevant contents are already recorded.',\n 'Do not reveal hidden chain-of-thought. Record only public progress, observed evidence, decisions, and actionable context.',\n 'If an earlier <compacted-summary> exists, merge still-valid facts with newer information and drop stale facts.',\n 'Output only the checkpoint. Do not call tools and do not acknowledge the compaction.',\n].join('\\n')\n\nexport class ContextCompactor {\n private readonly input: ContextCompactorOptions\n private active = false\n private overflowTurn = -1\n private overflowRetries = 0\n private recoveringOverflow = false\n /** Keyed by route + model + output reserve: an override changes all three. */\n private modelBudget: {\n readonly key: string\n readonly budget: { readonly contextWindow: number; readonly outputReserve: number } | null\n } | undefined\n private pressureCooldown = 0\n\n constructor(input: ContextCompactorOptions) {\n this.input = input\n }\n\n /** Fail-open pressure maintenance for a pre-step hook. */\n async beforeStep(context: BeforeStepContext): Promise<void> {\n if (!this.input.policy.auto || context.signal.aborted) return\n if (this.pressureCooldown > 0) {\n this.pressureCooldown--\n return\n }\n if (this.overflowTurn !== context.turn) {\n this.overflowTurn = context.turn\n this.overflowRetries = 0\n } else if (!this.recoveringOverflow) {\n this.overflowRetries = 0\n }\n this.recoveringOverflow = false\n try {\n let lastResult: CompactionResult | undefined\n for (let attempt = 0; attempt <= this.input.policy.compactionRetries; attempt++) {\n const result = await this.compactIfNeeded('pressure', context.signal, context.emit)\n if (result === null) break\n lastResult = result\n if (result.backoffReason !== undefined) {\n this.pressureCooldown = result.cooldownSteps ?? PRESSURE_BACKOFF_STEPS\n break\n }\n }\n if (lastResult !== undefined\n && lastResult.thresholdTokens !== undefined\n && lastResult.estimatedTokensAfter >= lastResult.thresholdTokens) {\n this.pressureCooldown = Math.max(\n this.pressureCooldown,\n PRESSURE_BACKOFF_STEPS,\n )\n }\n } catch {\n // Pressure maintenance is fail-open; the provider's canonical overflow\n // error still gets one forced recovery path below. Avoid retrying the same\n // broken maintenance request on every tool step and flooding logs/traces.\n // Mandatory usage decisions stay latched in the owning ledger and are\n // checked by modelRound after this hook, before any main dispatch.\n this.pressureCooldown = 2\n }\n }\n\n /** Compact and retry only when a provider confirmed context overflow. */\n async onRequestError(context: RequestErrorContext): Promise<'retry' | undefined> {\n if (context.failure.code !== 'CONTEXT_WINDOW_EXCEEDED' || context.signal.aborted) return undefined\n if (this.overflowTurn !== context.turn) {\n this.overflowTurn = context.turn\n this.overflowRetries = 0\n }\n if (this.overflowRetries >= this.input.policy.maxOverflowRetries) return undefined\n try {\n const result = await this.compactIfNeeded('context-overflow', context.signal, context.emit)\n if (result === null || context.signal.aborted) return undefined\n this.overflowRetries++\n this.recoveringOverflow = true\n return 'retry'\n } catch {\n return undefined\n }\n }\n\n /** Explicit idle-session checkpoint even below automatic pressure. */\n async compactNow(signal?: AbortSignal): Promise<CompactionResult | null> {\n const controller = signal === undefined ? new AbortController() : undefined\n const effectiveSignal = signal ?? controller?.signal\n if (effectiveSignal === undefined) throw new Error('failed to create compaction signal')\n return this.compactIfNeeded('manual', effectiveSignal)\n }\n\n private async compactIfNeeded(\n trigger: CompactionTrigger,\n signal: AbortSignal,\n emit?: (event: AgentMaintenanceEvent) => Promise<void>,\n ): Promise<CompactionResult | null> {\n if (this.active) return null\n const deadline = AbortSignal.timeout(this.input.policy.summaryTimeoutMs)\n const operationSignal = AbortSignal.any([signal, deadline])\n operationSignal.throwIfAborted()\n this.active = true\n try {\n const history = this.input.history()\n let surface = history.surface()\n if (surface.length < 2) return null\n const tools = this.input.tools()\n const system = this.input.system()\n const pinned = this.input.pinnedMessages?.() ?? []\n let totalBefore = estimateContextTokens({\n system, messages: [...pinned, ...surface.map(node => node.message)], tools,\n })\n let budget = await this.resolveBudget(totalBefore, operationSignal)\n operationSignal.throwIfAborted()\n if (trigger === 'pressure' && (budget === null || totalBefore < budget.thresholdTokens)) return null\n const pruned = pruneToolResults(history, surface, this.input.policy.maxToolResultChars)\n if (pruned > 0) {\n surface = history.surface()\n totalBefore = estimateContextTokens({\n system, messages: [...pinned, ...surface.map(node => node.message)], tools,\n })\n if (trigger === 'pressure' && budget !== null && totalBefore < budget.thresholdTokens) return null\n // With an absolute threshold and no provider context metadata, retainRatio\n // is inferred from the measured request. Pruning can change that request\n // by orders of magnitude, so selection must use the post-prune budget.\n budget = await this.resolveBudget(totalBefore, operationSignal)\n operationSignal.throwIfAborted()\n }\n const retainTokens = trigger === 'context-overflow'\n ? Math.max(1, estimateMessageTokens(surface.at(-1)?.message))\n : budget?.retainTokens ?? Math.max(1, Math.floor(totalBefore * 0.25))\n const selected = selectCompactablePrefix(surface, retainTokens)\n if (selected.length === 0) return null\n const selectedSeqs = new Set(selected.map(node => node.seq))\n const estimatedNonCompactableTokens = estimateContextTokens({\n system,\n messages: [\n ...pinned,\n ...surface.filter(node => !selectedSeqs.has(node.seq)).map(node => node.message),\n ],\n tools,\n })\n const compactionId = newCompactionId()\n const startedAt = new Date().toISOString()\n history.append({ kind: 'compaction-start', compactionId, trigger, at: startedAt })\n try {\n await this.emitObserver(emit, {\n type: 'compaction-start', compactionId, trigger, estimatedInputTokens: totalBefore,\n })\n const summarized = await this.summarize(\n [...pinned, ...selected.map(node => node.message)], system, tools, operationSignal,\n )\n // Adapters are contractually expected to honor cancellation, but the\n // compactor must not commit stale maintenance if one fails to do so.\n operationSignal.throwIfAborted()\n const checkpoint = createUserMessage({\n source: { kind: 'app', producer: `agent-compaction:${compactionId}` },\n content: [{\n type: 'text',\n text: `${CHECKPOINT_PREAMBLE}\\n\\n<compacted-summary>\\n${summarized.summary}\\n</compacted-summary>`,\n }],\n })\n const shadowedTokens = selected.reduce((total, node) => total + estimateMessageTokens(node.message), 0)\n const checkpointTokens = estimateMessageTokens(checkpoint)\n if (checkpointTokens >= shadowedTokens) {\n throw new Error(\n `compaction summary did not shrink selected context (${checkpointTokens} >= ${shadowedTokens} estimated tokens)`,\n )\n }\n const shadowedSeqs = Object.freeze(selected.map(node => node.seq))\n const estimatedTokensAfter = Math.max(0, totalBefore - shadowedTokens + checkpointTokens)\n const thresholdTokens = budget?.thresholdTokens\n const pressureBackoff = pressureBackoffReason({\n trigger,\n thresholdTokens,\n estimatedTokensBefore: totalBefore,\n estimatedTokensAfter,\n estimatedNonCompactableTokens,\n })\n history.appendBatch([\n { event: {\n kind: 'compaction-summary', compactionId, summary: summarized.summary, shadowedSeqs,\n estimatedTokensBefore: totalBefore, estimatedTokensAfter,\n provider: summarized.provider, model: summarized.model,\n ...(summarized.usage === undefined ? {} : { usage: summarized.usage }),\n } },\n { event: { kind: 'user', message: checkpoint }, surfaceOp: {\n op: 'replace',\n from: Math.min(...shadowedSeqs),\n to: Math.max(...shadowedSeqs),\n targets: shadowedSeqs,\n } },\n { event: {\n kind: 'compaction-end', compactionId, status: 'completed', at: new Date().toISOString(),\n ...(thresholdTokens === undefined ? {} : { thresholdTokens }),\n estimatedNonCompactableTokens,\n ...(pressureBackoff === undefined ? {} : {\n backoffReason: pressureBackoff,\n cooldownSteps: PRESSURE_BACKOFF_STEPS,\n }),\n } },\n ])\n const result: CompactionResult = Object.freeze({\n compactionId, trigger, summary: summarized.summary, shadowedSeqs,\n estimatedTokensBefore: totalBefore, estimatedTokensAfter,\n ...(thresholdTokens === undefined ? {} : { thresholdTokens }),\n estimatedNonCompactableTokens,\n ...(pressureBackoff === undefined ? {} : {\n backoffReason: pressureBackoff,\n cooldownSteps: PRESSURE_BACKOFF_STEPS,\n }),\n provider: summarized.provider, model: summarized.model,\n ...(summarized.usage === undefined ? {} : { usage: summarized.usage }),\n })\n await this.emitObserver(emit, {\n type: 'compaction-end', compactionId, trigger, status: 'completed', shadowedSeqs,\n estimatedTokensBefore: totalBefore, estimatedTokensAfter, summary: summarized.summary,\n ...(thresholdTokens === undefined ? {} : { thresholdTokens }),\n estimatedNonCompactableTokens,\n ...(pressureBackoff === undefined ? {} : {\n backoffReason: pressureBackoff,\n cooldownSteps: PRESSURE_BACKOFF_STEPS,\n }),\n ...(summarized.usage === undefined ? {} : { usage: summarized.usage }),\n })\n return result\n } catch (error: unknown) {\n const failure = deadline.aborted && !signal.aborted && errorCode(error) !== 'MODEL_TEARDOWN_TIMEOUT'\n ? modelTimeoutError(this.input.policy.summaryTimeoutMs, error)\n : error\n const message = errorMessage(failure)\n try {\n history.append({\n kind: 'compaction-end', compactionId, status: 'failed', at: new Date().toISOString(), error: message,\n })\n } catch {\n // Preserve the original compaction failure when the append-only history\n // has no remaining capacity for the diagnostic end marker.\n }\n await this.emitObserver(emit, {\n type: 'compaction-end', compactionId, trigger, status: 'failed', shadowedSeqs: [],\n estimatedTokensBefore: totalBefore, estimatedTokensAfter: totalBefore, error: message,\n })\n throw failure\n }\n } finally {\n this.active = false\n }\n }\n\n private async resolveBudget(totalTokens: number, signal: AbortSignal): Promise<ResolvedBudget | null> {\n const policy = this.input.policy\n const config = this.input.config()\n const key = `${config.provider}\\u0000${config.model}\\u0000${String(config.maxTokens ?? '')}`\n if (this.modelBudget?.key !== key) {\n try {\n const info = await raceWithSignal(this.input.registry.resolveModelInfo(\n config.provider, config.model, signal,\n ), signal)\n const contextWindow = info.context?.contextWindow\n this.modelBudget = { key, budget: contextWindow === undefined\n ? null\n : {\n contextWindow,\n outputReserve: config.maxTokens\n ?? info.defaultMaxTokens\n ?? info.maxOutputTokens\n ?? 0,\n } }\n } catch {\n if (policy.maxInputTokens === undefined) return null\n this.modelBudget = { key, budget: null }\n }\n }\n const contextWindow = this.modelBudget?.budget?.contextWindow\n const inputWindow = contextWindow === undefined\n ? undefined\n : Math.max(1, contextWindow - (this.modelBudget?.budget?.outputReserve ?? 0))\n const ratioThreshold = contextWindow === undefined\n ? undefined\n : Math.floor(contextWindow * policy.thresholdRatio)\n const safeThreshold = inputWindow === undefined || ratioThreshold === undefined\n ? undefined\n : Math.min(inputWindow, ratioThreshold)\n const thresholdTokens = policy.maxInputTokens === undefined\n ? safeThreshold\n : inputWindow === undefined\n ? policy.maxInputTokens\n : Math.min(policy.maxInputTokens, inputWindow)\n if (thresholdTokens === undefined) return null\n const inferredWindow = inputWindow ?? Math.max(totalTokens, Math.ceil(thresholdTokens / policy.thresholdRatio))\n const retainTokens = policy.retainTokens\n ?? Math.floor(inferredWindow * (policy.retainRatio ?? 0.2))\n return { thresholdTokens, retainTokens: Math.max(1, retainTokens) }\n }\n\n private async emitObserver(\n emit: ((event: AgentMaintenanceEvent) => Promise<void>) | undefined,\n event: AgentMaintenanceEvent,\n ): Promise<void> {\n if (emit === undefined) return\n const pending = Promise.resolve().then(() => emit(event))\n await waitForSettlement(pending, this.input.policy.teardownTimeoutMs)\n }\n\n private async summarize(\n messages: readonly Message[],\n system: string,\n tools: readonly ModelToolSchema[],\n signal: AbortSignal,\n ): Promise<{ summary: string; provider: string; model: string; usage?: TokenUsage }> {\n const policy = this.input.policy\n const active = this.input.config()\n const provider = policy.summarizationProvider ?? active.provider\n const model = policy.summarizationModel ?? active.model\n const accounting = compactionAccounting(this)\n assertUsageAdmission(accounting)\n const summaryInfo = await raceWithSignal(\n this.input.registry.resolveModelInfo(provider, model, signal),\n signal,\n )\n const maxSummaryTokens = Math.min(\n policy.maxSummaryTokens,\n summaryInfo.maxOutputTokens ?? Number.MAX_SAFE_INTEGER,\n summaryInfo.context === undefined\n ? Number.MAX_SAFE_INTEGER\n : Math.max(1, summaryInfo.context.contextWindow - 1),\n )\n const prepared = await raceWithSignal(this.input.registry.prepareCall({\n provider,\n model,\n ...(policy.summarizationEffort === undefined\n ? {}\n : { reasoningEffort: ReasoningEffortId(policy.summarizationEffort) }),\n maxTokens: maxSummaryTokens,\n }, signal, accounting?.modelInvocation), signal)\n // Compaction can encounter history written by an older runtime or a process\n // interrupted between a tool call and its result. Repair the replay just as\n // normal model requests do so a maintenance request cannot be provider-invalid.\n const characterBudget: CharacterBudget = { remaining: policy.maxSummaryRequestChars }\n const replay = normalizeToolPairing(messages)\n .map(message => sanitizeForSummary(message, policy.maxSummaryInputChars, characterBudget))\n const instruction = createUserMessage({\n source: { kind: 'app', producer: 'agent-compaction' },\n content: [{ type: 'text', text: COMPACTION_INSTRUCTION }],\n })\n const assembler = new BlockAssembler()\n const request = {\n ...prepared.config,\n messages: [...replay, instruction],\n ...(system.length === 0 ? {} : { system }),\n ...(tools.length === 0 ? {} : { tools }),\n toolChoice: 'none',\n signal,\n } as const\n const { signal: _signal, ...serializableRequest } = request\n if (serializedBytes(serializableRequest) > policy.maxSummaryRequestBytes) {\n throw codedError(\n `compaction summary request exceeds the ${policy.maxSummaryRequestBytes}-byte limit`,\n 'MODEL_REQUEST_TOO_LARGE',\n )\n }\n assertUsageAdmission(accounting)\n const handle = prepared.stream(request, accounting?.modelInvocation)\n const iterator = handle[Symbol.asyncIterator]()\n let exhausted = false\n let events = 0\n let responseBytes = 0\n let sawFinish = false\n let failure: unknown\n try {\n while (true) {\n const next = await raceWithSignal(iterator.next(), signal)\n if (next.done === true) {\n exhausted = true\n break\n }\n events++\n responseBytes += serializedBytes(next.value)\n if (events > policy.maxSummaryStreamEvents\n || responseBytes > policy.maxSummaryResponseBytes) {\n throw codedError('compaction summary response exceeded its resource limit', 'MODEL_RESPONSE_TOO_LARGE')\n }\n if (sawFinish) {\n throw codedError('compaction summarizer emitted data after its terminal finish chunk', 'INVALID_MODEL_STREAM')\n }\n assembler.push(next.value)\n if (next.value.type === 'finish') sawFinish = true\n }\n } catch (error: unknown) {\n failure = error\n } finally {\n if (!exhausted) {\n const close = iterator.return?.bind(iterator)\n if (close !== undefined) {\n try {\n const closing = Promise.resolve().then(async () => { await close() })\n if (!await waitForSettlement(closing, policy.teardownTimeoutMs)) failure = codedError(\n `compaction model stream ignored cancellation for more than ${policy.teardownTimeoutMs}ms`,\n 'MODEL_TEARDOWN_TIMEOUT',\n )\n } catch (error: unknown) { failure ??= error }\n }\n }\n }\n try {\n const report = await handle.report\n const decision = await accounting?.recordModelCall(report, request)\n if (decision?.usageRequired === true) {\n failure ??= codedError('compaction model usage is required by the configured run policy', 'USAGE_REQUIRED')\n }\n if (decision?.usageUnavailable === true) {\n failure ??= codedError('compaction model usage is unavailable for the configured budget', 'USAGE_UNAVAILABLE')\n }\n } catch (error: unknown) { failure ??= error }\n if (failure !== undefined) throw failure\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n const error = new Error(finish.failure.message) as Error & { code?: string }\n error.code = finish.failure.code\n throw error\n }\n if (finish.kind === 'max-tokens') throw new Error('compaction summary was truncated at maxSummaryTokens')\n if (finish.kind === 'tool-calls') throw new Error('compaction summarizer attempted a tool call')\n const summary = assembler.blocks().flatMap(block => block.type === 'text' ? [block.text] : []).join('\\n').trim()\n if (summary.length === 0) throw new Error('compaction summarizer produced no text')\n return {\n summary, provider, model,\n ...(assembler.usage === undefined ? {} : { usage: assembler.usage }),\n }\n }\n}\n\nfunction sanitizeForSummary(\n message: Message,\n maxChars: number,\n budget: CharacterBudget,\n): Message {\n const content = message.content.flatMap(block => sanitizeBlock(block, maxChars, budget))\n const source = message.source.kind === 'model'\n ? { kind: 'model' as const, provider: message.source.provider, model: message.source.model }\n : message.source\n return createMessage({ role: message.role, source, content })\n}\n\nfunction sanitizeBlock(\n block: ContentBlock,\n maxChars: number,\n budget: CharacterBudget,\n): ContentBlock[] {\n switch (block.type) {\n case 'text': return [{ type: 'text', text: boundedText(block.text, maxChars, budget) }]\n case 'reasoning': return block.text.length === 0 ? [] : [{\n type: 'text', text: boundedText(`[Reasoning summary] ${block.text}`, maxChars, budget),\n }]\n case 'image': return [{ type: 'text', text: boundedText(`[Image input: ${block.source.kind}]`, maxChars, budget) }]\n case 'document': return [{\n type: 'text',\n text: boundedText(`[Document input: ${block.filename ?? block.source.kind}]`, maxChars, budget),\n }]\n case 'tool-call': return [{\n ...block, arguments: boundedToolArguments(block.arguments, maxChars, budget),\n } satisfies ToolCallBlock]\n case 'tool-result': return [{\n ...block,\n content: block.content.flatMap(child => sanitizeBlock(child, maxChars, budget)),\n } satisfies ToolResultBlock]\n case 'native-tool-call': return [{\n type: 'text',\n text: boundedText(\n `[Native tool ${block.name} (${block.status ?? 'unknown'})] ${safeJson(block.arguments ?? {})}`,\n maxChars,\n budget,\n ),\n }]\n default: return [{\n type: 'text', text: boundedText(`[Unsupported content block: ${safeJson(block)}]`, maxChars, budget),\n }]\n }\n}\n\nfunction boundedText(value: string, maxChars: number, budget: CharacterBudget): string {\n const allowed = Math.max(0, Math.min(maxChars, budget.remaining))\n const result = truncateMiddle(value, allowed)\n budget.remaining -= result.length\n return result\n}\n\nfunction boundedToolArguments(value: string, maxChars: number, budget: CharacterBudget): string {\n const allowed = Math.max(0, Math.min(maxChars, budget.remaining))\n if (value.length <= allowed) {\n budget.remaining -= value.length\n return value\n }\n const placeholder = '{\"_compacted\":true}'\n if (placeholder.length <= allowed) {\n budget.remaining -= placeholder.length\n return placeholder\n }\n return '{}'\n}\n\nfunction safeJson(value: unknown): string {\n try { return JSON.stringify(value) ?? '' } catch { return String(value) }\n}\nfunction truncateMiddle(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n const marker = '\\n…[truncated for compaction]…\\n'\n if (maxChars <= marker.length) return value.slice(0, maxChars)\n const head = Math.ceil((maxChars - marker.length) / 2)\n const tail = Math.floor((maxChars - marker.length) / 2)\n return value.slice(0, head) + marker + value.slice(value.length - tail)\n}\nfunction newCompactionId(): string {\n return systemRandomId()\n}\nfunction errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error) }\nfunction errorCode(error: unknown): string | undefined {\n return typeof error === 'object' && error !== null && 'code' in error && typeof error.code === 'string'\n ? error.code\n : undefined\n}\n\nfunction serializedBytes(value: unknown): number {\n const serialized = JSON.stringify(value)\n if (serialized === undefined) throw new TypeError('compaction model payload is not JSON serializable')\n return new TextEncoder().encode(serialized).byteLength\n}\n\nfunction assertUsageAdmission(accounting: ReturnType<typeof compactionAccounting>): void {\n const stop = accounting?.usageStop\n if (stop !== undefined) throw codedError(\n 'compaction cannot dispatch after a mandatory usage stop',\n stop.usageRequired ? 'USAGE_REQUIRED' : 'USAGE_UNAVAILABLE',\n )\n}\n\nfunction codedError(message: string, code: string, cause?: unknown): Error & { code: string } {\n const error = new Error(message, cause === undefined ? undefined : { cause }) as Error & { code: string }\n error.code = code\n return error\n}\n\nfunction modelTimeoutError(timeoutMs: number, cause: unknown): Error & { code: string } {\n return codedError(`compaction model operation exceeded ${timeoutMs}ms`, 'MODEL_TIMEOUT', cause)\n}\n\nasync function raceWithSignal<T>(pending: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) {\n void pending.catch(() => undefined)\n throw signal.reason ?? new Error('operation aborted')\n }\n return await new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n signal.removeEventListener('abort', onAbort)\n reject(signal.reason ?? new Error('operation aborted'))\n }\n signal.addEventListener('abort', onAbort, { once: true })\n void pending.then(\n value => {\n signal.removeEventListener('abort', onAbort)\n resolve(value)\n },\n error => {\n signal.removeEventListener('abort', onAbort)\n reject(error)\n },\n )\n })\n}\n\nfunction pressureBackoffReason(input: {\n readonly trigger: CompactionTrigger\n readonly thresholdTokens: number | undefined\n readonly estimatedTokensBefore: number\n readonly estimatedTokensAfter: number\n readonly estimatedNonCompactableTokens: number\n}): CompactionBackoffReason | undefined {\n if (input.trigger !== 'pressure' || input.thresholdTokens === undefined\n || input.estimatedTokensAfter < input.thresholdTokens) return undefined\n if (input.estimatedNonCompactableTokens >= input.thresholdTokens) {\n return 'unreachable-threshold'\n }\n const savings = input.estimatedTokensBefore - input.estimatedTokensAfter\n const minimum = Math.max(\n MIN_PRESSURE_SAVINGS_TOKENS,\n Math.ceil(input.estimatedTokensBefore * MIN_PRESSURE_SAVINGS_RATIO),\n )\n return savings < minimum ? 'low-savings' : undefined\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compaction.js","names":[],"sources":["../../../src/agent/memory/compaction.ts"],"sourcesContent":["import { systemRandomId } from '../../platform/adapter.ts'\n/** Context-pressure compaction inspired by Codex checkpoints and deepseek-harness surface replacement. */\n\nimport type { CallConfig } from '../../contract/index.ts'\nimport type { ModelToolSchema } from '../../contract/index.ts'\nimport type { ContentBlock, ToolCallBlock, ToolResultBlock } from '../../message/index.ts'\nimport { createMessage, createUserMessage, type Message } from '../../message/index.ts'\nimport { ReasoningEffortId } from '../../primitives/index.ts'\nimport type { ModelRegistry } from '../../runtime/index.ts'\nimport { waitForSettlement } from '../../async/index.ts'\nimport { BlockAssembler } from '../../stream/index.ts'\nimport type { TokenUsage } from '../../stream/index.ts'\nimport type { History } from '../history/history.ts'\nimport { normalizeToolPairing } from '../history/normalize.ts'\nimport type {\n AgentMaintenanceEvent, BeforeStepContext, CompactionBackoffReason, CompactionTrigger, RequestErrorContext,\n} from '../loop/events.ts'\nimport type { AgentCompactionConfig } from './compaction-config.ts'\nimport { compactionAccounting } from './accounting-binding.ts'\nimport type { RunReport } from '../accounting/delivery-types.ts'\nimport { pruneToolResults, selectCompactablePrefix } from './surface-compaction.ts'\nimport { estimateContextTokens, estimateMessageTokens } from './token-estimator.ts'\n\nexport interface CompactionResult {\n readonly compactionId: string\n readonly trigger: CompactionTrigger\n readonly summary: string\n readonly shadowedSeqs: readonly number[]\n readonly estimatedTokensBefore: number\n readonly estimatedTokensAfter: number\n readonly thresholdTokens?: number\n readonly estimatedNonCompactableTokens: number\n readonly backoffReason?: CompactionBackoffReason\n readonly cooldownSteps?: number\n readonly provider: string\n readonly model: string\n readonly usage?: TokenUsage\n /** Additive runtime projection; absent on the preserved low-level compactor path. */\n readonly status?: 'completed'\n readonly report?: RunReport\n}\n\nexport interface ContextCompactorOptions {\n readonly registry: ModelRegistry\n /**\n * The call configuration in force RIGHT NOW, not when the compactor was built.\n *\n * A supplier rather than a value because a run may target a different model\n * than the session it belongs to: the context window a checkpoint is budgeted\n * against, and the model a summary is written by, both have to follow that\n * override or compaction would reason about a window nobody is calling.\n */\n readonly config: () => CallConfig\n readonly history: () => History\n readonly system: () => string\n readonly pinnedMessages?: () => readonly Message[]\n readonly tools: () => readonly ModelToolSchema[]\n readonly policy: AgentCompactionConfig\n}\n\ninterface ResolvedBudget {\n readonly thresholdTokens: number\n readonly retainTokens: number\n}\n\ninterface CharacterBudget { remaining: number }\n\nconst CHECKPOINT_PREAMBLE =\n 'This is an automatically generated checkpoint of earlier conversation context. Treat it as established background, preserve the original objective and constraints, and continue directly from the messages that follow.'\n\nconst MIN_PRESSURE_SAVINGS_TOKENS = 256\nconst MIN_PRESSURE_SAVINGS_RATIO = 0.01\nconst PRESSURE_BACKOFF_STEPS = 4\n\nexport const COMPACTION_INSTRUCTION = [\n 'You are performing a CONTEXT CHECKPOINT COMPACTION for a long-running AI agent task.',\n 'Condense the conversation above into a handoff that lets another model continue without forgetting the original purpose.',\n '',\n 'Output exactly these Markdown sections, in order, using terse bullets. Write \"(none)\" for an empty section.',\n '## Primary Request and Intent',\n '## Progress and Completed Work',\n '## Decisions and Rationale',\n '## Constraints and User Preferences',\n '## Important Files, Symbols, and Evidence',\n '## Errors and Failed Approaches',\n '## Pending Work',\n '## Current Work',\n '## Next Step',\n '## Critical Context',\n '',\n 'Preserve exact paths, identifiers, commands, error strings, numeric limits, and user corrections when they matter.',\n 'Under Important Files, mark relevant files as inspected or modified and preserve the exact declarations or findings needed next.',\n 'Under Current Work, record the last successful tool action, the latest verification result, and whether any edit is still partial.',\n 'Under Next Step, name one direct edit or command. Do not recommend rereading an unchanged file whose relevant contents are already recorded.',\n 'Do not reveal hidden chain-of-thought. Record only public progress, observed evidence, decisions, and actionable context.',\n 'If an earlier <compacted-summary> exists, merge still-valid facts with newer information and drop stale facts.',\n 'Output only the checkpoint. Do not call tools and do not acknowledge the compaction.',\n].join('\\n')\n\nexport class ContextCompactor {\n private readonly input: ContextCompactorOptions\n private active = false\n private overflowTurn = -1\n private overflowRetries = 0\n private recoveringOverflow = false\n /** Keyed by route + model + output reserve: an override changes all three. */\n private modelBudget: {\n readonly key: string\n readonly budget: { readonly contextWindow: number; readonly outputReserve: number } | null\n } | undefined\n private pressureCooldown = 0\n\n constructor(input: ContextCompactorOptions) {\n this.input = input\n }\n\n /** Fail-open pressure maintenance for a pre-step hook. */\n async beforeStep(context: BeforeStepContext): Promise<void> {\n if (!this.input.policy.auto || context.signal.aborted) return\n if (this.pressureCooldown > 0) {\n this.pressureCooldown--\n return\n }\n if (this.overflowTurn !== context.turn) {\n this.overflowTurn = context.turn\n this.overflowRetries = 0\n } else if (!this.recoveringOverflow) {\n this.overflowRetries = 0\n }\n this.recoveringOverflow = false\n try {\n let lastResult: CompactionResult | undefined\n for (let attempt = 0; attempt <= this.input.policy.compactionRetries; attempt++) {\n const result = await this.compactIfNeeded('pressure', context.signal, context.emit)\n if (result === null) break\n lastResult = result\n if (result.backoffReason !== undefined) {\n this.pressureCooldown = result.cooldownSteps ?? PRESSURE_BACKOFF_STEPS\n break\n }\n }\n if (lastResult !== undefined\n && lastResult.thresholdTokens !== undefined\n && lastResult.estimatedTokensAfter >= lastResult.thresholdTokens) {\n this.pressureCooldown = Math.max(\n this.pressureCooldown,\n PRESSURE_BACKOFF_STEPS,\n )\n }\n } catch {\n // Pressure maintenance is fail-open; the provider's canonical overflow\n // error still gets one forced recovery path below. Avoid retrying the same\n // broken maintenance request on every tool step and flooding logs/traces.\n // Mandatory usage decisions stay latched in the owning ledger and are\n // checked by modelRound after this hook, before any main dispatch.\n this.pressureCooldown = 2\n }\n }\n\n /** Compact and retry only when a provider confirmed context overflow. */\n async onRequestError(context: RequestErrorContext): Promise<'retry' | undefined> {\n if (context.failure.code !== 'CONTEXT_WINDOW_EXCEEDED' || context.signal.aborted) return undefined\n if (this.overflowTurn !== context.turn) {\n this.overflowTurn = context.turn\n this.overflowRetries = 0\n }\n if (this.overflowRetries >= this.input.policy.maxOverflowRetries) return undefined\n try {\n const result = await this.compactIfNeeded('context-overflow', context.signal, context.emit)\n if (result === null || context.signal.aborted) return undefined\n this.overflowRetries++\n this.recoveringOverflow = true\n return 'retry'\n } catch {\n return undefined\n }\n }\n\n /** Explicit idle-session checkpoint even below automatic pressure. */\n async compactNow(signal?: AbortSignal): Promise<CompactionResult | null> {\n const controller = signal === undefined ? new AbortController() : undefined\n const effectiveSignal = signal ?? controller?.signal\n if (effectiveSignal === undefined) throw new Error('failed to create compaction signal')\n return this.compactIfNeeded('manual', effectiveSignal)\n }\n\n private async compactIfNeeded(\n trigger: CompactionTrigger,\n signal: AbortSignal,\n emit?: (event: AgentMaintenanceEvent) => Promise<void>,\n ): Promise<CompactionResult | null> {\n if (this.active) return null\n const deadline = AbortSignal.timeout(this.input.policy.summaryTimeoutMs)\n const operationSignal = AbortSignal.any([signal, deadline])\n operationSignal.throwIfAborted()\n this.active = true\n try {\n const history = this.input.history()\n let surface = history.surface()\n if (surface.length < 2) return null\n const tools = this.input.tools()\n const system = this.input.system()\n const pinned = this.input.pinnedMessages?.() ?? []\n let totalBefore = estimateContextTokens({\n system, messages: [...pinned, ...surface.map(node => node.message)], tools,\n })\n let budget = await this.resolveBudget(totalBefore, operationSignal)\n operationSignal.throwIfAborted()\n if (trigger === 'pressure' && (budget === null || totalBefore < budget.thresholdTokens)) return null\n const pruned = pruneToolResults(history, surface, this.input.policy.maxToolResultChars)\n if (pruned > 0) {\n surface = history.surface()\n totalBefore = estimateContextTokens({\n system, messages: [...pinned, ...surface.map(node => node.message)], tools,\n })\n if (trigger === 'pressure' && budget !== null && totalBefore < budget.thresholdTokens) return null\n // With an absolute threshold and no provider context metadata, retainRatio\n // is inferred from the measured request. Pruning can change that request\n // by orders of magnitude, so selection must use the post-prune budget.\n budget = await this.resolveBudget(totalBefore, operationSignal)\n operationSignal.throwIfAborted()\n }\n const retainTokens = trigger === 'context-overflow'\n ? Math.max(1, estimateMessageTokens(surface.at(-1)?.message))\n : budget?.retainTokens ?? Math.max(1, Math.floor(totalBefore * 0.25))\n const selected = selectCompactablePrefix(surface, retainTokens)\n if (selected.length === 0) return null\n const selectedSeqs = new Set(selected.map(node => node.seq))\n const estimatedNonCompactableTokens = estimateContextTokens({\n system,\n messages: [\n ...pinned,\n ...surface.filter(node => !selectedSeqs.has(node.seq)).map(node => node.message),\n ],\n tools,\n })\n const compactionId = newCompactionId()\n const startedAt = new Date().toISOString()\n history.append({ kind: 'compaction-start', compactionId, trigger, at: startedAt })\n try {\n await this.emitObserver(emit, {\n type: 'compaction-start', compactionId, trigger, estimatedInputTokens: totalBefore,\n })\n const summarized = await this.summarize(\n [...pinned, ...selected.map(node => node.message)], system, tools, operationSignal,\n )\n // Adapters are contractually expected to honor cancellation, but the\n // compactor must not commit stale maintenance if one fails to do so.\n operationSignal.throwIfAborted()\n const checkpoint = createUserMessage({\n source: { kind: 'app', producer: `agent-compaction:${compactionId}` },\n content: [{\n type: 'text',\n text: `${CHECKPOINT_PREAMBLE}\\n\\n<compacted-summary>\\n${summarized.summary}\\n</compacted-summary>`,\n }],\n })\n const shadowedTokens = selected.reduce((total, node) => total + estimateMessageTokens(node.message), 0)\n const checkpointTokens = estimateMessageTokens(checkpoint)\n if (checkpointTokens >= shadowedTokens) {\n throw new Error(\n `compaction summary did not shrink selected context (${checkpointTokens} >= ${shadowedTokens} estimated tokens)`,\n )\n }\n const shadowedSeqs = Object.freeze(selected.map(node => node.seq))\n const estimatedTokensAfter = Math.max(0, totalBefore - shadowedTokens + checkpointTokens)\n const thresholdTokens = budget?.thresholdTokens\n const pressureBackoff = pressureBackoffReason({\n trigger,\n thresholdTokens,\n estimatedTokensBefore: totalBefore,\n estimatedTokensAfter,\n estimatedNonCompactableTokens,\n })\n history.appendBatch([\n { event: {\n kind: 'compaction-summary', compactionId, summary: summarized.summary, shadowedSeqs,\n estimatedTokensBefore: totalBefore, estimatedTokensAfter,\n provider: summarized.provider, model: summarized.model,\n ...(summarized.usage === undefined ? {} : { usage: summarized.usage }),\n } },\n { event: { kind: 'user', message: checkpoint }, surfaceOp: {\n op: 'replace',\n from: Math.min(...shadowedSeqs),\n to: Math.max(...shadowedSeqs),\n targets: shadowedSeqs,\n } },\n { event: {\n kind: 'compaction-end', compactionId, status: 'completed', at: new Date().toISOString(),\n ...(thresholdTokens === undefined ? {} : { thresholdTokens }),\n estimatedNonCompactableTokens,\n ...(pressureBackoff === undefined ? {} : {\n backoffReason: pressureBackoff,\n cooldownSteps: PRESSURE_BACKOFF_STEPS,\n }),\n } },\n ])\n const result: CompactionResult = Object.freeze({\n compactionId, trigger, summary: summarized.summary, shadowedSeqs,\n estimatedTokensBefore: totalBefore, estimatedTokensAfter,\n ...(thresholdTokens === undefined ? {} : { thresholdTokens }),\n estimatedNonCompactableTokens,\n ...(pressureBackoff === undefined ? {} : {\n backoffReason: pressureBackoff,\n cooldownSteps: PRESSURE_BACKOFF_STEPS,\n }),\n provider: summarized.provider, model: summarized.model,\n ...(summarized.usage === undefined ? {} : { usage: summarized.usage }),\n })\n await this.emitObserver(emit, {\n type: 'compaction-end', compactionId, trigger, status: 'completed', shadowedSeqs,\n estimatedTokensBefore: totalBefore, estimatedTokensAfter, summary: summarized.summary,\n ...(thresholdTokens === undefined ? {} : { thresholdTokens }),\n estimatedNonCompactableTokens,\n ...(pressureBackoff === undefined ? {} : {\n backoffReason: pressureBackoff,\n cooldownSteps: PRESSURE_BACKOFF_STEPS,\n }),\n ...(summarized.usage === undefined ? {} : { usage: summarized.usage }),\n })\n return result\n } catch (error: unknown) {\n const failure = deadline.aborted && !signal.aborted && errorCode(error) !== 'MODEL_TEARDOWN_TIMEOUT'\n ? modelTimeoutError(this.input.policy.summaryTimeoutMs, error)\n : error\n const message = errorMessage(failure)\n try {\n history.append({\n kind: 'compaction-end', compactionId, status: 'failed', at: new Date().toISOString(), error: message,\n })\n } catch {\n // Preserve the original compaction failure when the append-only history\n // has no remaining capacity for the diagnostic end marker.\n }\n await this.emitObserver(emit, {\n type: 'compaction-end', compactionId, trigger, status: 'failed', shadowedSeqs: [],\n estimatedTokensBefore: totalBefore, estimatedTokensAfter: totalBefore, error: message,\n })\n throw failure\n }\n } finally {\n this.active = false\n }\n }\n\n private async resolveBudget(totalTokens: number, signal: AbortSignal): Promise<ResolvedBudget | null> {\n const policy = this.input.policy\n const config = this.input.config()\n const key = `${config.provider}\\u0000${config.model}\\u0000${String(config.maxTokens ?? '')}`\n if (this.modelBudget?.key !== key) {\n try {\n const info = await raceWithSignal(this.input.registry.resolveModelInfo(\n config.provider, config.model, signal,\n ), signal)\n // The agent tier (CallConfig.contextWindow) wins over the model's own\n // resolved value, same precedence `resolveCallWithModelInfo` applies\n // at dispatch — this budget must agree with what actually gets sent.\n const contextWindow = config.contextWindow ?? info.context?.contextWindow\n this.modelBudget = { key, budget: contextWindow === undefined\n ? null\n : {\n contextWindow,\n outputReserve: config.maxTokens\n ?? info.defaultMaxTokens\n ?? info.maxOutputTokens\n ?? 0,\n } }\n } catch {\n if (policy.maxInputTokens === undefined) return null\n this.modelBudget = { key, budget: null }\n }\n }\n const contextWindow = this.modelBudget?.budget?.contextWindow\n const inputWindow = contextWindow === undefined\n ? undefined\n : Math.max(1, contextWindow - (this.modelBudget?.budget?.outputReserve ?? 0))\n const ratioThreshold = contextWindow === undefined\n ? undefined\n : Math.floor(contextWindow * policy.thresholdRatio)\n const safeThreshold = inputWindow === undefined || ratioThreshold === undefined\n ? undefined\n : Math.min(inputWindow, ratioThreshold)\n const thresholdTokens = policy.maxInputTokens === undefined\n ? safeThreshold\n : inputWindow === undefined\n ? policy.maxInputTokens\n : Math.min(policy.maxInputTokens, inputWindow)\n if (thresholdTokens === undefined) return null\n const inferredWindow = inputWindow ?? Math.max(totalTokens, Math.ceil(thresholdTokens / policy.thresholdRatio))\n const retainTokens = policy.retainTokens\n ?? Math.floor(inferredWindow * (policy.retainRatio ?? 0.2))\n return { thresholdTokens, retainTokens: Math.max(1, retainTokens) }\n }\n\n private async emitObserver(\n emit: ((event: AgentMaintenanceEvent) => Promise<void>) | undefined,\n event: AgentMaintenanceEvent,\n ): Promise<void> {\n if (emit === undefined) return\n const pending = Promise.resolve().then(() => emit(event))\n await waitForSettlement(pending, this.input.policy.teardownTimeoutMs)\n }\n\n private async summarize(\n messages: readonly Message[],\n system: string,\n tools: readonly ModelToolSchema[],\n signal: AbortSignal,\n ): Promise<{ summary: string; provider: string; model: string; usage?: TokenUsage }> {\n const policy = this.input.policy\n const active = this.input.config()\n const provider = policy.summarizationProvider ?? active.provider\n const model = policy.summarizationModel ?? active.model\n const accounting = compactionAccounting(this)\n assertUsageAdmission(accounting)\n const summaryInfo = await raceWithSignal(\n this.input.registry.resolveModelInfo(provider, model, signal),\n signal,\n )\n const maxSummaryTokens = Math.min(\n policy.maxSummaryTokens,\n summaryInfo.maxOutputTokens ?? Number.MAX_SAFE_INTEGER,\n summaryInfo.context === undefined\n ? Number.MAX_SAFE_INTEGER\n : Math.max(1, summaryInfo.context.contextWindow - 1),\n )\n const prepared = await raceWithSignal(this.input.registry.prepareCall({\n provider,\n model,\n ...(policy.summarizationEffort === undefined\n ? {}\n : { reasoningEffort: ReasoningEffortId(policy.summarizationEffort) }),\n maxTokens: maxSummaryTokens,\n }, signal, accounting?.modelInvocation), signal)\n // Compaction can encounter history written by an older runtime or a process\n // interrupted between a tool call and its result. Repair the replay just as\n // normal model requests do so a maintenance request cannot be provider-invalid.\n const characterBudget: CharacterBudget = { remaining: policy.maxSummaryRequestChars }\n const replay = normalizeToolPairing(messages)\n .map(message => sanitizeForSummary(message, policy.maxSummaryInputChars, characterBudget))\n const instruction = createUserMessage({\n source: { kind: 'app', producer: 'agent-compaction' },\n content: [{ type: 'text', text: COMPACTION_INSTRUCTION }],\n })\n const assembler = new BlockAssembler()\n const request = {\n ...prepared.config,\n messages: [...replay, instruction],\n ...(system.length === 0 ? {} : { system }),\n ...(tools.length === 0 ? {} : { tools }),\n toolChoice: 'none',\n signal,\n } as const\n const { signal: _signal, ...serializableRequest } = request\n if (serializedBytes(serializableRequest) > policy.maxSummaryRequestBytes) {\n throw codedError(\n `compaction summary request exceeds the ${policy.maxSummaryRequestBytes}-byte limit`,\n 'MODEL_REQUEST_TOO_LARGE',\n )\n }\n assertUsageAdmission(accounting)\n const handle = prepared.stream(request, accounting?.modelInvocation)\n const iterator = handle[Symbol.asyncIterator]()\n let exhausted = false\n let events = 0\n let responseBytes = 0\n let sawFinish = false\n let failure: unknown\n try {\n while (true) {\n const next = await raceWithSignal(iterator.next(), signal)\n if (next.done === true) {\n exhausted = true\n break\n }\n events++\n responseBytes += serializedBytes(next.value)\n if (events > policy.maxSummaryStreamEvents\n || responseBytes > policy.maxSummaryResponseBytes) {\n throw codedError('compaction summary response exceeded its resource limit', 'MODEL_RESPONSE_TOO_LARGE')\n }\n if (sawFinish) {\n throw codedError('compaction summarizer emitted data after its terminal finish chunk', 'INVALID_MODEL_STREAM')\n }\n assembler.push(next.value)\n if (next.value.type === 'finish') sawFinish = true\n }\n } catch (error: unknown) {\n failure = error\n } finally {\n if (!exhausted) {\n const close = iterator.return?.bind(iterator)\n if (close !== undefined) {\n try {\n const closing = Promise.resolve().then(async () => { await close() })\n if (!await waitForSettlement(closing, policy.teardownTimeoutMs)) failure = codedError(\n `compaction model stream ignored cancellation for more than ${policy.teardownTimeoutMs}ms`,\n 'MODEL_TEARDOWN_TIMEOUT',\n )\n } catch (error: unknown) { failure ??= error }\n }\n }\n }\n try {\n const report = await handle.report\n const decision = await accounting?.recordModelCall(report, request)\n if (decision?.usageRequired === true) {\n failure ??= codedError('compaction model usage is required by the configured run policy', 'USAGE_REQUIRED')\n }\n if (decision?.usageUnavailable === true) {\n failure ??= codedError('compaction model usage is unavailable for the configured budget', 'USAGE_UNAVAILABLE')\n }\n } catch (error: unknown) { failure ??= error }\n if (failure !== undefined) throw failure\n const finish = assembler.finish\n if (finish.kind === 'error' || finish.kind === 'aborted') {\n const error = new Error(finish.failure.message) as Error & { code?: string }\n error.code = finish.failure.code\n throw error\n }\n if (finish.kind === 'max-tokens') throw new Error('compaction summary was truncated at maxSummaryTokens')\n if (finish.kind === 'tool-calls') throw new Error('compaction summarizer attempted a tool call')\n const summary = assembler.blocks().flatMap(block => block.type === 'text' ? [block.text] : []).join('\\n').trim()\n if (summary.length === 0) throw new Error('compaction summarizer produced no text')\n return {\n summary, provider, model,\n ...(assembler.usage === undefined ? {} : { usage: assembler.usage }),\n }\n }\n}\n\nfunction sanitizeForSummary(\n message: Message,\n maxChars: number,\n budget: CharacterBudget,\n): Message {\n const content = message.content.flatMap(block => sanitizeBlock(block, maxChars, budget))\n const source = message.source.kind === 'model'\n ? { kind: 'model' as const, provider: message.source.provider, model: message.source.model }\n : message.source\n return createMessage({ role: message.role, source, content })\n}\n\nfunction sanitizeBlock(\n block: ContentBlock,\n maxChars: number,\n budget: CharacterBudget,\n): ContentBlock[] {\n switch (block.type) {\n case 'text': return [{ type: 'text', text: boundedText(block.text, maxChars, budget) }]\n case 'reasoning': return block.text.length === 0 ? [] : [{\n type: 'text', text: boundedText(`[Reasoning summary] ${block.text}`, maxChars, budget),\n }]\n case 'image': return [{ type: 'text', text: boundedText(`[Image input: ${block.source.kind}]`, maxChars, budget) }]\n case 'document': return [{\n type: 'text',\n text: boundedText(`[Document input: ${block.filename ?? block.source.kind}]`, maxChars, budget),\n }]\n case 'tool-call': return [{\n ...block, arguments: boundedToolArguments(block.arguments, maxChars, budget),\n } satisfies ToolCallBlock]\n case 'tool-result': return [{\n ...block,\n content: block.content.flatMap(child => sanitizeBlock(child, maxChars, budget)),\n } satisfies ToolResultBlock]\n case 'native-tool-call': return [{\n type: 'text',\n text: boundedText(\n `[Native tool ${block.name} (${block.status ?? 'unknown'})] ${safeJson(block.arguments ?? {})}`,\n maxChars,\n budget,\n ),\n }]\n default: return [{\n type: 'text', text: boundedText(`[Unsupported content block: ${safeJson(block)}]`, maxChars, budget),\n }]\n }\n}\n\nfunction boundedText(value: string, maxChars: number, budget: CharacterBudget): string {\n const allowed = Math.max(0, Math.min(maxChars, budget.remaining))\n const result = truncateMiddle(value, allowed)\n budget.remaining -= result.length\n return result\n}\n\nfunction boundedToolArguments(value: string, maxChars: number, budget: CharacterBudget): string {\n const allowed = Math.max(0, Math.min(maxChars, budget.remaining))\n if (value.length <= allowed) {\n budget.remaining -= value.length\n return value\n }\n const placeholder = '{\"_compacted\":true}'\n if (placeholder.length <= allowed) {\n budget.remaining -= placeholder.length\n return placeholder\n }\n return '{}'\n}\n\nfunction safeJson(value: unknown): string {\n try { return JSON.stringify(value) ?? '' } catch { return String(value) }\n}\nfunction truncateMiddle(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n const marker = '\\n…[truncated for compaction]…\\n'\n if (maxChars <= marker.length) return value.slice(0, maxChars)\n const head = Math.ceil((maxChars - marker.length) / 2)\n const tail = Math.floor((maxChars - marker.length) / 2)\n return value.slice(0, head) + marker + value.slice(value.length - tail)\n}\nfunction newCompactionId(): string {\n return systemRandomId()\n}\nfunction errorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error) }\nfunction errorCode(error: unknown): string | undefined {\n return typeof error === 'object' && error !== null && 'code' in error && typeof error.code === 'string'\n ? error.code\n : undefined\n}\n\nfunction serializedBytes(value: unknown): number {\n const serialized = JSON.stringify(value)\n if (serialized === undefined) throw new TypeError('compaction model payload is not JSON serializable')\n return new TextEncoder().encode(serialized).byteLength\n}\n\nfunction assertUsageAdmission(accounting: ReturnType<typeof compactionAccounting>): void {\n const stop = accounting?.usageStop\n if (stop !== undefined) throw codedError(\n 'compaction cannot dispatch after a mandatory usage stop',\n stop.usageRequired ? 'USAGE_REQUIRED' : 'USAGE_UNAVAILABLE',\n )\n}\n\nfunction codedError(message: string, code: string, cause?: unknown): Error & { code: string } {\n const error = new Error(message, cause === undefined ? undefined : { cause }) as Error & { code: string }\n error.code = code\n return error\n}\n\nfunction modelTimeoutError(timeoutMs: number, cause: unknown): Error & { code: string } {\n return codedError(`compaction model operation exceeded ${timeoutMs}ms`, 'MODEL_TIMEOUT', cause)\n}\n\nasync function raceWithSignal<T>(pending: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) {\n void pending.catch(() => undefined)\n throw signal.reason ?? new Error('operation aborted')\n }\n return await new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n signal.removeEventListener('abort', onAbort)\n reject(signal.reason ?? new Error('operation aborted'))\n }\n signal.addEventListener('abort', onAbort, { once: true })\n void pending.then(\n value => {\n signal.removeEventListener('abort', onAbort)\n resolve(value)\n },\n error => {\n signal.removeEventListener('abort', onAbort)\n reject(error)\n },\n )\n })\n}\n\nfunction pressureBackoffReason(input: {\n readonly trigger: CompactionTrigger\n readonly thresholdTokens: number | undefined\n readonly estimatedTokensBefore: number\n readonly estimatedTokensAfter: number\n readonly estimatedNonCompactableTokens: number\n}): CompactionBackoffReason | undefined {\n if (input.trigger !== 'pressure' || input.thresholdTokens === undefined\n || input.estimatedTokensAfter < input.thresholdTokens) return undefined\n if (input.estimatedNonCompactableTokens >= input.thresholdTokens) {\n return 'unreachable-threshold'\n }\n const savings = input.estimatedTokensBefore - input.estimatedTokensAfter\n const minimum = Math.max(\n MIN_PRESSURE_SAVINGS_TOKENS,\n Math.ceil(input.estimatedTokensBefore * MIN_PRESSURE_SAVINGS_RATIO),\n )\n return savings < minimum ? 'low-savings' : undefined\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ToolDefinition } from "../tool/definition.js";
|
|
2
|
+
import { SpillStore } from "../tool/output-budget.js";
|
|
3
|
+
import { HistorySnapshot } from "../history/types.js";
|
|
4
|
+
import { TurnHooks } from "../loop/events.js";
|
|
5
|
+
import { EvidenceReducer } from "../tool/evidence-reducer.js";
|
|
6
|
+
//#region src/agent/memory/context-optimizer.d.ts
|
|
7
|
+
interface ContextMilestone {
|
|
8
|
+
readonly id: string;
|
|
9
|
+
/** Inclusive append-only sequence boundary, captured after the completed step. */
|
|
10
|
+
readonly throughSeq: number;
|
|
11
|
+
/** Host-verified state, including decisions and unresolved constraints needed later. */
|
|
12
|
+
readonly summary: string;
|
|
13
|
+
readonly remainingTurns: number;
|
|
14
|
+
/** Same cost units as historyTokenCost; include input/output and summary-model price. */
|
|
15
|
+
readonly compactionCost: number;
|
|
16
|
+
readonly historyTokenCost?: number;
|
|
17
|
+
}
|
|
18
|
+
interface ContextOptimizerOptions {
|
|
19
|
+
/** Mount the returned retrievalTool in the same session. Use a store scoped to that session. */
|
|
20
|
+
readonly store: SpillStore;
|
|
21
|
+
readonly observationThresholdBytes?: number;
|
|
22
|
+
readonly summaryBytes?: number;
|
|
23
|
+
readonly fullRequests?: number;
|
|
24
|
+
readonly maxObservations?: number;
|
|
25
|
+
readonly maxMilestones?: number;
|
|
26
|
+
/** Persist raw snapshots locally/durably before a milestone projection is accepted. */
|
|
27
|
+
readonly archive?: (snapshot: HistorySnapshot, milestone: ContextMilestone, signal: AbortSignal) => Promise<void>;
|
|
28
|
+
readonly reducer?: EvidenceReducer;
|
|
29
|
+
/** Explicitly identify logs and their authoritative outcome; core does not guess exit status. */
|
|
30
|
+
readonly log?: (toolName: string, text: string) => {
|
|
31
|
+
readonly status: 'pass' | 'fail' | 'unknown';
|
|
32
|
+
readonly requiredLines?: readonly number[];
|
|
33
|
+
} | undefined;
|
|
34
|
+
readonly reductionThresholdBytes?: number;
|
|
35
|
+
}
|
|
36
|
+
interface ContextOptimizationMetrics {
|
|
37
|
+
readonly packedObservations: number;
|
|
38
|
+
readonly verifiedReductions: number;
|
|
39
|
+
readonly rejectedReductions: number;
|
|
40
|
+
readonly compactedMilestones: number;
|
|
41
|
+
readonly skippedMilestones: number;
|
|
42
|
+
/** Estimate of payload tokens avoided across prepared requests; never billing usage. */
|
|
43
|
+
readonly estimatedTokensSaved: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* One controller per session. Projection affects requests only; snapshots retain raw history.
|
|
47
|
+
* Compose its hooks with application hooks using wrapHooks(), and register retrievalTool.
|
|
48
|
+
* Disposal drops projection state, never tool receipts or host-owned storage.
|
|
49
|
+
*/
|
|
50
|
+
declare function createContextOptimizer(options: ContextOptimizerOptions): Readonly<{
|
|
51
|
+
hooks: TurnHooks;
|
|
52
|
+
retrievalTool: ToolDefinition<unknown>;
|
|
53
|
+
completeMilestone(milestone: ContextMilestone): void;
|
|
54
|
+
/** Application hook runs first; checkpoints count only after host persistence succeeds. */
|
|
55
|
+
wrapHooks(application?: TurnHooks): TurnHooks;
|
|
56
|
+
metrics(): ContextOptimizationMetrics;
|
|
57
|
+
dispose(): void;
|
|
58
|
+
}>;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { ContextMilestone, ContextOptimizationMetrics, ContextOptimizerOptions, createContextOptimizer };
|
|
61
|
+
//# sourceMappingURL=context-optimizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-optimizer.d.ts","names":[],"sources":["../../../src/agent/memory/context-optimizer.ts"],"mappings":";;;;;;UAUiB;WACN;;WAEA;;WAEA;WACA;;WAEA;WACA;;UAEM;;WAEN,OAAO;WACP;WACA;WACA;WACA;WACA;;WAEA,WAAW,UAAU,iBAAiB,WAAW,kBAAkB,QAAQ,gBAAgB;WAC3F,UAAU;;WAEV,OAAO,kBAAkB;aACvB;aAA8C;;WAEhD;;UAEM;WACN;WACA;WACA;WACA;WACA;;WAEA;;;;;;;iBAkBK,uBAAuB,SAAS,0BAAuB;;;EAoMtC,kBAAA,WAAA;;EAaN,UAAA,cAAA,YAAiB;EAY7B,WAAA;EACA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{createMessage as e}from"../../message/message.js";import{estimateMessageTokens as t}from"./token-estimator.js";import{projectHistorySurface as n}from"../history/project.js";import{readSpillTool as r}from"../tool/output-budget.js";import{diagnosticLineNumbers as i,reduceEvidence as a}from"../tool/evidence-reducer.js";function o(o){let f=s(o.observationThresholdBytes??10240,`observationThresholdBytes`),p=s(o.summaryBytes??1024,`summaryBytes`);if(p<256)throw RangeError(`summaryBytes must be at least 256`);let m=s(o.fullRequests??2,`fullRequests`),h=s(o.maxObservations??64,`maxObservations`),g=s(o.maxMilestones??128,`maxMilestones`),_=s(o.reductionThresholdBytes??4096,`reductionThresholdBytes`),v=o.store,y=o.archive,b=o.reducer,x=o.log,S=new Map,C=[],w=[],T=new Set,E={packedObservations:0,verifiedReductions:0,rejectedReductions:0,compactedMilestones:0,skippedMilestones:0,estimatedTokensSaved:0},D=!1,O=!1,k,A=new AbortController,j=0,M=r(v),N=async(r,i)=>{let a=new Map((w.length||C.length?i:[]).map(e=>[String(e.id),JSON.stringify({content:e.content,source:e.source,role:e.role})]));for(let i of C.splice(0)){if(i.throughSeq>(r.snapshot.entries.at(-1)?.seq??0)){E.skippedMilestones++;continue}let o=n(r.snapshot.entries),s=new Set(r.messages.map(e=>e.id)),c=o.slice(1).filter(e=>e.seq>j&&e.seq<=i.throughSeq&&e.message.source.kind!==`app`&&s.has(e.message.id)),l=new Map(r.messages.map(e=>[e.id,e])),u=c.map(e=>l.get(e.message.id)),f=new Set(w.flatMap(e=>[...e.targets]));if(c.length===0||c.some(e=>f.has(e.message.id))||!d(u)){E.skippedMilestones++;continue}let p=e({role:`user`,source:{kind:`user`},content:[{type:`text`,text:`[Completed ${i.id}: ${i.summary}]`}]}),m=u.reduce((e,n)=>e+t(n),0)-t(p);if(m<=0||i.compactionCost>=m*i.remainingTurns*(i.historyTokenCost??1)){E.skippedMilestones++;continue}if(y===void 0){E.skippedMilestones++;continue}try{if(await y(r.snapshot,i,r.signal),D)break;r.signal.throwIfAborted()}catch{if(D)break;r.signal.throwIfAborted(),E.skippedMilestones++;continue}w.push({targets:new Set(u.map(e=>e.id)),inputs:new Map(u.map(e=>[String(e.id),a.get(e.id)])),message:p}),j=i.throughSeq,E.compactedMilestones++}let o=[...r.messages];for(let e=w.length-1;e>=0;e--){let t=w[e],n=new Set(o.map(e=>String(e.id)));if(![...t.targets].every(e=>n.has(e)&&a.get(e)===t.inputs.get(e))){w.splice(e,1);continue}let r=o.findIndex(e=>t.targets.has(e.id)),i=o.filter(e=>!t.targets.has(e.id));i.splice(r,0,t.message),o=i}return o},P=async e=>{let n=e.messages,r=new Map(e.snapshot.entries.flatMap(e=>e.event.kind===`tool-call`?[[String(e.event.callId),e.event.name]]:[]));if(n=await l(n,async t=>{let n=await l(t.content,async n=>{if(n.type!==`tool-result`)return n;let o=r.get(String(n.toolCallId))??``;if(o===M.name)return n;let s=await l(n.content,async(r,s)=>{if(D||(e.signal.throwIfAborted(),r.type!==`text`))return r;let l=`${t.id}:${n.toolCallId}:${s}`,d=S.get(l);if(d!==void 0&&d.text!==r.text)return r;let g=d?.record.bytes??c(r.text),y=d===void 0?g>_?x?.(o,r.text):void 0:d.log,C=y===void 0?void 0:Object.freeze({status:y.status,...y.requiredLines===void 0?{}:{requiredLines:Object.freeze([...y.requiredLines])}}),w=g>f,T=b!==void 0&&C!==void 0&&g>_;if(!w&&!T)return r;if(d===void 0){if(S.size>=h)return r;try{let t=await v.save(r.text,{toolName:o,callId:String(n.toolCallId)});if(D)return r;e.signal.throwIfAborted();let i=Object.freeze({locator:t.locator,retrieval:t.retrieval,bytes:t.bytes});if(typeof i.locator!=`string`||!i.locator||typeof i.retrieval!=`string`||!i.retrieval||i.bytes!==g)return r;d={text:r.text,record:i,exposures:0,reductionAttempted:!1,...C===void 0?{}:{log:{...C,...C.requiredLines===void 0?{}:{requiredLines:Object.freeze([...C.requiredLines])}}}},S.set(l,d)}catch{return r}}if(w&&d.exposures<m)return r;try{if(await v.read(d.record.locator,{offset:0,limit:1})===void 0)return r}catch{return r}if(D)return r;if(e.signal.throwIfAborted(),d.packed!==void 0)return E.packedObservations++,{...r,text:d.packed};if(T&&!d.reductionAttempted){d.reductionAttempted=!0;let t=await a({text:r.text,status:C.status,signal:e.signal},b,C.requiredLines);if(D)return r;if(e.signal.throwIfAborted(),t.accepted)d.reduced=t.text,E.verifiedReductions++;else return E.rejectedReductions++,r}if(T&&d.reduced===void 0)return r;let O=`[Observation stored. ID: ${d.record.locator}; bytes: ${g}]\n${d.record.retrieval}\n`,k=d.reduced;if(k===void 0){let e=r.text.split(`
|
|
2
|
+
`),t=i(r.text).map(t=>`${t}: ${e[t-1]}`).join(`
|
|
3
|
+
`),n=p-c(O)-c(t)-40;if(n<=0)return r;k=`${u(r.text,n)}\n[Preview; omitted text is retrievable]\n${t}`}let A=O+k;return c(A)>=g?r:(d.packed=A,E.packedObservations++,{...r,text:A})});return{...n,content:s}});return{...t,content:n}}),D||(e.signal.throwIfAborted(),n=await N({...e,messages:n},e.messages),D))return{kind:`proceed`,messages:e.messages};e.signal.throwIfAborted();let o=e.messages.reduce((e,n)=>e+t(n),0),s=n.reduce((e,n)=>e+t(n),0);return E.estimatedTokensSaved+=Math.max(0,o-s),{kind:`proceed`,messages:Object.freeze(n)}},F=async e=>{if(D)return{kind:`proceed`,messages:e.messages};if(O)return{kind:`reject`,reason:`Context optimizer already has a prepare in flight; use one controller per session.`};let t=e.snapshot.entries.find(e=>`message`in e.event),n=t!==void 0&&`message`in t.event?String(t.event.message.id):void 0;if(k!==void 0&&n!==k)return{kind:`reject`,reason:`Context optimizer belongs to a different history; create a fresh controller after reset/resume.`};k??=n,O=!0;try{return await P({...e,signal:AbortSignal.any([e.signal,A.signal])})}finally{O=!1}},I=e=>{if(!(D||e.kind!==`before-model-request`))for(let t of e.request.messages??[])for(let e of t.content)e.type===`tool-result`&&e.content.forEach((n,r)=>{let i=S.get(`${t.id}:${e.toolCallId}:${r}`);i!==void 0&&i.exposures++})},L=Object.freeze({beforeStep:F,checkpoint:I});return Object.freeze({hooks:L,retrievalTool:M,completeMilestone(e){if(D)throw Error(`context optimizer is disposed`);if(T.size>=g)throw RangeError(`context optimizer milestone capacity reached`);if(!e.id.trim()||!e.summary.trim()||T.has(e.id))throw TypeError(`milestone requires a unique id and non-empty summary`);if(c(e.id)>128||c(e.summary)>8192)throw RangeError(`milestone id/summary exceeds 128/8192 bytes`);if(s(e.throughSeq,`throughSeq`),s(e.remainingTurns,`remainingTurns`),!Number.isFinite(e.compactionCost)||e.compactionCost<0||!Number.isFinite(e.historyTokenCost??1)||(e.historyTokenCost??1)<=0)throw RangeError(`milestone costs must be finite and non-negative; token cost must be positive`);T.add(e.id),C.push(Object.freeze({...e}))},wrapHooks(e={}){return{...e,async beforeStep(t){let n=await e.beforeStep?.(t);return n?.kind===`reject`?n:{...await F({...t,messages:n?.messages??t.messages}),...n?.prepend===void 0?{}:{prepend:n.prepend}}},async checkpoint(t){await e.checkpoint?.(t),await I(t)}}},metrics(){return Object.freeze({...E})},dispose(){D=!0,A.abort(Error(`Context optimizer disposed`)),S.clear(),C.length=0,w.length=0,T.clear()}})}function s(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`${t} must be a positive safe integer`);return e}function c(e){return new TextEncoder().encode(e).byteLength}async function l(e,t){let n=[];for(let r=0;r<e.length;r++)n.push(await t(e[r],r));return n}function u(e,t){let n=``,r=0;for(let i of e){if(r+=c(i),r>t)break;n+=i}return n}function d(e){let t=new Set,n=new Set;for(let r of e)for(let e of r.content)e.type===`tool-call`&&t.add(String(e.id)),e.type===`tool-result`&&n.add(String(e.toolCallId));return t.size===n.size&&[...t].every(e=>n.has(e))}export{o as createContextOptimizer};
|
|
4
|
+
//# sourceMappingURL=context-optimizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-optimizer.js","names":[],"sources":["../../../src/agent/memory/context-optimizer.ts"],"sourcesContent":["import { createMessage, type Message } from '../../message/index.ts'\nimport { projectHistorySurface } from '../history/project.ts'\nimport type { HistorySnapshot } from '../history/types.ts'\nimport type { BeforeStepContext, TurnHooks } from '../loop/events.ts'\nimport type { SpillRecord, SpillStore } from '../tool/output-budget.ts'\nimport type { ToolDefinition } from '../tool/definition.ts'\nimport { readSpillTool } from '../tool/output-budget.ts'\nimport { diagnosticLineNumbers, reduceEvidence, type EvidenceReducer } from '../tool/evidence-reducer.ts'\nimport { estimateMessageTokens } from './token-estimator.ts'\n\nexport interface ContextMilestone {\n readonly id: string\n /** Inclusive append-only sequence boundary, captured after the completed step. */\n readonly throughSeq: number\n /** Host-verified state, including decisions and unresolved constraints needed later. */\n readonly summary: string\n readonly remainingTurns: number\n /** Same cost units as historyTokenCost; include input/output and summary-model price. */\n readonly compactionCost: number\n readonly historyTokenCost?: number\n}\nexport interface ContextOptimizerOptions {\n /** Mount the returned retrievalTool in the same session. Use a store scoped to that session. */\n readonly store: SpillStore\n readonly observationThresholdBytes?: number\n readonly summaryBytes?: number\n readonly fullRequests?: number\n readonly maxObservations?: number\n readonly maxMilestones?: number\n /** Persist raw snapshots locally/durably before a milestone projection is accepted. */\n readonly archive?: (snapshot: HistorySnapshot, milestone: ContextMilestone, signal: AbortSignal) => Promise<void>\n readonly reducer?: EvidenceReducer\n /** Explicitly identify logs and their authoritative outcome; core does not guess exit status. */\n readonly log?: (toolName: string, text: string) => {\n readonly status: 'pass' | 'fail' | 'unknown'; readonly requiredLines?: readonly number[]\n } | undefined\n readonly reductionThresholdBytes?: number\n}\nexport interface ContextOptimizationMetrics {\n readonly packedObservations: number\n readonly verifiedReductions: number\n readonly rejectedReductions: number\n readonly compactedMilestones: number\n readonly skippedMilestones: number\n /** Estimate of payload tokens avoided across prepared requests; never billing usage. */\n readonly estimatedTokensSaved: number\n}\ninterface Observation {\n readonly text: string\n readonly record: SpillRecord\n exposures: number\n reduced?: string\n reductionAttempted: boolean\n readonly log?: { readonly status: 'pass' | 'fail' | 'unknown'; readonly requiredLines?: readonly number[] }\n packed?: string\n}\ninterface Completed { readonly targets: ReadonlySet<string>; readonly inputs: ReadonlyMap<string, string>; readonly message: Message }\n\n/**\n * One controller per session. Projection affects requests only; snapshots retain raw history.\n * Compose its hooks with application hooks using wrapHooks(), and register retrievalTool.\n * Disposal drops projection state, never tool receipts or host-owned storage.\n */\nexport function createContextOptimizer(options: ContextOptimizerOptions) {\n const threshold = positive(options.observationThresholdBytes ?? 10 * 1024, 'observationThresholdBytes')\n const summaryBytes = positive(options.summaryBytes ?? 1024, 'summaryBytes')\n if (summaryBytes < 256) throw new RangeError('summaryBytes must be at least 256')\n const fullRequests = positive(options.fullRequests ?? 2, 'fullRequests')\n const maxObservations = positive(options.maxObservations ?? 64, 'maxObservations')\n const maxMilestones = positive(options.maxMilestones ?? 128, 'maxMilestones')\n const reductionThreshold = positive(options.reductionThresholdBytes ?? 4 * 1024, 'reductionThresholdBytes')\n const store = options.store\n const archive = options.archive\n const reducer = options.reducer\n const log = options.log\n const observations = new Map<string, Observation>()\n const pending: ContextMilestone[] = []\n const completed: Completed[] = []\n const milestoneIds = new Set<string>()\n const metrics = { packedObservations: 0, verifiedReductions: 0, rejectedReductions: 0,\n compactedMilestones: 0, skippedMilestones: 0, estimatedTokensSaved: 0 }\n let disposed = false\n let active = false\n let scope: string | undefined\n const lifetime = new AbortController()\n let completedThroughSeq = 0\n const retrievalTool: ToolDefinition = readSpillTool(store)\n\n const projectMilestones = async (context: BeforeStepContext, sourceMessages: readonly Message[]): Promise<readonly Message[]> => {\n const fingerprints = new Map((completed.length || pending.length ? sourceMessages : []).map(message => [String(message.id), JSON.stringify({ content: message.content, source: message.source, role: message.role })]))\n for (const milestone of pending.splice(0)) {\n if (milestone.throughSeq > (context.snapshot.entries.at(-1)?.seq ?? 0)) {\n metrics.skippedMilestones++; continue\n }\n const surface = projectHistorySurface(context.snapshot.entries)\n const visible = new Set(context.messages.map(message => message.id))\n // Preserve the initial request and injected state. Only completed, balanced ranges.\n const nodes = surface.slice(1).filter(node => node.seq > completedThroughSeq\n && node.seq <= milestone.throughSeq && node.message.source.kind !== 'app' && visible.has(node.message.id))\n const byId = new Map(context.messages.map(message => [message.id, message]))\n const messages = nodes.map(node => byId.get(node.message.id)!)\n const covered = new Set(completed.flatMap(group => [...group.targets]))\n if (nodes.length === 0 || nodes.some(node => covered.has(node.message.id)) || !balanced(messages)) {\n metrics.skippedMilestones++; continue\n }\n const summary = createMessage({ role: 'user', source: { kind: 'user' },\n content: [{ type: 'text', text: `[Completed ${milestone.id}: ${milestone.summary}]` }] })\n const saved = messages.reduce((sum, message) => sum + estimateMessageTokens(message), 0) - estimateMessageTokens(summary)\n if (saved <= 0 || milestone.compactionCost >= saved * milestone.remainingTurns * (milestone.historyTokenCost ?? 1)) {\n metrics.skippedMilestones++; continue\n }\n if (archive === undefined) { metrics.skippedMilestones++; continue }\n try {\n await archive(context.snapshot, milestone, context.signal)\n if (disposed) break\n context.signal.throwIfAborted()\n } catch {\n if (disposed) break\n context.signal.throwIfAborted()\n metrics.skippedMilestones++; continue\n }\n completed.push({ targets: new Set(messages.map(message => message.id)),\n inputs: new Map(messages.map(message => [String(message.id), fingerprints.get(message.id)!])), message: summary })\n completedThroughSeq = milestone.throughSeq\n metrics.compactedMilestones++\n }\n let messages = [...context.messages]\n // If regular pressure compaction has shadowed a target, its summary now owns that range.\n for (let i = completed.length - 1; i >= 0; i--) {\n const group = completed[i]!\n const visible = new Set(messages.map(message => String(message.id)))\n if (![...group.targets].every(id => visible.has(id) && fingerprints.get(id) === group.inputs.get(id))) {\n completed.splice(i, 1); continue\n }\n const first = messages.findIndex(message => group.targets.has(message.id))\n const retained = messages.filter(message => !group.targets.has(message.id))\n retained.splice(first, 0, group.message)\n messages = retained\n }\n return messages\n }\n\n const prepare: NonNullable<TurnHooks['beforeStep']> = async context => {\n let messages = context.messages\n const toolNames = new Map(context.snapshot.entries.flatMap(entry => entry.event.kind === 'tool-call'\n ? [[String(entry.event.callId), entry.event.name] as const] : []))\n messages = await mapSequential(messages, async message => {\n const content = await mapSequential(message.content, async block => {\n if (block.type !== 'tool-result') return block\n const toolName = toolNames.get(String(block.toolCallId)) ?? ''\n if (toolName === retrievalTool.name) return block\n const children = await mapSequential(block.content, async (child, index) => {\n if (disposed) return child\n context.signal.throwIfAborted()\n if (child.type !== 'text') return child\n const key = `${message.id}:${block.toolCallId}:${index}`\n let observation = observations.get(key)\n if (observation !== undefined && observation.text !== child.text) return child\n const bytes = observation?.record.bytes ?? byteLength(child.text)\n const identifiedLog = observation === undefined\n ? bytes > reductionThreshold ? log?.(toolName, child.text) : undefined\n : observation.log\n // Save may await a host backend. Capture its authority before that await.\n const logInfo = identifiedLog === undefined ? undefined : Object.freeze({ status: identifiedLog.status,\n ...identifiedLog.requiredLines === undefined ? {} : { requiredLines: Object.freeze([...identifiedLog.requiredLines]) } })\n const large = bytes > threshold\n const reducible = reducer !== undefined && logInfo !== undefined && bytes > reductionThreshold\n if (!large && !reducible) return child\n if (observation === undefined) {\n if (observations.size >= maxObservations) return child\n try {\n const record = await store.save(child.text, { toolName, callId: String(block.toolCallId) })\n if (disposed) return child\n context.signal.throwIfAborted()\n const captured = Object.freeze({ locator: record.locator, retrieval: record.retrieval, bytes: record.bytes })\n if (typeof captured.locator !== 'string' || !captured.locator || typeof captured.retrieval !== 'string'\n || !captured.retrieval || captured.bytes !== bytes) return child\n observation = { text: child.text, record: captured, exposures: 0, reductionAttempted: false,\n ...logInfo === undefined ? {} : { log: { ...logInfo,\n ...logInfo.requiredLines === undefined ? {} : { requiredLines: Object.freeze([...logInfo.requiredLines]) } } } }\n observations.set(key, observation)\n } catch { return child }\n }\n if (large && observation.exposures < fullRequests) return child\n // Retention is owned by the store. Do not emit a pointer known to be expired.\n try {\n if (await store.read(observation.record.locator, { offset: 0, limit: 1 }) === undefined) return child\n } catch { return child }\n if (disposed) return child\n context.signal.throwIfAborted()\n if (observation.packed !== undefined) {\n metrics.packedObservations++\n return { ...child, text: observation.packed }\n }\n if (reducible && !observation.reductionAttempted) {\n observation.reductionAttempted = true\n const result = await reduceEvidence({ text: child.text, status: logInfo.status, signal: context.signal }, reducer, logInfo.requiredLines)\n if (disposed) return child\n context.signal.throwIfAborted()\n if (result.accepted) { observation.reduced = result.text; metrics.verifiedReductions++ }\n else { metrics.rejectedReductions++; return child }\n }\n // Invalid reductions always preserve the original, including on later requests.\n if (reducible && observation.reduced === undefined) return child\n const header = `[Observation stored. ID: ${observation.record.locator}; bytes: ${bytes}]\\n${observation.record.retrieval}\\n`\n let body = observation.reduced\n if (body === undefined) {\n const lines = child.text.split('\\n')\n const evidence = diagnosticLineNumbers(child.text).map(line => `${line}: ${lines[line - 1]}`).join('\\n')\n const room = summaryBytes - byteLength(header) - byteLength(evidence) - 40\n if (room <= 0) return child\n body = `${utf8Prefix(child.text, room)}\\n[Preview; omitted text is retrievable]\\n${evidence}`\n }\n const text = header + body\n if (byteLength(text) >= bytes) return child\n observation.packed = text\n metrics.packedObservations++\n return { ...child, text }\n })\n return { ...block, content: children }\n })\n return { ...message, content }\n })\n if (disposed) return { kind: 'proceed', messages: context.messages }\n context.signal.throwIfAborted()\n messages = await projectMilestones({ ...context, messages }, context.messages)\n if (disposed) return { kind: 'proceed', messages: context.messages }\n context.signal.throwIfAborted()\n const before = context.messages.reduce((sum, message) => sum + estimateMessageTokens(message), 0)\n const after = messages.reduce((sum, message) => sum + estimateMessageTokens(message), 0)\n metrics.estimatedTokensSaved += Math.max(0, before - after)\n return { kind: 'proceed', messages: Object.freeze(messages) }\n }\n const beforeStep: NonNullable<TurnHooks['beforeStep']> = async context => {\n if (disposed) return { kind: 'proceed', messages: context.messages }\n if (active) return { kind: 'reject', reason: 'Context optimizer already has a prepare in flight; use one controller per session.' }\n const first = context.snapshot.entries.find(entry => 'message' in entry.event)\n const identity = first !== undefined && 'message' in first.event ? String(first.event.message.id) : undefined\n if (scope !== undefined && identity !== scope) return { kind: 'reject', reason: 'Context optimizer belongs to a different history; create a fresh controller after reset/resume.' }\n scope ??= identity\n active = true\n try { return await prepare({ ...context, signal: AbortSignal.any([context.signal, lifetime.signal]) }) }\n finally { active = false }\n }\n const checkpoint: NonNullable<TurnHooks['checkpoint']> = context => {\n if (disposed || context.kind !== 'before-model-request') return\n for (const message of context.request.messages ?? []) {\n for (const block of message.content) {\n if (block.type !== 'tool-result') continue\n block.content.forEach((_child, index) => {\n const observation = observations.get(`${message.id}:${block.toolCallId}:${index}`)\n if (observation !== undefined) observation.exposures++\n })\n }\n }\n }\n const hooks: TurnHooks = Object.freeze({ beforeStep, checkpoint })\n return Object.freeze({\n hooks, retrievalTool,\n completeMilestone(milestone: ContextMilestone): void {\n if (disposed) throw new Error('context optimizer is disposed')\n if (milestoneIds.size >= maxMilestones) throw new RangeError('context optimizer milestone capacity reached')\n if (!milestone.id.trim() || !milestone.summary.trim() || milestoneIds.has(milestone.id)) throw new TypeError('milestone requires a unique id and non-empty summary')\n if (byteLength(milestone.id) > 128 || byteLength(milestone.summary) > 8192) throw new RangeError('milestone id/summary exceeds 128/8192 bytes')\n positive(milestone.throughSeq, 'throughSeq')\n positive(milestone.remainingTurns, 'remainingTurns')\n if (!Number.isFinite(milestone.compactionCost) || milestone.compactionCost < 0\n || !Number.isFinite(milestone.historyTokenCost ?? 1) || (milestone.historyTokenCost ?? 1) <= 0) throw new RangeError('milestone costs must be finite and non-negative; token cost must be positive')\n milestoneIds.add(milestone.id)\n pending.push(Object.freeze({ ...milestone }))\n },\n /** Application hook runs first; checkpoints count only after host persistence succeeds. */\n wrapHooks(application: TurnHooks = {}): TurnHooks {\n return {\n ...application,\n async beforeStep(context) {\n const decision = await application.beforeStep?.(context)\n if (decision?.kind === 'reject') return decision\n const optimized = await beforeStep({ ...context, messages: decision?.messages ?? context.messages })\n return { ...optimized, ...decision?.prepend === undefined ? {} : { prepend: decision.prepend } }\n },\n async checkpoint(context) { await application.checkpoint?.(context); await checkpoint(context) },\n }\n },\n metrics(): ContextOptimizationMetrics { return Object.freeze({ ...metrics }) },\n dispose(): void { disposed = true; lifetime.abort(new Error('Context optimizer disposed')); observations.clear(); pending.length = 0; completed.length = 0; milestoneIds.clear() },\n })\n}\n\nfunction positive(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${name} must be a positive safe integer`)\n return value\n}\nfunction byteLength(text: string): number { return new TextEncoder().encode(text).byteLength }\nasync function mapSequential<T, U>(items: readonly T[], map: (item: T, index: number) => Promise<U>): Promise<U[]> {\n const result: U[] = []\n for (let index = 0; index < items.length; index++) result.push(await map(items[index]!, index))\n return result\n}\nfunction utf8Prefix(text: string, bytes: number): string {\n let result = '', used = 0\n for (const point of text) { used += byteLength(point); if (used > bytes) break; result += point }\n return result\n}\nfunction balanced(messages: readonly Message[]): boolean {\n const calls = new Set<string>(), results = new Set<string>()\n for (const message of messages) for (const block of message.content) {\n if (block.type === 'tool-call') calls.add(String(block.id))\n if (block.type === 'tool-result') results.add(String(block.toolCallId))\n }\n return calls.size === results.size && [...calls].every(id => results.has(id))\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const e=Object.freeze({objective:0,constraint:1,decision:2,"next-step":3,progress:4,fact:5});var t=class t{records=new Map;limits;nextId=1;storedChars=0;constructor(e=[],t={}){this.limits=i(t);for(let t of e)this.remember(t)}static fromSnapshot(e,n={}){if(typeof e!=`object`||!e||e.version!==1||!Array.isArray(e.items))throw TypeError(`unsupported agent memory snapshot`);let r=new t([],n);if(e.items.length>r.limits.maxItems)throw RangeError(`agent memory snapshot exceeds the ${r.limits.maxItems}-item limit`);for(let t of e.items){if(o(t),r.records.has(t.id))throw TypeError(`duplicate agent memory id '${t.id}'`);if(t.content.length>r.limits.maxItemChars)throw RangeError(`agent memory item '${t.id}' exceeds the ${r.limits.maxItemChars}-character limit`);if(r.storedChars+t.content.length>r.limits.maxStoredChars)throw RangeError(`agent memory snapshot exceeds the ${r.limits.maxStoredChars}-character limit`);r.records.set(t.id,Object.freeze({id:t.id,kind:t.kind,content:t.content,createdAt:t.createdAt,updatedAt:t.updatedAt})),r.storedChars+=t.content.length;let e=c(t.id);e!==void 0&&(r.nextId=Math.max(r.nextId,e))}return r}remember(e){r(e);let t=e.id??`memory-${this.nextId}`,n=e.content.trim();if(n.length>this.limits.maxItemChars)throw RangeError(`agent memory item '${t}' exceeds the ${this.limits.maxItemChars}-character limit`);let i=c(t),a=this.records.get(t);if(a===void 0&&this.records.size>=this.limits.maxItems)throw RangeError(`agent memory reached its ${this.limits.maxItems}-item limit`);let o=this.storedChars-(a?.content.length??0)+n.length;if(o>this.limits.maxStoredChars)throw RangeError(`agent memory reached its ${this.limits.maxStoredChars}-character limit`);let s=new Date().toISOString(),l=Object.freeze({id:t,kind:e.kind,content:n,createdAt:a?.createdAt??s,updatedAt:s});return this.records.set(t,l),this.storedChars=o,i!==void 0&&(this.nextId=Math.max(this.nextId,i)),l}forget(e){let t=this.records.get(e);return t!==void 0&&(this.records.delete(e),this.storedChars-=t.content.length,!0)}items(){return Object.freeze([...this.records.values()])}snapshot(){return Object.freeze({version:1,items:Object.freeze(this.items().map(e=>Object.freeze(structuredClone(e))))})}captureOriginalObjective(e){if([...this.records.values()].some(e=>e.kind===`objective`))return;let t=l(e,this.limits.maxItemChars).trim();return this.remember({id:`original-objective`,kind:`objective`,content:t.length>0?t:`[The original request contained non-text input.]`})}render(t=12e3){if(!Number.isInteger(t)||t<256)throw RangeError(`agent memory render maxChars must be an integer >= 256`);let n=[...this.records.values()].sort((t,n)=>e[t.kind]-e[n.kind]||t.createdAt.localeCompare(n.createdAt));if(n.length===0)return``;let r=[`<task-memory>`,`
|
|
1
|
+
const e=Object.freeze({objective:0,constraint:1,decision:2,"next-step":3,progress:4,fact:5});var t=class t{records=new Map;limits;nextId=1;storedChars=0;constructor(e=[],t={}){this.limits=i(t);for(let t of e)this.remember(t)}static fromSnapshot(e,n={}){if(typeof e!=`object`||!e||e.version!==1||!Array.isArray(e.items))throw TypeError(`unsupported agent memory snapshot`);let r=new t([],n);if(e.items.length>r.limits.maxItems)throw RangeError(`agent memory snapshot exceeds the ${r.limits.maxItems}-item limit`);for(let t of e.items){if(o(t),r.records.has(t.id))throw TypeError(`duplicate agent memory id '${t.id}'`);if(t.content.length>r.limits.maxItemChars)throw RangeError(`agent memory item '${t.id}' exceeds the ${r.limits.maxItemChars}-character limit`);if(r.storedChars+t.content.length>r.limits.maxStoredChars)throw RangeError(`agent memory snapshot exceeds the ${r.limits.maxStoredChars}-character limit`);r.records.set(t.id,Object.freeze({id:t.id,kind:t.kind,content:t.content,createdAt:t.createdAt,updatedAt:t.updatedAt})),r.storedChars+=t.content.length;let e=c(t.id);e!==void 0&&(r.nextId=Math.max(r.nextId,e))}return r}remember(e){r(e);let t=e.id??`memory-${this.nextId}`,n=e.content.trim();if(n.length>this.limits.maxItemChars)throw RangeError(`agent memory item '${t}' exceeds the ${this.limits.maxItemChars}-character limit`);let i=c(t),a=this.records.get(t);if(a===void 0&&this.records.size>=this.limits.maxItems)throw RangeError(`agent memory reached its ${this.limits.maxItems}-item limit`);let o=this.storedChars-(a?.content.length??0)+n.length;if(o>this.limits.maxStoredChars)throw RangeError(`agent memory reached its ${this.limits.maxStoredChars}-character limit`);let s=new Date().toISOString(),l=Object.freeze({id:t,kind:e.kind,content:n,createdAt:a?.createdAt??s,updatedAt:s});return this.records.set(t,l),this.storedChars=o,i!==void 0&&(this.nextId=Math.max(this.nextId,i)),l}forget(e){let t=this.records.get(e);return t!==void 0&&(this.records.delete(e),this.storedChars-=t.content.length,!0)}items(){return Object.freeze([...this.records.values()])}snapshot(){return Object.freeze({version:1,items:Object.freeze(this.items().map(e=>Object.freeze(structuredClone(e))))})}captureOriginalObjective(e){if([...this.records.values()].some(e=>e.kind===`objective`))return;let t=l(e,this.limits.maxItemChars).trim();return this.remember({id:`original-objective`,kind:`objective`,content:t.length>0?t:`[The original request contained non-text input.]`})}render(t=12e3){if(!Number.isInteger(t)||t<256)throw RangeError(`agent memory render maxChars must be an integer >= 256`);let n=[...this.records.values()].sort((t,n)=>e[t.kind]-e[n.kind]||t.createdAt.localeCompare(n.createdAt));if(n.length===0)return``;let r=[`<task-memory>`,`Retained context is background from earlier work. Newer user messages supersede conflicting objectives or constraints below. Follow the latest request and its output format.`],i=t-r.join(`
|
|
2
2
|
`).length-14-2,a=[];for(let e of n){if(i<=0)break;let t=`- ${e.kind} [${e.id}]: `,n=Math.max(0,i-t.length-1),r=t+u(JSON.stringify(e.content),n);a.push(r),i-=r.length+1}return[...r,...a,`</task-memory>`].join(`
|
|
3
3
|
`)}};function n(e){let t=e?.maxInjectedChars??12e3;if(!Number.isInteger(t)||t<256)throw RangeError(`agent memory maxInjectedChars must be an integer >= 256`);let n=i(e??{}),a=new Set,o=1,s=0,l=Object.freeze([...e?.seed??[]].map(e=>{r(e);let t=e.id??`memory-${o++}`,i=c(t);if(i!==void 0&&(o=Math.max(o,i)),a.has(t))throw TypeError(`duplicate agent memory seed id '${t}'`);a.add(t);let l=e.content.trim().length;if(l>n.maxItemChars)throw RangeError(`agent memory seed '${t}' exceeds the ${n.maxItemChars}-character limit`);if(s+=l,s>n.maxStoredChars)throw RangeError(`agent memory seed exceeds the ${n.maxStoredChars}-character limit`);return Object.freeze({...e.id===void 0?{}:{id:e.id},kind:e.kind,content:e.content})}));if(l.length>n.maxItems)throw RangeError(`agent memory seed exceeds the ${n.maxItems}-item limit`);return Object.freeze({autoCaptureObjective:e?.autoCaptureObjective??!0,maxInjectedChars:t,...n,seed:l})}function r(t){if(t.id!==void 0&&(typeof t.id!=`string`||t.id.trim().length===0||t.id.length>256||/\s/.test(t.id)))throw TypeError(`agent memory id must be non-empty, at most 256 characters, and contain no whitespace`);if(!Object.hasOwn(e,t.kind))throw TypeError(`unsupported agent memory kind '${String(t.kind)}'`);if(typeof t.content!=`string`||t.content.trim().length===0)throw TypeError(`agent memory content must be a non-empty string`)}function i(e){let t=a(e.maxItems??1024,`maxItems`),n=a(e.maxItemChars??65536,`maxItemChars`),r=a(e.maxStoredChars??1048576,`maxStoredChars`);if(n>r)throw RangeError(`agent memory maxItemChars must not exceed maxStoredChars`);return Object.freeze({maxItems:t,maxItemChars:n,maxStoredChars:r})}function a(e,t){if(!Number.isSafeInteger(e)||e<1)throw RangeError(`agent memory ${t} must be a positive safe integer`);return e}function o(e){if(r(e),!s(e.createdAt)||!s(e.updatedAt))throw TypeError(`agent memory timestamps must be bounded ISO date strings`)}function s(e){if(typeof e!=`string`||e.length===0||e.length>64)return!1;let t=Date.parse(e);if(!Number.isFinite(t))return!1;try{return new Date(t).toISOString()===e}catch{return!1}}function c(e){let t=/^memory-(\d+)$/.exec(e)?.[1];if(t===void 0)return;let n=Number(t);return Number.isSafeInteger(n)&&n>=1&&n<2**53-1?n+1:void 0}function l(e,t){let n=``,r=!1;for(let i of e.content){if(i.type!==`text`)continue;let e=n.length===0?``:`
|
|
4
4
|
`,a=t-n.length;if(e.length+i.text.length<=a){n+=e+i.text;continue}let o=Math.max(0,a-e.length-22);n+=e+i.text.slice(0,o)+`…[objective truncated]`.slice(0,Math.max(0,a-e.length-o)),r=!0;break}return r?n.slice(0,t):n}function u(e,t){if(e.length<=t)return e;if(t<=1)return e.slice(0,t);let n=Math.ceil((t-1)/2),r=Math.floor((t-1)/2);return e.slice(0,n)+`…`+e.slice(e.length-r)}export{t as AgentMemory,n as resolveMemoryConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","names":[],"sources":["../../../src/agent/memory/memory.ts"],"sourcesContent":["/** Durable, explicitly inspectable task memory kept outside compactable history. */\n\nimport type { UserMessage } from '../../message/index.ts'\n\nexport type AgentMemoryKind =\n | 'objective'\n | 'constraint'\n | 'decision'\n | 'fact'\n | 'progress'\n | 'next-step'\n\nexport interface AgentMemorySeed {\n readonly id?: string\n readonly kind: AgentMemoryKind\n readonly content: string\n}\n\nexport interface AgentMemoryItem {\n readonly id: string\n readonly kind: AgentMemoryKind\n readonly content: string\n readonly createdAt: string\n readonly updatedAt: string\n}\n\nexport interface AgentMemorySnapshot {\n readonly version: 1\n readonly items: readonly AgentMemoryItem[]\n}\n\nexport interface AgentMemoryConfigInput {\n /** Pin the first real user message as the original objective. Defaults to true. */\n readonly autoCaptureObjective?: boolean\n /** Maximum rendered memory characters injected into every model request. Defaults to 12,000. */\n readonly maxInjectedChars?: number\n /** Maximum retained memory records. Defaults to 1,024. */\n readonly maxItems?: number\n /** Maximum characters retained by one record. Defaults to 65,536. */\n readonly maxItemChars?: number\n /** Maximum cumulative retained content characters. Defaults to 1 MiB. */\n readonly maxStoredChars?: number\n readonly seed?: readonly AgentMemorySeed[]\n}\n\nexport interface AgentMemoryConfig {\n readonly autoCaptureObjective: boolean\n readonly maxInjectedChars: number\n readonly maxItems: number\n readonly maxItemChars: number\n readonly maxStoredChars: number\n readonly seed: readonly AgentMemorySeed[]\n}\n\nconst PRIORITY: Readonly<Record<AgentMemoryKind, number>> = Object.freeze({\n objective: 0,\n constraint: 1,\n decision: 2,\n 'next-step': 3,\n progress: 4,\n fact: 5,\n})\n\nexport class AgentMemory {\n private readonly records = new Map<string, AgentMemoryItem>()\n private readonly limits: Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>\n private nextId = 1\n private storedChars = 0\n\n constructor(\n seed: readonly AgentMemorySeed[] = [],\n limits: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>> = {},\n ) {\n this.limits = resolveMemoryLimits(limits)\n for (const item of seed) this.remember(item)\n }\n\n static fromSnapshot(\n snapshot: AgentMemorySnapshot,\n limits: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>> = {},\n ): AgentMemory {\n if (typeof snapshot !== 'object' || snapshot === null\n || snapshot.version !== 1 || !Array.isArray(snapshot.items)) {\n throw new TypeError('unsupported agent memory snapshot')\n }\n const memory = new AgentMemory([], limits)\n if (snapshot.items.length > memory.limits.maxItems) {\n throw new RangeError(`agent memory snapshot exceeds the ${memory.limits.maxItems}-item limit`)\n }\n for (const item of snapshot.items) {\n validateItem(item)\n if (memory.records.has(item.id)) throw new TypeError(`duplicate agent memory id '${item.id}'`)\n if (item.content.length > memory.limits.maxItemChars) {\n throw new RangeError(`agent memory item '${item.id}' exceeds the ${memory.limits.maxItemChars}-character limit`)\n }\n if (memory.storedChars + item.content.length > memory.limits.maxStoredChars) {\n throw new RangeError(`agent memory snapshot exceeds the ${memory.limits.maxStoredChars}-character limit`)\n }\n memory.records.set(item.id, Object.freeze({\n id: item.id,\n kind: item.kind,\n content: item.content,\n createdAt: item.createdAt,\n updatedAt: item.updatedAt,\n }))\n memory.storedChars += item.content.length\n const successor = memoryIdSuccessor(item.id)\n if (successor !== undefined) memory.nextId = Math.max(memory.nextId, successor)\n }\n return memory\n }\n\n /** Add or update one stable memory item. Supplying the same id is an upsert. */\n remember(input: AgentMemorySeed): AgentMemoryItem {\n validateSeed(input)\n const id = input.id ?? `memory-${this.nextId}`\n const content = input.content.trim()\n if (content.length > this.limits.maxItemChars) {\n throw new RangeError(`agent memory item '${id}' exceeds the ${this.limits.maxItemChars}-character limit`)\n }\n const successor = memoryIdSuccessor(id)\n const previous = this.records.get(id)\n if (previous === undefined && this.records.size >= this.limits.maxItems) {\n throw new RangeError(`agent memory reached its ${this.limits.maxItems}-item limit`)\n }\n const nextStoredChars = this.storedChars - (previous?.content.length ?? 0) + content.length\n if (nextStoredChars > this.limits.maxStoredChars) {\n throw new RangeError(`agent memory reached its ${this.limits.maxStoredChars}-character limit`)\n }\n const at = new Date().toISOString()\n const item = Object.freeze({\n id,\n kind: input.kind,\n content,\n createdAt: previous?.createdAt ?? at,\n updatedAt: at,\n })\n this.records.set(id, item)\n this.storedChars = nextStoredChars\n if (successor !== undefined) this.nextId = Math.max(this.nextId, successor)\n return item\n }\n\n forget(id: string): boolean {\n const item = this.records.get(id)\n if (item === undefined) return false\n this.records.delete(id)\n this.storedChars -= item.content.length\n return true\n }\n\n items(): readonly AgentMemoryItem[] {\n return Object.freeze([...this.records.values()])\n }\n\n snapshot(): AgentMemorySnapshot {\n return Object.freeze({\n version: 1,\n items: Object.freeze(this.items().map(item => Object.freeze(structuredClone(item)))),\n })\n }\n\n /** Capture the first user request verbatim enough to survive every later compaction. */\n captureOriginalObjective(message: UserMessage): AgentMemoryItem | undefined {\n if ([...this.records.values()].some(item => item.kind === 'objective')) return undefined\n const text = collectTextPrefix(message, this.limits.maxItemChars).trim()\n return this.remember({\n id: 'original-objective',\n kind: 'objective',\n content: text.length > 0 ? text : '[The original request contained non-text input.]',\n })\n }\n\n /** Render bounded pinned context, ordered by importance rather than insertion accident. */\n render(maxChars = 12_000): string {\n if (!Number.isInteger(maxChars) || maxChars < 256) {\n throw new RangeError('agent memory render maxChars must be an integer >= 256')\n }\n const ordered = [...this.records.values()].sort((a, b) =>\n PRIORITY[a.kind] - PRIORITY[b.kind] || a.createdAt.localeCompare(b.createdAt))\n if (ordered.length === 0) return ''\n const preamble = [\n '<task-memory>',\n 'Durable task facts follow. Preserve the original objective and obey active constraints even when older conversation turns were compacted.',\n ]\n const closing = '</task-memory>'\n let remaining = maxChars - preamble.join('\\n').length - closing.length - 2\n const lines: string[] = []\n for (const item of ordered) {\n if (remaining <= 0) break\n const prefix = `- ${item.kind} [${item.id}]: `\n const contentBudget = Math.max(0, remaining - prefix.length - 1)\n const content = truncateMiddle(JSON.stringify(item.content), contentBudget)\n const line = prefix + content\n lines.push(line)\n remaining -= line.length + 1\n }\n return [...preamble, ...lines, closing].join('\\n')\n }\n}\n\nexport function resolveMemoryConfig(input: AgentMemoryConfigInput | undefined): AgentMemoryConfig {\n const maxInjectedChars = input?.maxInjectedChars ?? 12_000\n if (!Number.isInteger(maxInjectedChars) || maxInjectedChars < 256) {\n throw new RangeError('agent memory maxInjectedChars must be an integer >= 256')\n }\n const limits = resolveMemoryLimits(input ?? {})\n const ids = new Set<string>()\n let nextId = 1\n let storedChars = 0\n const seed = Object.freeze([...(input?.seed ?? [])].map(item => {\n validateSeed(item)\n const id = item.id ?? `memory-${nextId++}`\n const successor = memoryIdSuccessor(id)\n if (successor !== undefined) nextId = Math.max(nextId, successor)\n if (ids.has(id)) throw new TypeError(`duplicate agent memory seed id '${id}'`)\n ids.add(id)\n const chars = item.content.trim().length\n if (chars > limits.maxItemChars) {\n throw new RangeError(`agent memory seed '${id}' exceeds the ${limits.maxItemChars}-character limit`)\n }\n storedChars += chars\n if (storedChars > limits.maxStoredChars) {\n throw new RangeError(`agent memory seed exceeds the ${limits.maxStoredChars}-character limit`)\n }\n return Object.freeze({\n ...(item.id === undefined ? {} : { id: item.id }),\n kind: item.kind,\n content: item.content,\n })\n }))\n if (seed.length > limits.maxItems) {\n throw new RangeError(`agent memory seed exceeds the ${limits.maxItems}-item limit`)\n }\n return Object.freeze({\n autoCaptureObjective: input?.autoCaptureObjective ?? true,\n maxInjectedChars,\n ...limits,\n seed,\n })\n}\n\nfunction validateSeed(input: AgentMemorySeed): void {\n if (input.id !== undefined && (typeof input.id !== 'string'\n || input.id.trim().length === 0 || input.id.length > 256 || /\\s/.test(input.id))) {\n throw new TypeError('agent memory id must be non-empty, at most 256 characters, and contain no whitespace')\n }\n if (!Object.hasOwn(PRIORITY, input.kind)) throw new TypeError(`unsupported agent memory kind '${String(input.kind)}'`)\n if (typeof input.content !== 'string' || input.content.trim().length === 0) {\n throw new TypeError('agent memory content must be a non-empty string')\n }\n}\n\nfunction resolveMemoryLimits(\n input: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>>,\n): Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'> {\n const maxItems = positiveInteger(input.maxItems ?? 1_024, 'maxItems')\n const maxItemChars = positiveInteger(input.maxItemChars ?? 65_536, 'maxItemChars')\n const maxStoredChars = positiveInteger(input.maxStoredChars ?? 1024 * 1024, 'maxStoredChars')\n if (maxItemChars > maxStoredChars) {\n throw new RangeError('agent memory maxItemChars must not exceed maxStoredChars')\n }\n return Object.freeze({ maxItems, maxItemChars, maxStoredChars })\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new RangeError(`agent memory ${name} must be a positive safe integer`)\n }\n return value\n}\n\nfunction validateItem(item: AgentMemoryItem): void {\n validateSeed(item)\n if (!validTimestamp(item.createdAt) || !validTimestamp(item.updatedAt)) {\n throw new TypeError('agent memory timestamps must be bounded ISO date strings')\n }\n}\n\nfunction validTimestamp(value: unknown): value is string {\n if (typeof value !== 'string' || value.length === 0 || value.length > 64) return false\n const parsed = Date.parse(value)\n if (!Number.isFinite(parsed)) return false\n try { return new Date(parsed).toISOString() === value } catch { return false }\n}\n\nfunction memoryIdSuccessor(id: string): number | undefined {\n const suffix = /^memory-(\\d+)$/.exec(id)?.[1]\n if (suffix === undefined) return undefined\n const numeric = Number(suffix)\n return Number.isSafeInteger(numeric) && numeric >= 1 && numeric < Number.MAX_SAFE_INTEGER\n ? numeric + 1\n : undefined\n}\n\nfunction collectTextPrefix(message: UserMessage, maxChars: number): string {\n const marker = '…[objective truncated]'\n let text = ''\n let truncated = false\n for (const block of message.content) {\n if (block.type !== 'text') continue\n const separator = text.length === 0 ? '' : '\\n'\n const remaining = maxChars - text.length\n if (separator.length + block.text.length <= remaining) {\n text += separator + block.text\n continue\n }\n const available = Math.max(0, remaining - separator.length - marker.length)\n text += separator + block.text.slice(0, available) + marker.slice(0, Math.max(0, remaining - separator.length - available))\n truncated = true\n break\n }\n return truncated ? text.slice(0, maxChars) : text\n}\n\nfunction truncateMiddle(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n if (maxChars <= 1) return value.slice(0, maxChars)\n const marker = '…'\n const head = Math.ceil((maxChars - marker.length) / 2)\n const tail = Math.floor((maxChars - marker.length) / 2)\n return value.slice(0, head) + marker + value.slice(value.length - tail)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory.js","names":[],"sources":["../../../src/agent/memory/memory.ts"],"sourcesContent":["/** Durable, explicitly inspectable task memory kept outside compactable history. */\n\nimport type { UserMessage } from '../../message/index.ts'\n\nexport type AgentMemoryKind =\n | 'objective'\n | 'constraint'\n | 'decision'\n | 'fact'\n | 'progress'\n | 'next-step'\n\nexport interface AgentMemorySeed {\n readonly id?: string\n readonly kind: AgentMemoryKind\n readonly content: string\n}\n\nexport interface AgentMemoryItem {\n readonly id: string\n readonly kind: AgentMemoryKind\n readonly content: string\n readonly createdAt: string\n readonly updatedAt: string\n}\n\nexport interface AgentMemorySnapshot {\n readonly version: 1\n readonly items: readonly AgentMemoryItem[]\n}\n\nexport interface AgentMemoryConfigInput {\n /** Pin the first real user message as the original objective. Defaults to true. */\n readonly autoCaptureObjective?: boolean\n /** Maximum rendered memory characters injected into every model request. Defaults to 12,000. */\n readonly maxInjectedChars?: number\n /** Maximum retained memory records. Defaults to 1,024. */\n readonly maxItems?: number\n /** Maximum characters retained by one record. Defaults to 65,536. */\n readonly maxItemChars?: number\n /** Maximum cumulative retained content characters. Defaults to 1 MiB. */\n readonly maxStoredChars?: number\n readonly seed?: readonly AgentMemorySeed[]\n}\n\nexport interface AgentMemoryConfig {\n readonly autoCaptureObjective: boolean\n readonly maxInjectedChars: number\n readonly maxItems: number\n readonly maxItemChars: number\n readonly maxStoredChars: number\n readonly seed: readonly AgentMemorySeed[]\n}\n\nconst PRIORITY: Readonly<Record<AgentMemoryKind, number>> = Object.freeze({\n objective: 0,\n constraint: 1,\n decision: 2,\n 'next-step': 3,\n progress: 4,\n fact: 5,\n})\n\nexport class AgentMemory {\n private readonly records = new Map<string, AgentMemoryItem>()\n private readonly limits: Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>\n private nextId = 1\n private storedChars = 0\n\n constructor(\n seed: readonly AgentMemorySeed[] = [],\n limits: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>> = {},\n ) {\n this.limits = resolveMemoryLimits(limits)\n for (const item of seed) this.remember(item)\n }\n\n static fromSnapshot(\n snapshot: AgentMemorySnapshot,\n limits: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>> = {},\n ): AgentMemory {\n if (typeof snapshot !== 'object' || snapshot === null\n || snapshot.version !== 1 || !Array.isArray(snapshot.items)) {\n throw new TypeError('unsupported agent memory snapshot')\n }\n const memory = new AgentMemory([], limits)\n if (snapshot.items.length > memory.limits.maxItems) {\n throw new RangeError(`agent memory snapshot exceeds the ${memory.limits.maxItems}-item limit`)\n }\n for (const item of snapshot.items) {\n validateItem(item)\n if (memory.records.has(item.id)) throw new TypeError(`duplicate agent memory id '${item.id}'`)\n if (item.content.length > memory.limits.maxItemChars) {\n throw new RangeError(`agent memory item '${item.id}' exceeds the ${memory.limits.maxItemChars}-character limit`)\n }\n if (memory.storedChars + item.content.length > memory.limits.maxStoredChars) {\n throw new RangeError(`agent memory snapshot exceeds the ${memory.limits.maxStoredChars}-character limit`)\n }\n memory.records.set(item.id, Object.freeze({\n id: item.id,\n kind: item.kind,\n content: item.content,\n createdAt: item.createdAt,\n updatedAt: item.updatedAt,\n }))\n memory.storedChars += item.content.length\n const successor = memoryIdSuccessor(item.id)\n if (successor !== undefined) memory.nextId = Math.max(memory.nextId, successor)\n }\n return memory\n }\n\n /** Add or update one stable memory item. Supplying the same id is an upsert. */\n remember(input: AgentMemorySeed): AgentMemoryItem {\n validateSeed(input)\n const id = input.id ?? `memory-${this.nextId}`\n const content = input.content.trim()\n if (content.length > this.limits.maxItemChars) {\n throw new RangeError(`agent memory item '${id}' exceeds the ${this.limits.maxItemChars}-character limit`)\n }\n const successor = memoryIdSuccessor(id)\n const previous = this.records.get(id)\n if (previous === undefined && this.records.size >= this.limits.maxItems) {\n throw new RangeError(`agent memory reached its ${this.limits.maxItems}-item limit`)\n }\n const nextStoredChars = this.storedChars - (previous?.content.length ?? 0) + content.length\n if (nextStoredChars > this.limits.maxStoredChars) {\n throw new RangeError(`agent memory reached its ${this.limits.maxStoredChars}-character limit`)\n }\n const at = new Date().toISOString()\n const item = Object.freeze({\n id,\n kind: input.kind,\n content,\n createdAt: previous?.createdAt ?? at,\n updatedAt: at,\n })\n this.records.set(id, item)\n this.storedChars = nextStoredChars\n if (successor !== undefined) this.nextId = Math.max(this.nextId, successor)\n return item\n }\n\n forget(id: string): boolean {\n const item = this.records.get(id)\n if (item === undefined) return false\n this.records.delete(id)\n this.storedChars -= item.content.length\n return true\n }\n\n items(): readonly AgentMemoryItem[] {\n return Object.freeze([...this.records.values()])\n }\n\n snapshot(): AgentMemorySnapshot {\n return Object.freeze({\n version: 1,\n items: Object.freeze(this.items().map(item => Object.freeze(structuredClone(item)))),\n })\n }\n\n /** Capture the first user request verbatim enough to survive every later compaction. */\n captureOriginalObjective(message: UserMessage): AgentMemoryItem | undefined {\n if ([...this.records.values()].some(item => item.kind === 'objective')) return undefined\n const text = collectTextPrefix(message, this.limits.maxItemChars).trim()\n return this.remember({\n id: 'original-objective',\n kind: 'objective',\n content: text.length > 0 ? text : '[The original request contained non-text input.]',\n })\n }\n\n /** Render bounded pinned context, ordered by importance rather than insertion accident. */\n render(maxChars = 12_000): string {\n if (!Number.isInteger(maxChars) || maxChars < 256) {\n throw new RangeError('agent memory render maxChars must be an integer >= 256')\n }\n const ordered = [...this.records.values()].sort((a, b) =>\n PRIORITY[a.kind] - PRIORITY[b.kind] || a.createdAt.localeCompare(b.createdAt))\n if (ordered.length === 0) return ''\n const preamble = [\n '<task-memory>',\n 'Retained context is background from earlier work. Newer user messages supersede conflicting objectives or constraints below. Follow the latest request and its output format.',\n ]\n const closing = '</task-memory>'\n let remaining = maxChars - preamble.join('\\n').length - closing.length - 2\n const lines: string[] = []\n for (const item of ordered) {\n if (remaining <= 0) break\n const prefix = `- ${item.kind} [${item.id}]: `\n const contentBudget = Math.max(0, remaining - prefix.length - 1)\n const content = truncateMiddle(JSON.stringify(item.content), contentBudget)\n const line = prefix + content\n lines.push(line)\n remaining -= line.length + 1\n }\n return [...preamble, ...lines, closing].join('\\n')\n }\n}\n\nexport function resolveMemoryConfig(input: AgentMemoryConfigInput | undefined): AgentMemoryConfig {\n const maxInjectedChars = input?.maxInjectedChars ?? 12_000\n if (!Number.isInteger(maxInjectedChars) || maxInjectedChars < 256) {\n throw new RangeError('agent memory maxInjectedChars must be an integer >= 256')\n }\n const limits = resolveMemoryLimits(input ?? {})\n const ids = new Set<string>()\n let nextId = 1\n let storedChars = 0\n const seed = Object.freeze([...(input?.seed ?? [])].map(item => {\n validateSeed(item)\n const id = item.id ?? `memory-${nextId++}`\n const successor = memoryIdSuccessor(id)\n if (successor !== undefined) nextId = Math.max(nextId, successor)\n if (ids.has(id)) throw new TypeError(`duplicate agent memory seed id '${id}'`)\n ids.add(id)\n const chars = item.content.trim().length\n if (chars > limits.maxItemChars) {\n throw new RangeError(`agent memory seed '${id}' exceeds the ${limits.maxItemChars}-character limit`)\n }\n storedChars += chars\n if (storedChars > limits.maxStoredChars) {\n throw new RangeError(`agent memory seed exceeds the ${limits.maxStoredChars}-character limit`)\n }\n return Object.freeze({\n ...(item.id === undefined ? {} : { id: item.id }),\n kind: item.kind,\n content: item.content,\n })\n }))\n if (seed.length > limits.maxItems) {\n throw new RangeError(`agent memory seed exceeds the ${limits.maxItems}-item limit`)\n }\n return Object.freeze({\n autoCaptureObjective: input?.autoCaptureObjective ?? true,\n maxInjectedChars,\n ...limits,\n seed,\n })\n}\n\nfunction validateSeed(input: AgentMemorySeed): void {\n if (input.id !== undefined && (typeof input.id !== 'string'\n || input.id.trim().length === 0 || input.id.length > 256 || /\\s/.test(input.id))) {\n throw new TypeError('agent memory id must be non-empty, at most 256 characters, and contain no whitespace')\n }\n if (!Object.hasOwn(PRIORITY, input.kind)) throw new TypeError(`unsupported agent memory kind '${String(input.kind)}'`)\n if (typeof input.content !== 'string' || input.content.trim().length === 0) {\n throw new TypeError('agent memory content must be a non-empty string')\n }\n}\n\nfunction resolveMemoryLimits(\n input: Partial<Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'>>,\n): Pick<AgentMemoryConfig, 'maxItems' | 'maxItemChars' | 'maxStoredChars'> {\n const maxItems = positiveInteger(input.maxItems ?? 1_024, 'maxItems')\n const maxItemChars = positiveInteger(input.maxItemChars ?? 65_536, 'maxItemChars')\n const maxStoredChars = positiveInteger(input.maxStoredChars ?? 1024 * 1024, 'maxStoredChars')\n if (maxItemChars > maxStoredChars) {\n throw new RangeError('agent memory maxItemChars must not exceed maxStoredChars')\n }\n return Object.freeze({ maxItems, maxItemChars, maxStoredChars })\n}\n\nfunction positiveInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new RangeError(`agent memory ${name} must be a positive safe integer`)\n }\n return value\n}\n\nfunction validateItem(item: AgentMemoryItem): void {\n validateSeed(item)\n if (!validTimestamp(item.createdAt) || !validTimestamp(item.updatedAt)) {\n throw new TypeError('agent memory timestamps must be bounded ISO date strings')\n }\n}\n\nfunction validTimestamp(value: unknown): value is string {\n if (typeof value !== 'string' || value.length === 0 || value.length > 64) return false\n const parsed = Date.parse(value)\n if (!Number.isFinite(parsed)) return false\n try { return new Date(parsed).toISOString() === value } catch { return false }\n}\n\nfunction memoryIdSuccessor(id: string): number | undefined {\n const suffix = /^memory-(\\d+)$/.exec(id)?.[1]\n if (suffix === undefined) return undefined\n const numeric = Number(suffix)\n return Number.isSafeInteger(numeric) && numeric >= 1 && numeric < Number.MAX_SAFE_INTEGER\n ? numeric + 1\n : undefined\n}\n\nfunction collectTextPrefix(message: UserMessage, maxChars: number): string {\n const marker = '…[objective truncated]'\n let text = ''\n let truncated = false\n for (const block of message.content) {\n if (block.type !== 'text') continue\n const separator = text.length === 0 ? '' : '\\n'\n const remaining = maxChars - text.length\n if (separator.length + block.text.length <= remaining) {\n text += separator + block.text\n continue\n }\n const available = Math.max(0, remaining - separator.length - marker.length)\n text += separator + block.text.slice(0, available) + marker.slice(0, Math.max(0, remaining - separator.length - available))\n truncated = true\n break\n }\n return truncated ? text.slice(0, maxChars) : text\n}\n\nfunction truncateMiddle(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n if (maxChars <= 1) return value.slice(0, maxChars)\n const marker = '…'\n const head = Math.ceil((maxChars - marker.length) / 2)\n const tail = Math.floor((maxChars - marker.length) / 2)\n return value.slice(0, head) + marker + value.slice(value.length - tail)\n}\n"],"mappings":"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"}
|
|
@@ -9,6 +9,20 @@ declare const AGENT_CONTROL_TOOLS: Readonly<{
|
|
|
9
9
|
readonly complete: 'submit_result';
|
|
10
10
|
readonly requestUserInput: 'request_user_input';
|
|
11
11
|
}>;
|
|
12
|
+
/**
|
|
13
|
+
* What the model replies with, after an accepted self-check, when the answer
|
|
14
|
+
* it already gave the user earlier in the run still stands.
|
|
15
|
+
*
|
|
16
|
+
* Deep modes gate on an accepted `submit_result`, so a model that answered
|
|
17
|
+
* before checking is asked to check, then told to "deliver the answer"
|
|
18
|
+
* again — even when the check found nothing to add. Without this marker that
|
|
19
|
+
* second pass either pads an already-adequate answer or, worse, quietly
|
|
20
|
+
* changes it while claiming to only be confirming it. `driveAgent` treats a
|
|
21
|
+
* reply that is exactly this marker as "no change": the run's `text` (and,
|
|
22
|
+
* where the earlier answer is still the history's current surface, the
|
|
23
|
+
* transcript) keep the answer already given instead of this marker.
|
|
24
|
+
*/
|
|
25
|
+
declare const UNCHANGED_ANSWER_MARKER = "<<deep-mode:answer-unchanged>>";
|
|
12
26
|
/** Default high-level runtime requested by this SDK; `runTurn` remains provider-neutral. */
|
|
13
27
|
type AgentMode = 'basic' | 'deep' | 'deep-human-in-loop';
|
|
14
28
|
interface AgentRunCommon extends Omit<RunTurnOptions, 'bounds' | 'commentary' | 'config' | 'hooks' | 'system' | 'tools'> {
|
|
@@ -75,5 +89,5 @@ type AgentRunEvent = AgentEvent | AgentModeStartEvent | UserInputRequestEvent |
|
|
|
75
89
|
*/
|
|
76
90
|
declare function runAgent(options: RunAgentOptions): AsyncIterable<AgentRunEvent>;
|
|
77
91
|
//#endregion
|
|
78
|
-
export { AGENT_CONTROL_TOOLS, AgentMode, AgentRunEvent, AgentRunOutcome, BasicAgentOptions, CompletionSubmission, DeepAgentOptions, HumanInLoopAgentOptions, RunAgentOptions, runAgent };
|
|
92
|
+
export { AGENT_CONTROL_TOOLS, AgentMode, AgentRunEvent, AgentRunOutcome, BasicAgentOptions, CompletionSubmission, DeepAgentOptions, HumanInLoopAgentOptions, RunAgentOptions, UNCHANGED_ANSWER_MARKER, runAgent };
|
|
79
93
|
//# sourceMappingURL=run-agent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-agent.d.ts","names":[],"sources":["../../../src/agent/mode/run-agent.ts"],"mappings":";;;;;;;cAkBa,qBAAmB;WACpB;WACQ
|
|
1
|
+
{"version":3,"file":"run-agent.d.ts","names":[],"sources":["../../../src/agent/mode/run-agent.ts"],"mappings":";;;;;;;cAkBa,qBAAmB;WACpB;WACQ;;;;;;;;;;;;;;;cAgBP;;KAGD;UAEF,uBAAuB,KAAK;;WAE3B,QAAQ;WACR,QAAQ;WACR;;;WAGA;WACA,SAAS,KAAK,QAAQ;WACtB,aAAa;WACb,QAAQ;;UAGF,0BAA0B;WAChC;;UAGM,yBAAyB;WAC/B;;WAEA,YAAY;;UAGN,gCAAgC;WACtC;;WAEA,WAAW;;KAGV,kBAAkB,oBAAoB,mBAAmB;UAEpD;WACN;WACA;;UAGM,wBAAwB;WAC9B,MAAM;;WAEN;WACA,aAAa;;UAGd;WACC;WACA,MAAM;WACN;;UAED;WACC;WACA,SAAS;;UAEV;WACC;WACA,SAAS;WACT,UAAU;;UAEX;WACC;WACA,SAAS;;KAGR,gBAAgB,aACxB,sBACA,wBACA,yBACA;;;;;;;;iBA8BY,SAAS,SAAS,kBAAkB,cAAc"}
|