Merge master into tool-result pruning

Retarget the pruning feature onto the merged compaction foundation.

Adapt content-only tool-result rewrite validation to the session-owned surface manager, migrate the demo wiring to repl-agent, and refresh generated type, catalog, and website contracts while preserving pressure and overflow pruning behavior.
This commit is contained in:
Tianyi Cui
2026-07-19 17:54:55 +08:00
972 changed files with 59334 additions and 11465 deletions

View File

@@ -9,7 +9,7 @@ import z from 'schemastery'
import { CompactService } from '@deepseek-ai/dsh-compact'
import type { CompactionResult, CompactionTrigger } from '@deepseek-ai/dsh-compact'
import type { Session } from '@deepseek-ai/dsh-session'
import { CONTEXT_WINDOW_EXCEEDED_CODE } from '@deepseek-ai/dsh-llm'
import { CONTEXT_WINDOW_EXCEEDED_CODE, assertNever } from '@deepseek-ai/dsh-llm'
import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { Agent } from '@deepseek-ai/dsh-agent'
// Type-only: makes the optional sibling service available to `ctx.get()`.
@@ -22,7 +22,6 @@ import type {
ResolvedConfig,
} from './types.ts'
export { resolveConfig } from './config.ts'
export type {
BasicCompactConfig,
ResolvedConfig,
@@ -48,6 +47,7 @@ export class BasicCompactService extends CompactService {
static Config: z<BasicCompactConfig> = z.object({
thresholdRatio: z.number().default(0.8),
retainTokens: z.number().step(1),
summarizationProvider: z.string().default(''),
summarizationModel: z.string().default(''),
maxTokens: z.number().step(1).min(1).default(8192),
compactionRetries: z.number().step(1).min(0).default(1),
@@ -141,11 +141,11 @@ export class BasicCompactService extends CompactService {
* @param signal - optional cancellation forwarded to the adapter.
* @returns safe text summary blocks and exact auxiliary-call provenance.
*/
async summarize(
protected async summarize(
text: string,
agent: Agent,
signal?: AbortSignal,
): Promise<{ summary: ContentBlock[]; model: string; maxTokens?: number }> {
): Promise<{ summary: ContentBlock[]; provider: string; model: string; maxTokens?: number }> {
return summarizeWithLlm(this.ctx, this.config, text, agent, signal)
}
@@ -169,7 +169,16 @@ export class BasicCompactService extends CompactService {
const meter = this.ctx.tokenMeter
const threshold = Math.floor(meter.contextWindow * this.config.thresholdRatio)
let measurement = meter.measure(agent.session)
if (trigger === 'pressure' && measurement.totalTokens < threshold) return null
switch (trigger) {
case 'context-overflow':
break
case 'pressure':
if (measurement.totalTokens < threshold) return null
break
/* v8 ignore next -- closed-union exhaustiveness guard */
default:
assertNever(trigger, 'compaction trigger')
}
// Pruning is optional so compact-basic remains independently composable.
// Once either trigger qualifies, land the model-free pass before choosing
@@ -183,7 +192,7 @@ export class BasicCompactService extends CompactService {
if (trigger === 'context-overflow') {
const range = selectCompactableRange(agent.session, measurement, 0)
if (range === null) return null
return this.compactRegion(agent.session, range.start, range.end, agent, signal)
return this.compactRegion(range.start, range.end, agent, signal)
}
if (measurement.totalTokens < threshold) return null
@@ -197,7 +206,7 @@ export class BasicCompactService extends CompactService {
/* v8 ignore next -- paired with the defensive post-success branch above. */
break
}
result = await this.compactRegion(agent.session, range.start, range.end, agent, signal)
result = await this.compactRegion(range.start, range.end, agent, signal)
measurement = meter.measure(agent.session)
if (measurement.totalTokens < threshold) return result
}
@@ -209,10 +218,8 @@ export class BasicCompactService extends CompactService {
}
/**
* Compact one inclusive positional surface range using the effective
* token meter for all retention and shrink pricing. Reject an agent that does
* not own the exact target before any mutation.
* @param session - session whose surface is mutated; must equal `agent.session`.
* Compact one inclusive positional range from the agent-owned surface using
* the effective token meter for all retention and shrink pricing.
* @param start - inclusive first surface-node seq.
* @param end - inclusive last surface-node seq.
* @param agent - owner of the target session, used by the summarizer.
@@ -220,15 +227,12 @@ export class BasicCompactService extends CompactService {
* @returns the successful durable compaction result.
*/
override async compactRegion(
session: Session,
start: number,
end: number,
agent: Agent,
signal?: AbortSignal,
): Promise<CompactionResult> {
if (session !== agent.session) {
throw new Error('compactRegion: agent.session must be the exact target session')
}
const session = agent.session
return compactSurfaceRegion({
meter: this.ctx.tokenMeter,
summarize: (text, owner, abort) => this.summarize(text, owner, abort),