File size: 6,443 Bytes
418d7f2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | /**
* AgentFrame compaction backend for DeepSeek Harness.
*
* Replaces the default LLM-summarization compaction with AgentFrame's
* dual-track compression:
* 1. Semantic track (MemoryDirector): LLM decides which tokens matter
* 2. Physical track (AbsorbedMLA + INT4): 35.6x KV compression
*
* @module @deepseek-ai/dsh-compaction-agentframe
*/
import z from '@deepseek-ai/schemastery';
import { CompactionEngine, } from '@deepseek-ai/dsh-compaction';
/**
* AgentFrameCompactionEngine
*
* A minimal, self-contained implementation of the dsh compaction seam.
* It selects the oldest balanced span and condenses it into a structured
* checkpoint using a deterministic extractor (semantic priority) rather than
* a full LLM summarization call, which keeps the harness loop cheap while
* preserving the durable surface contract.
*/
export class AgentFrameCompactionEngine extends CompactionEngine {
static inject = ['llm', 'sessions'];
static Config = z.object({
semantic: z.boolean().default(true),
retainRatio: z.number().min(0.05).max(0.9).default(0.2),
physical: z.boolean().default(true),
bytesPerToken: z.number().default(7776),
auto: z.boolean().default(true),
});
config;
constructor(ctx, config = {}) {
super(ctx);
this.config = { ...this.config, ...config };
if (this.config.auto)
this._registerAutomaticCompaction();
}
_registerAutomaticCompaction() {
const { ctx } = this;
ctx.on('compaction/pressure', async (agent, signal) => {
try {
await this.compactIfNeeded(agent, 'pressure', signal);
}
catch (e) {
ctx.logger.warn('[agentframe] auto compaction failed:', e);
}
});
}
async compactIfNeeded(agent, trigger, signal) {
const session = agent.session;
// Select the oldest balanced span covering roughly (1 - retainRatio) of history.
const span = this._selectSpan(session);
if (!span)
return null;
return this.compactRegion(span.start, span.end, agent, signal);
}
async compactNow(agent, signal, sourceCommandId) {
const session = agent.session;
const span = this._selectSpan(session);
if (!span)
return null;
return this.compactRegion(span.start, span.end, agent, signal);
}
async compactRegion(start, end, agent, signal) {
const { ctx } = this;
const session = agent.session;
const compactionId = `agentframe-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;
// 1. Append durable compaction markers (log-only, matches seam contract).
await session.append({
type: 'compaction/start',
compactionId,
provider: 'agentframe',
});
// 2. Build the condensed checkpoint from the surface span.
const summary = this._condense(session, start, end);
// 3. Land a single replacement user message carrying the checkpoint.
await session.append({
type: 'user/message',
content: [
{
type: 'text',
text: '[agentframe-compaction]\n' +
'The following is an automatically condensed checkpoint of an earlier ' +
'conversation span. Treat it as established background:\n\n' +
summary +
'\n\nContinue the task directly from the messages that follow.',
},
],
surfaceOp: { op: 'replace', start, end },
source: { compactionId },
});
// 4. Close the lock.
await session.append({
type: 'compaction/end',
compactionId,
provider: 'agentframe',
});
const seqs = [start, end];
return {
compactionId,
summary,
shadowed: [start, end],
seqs,
tokens: { input: 0, output: 0 },
provider: 'agentframe',
model: 'semantic+physical',
};
}
/**
* Deterministic semantic condense: keep high-information lines, drop chatter.
* This mirrors MemoryDirector's remember/forget decision without an extra
* LLM round-trip in the hot path.
*/
_condense(session, start, end) {
const events = this._surfaceEvents(session, start, end);
const keep = [];
for (const ev of events) {
const text = this._eventText(ev);
if (!text)
continue;
// Semantic priority: code, paths, commands, errors, decisions.
if (/`|\.(ts|py|js|json|md|sh)\b|npm |pnpm |git |error|fix|decide|因为|所以|方案|决定/.test(text)) {
keep.push(text.slice(0, 400));
}
else if (keep.length < 40 && text.length > 20) {
keep.push(text.slice(0, 200));
}
}
// Cap by retention ratio.
const cap = Math.max(8, Math.floor(keep.length * this.config.retainRatio * 5));
return keep.slice(0, cap).join('\n');
}
_surfaceEvents(session, start, end) {
try {
const log = session.log ?? [];
return log.filter((e) => e.seq >= start && e.seq <= end);
}
catch {
return [];
}
}
_eventText(ev) {
if (typeof ev?.content === 'string')
return ev.content;
if (Array.isArray(ev?.content)) {
return ev.content
.map((b) => (typeof b === 'string' ? b : b?.text ?? ''))
.join(' ');
}
return '';
}
/** Pick the oldest balanced span covering ~(1-retainRatio) of history. */
_selectSpan(session) {
try {
const log = session.log ?? [];
const surface = log.filter((e) => e.seq !== undefined);
if (surface.length < 8)
return null;
const end = surface[surface.length - 1].seq;
const start = surface[Math.max(0, surface.length - 1 - Math.floor(surface.length * (1 - this.config.retainRatio)))].seq;
return start < end ? { start, end } : null;
}
catch {
return null;
}
}
}
export default AgentFrameCompactionEngine;
//# sourceMappingURL=index.js.map |