feat: 问就是封包

This commit is contained in:
claude-code-best
2026-03-31 23:32:58 +08:00
parent d7a729ca68
commit dd9cd782a7
67 changed files with 423 additions and 172 deletions

View File

@@ -553,8 +553,8 @@ export function buildMissedTaskNotification(missed: CronTask[]): string {
// Use a fence one longer than any backtick run in the prompt so a
// prompt containing ``` cannot close the fence early and un-wrap the
// trailing text (CommonMark fence-matching rule).
const longestRun = (t.prompt.match(/`+/g) ?? []).reduce(
(max, run) => Math.max(max, run.length),
const longestRun = (t.prompt.match(/`+/g) ?? ([] as string[])).reduce(
(max: number, run: string) => Math.max(max, run.length),
0,
)
const fence = '`'.repeat(Math.max(3, longestRun + 1))

View File

@@ -1,4 +1,4 @@
import type { McpbManifest } from '@anthropic-ai/mcpb'
import type { McpbManifestAny } from '@anthropic-ai/mcpb'
import { errorMessage } from '../errors.js'
import { jsonParse } from '../slowOperations.js'
@@ -12,15 +12,15 @@ import { jsonParse } from '../slowOperations.js'
*/
export async function validateManifest(
manifestJson: unknown,
): Promise<McpbManifest> {
const { McpbManifestSchema } = await import('@anthropic-ai/mcpb')
const parseResult = McpbManifestSchema.safeParse(manifestJson)
): Promise<McpbManifestAny> {
const { vAny } = await import('@anthropic-ai/mcpb')
const parseResult = vAny.McpbManifestSchema.safeParse(manifestJson)
if (!parseResult.success) {
const errors = parseResult.error.flatten()
const errorMessages = [
...Object.entries(errors.fieldErrors).map(
([field, errs]) => `${field}: ${errs?.join(', ')}`,
([field, errs]) => `${field}: ${(errs as any)?.join(', ')}`,
),
...(errors.formErrors || []),
]
@@ -38,7 +38,7 @@ export async function validateManifest(
*/
export async function parseAndValidateManifestFromText(
manifestText: string,
): Promise<McpbManifest> {
): Promise<McpbManifestAny> {
let manifestJson: unknown
try {
@@ -55,7 +55,7 @@ export async function parseAndValidateManifestFromText(
*/
export async function parseAndValidateManifestFromBytes(
manifestData: Uint8Array,
): Promise<McpbManifest> {
): Promise<McpbManifestAny> {
const manifestText = new TextDecoder().decode(manifestData)
return parseAndValidateManifestFromText(manifestText)
}
@@ -65,7 +65,7 @@ export async function parseAndValidateManifestFromBytes(
* Uses the same algorithm as the directory backend for consistency.
*/
export function generateExtensionId(
manifest: McpbManifest,
manifest: McpbManifestAny,
prefix?: 'local.unpacked' | 'local.dxt',
): string {
const sanitize = (str: string) =>

View File

@@ -64,12 +64,12 @@ export async function findModifiedFiles(
outputsDir: string,
): Promise<string[]> {
// Use recursive flag to get all entries in one call
let entries: Awaited<ReturnType<typeof fs.readdir>>
let entries: Awaited<ReturnType<typeof fs.readdir>> | any[]
try {
entries = await fs.readdir(outputsDir, {
withFileTypes: true,
recursive: true,
})
}) as any[]
} catch {
// Directory doesn't exist or is not accessible
return []
@@ -113,7 +113,7 @@ export async function findModifiedFiles(
// Filter to files modified since turn start
const modifiedFiles: string[] = []
for (const result of statResults) {
if (result && result.mtimeMs >= turnStartTime) {
if (result && result.mtimeMs >= (turnStartTime as any as number)) {
modifiedFiles.push(result.filePath)
}
}

View File

@@ -558,10 +558,10 @@ export async function runForkedAgent({
if (message.type === 'stream_event') {
if (
'event' in message &&
message.event?.type === 'message_delta' &&
message.event.usage
(message as any).event?.type === 'message_delta' &&
(message as any).event.usage
) {
const turnUsage = updateUsage({ ...EMPTY_USAGE }, message.event.usage)
const turnUsage = updateUsage({ ...EMPTY_USAGE }, (message as any).event.usage)
totalUsage = accumulateUsage(totalUsage, turnUsage)
}
continue

View File

@@ -1325,14 +1325,15 @@ export function checkWritePermissionForTool<Input extends AnyObject>(
},
]
: generateSuggestions(path, 'write', toolPermissionContext, pathsToCheck)
const failedCheck = safetyCheck as { safe: false; message: string; classifierApprovable: boolean }
return {
behavior: 'ask',
message: safetyCheck.message,
message: failedCheck.message,
suggestions: safetySuggestions,
decisionReason: {
type: 'safetyCheck',
reason: safetyCheck.message,
classifierApprovable: safetyCheck.classifierApprovable,
reason: failedCheck.message,
classifierApprovable: failedCheck.classifierApprovable,
},
}
}

View File

@@ -112,8 +112,8 @@ export function isPathInSandboxWriteAllowlist(resolvedPath: string): boolean {
// their resolution to avoid N × config.length redundant syscalls per
// command with N write targets (matching getResolvedWorkingDirPaths).
const pathsToCheck = getPathsForPermissionCheck(resolvedPath)
const resolvedAllow = allowOnly.flatMap(getResolvedSandboxConfigPath)
const resolvedDeny = denyWithinAllow.flatMap(getResolvedSandboxConfigPath)
const resolvedAllow = allowOnly.flatMap(getResolvedSandboxConfigPath) as string[]
const resolvedDeny = denyWithinAllow.flatMap(getResolvedSandboxConfigPath) as string[]
return pathsToCheck.every(p => {
for (const denyPath of resolvedDeny) {
if (pathInWorkingPath(p, denyPath)) return false
@@ -184,12 +184,13 @@ export function isPathAllowed(
precomputedPathsToCheck,
)
if (!safetyCheck.safe) {
const failedCheck = safetyCheck as { safe: false; message: string; classifierApprovable: boolean }
return {
allowed: false,
decisionReason: {
type: 'safetyCheck',
reason: safetyCheck.message,
classifierApprovable: safetyCheck.classifierApprovable,
reason: failedCheck.message,
classifierApprovable: failedCheck.classifierApprovable,
},
}
}

View File

@@ -412,7 +412,7 @@ async function runPermissionRequestHooksForHeadlessAgent(
input,
context,
permissionMode,
suggestions,
suggestions as any,
context.abortController.signal,
)) {
if (!hookResult.permissionRequestResult) {
@@ -423,12 +423,12 @@ async function runPermissionRequestHooksForHeadlessAgent(
const finalInput = decision.updatedInput ?? input
// Persist permission updates if provided
if (decision.updatedPermissions?.length) {
persistPermissionUpdates(decision.updatedPermissions)
persistPermissionUpdates(decision.updatedPermissions as any)
context.setAppState(prev => ({
...prev,
toolPermissionContext: applyPermissionUpdates(
prev.toolPermissionContext,
decision.updatedPermissions!,
decision.updatedPermissions as any,
),
}))
}

View File

@@ -251,12 +251,12 @@ export async function processUserInput({
...hookResult.message,
attachment: {
...hookResult.message.attachment,
content: applyTruncation(hookResult.message.attachment.content),
content: applyTruncation(hookResult.message.attachment.content as string),
},
})
} as AttachmentMessage)
break
default:
result.messages.push(hookResult.message)
result.messages.push(hookResult.message as AttachmentMessage)
break
}
}

View File

@@ -125,7 +125,7 @@ ${question}`
function extractSideQuestionResponse(messages: Message[]): string | null {
// Flatten all assistant content blocks across the per-block messages.
const assistantBlocks = messages.flatMap(m =>
m.type === 'assistant' ? m.message.content : [],
m.type === 'assistant' ? (m.message.content as unknown as Array<{ type: string; [key: string]: unknown }>) : [],
)
if (assistantBlocks.length > 0) {
@@ -136,7 +136,7 @@ function extractSideQuestionResponse(messages: Message[]): string | null {
// No text — check if the model tried to call a tool despite instructions.
const toolUse = assistantBlocks.find(b => b.type === 'tool_use')
if (toolUse) {
const toolName = 'name' in toolUse ? toolUse.name : 'a tool'
const toolName = 'name' in toolUse ? (toolUse as any).name : 'a tool'
return `(The model tried to call ${toolName} instead of answering directly. Try rephrasing or ask in the main conversation.)`
}
}
@@ -148,7 +148,7 @@ function extractSideQuestionResponse(messages: Message[]): string | null {
m.type === 'system' && 'subtype' in m && m.subtype === 'api_error',
)
if (apiErr) {
return `(API error: ${formatAPIError(apiErr.error)})`
return `(API error: ${formatAPIError(apiErr.error as any)})`
}
return null

View File

@@ -43,7 +43,7 @@ export default function sliceAnsi(
// pass start/end in display cells (via stringWidth), so position must
// track the same units.
const width =
token.type === 'ansi' ? 0 : token.fullWidth ? 2 : stringWidth(token.value)
token.type === 'ansi' ? 0 : token.type === 'char' ? (token.fullWidth ? 2 : stringWidth(token.value)) : 0
// Break AFTER trailing zero-width marks — a combining mark attaches to
// the preceding base char, so "भा" (भ + ा, 1 display cell) sliced at
@@ -77,7 +77,7 @@ export default function sliceAnsi(
}
if (include) {
result += token.value
result += (token as any).value
}
position += width

View File

@@ -231,16 +231,17 @@ export async function createAndUploadGitBundle(
)
if (!bundle.ok) {
logForDebugging(`[gitBundle] ${bundle.error}`)
const failedBundle = bundle as { ok: false; error: string; failReason: BundleFailReason }
logForDebugging(`[gitBundle] ${failedBundle.error}`)
logEvent('tengu_ccr_bundle_upload', {
outcome:
bundle.failReason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
failedBundle.failReason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
max_bytes: maxBytes,
})
return {
success: false,
error: bundle.error,
failReason: bundle.failReason,
error: failedBundle.error,
failReason: failedBundle.failReason,
}
}
@@ -254,7 +255,7 @@ export async function createAndUploadGitBundle(
outcome:
'failed' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
})
return { success: false, error: upload.error }
return { success: false, error: (upload as { success: false; error: string }).error }
}
logForDebugging(

View File

@@ -551,7 +551,7 @@ export function extractDiscoveredToolNames(messages: Message[]): Set<string> {
// check rather than isCompactBoundaryMessage — utils/messages.ts imports
// from this file, so importing back would be circular.
if (msg.type === 'system' && msg.subtype === 'compact_boundary') {
const carried = msg.compactMetadata?.preCompactDiscoveredTools
const carried = (msg as any).compactMetadata?.preCompactDiscoveredTools as string[] | undefined
if (carried) {
for (const name of carried) discoveredTools.add(name)
carriedFromBoundary += carried.length
@@ -658,8 +658,8 @@ export function getDeferredToolsDelta(
attachmentTypesSeen.add(msg.attachment.type)
if (msg.attachment.type !== 'deferred_tools_delta') continue
dtdCount++
for (const n of msg.attachment.addedNames) announced.add(n)
for (const n of msg.attachment.removedNames) announced.delete(n)
for (const n of (msg.attachment as any).addedNames) announced.add(n)
for (const n of (msg.attachment as any).removedNames) announced.delete(n)
}
const deferred: Tool[] = tools.filter(isDeferredTool)