mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-19 13:33:35 +08:00
Desktop @ file search was recursively walking the filesystem with a local skip list, which let Python and Node generated directories leak into results and diverged from CLI behavior. Route candidate discovery through the same git-first model: tracked files, untracked files with exclude-standard, and ripgrep fallback for non-git folders, then derive selectable directories from those candidates. Constraint: Desktop picker must select both files and directories without surfacing ignored project artifacts. Constraint: No new dependencies; reuse the existing git, ripgrep, settings, and ignore utilities. Rejected: Maintain a hardcoded directory denylist | it would drift from CLI and miss project-specific ignore rules. Rejected: Full recursive readdir scanning | it ignores git index semantics and makes large dependency trees visible. Confidence: high Scope-risk: moderate Directive: Keep desktop @ file candidate discovery aligned with src/hooks/fileSuggestions.ts before changing ranking or ignore behavior. Tested: bun test src/server/__tests__/filesystem.test.ts Tested: cd desktop && bun run test -- FileSearchMenu.test.tsx ChatInput.test.tsx Tested: cd desktop && bun run lint && bun run test -- --run && bun run build Tested: bun run check:server Tested: bun run check:coverage Tested: bun run verify Not-tested: Native Windows/Linux manual UI smoke; path handling relies on cross-platform git/ripgrep wrappers and normalized relative paths.
151 lines
4.5 KiB
TypeScript
151 lines
4.5 KiB
TypeScript
/**
|
|
* WebSocket event type definitions
|
|
*
|
|
* 定义客户端与服务器之间 WebSocket 通信的消息类型。
|
|
*/
|
|
|
|
// ============================================================================
|
|
// Client → Server
|
|
// ============================================================================
|
|
|
|
export type ClientMessage =
|
|
| { type: 'prewarm_session' }
|
|
| { type: 'user_message'; content: string; attachments?: AttachmentRef[] }
|
|
| {
|
|
type: 'permission_response'
|
|
requestId: string
|
|
allowed: boolean
|
|
rule?: string
|
|
updatedInput?: Record<string, unknown>
|
|
}
|
|
| {
|
|
type: 'computer_use_permission_response'
|
|
requestId: string
|
|
response: ComputerUsePermissionResponse
|
|
}
|
|
| { type: 'set_permission_mode'; mode: string }
|
|
| { type: 'set_runtime_config'; providerId: string | null; modelId: string }
|
|
| { type: 'stop_generation' }
|
|
| { type: 'ping' }
|
|
|
|
export type AttachmentRef = {
|
|
type: 'file' | 'image'
|
|
name?: string
|
|
path?: string
|
|
data?: string // base64 for images
|
|
mimeType?: string
|
|
isDirectory?: boolean
|
|
}
|
|
|
|
// ============================================================================
|
|
// Server → Client
|
|
// ============================================================================
|
|
|
|
export type ServerMessage =
|
|
| { type: 'connected'; sessionId: string }
|
|
| { type: 'content_start'; blockType: 'text' | 'tool_use'; toolName?: string; toolUseId?: string; parentToolUseId?: string }
|
|
| { type: 'content_delta'; text?: string; toolInput?: string }
|
|
| { type: 'tool_use_complete'; toolName: string; toolUseId: string; input: unknown; parentToolUseId?: string }
|
|
| { type: 'tool_result'; toolUseId: string; content: unknown; isError: boolean; parentToolUseId?: string }
|
|
| {
|
|
type: 'permission_request'
|
|
requestId: string
|
|
toolName: string
|
|
toolUseId?: string
|
|
input: unknown
|
|
description?: string
|
|
}
|
|
| {
|
|
type: 'computer_use_permission_request'
|
|
requestId: string
|
|
request: ComputerUsePermissionRequest
|
|
}
|
|
| { type: 'message_complete'; usage: TokenUsage }
|
|
| { type: 'thinking'; text: string }
|
|
| { type: 'status'; state: ChatState; verb?: string; elapsed?: number; tokens?: number }
|
|
| { type: 'error'; message: string; code: string; retryable?: boolean }
|
|
| { type: 'system_notification'; subtype: string; message?: string; data?: unknown }
|
|
| { type: 'pong' }
|
|
| { type: 'team_update'; teamName: string; members: TeamMemberStatus[] }
|
|
| { type: 'team_created'; teamName: string }
|
|
| { type: 'team_deleted'; teamName: string }
|
|
| { type: 'task_update'; taskId: string; status: string; progress?: string }
|
|
| { type: 'session_title_updated'; sessionId: string; title: string }
|
|
|
|
export type TokenUsage = {
|
|
input_tokens: number
|
|
output_tokens: number
|
|
cache_read_tokens?: number
|
|
cache_creation_tokens?: number
|
|
}
|
|
|
|
export type ChatState = 'idle' | 'thinking' | 'tool_executing' | 'streaming' | 'permission_pending'
|
|
|
|
export type TeamMemberStatus = {
|
|
agentId: string
|
|
role: string
|
|
status: 'running' | 'idle' | 'completed' | 'error'
|
|
currentTask?: string
|
|
}
|
|
|
|
export type ComputerUseGrantFlags = {
|
|
clipboardRead: boolean
|
|
clipboardWrite: boolean
|
|
systemKeyCombos: boolean
|
|
}
|
|
|
|
export type ComputerUseResolvedApp = {
|
|
bundleId: string
|
|
displayName: string
|
|
path?: string
|
|
iconDataUrl?: string
|
|
}
|
|
|
|
export type ComputerUseResolvedAppRequest = {
|
|
requestedName: string
|
|
resolved?: ComputerUseResolvedApp
|
|
isSentinel: boolean
|
|
alreadyGranted: boolean
|
|
proposedTier: 'read' | 'click' | 'full'
|
|
}
|
|
|
|
export type ComputerUsePermissionRequest = {
|
|
requestId: string
|
|
reason: string
|
|
apps: ComputerUseResolvedAppRequest[]
|
|
requestedFlags: Partial<ComputerUseGrantFlags>
|
|
screenshotFiltering: 'native' | 'none'
|
|
tccState?: {
|
|
accessibility: boolean
|
|
screenRecording: boolean
|
|
}
|
|
willHide?: Array<{ bundleId: string; displayName: string }>
|
|
autoUnhideEnabled?: boolean
|
|
}
|
|
|
|
export type ComputerUsePermissionResponse = {
|
|
granted: Array<{
|
|
bundleId: string
|
|
displayName: string
|
|
grantedAt: number
|
|
tier?: 'read' | 'click' | 'full'
|
|
}>
|
|
denied: Array<{
|
|
bundleId: string
|
|
reason: 'user_denied' | 'not_installed'
|
|
}>
|
|
flags: ComputerUseGrantFlags
|
|
userConsented?: boolean
|
|
}
|
|
|
|
// ============================================================================
|
|
// Internal types
|
|
// ============================================================================
|
|
|
|
export type WebSocketSession = {
|
|
sessionId: string
|
|
connectedAt: number
|
|
abortController?: AbortController
|
|
isGenerating: boolean
|
|
}
|