revert: 恢复 HISTORY_SNIP

This commit is contained in:
claude-code-best
2026-05-04 23:51:36 +08:00
parent 5b333e2246
commit 45c892fc18
4 changed files with 2 additions and 124 deletions

View File

@@ -59,7 +59,7 @@ export const DEFAULT_BUILD_FEATURES = [
'DAEMON', // 守护进程模式,长驻 supervisor 管理后台 worker非 GB 级主因)
'ACP', // ACP 代理协议,支持外部 agent 接入
'WORKFLOW_SCRIPTS', // 工作流脚本(.claude/workflows/ 中的 YAML/MD
// 'HISTORY_SNIP', // 历史消息裁剪,压缩上下文窗口
'HISTORY_SNIP', // 历史消息裁剪,压缩上下文窗口
// 'CONTEXT_COLLAPSE', // 已禁用:实现是空壳 stub启用后会抑制 auto compact 导致上下文管理完全失效
'MONITOR_TOOL', // Monitor 工具,流式监控后台进程输出
// 'FORK_SUBAGENT', // 已禁用:显式 `fork: true` 参数触发 fork 路径(继承父级上下文和模型),不影响 forceAsync 和探索任务模型选择

View File

@@ -1003,15 +1003,6 @@ export class QueryEngine {
uuid: msg.uuid,
}
}
// Proactive truncation: prevent unbounded growth when API doesn't
// return compact_boundary (e.g. third-party compat layers).
if (feature('HISTORY_SNIP') && snipModule) {
const truncated = snipModule.proactiveTruncate(this.mutableMessages)
if (truncated !== this.mutableMessages) {
this.mutableMessages.length = 0
this.mutableMessages.push(...truncated)
}
}
// Don't yield other system messages in headless mode
break
}

View File

@@ -163,77 +163,3 @@ export function isSnipRuntimeEnabled(): boolean {
export function shouldNudgeForSnips(messages: Message[]): boolean {
return messages.length >= SNIP_NUDGE_THRESHOLD
}
/**
* Maximum total character length of message content before proactive
* truncation kicks in. ~150 MB of string data corresponds to roughly
* 1.5x the default 200k-token context window at 4 chars/token — well
* beyond what any model can actually use in a single request.
*/
const PROACTIVE_TRUNCATE_CHARS = 150_000_000
/**
* Minimum number of messages to keep when falling back to tail-only
* retention (i.e. when no compact_boundary exists in the array).
*/
const PROACTIVE_TRUNCATE_MIN_TAIL = 50
/**
* Proactively truncate old messages when the in-memory store grows too
* large. Unlike `snipCompactIfNeeded` (which waits for a snip_boundary
* from the API), this runs client-side after every push — ensuring
* unbounded growth cannot happen even when the API never returns a
* compact_boundary (e.g. third-party compat layers).
*
* Strategy:
* 1. If a `compact_boundary` exists, keep it and everything after it.
* 2. Otherwise, keep only the last `PROACTIVE_TRUNCATE_MIN_TAIL` messages.
*
* Returns the same array reference when no truncation is needed.
*/
export function proactiveTruncate(messages: Message[]): Message[] {
if (messages.length < PROACTIVE_TRUNCATE_MIN_TAIL) return messages
let totalChars = 0
for (const msg of messages) {
const content = msg.message?.content
if (typeof content === 'string') {
totalChars += content.length
} else if (Array.isArray(content)) {
for (const block of content) {
if (typeof block === 'string') {
totalChars += (block as string).length
} else if (block && typeof block === 'object') {
const obj = block as unknown as Record<string, unknown>
const text = obj.text ?? obj.content
if (typeof text === 'string') {
totalChars += text.length
}
}
}
}
}
if (totalChars < PROACTIVE_TRUNCATE_CHARS) return messages
// Find last compact_boundary — the standard anchor point
let boundaryIdx = -1
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i]!
if (
msg.type === 'system' &&
(msg as Record<string, unknown>).subtype === 'compact_boundary'
) {
boundaryIdx = i
break
}
}
const keepFrom =
boundaryIdx >= 0
? boundaryIdx
: Math.max(0, messages.length - PROACTIVE_TRUNCATE_MIN_TAIL)
if (keepFrom === 0) return messages
return messages.slice(keepFrom)
}

View File

@@ -206,49 +206,10 @@ async function getOtlpReaders() {
return exporters.map(exporter => {
if ('export' in exporter) {
const reader = new PeriodicExportingMetricReader({
return new PeriodicExportingMetricReader({
exporter,
exportIntervalMillis: exportInterval,
})
// Wrap the export callback to auto-shutdown the reader on auth
// failures (401/403). Without this the PeriodicExportingMetricReader's
// internal setInterval keeps retrying forever, leaking handles.
const originalExport = (
exporter as unknown as {
export: (
metrics: unknown,
callback: (result: { error?: Error }) => void,
) => unknown
}
).export.bind(exporter)
;(
exporter as unknown as {
export: (
metrics: unknown,
callback: (result: { error?: Error }) => void,
) => unknown
}
).export = (metrics, callback) => {
return originalExport(metrics, result => {
if (result.error) {
const msg = result.error.message || ''
if (
msg.includes('401') ||
msg.includes('403') ||
msg.includes('Unauthorized') ||
msg.includes('authentication')
) {
logForDebugging(
`[3P telemetry] Auth error detected, shutting down metric reader`,
{ level: 'error' },
)
void reader.shutdown()
}
}
callback(result)
})
}
return reader
}
return exporter
})