nolo-cli 0.1.7 → 0.1.9

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.
Files changed (247) hide show
  1. package/README.md +107 -5
  2. package/agentRuntimeCommands.ts +464 -0
  3. package/ai/agent/_executeModel.ts +118 -0
  4. package/ai/agent/agentSlice.ts +525 -0
  5. package/ai/agent/appWorkingMemory.ts +126 -0
  6. package/ai/agent/avatarUtils.ts +24 -0
  7. package/ai/agent/buildEditingContext.ts +373 -0
  8. package/ai/agent/buildSystemPrompt.ts +532 -0
  9. package/ai/agent/cleanAgentMessages.ts +140 -0
  10. package/ai/agent/cliChatClient.ts +119 -0
  11. package/ai/agent/cliExecutor.ts +733 -0
  12. package/ai/agent/cliPrompt.ts +10 -0
  13. package/ai/agent/contextCompiler.ts +107 -0
  14. package/ai/agent/contextLayerContract.ts +44 -0
  15. package/ai/agent/createAgentSchema.ts +234 -0
  16. package/ai/agent/executeToolCall.ts +58 -0
  17. package/ai/agent/fetchAgentContexts.ts +42 -0
  18. package/ai/agent/generatePrompt.ts +3 -0
  19. package/ai/agent/getFullChatContextKeys.ts +168 -0
  20. package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
  21. package/ai/agent/hooks/useAgentConfig.ts +61 -0
  22. package/ai/agent/hooks/useAgentDialog.ts +35 -0
  23. package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
  24. package/ai/agent/hooks/usePublicAgents.ts +473 -0
  25. package/ai/agent/machineRunPermissions.ts +95 -0
  26. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  27. package/ai/agent/planSlice.ts +259 -0
  28. package/ai/agent/referenceUtils.ts +229 -0
  29. package/ai/agent/runAgentBackground.ts +238 -0
  30. package/ai/agent/runAgentClientLoop.ts +138 -0
  31. package/ai/agent/runtimeGuidance.ts +97 -0
  32. package/ai/agent/runtimeServerBase.ts +37 -0
  33. package/ai/agent/server/fetchPublicAgents.ts +128 -0
  34. package/ai/agent/startParallelAgentStreams.ts +424 -0
  35. package/ai/agent/startupProtocol.ts +53 -0
  36. package/ai/agent/streamAgentChatTurn.ts +1278 -0
  37. package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
  38. package/ai/agent/types.ts +71 -0
  39. package/ai/agent/utils/imageOutput.ts +33 -0
  40. package/ai/agent/utils/sortUtils.ts +250 -0
  41. package/ai/agent/web/referencePickerUtils.ts +146 -0
  42. package/ai/ai.locale.ts +1075 -0
  43. package/ai/chat/accumulateToolCallChunks.ts +95 -0
  44. package/ai/chat/fetchUtils.native.ts +276 -0
  45. package/ai/chat/fetchUtils.ts +153 -0
  46. package/ai/chat/parseApiError.ts +64 -0
  47. package/ai/chat/parseMultilineSSE.ts +95 -0
  48. package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
  49. package/ai/chat/sendOpenAICompletionsRequest.ts +703 -0
  50. package/ai/chat/sendOpenAIResponseRequest.ts +491 -0
  51. package/ai/chat/shouldUseServerProxy.ts +18 -0
  52. package/ai/chat/sseClient.native.ts +91 -0
  53. package/ai/chat/sseClient.ts +67 -0
  54. package/ai/chat/streamReader.native.ts +31 -0
  55. package/ai/chat/streamReader.ts +62 -0
  56. package/ai/chat/updateTotalUsage.ts +72 -0
  57. package/ai/context/buildReferenceContext.ts +437 -0
  58. package/ai/context/calculateContextUsage.ts +133 -0
  59. package/ai/context/retention.ts +165 -0
  60. package/ai/context/tokenUtils.ts +78 -0
  61. package/ai/index.ts +1 -0
  62. package/ai/llm/calculateGeminiImageTokens.ts +57 -0
  63. package/ai/llm/deepinfra.ts +28 -0
  64. package/ai/llm/fireworks.ts +50 -0
  65. package/ai/llm/generateRequestBody.ts +165 -0
  66. package/ai/llm/getModelContextWindow.ts +84 -0
  67. package/ai/llm/getNoloKey.ts +31 -0
  68. package/ai/llm/getPricing.ts +199 -0
  69. package/ai/llm/hooks/useModelPricing.ts +75 -0
  70. package/ai/llm/imagePricing.ts +40 -0
  71. package/ai/llm/isResponseAPIModel.ts +13 -0
  72. package/ai/llm/mimo.ts +71 -0
  73. package/ai/llm/mistral.ts +22 -0
  74. package/ai/llm/modelAvatar.ts +427 -0
  75. package/ai/llm/models.ts +45 -0
  76. package/ai/llm/openrouterModels.ts +269 -0
  77. package/ai/llm/providers.ts +306 -0
  78. package/ai/llm/reasoningModels.ts +28 -0
  79. package/ai/llm/types.ts +59 -0
  80. package/ai/llm/usageRequestOptions.ts +59 -0
  81. package/ai/memory/capture.ts +148 -0
  82. package/ai/memory/consolidate.ts +104 -0
  83. package/ai/memory/delete.ts +147 -0
  84. package/ai/memory/overlay.ts +84 -0
  85. package/ai/memory/query.ts +38 -0
  86. package/ai/memory/queryShared.ts +160 -0
  87. package/ai/memory/rank.ts +105 -0
  88. package/ai/memory/recentRelationshipRecap.ts +249 -0
  89. package/ai/memory/remember.ts +167 -0
  90. package/ai/memory/runtime.ts +76 -0
  91. package/ai/memory/store.ts +20 -0
  92. package/ai/memory/storeShared.ts +76 -0
  93. package/ai/memory/types.ts +46 -0
  94. package/ai/memory/understanding.ts +349 -0
  95. package/ai/memory/understandingGreeting.ts +264 -0
  96. package/ai/messages/type.ts +20 -0
  97. package/ai/policy/personalizationDialog.ts +333 -0
  98. package/ai/policy/runtimePolicy.ts +440 -0
  99. package/ai/policy/selfUpdateFields.ts +48 -0
  100. package/ai/policy/types.ts +64 -0
  101. package/ai/skills/referenceRuntime.ts +274 -0
  102. package/ai/skills/skillDiagnostics.ts +251 -0
  103. package/ai/skills/skillDocBuilder.ts +139 -0
  104. package/ai/skills/skillDocProtocol.ts +434 -0
  105. package/ai/skills/skillReferenceSummary.ts +63 -0
  106. package/ai/skills/skillSummaryMarker.ts +26 -0
  107. package/ai/token/calculatePrice.ts +544 -0
  108. package/ai/token/db.ts +98 -0
  109. package/ai/token/externalToolCost.ts +330 -0
  110. package/ai/token/hooks/useRecords.ts +65 -0
  111. package/ai/token/missingUsageEstimate.ts +42 -0
  112. package/ai/token/modelUsageQuery.ts +252 -0
  113. package/ai/token/normalizeUsage.ts +84 -0
  114. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  115. package/ai/token/prepareTokenUsageData.ts +88 -0
  116. package/ai/token/query.ts +88 -0
  117. package/ai/token/queryUserTokens.ts +59 -0
  118. package/ai/token/resolveBillingTarget.ts +52 -0
  119. package/ai/token/saveTokenRecord.ts +53 -0
  120. package/ai/token/serverDialogProjection.ts +78 -0
  121. package/ai/token/serverTokenWriter.ts +143 -0
  122. package/ai/token/stats.ts +21 -0
  123. package/ai/token/tokenThunks.ts +24 -0
  124. package/ai/token/types.ts +93 -0
  125. package/ai/tools/agent/agentTools.ts +176 -0
  126. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  127. package/ai/tools/agent/callAgentTool.ts +139 -0
  128. package/ai/tools/agent/createAgentTool.ts +512 -0
  129. package/ai/tools/agent/createDialogTool.ts +69 -0
  130. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  131. package/ai/tools/agent/parallelBudget.ts +221 -0
  132. package/ai/tools/agent/presets/appBuilderPreset.ts +145 -0
  133. package/ai/tools/agent/runLlmTool.ts +96 -0
  134. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  135. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  136. package/ai/tools/agent/skillAgentPreset.ts +89 -0
  137. package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
  138. package/ai/tools/agent/updateAgentTool.ts +96 -0
  139. package/ai/tools/agent/updateSelfTool.ts +113 -0
  140. package/ai/tools/amazonProductScraperTool.ts +86 -0
  141. package/ai/tools/apifyActorClient.ts +45 -0
  142. package/ai/tools/appEditGuard.ts +372 -0
  143. package/ai/tools/appReadSnapshot.ts +153 -0
  144. package/ai/tools/appTools.ts +1549 -0
  145. package/ai/tools/applyEditTool.ts +256 -0
  146. package/ai/tools/applyLineEditsTool.ts +312 -0
  147. package/ai/tools/browserTools/click.ts +33 -0
  148. package/ai/tools/browserTools/closeSession.ts +29 -0
  149. package/ai/tools/browserTools/common.ts +27 -0
  150. package/ai/tools/browserTools/openSession.ts +48 -0
  151. package/ai/tools/browserTools/readContent.ts +38 -0
  152. package/ai/tools/browserTools/selectOption.ts +46 -0
  153. package/ai/tools/browserTools/typeText.ts +42 -0
  154. package/ai/tools/category/createCategoryTool.ts +66 -0
  155. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  156. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  157. package/ai/tools/cfBrowserTools.ts +319 -0
  158. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  159. package/ai/tools/checkEnvTool.ts +65 -0
  160. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  161. package/ai/tools/codeSearchTool.ts +111 -0
  162. package/ai/tools/codeTools.ts +101 -0
  163. package/ai/tools/createDocTool.ts +132 -0
  164. package/ai/tools/createPlanTool.ts +999 -0
  165. package/ai/tools/createSkillDocTool.ts +155 -0
  166. package/ai/tools/createWorkflowTool.ts +154 -0
  167. package/ai/tools/deepseekOcrTool.ts +34 -0
  168. package/ai/tools/delayTool.ts +31 -0
  169. package/ai/tools/deleteSpacesTool.ts +325 -0
  170. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  171. package/ai/tools/devReloadUtils.ts +29 -0
  172. package/ai/tools/dialogMessageSearch.ts +137 -0
  173. package/ai/tools/doctorSkillTool.ts +72 -0
  174. package/ai/tools/ecommerceScraperTool.ts +86 -0
  175. package/ai/tools/emailTools.ts +549 -0
  176. package/ai/tools/evalSkillTool.ts +92 -0
  177. package/ai/tools/exaSearchTool.ts +64 -0
  178. package/ai/tools/execBashTool.ts +379 -0
  179. package/ai/tools/executeSqlTool.ts +192 -0
  180. package/ai/tools/fetchWebpageSupport.ts +309 -0
  181. package/ai/tools/fetchWebpageTool.ts +84 -0
  182. package/ai/tools/geminiImagePreviewTool.ts +361 -0
  183. package/ai/tools/generateDocxTool.ts +215 -0
  184. package/ai/tools/googleSearchScraperTool.ts +106 -0
  185. package/ai/tools/importDataTool.ts +133 -0
  186. package/ai/tools/importSkillTool.ts +162 -0
  187. package/ai/tools/index.ts +1858 -0
  188. package/ai/tools/listFilesTool.ts +82 -0
  189. package/ai/tools/listUserSpacesTool.ts +113 -0
  190. package/ai/tools/modelUsageTools.ts +142 -0
  191. package/ai/tools/olmOcrTool.ts +34 -0
  192. package/ai/tools/openaiImageTool.ts +218 -0
  193. package/ai/tools/paddleOcrTool.ts +34 -0
  194. package/ai/tools/prepareTools.ts +23 -0
  195. package/ai/tools/readDocTool.ts +84 -0
  196. package/ai/tools/readFileTool.ts +211 -0
  197. package/ai/tools/readTool.ts +163 -0
  198. package/ai/tools/readXPostTool.ts +233 -0
  199. package/ai/tools/rememberMemoryTool.ts +84 -0
  200. package/ai/tools/remotionVideoTool.ts +151 -0
  201. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  202. package/ai/tools/searchRepoTool.ts +115 -0
  203. package/ai/tools/searchWorkspaceTool.ts +259 -0
  204. package/ai/tools/skillFollowup.ts +86 -0
  205. package/ai/tools/surfWeatherTool.ts +169 -0
  206. package/ai/tools/table/addTableRowTool.ts +217 -0
  207. package/ai/tools/table/createTableTool.ts +315 -0
  208. package/ai/tools/table/rowTools.ts +366 -0
  209. package/ai/tools/table/schemaTools.ts +244 -0
  210. package/ai/tools/table/shareTableTool.ts +148 -0
  211. package/ai/tools/table/toolShared.ts +129 -0
  212. package/ai/tools/toolApiClient.ts +198 -0
  213. package/ai/tools/toolNameAliases.ts +57 -0
  214. package/ai/tools/toolResultError.ts +42 -0
  215. package/ai/tools/toolRunSlice.ts +303 -0
  216. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  217. package/ai/tools/toolVisibility.ts +4 -0
  218. package/ai/tools/types.ts +20 -0
  219. package/ai/tools/uiAskChoiceTool.ts +104 -0
  220. package/ai/tools/updateContentTitleTool.ts +84 -0
  221. package/ai/tools/updateDocTool.ts +105 -0
  222. package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
  223. package/ai/tools/whisperTool.ts +77 -0
  224. package/ai/tools/writeFileTool.ts +210 -0
  225. package/ai/tools/youtubeScraperTool.ts +116 -0
  226. package/ai/tools/ziweiChartTool.ts +678 -0
  227. package/ai/types.ts +55 -0
  228. package/ai/workflow/workflowExecutor.ts +323 -0
  229. package/ai/workflow/workflowSlice.ts +73 -0
  230. package/ai/workflow/workflowTypes.ts +106 -0
  231. package/client/agentRun.ts +198 -167
  232. package/client/compactDialog.ts +222 -0
  233. package/commandRegistry.ts +14 -0
  234. package/connector-experimental/capabilities.ts +73 -0
  235. package/connector-experimental/codexBinary.ts +41 -0
  236. package/connector-experimental/heartbeatLoop.ts +22 -0
  237. package/connector-experimental/index.ts +5 -0
  238. package/connector-experimental/machineInfo.ts +46 -0
  239. package/connector-experimental/protocol.ts +54 -0
  240. package/connectorWebSocketTarget.ts +29 -0
  241. package/defaultServer.ts +1 -0
  242. package/index.ts +158 -104
  243. package/machineCommands.ts +382 -0
  244. package/package.json +12 -2
  245. package/tui/readlineWorkspace.ts +50 -0
  246. package/tui/session.ts +40 -2
  247. package/updateCommands.ts +70 -5
