Files
claude-code/src/services/awaySummary.ts
claude-code-best ed4bdb9338 feat: 增强 auto mode 的易用性 (#312)
* feat: poor 模式降级 yolo 审阅模型

* feat: 为多模块添加 Langfuse tracing 支持

在 web search、agent creation、away summary、token estimation、
skill improvement 等模块中集成 Langfuse trace,并透传至
compact/apiQueryHook/execPromptHook 等调用链。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 让 auto mode 记录回主 trace

* fix: reopen auto mode prompt when classifier is unavailable

* fix: 修复 auto mode 情况下, llm 报错导致弹窗也不打开的问题

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 21:13:09 +08:00

100 lines
3.5 KiB
TypeScript

import { APIUserAbortError } from '@anthropic-ai/sdk'
import { getEmptyToolPermissionContext } from '../Tool.js'
import type { Message } from '../types/message.js'
import { logForDebugging } from '../utils/debug.js'
import {
createUserMessage,
getAssistantMessageText,
} from '../utils/messages.js'
import { getSmallFastModel } from '../utils/model/model.js'
import { asSystemPrompt } from '../utils/systemPromptType.js'
import { getResolvedLanguage } from '../utils/language.js'
import { queryModelWithoutStreaming } from './api/claude.js'
import { createTrace, endTrace, isLangfuseEnabled } from './langfuse/index.js'
import { getSessionId } from '../bootstrap/state.js'
import { getAPIProvider } from '../utils/model/providers.js'
import { getSessionMemoryContent } from './SessionMemory/sessionMemoryUtils.js'
// Recap only needs recent context — truncate to avoid "prompt too long" on
// large sessions. 30 messages ≈ ~15 exchanges, plenty for "where we left off."
const RECENT_MESSAGE_WINDOW = 30
const PROMPT_EN =
'The user stepped away and is coming back. Write exactly 1-3 short sentences. Start by stating the high-level task — what they are building or debugging, not implementation details. Next: the concrete next step. Skip status reports and commit recaps.'
const PROMPT_ZH =
'用户离开后回来了。用中文写 1-3 句话。先说明用户在做什么(高层目标,不是实现细节),然后说明下一步具体操作。不要写状态报告或提交总结。'
function buildAwaySummaryPrompt(memory: string | null): string {
const memoryBlock = memory
? `Session memory (broader context):\n${memory}\n\n`
: ''
const prompt = getResolvedLanguage() === 'zh' ? PROMPT_ZH : PROMPT_EN
return `${memoryBlock}${prompt}`
}
/**
* Generates a short session recap for the "while you were away" card.
* Returns null on abort, empty transcript, or error.
*/
export async function generateAwaySummary(
messages: readonly Message[],
signal: AbortSignal,
): Promise<string | null> {
if (messages.length === 0) {
return null
}
const model = getSmallFastModel()
const langfuseTrace = isLangfuseEnabled()
? createTrace({
sessionId: getSessionId(),
model,
provider: getAPIProvider(),
name: 'away-summary',
})
: null
try {
const memory = await getSessionMemoryContent()
const recent = messages.slice(-RECENT_MESSAGE_WINDOW)
recent.push(createUserMessage({ content: buildAwaySummaryPrompt(memory) }))
const response = await queryModelWithoutStreaming({
messages: recent,
systemPrompt: asSystemPrompt([]),
thinkingConfig: { type: 'disabled' },
tools: [],
signal,
options: {
getToolPermissionContext: async () => getEmptyToolPermissionContext(),
model,
toolChoice: undefined,
isNonInteractiveSession: false,
hasAppendSystemPrompt: false,
agents: [],
querySource: 'away_summary',
mcpTools: [],
skipCacheWrite: true,
langfuseTrace,
},
})
if (response.isApiErrorMessage) {
logForDebugging(
`[awaySummary] API error: ${getAssistantMessageText(response)}`,
)
endTrace(langfuseTrace, undefined, 'error')
return null
}
endTrace(langfuseTrace)
return getAssistantMessageText(response)
} catch (err) {
if (err instanceof APIUserAbortError || signal.aborted) {
return null
}
logForDebugging(`[awaySummary] generation failed: ${err}`)
endTrace(langfuseTrace, undefined, 'error')
return null
}
}