mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 12:55:51 +00:00
Compare commits
4 Commits
v2.0.3
...
memory-lea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e215bb061 | ||
|
|
b28de717dd | ||
|
|
5c1be19511 | ||
|
|
2545dcabfd |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 2.2 MiB |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-code-best",
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.4",
|
||||
"description": "Reverse-engineered Anthropic Claude Code CLI — interactive AI coding assistant in the terminal",
|
||||
"type": "module",
|
||||
"author": "claude-code-best <claude-code-best@proton.me>",
|
||||
|
||||
@@ -49,7 +49,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 和探索任务模型选择
|
||||
|
||||
@@ -1003,6 +1003,15 @@ 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
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ export async function updateCCB(): Promise<void> {
|
||||
|
||||
try {
|
||||
if (pkgManager === 'bun') {
|
||||
execSync(`bun update -g ${PACKAGE_NAME}`, {
|
||||
execSync(`bun install -g ${PACKAGE_NAME}@latest`, {
|
||||
stdio: 'inherit',
|
||||
cwd: homedir(),
|
||||
timeout: 120_000,
|
||||
@@ -153,7 +153,9 @@ export async function updateCCB(): Promise<void> {
|
||||
process.stderr.write('\n')
|
||||
process.stderr.write('Try manually updating with:\n')
|
||||
if (pkgManager === 'bun') {
|
||||
process.stderr.write(chalk.bold(` bun update -g ${PACKAGE_NAME}`) + '\n')
|
||||
process.stderr.write(
|
||||
chalk.bold(` bun install -g ${PACKAGE_NAME}@latest`) + '\n',
|
||||
)
|
||||
} else {
|
||||
process.stderr.write(
|
||||
chalk.bold(` npm install -g ${PACKAGE_NAME}@latest`) + '\n',
|
||||
|
||||
@@ -320,6 +320,16 @@ async function doInitializeTelemetry(): Promise<void> {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip entire OTel initialization when telemetry is not enabled.
|
||||
// Prevents PerformanceMeasure accumulation in long-running sessions.
|
||||
if (!isEnvTruthy(process.env.CLAUDE_CODE_ENABLE_TELEMETRY)) {
|
||||
telemetryInitialized = true
|
||||
logForDebugging(
|
||||
'[3P telemetry] Skipped — CLAUDE_CODE_ENABLE_TELEMETRY not set',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Set flag before init to prevent double initialization
|
||||
telemetryInitialized = true
|
||||
try {
|
||||
|
||||
@@ -163,3 +163,77 @@ 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)
|
||||
}
|
||||
|
||||
@@ -206,10 +206,49 @@ async function getOtlpReaders() {
|
||||
|
||||
return exporters.map(exporter => {
|
||||
if ('export' in exporter) {
|
||||
return new PeriodicExportingMetricReader({
|
||||
const reader = 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
|
||||
})
|
||||
|
||||
@@ -83,7 +83,7 @@ export default defineConfig({
|
||||
target: 'es2020',
|
||||
copyPublicDir: false,
|
||||
sourcemap: false,
|
||||
minify: false,
|
||||
minify: true,
|
||||
|
||||
// SSR build mode — uses Rollup with Node.js target
|
||||
ssr: true,
|
||||
|
||||
Reference in New Issue
Block a user