agentdev 0.1.9 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BasicAgent-7TNMYC3X.js +13 -0
- package/dist/ExplorerAgent-L3ZTVQGM.js +13 -0
- package/dist/{chunk-QFHPUAUQ.js → chunk-35LBACUK.js} +8 -8
- package/dist/{chunk-QFHPUAUQ.js.map → chunk-35LBACUK.js.map} +1 -1
- package/dist/{chunk-NORTAQIL.js → chunk-4WK7UENZ.js} +1011 -11
- package/dist/chunk-4WK7UENZ.js.map +1 -0
- package/dist/{chunk-BAP2GCYH.js → chunk-7GTVQ55R.js} +1 -1
- package/dist/chunk-7GTVQ55R.js.map +1 -0
- package/dist/{chunk-G5ECPY4K.js → chunk-EK6KGS2M.js} +87 -8
- package/dist/{chunk-G5ECPY4K.js.map → chunk-EK6KGS2M.js.map} +1 -1
- package/dist/{chunk-5T4C2XRT.js → chunk-KE3KYZVJ.js} +21 -8
- package/dist/chunk-KE3KYZVJ.js.map +1 -0
- package/dist/{chunk-EECW6PYP.js → chunk-UL2ZBPBL.js} +60 -4
- package/dist/chunk-UL2ZBPBL.js.map +1 -0
- package/dist/{chunk-A354ZCZF.js → chunk-XRB6MD2J.js} +5593 -982
- package/dist/chunk-XRB6MD2J.js.map +1 -0
- package/dist/cli/server.js +2 -2
- package/dist/cli/viewer.js +2 -2
- package/dist/create-feature-cli.js +26 -7
- package/dist/features/mcp/templates/mcp-tool.render.js +14 -1
- package/dist/features/mcp/templates/mcp-tool.render.js.map +1 -1
- package/dist/features/shell/templates/bash.render.d.ts +1 -1
- package/dist/features/websearch/templates/web-fetch.render.d.ts +1 -1
- package/dist/index.d.ts +778 -21
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/{notification-NWVOS2WR.js → notification-QPH37BHW.js} +29 -6
- package/dist/notification-QPH37BHW.js.map +1 -0
- package/dist/{tools-LDR3LIJP.js → tools-OKH7SPMP.js} +2 -2
- package/dist/{types-CF5UsxD9.d.ts → types-NVwNUVFR.d.ts} +180 -14
- package/package.json +7 -14
- package/dist/BasicAgent-UWXLSZP2.js +0 -13
- package/dist/ExplorerAgent-LCM3JQS4.js +0 -13
- package/dist/chunk-5T4C2XRT.js.map +0 -1
- package/dist/chunk-A354ZCZF.js.map +0 -1
- package/dist/chunk-BAP2GCYH.js.map +0 -1
- package/dist/chunk-EECW6PYP.js.map +0 -1
- package/dist/chunk-NORTAQIL.js.map +0 -1
- package/dist/notification-NWVOS2WR.js.map +0 -1
- /package/dist/{BasicAgent-UWXLSZP2.js.map → BasicAgent-7TNMYC3X.js.map} +0 -0
- /package/dist/{ExplorerAgent-LCM3JQS4.js.map → ExplorerAgent-L3ZTVQGM.js.map} +0 -0
- /package/dist/{tools-LDR3LIJP.js.map → tools-OKH7SPMP.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/types.ts"],"sourcesContent":["/**\r\n * 基础类型定义\r\n * 所有类型集中在这里,简单直观\r\n */\r\n\r\n// ========== 通知系统类型 ==========\r\n\r\n/**\r\n * 通知分类\r\n * - state: 覆盖式更新(如 LLM 字符计数)\r\n * - event: 追加式记录(如工具开始/完成)\r\n */\r\nexport type NotificationCategory = 'state' | 'event';\r\n\r\n/**\r\n * LLM 生成阶段\r\n */\r\nexport type LLMPhase = 'thinking' | 'content' | 'tool_calling';\r\n\r\n/**\r\n * 通知基础接口\r\n */\r\nexport interface Notification {\r\n type: string;\r\n category: NotificationCategory;\r\n timestamp: number;\r\n data: unknown;\r\n}\r\n\r\nexport type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';\r\n\r\nexport type DebugLogDeliveryReason =\r\n | 'hub'\r\n | 'hub-unavailable'\r\n | 'no-agent-context';\r\n\r\nexport interface DebugLogDelivery {\r\n hub: boolean;\r\n console: boolean;\r\n reason: DebugLogDeliveryReason;\r\n}\r\n\r\nexport interface LogContextRef {\r\n agentId?: string;\r\n agentName?: string;\r\n parentAgentId?: string;\r\n callIndex?: number;\r\n step?: number;\r\n toolName?: string;\r\n toolCallId?: string;\r\n feature?: string;\r\n lifecycle?: string;\r\n hookMethod?: string;\r\n hookKind?: 'forward' | 'reverse';\r\n sourceFile?: string;\r\n sourceLine?: number;\r\n tags?: string[];\r\n [key: string]: unknown;\r\n}\r\n\r\nexport interface DebugLogEntry {\r\n id: string;\r\n timestamp: number;\r\n level: LogLevel;\r\n message: string;\r\n namespace: string;\r\n context: LogContextRef;\r\n data?: unknown;\r\n delivery: DebugLogDelivery;\r\n}\r\n\r\n/**\r\n * LLM 字符计数通知数据\r\n */\r\nexport interface LLMCharCountData {\r\n charCount: number;\r\n phase: LLMPhase;\r\n thinkingChars?: number;\r\n contentChars?: number;\r\n toolCallCount?: number;\r\n}\r\n\r\n/**\r\n * LLM 完成通知数据\r\n */\r\nexport interface LLMCompleteData {\r\n totalChars: number;\r\n}\r\n\r\nexport interface ToolStartData {\r\n toolName: string;\r\n}\r\n\r\nexport type RuntimeStage =\r\n | 'idle'\r\n | 'llm_thinking'\r\n | 'llm_content'\r\n | 'llm_tool_call_building'\r\n | 'awaiting_runtime'\r\n | 'tool_executing'\r\n | 'retry_waiting'\r\n | 'retry_requesting'\r\n | 'completed'\r\n | 'failed';\r\n\r\nexport interface AgentRuntimeSnapshot {\r\n stage: RuntimeStage;\r\n callActive: boolean;\r\n charCount: number;\r\n thinkingChars: number;\r\n contentChars: number;\r\n toolCallCount: number;\r\n activeToolNames: string[];\r\n activeToolCount: number;\r\n callStartedAt?: number;\r\n stageStartedAt?: number;\r\n retryAttempt?: number;\r\n maxRetries?: number;\r\n nextRetryDelayMs?: number;\r\n lastErrorType?: string | null;\r\n lastErrorMessage?: string | null;\r\n updatedAt: number;\r\n}\r\n\r\n/**\r\n * 工具定义\r\n */\r\nexport interface Tool {\r\n name: string;\r\n description: string;\r\n parameters?: Record<string, any>;\r\n /**\r\n * 执行工具\r\n *\r\n * @param args 工具参数\r\n * @param context 执行上下文,可能包含:\r\n * - signal?: AbortSignal - 用于中断工具执行\r\n * - ...其他上下文信息\r\n */\r\n execute: (args: any, context?: any) => Promise<any>;\r\n /** 可选:渲染配置 */\r\n render?: ToolRenderConfig;\r\n /**\r\n * 工具执行模式\r\n * - 'normal'(默认):普通工具,可与其他工具在同一次 assistant turn 中并行调用\r\n * - 'exclusive':独占工具,必须是 assistant turn 中唯一的工具调用\r\n *\r\n * exclusive 工具适用于控制流工具(如 checkpoint、rollback),\r\n * 它们不应与其他工具产生副作用交织。\r\n */\r\n executionMode?: 'normal' | 'exclusive';\r\n /**\r\n * 工具是否可并行执行。\r\n *\r\n * - true: 该工具可以与同批次中其他 parallelizable 工具并发执行\r\n * - false/undefined: 串行执行(默认,向后兼容)\r\n *\r\n * 约束:\r\n * - exclusive 工具忽略此属性(exclusive 总是独占批次)\r\n * - 标记为 parallelizable 的工具应是无副作用的只读操作,\r\n * 或其副作用不会与同批次其他工具冲突\r\n */\r\n parallelizable?: boolean;\r\n}\r\n\r\n/**\r\n * 工具完成通知数据\r\n */\r\nexport interface ToolCompleteData {\r\n toolName: string;\r\n success: boolean;\r\n duration: number;\r\n}\r\n\r\n/**\r\n * 通知状态响应(GET /api/agents/:id/notification)\r\n */\r\nexport interface NotificationStateResponse {\r\n state: Notification | null;\r\n event: Notification | null;\r\n runtime: AgentRuntimeSnapshot;\r\n callActive: boolean;\r\n hasNewEvents: boolean;\r\n}\r\n\r\nexport interface AgentLogsResponse {\r\n scope: 'current' | 'all';\r\n currentAgentId: string | null;\r\n selectedAgentId: string | null;\r\n total: number;\r\n logs: DebugLogEntry[];\r\n truncation?: {\r\n truncated: boolean;\r\n appliedLimit?: number;\r\n returnedCount: number;\r\n availableCount: number;\r\n nextOffset?: number;\r\n reason?: string;\r\n guidance?: string;\r\n };\r\n collectionPolicy: {\r\n hubConnected: boolean;\r\n includesOnlyHubDeliveredLogs: boolean;\r\n fallbackBehavior: string;\r\n };\r\n}\r\n\r\n/**\r\n * Agent 连接状态响应(GET /api/agents/:id/connection)\r\n */\r\nexport interface AgentConnectionResponse {\r\n connected: boolean;\r\n}\r\n\r\n// ========== 消息类型 ==========\r\n\r\n// 消息角色(支持子代理 ID 作为消息来源)\r\nexport type MessageRole = 'system' | 'user' | 'assistant' | 'tool' | string;\r\n\r\n// 消息结构\r\nexport interface Message {\r\n role: MessageRole;\r\n content: string;\r\n turn?: number;\r\n toolCallId?: string;\r\n toolCalls?: ToolCall[];\r\n reasoning?: string; // 思考内容(GLM-4.7等模型的扩展字段)\r\n thinkingBlocks?: ThinkingBlock[];\r\n /**\r\n * LLM 用量信息(仅 assistant 消息有值)。\r\n *\r\n * 由 LLM provider 在生成响应时返回,表示生成此消息时的上下文 token 开销。\r\n * inputTokens 是发送给 LLM 的完整上下文大小(包含所有历史消息),\r\n * 不是单条消息的 token 数。\r\n */\r\n usage?: MessageUsage;\r\n}\r\n\r\n/**\r\n * 消息级用量记录(盖戳在 assistant 消息上)\r\n */\r\nexport interface MessageUsage {\r\n /** 生成此消息时,发送给 LLM 的总输入 token(即当时的完整上下文大小) */\r\n inputTokens: number;\r\n /** LLM 生成的输出 token */\r\n outputTokens: number;\r\n}\r\n\r\nexport interface ThinkingBlock {\r\n signature: string;\r\n thinking: string;\r\n}\r\n\r\n// 工具调用\r\nexport interface ToolCall {\r\n id: string;\r\n name: string;\r\n arguments: Record<string, any>;\r\n}\r\n\r\n/**\r\n * 统一用量格式(兼容 Anthropic 和 OpenAI)\r\n */\r\nexport interface UsageInfo {\r\n /** 输入 token 数 */\r\n inputTokens: number;\r\n /** 输出 token 数 */\r\n outputTokens: number;\r\n /** 总 token 数 */\r\n totalTokens: number;\r\n\r\n // ========== Anthropic 特有(可选)==========\r\n /** 创建缓存消耗的 token 数 */\r\n cacheCreationTokens?: number;\r\n /** 从缓存读取的 token 数 */\r\n cacheReadTokens?: number;\r\n\r\n // ========== OpenAI 特有(可选)==========\r\n /** 推理 token 数 */\r\n reasoningTokens?: number;\r\n /** 音频 token 数 */\r\n audioTokens?: number;\r\n}\r\n\r\n// LLM 响应\r\nexport interface LLMResponse {\r\n content: string;\r\n toolCalls?: ToolCall[];\r\n reasoning?: string; // 思考内容(GLM-4.7等模型的扩展字段)\r\n thinkingBlocks?: ThinkingBlock[];\r\n /** 用量统计(可选) */\r\n usage?: UsageInfo;\r\n /** 停止原因,由 LLM API 返回(如 end_turn, tool_use, stop 等) */\r\n stopReason?: string | null;\r\n}\r\n\r\n// ============= 渲染模板类型 =============\r\n/**\r\n * 渲染模板项\r\n * 可以是字符串模板或函数模板\r\n */\r\nexport type RenderTemplateItem =\r\n | string // 字符串模板,使用 {{key}} 插值\r\n | RenderTemplateFn; // 函数模板,处理复杂逻辑\r\n\r\n/**\r\n * 渲染模板函数类型\r\n */\r\nexport type RenderTemplateFn = (data: Record<string, any>, success?: boolean) => string;\r\n\r\n/**\r\n * 内联渲染模板\r\n * 直接定义在工具中的渲染模板(无需引用预设模板)\r\n */\r\nexport interface InlineRenderTemplate {\r\n call: RenderTemplateItem;\r\n result: RenderTemplateItem;\r\n}\r\n\r\n// 工具渲染配置\r\nexport interface ToolRenderConfig {\r\n /** 调用时的渲染模板(字符串引用或内联模板) */\r\n call?: string | InlineRenderTemplate;\r\n /** 结果时的渲染模板(字符串引用或内联模板) */\r\n result?: string | InlineRenderTemplate;\r\n}\r\n\r\n// LLM 接口 - 所有 LLM 适配器都需要实现这个\r\nexport interface LLMClient {\r\n chat(messages: Message[], tools: Tool[], options?: LLMChatOptions): Promise<LLMResponse>;\r\n /** 可选:返回当前 LLM 实例使用的模型名(用于调试显示) */\r\n readonly modelName?: string;\r\n}\r\n\r\n// LLM 调用选项\r\nexport interface LLMChatOptions {\r\n /** 允许中断正在进行的 LLM 调用 */\r\n signal?: AbortSignal;\r\n}\r\n\r\n// 占位符上下文类型\r\nimport type { PlaceholderContext, TemplateSource } from '../template/types.js';\r\n\r\n// MCP 类型导入\r\nimport type { MCPConfig } from '../mcp/types.js';\r\nimport type { UsageStatsSnapshot } from './usage.js';\r\n\r\n// Agent 配置\r\nexport interface AgentConfig {\r\n llm: LLMClient;\r\n tools?: Tool[];\r\n maxTurns?: number;\r\n systemMessage?: string | TemplateSource;\r\n name?: string; // Agent 显示名称(用于调试)\r\n projectRoot?: string;\r\n workspaceDir?: string;\r\n\r\n // ========== Feature 系统 ==========\r\n /**\r\n * Feature 配置\r\n *\r\n * 新的声明式 Feature 注册方式\r\n */\r\n features?: {\r\n /** 启用的 Feature 列表 */\r\n enabled?: string[];\r\n /** Feature 特定配置 */\r\n [key: string]: unknown;\r\n };\r\n}\r\n\r\n// 上下文中间件 - 用于处理消息数组\r\nexport type ContextMiddleware = (messages: Message[]) => Message[];\r\n\r\n// ============= 多 Agent 调试支持 =============\r\n\r\n/**\r\n * Agent 注册信息(Hub 端)\r\n */\r\nexport interface AgentInfo {\r\n id: string; // 唯一标识,如 \"agent-1\"\r\n name: string; // 显示名称\r\n registeredAt: number; // 注册时间戳\r\n projectRoot?: string;\r\n}\r\n\r\n/**\r\n * 工具元数据(用于前端渲染)\r\n */\r\nexport interface ToolMetadata {\r\n name: string;\r\n description: string;\r\n render: {\r\n call: string | InlineRenderTemplate; // 模板名称或内联模板\r\n result: string | InlineRenderTemplate; // 模板名称或内联模板\r\n // 内联模板的可选直接存储(用于前端特殊标记)\r\n inlineCall?: InlineRenderTemplate;\r\n inlineResult?: InlineRenderTemplate;\r\n };\r\n}\r\n\r\nexport interface HookSourceLocation {\r\n file?: string;\r\n line?: number;\r\n column?: number;\r\n display: string;\r\n}\r\n\r\nexport interface HookEntryMetadata {\r\n order: number;\r\n featureName: string;\r\n methodName: string;\r\n lifecycle: string;\r\n kind: 'decision' | 'notify';\r\n source?: HookSourceLocation;\r\n description?: string;\r\n}\r\n\r\nexport interface HookLifecycleSnapshot {\r\n lifecycle: string;\r\n kind: 'decision' | 'notify';\r\n entries: HookEntryMetadata[];\r\n}\r\n\r\nexport interface FeatureInspectorSnapshot {\r\n name: string;\r\n enabled: boolean;\r\n status: 'enabled' | 'disabled' | 'removed' | 'partial';\r\n hookCount: number;\r\n toolCount: number;\r\n enabledToolCount: number;\r\n source?: string;\r\n description?: string;\r\n tools: Array<{\r\n name: string;\r\n description: string;\r\n state: 'enabled' | 'disabled' | 'removed' | 'superseded';\r\n enabled?: boolean;\r\n renderCall?: string;\r\n renderResult?: string;\r\n }>;\r\n}\r\n\r\nexport interface HookInspectorSnapshot {\r\n lifecycleOrder: string[];\r\n features: FeatureInspectorSnapshot[];\r\n hooks: HookLifecycleSnapshot[];\r\n standaloneTools?: Array<{\r\n name: string;\r\n description: string;\r\n state: 'enabled' | 'disabled' | 'removed' | 'superseded';\r\n enabled?: boolean;\r\n source?: string;\r\n renderCall?: string;\r\n renderResult?: string;\r\n }>;\r\n}\r\n\r\nexport interface AgentContextMetrics {\r\n messageCount: number;\r\n charCount: number;\r\n toolCallCount: number;\r\n turnCount: number;\r\n}\r\n\r\nexport interface AgentOverviewSnapshot {\r\n updatedAt: number;\r\n context: AgentContextMetrics;\r\n usageStats: UsageStatsSnapshot;\r\n runtime?: AgentRuntimeSnapshot;\r\n /** 可选:当前使用的模型名(由 agent 实例注入) */\r\n modelName?: string;\r\n}\r\n\r\n/**\r\n * Agent 会话数据(Worker 端)\r\n */\r\nexport interface AgentSession {\r\n id: string;\r\n name: string;\r\n messages: Message[];\r\n tools: ToolMetadata[];\r\n createdAt: number;\r\n lastActive: number;\r\n // 项目根目录(用于定位模板文件)\r\n projectRoot?: string;\r\n // 通知系统扩展\r\n currentState: Notification | null;\r\n // call 运行状态(独立于 currentState,不受 state 覆盖影响)\r\n callActive?: boolean;\r\n runtimeState?: AgentRuntimeSnapshot;\r\n events: Notification[];\r\n lastEventCount: number;\r\n logs: DebugLogEntry[];\r\n // 所属 UDS 客户端连接 ID(用于多进程输入响应路由)\r\n clientId?: string;\r\n // 内部:上次最后一条消息的签名(用于推送去重)\r\n _lastMessageSig?: string;\r\n hookInspector?: HookInspectorSnapshot;\r\n overview?: AgentOverviewSnapshot;\r\n // 运行期间排队等待的用户输入(用于输入框常驻 + 队列注入)\r\n queuedInputs: QueuedInput[];\r\n}\r\n\r\n/**\r\n * 排队的用户输入\r\n */\r\nexport interface QueuedInput {\r\n id: string;\r\n text: string;\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * DebugHub IPC 消息类型(主进程 → Worker)\r\n * 使用 discriminated union 确保类型安全\r\n */\r\nexport type DebugHubIPCMessage =\r\n | RegisterAgentMsg\r\n | UpdateAgentInspectorMsg\r\n | UpdateAgentOverviewMsg\r\n | PushMessagesMsg\r\n | RegisterToolsMsg\r\n | SetCurrentAgentMsg\r\n | UnregisterAgentMsg\r\n | PushNotificationMsg\r\n | RequestInputMsg\r\n | QueueInputMsg\r\n | ConsumeQueuedInputMsg\r\n | InterruptAgentMsg\r\n | StopMsg;\r\n\r\n/**\r\n * 注册新 Agent\r\n */\r\nexport interface RegisterAgentMsg {\r\n type: 'register-agent';\r\n agentId: string;\r\n name: string;\r\n createdAt: number;\r\n projectRoot?: string; // 项目根目录,用于模板文件加载\r\n featureTemplates?: Record<string, string>; // Feature 模板路径映射\r\n hookInspector?: HookInspectorSnapshot;\r\n overview?: AgentOverviewSnapshot;\r\n activeInputRequest?: ActiveInputRequest; // 活跃的输入请求(用于重连后恢复)\r\n}\r\n\r\nexport interface UpdateAgentInspectorMsg {\r\n type: 'update-agent-inspector';\r\n agentId: string;\r\n hookInspector: HookInspectorSnapshot;\r\n}\r\n\r\nexport interface UpdateAgentOverviewMsg {\r\n type: 'update-agent-overview';\r\n agentId: string;\r\n overview: AgentOverviewSnapshot;\r\n}\r\n\r\n/**\r\n * 推送 Agent 消息\r\n */\r\nexport interface PushMessagesMsg {\r\n type: 'push-messages';\r\n agentId: string;\r\n messages: Message[];\r\n}\r\n\r\n/**\r\n * 注册 Agent 工具\r\n */\r\nexport interface RegisterToolsMsg {\r\n type: 'register-tools';\r\n agentId: string;\r\n tools: Tool[];\r\n}\r\n\r\n/**\r\n * 切换当前选中的 Agent\r\n */\r\nexport interface SetCurrentAgentMsg {\r\n type: 'set-current-agent';\r\n agentId: string;\r\n}\r\n\r\n/**\r\n * 活跃的输入请求(用于重连后恢复)\r\n */\r\nexport interface ActiveInputRequest {\r\n requestId: string;\r\n prompt: string;\r\n placeholder?: string;\r\n initialValue?: string;\r\n actions?: UserInputAction[];\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * 注销 Agent\r\n */\r\nexport interface UnregisterAgentMsg {\r\n type: 'unregister-agent';\r\n agentId: string;\r\n}\r\n\r\n/**\r\n * 停止 Worker\r\n */\r\nexport interface StopMsg {\r\n type: 'stop';\r\n}\r\n\r\n/**\r\n * 推送通知\r\n */\r\nexport interface PushNotificationMsg {\r\n type: 'push-notification';\r\n agentId: string;\r\n notification: Notification;\r\n}\r\n\r\n/**\r\n * 请求用户输入\r\n */\r\nexport interface RequestInputMsg {\r\n type: 'request-input';\r\n agentId: string;\r\n requestId: string;\r\n prompt: string;\r\n timeout?: number;\r\n placeholder?: string;\r\n initialValue?: string;\r\n actions?: UserInputAction[];\r\n mode?: UserInputRequestMode;\r\n questions?: UserInputQuestion[];\r\n}\r\n\r\n/**\r\n * 排队用户输入(运行期间提交的消息)\r\n */\r\nexport interface QueueInputMsg {\r\n type: 'queue-input';\r\n agentId: string;\r\n input: QueuedInput;\r\n}\r\n\r\n/**\r\n * 通知 Worker 移除一条已被运行时接管/开始处理的排队输入\r\n */\r\nexport interface ConsumeQueuedInputMsg {\r\n type: 'consume-queued-input';\r\n agentId: string;\r\n inputId: string;\r\n}\r\n\r\n/**\r\n * 中断正在运行的 Agent\r\n */\r\nexport interface InterruptAgentMsg {\r\n type: 'interrupt-agent';\r\n agentId: string;\r\n clearQueue?: boolean;\r\n}\r\n\r\n/**\r\n * 用户输入响应(Worker → Agent,通过 UDS)\r\n */\r\nexport interface InputResponseMsg {\r\n type: 'input-response';\r\n agentId: string;\r\n requestId: string;\r\n input: string;\r\n response?: UserInputResponse;\r\n}\r\n\r\nexport interface UserInputAction {\r\n id: string;\r\n label: string;\r\n kind?: 'rollback' | 'custom';\r\n variant?: 'primary' | 'secondary' | 'danger';\r\n payload?: Record<string, unknown>;\r\n}\r\n\r\nexport type UserInputRequestMode = 'text' | 'choices';\r\n\r\nexport interface UserInputOption {\r\n id: string;\r\n label: string;\r\n description?: string;\r\n /** Whether this option allows supplementary free-text input */\r\n allowSupplement?: boolean;\r\n /** Whether the supplement text is required (only meaningful when allowSupplement is true) */\r\n supplementRequired?: boolean;\r\n /** Label shown above the supplement textarea */\r\n supplementLabel?: string;\r\n /** Placeholder for the supplement textarea */\r\n supplementPlaceholder?: string;\r\n}\r\n\r\nexport interface UserInputQuestion {\r\n id: string;\r\n question: string;\r\n options: UserInputOption[];\r\n allowCustom?: boolean;\r\n customLabel?: string;\r\n customPlaceholder?: string;\r\n}\r\n\r\nexport interface UserInputRequest {\r\n prompt: string;\r\n placeholder?: string;\r\n initialValue?: string;\r\n actions?: UserInputAction[];\r\n mode?: UserInputRequestMode;\r\n questions?: UserInputQuestion[];\r\n}\r\n\r\nexport interface UserInputChoiceAnswer {\r\n questionId: string;\r\n optionId?: string;\r\n customText?: string;\r\n /** Supplementary free-text provided alongside the selected option */\r\n supplementText?: string;\r\n}\r\n\r\nexport interface UserInputResponse {\r\n kind: 'text' | 'action' | 'choices';\r\n text?: string;\r\n actionId?: string;\r\n choices?: UserInputChoiceAnswer[];\r\n payload?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Worker → 主进程 消息\r\n */\r\nexport type WorkerIPCMessage =\r\n | ReadyMsg\r\n | AgentSwitchedMsg\r\n | InputResponseMsg;\r\n\r\n/**\r\n * Worker 就绪\r\n */\r\nexport interface ReadyMsg {\r\n type: 'ready';\r\n}\r\n\r\n/**\r\n * Agent 切换确认\r\n */\r\nexport interface AgentSwitchedMsg {\r\n type: 'agent-switched';\r\n agentId: string;\r\n}\r\n\r\n// ========== 上下文管理类型 ==========\r\n/**\r\n * 消息标签枚举\r\n *\r\n * 用于快速分类和过滤消息,一条消息可能有多个标签\r\n */\r\nexport type MessageTag =\r\n | 'user' // 用户输入消息\r\n | 'system' // 系统消息\r\n | 'assistant' // LLM 响应消息\r\n | 'tool-call' // assistant 消息且包含 toolCalls\r\n | 'tool-result' // role === 'tool' 的工具执行结果\r\n | 'sub-agent' // 来自子代理的消息(与 assistant/tool-result 组合使用)\r\n | 'reminder'; // Feature 注入的提醒消息(与 system 组合使用)\r\n\r\n/**\r\n * 解析结果结构\r\n *\r\n * 从消息 content 中提取的结构化信息\r\n */\r\nexport interface ParsedContent {\r\n /** 从 content 提取的任务 ID(正则匹配 \"taskId\":\"xxx\") */\r\n taskIds: string[];\r\n /** 从 content 提取的工具调用名称(从 toolCalls 或 content 解析) */\r\n toolCalls: string[];\r\n /** @ 提及的内容 */\r\n mentions: string[];\r\n /** 用户可继承扩展更多字段 */\r\n [key: string]: any;\r\n}\r\n\r\n/**\r\n * 消息元数据\r\n *\r\n * 用于 addMessage() 的元数据参数\r\n */\r\nexport interface MessageMeta {\r\n /** ReAct 循环轮次 */\r\n turn: number;\r\n /** 子代理 ID(子代理消息时填写) */\r\n agentId?: string;\r\n /** 来源 Feature(reminder 等消息时填写) */\r\n source?: string;\r\n}\r\n\r\n/**\r\n * 扩展的消息结构\r\n *\r\n * 在原始 Message 基础上添加元数据\r\n * 不破坏现有 Message 类型,保证 LLM 调用兼容性\r\n */\r\nexport interface EnrichedMessage extends Message {\r\n // === 元数据字段 ===\r\n\r\n /** 唯一标识(用于索引关联) */\r\n id: string;\r\n /** 消息产生时间戳(毫秒) */\r\n timestamp: number;\r\n /** 所属 ReAct 循环轮次(从 0 开始) */\r\n turn: number;\r\n /** 全局消息序号(从 0 开始递增) */\r\n sequence: number;\r\n /** 来源 Agent ID(子代理消息) */\r\n agentId?: string;\r\n /** 来源 Feature(如 'todo-feature',仅 reminder 等) */\r\n source?: string;\r\n\r\n // === 分类标签 ===\r\n\r\n /** 消息分类标签(用于快速查询) */\r\n tags: MessageTag[];\r\n\r\n // === 解析结果 ===\r\n\r\n /** 从 content 中提取的结构化信息 */\r\n parsed: ParsedContent;\r\n}\r\n\r\n// ========== 生命周期类型 re-export ==========\r\n// 生命周期类型从 lifecycle.ts 导出,保持类型定义集中管理\r\nexport type {\r\n AgentInitiateContext,\r\n AgentDestroyContext,\r\n CallStartContext,\r\n CallFinishContext,\r\n CallFinishReason,\r\n StepStartContext,\r\n StepFinishedContext,\r\n HookResult,\r\n ToolContext,\r\n ToolResult,\r\n} from './lifecycle.js';\r\n\r\n// ========== 决策上下文类型 ==========\r\n/**\r\n * 决策上下文(反向钩子参数)\r\n *\r\n * 所有决策上下文的联合类型\r\n */\r\nexport type DecisionContext =\r\n | import('./lifecycle.js').AgentInitiateContext\r\n | import('./lifecycle.js').AgentDestroyContext\r\n | import('./lifecycle.js').CallStartContext\r\n | import('./lifecycle.js').CallFinishContext\r\n | import('./lifecycle.js').StepStartContext\r\n | import('./lifecycle.js').StepFinishedContext\r\n | import('./lifecycle.js').ToolContext\r\n | import('./lifecycle.js').ToolResult\r\n | import('./lifecycle.js').StepFinishDecisionContext\r\n | import('./lifecycle.js').ToolFinishedDecisionContext;\r\n\r\n// ========== UDS 通信类型 ==========\r\n\r\n/**\r\n * UDS 配置\r\n */\r\nexport interface UDSConfig {\r\n /** UDS 路径(默认自动检测平台) */\r\n path?: string;\r\n /** HTTP 端口(Web 界面) */\r\n httpPort?: number;\r\n /** 是否自动打开浏览器 */\r\n openBrowser?: boolean;\r\n}\r\n\r\n/**\r\n * 平台检测后的 UDS 路径\r\n */\r\nexport function getDefaultUDSPath(): string {\r\n if (process.platform === 'win32') {\r\n return '\\\\\\\\.\\\\pipe\\\\agentdev-viewer';\r\n }\r\n return '/tmp/agentdev-viewer.sock';\r\n}\r\n"],"mappings":";AAo3BO,SAAS,oBAA4B;AAC1C,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
|
@@ -29,26 +29,48 @@ function createTool(config, sourceFile) {
|
|
|
29
29
|
description: config.description,
|
|
30
30
|
parameters: config.parameters,
|
|
31
31
|
execute: config.execute,
|
|
32
|
-
render: finalRender
|
|
32
|
+
render: finalRender,
|
|
33
|
+
...config.executionMode ? { executionMode: config.executionMode } : {},
|
|
34
|
+
...config.parallelizable ? { parallelizable: config.parallelizable } : {}
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
var ToolRegistry = class {
|
|
36
38
|
tools = /* @__PURE__ */ new Map();
|
|
37
39
|
enabled = /* @__PURE__ */ new Set();
|
|
38
40
|
// 启用的工具名
|
|
41
|
+
disabled = /* @__PURE__ */ new Set();
|
|
42
|
+
// 禁用(屏蔽)的工具名
|
|
39
43
|
pendingDisabled = /* @__PURE__ */ new Set();
|
|
40
44
|
// 工具注册前的预禁用状态
|
|
45
|
+
pendingRemoved = /* @__PURE__ */ new Set();
|
|
46
|
+
// 工具注册前的预移除状态
|
|
41
47
|
sources = /* @__PURE__ */ new Map();
|
|
42
48
|
// 工具来源追踪
|
|
49
|
+
superseded = /* @__PURE__ */ new Map();
|
|
50
|
+
// 被同名覆盖的旧条目
|
|
43
51
|
/**
|
|
44
52
|
* 注册工具(默认启用,记录来源)
|
|
45
53
|
*/
|
|
46
54
|
register(tool, source) {
|
|
55
|
+
if (this.tools.has(tool.name)) {
|
|
56
|
+
if (!this.superseded.has(tool.name)) {
|
|
57
|
+
this.superseded.set(tool.name, []);
|
|
58
|
+
}
|
|
59
|
+
this.superseded.get(tool.name).push({
|
|
60
|
+
tool: this.tools.get(tool.name),
|
|
61
|
+
source: this.sources.get(tool.name)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
47
64
|
this.tools.set(tool.name, tool);
|
|
48
|
-
if (this.
|
|
65
|
+
if (this.pendingRemoved.has(tool.name)) {
|
|
49
66
|
this.enabled.delete(tool.name);
|
|
67
|
+
this.disabled.delete(tool.name);
|
|
68
|
+
} else if (this.pendingDisabled.has(tool.name)) {
|
|
69
|
+
this.enabled.delete(tool.name);
|
|
70
|
+
this.disabled.add(tool.name);
|
|
50
71
|
} else {
|
|
51
72
|
this.enabled.add(tool.name);
|
|
73
|
+
this.disabled.delete(tool.name);
|
|
52
74
|
}
|
|
53
75
|
if (source) {
|
|
54
76
|
this.sources.set(tool.name, source);
|
|
@@ -56,32 +78,82 @@ var ToolRegistry = class {
|
|
|
56
78
|
return this;
|
|
57
79
|
}
|
|
58
80
|
/**
|
|
59
|
-
*
|
|
81
|
+
* 禁用工具(LLM 可见,但执行时会被拦截)
|
|
60
82
|
*/
|
|
61
83
|
disable(name) {
|
|
62
84
|
this.pendingDisabled.add(name);
|
|
85
|
+
this.pendingRemoved.delete(name);
|
|
63
86
|
if (!this.tools.has(name)) {
|
|
64
87
|
return true;
|
|
65
88
|
}
|
|
66
|
-
|
|
89
|
+
this.enabled.delete(name);
|
|
90
|
+
this.disabled.add(name);
|
|
91
|
+
return true;
|
|
67
92
|
}
|
|
68
93
|
/**
|
|
69
94
|
* 启用工具
|
|
70
95
|
*/
|
|
71
96
|
enable(name) {
|
|
72
97
|
this.pendingDisabled.delete(name);
|
|
98
|
+
this.pendingRemoved.delete(name);
|
|
99
|
+
this.disabled.delete(name);
|
|
73
100
|
if (this.tools.has(name)) {
|
|
74
101
|
this.enabled.add(name);
|
|
75
102
|
return true;
|
|
76
103
|
}
|
|
77
104
|
return false;
|
|
78
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* 移除工具(LLM 不可见,承接旧 disable 行为)
|
|
108
|
+
*/
|
|
109
|
+
remove(name) {
|
|
110
|
+
this.pendingRemoved.add(name);
|
|
111
|
+
this.pendingDisabled.delete(name);
|
|
112
|
+
this.disabled.delete(name);
|
|
113
|
+
if (!this.tools.has(name)) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
this.enabled.delete(name);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 取消移除工具,恢复为启用状态
|
|
121
|
+
*/
|
|
122
|
+
unremove(name) {
|
|
123
|
+
return this.enable(name);
|
|
124
|
+
}
|
|
79
125
|
/**
|
|
80
126
|
* 检查工具是否启用
|
|
81
127
|
*/
|
|
82
128
|
isEnabled(name) {
|
|
83
129
|
return this.enabled.has(name);
|
|
84
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* 检查工具是否禁用(屏蔽)
|
|
133
|
+
*/
|
|
134
|
+
isDisabled(name) {
|
|
135
|
+
return this.disabled.has(name);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 检查工具是否移除
|
|
139
|
+
*/
|
|
140
|
+
isRemoved(name) {
|
|
141
|
+
return this.tools.has(name) && !this.enabled.has(name) && !this.disabled.has(name);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 检查工具是否为 exclusive 模式
|
|
145
|
+
*/
|
|
146
|
+
isExclusive(name) {
|
|
147
|
+
const tool = this.tools.get(name);
|
|
148
|
+
return tool?.executionMode === "exclusive";
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* 检查工具是否可并行执行
|
|
152
|
+
*/
|
|
153
|
+
isParallelizable(name) {
|
|
154
|
+
const tool = this.tools.get(name);
|
|
155
|
+
return tool?.parallelizable === true && tool?.executionMode !== "exclusive";
|
|
156
|
+
}
|
|
85
157
|
/**
|
|
86
158
|
* 获取工具来源(调试用)
|
|
87
159
|
*/
|
|
@@ -92,11 +164,18 @@ var ToolRegistry = class {
|
|
|
92
164
|
* 获取工具条目(调试快照用)
|
|
93
165
|
*/
|
|
94
166
|
getEntries() {
|
|
95
|
-
|
|
167
|
+
const entries = Array.from(this.tools.entries()).map(([name, tool]) => ({
|
|
96
168
|
tool,
|
|
169
|
+
state: this.enabled.has(name) ? "enabled" : this.disabled.has(name) ? "disabled" : "removed",
|
|
97
170
|
enabled: this.enabled.has(name),
|
|
98
171
|
source: this.sources.get(name)
|
|
99
172
|
}));
|
|
173
|
+
for (const [, list] of this.superseded) {
|
|
174
|
+
for (const { tool, source } of list) {
|
|
175
|
+
entries.push({ tool, state: "superseded", enabled: false, source });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return entries;
|
|
100
179
|
}
|
|
101
180
|
/**
|
|
102
181
|
* 获取工具
|
|
@@ -105,10 +184,10 @@ var ToolRegistry = class {
|
|
|
105
184
|
return this.tools.get(name);
|
|
106
185
|
}
|
|
107
186
|
/**
|
|
108
|
-
*
|
|
187
|
+
* 获取所有 LLM 可见工具(启用 + 禁用)
|
|
109
188
|
*/
|
|
110
189
|
getAll() {
|
|
111
|
-
return Array.from(this.enabled).map((name) => this.tools.get(name)).filter((t) => t !== void 0);
|
|
190
|
+
return Array.from(/* @__PURE__ */ new Set([...this.enabled, ...this.disabled])).map((name) => this.tools.get(name)).filter((t) => t !== void 0);
|
|
112
191
|
}
|
|
113
192
|
/**
|
|
114
193
|
* 检查工具是否存在
|
|
@@ -548,4 +627,4 @@ export {
|
|
|
548
627
|
createCaptureAndUnderstandTool,
|
|
549
628
|
createCaptureAndUnderstandAdvancedTool
|
|
550
629
|
};
|
|
551
|
-
//# sourceMappingURL=chunk-
|
|
630
|
+
//# sourceMappingURL=chunk-EK6KGS2M.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/features/visual/tools.ts","../src/core/tool.ts"],"sourcesContent":["/**\n * VisualFeature 工具定义\n *\n * 提供两个视觉理解工具:\n * 1. capture_and_understand_window - 使用 4B 模型(快速)\n * 2. capture_and_understand_window_advanced - 使用 9B 模型(准确)\n *\n * 两个工具都支持缓存回退:\n * - 截图失败时(窗口缩小)使用缓存的图片\n * - 检测到纯色图时使用缓存的图片\n */\n\nimport { spawn } from 'child_process';\nimport { readFileSync } from 'fs';\nimport OpenAI from 'openai';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { createTool } from '../../core/tool.js';\nimport type { Tool } from '../../core/types.js';\nimport type {\n CaptureResult,\n VisualUnderstandingResult,\n} from './types.js';\nimport type { VisualCacheManager } from './cache.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// ========== LLM 配置 ==========\n\nconst DEFAULT_BASE_URL = 'http://localhost:7575';\nconst DEFAULT_MODEL = 'Qwen3.5-4B-Q5_K_M';\n\nconst DEFAULT_ADVANCED_BASE_URL = 'http://localhost:7577';\nconst DEFAULT_ADVANCED_MODEL = 'Qwen3.5-9B-Q4_K_M';\n\n// 视觉理解系统提示词(增量分析版本)\nconst VISION_SYSTEM_PROMPT = `# Role\n你是一位专业的视觉内容分析专家。你的任务是仔细观察用户提供的截图,并给出准确、详细的内容描述。\n\n# Task\n分析截图中的视觉内容,重点描述:\n1. **主要内容**:截图显示的是什么应用、什么界面\n2. **文本内容**:所有可见的文字、标签、提示信息\n3. **关键元素**:重要的按钮、输入框、菜单项等可交互元素\n4. **状态信息**:当前界面状态(如:选中/未选中、展开/收起等)\n5. **异常或特殊点**:错误提示、警告信息、弹窗等\n\n# 增量分析策略\n你会收到窗口上下文信息(进程名、窗口标题)和上次的识别结果。请根据这些信息进行增量分析:\n\n## 情况 1:完全不同的页面\n如果新截图显示的页面与上次识别结果完全不同(例如:从代码编辑器切换到浏览器),请直接描述新页面的完整内容。\n\n## 情况 2:相同页面,视角变化\n如果新截图与上次是同一个页面,但视角发生了变化(例如:滚动到了新位置、展开了新的菜单、切换了标签页),请只描述**新看到的内容**,不需要重复描述之前已经识别过的内容。\n\n## 情况 3:相同页面,更正之前的内容\n如果新截图与上次是同一个页面,但你发现之前的识别有错误或遗漏,请直接更正或补充。例如:\n- \"更正:之前识别为'确认'的按钮实际是'取消'\"\n- \"补充:页面上方还有一个搜索栏,输入框显示'请输入关键词'\"\n\n# Output Requirements\n- 使用自然语言描述,不要使用 JSON 或其他结构化格式\n- 描述要准确、清晰,便于后续处理\n- 如果图片不清晰或无法理解,直接说明\n- 输出语言与截图中的主要语言保持一致\n\n# Example Output (完全不同的页面)\n这是一个浏览器窗口,显示的是一个购物网站。顶部有导航栏,显示\"首页、分类、购物车、我的\"。中间部分是商品列表,每个商品卡片包含图片、名称、价格和\"加入购物车\"按钮。当前显示的是\"电子产品\"分类。\n\n# Example Output (相同页面,视角变化)\n页面向下滚动后,显示了更多的商品。新看到的商品包括:一款无线鼠标(价格:199元)、一个机械键盘(价格:399元)和一个显示器(价格:1299元)。每个商品都有\"加入购物车\"按钮。\n\n# Example Output (更正之前的内容)\n更正:之前将页面顶部的文字识别为\"登录\",实际应该是\"注册\"。页面顶部右侧有\"注册\"和\"登录\"两个按钮,当前\"注册\"按钮处于高亮状态。`;\n\n// ========== 辅助函数 ==========\n\n/**\n * 检测是否为纯色图\n * 通过采样图片的几个关键点来判断是否为纯色\n */\nfunction isSolidColorImage(base64Image: string): boolean {\n try {\n // 解码 base64 获取 PNG 数据\n const imageBuffer = Buffer.from(base64Image, 'base64');\n\n // 简单检测:检查图片大小,如果太小(小于 1KB)可能是纯色或错误\n if (imageBuffer.length < 1024) {\n return true;\n }\n\n // PNG 文件头的 IHDR chunk 之后是 IDAT(图像数据)\n // 我们可以检查图片是否包含足够的颜色变化\n // 这里做一个简单的启发式检查:如果压缩后非常小,可能是纯色\n\n // 更精确的方法是解码 PNG,但这需要额外的库\n // 作为替代,我们检查文件大小与像素数的比例\n // 对于普通截图,这个比例通常在 0.1 - 1 字节/像素之间\n // 纯色图压缩后通常 < 0.01 字节/像素\n\n // 从 PNG IHDR chunk 读取宽高(简化版)\n // IHDR 位于 PNG 签名后 8 字节,宽度 4 字节,高度 4 字节\n if (imageBuffer.length > 24) {\n const width = imageBuffer.readUInt32BE(16);\n const height = imageBuffer.readUInt32BE(20);\n const pixelCount = width * height;\n const bytesPerPixel = imageBuffer.length / pixelCount;\n\n // 如果每个像素平均 < 0.02 字节,很可能是纯色\n if (bytesPerPixel < 0.02) {\n return true;\n }\n }\n\n return false;\n } catch (error) {\n console.warn('[VisualFeature] Error checking for solid color image:', error);\n return false;\n }\n}\n\n/**\n * 从缓存读取最新的图片\n */\nfunction getCachedImageBase64(cacheManager: VisualCacheManager, hwnd: string): string | null {\n try {\n const capturePath = cacheManager.getCapturePath(hwnd);\n if (!capturePath) {\n return null;\n }\n\n // 读取图片文件并转换为 base64\n const imageBuffer = readFileSync(capturePath);\n return imageBuffer.toString('base64');\n } catch (error) {\n console.warn(`[VisualFeature] Failed to read cached image for ${hwnd}:`, error);\n return null;\n }\n}\n\n/**\n * 从缓存获取上次的窗口信息(用于标题、进程名等)\n */\nfunction getCachedWindowInfo(cacheManager: VisualCacheManager, hwnd: string): {\n title: string;\n processName?: string;\n} | null {\n try {\n const entries = cacheManager.getAllEntries();\n const entry = entries.find(e => e.hwnd === hwnd);\n if (entry) {\n return {\n title: entry.title,\n };\n }\n return null;\n } catch (error) {\n return null;\n }\n}\n\n/**\n * 检查缓存图片是否已有该模型的分析结果\n * 返回分析结果(如果存在且模型匹配)或 null\n */\nfunction getCachedAnalysisForModel(\n cacheManager: VisualCacheManager,\n hwnd: string,\n model: string\n): { description: string; createdAt: number } | null {\n try {\n const entry = cacheManager['metadata']?.entries?.[hwnd];\n if (!entry?.analysis?.description) {\n return null;\n }\n\n // 检查模型是否匹配\n if (entry.analysis.model !== model) {\n console.log(`[VisualFeature] Cached analysis model mismatch: cached=${entry.analysis.model}, requested=${model}`);\n return null;\n }\n\n // 检查分析结果是否过期(使用较长的 TTL,因为缓存图片的分析结果可以复用)\n const now = Date.now();\n const age = now - entry.analysis.createdAt;\n const cacheReuseTTL = 24 * 60 * 60 * 1000; // 24小时 - 缓存图片的分析结果可以复用更久\n\n if (age > cacheReuseTTL) {\n console.log(`[VisualFeature] Cached analysis too old: ${Math.round(age / 1000)}s`);\n return null;\n }\n\n console.log(`[VisualFeature] Reusing cached analysis for ${hwnd} (model: ${model})`);\n return {\n description: entry.analysis.description,\n createdAt: entry.analysis.createdAt,\n };\n } catch (error) {\n return null;\n }\n}\n\n// ========== 核心功能函数 ==========\n\n/**\n * 调用 Python 脚本截图指定窗口\n */\nexport async function captureWindow(\n hwnd: string,\n pythonPath: string = 'python',\n pythonArgs?: string[]\n): Promise<CaptureResult> {\n const scriptPath = join(__dirname, 'python', 'capture.py');\n\n return new Promise((resolve) => {\n // 支持 pythonArgs 配置(如 uv run)\n const args = pythonArgs\n ? [...pythonArgs, scriptPath, hwnd]\n : [scriptPath, hwnd];\n\n // 继承父进程的环境变量(包括 PATH),这样能找到 uv 管理的 Python\n const child = spawn(pythonPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: { ...process.env }, // 继承环境变量\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout?.on('data', (data) => {\n stdout += data.toString();\n });\n\n child.stderr?.on('data', (data) => {\n stderr += data.toString();\n });\n\n child.on('close', (code) => {\n if (code !== 0) {\n resolve({ error: `Python script failed (exit code ${code}): ${stderr}` });\n return;\n }\n\n try {\n const result: CaptureResult = JSON.parse(stdout.trim());\n resolve(result);\n } catch (error) {\n resolve({ error: `Failed to parse Python output: ${error}` });\n }\n });\n\n child.on('error', (error) => {\n resolve({ error: `Failed to spawn Python: ${error.message}` });\n });\n });\n}\n\n/**\n * 调用 LLM 进行视觉理解\n * @param base64Image base64 编码的图片\n * @param client OpenAI 客户端\n * @param model 模型名称\n * @param windowInfo 窗口信息(进程名、标题等)\n * @param previousAnalysis 上次的分析结果(可选)\n */\nexport async function understandImage(\n base64Image: string,\n client: OpenAI,\n model: string,\n windowInfo?: {\n processName?: string;\n windowTitle?: string;\n },\n previousAnalysis?: string\n): Promise<string> {\n // 构建用户消息,包含窗口上下文和上次分析结果\n let userText = '请分析这张截图的内容。';\n\n if (windowInfo?.processName || windowInfo?.windowTitle) {\n userText += '\\n\\n# 窗口信息';\n if (windowInfo.processName) {\n userText += `\\n- 进程名称:${windowInfo.processName}`;\n }\n if (windowInfo.windowTitle) {\n userText += `\\n- 窗口标题:${windowInfo.windowTitle}`;\n }\n }\n\n if (previousAnalysis) {\n userText += `\\n\\n# 上次识别结果\\n${previousAnalysis}`;\n userText += '\\n\\n请根据上次识别结果进行增量分析。如果页面完全不同,请直接描述新页面的完整内容;如果是相同页面但视角变化,请只描述新看到的内容;如果需要更正之前的内容,请直接说明。';\n }\n\n const response = await client.chat.completions.create({\n model,\n messages: [\n {\n role: 'system',\n content: VISION_SYSTEM_PROMPT,\n },\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: userText,\n },\n {\n type: 'image_url',\n image_url: {\n url: `data:image/png;base64,${base64Image}`,\n },\n },\n ],\n },\n ],\n temperature: 0.3,\n });\n\n return response.choices[0]?.message?.content ?? '无法理解图片内容';\n}\n\n// ========== 工具工厂函数 ==========\n\n/**\n * 创建基础视觉理解工具(4B 模型)\n * 支持缓存回退:截图失败或纯色图时使用缓存图片\n */\nexport function createCaptureAndUnderstandTool(\n client: OpenAI,\n model: string,\n pythonPath: string = 'python',\n pythonArgs?: string[],\n cacheManager?: VisualCacheManager | null\n): Tool {\n return createTool({\n name: 'capture_and_understand_window',\n description: '截取指定窗口的截图并使用视觉模型理解其内容。输入参数为窗口句柄(HWND),如 \"0x12345\" 或 \"12345\"。返回截图内容的详细描述。',\n parameters: {\n type: 'object',\n properties: {\n hwnd: {\n type: 'string',\n description: '窗口句柄,支持 16 进制格式(如 \"0x12345\")或 10 进制格式(如 \"12345\")',\n },\n },\n required: ['hwnd'],\n },\n render: { call: 'capture', result: 'capture' },\n execute: async ({ hwnd }: { hwnd: string }) => {\n // 1. 尝试截图\n let captureResult = await captureWindow(hwnd, pythonPath, pythonArgs);\n\n let imageToAnalyze = captureResult.data;\n let imageSource = '新截图';\n let analysisNote = '';\n let usedCachedImage = false;\n\n // 2. 如果截图失败,尝试使用缓存\n if (captureResult.error || !captureResult.data) {\n if (cacheManager) {\n console.log(`[capture_and_understand_window] Screenshot failed for ${hwnd}, trying cache...`);\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = `[使用缓存图片,因为截图失败:${captureResult.error}]`;\n console.log(`[capture_and_understand_window] Using cached image for ${hwnd}`);\n } else {\n return `截图失败且无缓存可用:${captureResult.error ?? '未知错误'}`;\n }\n } else {\n return `截图失败:${captureResult.error ?? '未知错误'}`;\n }\n }\n\n // 3. 检查是否为纯色图,如果是则使用缓存\n if (imageToAnalyze && isSolidColorImage(imageToAnalyze)) {\n console.log(`[capture_and_understand_window] Detected solid color image for ${hwnd}, trying cache...`);\n if (cacheManager) {\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = '[使用缓存图片,因为新截图是纯色图(窗口可能被最小化或遮挡)]';\n console.log(`[capture_and_understand_window] Using cached image for ${hwnd} (solid color detected)`);\n }\n }\n }\n\n // 4. 如果没有可用的图片\n if (!imageToAnalyze) {\n return '无法获取可用的图片进行分析';\n }\n\n // 5. 缓存去重:如果使用了缓存图片,检查是否已有该模型的分析结果\n if (usedCachedImage && cacheManager) {\n const cachedAnalysis = getCachedAnalysisForModel(cacheManager, hwnd, model);\n if (cachedAnalysis) {\n // 直接返回缓存的分析结果,不需要重新调用模型\n const age = Math.round((Date.now() - cachedAnalysis.createdAt) / 1000);\n const timeAgo = age < 60 ? `${age}秒前` : age < 3600 ? `${Math.round(age / 60)}分钟前` : `${Math.round(age / 3600)}小时前`;\n\n const parts = [\n `使用缓存图片(来源:${imageSource})`,\n analysisNote || '',\n '',\n `内容分析(${timeAgo}的分析结果):`,\n cachedAnalysis.description,\n ].filter(Boolean);\n\n return parts.join('\\n');\n }\n }\n\n // 6. 获取窗口信息(从缓存)\n let windowInfo: { processName?: string; windowTitle?: string } | undefined;\n if (cacheManager) {\n const cached = getCachedWindowInfo(cacheManager, hwnd);\n if (cached) {\n windowInfo = {\n windowTitle: cached.title,\n };\n }\n }\n\n // 7. 获取上次的分析结果(从缓存,用于增量分析)\n let previousAnalysis: string | undefined;\n if (cacheManager) {\n const cachedAnalysis = cacheManager.getAnalysis(hwnd);\n if (cachedAnalysis) {\n previousAnalysis = cachedAnalysis.description;\n }\n }\n\n // 8. 视觉理解\n try {\n const description = await understandImage(\n imageToAnalyze,\n client,\n model,\n windowInfo,\n previousAnalysis\n );\n\n const parts = [\n `截图成功(${captureResult.width ?? '?'}x${captureResult.height ?? '?'},来源:${imageSource})`,\n ];\n\n if (analysisNote) {\n parts.push(analysisNote);\n }\n\n parts.push('');\n parts.push('内容分析:');\n parts.push(description);\n\n return parts.join('\\n');\n } catch (error) {\n return `视觉理解失败:${error}`;\n }\n },\n });\n}\n\n/**\n * 创建高级视觉理解工具(9B 模型)\n * 支持缓存回退:截图失败或纯色图时使用缓存图片\n */\nexport function createCaptureAndUnderstandAdvancedTool(\n client: OpenAI,\n model: string,\n pythonPath: string = 'python',\n pythonArgs?: string[],\n cacheManager?: VisualCacheManager | null\n): Tool {\n return createTool({\n name: 'capture_and_understand_window_advanced',\n description: '使用高级视觉模型(9B)截取指定窗口的截图并进行深度理解。提供更准确的内容识别和分析。输入参数为窗口句柄(HWND),如 \"0x12345\" 或 \"12345\"。',\n parameters: {\n type: 'object',\n properties: {\n hwnd: {\n type: 'string',\n description: '窗口句柄,支持 16 进制格式(如 \"0x12345\")或 10 进制格式(如 \"12345\")',\n },\n },\n required: ['hwnd'],\n },\n render: { call: 'capture', result: 'capture' },\n execute: async ({ hwnd }: { hwnd: string }) => {\n // 1. 尝试截图(与基础工具相同的逻辑)\n let captureResult = await captureWindow(hwnd, pythonPath, pythonArgs);\n\n let imageToAnalyze = captureResult.data;\n let imageSource = '新截图';\n let analysisNote = '';\n let usedCachedImage = false;\n\n // 2. 如果截图失败,尝试使用缓存\n if (captureResult.error || !captureResult.data) {\n if (cacheManager) {\n console.log(`[capture_and_understand_window_advanced] Screenshot failed for ${hwnd}, trying cache...`);\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = `[使用缓存图片,因为截图失败:${captureResult.error}]`;\n console.log(`[capture_and_understand_window_advanced] Using cached image for ${hwnd}`);\n } else {\n return `截图失败且无缓存可用:${captureResult.error ?? '未知错误'}`;\n }\n } else {\n return `截图失败:${captureResult.error ?? '未知错误'}`;\n }\n }\n\n // 3. 检查是否为纯色图\n if (imageToAnalyze && isSolidColorImage(imageToAnalyze)) {\n console.log(`[capture_and_understand_window_advanced] Detected solid color image for ${hwnd}, trying cache...`);\n if (cacheManager) {\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = '[使用缓存图片,因为新截图是纯色图(窗口可能被最小化或遮挡)]';\n console.log(`[capture_and_understand_window_advanced] Using cached image for ${hwnd} (solid color detected)`);\n }\n }\n }\n\n // 4. 如果没有可用的图片\n if (!imageToAnalyze) {\n return '无法获取可用的图片进行分析';\n }\n\n // 5. 缓存去重:如果使用了缓存图片,检查是否已有该模型的分析结果\n if (usedCachedImage && cacheManager) {\n const cachedAnalysis = getCachedAnalysisForModel(cacheManager, hwnd, model);\n if (cachedAnalysis) {\n // 直接返回缓存的分析结果,不需要重新调用模型\n const age = Math.round((Date.now() - cachedAnalysis.createdAt) / 1000);\n const timeAgo = age < 60 ? `${age}秒前` : age < 3600 ? `${Math.round(age / 60)}分钟前` : `${Math.round(age / 3600)}小时前`;\n\n const parts = [\n `[高级模型] 使用缓存图片(来源:${imageSource})`,\n analysisNote || '',\n '',\n `内容分析(${timeAgo}的分析结果):`,\n cachedAnalysis.description,\n ].filter(Boolean);\n\n return parts.join('\\n');\n }\n }\n\n // 6. 获取窗口信息\n let windowInfo: { processName?: string; windowTitle?: string } | undefined;\n if (cacheManager) {\n const cached = getCachedWindowInfo(cacheManager, hwnd);\n if (cached) {\n windowInfo = {\n windowTitle: cached.title,\n };\n }\n }\n\n // 7. 获取上次的分析结果(用于增量分析)\n let previousAnalysis: string | undefined;\n if (cacheManager) {\n const cachedAnalysis = cacheManager.getAnalysis(hwnd);\n if (cachedAnalysis) {\n previousAnalysis = cachedAnalysis.description;\n }\n }\n\n // 8. 高级视觉理解\n try {\n const description = await understandImage(\n imageToAnalyze,\n client,\n model,\n windowInfo,\n previousAnalysis\n );\n\n const parts = [\n `[高级模型] 截图成功(${captureResult.width ?? '?'}x${captureResult.height ?? '?'},来源:${imageSource})`,\n ];\n\n if (analysisNote) {\n parts.push(analysisNote);\n }\n\n parts.push('');\n parts.push('内容分析:');\n parts.push(description);\n\n return parts.join('\\n');\n } catch (error) {\n return `视觉理解失败:${error}`;\n }\n },\n });\n}\n","/**\n * 工具定义\n * 简单的工具创建函数\n */\n\nimport type { Tool, ToolRenderConfig } from './types.js';\n\n/**\n * 渲染配置扩展类型\n * 支持字符串(模板名称)或配置对象\n */\nexport type ToolRenderInput = ToolRenderConfig | string;\n\n/**\n * 创建一个工具\n * @param config 工具配置\n * @param sourceFile 可选:调用此函数的源文件路径(用于自动查找渲染文件)\n */\nexport function createTool(\n config: {\n name: string;\n description: string;\n parameters?: Record<string, any>;\n execute: (args: any, context?: any) => Promise<any>;\n render?: ToolRenderInput;\n },\n sourceFile?: string\n): Tool {\n let finalRender: ToolRenderConfig | undefined = undefined;\n\n if (config.render) {\n // 如果 render 是字符串,转换为配置对象(call 和 result 使用同一模板)\n if (typeof config.render === 'string') {\n finalRender = {\n call: config.render,\n result: config.render,\n };\n } else {\n finalRender = config.render;\n }\n } else if (sourceFile) {\n // render 未定义时,尝试自动查找同目录下的 .render.ts 文件\n // 例如:tools/fs.ts -> tools/fs.render.ts\n const renderPath = sourceFile.replace(/\\.ts$/, '.render.ts');\n // 使用特殊标记,延迟加载\n (finalRender as any) = {\n __renderPath: renderPath,\n call: undefined,\n result: undefined,\n };\n }\n\n return {\n name: config.name,\n description: config.description,\n parameters: config.parameters,\n execute: config.execute,\n render: finalRender,\n };\n}\n\n/**\n * 工具注册表 - 管理多个工具\n */\nexport class ToolRegistry {\n private tools = new Map<string, Tool>();\n private enabled = new Set<string>(); // 启用的工具名\n private pendingDisabled = new Set<string>(); // 工具注册前的预禁用状态\n private sources = new Map<string, string>(); // 工具来源追踪\n\n /**\n * 注册工具(默认启用,记录来源)\n */\n register(tool: Tool, source?: string): this {\n this.tools.set(tool.name, tool);\n if (this.pendingDisabled.has(tool.name)) {\n this.enabled.delete(tool.name);\n } else {\n this.enabled.add(tool.name); // 默认启用\n }\n if (source) {\n this.sources.set(tool.name, source);\n }\n return this;\n }\n\n /**\n * 禁用工具\n */\n disable(name: string): boolean {\n this.pendingDisabled.add(name);\n if (!this.tools.has(name)) {\n return true;\n }\n return this.enabled.delete(name);\n }\n\n /**\n * 启用工具\n */\n enable(name: string): boolean {\n this.pendingDisabled.delete(name);\n if (this.tools.has(name)) {\n this.enabled.add(name);\n return true;\n }\n return false;\n }\n\n /**\n * 检查工具是否启用\n */\n isEnabled(name: string): boolean {\n return this.enabled.has(name);\n }\n\n /**\n * 获取工具来源(调试用)\n */\n getSource(name: string): string | undefined {\n return this.sources.get(name);\n }\n\n /**\n * 获取工具条目(调试快照用)\n */\n getEntries(): Array<{ tool: Tool; enabled: boolean; source?: string }> {\n return Array.from(this.tools.entries()).map(([name, tool]) => ({\n tool,\n enabled: this.enabled.has(name),\n source: this.sources.get(name),\n }));\n }\n\n /**\n * 获取工具\n */\n get(name: string): Tool | undefined {\n return this.tools.get(name);\n }\n\n /**\n * 获取所有工具(只返回启用的)\n */\n getAll(): Tool[] {\n return Array.from(this.enabled)\n .map(name => this.tools.get(name))\n .filter((t): t is Tool => t !== undefined);\n }\n\n /**\n * 检查工具是否存在\n */\n has(name: string): boolean {\n return this.tools.has(name);\n }\n\n /**\n * 获取工具的渲染配置\n */\n getRenderConfig(name: string): ToolRenderConfig | undefined {\n const tool = this.tools.get(name);\n return tool?.render;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/features/visual/tools.ts","../src/core/tool.ts"],"sourcesContent":["/**\n * VisualFeature 工具定义\n *\n * 提供两个视觉理解工具:\n * 1. capture_and_understand_window - 使用 4B 模型(快速)\n * 2. capture_and_understand_window_advanced - 使用 9B 模型(准确)\n *\n * 两个工具都支持缓存回退:\n * - 截图失败时(窗口缩小)使用缓存的图片\n * - 检测到纯色图时使用缓存的图片\n */\n\nimport { spawn } from 'child_process';\nimport { readFileSync } from 'fs';\nimport OpenAI from 'openai';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { createTool } from '../../core/tool.js';\nimport type { Tool } from '../../core/types.js';\nimport type {\n CaptureResult,\n VisualUnderstandingResult,\n} from './types.js';\nimport type { VisualCacheManager } from './cache.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// ========== LLM 配置 ==========\n\nconst DEFAULT_BASE_URL = 'http://localhost:7575';\nconst DEFAULT_MODEL = 'Qwen3.5-4B-Q5_K_M';\n\nconst DEFAULT_ADVANCED_BASE_URL = 'http://localhost:7577';\nconst DEFAULT_ADVANCED_MODEL = 'Qwen3.5-9B-Q4_K_M';\n\n// 视觉理解系统提示词(增量分析版本)\nconst VISION_SYSTEM_PROMPT = `# Role\n你是一位专业的视觉内容分析专家。你的任务是仔细观察用户提供的截图,并给出准确、详细的内容描述。\n\n# Task\n分析截图中的视觉内容,重点描述:\n1. **主要内容**:截图显示的是什么应用、什么界面\n2. **文本内容**:所有可见的文字、标签、提示信息\n3. **关键元素**:重要的按钮、输入框、菜单项等可交互元素\n4. **状态信息**:当前界面状态(如:选中/未选中、展开/收起等)\n5. **异常或特殊点**:错误提示、警告信息、弹窗等\n\n# 增量分析策略\n你会收到窗口上下文信息(进程名、窗口标题)和上次的识别结果。请根据这些信息进行增量分析:\n\n## 情况 1:完全不同的页面\n如果新截图显示的页面与上次识别结果完全不同(例如:从代码编辑器切换到浏览器),请直接描述新页面的完整内容。\n\n## 情况 2:相同页面,视角变化\n如果新截图与上次是同一个页面,但视角发生了变化(例如:滚动到了新位置、展开了新的菜单、切换了标签页),请只描述**新看到的内容**,不需要重复描述之前已经识别过的内容。\n\n## 情况 3:相同页面,更正之前的内容\n如果新截图与上次是同一个页面,但你发现之前的识别有错误或遗漏,请直接更正或补充。例如:\n- \"更正:之前识别为'确认'的按钮实际是'取消'\"\n- \"补充:页面上方还有一个搜索栏,输入框显示'请输入关键词'\"\n\n# Output Requirements\n- 使用自然语言描述,不要使用 JSON 或其他结构化格式\n- 描述要准确、清晰,便于后续处理\n- 如果图片不清晰或无法理解,直接说明\n- 输出语言与截图中的主要语言保持一致\n\n# Example Output (完全不同的页面)\n这是一个浏览器窗口,显示的是一个购物网站。顶部有导航栏,显示\"首页、分类、购物车、我的\"。中间部分是商品列表,每个商品卡片包含图片、名称、价格和\"加入购物车\"按钮。当前显示的是\"电子产品\"分类。\n\n# Example Output (相同页面,视角变化)\n页面向下滚动后,显示了更多的商品。新看到的商品包括:一款无线鼠标(价格:199元)、一个机械键盘(价格:399元)和一个显示器(价格:1299元)。每个商品都有\"加入购物车\"按钮。\n\n# Example Output (更正之前的内容)\n更正:之前将页面顶部的文字识别为\"登录\",实际应该是\"注册\"。页面顶部右侧有\"注册\"和\"登录\"两个按钮,当前\"注册\"按钮处于高亮状态。`;\n\n// ========== 辅助函数 ==========\n\n/**\n * 检测是否为纯色图\n * 通过采样图片的几个关键点来判断是否为纯色\n */\nfunction isSolidColorImage(base64Image: string): boolean {\n try {\n // 解码 base64 获取 PNG 数据\n const imageBuffer = Buffer.from(base64Image, 'base64');\n\n // 简单检测:检查图片大小,如果太小(小于 1KB)可能是纯色或错误\n if (imageBuffer.length < 1024) {\n return true;\n }\n\n // PNG 文件头的 IHDR chunk 之后是 IDAT(图像数据)\n // 我们可以检查图片是否包含足够的颜色变化\n // 这里做一个简单的启发式检查:如果压缩后非常小,可能是纯色\n\n // 更精确的方法是解码 PNG,但这需要额外的库\n // 作为替代,我们检查文件大小与像素数的比例\n // 对于普通截图,这个比例通常在 0.1 - 1 字节/像素之间\n // 纯色图压缩后通常 < 0.01 字节/像素\n\n // 从 PNG IHDR chunk 读取宽高(简化版)\n // IHDR 位于 PNG 签名后 8 字节,宽度 4 字节,高度 4 字节\n if (imageBuffer.length > 24) {\n const width = imageBuffer.readUInt32BE(16);\n const height = imageBuffer.readUInt32BE(20);\n const pixelCount = width * height;\n const bytesPerPixel = imageBuffer.length / pixelCount;\n\n // 如果每个像素平均 < 0.02 字节,很可能是纯色\n if (bytesPerPixel < 0.02) {\n return true;\n }\n }\n\n return false;\n } catch (error) {\n console.warn('[VisualFeature] Error checking for solid color image:', error);\n return false;\n }\n}\n\n/**\n * 从缓存读取最新的图片\n */\nfunction getCachedImageBase64(cacheManager: VisualCacheManager, hwnd: string): string | null {\n try {\n const capturePath = cacheManager.getCapturePath(hwnd);\n if (!capturePath) {\n return null;\n }\n\n // 读取图片文件并转换为 base64\n const imageBuffer = readFileSync(capturePath);\n return imageBuffer.toString('base64');\n } catch (error) {\n console.warn(`[VisualFeature] Failed to read cached image for ${hwnd}:`, error);\n return null;\n }\n}\n\n/**\n * 从缓存获取上次的窗口信息(用于标题、进程名等)\n */\nfunction getCachedWindowInfo(cacheManager: VisualCacheManager, hwnd: string): {\n title: string;\n processName?: string;\n} | null {\n try {\n const entries = cacheManager.getAllEntries();\n const entry = entries.find(e => e.hwnd === hwnd);\n if (entry) {\n return {\n title: entry.title,\n };\n }\n return null;\n } catch (error) {\n return null;\n }\n}\n\n/**\n * 检查缓存图片是否已有该模型的分析结果\n * 返回分析结果(如果存在且模型匹配)或 null\n */\nfunction getCachedAnalysisForModel(\n cacheManager: VisualCacheManager,\n hwnd: string,\n model: string\n): { description: string; createdAt: number } | null {\n try {\n const entry = cacheManager['metadata']?.entries?.[hwnd];\n if (!entry?.analysis?.description) {\n return null;\n }\n\n // 检查模型是否匹配\n if (entry.analysis.model !== model) {\n console.log(`[VisualFeature] Cached analysis model mismatch: cached=${entry.analysis.model}, requested=${model}`);\n return null;\n }\n\n // 检查分析结果是否过期(使用较长的 TTL,因为缓存图片的分析结果可以复用)\n const now = Date.now();\n const age = now - entry.analysis.createdAt;\n const cacheReuseTTL = 24 * 60 * 60 * 1000; // 24小时 - 缓存图片的分析结果可以复用更久\n\n if (age > cacheReuseTTL) {\n console.log(`[VisualFeature] Cached analysis too old: ${Math.round(age / 1000)}s`);\n return null;\n }\n\n console.log(`[VisualFeature] Reusing cached analysis for ${hwnd} (model: ${model})`);\n return {\n description: entry.analysis.description,\n createdAt: entry.analysis.createdAt,\n };\n } catch (error) {\n return null;\n }\n}\n\n// ========== 核心功能函数 ==========\n\n/**\n * 调用 Python 脚本截图指定窗口\n */\nexport async function captureWindow(\n hwnd: string,\n pythonPath: string = 'python',\n pythonArgs?: string[]\n): Promise<CaptureResult> {\n const scriptPath = join(__dirname, 'python', 'capture.py');\n\n return new Promise((resolve) => {\n // 支持 pythonArgs 配置(如 uv run)\n const args = pythonArgs\n ? [...pythonArgs, scriptPath, hwnd]\n : [scriptPath, hwnd];\n\n // 继承父进程的环境变量(包括 PATH),这样能找到 uv 管理的 Python\n const child = spawn(pythonPath, args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n env: { ...process.env }, // 继承环境变量\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout?.on('data', (data) => {\n stdout += data.toString();\n });\n\n child.stderr?.on('data', (data) => {\n stderr += data.toString();\n });\n\n child.on('close', (code) => {\n if (code !== 0) {\n resolve({ error: `Python script failed (exit code ${code}): ${stderr}` });\n return;\n }\n\n try {\n const result: CaptureResult = JSON.parse(stdout.trim());\n resolve(result);\n } catch (error) {\n resolve({ error: `Failed to parse Python output: ${error}` });\n }\n });\n\n child.on('error', (error) => {\n resolve({ error: `Failed to spawn Python: ${error.message}` });\n });\n });\n}\n\n/**\n * 调用 LLM 进行视觉理解\n * @param base64Image base64 编码的图片\n * @param client OpenAI 客户端\n * @param model 模型名称\n * @param windowInfo 窗口信息(进程名、标题等)\n * @param previousAnalysis 上次的分析结果(可选)\n */\nexport async function understandImage(\n base64Image: string,\n client: OpenAI,\n model: string,\n windowInfo?: {\n processName?: string;\n windowTitle?: string;\n },\n previousAnalysis?: string\n): Promise<string> {\n // 构建用户消息,包含窗口上下文和上次分析结果\n let userText = '请分析这张截图的内容。';\n\n if (windowInfo?.processName || windowInfo?.windowTitle) {\n userText += '\\n\\n# 窗口信息';\n if (windowInfo.processName) {\n userText += `\\n- 进程名称:${windowInfo.processName}`;\n }\n if (windowInfo.windowTitle) {\n userText += `\\n- 窗口标题:${windowInfo.windowTitle}`;\n }\n }\n\n if (previousAnalysis) {\n userText += `\\n\\n# 上次识别结果\\n${previousAnalysis}`;\n userText += '\\n\\n请根据上次识别结果进行增量分析。如果页面完全不同,请直接描述新页面的完整内容;如果是相同页面但视角变化,请只描述新看到的内容;如果需要更正之前的内容,请直接说明。';\n }\n\n const response = await client.chat.completions.create({\n model,\n messages: [\n {\n role: 'system',\n content: VISION_SYSTEM_PROMPT,\n },\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: userText,\n },\n {\n type: 'image_url',\n image_url: {\n url: `data:image/png;base64,${base64Image}`,\n },\n },\n ],\n },\n ],\n temperature: 0.3,\n });\n\n return response.choices[0]?.message?.content ?? '无法理解图片内容';\n}\n\n// ========== 工具工厂函数 ==========\n\n/**\n * 创建基础视觉理解工具(4B 模型)\n * 支持缓存回退:截图失败或纯色图时使用缓存图片\n */\nexport function createCaptureAndUnderstandTool(\n client: OpenAI,\n model: string,\n pythonPath: string = 'python',\n pythonArgs?: string[],\n cacheManager?: VisualCacheManager | null\n): Tool {\n return createTool({\n name: 'capture_and_understand_window',\n description: '截取指定窗口的截图并使用视觉模型理解其内容。输入参数为窗口句柄(HWND),如 \"0x12345\" 或 \"12345\"。返回截图内容的详细描述。',\n parameters: {\n type: 'object',\n properties: {\n hwnd: {\n type: 'string',\n description: '窗口句柄,支持 16 进制格式(如 \"0x12345\")或 10 进制格式(如 \"12345\")',\n },\n },\n required: ['hwnd'],\n },\n render: { call: 'capture', result: 'capture' },\n execute: async ({ hwnd }: { hwnd: string }) => {\n // 1. 尝试截图\n let captureResult = await captureWindow(hwnd, pythonPath, pythonArgs);\n\n let imageToAnalyze = captureResult.data;\n let imageSource = '新截图';\n let analysisNote = '';\n let usedCachedImage = false;\n\n // 2. 如果截图失败,尝试使用缓存\n if (captureResult.error || !captureResult.data) {\n if (cacheManager) {\n console.log(`[capture_and_understand_window] Screenshot failed for ${hwnd}, trying cache...`);\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = `[使用缓存图片,因为截图失败:${captureResult.error}]`;\n console.log(`[capture_and_understand_window] Using cached image for ${hwnd}`);\n } else {\n return `截图失败且无缓存可用:${captureResult.error ?? '未知错误'}`;\n }\n } else {\n return `截图失败:${captureResult.error ?? '未知错误'}`;\n }\n }\n\n // 3. 检查是否为纯色图,如果是则使用缓存\n if (imageToAnalyze && isSolidColorImage(imageToAnalyze)) {\n console.log(`[capture_and_understand_window] Detected solid color image for ${hwnd}, trying cache...`);\n if (cacheManager) {\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = '[使用缓存图片,因为新截图是纯色图(窗口可能被最小化或遮挡)]';\n console.log(`[capture_and_understand_window] Using cached image for ${hwnd} (solid color detected)`);\n }\n }\n }\n\n // 4. 如果没有可用的图片\n if (!imageToAnalyze) {\n return '无法获取可用的图片进行分析';\n }\n\n // 5. 缓存去重:如果使用了缓存图片,检查是否已有该模型的分析结果\n if (usedCachedImage && cacheManager) {\n const cachedAnalysis = getCachedAnalysisForModel(cacheManager, hwnd, model);\n if (cachedAnalysis) {\n // 直接返回缓存的分析结果,不需要重新调用模型\n const age = Math.round((Date.now() - cachedAnalysis.createdAt) / 1000);\n const timeAgo = age < 60 ? `${age}秒前` : age < 3600 ? `${Math.round(age / 60)}分钟前` : `${Math.round(age / 3600)}小时前`;\n\n const parts = [\n `使用缓存图片(来源:${imageSource})`,\n analysisNote || '',\n '',\n `内容分析(${timeAgo}的分析结果):`,\n cachedAnalysis.description,\n ].filter(Boolean);\n\n return parts.join('\\n');\n }\n }\n\n // 6. 获取窗口信息(从缓存)\n let windowInfo: { processName?: string; windowTitle?: string } | undefined;\n if (cacheManager) {\n const cached = getCachedWindowInfo(cacheManager, hwnd);\n if (cached) {\n windowInfo = {\n windowTitle: cached.title,\n };\n }\n }\n\n // 7. 获取上次的分析结果(从缓存,用于增量分析)\n let previousAnalysis: string | undefined;\n if (cacheManager) {\n const cachedAnalysis = cacheManager.getAnalysis(hwnd);\n if (cachedAnalysis) {\n previousAnalysis = cachedAnalysis.description;\n }\n }\n\n // 8. 视觉理解\n try {\n const description = await understandImage(\n imageToAnalyze,\n client,\n model,\n windowInfo,\n previousAnalysis\n );\n\n const parts = [\n `截图成功(${captureResult.width ?? '?'}x${captureResult.height ?? '?'},来源:${imageSource})`,\n ];\n\n if (analysisNote) {\n parts.push(analysisNote);\n }\n\n parts.push('');\n parts.push('内容分析:');\n parts.push(description);\n\n return parts.join('\\n');\n } catch (error) {\n return `视觉理解失败:${error}`;\n }\n },\n });\n}\n\n/**\n * 创建高级视觉理解工具(9B 模型)\n * 支持缓存回退:截图失败或纯色图时使用缓存图片\n */\nexport function createCaptureAndUnderstandAdvancedTool(\n client: OpenAI,\n model: string,\n pythonPath: string = 'python',\n pythonArgs?: string[],\n cacheManager?: VisualCacheManager | null\n): Tool {\n return createTool({\n name: 'capture_and_understand_window_advanced',\n description: '使用高级视觉模型(9B)截取指定窗口的截图并进行深度理解。提供更准确的内容识别和分析。输入参数为窗口句柄(HWND),如 \"0x12345\" 或 \"12345\"。',\n parameters: {\n type: 'object',\n properties: {\n hwnd: {\n type: 'string',\n description: '窗口句柄,支持 16 进制格式(如 \"0x12345\")或 10 进制格式(如 \"12345\")',\n },\n },\n required: ['hwnd'],\n },\n render: { call: 'capture', result: 'capture' },\n execute: async ({ hwnd }: { hwnd: string }) => {\n // 1. 尝试截图(与基础工具相同的逻辑)\n let captureResult = await captureWindow(hwnd, pythonPath, pythonArgs);\n\n let imageToAnalyze = captureResult.data;\n let imageSource = '新截图';\n let analysisNote = '';\n let usedCachedImage = false;\n\n // 2. 如果截图失败,尝试使用缓存\n if (captureResult.error || !captureResult.data) {\n if (cacheManager) {\n console.log(`[capture_and_understand_window_advanced] Screenshot failed for ${hwnd}, trying cache...`);\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = `[使用缓存图片,因为截图失败:${captureResult.error}]`;\n console.log(`[capture_and_understand_window_advanced] Using cached image for ${hwnd}`);\n } else {\n return `截图失败且无缓存可用:${captureResult.error ?? '未知错误'}`;\n }\n } else {\n return `截图失败:${captureResult.error ?? '未知错误'}`;\n }\n }\n\n // 3. 检查是否为纯色图\n if (imageToAnalyze && isSolidColorImage(imageToAnalyze)) {\n console.log(`[capture_and_understand_window_advanced] Detected solid color image for ${hwnd}, trying cache...`);\n if (cacheManager) {\n const cachedImage = getCachedImageBase64(cacheManager, hwnd);\n if (cachedImage) {\n imageToAnalyze = cachedImage;\n imageSource = '缓存图片';\n usedCachedImage = true;\n analysisNote = '[使用缓存图片,因为新截图是纯色图(窗口可能被最小化或遮挡)]';\n console.log(`[capture_and_understand_window_advanced] Using cached image for ${hwnd} (solid color detected)`);\n }\n }\n }\n\n // 4. 如果没有可用的图片\n if (!imageToAnalyze) {\n return '无法获取可用的图片进行分析';\n }\n\n // 5. 缓存去重:如果使用了缓存图片,检查是否已有该模型的分析结果\n if (usedCachedImage && cacheManager) {\n const cachedAnalysis = getCachedAnalysisForModel(cacheManager, hwnd, model);\n if (cachedAnalysis) {\n // 直接返回缓存的分析结果,不需要重新调用模型\n const age = Math.round((Date.now() - cachedAnalysis.createdAt) / 1000);\n const timeAgo = age < 60 ? `${age}秒前` : age < 3600 ? `${Math.round(age / 60)}分钟前` : `${Math.round(age / 3600)}小时前`;\n\n const parts = [\n `[高级模型] 使用缓存图片(来源:${imageSource})`,\n analysisNote || '',\n '',\n `内容分析(${timeAgo}的分析结果):`,\n cachedAnalysis.description,\n ].filter(Boolean);\n\n return parts.join('\\n');\n }\n }\n\n // 6. 获取窗口信息\n let windowInfo: { processName?: string; windowTitle?: string } | undefined;\n if (cacheManager) {\n const cached = getCachedWindowInfo(cacheManager, hwnd);\n if (cached) {\n windowInfo = {\n windowTitle: cached.title,\n };\n }\n }\n\n // 7. 获取上次的分析结果(用于增量分析)\n let previousAnalysis: string | undefined;\n if (cacheManager) {\n const cachedAnalysis = cacheManager.getAnalysis(hwnd);\n if (cachedAnalysis) {\n previousAnalysis = cachedAnalysis.description;\n }\n }\n\n // 8. 高级视觉理解\n try {\n const description = await understandImage(\n imageToAnalyze,\n client,\n model,\n windowInfo,\n previousAnalysis\n );\n\n const parts = [\n `[高级模型] 截图成功(${captureResult.width ?? '?'}x${captureResult.height ?? '?'},来源:${imageSource})`,\n ];\n\n if (analysisNote) {\n parts.push(analysisNote);\n }\n\n parts.push('');\n parts.push('内容分析:');\n parts.push(description);\n\n return parts.join('\\n');\n } catch (error) {\n return `视觉理解失败:${error}`;\n }\n },\n });\n}\n","/**\r\n * 工具定义\r\n * 简单的工具创建函数\r\n */\r\n\r\nimport type { Tool, ToolRenderConfig } from './types.js';\r\n\r\n/**\r\n * 渲染配置扩展类型\r\n * 支持字符串(模板名称)或配置对象\r\n */\r\nexport type ToolRenderInput = ToolRenderConfig | string;\r\n\r\n/**\r\n * 创建一个工具\r\n * @param config 工具配置\r\n * @param sourceFile 可选:调用此函数的源文件路径(用于自动查找渲染文件)\r\n */\r\nexport function createTool(\r\n config: {\r\n name: string;\r\n description: string;\r\n parameters?: Record<string, any>;\r\n execute: (args: any, context?: any) => Promise<any>;\r\n render?: ToolRenderInput;\r\n executionMode?: 'normal' | 'exclusive';\r\n parallelizable?: boolean;\r\n },\r\n sourceFile?: string\r\n): Tool {\r\n let finalRender: ToolRenderConfig | undefined = undefined;\r\n\r\n if (config.render) {\r\n // 如果 render 是字符串,转换为配置对象(call 和 result 使用同一模板)\r\n if (typeof config.render === 'string') {\r\n finalRender = {\r\n call: config.render,\r\n result: config.render,\r\n };\r\n } else {\r\n finalRender = config.render;\r\n }\r\n } else if (sourceFile) {\r\n // render 未定义时,尝试自动查找同目录下的 .render.ts 文件\r\n // 例如:tools/fs.ts -> tools/fs.render.ts\r\n const renderPath = sourceFile.replace(/\\.ts$/, '.render.ts');\r\n // 使用特殊标记,延迟加载\r\n (finalRender as any) = {\r\n __renderPath: renderPath,\r\n call: undefined,\r\n result: undefined,\r\n };\r\n }\r\n\r\n return {\r\n name: config.name,\r\n description: config.description,\r\n parameters: config.parameters,\r\n execute: config.execute,\r\n render: finalRender,\r\n ...(config.executionMode ? { executionMode: config.executionMode } : {}),\r\n ...(config.parallelizable ? { parallelizable: config.parallelizable } : {}),\r\n };\r\n}\r\n\r\n/**\r\n * 工具注册表 - 管理多个工具\r\n */\r\nexport class ToolRegistry {\r\n private tools = new Map<string, Tool>();\r\n private enabled = new Set<string>(); // 启用的工具名\r\n private disabled = new Set<string>(); // 禁用(屏蔽)的工具名\r\n private pendingDisabled = new Set<string>(); // 工具注册前的预禁用状态\r\n private pendingRemoved = new Set<string>(); // 工具注册前的预移除状态\r\n private sources = new Map<string, string>(); // 工具来源追踪\r\n private superseded = new Map<string, Array<{ tool: Tool; source?: string }>>(); // 被同名覆盖的旧条目\r\n\r\n /**\r\n * 注册工具(默认启用,记录来源)\r\n */\r\n register(tool: Tool, source?: string): this {\r\n // 追踪被覆盖的旧条目\r\n if (this.tools.has(tool.name)) {\r\n if (!this.superseded.has(tool.name)) {\r\n this.superseded.set(tool.name, []);\r\n }\r\n this.superseded.get(tool.name)!.push({\r\n tool: this.tools.get(tool.name)!,\r\n source: this.sources.get(tool.name),\r\n });\r\n }\r\n\r\n this.tools.set(tool.name, tool);\r\n if (this.pendingRemoved.has(tool.name)) {\r\n this.enabled.delete(tool.name);\r\n this.disabled.delete(tool.name);\r\n } else if (this.pendingDisabled.has(tool.name)) {\r\n this.enabled.delete(tool.name);\r\n this.disabled.add(tool.name);\r\n } else {\r\n this.enabled.add(tool.name); // 默认启用\r\n this.disabled.delete(tool.name);\r\n }\r\n if (source) {\r\n this.sources.set(tool.name, source);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * 禁用工具(LLM 可见,但执行时会被拦截)\r\n */\r\n disable(name: string): boolean {\r\n this.pendingDisabled.add(name);\r\n this.pendingRemoved.delete(name);\r\n if (!this.tools.has(name)) {\r\n return true;\r\n }\r\n this.enabled.delete(name);\r\n this.disabled.add(name);\r\n return true;\r\n }\r\n\r\n /**\r\n * 启用工具\r\n */\r\n enable(name: string): boolean {\r\n this.pendingDisabled.delete(name);\r\n this.pendingRemoved.delete(name);\r\n this.disabled.delete(name);\r\n if (this.tools.has(name)) {\r\n this.enabled.add(name);\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * 移除工具(LLM 不可见,承接旧 disable 行为)\r\n */\r\n remove(name: string): boolean {\r\n this.pendingRemoved.add(name);\r\n this.pendingDisabled.delete(name);\r\n this.disabled.delete(name);\r\n if (!this.tools.has(name)) {\r\n return true;\r\n }\r\n this.enabled.delete(name);\r\n return true;\r\n }\r\n\r\n /**\r\n * 取消移除工具,恢复为启用状态\r\n */\r\n unremove(name: string): boolean {\r\n return this.enable(name);\r\n }\r\n\r\n /**\r\n * 检查工具是否启用\r\n */\r\n isEnabled(name: string): boolean {\r\n return this.enabled.has(name);\r\n }\r\n\r\n /**\r\n * 检查工具是否禁用(屏蔽)\r\n */\r\n isDisabled(name: string): boolean {\r\n return this.disabled.has(name);\r\n }\r\n\r\n /**\r\n * 检查工具是否移除\r\n */\r\n isRemoved(name: string): boolean {\r\n return this.tools.has(name) && !this.enabled.has(name) && !this.disabled.has(name);\r\n }\r\n\r\n /**\r\n * 检查工具是否为 exclusive 模式\r\n */\r\n isExclusive(name: string): boolean {\r\n const tool = this.tools.get(name);\r\n return tool?.executionMode === 'exclusive';\r\n }\r\n\r\n /**\r\n * 检查工具是否可并行执行\r\n */\r\n isParallelizable(name: string): boolean {\r\n const tool = this.tools.get(name);\r\n return tool?.parallelizable === true && tool?.executionMode !== 'exclusive';\r\n }\r\n\r\n /**\r\n * 获取工具来源(调试用)\r\n */\r\n getSource(name: string): string | undefined {\r\n return this.sources.get(name);\r\n }\r\n\r\n /**\r\n * 获取工具条目(调试快照用)\r\n */\r\n getEntries(): Array<{ tool: Tool; state: 'enabled' | 'disabled' | 'removed' | 'superseded'; enabled: boolean; source?: string }> {\r\n const entries: Array<{ tool: Tool; state: 'enabled' | 'disabled' | 'removed' | 'superseded'; enabled: boolean; source?: string }> =\r\n Array.from(this.tools.entries()).map(([name, tool]) => ({\r\n tool,\r\n state: this.enabled.has(name) ? 'enabled' : this.disabled.has(name) ? 'disabled' : 'removed',\r\n enabled: this.enabled.has(name),\r\n source: this.sources.get(name),\r\n }));\r\n\r\n // 追加被同名覆盖的旧条目\r\n for (const [, list] of this.superseded) {\r\n for (const { tool, source } of list) {\r\n entries.push({ tool, state: 'superseded', enabled: false, source });\r\n }\r\n }\r\n\r\n return entries;\r\n }\r\n\r\n /**\r\n * 获取工具\r\n */\r\n get(name: string): Tool | undefined {\r\n return this.tools.get(name);\r\n }\r\n\r\n /**\r\n * 获取所有 LLM 可见工具(启用 + 禁用)\r\n */\r\n getAll(): Tool[] {\r\n return Array.from(new Set([...this.enabled, ...this.disabled]))\r\n .map(name => this.tools.get(name))\r\n .filter((t): t is Tool => t !== undefined);\r\n }\r\n\r\n /**\r\n * 检查工具是否存在\r\n */\r\n has(name: string): boolean {\r\n return this.tools.has(name);\r\n }\r\n\r\n /**\r\n * 获取工具的渲染配置\r\n */\r\n getRenderConfig(name: string): ToolRenderConfig | undefined {\r\n const tool = this.tools.get(name);\r\n return tool?.render;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -7,11 +7,21 @@ import {
|
|
|
7
7
|
createLLM,
|
|
8
8
|
getDefaultMCPConfigDir,
|
|
9
9
|
loadConfigSync
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XRB6MD2J.js";
|
|
11
11
|
|
|
12
12
|
// src/agents/system/BasicAgent.ts
|
|
13
13
|
import { existsSync } from "fs";
|
|
14
|
+
import { join } from "path";
|
|
14
15
|
import { cwd, platform } from "process";
|
|
16
|
+
function hasMeaningfulMcpFeatureConfig(value) {
|
|
17
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const config = value;
|
|
21
|
+
const scanAgentdevDir = config.scanAgentdevDir;
|
|
22
|
+
const extraConfigFiles = Array.isArray(config.extraConfigFiles) ? config.extraConfigFiles.map((item) => String(item || "").trim()).filter(Boolean) : [];
|
|
23
|
+
return scanAgentdevDir === false || extraConfigFiles.length > 0;
|
|
24
|
+
}
|
|
15
25
|
var BasicAgent = class _BasicAgent extends AgentBase {
|
|
16
26
|
_systemContext;
|
|
17
27
|
_mcpServer;
|
|
@@ -51,7 +61,8 @@ var BasicAgent = class _BasicAgent extends AgentBase {
|
|
|
51
61
|
systemMessage: config.systemMessage,
|
|
52
62
|
name: config.name,
|
|
53
63
|
projectRoot: config.projectRoot,
|
|
54
|
-
workspaceDir
|
|
64
|
+
workspaceDir,
|
|
65
|
+
features: config.features
|
|
55
66
|
};
|
|
56
67
|
super(agentConfig);
|
|
57
68
|
this._systemContext = systemContext;
|
|
@@ -61,7 +72,8 @@ var BasicAgent = class _BasicAgent extends AgentBase {
|
|
|
61
72
|
this._skillsDir = config.skillsDir;
|
|
62
73
|
this.setSystemContext(systemContext);
|
|
63
74
|
const hasDefaultMCPConfigs = existsSync(getDefaultMCPConfigDir());
|
|
64
|
-
const
|
|
75
|
+
const hasFeatureScopedMcpConfig = hasMeaningfulMcpFeatureConfig(config.features?.mcp);
|
|
76
|
+
const shouldEnableMCP = config.mcpServer !== false && (typeof config.mcpServer === "string" || hasDefaultMCPConfigs || hasFeatureScopedMcpConfig);
|
|
65
77
|
if (shouldEnableMCP) {
|
|
66
78
|
this._mcpFeature = typeof config.mcpServer === "string" ? new MCPFeature(config.mcpServer) : new MCPFeature(void 0, { excludeServers: config.excludeMcpServers });
|
|
67
79
|
if (config.mcpContext) {
|
|
@@ -70,12 +82,13 @@ var BasicAgent = class _BasicAgent extends AgentBase {
|
|
|
70
82
|
this.use(this._mcpFeature);
|
|
71
83
|
}
|
|
72
84
|
this.use(new OpencodeBasicFeature({ workspaceDir }));
|
|
73
|
-
|
|
85
|
+
const skillInput = config.skillConfig || config.skillsDir || join(workspaceDir, ".agentdev", "skills");
|
|
86
|
+
this.use(new SkillFeature(skillInput));
|
|
74
87
|
this.use(new SubAgentFeature());
|
|
75
|
-
this.getTools().
|
|
76
|
-
this.getTools().
|
|
88
|
+
this.getTools().remove("list_agents");
|
|
89
|
+
this.getTools().remove("close_agent");
|
|
77
90
|
this.registerAgentType("BasicAgent", () => new _BasicAgent({ llm: this.llm }));
|
|
78
|
-
this.registerAgentType("ExplorerAgent", () => import("./ExplorerAgent-
|
|
91
|
+
this.registerAgentType("ExplorerAgent", () => import("./ExplorerAgent-L3ZTVQGM.js").then((m) => new m.ExplorerAgent({ llm: this.llm })));
|
|
79
92
|
}
|
|
80
93
|
/**
|
|
81
94
|
* 获取系统环境信息
|
|
@@ -100,4 +113,4 @@ var BasicAgent = class _BasicAgent extends AgentBase {
|
|
|
100
113
|
export {
|
|
101
114
|
BasicAgent
|
|
102
115
|
};
|
|
103
|
-
//# sourceMappingURL=chunk-
|
|
116
|
+
//# sourceMappingURL=chunk-KE3KYZVJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agents/system/BasicAgent.ts"],"sourcesContent":["/**\r\n * BasicAgent - 基础 Agent 类\r\n *\r\n * 集成了常用 Feature 和系统环境信息\r\n * 适用于大多数 Agent 场景\r\n *\r\n * 默认自动加载配置文件,开箱即用\r\n */\r\n\r\nimport { Agent } from '../../core/agent.js';\r\nimport { MCPFeature, SkillFeature, SubAgentFeature, OpencodeBasicFeature } from '../../features/index.js';\r\nimport type { SkillFeatureConfig } from '../../features/index.js';\r\nimport type { AgentConfig, LLMClient, Tool } from '../../core/types.js';\r\nimport type { AgentConfigFile } from '../../core/config.js';\r\nimport { loadConfigSync } from '../../core/config.js';\r\nimport { createLLM } from '../../llm/index.js';\r\nimport { existsSync } from 'fs';\r\nimport { join } from 'path';\r\nimport { cwd, platform } from 'process';\r\nimport { getDefaultMCPConfigDir } from '../../mcp/config.js';\r\n\r\nfunction hasMeaningfulMcpFeatureConfig(value: unknown): boolean {\r\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\r\n return false;\r\n }\r\n const config = value as Record<string, unknown>;\r\n const scanAgentdevDir = config.scanAgentdevDir;\r\n const extraConfigFiles = Array.isArray(config.extraConfigFiles)\r\n ? config.extraConfigFiles.map((item) => String(item || '').trim()).filter(Boolean)\r\n : [];\r\n return scanAgentdevDir === false || extraConfigFiles.length > 0;\r\n}\r\n\r\n/**\r\n * 系统环境信息上下文\r\n */\r\nexport interface SystemContext {\r\n /** 当前工作目录 */\r\n SYSTEM_WORKING_DIR: string;\r\n /** 是否是 Git 仓库 */\r\n SYSTEM_IS_GIT_REPOSITORY: boolean;\r\n /** 操作系统平台 */\r\n SYSTEM_PLATFORM: NodeJS.Platform;\r\n /** 当前日期 (YYYY-MM-DD) */\r\n SYSTEM_DATE: string;\r\n /** 当前使用的模型名称 */\r\n SYSTEM_CURRENT_MODEL: string;\r\n /** 索引签名,允许作为 PlaceholderContext 使用 */\r\n [key: string]: any;\r\n}\r\n\r\n/**\r\n * BasicAgent 配置选项\r\n *\r\n * 所有参数都是可选的,默认会自动同步加载配置文件\r\n */\r\nexport interface BasicAgentConfig {\r\n /** LLM 客户端(可选,不传则自动同步加载配置创建) */\r\n llm?: LLMClient;\r\n /** 配置文件名(可选,默认 'default') */\r\n configName?: string;\r\n /** Agent 显示名称(可选) */\r\n name?: string;\r\n /** 系统提示词(可选,后续可通过 setPrompt() 设置) */\r\n systemMessage?: string;\r\n /** MCP 配置:传字符串时加载指定配置;传 false 时禁用自动加载;不传时若 .agentdev/mcps 存在则自动加载全部 */\r\n mcpServer?: string | false;\r\n /** MCP 运行时上下文(可选,如 GitHub Token) */\r\n mcpContext?: Record<string, unknown>;\r\n /** 自动扫描 MCP 时排除的 serverId 列表 */\r\n excludeMcpServers?: string[];\r\n /** 自定义工具集(可选,默认使用 Feature 提供的工具) */\r\n tools?: Tool[];\r\n /** Skills 目录(可选,默认使用 .agentdev/skills) */\r\n skillsDir?: string;\r\n /** SkillFeature 完整配置(可选,覆盖 skillsDir) */\r\n skillConfig?: SkillFeatureConfig;\r\n /** Feature 特定配置(可选) */\r\n features?: AgentConfig['features'];\r\n /** 调试器和模板解析使用的项目根目录 */\r\n projectRoot?: string;\r\n /** 工具默认操作的工作目录 */\r\n workspaceDir?: string;\r\n}\r\n\r\n/**\r\n * 基础 Agent 类\r\n *\r\n * 集成常用 Feature 和系统环境信息,开箱即用\r\n * 构造函数不传任何参数时,会自动同步加载配置文件创建 LLM\r\n */\r\nexport class BasicAgent extends Agent {\r\n protected _systemContext: SystemContext;\r\n protected _mcpServer?: string | false;\r\n protected _mcpContext?: Record<string, unknown>;\r\n protected _config?: AgentConfigFile;\r\n protected _skillsDir?: string;\r\n protected _mcpFeature?: MCPFeature;\r\n\r\n /**\r\n * 构造函数\r\n *\r\n * @param config 基础配置(全部可选,不传则使用默认配置)\r\n */\r\n constructor(config: BasicAgentConfig = {}) {\r\n const workspaceDir = config.workspaceDir ?? cwd();\r\n // 建立系统环境信息\r\n const systemContext: SystemContext = {\r\n SYSTEM_WORKING_DIR: workspaceDir,\r\n SYSTEM_IS_GIT_REPOSITORY: existsSync(workspaceDir + '/.git'),\r\n SYSTEM_PLATFORM: platform,\r\n SYSTEM_DATE: new Date().toISOString().split('T')[0], // YYYY-MM-DD\r\n SYSTEM_CURRENT_MODEL: 'unknown', // 稍后更新\r\n };\r\n\r\n // 准备 LLM:如果没传入,同步加载配置\r\n let llm = config.llm;\r\n let fileConfig: AgentConfigFile | undefined;\r\n if (!llm) {\r\n const configName = config.configName ?? 'default';\r\n fileConfig = loadConfigSync(configName);\r\n llm = createLLM(fileConfig);\r\n systemContext.SYSTEM_CURRENT_MODEL = fileConfig.defaultModel.model;\r\n console.log(`[BasicAgent] 已加载配置: ${configName}, 模型: ${fileConfig.defaultModel.model}`);\r\n }\r\n\r\n // 构建完整的 Agent 配置\r\n const agentConfig: AgentConfig = {\r\n llm: llm!,\r\n tools: config.tools ?? [],\r\n maxTurns: Infinity,\r\n systemMessage: config.systemMessage,\r\n name: config.name,\r\n projectRoot: config.projectRoot,\r\n workspaceDir,\r\n features: config.features,\r\n };\r\n\r\n super(agentConfig);\r\n\r\n // 保存配置(必须在 super() 之后)\r\n this._systemContext = systemContext;\r\n this._config = fileConfig;\r\n this._mcpServer = config.mcpServer;\r\n this._mcpContext = config.mcpContext;\r\n this._skillsDir = config.skillsDir;\r\n this.setSystemContext(systemContext);\r\n\r\n const hasDefaultMCPConfigs = existsSync(getDefaultMCPConfigDir());\r\n const hasFeatureScopedMcpConfig = hasMeaningfulMcpFeatureConfig(config.features?.mcp);\r\n const shouldEnableMCP = config.mcpServer !== false && (typeof config.mcpServer === 'string' || hasDefaultMCPConfigs || hasFeatureScopedMcpConfig);\r\n if (shouldEnableMCP) {\r\n this._mcpFeature = typeof config.mcpServer === 'string'\r\n ? new MCPFeature(config.mcpServer)\r\n : new MCPFeature(undefined, { excludeServers: config.excludeMcpServers });\r\n if (config.mcpContext) {\r\n this._mcpFeature.setMCPContext(config.mcpContext);\r\n }\r\n this.use(this._mcpFeature);\r\n }\r\n\r\n // 注册 OpencodeBasicFeature(文件操作工具集)\r\n this.use(new OpencodeBasicFeature({ workspaceDir }));\r\n\r\n // 注册 SkillFeature(invokeSkill 工具和 skills 上下文注入)\r\n const skillInput = config.skillConfig || config.skillsDir || join(workspaceDir, '.agentdev', 'skills');\r\n this.use(new SkillFeature(skillInput));\r\n\r\n // 注册 SubAgentFeature(子代理工具和消息处理)\r\n this.use(new SubAgentFeature());\r\n\r\n // 预禁用不需要的子代理工具,确保首次快照与运行时一致\r\n this.getTools().remove('list_agents');\r\n this.getTools().remove('close_agent');\r\n\r\n // 注册可创建的子代理类型\r\n this.registerAgentType('BasicAgent', () => new BasicAgent({ llm: this.llm }));\r\n this.registerAgentType('ExplorerAgent', () => import('./ExplorerAgent.js').then(m => new m.ExplorerAgent({ llm: this.llm })));\r\n }\r\n\r\n /**\r\n * 获取系统环境信息\r\n */\r\n getSystemContext(): SystemContext {\r\n return this._systemContext;\r\n }\r\n\r\n /**\r\n * 获取 MCP 服务器配置\r\n */\r\n getMcpServer(): string | false | undefined {\r\n return this._mcpServer;\r\n }\r\n\r\n /**\r\n * 获取 MCP 上下文\r\n */\r\n getMcpContext(): Record<string, unknown> | undefined {\r\n return this._mcpContext;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|