@@ -0,0 +1,999 @@
1
+ // 文件路径: ai/tools/createPlanTool.ts
2
+ // 完整的 createPlan 工具,支持普通步骤和反思步骤(reflect step)
3
+
4
+ import { createAsyncThunk } from "@reduxjs/toolkit";
5
+ import {
6
+ setPlan,
7
+ setSteps,
8
+ updateStep,
9
+ setCurrentStep,
10
+ selectSteps,
11
+ insertStepsAfter,
12
+ removeStepsAfter,
13
+ setExecutionConfig,
14
+ incrementReflectCount,
15
+ incrementStepsExecuted,
16
+ setStartTime,
17
+ selectPlanStats,
18
+ selectExecutionConfig,
19
+ type Step,
20
+ type ToolCall,
21
+ type ReflectDecision,
22
+ type PlanExecutionConfig,
23
+ } from "ai/agent/planSlice";
24
+ // ✅ 不再直接在 Plan 里使用 runLlm / runAgent,统一走 toolExecutors
25
+ // import { runLlm, runAgent } from "ai/agent/agentSlice";
26
+ import { toolExecutors, toolDefinitionsByName } from "ai/tools";
27
+ import {
28
+ toolRunStarted,
29
+ toolRunSucceeded,
30
+ toolRunFailed,
31
+ createToolRunId,
32
+ } from "ai/tools/toolRunSlice";
33
+ import type { RootState } from "app/store";
34
+ import { selectCurrentDialogConfig } from "chat/dialog/dialogSlice";
35
+ import { messageStreamEnd, messageStreaming } from "chat/messages/messageSlice";
36
+ import { extractCustomId } from "core/prefix";
37
+ import { createDialogMessageKeyAndId } from "database/keys";
38
+ import { callAgentFunc } from "./agent/callAgentTool";
39
+ const PLAN_EXECUTOR_CYBOT_KEY = "PLAN_EXECUTOR";
40
+ const PLAN_EXECUTOR_AGENT_CONFIG = { dbKey: PLAN_EXECUTOR_CYBOT_KEY } as any;
41
+
42
+ // --- Schema ---
43
+
44
+ export const createPlanFunctionSchema = {
45
+ name: "createPlan",
46
+ description:
47
+ "用于处理需要多个工具协作、有顺序依赖或可并行执行的复杂任务。当你发现一个请求无法通过单次工具调用完成时,应优先使用本工具来制定并执行一个清晰、可靠的多步骤计划。" +
48
+ "在 Plan 中,你可以调用任意已注册的工具(例如 youtubeScraper, addTableRow, callAgent, runStreamingAgent 等)," +
49
+ "并通过 steps.STEP_ID.result 在步骤之间传递数据。",
50
+ parameters: {
51
+ type: "object",
52
+ properties: {
53
+ planTitle: {
54
+ type: "string",
55
+ description: "为整个计划的目标设定一个简明扼要的标题。",
56
+ },
57
+ strategy: {
58
+ type: "string",
59
+ description:
60
+ "[至关重要] 详细阐述制定此计划的整体策略和思考过程。解释为什么选择这些步骤、它们如何协同工作、以及数据如何在步骤间传递,以最终达成目标。",
61
+ },
62
+ steps: {
63
+ type: "array",
64
+ description:
65
+ "一个有序的步骤数组。每个步骤可以是普通工具调用步骤(type: normal)或反思决策步骤(type: reflect)。",
66
+ items: {
67
+ type: "object",
68
+ properties: {
69
+ id: {
70
+ type: "string",
71
+ description:
72
+ "步骤的唯一标识符 (例如 'fetch_data', 'step_2', 'reflect_1')。此ID用于在后续步骤中引用本步骤的执行结果。",
73
+ },
74
+ title: {
75
+ type: "string",
76
+ description: "对该步骤目标的简短、人类可读的描述。",
77
+ },
78
+ type: {
79
+ type: "string",
80
+ enum: ["normal", "reflect"],
81
+ description:
82
+ "步骤类型。'normal' 是普通工具调用步骤,'reflect' 是反思决策步骤,用于根据前面步骤的结果动态调整计划。默认为 'normal'。",
83
+ },
84
+ calls: {
85
+ type: "array",
86
+ description:
87
+ "(仅 type='normal' 时使用)一个或多个工具调用的数组,它们将在本步骤内并行执行。",
88
+ items: {
89
+ type: "object",
90
+ properties: {
91
+ tool_name: {
92
+ type: "string",
93
+ description:
94
+ "要调用的工具名称(必须是系统中已注册的工具之一,例如 'youtubeScraper', 'addTableRow', 'callAgent', 'runStreamingAgent' 等)。",
95
+ },
96
+ parameters: {
97
+ type: "object",
98
+ description:
99
+ "调用工具所需的参数对象。要引用先前步骤的结果,请使用占位符 '{{steps.STEP_ID.result}}' 或 '{{steps.STEP_ID.result[INDEX]}}' (当结果是数组时)。",
100
+ },
101
+ },
102
+ required: ["tool_name", "parameters"],
103
+ },
104
+ },
105
+ reflectInput: {
106
+ type: "string",
107
+ description:
108
+ "(仅 type='reflect' 时使用)给反思 LLM 的提示,说明需要检查什么、基于什么条件做决策。反思 LLM 会看到所有已完成步骤的结果摘要,并决定是继续、停止还是插入新步骤。",
109
+ },
110
+ },
111
+ required: ["id", "title"],
112
+ },
113
+ },
114
+ executionConfig: {
115
+ type: "object",
116
+ description: "(可选)计划执行的边界控制配置。",
117
+ properties: {
118
+ maxReflectCount: {
119
+ type: "integer",
120
+ description: "最多允许执行多少次反思步骤。",
121
+ },
122
+ maxTotalSteps: {
123
+ type: "integer",
124
+ description: "最多执行多少个步骤(包括动态插入的)。",
125
+ },
126
+ maxTimeMs: {
127
+ type: "integer",
128
+ description: "最大执行时间(毫秒)。",
129
+ },
130
+ },
131
+ },
132
+ },
133
+ required: ["planTitle", "strategy", "steps"],
134
+ },
135
+ };
136
+
137
+ // --- 辅助函数 ---
138
+
139
+ /**
140
+ * 解析步骤参数中的占位符,替换为实际的步骤结果
141
+ */
142
+ const resolveParameters = (params: any, allSteps: Step[]): any => {
143
+ if (typeof params !== "string") {
144
+ if (Array.isArray(params)) {
145
+ return params.map((p) => resolveParameters(p, allSteps));
146
+ }
147
+ if (typeof params === "object" && params !== null) {
148
+ return Object.fromEntries(
149
+ Object.entries(params).map(([key, value]) => [
150
+ key,
151
+ resolveParameters(value, allSteps),
152
+ ])
153
+ );
154
+ }
155
+ return params;
156
+ }
157
+
158
+ return params.replace(
159
+ /\{\{steps\.([^}]+)\.result(\[\d+\])?\}\}/g,
160
+ (match, stepId, indexPart) => {
161
+ const referencedStep = allSteps.find((s) => s.id === stepId);
162
+ if (
163
+ !referencedStep ||
164
+ referencedStep.status !== "completed" ||
165
+ !referencedStep.result
166
+ ) {
167
+ console.warn(
168
+ `无法解析引用:步骤 "${stepId}" 未完成或没有结果。将返回原始占位符。`
169
+ );
170
+ return match;
171
+ }
172
+
173
+ let result = referencedStep.result;
174
+ if (indexPart) {
175
+ const index = parseInt(indexPart.slice(1, -1), 10);
176
+ if (Array.isArray(result) && index < result.length) {
177
+ result = result[index];
178
+ } else {
179
+ console.warn(
180
+ `无法解析引用:步骤 "${stepId}" 的结果中没有索引 ${index}。将返回原始占位符。`
181
+ );
182
+ return match;
183
+ }
184
+ }
185
+
186
+ if (typeof result === "object") {
187
+ return JSON.stringify(result);
188
+ }
189
+ return String(result);
190
+ }
191
+ );
192
+ };
193
+
194
+ /**
195
+ * 格式化参数用于展示
196
+ */
197
+ function formatParameters(params: Record<string, any>): string {
198
+ if (!params || Object.keys(params).length === 0) return "无";
199
+ return Object.entries(params)
200
+ .map(([key, value]) => ` - \`${key}\`: \`${JSON.stringify(value)}\``)
201
+ .join("\n");
202
+ }
203
+
204
+ /**
205
+ * 对步骤结果生成摘要,用于反思 LLM
206
+ */
207
+ function summarizeStepResult(result: any[] | undefined): any {
208
+ if (!result || result.length === 0) {
209
+ return { empty: true };
210
+ }
211
+
212
+ return result.map((item) => {
213
+ if (item === null || item === undefined) {
214
+ return { type: "null" };
215
+ }
216
+
217
+ if (Array.isArray(item)) {
218
+ return {
219
+ type: "array",
220
+ length: item.length,
221
+ sample: item.slice(0, 2),
222
+ fields:
223
+ item[0] && typeof item[0] === "object" ? Object.keys(item[0]) : [],
224
+ };
225
+ }
226
+
227
+ if (typeof item === "object") {
228
+ // 检查是否是错误对象
229
+ if (item.error) {
230
+ return {
231
+ type: "error",
232
+ message: item.message || item.error,
233
+ };
234
+ }
235
+
236
+ const keys = Object.keys(item);
237
+ const preview = JSON.stringify(item);
238
+ return {
239
+ type: "object",
240
+ keys,
241
+ preview: preview.length > 300 ? preview.slice(0, 300) + "..." : preview,
242
+ };
243
+ }
244
+
245
+ if (typeof item === "string") {
246
+ return {
247
+ type: "string",
248
+ length: item.length,
249
+ preview: item.length > 200 ? item.slice(0, 200) + "..." : item,
250
+ };
251
+ }
252
+
253
+ return {
254
+ type: typeof item,
255
+ value: item,
256
+ };
257
+ });
258
+ }
259
+
260
+ /**
261
+ * 构建反思 LLM 的上下文输入
262
+ */
263
+ function buildReflectContext(
264
+ currentStep: Step,
265
+ allSteps: Step[],
266
+ originalRequest?: string
267
+ ): string {
268
+ const completedSteps = allSteps
269
+ .filter((s) => s.status === "completed" && s.id !== currentStep.id)
270
+ .map((s) => ({
271
+ id: s.id,
272
+ title: s.title,
273
+ type: s.type || "normal",
274
+ resultSummary: summarizeStepResult(s.result),
275
+ }));
276
+
277
+ const pendingSteps = allSteps
278
+ .filter((s) => s.status === "pending" && s.id !== currentStep.id)
279
+ .map((s) => ({
280
+ id: s.id,
281
+ title: s.title,
282
+ type: s.type || "normal",
283
+ }));
284
+
285
+ const failedSteps = allSteps
286
+ .filter((s) => s.status === "failed")
287
+ .map((s) => ({
288
+ id: s.id,
289
+ title: s.title,
290
+ result: s.result,
291
+ }));
292
+
293
+ return `
294
+ ## 当前 Plan 执行状态
295
+
296
+ ### 已完成的步骤 (${completedSteps.length})
297
+ ${completedSteps.length > 0 ? JSON.stringify(completedSteps, null, 2) : "(暂无)"}
298
+
299
+ ### 失败的步骤 (${failedSteps.length})
300
+ ${failedSteps.length > 0 ? JSON.stringify(failedSteps, null, 2) : "(暂无)"}
301
+
302
+ ### 待执行的步骤 (${pendingSteps.length})
303
+ ${pendingSteps.length > 0 ? JSON.stringify(pendingSteps, null, 2) : "(暂无)"}
304
+
305
+ ### 反思任务
306
+ ${currentStep.reflectInput || "请检查当前执行状态,决定下一步行动。"}
307
+
308
+ ---
309
+
310
+ 请根据以上信息,决定下一步行动。你必须返回一个 JSON 对象,格式如下:
311
+
312
+ \`\`\`json
313
+ {
314
+ "action": "continue" | "stop" | "insert_steps",
315
+ "reason": "你的决策原因",
316
+ "steps": [] // 仅当 action 是 "insert_steps" 时需要填写新步骤
317
+ }
318
+ \`\`\`
319
+
320
+ **action 说明:**
321
+ - "continue":继续执行剩余的待执行步骤
322
+ - "stop":停止执行,当前结果已足够或无法继续
323
+ - "insert_steps":需要插入新的步骤来完成任务
324
+
325
+ **如果 action 是 "insert_steps",steps 数组中的每个步骤格式:**
326
+ \`\`\`json
327
+ {
328
+ "id": "新步骤的唯一ID",
329
+ "title": "步骤描述",
330
+ "type": "normal",
331
+ "calls": [
332
+ {
333
+ "tool_name": "工具名称",
334
+ "parameters": { ... }
335
+ }
336
+ ]
337
+ }
338
+ \`\`\`
339
+
340
+ 请只返回 JSON,不要有其他内容。
341
+ `.trim();
342
+ }
343
+
344
+ /**
345
+ * 解析反思 LLM 的输出
346
+ */
347
+ function parseReflectDecision(llmOutput: string): ReflectDecision {
348
+ // 尝试从输出中提取 JSON
349
+ let jsonStr = llmOutput;
350
+
351
+ // 如果输出包含 markdown 代码块,提取其中的 JSON
352
+ const jsonMatch = llmOutput.match(/```(?:json)?\s*([\s\S]*?)```/);
353
+ if (jsonMatch) {
354
+ jsonStr = jsonMatch[1].trim();
355
+ }
356
+
357
+ try {
358
+ const parsed = JSON.parse(jsonStr);
359
+
360
+ // 验证必需字段
361
+ if (
362
+ !parsed.action ||
363
+ !["continue", "stop", "insert_steps"].includes(parsed.action)
364
+ ) {
365
+ console.warn("反思 LLM 输出的 action 无效,默认继续执行");
366
+ return {
367
+ action: "continue",
368
+ reason: "反思输出格式无效,默认继续",
369
+ };
370
+ }
371
+
372
+ // 如果是 insert_steps,验证 steps 数组
373
+ if (parsed.action === "insert_steps") {
374
+ if (!Array.isArray(parsed.steps) || parsed.steps.length === 0) {
375
+ console.warn(
376
+ "反思 LLM 输出 insert_steps 但没有提供 steps,改为继续执行"
377
+ );
378
+ return {
379
+ action: "continue",
380
+ reason: parsed.reason || "未提供新步骤,继续执行原计划",
381
+ };
382
+ }
383
+
384
+ // 确保每个新步骤都有必需字段
385
+ const validatedSteps = parsed.steps.map((s: any, index: number) => ({
386
+ id: s.id || `inserted_step_${Date.now()}_${index}`,
387
+ title: s.title || `插入的步骤 ${index + 1}`,
388
+ type: s.type || "normal",
389
+ calls: s.calls || [],
390
+ status: "pending" as const,
391
+ result: [],
392
+ }));
393
+
394
+ return {
395
+ action: "insert_steps",
396
+ reason: parsed.reason || "需要插入新步骤",
397
+ steps: validatedSteps,
398
+ };
399
+ }
400
+
401
+ return {
402
+ action: parsed.action,
403
+ reason: parsed.reason || "",
404
+ };
405
+ } catch (e) {
406
+ console.error("解析反思 LLM 输出失败:", e, "原始输出:", llmOutput);
407
+ return {
408
+ action: "continue",
409
+ reason: "解析反思输出失败,默认继续执行",
410
+ };
411
+ }
412
+ }
413
+
414
+ /**
415
+ * 检查是否超出执行边界
416
+ */
417
+ function checkExecutionBoundary(
418
+ config: PlanExecutionConfig | null,
419
+ stats: {
420
+ reflectCount: number;
421
+ totalStepsExecuted: number;
422
+ startTime: number | null;
423
+ },
424
+ isReflectStep: boolean
425
+ ): { exceeded: boolean; reason?: string } {
426
+ if (!config) {
427
+ return { exceeded: false };
428
+ }
429
+
430
+ if (config.maxTimeMs && stats.startTime) {
431
+ const elapsed = Date.now() - stats.startTime;
432
+ if (elapsed > config.maxTimeMs) {
433
+ return {
434
+ exceeded: true,
435
+ reason: `执行时间超过限制 (${config.maxTimeMs}ms)`,
436
+ };
437
+ }
438
+ }
439
+
440
+ if (
441
+ config.maxTotalSteps &&
442
+ stats.totalStepsExecuted >= config.maxTotalSteps
443
+ ) {
444
+ return {
445
+ exceeded: true,
446
+ reason: `执行步骤数超过限制 (${config.maxTotalSteps})`,
447
+ };
448
+ }
449
+
450
+ if (
451
+ isReflectStep &&
452
+ config.maxReflectCount &&
453
+ stats.reflectCount >= config.maxReflectCount
454
+ ) {
455
+ return {
456
+ exceeded: true,
457
+ reason: `反思次数超过限制 (${config.maxReflectCount})`,
458
+ };
459
+ }
460
+
461
+ return { exceeded: false };
462
+ }
463
+
464
+ // --- 异步 Thunk: 计划执行器 ---
465
+
466
+ interface RunPlanArgs {
467
+ dialogKey: string;
468
+ planCybotId?: string; // 用于反思的 cybot ID,如果不传则使用当前对话的主 cybot
469
+ }
470
+
471
+ export const runPlanSteps = createAsyncThunk(
472
+ "plan/runPlanSteps",
473
+ async ({ dialogKey, planCybotId }: RunPlanArgs, thunkApi) => {
474
+ const { getState, dispatch, signal } = thunkApi;
475
+
476
+ // 设置开始时间
477
+ dispatch(setStartTime(Date.now()));
478
+
479
+ const dialogId = extractCustomId(dialogKey);
480
+
481
+ // 获取反思用的 cybot ID
482
+ const state = getState() as RootState;
483
+ const currentDialog = selectCurrentDialogConfig(state);
484
+ const reflectCybotId = planCybotId || currentDialog?.cybots?.[0];
485
+
486
+ // 使用 while 循环而不是 for,因为 steps 可能会动态变化
487
+ while (true) {
488
+ if (signal.aborted) {
489
+ throw new DOMException("Aborted", "AbortError");
490
+ }
491
+
492
+ // 每次循环都重新获取最新的 steps
493
+ const currentState = getState() as RootState;
494
+ const allSteps = selectSteps(currentState);
495
+ const config = selectExecutionConfig(currentState);
496
+ const stats = selectPlanStats(currentState);
497
+
498
+ // 找到下一个待执行的步骤
499
+ const nextStep = allSteps.find((s) => s.status === "pending");
500
+ if (!nextStep) {
501
+ console.log("[runPlanSteps] 所有步骤已完成");
502
+ break;
503
+ }
504
+
505
+ const isReflectStep = nextStep.type === "reflect";
506
+
507
+ // 检查执行边界
508
+ const boundaryCheck = checkExecutionBoundary(
509
+ config,
510
+ stats,
511
+ isReflectStep
512
+ );
513
+ if (boundaryCheck.exceeded) {
514
+ console.log(`[runPlanSteps] 超出执行边界: ${boundaryCheck.reason}`);
515
+
516
+ // 创建一条消息通知用户
517
+ const { key: msgKey, messageId: boundaryMessageId } =
518
+ createDialogMessageKeyAndId(dialogId);
519
+
520
+ dispatch(
521
+ messageStreaming({
522
+ id: boundaryMessageId,
523
+ dialogId,
524
+ dbKey: msgKey,
525
+ role: "assistant",
526
+ content: `⚠️ **计划执行已停止**\n\n原因: ${boundaryCheck.reason}`,
527
+ cybotKey: PLAN_EXECUTOR_CYBOT_KEY,
528
+ isStreaming: true,
529
+ })
530
+ );
531
+
532
+ await dispatch(
533
+ messageStreamEnd({
534
+ finalContentBuffer: [
535
+ {
536
+ type: "text",
537
+ text: `⚠️ **计划执行已停止**\n\n原因: ${boundaryCheck.reason}`,
538
+ },
539
+ ],
540
+ msgKey,
541
+ dialogId,
542
+ dialogKey,
543
+ messageId: boundaryMessageId,
544
+ totalUsage: null,
545
+ agentConfig: PLAN_EXECUTOR_AGENT_CONFIG,
546
+ reasoningBuffer: "",
547
+ })
548
+ );
549
+
550
+ break;
551
+ }
552
+
553
+ // 更新当前步骤状态
554
+ dispatch(setCurrentStep(nextStep.id));
555
+ dispatch(
556
+ updateStep({ id: nextStep.id, updates: { status: "in-progress" } })
557
+ );
558
+ dispatch(incrementStepsExecuted());
559
+
560
+ // 创建步骤执行消息
561
+ const { key: msgKey, messageId: stepMessageId } =
562
+ createDialogMessageKeyAndId(dialogId);
563
+
564
+ if (isReflectStep) {
565
+ // ===== 反思步骤 =====
566
+ dispatch(incrementReflectCount());
567
+
568
+ dispatch(
569
+ messageStreaming({
570
+ id: stepMessageId,
571
+ dialogId,
572
+ dbKey: msgKey,
573
+ role: "assistant",
574
+ content: `🤔 **正在反思: ${nextStep.title}**`,
575
+ cybotKey: PLAN_EXECUTOR_CYBOT_KEY,
576
+ isStreaming: true,
577
+ })
578
+ );
579
+
580
+ try {
581
+ // 构建反思上下文
582
+ const reflectContext = buildReflectContext(nextStep, allSteps);
583
+
584
+ if (!reflectCybotId) {
585
+ throw new Error("无法确定反思 LLM 的 cybot ID");
586
+ }
587
+
588
+ // 这里仍然可以用 runLlm/ runAgent 封装,暂不改动
589
+ // 为了不引入新的依赖,这里可以简单使用全上下文 Agent,
590
+ // 也可以在后续将其改造成独立的反思 Agent。
591
+ const reflectState = getState() as RootState;
592
+ const reflectDialog = selectCurrentDialogConfig(reflectState);
593
+ const reflectAgentId = reflectCybotId || reflectDialog?.cybots?.[0];
594
+ if (!reflectAgentId) {
595
+ throw new Error("反思步骤: 找不到可用的 Agent。");
596
+ }
597
+
598
+ // 这里简单调用当前 Agent,一次性 LLM(无历史)
599
+ // 你也可以改为调用单独的 runLlm / runAgent 封装
600
+ const llmResult = await callAgentFunc(
601
+ {
602
+ agentKey: reflectAgentId,
603
+ task: reflectContext,
604
+ mode: "subtask",
605
+ },
606
+ thunkApi,
607
+ { parentMessageId: stepMessageId }
608
+ ).then((res) => res.rawData ?? "");
609
+
610
+
611
+ const decision = parseReflectDecision(llmResult as string);
612
+ console.log("[runPlanSteps] 反思决策:", decision);
613
+
614
+ if (decision.action === "stop") {
615
+ dispatch(
616
+ updateStep({
617
+ id: nextStep.id,
618
+ updates: { status: "completed", result: [decision] },
619
+ })
620
+ );
621
+
622
+ await dispatch(
623
+ messageStreamEnd({
624
+ finalContentBuffer: [
625
+ {
626
+ type: "text",
627
+ text: `🛑 **反思决定: 停止执行**\n\n原因: ${decision.reason}`,
628
+ },
629
+ ],
630
+ msgKey,
631
+ dialogId,
632
+ dialogKey,
633
+ messageId: stepMessageId,
634
+ totalUsage: null,
635
+ agentConfig: PLAN_EXECUTOR_AGENT_CONFIG,
636
+ reasoningBuffer: "",
637
+ })
638
+ );
639
+
640
+ // 移除后续待执行步骤
641
+ dispatch(removeStepsAfter(nextStep.id));
642
+ break;
643
+ }
644
+
645
+ if (
646
+ decision.action === "insert_steps" &&
647
+ decision.steps &&
648
+ decision.steps.length > 0
649
+ ) {
650
+ const newSteps: Step[] = decision.steps.map((s) => ({
651
+ ...s,
652
+ status: "pending" as const,
653
+ result: [],
654
+ }));
655
+
656
+ dispatch(insertStepsAfter({ afterStepId: nextStep.id, newSteps }));
657
+
658
+ dispatch(
659
+ updateStep({
660
+ id: nextStep.id,
661
+ updates: { status: "completed", result: [decision] },
662
+ })
663
+ );
664
+
665
+ const newStepsSummary = newSteps
666
+ .map((s, i) => `${i + 1}. ${s.title}`)
667
+ .join("\n");
668
+
669
+ await dispatch(
670
+ messageStreamEnd({
671
+ finalContentBuffer: [
672
+ {
673
+ type: "text",
674
+ text: `🔄 **反思决定: 插入新步骤**\n\n原因: ${decision.reason}\n\n新增步骤:\n${newStepsSummary}`,
675
+ },
676
+ ],
677
+ msgKey,
678
+ dialogId,
679
+ dialogKey,
680
+ messageId: stepMessageId,
681
+ totalUsage: null,
682
+ agentConfig: PLAN_EXECUTOR_AGENT_CONFIG,
683
+ reasoningBuffer: "",
684
+ })
685
+ );
686
+ } else {
687
+ dispatch(
688
+ updateStep({
689
+ id: nextStep.id,
690
+ updates: { status: "completed", result: [decision] },
691
+ })
692
+ );
693
+
694
+ await dispatch(
695
+ messageStreamEnd({
696
+ finalContentBuffer: [
697
+ {
698
+ type: "text",
699
+ text: `✅ **反思完成: 继续执行**\n\n${decision.reason}`,
700
+ },
701
+ ],
702
+ msgKey,
703
+ dialogId,
704
+ dialogKey,
705
+ messageId: stepMessageId,
706
+ totalUsage: null,
707
+ agentConfig: PLAN_EXECUTOR_AGENT_CONFIG,
708
+ reasoningBuffer: "",
709
+ })
710
+ );
711
+ }
712
+ } catch (e: any) {
713
+ console.error(`反思步骤 ${nextStep.id} 执行失败:`, e);
714
+
715
+ dispatch(
716
+ updateStep({
717
+ id: nextStep.id,
718
+ updates: { status: "failed", result: [{ error: e.message }] },
719
+ })
720
+ );
721
+
722
+ await dispatch(
723
+ messageStreamEnd({
724
+ finalContentBuffer: [
725
+ {
726
+ type: "text",
727
+ text: `❌ **反思失败: ${nextStep.title}**\n\n错误: ${e.message}\n\n将继续执行剩余步骤...`,
728
+ },
729
+ ],
730
+ msgKey,
731
+ dialogId,
732
+ dialogKey,
733
+ messageId: stepMessageId,
734
+ totalUsage: null,
735
+ agentConfig: PLAN_EXECUTOR_AGENT_CONFIG,
736
+ reasoningBuffer: "",
737
+ })
738
+ );
739
+
740
+ // 反思失败不中断整个计划,继续执行
741
+ }
742
+ } else {
743
+ // ===== 普通步骤 =====
744
+ const calls = nextStep.calls || [];
745
+
746
+ dispatch(
747
+ messageStreaming({
748
+ id: stepMessageId,
749
+ dialogId,
750
+ dbKey: msgKey,
751
+ role: "assistant",
752
+ content: `🔄 **正在执行: ${nextStep.title}** (${calls.length}个任务${calls.length > 1 ? "并行" : ""})`,
753
+ cybotKey: PLAN_EXECUTOR_CYBOT_KEY,
754
+ isStreaming: true,
755
+ })
756
+ );
757
+
758
+ try {
759
+ // 并行执行当前步骤的所有调用
760
+ const latestSteps = selectSteps(getState() as RootState);
761
+
762
+ const callPromises = calls.map(async (call: ToolCall) => {
763
+ const resolvedParameters = resolveParameters(
764
+ call.parameters,
765
+ latestSteps
766
+ );
767
+
768
+ const toolRunId = createToolRunId();
769
+ const def = toolDefinitionsByName[call.tool_name];
770
+ const behavior = def?.behavior;
771
+ const inputSummary = JSON.stringify(resolvedParameters).slice(
772
+ 0,
773
+ 400
774
+ );
775
+
776
+ // 启动 ToolRun 记录
777
+ (dispatch as any)(
778
+ toolRunStarted({
779
+ id: toolRunId,
780
+ messageId: stepMessageId,
781
+ toolName: call.tool_name,
782
+ behavior,
783
+ inputSummary,
784
+ })
785
+ );
786
+
787
+ try {
788
+ const executor = toolExecutors[call.tool_name];
789
+ if (!executor) {
790
+ throw new Error(`未知工具: ${call.tool_name}`);
791
+ }
792
+
793
+ const toolResult = await executor(
794
+ resolvedParameters,
795
+ thunkApi,
796
+ {
797
+ parentMessageId: stepMessageId,
798
+ }
799
+ );
800
+
801
+ const rawData = toolResult?.rawData ?? toolResult;
802
+ const displayData =
803
+ toolResult?.displayData ||
804
+ `**${call.tool_name}**: 执行成功`;
805
+
806
+ (dispatch as any)(
807
+ toolRunSucceeded({
808
+ id: toolRunId,
809
+ outputSummary: displayData,
810
+ })
811
+ );
812
+
813
+ return { rawData, displayData };
814
+ } catch (e: any) {
815
+ (dispatch as any)(
816
+ toolRunFailed({
817
+ id: toolRunId,
818
+ error: e?.message || String(e),
819
+ })
820
+ );
821
+ throw e;
822
+ }
823
+ });
824
+
825
+ const toolResults = await Promise.all(callPromises);
826
+
827
+ // 更新步骤状态为 completed 并保存结果
828
+ dispatch(
829
+ updateStep({
830
+ id: nextStep.id,
831
+ updates: {
832
+ status: "completed",
833
+ result: toolResults.map((res) => res.rawData),
834
+ },
835
+ })
836
+ );
837
+
838
+ // 构建完成消息
839
+ const finalContent = `✅ **${nextStep.title} 完成**\n\n${toolResults
840
+ .map((r) => r.displayData || "执行成功")
841
+ .join("\n")}`;
842
+
843
+ await dispatch(
844
+ messageStreamEnd({
845
+ finalContentBuffer: [{ type: "text", text: finalContent }],
846
+ msgKey,
847
+ dialogId,
848
+ dialogKey,
849
+ messageId: stepMessageId,
850
+ totalUsage: null,
851
+ agentConfig: PLAN_EXECUTOR_AGENT_CONFIG,
852
+ reasoningBuffer: "",
853
+ })
854
+ );
855
+ } catch (e: any) {
856
+ console.error(`步骤 ${nextStep.id} 执行失败:`, e);
857
+
858
+ dispatch(
859
+ updateStep({
860
+ id: nextStep.id,
861
+ updates: { status: "failed", result: [{ error: e.message }] },
862
+ })
863
+ );
864
+
865
+ await dispatch(
866
+ messageStreamEnd({
867
+ finalContentBuffer: [
868
+ {
869
+ type: "text",
870
+ text: `❌ **${nextStep.title} 失败**\n\n错误: ${e.message}`,
871
+ },
872
+ ],
873
+ msgKey,
874
+ dialogId,
875
+ dialogKey,
876
+ messageId: stepMessageId,
877
+ totalUsage: null,
878
+ agentConfig: PLAN_EXECUTOR_AGENT_CONFIG,
879
+ reasoningBuffer: "",
880
+ })
881
+ );
882
+
883
+ // 普通步骤失败,中断整个计划
884
+ break;
885
+ }
886
+ }
887
+ }
888
+
889
+ // 所有步骤执行完毕,重置当前步骤
890
+ dispatch(setCurrentStep(null));
891
+ }
892
+ );
893
+
894
+ // --- 工具主函数 ---
895
+
896
+ export async function createPlanAndOrchestrateFunc(
897
+ args: any,
898
+ thunkApi: any
899
+ ): Promise<{ rawData: string; displayData: string }> {
900
+ const { dispatch, getState } = thunkApi;
901
+ const { planTitle, strategy, steps: stepBlueprints, executionConfig } = args;
902
+
903
+ if (
904
+ !planTitle ||
905
+ !strategy ||
906
+ !Array.isArray(stepBlueprints) ||
907
+ stepBlueprints.length === 0
908
+ ) {
909
+ throw new Error(
910
+ "创建计划需要 'planTitle', 'strategy', 以及至少一个 'steps'。"
911
+ );
912
+ }
913
+
914
+ // 处理步骤,设置默认值
915
+ const processedSteps: Step[] = stepBlueprints.map((blueprint: any) => ({
916
+ id: blueprint.id,
917
+ title: blueprint.title,
918
+ type: blueprint.type || "normal",
919
+ status: "pending" as const,
920
+ calls: blueprint.calls || [],
921
+ reflectInput: blueprint.reflectInput,
922
+ result: [],
923
+ }));
924
+
925
+ // TODO: 可以在这里检查当前是否已有 Plan 在执行,必要时只返回 Plan 描述而不自动执行
926
+
927
+ // 设置计划
928
+ dispatch(setPlan({ planDetails: strategy, currentProgress: 0 }));
929
+ dispatch(setSteps(processedSteps));
930
+
931
+ // 设置执行配置(如果有)
932
+ if (executionConfig) {
933
+ dispatch(setExecutionConfig(executionConfig));
934
+ }
935
+
936
+ // 获取 dialogKey 并启动执行
937
+ const state = getState() as RootState;
938
+ const dialogKey = (state as any).dialog.currentDialogKey;
939
+
940
+ if (dialogKey) {
941
+ dispatch(runPlanSteps({ dialogKey }));
942
+ } else {
943
+ console.error("无法执行计划: 未能获取到 currentDialogKey。");
944
+ const errorMarkdown = `\n\n**严重错误:** 找不到当前对话。计划已创建但**不会被执行**。`;
945
+ return { rawData: errorMarkdown, displayData: errorMarkdown };
946
+ }
947
+
948
+ // 构建展示用的 Markdown
949
+ const normalSteps = processedSteps.filter((s) => s.type !== "reflect");
950
+ const reflectSteps = processedSteps.filter((s) => s.type === "reflect");
951
+
952
+ const markdownResult = `
953
+ ### 计划已创建: ${planTitle}
954
+
955
+ **策略:**
956
+ ${strategy}
957
+
958
+ ---
959
+
960
+ **执行步骤 (${processedSteps.length} 步,其中 ${reflectSteps.length} 个反思点):**
961
+
962
+ ${processedSteps
963
+ .map((step, index) => {
964
+ if (step.type === "reflect") {
965
+ return `
966
+ **${index + 1}. 🤔 ${step.title}** (\`ID: ${step.id}\`, 类型: 反思)
967
+ - **反思任务:** ${step.reflectInput || "检查执行状态并决定下一步"}`;
968
+ }
969
+
970
+ return `
971
+ **${index + 1}. ${step.title}** (\`ID: ${step.id}\`)
972
+ ${(step.calls || [])
973
+ .map(
974
+ (call) => `
975
+ - **工具:** \`${call.tool_name}\`
976
+ - **参数:**
977
+ ${formatParameters(call.parameters)}`
978
+ )
979
+ .join("")}`;
980
+ })
981
+ .join("\n---\n")}
982
+
983
+ ${executionConfig
984
+ ? `
985
+ ---
986
+ **执行限制:**
987
+ ${executionConfig.maxReflectCount ? `- 最大反思次数: ${executionConfig.maxReflectCount}` : ""}
988
+ ${executionConfig.maxTotalSteps ? `- 最大步骤数: ${executionConfig.maxTotalSteps}` : ""}
989
+ ${executionConfig.maxTimeMs ? `- 最大执行时间: ${executionConfig.maxTimeMs}ms` : ""}
990
+ `
991
+ : ""
992
+ }
993
+
994
+ ---
995
+ **计划已开始自动执行...**
996
+ `;
997
+
998
+ return { rawData: markdownResult, displayData: markdownResult };
999
+ }