minimal-agent 0.10.0 → 0.12.0

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 (573) hide show
  1. package/README.md +11 -3
  2. package/package.json +52 -5
  3. package/plugins/computer_use/.claude-plugin/plugin.json +6 -0
  4. package/plugins/computer_use/README.md +94 -0
  5. package/plugins/computer_use/app/_template.md +142 -0
  6. package/plugins/computer_use/app/chrome.md +165 -0
  7. package/plugins/computer_use/app/finder.md +76 -0
  8. package/plugins/computer_use/app/macos-system-settings.md +81 -0
  9. package/plugins/computer_use/app/microsoft-excel.md +80 -0
  10. package/plugins/computer_use/app/microsoft-powerpoint.md +80 -0
  11. package/plugins/computer_use/app/microsoft-word.md +79 -0
  12. package/plugins/computer_use/app/wechat.md +413 -0
  13. package/plugins/computer_use/app/weread.md +319 -0
  14. package/plugins/computer_use/commands/computeruse.md +25 -0
  15. package/plugins/computer_use/commands/cu.md +9 -0
  16. package/plugins/computer_use/plugin.js +26 -0
  17. package/plugins/computer_use/plugin.ts +30 -0
  18. package/plugins/computer_use/src/capture.js +90 -0
  19. package/plugins/computer_use/src/capture.ts +104 -0
  20. package/plugins/computer_use/src/commands.js +91 -0
  21. package/plugins/computer_use/src/commands.ts +103 -0
  22. package/plugins/computer_use/src/daemon.js +134 -0
  23. package/plugins/computer_use/src/daemon.ts +161 -0
  24. package/plugins/computer_use/src/manuals.js +135 -0
  25. package/plugins/computer_use/src/manuals.ts +159 -0
  26. package/plugins/computer_use/src/verify.js +168 -0
  27. package/plugins/computer_use/src/verify.ts +219 -0
  28. package/plugins/computer_use/test/commands.test.ts +73 -0
  29. package/plugins/cron-runner/plugin.ts +38 -0
  30. package/plugins/cron-runner/src/commands.ts +373 -0
  31. package/plugins/cron-runner/src/cronTime.ts +155 -0
  32. package/plugins/cron-runner/src/lock.ts +79 -0
  33. package/plugins/cron-runner/src/runs.ts +99 -0
  34. package/plugins/cron-runner/src/scheduleEngine.ts +76 -0
  35. package/plugins/cron-runner/src/scheduler.ts +376 -0
  36. package/plugins/cron-runner/src/schema.ts +139 -0
  37. package/plugins/cron-runner/src/service.ts +193 -0
  38. package/plugins/cron-runner/src/store.ts +333 -0
  39. package/plugins/cron-runner/src/types.ts +141 -0
  40. package/plugins/cron-runner/test/cronTime.test.ts +41 -0
  41. package/plugins/cron-runner/test/plugin-integration.test.ts +47 -0
  42. package/plugins/cron-runner/test/scheduleEngine.test.ts +41 -0
  43. package/plugins/cron-runner/test/service.test.ts +76 -0
  44. package/plugins/cron-runner/test/store-and-scheduler.test.ts +501 -0
  45. package/plugins/workflow-runner/plugin.js +56 -2
  46. package/plugins/workflow-runner/plugin.ts +113 -0
  47. package/plugins/workflow-runner/src/agentCommon.js +43 -4
  48. package/plugins/workflow-runner/src/agentCommon.ts +435 -0
  49. package/plugins/workflow-runner/src/controlFlow/branch.js +40 -0
  50. package/plugins/workflow-runner/src/controlFlow/branch.ts +46 -0
  51. package/plugins/workflow-runner/src/controlFlow/loop.js +211 -0
  52. package/plugins/workflow-runner/src/controlFlow/loop.ts +248 -0
  53. package/plugins/workflow-runner/src/controlFlow/pause.js +129 -0
  54. package/plugins/workflow-runner/src/controlFlow/pause.ts +141 -0
  55. package/plugins/workflow-runner/src/controlFlow/signal.js +21 -0
  56. package/plugins/workflow-runner/src/controlFlow/signal.ts +29 -0
  57. package/plugins/workflow-runner/src/controlFlow/types.js +18 -0
  58. package/plugins/workflow-runner/src/controlFlow/types.ts +63 -0
  59. package/plugins/workflow-runner/src/dslManifest.ts +127 -0
  60. package/plugins/workflow-runner/src/expressions.ts +622 -0
  61. package/plugins/workflow-runner/src/index.js +13 -4
  62. package/plugins/workflow-runner/src/index.ts +338 -0
  63. package/plugins/workflow-runner/src/jsonPayload.ts +84 -0
  64. package/plugins/workflow-runner/src/loader.js +23 -0
  65. package/plugins/workflow-runner/src/loader.ts +721 -0
  66. package/plugins/workflow-runner/src/multimodal.js +2 -1
  67. package/plugins/workflow-runner/src/multimodal.ts +64 -0
  68. package/plugins/workflow-runner/src/recovery.ts +149 -0
  69. package/plugins/workflow-runner/src/runner.js +75 -659
  70. package/plugins/workflow-runner/src/runner.ts +604 -0
  71. package/plugins/workflow-runner/src/stepExecutors/agent.ts +234 -0
  72. package/plugins/workflow-runner/src/stepExecutors/assert.ts +50 -0
  73. package/plugins/workflow-runner/src/stepExecutors/echo.js +31 -0
  74. package/plugins/workflow-runner/src/stepExecutors/echo.ts +38 -0
  75. package/plugins/workflow-runner/src/stepExecutors/llm.ts +133 -0
  76. package/plugins/workflow-runner/src/stepExecutors/set.js +49 -0
  77. package/plugins/workflow-runner/src/stepExecutors/set.ts +56 -0
  78. package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
  79. package/plugins/workflow-runner/src/stepExecutors/skill.ts +140 -0
  80. package/plugins/workflow-runner/src/stepExecutors/tool.js +80 -8
  81. package/plugins/workflow-runner/src/stepExecutors/tool.ts +256 -0
  82. package/plugins/workflow-runner/src/stepExecutors/verify.ts +68 -0
  83. package/plugins/workflow-runner/src/stepPipeline.js +277 -0
  84. package/plugins/workflow-runner/src/stepPipeline.ts +366 -0
  85. package/plugins/workflow-runner/src/taskIdentity.ts +405 -0
  86. package/plugins/workflow-runner/src/toolRuntime.js +48 -0
  87. package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
  88. package/plugins/workflow-runner/src/types.js +37 -1
  89. package/plugins/workflow-runner/src/types.ts +602 -0
  90. package/plugins/workflow-runner/src/workflowState.ts +733 -0
  91. package/plugins/workflow-runner/test/agent.test.ts +587 -0
  92. package/plugins/workflow-runner/test/agentLlmError.test.ts +373 -0
  93. package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +65 -0
  94. package/plugins/workflow-runner/test/captureMisbind.test.ts +99 -0
  95. package/plugins/workflow-runner/test/cli.e2e.test.ts +147 -0
  96. package/plugins/workflow-runner/test/context-files.test.ts +320 -0
  97. package/plugins/workflow-runner/test/e2e.test.ts +370 -0
  98. package/plugins/workflow-runner/test/engineRobustness.test.ts +226 -0
  99. package/plugins/workflow-runner/test/expressions.test.ts +397 -0
  100. package/plugins/workflow-runner/test/foundation.test.ts +126 -0
  101. package/plugins/workflow-runner/test/graceful.test.ts +139 -0
  102. package/plugins/workflow-runner/test/greedyInputs.load.test.ts +47 -0
  103. package/plugins/workflow-runner/test/hostCapability.test.ts +108 -0
  104. package/plugins/workflow-runner/test/illustrate.load.test.ts +183 -0
  105. package/plugins/workflow-runner/test/jsonPayload.test.ts +83 -0
  106. package/plugins/workflow-runner/test/loader.test.ts +617 -0
  107. package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +33 -0
  108. package/plugins/workflow-runner/test/multimodal.test.ts +342 -0
  109. package/plugins/workflow-runner/test/output-schema.test.ts +213 -0
  110. package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +87 -0
  111. package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +269 -0
  112. package/plugins/workflow-runner/test/ppt.load.test.ts +1220 -0
  113. package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +220 -0
  114. package/plugins/workflow-runner/test/recovery.test.ts +265 -0
  115. package/plugins/workflow-runner/test/runner.test.ts +911 -0
  116. package/plugins/workflow-runner/test/runnerPreflight.test.ts +379 -0
  117. package/plugins/workflow-runner/test/skillTurnDone.test.ts +71 -0
  118. package/plugins/workflow-runner/test/stateLifecycle.test.ts +150 -0
  119. package/plugins/workflow-runner/test/stepPipeline.test.ts +114 -0
  120. package/plugins/workflow-runner/test/taskArbitration.test.ts +338 -0
  121. package/plugins/workflow-runner/test/taskIdentity.test.ts +237 -0
  122. package/plugins/workflow-runner/test/taskLifecycle.test.ts +270 -0
  123. package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +221 -0
  124. package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +29 -0
  125. package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
  126. package/plugins/workflow-runner/test/webppt.load.test.ts +250 -0
  127. package/plugins/workflow-runner/test/word.load.test.ts +208 -0
  128. package/plugins/workflow-runner/test/workflows.smoke.test.ts +76 -0
  129. package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +207 -0
  130. package/skills/browser-automation/SKILL.md +9 -19
  131. package/skills/web-crawler/SKILL.md +9 -11
  132. package/src/askBridge.ts +249 -0
  133. package/src/bootstrap/cwdArg.js +10 -3
  134. package/src/bootstrap/cwdArg.ts +31 -0
  135. package/src/bootstrap/workingDir.ts +48 -0
  136. package/src/cli/args.js +70 -7
  137. package/src/cli/args.ts +146 -0
  138. package/src/cli/computerUse.js +185 -0
  139. package/src/cli/computerUse.ts +221 -0
  140. package/src/cli/configMenu.js +274 -202
  141. package/src/cli/configMenu.tsx +521 -0
  142. package/src/cli/memoryAccess.js +45 -0
  143. package/src/cli/memoryAccess.ts +53 -0
  144. package/src/cli/print.js +119 -5
  145. package/src/cli/print.ts +597 -0
  146. package/src/cli/quickModelSetup.js +60 -0
  147. package/src/cli/quickModelSetup.tsx +96 -0
  148. package/src/cli/streamJson.js +80 -4
  149. package/src/cli/streamJson.ts +433 -0
  150. package/src/cli/wizards/advisorWizard.js +1 -0
  151. package/src/cli/wizards/advisorWizard.tsx +195 -0
  152. package/src/cli/wizards/agentRuntimeWizard.js +84 -0
  153. package/src/cli/wizards/agentRuntimeWizard.tsx +134 -0
  154. package/src/cli/wizards/browserWizard.js +55 -0
  155. package/src/cli/wizards/browserWizard.tsx +77 -0
  156. package/src/cli/wizards/contextAdvisorWizard.js +84 -0
  157. package/src/cli/wizards/contextAdvisorWizard.tsx +124 -0
  158. package/src/cli/wizards/fieldForm.js +7 -4
  159. package/src/cli/wizards/fieldForm.tsx +250 -0
  160. package/src/cli/wizards/imageWizard.js +4 -5
  161. package/src/cli/wizards/imageWizard.tsx +96 -0
  162. package/src/cli/wizards/openRouterWizard.tsx +48 -0
  163. package/src/cli/wizards/providerPresets.js +1 -8
  164. package/src/cli/wizards/providerPresets.ts +106 -0
  165. package/src/cli/wizards/providerWizard.js +152 -205
  166. package/src/cli/wizards/providerWizard.tsx +266 -0
  167. package/src/cli/wizards/searchWizard.tsx +47 -0
  168. package/src/cli/wizards/ttsWizard.js +4 -3
  169. package/src/cli/wizards/ttsWizard.tsx +61 -0
  170. package/src/config/configDefaults.js +25 -0
  171. package/src/config/configDefaults.ts +33 -0
  172. package/src/config/configDiagnostics.js +117 -0
  173. package/src/config/configDiagnostics.ts +166 -0
  174. package/src/config/configFile.js +52 -32
  175. package/src/config/configFile.ts +633 -0
  176. package/src/config/configManagement.js +126 -0
  177. package/src/config/configManagement.ts +159 -0
  178. package/src/config/configService.js +66 -11
  179. package/src/config/configService.ts +554 -0
  180. package/src/config/numericLimits.js +61 -0
  181. package/src/config/numericLimits.ts +80 -0
  182. package/src/config.ts +109 -0
  183. package/src/context/agentHome.ts +11 -0
  184. package/src/context/archive.ts +144 -0
  185. package/src/context/budgetPlan.js +54 -0
  186. package/src/context/budgetPlan.ts +88 -0
  187. package/src/context/compact.js +179 -106
  188. package/src/context/compact.ts +584 -0
  189. package/src/context/contextBundle.js +119 -0
  190. package/src/context/contextBundle.ts +176 -0
  191. package/src/context/contextLock.ts +84 -0
  192. package/src/context/microCompactLite.ts +218 -0
  193. package/src/context/persistContext.ts +177 -0
  194. package/src/context/reactiveCompact.js +19 -4
  195. package/src/context/reactiveCompact.ts +240 -0
  196. package/src/context/recentDirs.ts +42 -0
  197. package/src/context/sessionPath.ts +206 -0
  198. package/src/context/sessionRegistry.js +8 -2
  199. package/src/context/sessionRegistry.ts +414 -0
  200. package/src/context/snipCompact.ts +163 -0
  201. package/src/context/tokenCounter.ts +106 -0
  202. package/src/context/workspaceIndex.js +2 -2
  203. package/src/context/workspaceIndex.ts +751 -0
  204. package/src/entry.compiled.tsx +20 -0
  205. package/src/harness/claimValidation.js +9 -0
  206. package/src/harness/claimValidation.ts +22 -0
  207. package/src/harness/evidenceLedger.js +92 -0
  208. package/src/harness/evidenceLedger.ts +96 -0
  209. package/src/harness/stallGuard.js +60 -0
  210. package/src/harness/stallGuard.ts +81 -0
  211. package/src/humanIntervention.js +129 -0
  212. package/src/humanIntervention.ts +216 -0
  213. package/src/infra/verification.ts +295 -0
  214. package/src/irreversible/rules.js +216 -0
  215. package/src/irreversible/rules.ts +261 -0
  216. package/src/llm/client.js +40 -3
  217. package/src/llm/client.ts +621 -0
  218. package/src/llm/promptCache.js +27 -0
  219. package/src/llm/promptCache.ts +41 -0
  220. package/src/llm/providers/anthropicFormat.js +41 -5
  221. package/src/llm/providers/anthropicFormat.ts +290 -0
  222. package/src/llm/providers/openaiFormat.js +186 -8
  223. package/src/llm/providers/openaiFormat.ts +428 -0
  224. package/src/loop.js +614 -63
  225. package/src/loop.ts +1454 -0
  226. package/src/main.js +67 -10
  227. package/src/main.tsx +344 -0
  228. package/src/memory/codec.js +204 -0
  229. package/src/memory/codec.ts +249 -0
  230. package/src/memory/constants.js +34 -0
  231. package/src/memory/constants.ts +50 -0
  232. package/src/memory/episodicMemory.js +684 -0
  233. package/src/memory/episodicMemory.ts +815 -0
  234. package/src/memory/fileStore.js +148 -0
  235. package/src/memory/fileStore.ts +184 -0
  236. package/src/memory/memoryService.js +570 -0
  237. package/src/memory/memoryService.ts +859 -0
  238. package/src/memory/migration.js +128 -0
  239. package/src/memory/migration.ts +160 -0
  240. package/src/memory/repository.js +789 -0
  241. package/src/memory/repository.ts +893 -0
  242. package/src/memory/retrieval.js +108 -0
  243. package/src/memory/retrieval.ts +145 -0
  244. package/src/memory/searchIndex.js +107 -0
  245. package/src/memory/searchIndex.ts +153 -0
  246. package/src/memory/topicView.js +82 -0
  247. package/src/memory/topicView.ts +95 -0
  248. package/src/memory/turnJournal.js +95 -0
  249. package/src/memory/turnJournal.ts +120 -0
  250. package/src/memory/types.js +2 -0
  251. package/src/memory/types.ts +186 -0
  252. package/src/messageQueue.ts +123 -0
  253. package/src/multimodal.js +128 -46
  254. package/src/multimodal.ts +349 -0
  255. package/src/plugin-sdk.js +21 -1
  256. package/src/plugin-sdk.ts +134 -0
  257. package/src/plugins/builtinPluginIds.js +47 -0
  258. package/src/plugins/builtinPluginIds.ts +51 -0
  259. package/src/plugins/builtinRegistry.ts +43 -0
  260. package/src/plugins/commandRouter.js +40 -26
  261. package/src/plugins/commandRouter.ts +261 -0
  262. package/src/plugins/hookEngine.ts +341 -0
  263. package/src/plugins/pluginApi.ts +133 -0
  264. package/src/plugins/pluginLifecycle.ts +34 -0
  265. package/src/plugins/pluginLoader.ts +147 -0
  266. package/src/plugins/pluginManifest.js +127 -0
  267. package/src/plugins/pluginManifest.ts +152 -0
  268. package/src/plugins/pluginRunner.js +47 -2
  269. package/src/plugins/pluginRunner.ts +207 -0
  270. package/src/plugins/transcript.ts +208 -0
  271. package/src/prompts/lint.js +169 -0
  272. package/src/prompts/lint.ts +220 -0
  273. package/src/prompts/pluginList.js +32 -16
  274. package/src/prompts/pluginList.ts +86 -0
  275. package/src/prompts/policyRegistry.js +318 -0
  276. package/src/prompts/policyRegistry.ts +358 -0
  277. package/src/prompts/projectInstructions.js +24 -0
  278. package/src/prompts/projectInstructions.ts +81 -0
  279. package/src/prompts/promptAssembly.js +311 -202
  280. package/src/prompts/promptAssembly.ts +510 -0
  281. package/src/prompts/skillList.js +106 -45
  282. package/src/prompts/skillList.ts +198 -0
  283. package/src/prompts/system.js +31 -6
  284. package/src/prompts/system.ts +81 -0
  285. package/src/runtime/agentRuntimeContext.js +53 -0
  286. package/src/runtime/agentRuntimeContext.ts +112 -0
  287. package/src/session/runAgentTurn.js +71 -12
  288. package/src/session/runAgentTurn.ts +181 -0
  289. package/src/session/runTurn.js +29 -0
  290. package/src/session/runTurn.ts +160 -0
  291. package/src/session/runtimeStore.js +149 -0
  292. package/src/session/runtimeStore.ts +197 -0
  293. package/src/session/sessionState.js +79 -3
  294. package/src/session/sessionState.ts +134 -0
  295. package/src/toolGate.ts +50 -0
  296. package/src/tools/advisor/advisor.js +76 -15
  297. package/src/tools/advisor/advisor.ts +512 -0
  298. package/src/tools/agent/agent.js +245 -29
  299. package/src/tools/agent/agent.ts +837 -0
  300. package/src/tools/agent/agentTaskService.js +60 -0
  301. package/src/tools/agent/agentTaskService.ts +113 -0
  302. package/src/tools/agent/agentTypes.js +20 -8
  303. package/src/tools/agent/agentTypes.ts +94 -0
  304. package/src/tools/agent/coordinator.ts +111 -0
  305. package/src/tools/agent/worktree.ts +296 -0
  306. package/src/tools/ask/ask.js +17 -0
  307. package/src/tools/ask/ask.ts +122 -0
  308. package/src/tools/bash/attachImage.js +223 -0
  309. package/src/tools/bash/attachImage.ts +281 -0
  310. package/src/tools/bash/bash.js +132 -14
  311. package/src/tools/bash/bash.ts +642 -0
  312. package/src/tools/bash/semantics.ts +103 -0
  313. package/src/tools/bash/warnings.js +17 -11
  314. package/src/tools/bash/warnings.ts +117 -0
  315. package/src/tools/capabilities.js +42 -0
  316. package/src/tools/capabilities.ts +69 -0
  317. package/src/tools/computeruse/accessibilityPermission.js +261 -0
  318. package/src/tools/computeruse/accessibilityPermission.ts +294 -0
  319. package/src/tools/computeruse/axSearch.js +96 -0
  320. package/src/tools/computeruse/axSearch.ts +120 -0
  321. package/src/tools/computeruse/axSnap.js +272 -0
  322. package/src/tools/computeruse/axSnap.ts +349 -0
  323. package/src/tools/computeruse/capabilityLabels.js +95 -0
  324. package/src/tools/computeruse/capabilityLabels.ts +115 -0
  325. package/src/tools/computeruse/computerUseService.js +2235 -0
  326. package/src/tools/computeruse/computerUseService.ts +2686 -0
  327. package/src/tools/computeruse/computeruse.js +2742 -0
  328. package/src/tools/computeruse/computeruse.ts +3265 -0
  329. package/src/tools/computeruse/diagnosticTrace.js +154 -0
  330. package/src/tools/computeruse/diagnosticTrace.ts +195 -0
  331. package/src/tools/computeruse/diagnostics.js +369 -0
  332. package/src/tools/computeruse/diagnostics.ts +453 -0
  333. package/src/tools/computeruse/grants.js +53 -0
  334. package/src/tools/computeruse/grants.ts +70 -0
  335. package/src/tools/computeruse/humanBoundary.js +159 -0
  336. package/src/tools/computeruse/humanBoundary.ts +199 -0
  337. package/src/tools/computeruse/intervention.js +77 -0
  338. package/src/tools/computeruse/intervention.ts +110 -0
  339. package/src/tools/computeruse/manualSchema.js +709 -0
  340. package/src/tools/computeruse/manualSchema.ts +916 -0
  341. package/src/tools/computeruse/observationImages.js +705 -0
  342. package/src/tools/computeruse/observationImages.ts +889 -0
  343. package/src/tools/computeruse/protocol.js +60 -0
  344. package/src/tools/computeruse/protocol.ts +515 -0
  345. package/src/tools/computeruse/telemetryLog.js +57 -0
  346. package/src/tools/computeruse/telemetryLog.ts +67 -0
  347. package/src/tools/computeruse/vision/appGuide.js +325 -0
  348. package/src/tools/computeruse/vision/appGuide.ts +482 -0
  349. package/src/tools/computeruse/vision/focus.js +291 -0
  350. package/src/tools/computeruse/vision/focus.ts +382 -0
  351. package/src/tools/computeruse/vision/frames.js +248 -0
  352. package/src/tools/computeruse/vision/frames.ts +329 -0
  353. package/src/tools/computeruse/vision/geometry.js +131 -0
  354. package/src/tools/computeruse/vision/geometry.ts +163 -0
  355. package/src/tools/computeruse/vision/imaging.js +201 -0
  356. package/src/tools/computeruse/vision/imaging.ts +281 -0
  357. package/src/tools/computeruse/vision/png.js +274 -0
  358. package/src/tools/computeruse/vision/png.ts +317 -0
  359. package/src/tools/computeruse/vision/raster.js +320 -0
  360. package/src/tools/computeruse/vision/raster.ts +408 -0
  361. package/src/tools/computeruse/vision/settle.js +131 -0
  362. package/src/tools/computeruse/vision/settle.ts +220 -0
  363. package/src/tools/computeruse/vision/telemetry.js +196 -0
  364. package/src/tools/computeruse/vision/telemetry.ts +241 -0
  365. package/src/tools/computeruse/visionOcr.js +115 -0
  366. package/src/tools/computeruse/visionOcr.ts +128 -0
  367. package/src/tools/computeruse/waitFor.js +237 -0
  368. package/src/tools/computeruse/waitFor.ts +291 -0
  369. package/src/tools/download/download.js +10 -6
  370. package/src/tools/download/download.ts +187 -0
  371. package/src/tools/edit/edit.js +30 -18
  372. package/src/tools/edit/edit.ts +349 -0
  373. package/src/tools/edit/multi-edit.js +25 -14
  374. package/src/tools/edit/multi-edit.ts +223 -0
  375. package/src/tools/effectiveToolSet.js +73 -0
  376. package/src/tools/effectiveToolSet.ts +91 -0
  377. package/src/tools/genimage/genimage.ts +257 -0
  378. package/src/tools/genspeech/genspeech.ts +83 -0
  379. package/src/tools/glob/glob.js +176 -29
  380. package/src/tools/glob/glob.ts +283 -0
  381. package/src/tools/grep/grep.js +61 -19
  382. package/src/tools/grep/grep.ts +239 -0
  383. package/src/tools/grep/rgPath.ts +206 -0
  384. package/src/tools/index.js +99 -5
  385. package/src/tools/index.ts +293 -0
  386. package/src/tools/lsp/config.js +113 -0
  387. package/src/tools/lsp/config.ts +113 -0
  388. package/src/tools/lsp/lsp.js +256 -0
  389. package/src/tools/lsp/lsp.ts +274 -0
  390. package/src/tools/lsp/service.js +303 -0
  391. package/src/tools/lsp/service.ts +296 -0
  392. package/src/tools/memory/memory.js +520 -0
  393. package/src/tools/memory/memory.ts +625 -0
  394. package/src/tools/memory/policy.js +53 -0
  395. package/src/tools/memory/policy.ts +72 -0
  396. package/src/tools/notebook/notebookEdit.js +114 -0
  397. package/src/tools/notebook/notebookEdit.ts +126 -0
  398. package/src/tools/powershell/powershell.js +210 -0
  399. package/src/tools/powershell/powershell.ts +239 -0
  400. package/src/tools/presentresult/presentresult.js +145 -0
  401. package/src/tools/presentresult/presentresult.ts +156 -0
  402. package/src/tools/read/read.js +371 -163
  403. package/src/tools/read/read.ts +558 -0
  404. package/src/tools/shared/diffSummary.js +44 -0
  405. package/src/tools/shared/diffSummary.ts +63 -0
  406. package/src/tools/shared/fileActivity.js +51 -0
  407. package/src/tools/shared/fileActivity.ts +66 -0
  408. package/src/tools/shared/fileMutationBus.js +13 -0
  409. package/src/tools/shared/fileMutationBus.ts +14 -0
  410. package/src/tools/shared/fileState.js +73 -65
  411. package/src/tools/shared/fileState.ts +145 -0
  412. package/src/tools/shared/fileUtils.js +81 -4
  413. package/src/tools/shared/fileUtils.ts +504 -0
  414. package/src/tools/shared/killTree.ts +50 -0
  415. package/src/tools/shared/permanentDeleteGuard.js +26 -0
  416. package/src/tools/shared/permanentDeleteGuard.ts +34 -0
  417. package/src/tools/shared/schemas.ts +20 -0
  418. package/src/tools/shared/toolOutputStore.js +97 -0
  419. package/src/tools/shared/toolOutputStore.ts +110 -0
  420. package/src/tools/skill/skill.js +86 -0
  421. package/src/tools/skill/skill.ts +94 -0
  422. package/src/tools/snip/snip.js +67 -0
  423. package/src/tools/snip/snip.ts +80 -0
  424. package/src/tools/tasks/backgroundTaskService.js +275 -0
  425. package/src/tools/tasks/backgroundTaskService.ts +369 -0
  426. package/src/tools/tasks/processEvidence.js +24 -0
  427. package/src/tools/tasks/processEvidence.ts +34 -0
  428. package/src/tools/tasks/taskManagement.js +163 -0
  429. package/src/tools/tasks/taskManagement.ts +180 -0
  430. package/src/tools/tasks/taskOutput.js +113 -0
  431. package/src/tools/tasks/taskOutput.ts +145 -0
  432. package/src/tools/tasks/taskStop.js +74 -0
  433. package/src/tools/tasks/taskStop.ts +99 -0
  434. package/src/tools/tasks/taskStore.js +46 -0
  435. package/src/tools/tasks/taskStore.ts +77 -0
  436. package/src/tools/terminal/terminalCapture.js +167 -0
  437. package/src/tools/terminal/terminalCapture.ts +179 -0
  438. package/src/tools/trash/trash.js +233 -0
  439. package/src/tools/trash/trash.ts +283 -0
  440. package/src/tools/types.js +1 -1
  441. package/src/tools/types.ts +24 -0
  442. package/src/tools/webbrowser/agentBrowserPath.ts +286 -0
  443. package/src/tools/webbrowser/browser.js +214 -11
  444. package/src/tools/webbrowser/browser.ts +293 -0
  445. package/src/tools/webbrowser/browserControl.js +153 -48
  446. package/src/tools/webbrowser/browserControl.ts +262 -0
  447. package/src/tools/webbrowser/browserDaemon.js +147 -39
  448. package/src/tools/webbrowser/browserDaemon.ts +270 -0
  449. package/src/tools/webbrowser/browserOutcome.js +273 -0
  450. package/src/tools/webbrowser/browserOutcome.ts +306 -0
  451. package/src/tools/webbrowser/browserRunRegistry.js +150 -0
  452. package/src/tools/webbrowser/browserRunRegistry.ts +179 -0
  453. package/src/tools/webbrowser/browserService.js +20 -6
  454. package/src/tools/webbrowser/browserService.ts +56 -0
  455. package/src/tools/webbrowser/browserStream.js +30 -1
  456. package/src/tools/webbrowser/browserStream.ts +216 -0
  457. package/src/tools/webbrowser/browserTabs.js +143 -0
  458. package/src/tools/webbrowser/browserTabs.ts +210 -0
  459. package/src/tools/webbrowser/webbrowser.js +831 -150
  460. package/src/tools/webbrowser/webbrowser.ts +1537 -0
  461. package/src/tools/webfetch/preapproved.ts +274 -0
  462. package/src/tools/webfetch/webfetch.js +24 -23
  463. package/src/tools/webfetch/webfetch.ts +383 -0
  464. package/src/tools/websearch/websearch.js +12 -11
  465. package/src/tools/websearch/websearch.ts +292 -0
  466. package/src/tools/worktree/sessionWorktree.js +128 -0
  467. package/src/tools/worktree/sessionWorktree.ts +158 -0
  468. package/src/tools/write/write.js +27 -44
  469. package/src/tools/write/write.ts +136 -0
  470. package/src/types.ts +960 -0
  471. package/src/ui/App.js +216 -12
  472. package/src/ui/App.tsx +879 -0
  473. package/src/ui/AskQuestion.tsx +300 -0
  474. package/src/ui/InputBox.js +77 -1
  475. package/src/ui/InputBox.tsx +567 -0
  476. package/src/ui/LiveArea.tsx +344 -0
  477. package/src/ui/MessageList.js +27 -6
  478. package/src/ui/MessageList.tsx +559 -0
  479. package/src/ui/ModelPicker.tsx +107 -0
  480. package/src/ui/QueuedMessagesBanner.tsx +51 -0
  481. package/src/ui/Root.js +6 -8
  482. package/src/ui/Root.tsx +100 -0
  483. package/src/ui/SessionPicker.tsx +169 -0
  484. package/src/ui/StatusLine.js +22 -1
  485. package/src/ui/StatusLine.tsx +145 -0
  486. package/src/ui/WorkspacePicker.tsx +84 -0
  487. package/src/ui/advisorCommand.js +54 -0
  488. package/src/ui/advisorCommand.ts +63 -0
  489. package/src/ui/computerCommand.js +19 -0
  490. package/src/ui/computerCommand.ts +35 -0
  491. package/src/ui/figures.ts +45 -0
  492. package/src/ui/hooks/askSlot.ts +112 -0
  493. package/src/ui/hooks/useChat.js +271 -7
  494. package/src/ui/hooks/useChat.ts +1578 -0
  495. package/src/ui/hooks/useInputHistory.ts +235 -0
  496. package/src/ui/hooks/usePasteHandler.ts +220 -0
  497. package/src/ui/hooks/useTerminalSize.ts +41 -0
  498. package/src/ui/hooks/useTextBuffer.ts +108 -0
  499. package/src/ui/hooks/useTokenUsage.ts +44 -0
  500. package/src/ui/liveViewport.ts +189 -0
  501. package/src/ui/markdown.ts +230 -0
  502. package/src/ui/textBuffer.ts +239 -0
  503. package/src/ui/theme.ts +35 -0
  504. package/src/utils/clipboardImage.js +358 -0
  505. package/src/utils/clipboardImage.ts +420 -0
  506. package/src/utils/debugLog.ts +50 -0
  507. package/src/utils/greenRoot.ts +35 -0
  508. package/src/utils/packageRoot.ts +40 -0
  509. package/src/utils/pathContains.js +28 -0
  510. package/src/utils/pathContains.ts +31 -0
  511. package/src/utils/resourcePaths.ts +71 -0
  512. package/src/utils/stripThink.ts +49 -0
  513. package/src/utils/zodToJson.ts +73 -0
  514. package/src/web/browserBridge.js +365 -66
  515. package/src/web/browserBridge.ts +672 -0
  516. package/src/web/protocol.ts +1804 -0
  517. package/vendor/computer-use/arm64-darwin/cu-helperd +0 -0
  518. package/workflows/ppt.yaml +13 -2
  519. package/workflows/wechat-reading-shots.yaml +315 -0
  520. package/workflows/word.yaml +11 -1
  521. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  522. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  523. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  524. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  525. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  526. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  527. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  528. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  529. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  530. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  531. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  532. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  533. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  534. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  535. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  536. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  537. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  538. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  539. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  540. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  541. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  542. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  543. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  544. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  545. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  546. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  547. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  548. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  549. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  550. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  551. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  552. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  553. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  554. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  555. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  556. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  557. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  558. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  559. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  560. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  561. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  562. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  563. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  564. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  565. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  566. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  567. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  568. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  569. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  570. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  571. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  572. /package/skills/ai-image/scripts/{generate_test.mjs → generate.selftest.mjs} +0 -0
  573. /package/skills/ai-image/scripts/{generate_from_plan_test.mjs → generate_from_plan.selftest.mjs} +0 -0
@@ -0,0 +1,559 @@
1
+ /**
2
+ * ============================================================
3
+ * src/ui/MessageList.tsx —— 消息历史展示
4
+ * ------------------------------------------------------------
5
+ * 渲染历史消息(system 不显示)。
6
+ * 使用 Ink 5.x 的 <Static> commit 历史到终端 scrollback:
7
+ * 鼠标滚轮可以自然翻阅,性能也更好(已 commit 的行不会再 rerender)。
8
+ *
9
+ * 流式中的 assistant 文本不再在这里"假消息"显示——
10
+ * streaming 阶段不渲染 live answer,turn 结束后再以正式的
11
+ * assistant Message 进入 history 并被 <Static> commit。
12
+ *
13
+ * banner 也走 Static 流(items[0])。
14
+ * Ink 5 的 Static 约定要求 Static 是兄弟节点中第一个,否则它上方的 live
15
+ * 兄弟会跟 Static commit 一起被重打到 stdout(用户实测看到 banner 被
16
+ * 反复打印 N 次)。把 banner 作为 Static 的第一个 item,banner 自然
17
+ * commit 进 scrollback,启动时打印一次,不再反复出现。
18
+ *
19
+ * v2 新增:
20
+ * - assistant 分支渲染 reasoning_content / reasoning(💭 dim gray);
21
+ * 窄终端(rows<28)折叠 2 行 + "▾ N 行 reasoning 折叠"
22
+ * - tool 分支显示 tool_call_id 末 8 字符作为 [tool result · #abc12345],
23
+ * "▾ 完整 N 行(输入 /show xxx 查看)" 提示用户用命令展开
24
+ * - user 分支前插 turn 头横线 "────── Turn N · 14:32 · 8s · 6 tools ──────",
25
+ * turn 边界扫描结果由 useMemo 一次性算出,挂在 StaticItem 上
26
+ *
27
+ * 样式约定:
28
+ * banner: 青色加粗 minimal-agent + 暗灰色 provider/快捷键提示
29
+ * user: 整条青色 + "> " 前缀(输入;前置 turn 横线,若 turnMeta 非空)
30
+ * assistant: reasoning 暗灰带 💭,content 绿色 ⏺ 锚点 + 默认亮色正文(输出),tool_calls 黄色
31
+ * tool: 暗灰色,折叠(默认只显示前 3 行,末尾提示用 /show 查看)
32
+ * ============================================================
33
+ */
34
+
35
+ import React from 'react';
36
+ import { Box, Static, Text } from 'ink';
37
+
38
+ import { friendlyToolDescription } from '../loop.js';
39
+ import { contentToDisplayText, contentToPlainText } from '../multimodal.js';
40
+ import type { Message } from '../types.js';
41
+ import { splitThinkBlocks } from '../utils/stripThink.js';
42
+ import { figures } from './figures.js';
43
+ import { type MdLineKind, renderMarkdownLines } from './markdown.js';
44
+ import { theme } from './theme.js';
45
+
46
+ /**
47
+ * Turn 元信息:每个非中断 user message 是一个 turn 的起点。
48
+ * 由 summarizeTurn() 一次性扫 history 算出,挂在 StaticItem 上喂给 MessageRow。
49
+ */
50
+ export interface TurnMeta {
51
+ turnN: number;
52
+ /** turn 内最后一条 assistant/tool timestamp - user timestamp(缺失 fallback 0) */
53
+ durationMs: number;
54
+ /** turn 内所有 assistant 的 tool_calls.length 累加 */
55
+ toolsCount: number;
56
+ /** user message 的 timestamp(缺失 fallback Date.now()) */
57
+ timestamp: number;
58
+ }
59
+
60
+ /** 中断标记 user message 的固定内容(loop.ts 写入) */
61
+ const INTERRUPT_USER_CONTENT = '(用户按下了 ESC/Ctrl+C 中断了任务)';
62
+
63
+ /** Static items 的混合类型:banner 在 items[0],messages 在后面。 */
64
+ type StaticItem =
65
+ | { kind: 'banner'; text: string; key: string }
66
+ | {
67
+ kind: 'message';
68
+ msg: Message;
69
+ turnMeta: TurnMeta | null;
70
+ /** tool 消息的发起方人话描述(buildToolCallIndex 反查);其余消息 null */
71
+ toolMeta: ToolCallMeta | null;
72
+ key: string;
73
+ };
74
+
75
+ interface Props {
76
+ history: Message[];
77
+ /** compact / clearHistory 触发递增;作为 <Static key={}> 的 remount 触发器 */
78
+ compactGeneration: number;
79
+ /** banner 文案;不传则不渲染 banner 行 */
80
+ bannerText?: string;
81
+ /** 当前终端高度(行数);用于窄终端 reasoning 折叠判断 */
82
+ terminalRows?: number;
83
+ }
84
+
85
+ const MAX_TOOL_PREVIEW_LINES = 3;
86
+ /**
87
+ * 顾问意见的例外:它不是「工具输出」那类可略读的噪声,而是**第二个模型的独立评审**,
88
+ * 用户此刻最想看的就是它说了什么。折叠到 3 行等于藏起来 —— 而 `/advisor ask` 手动路径
89
+ * 反而是 ephemeralLine 全文输出,同一份内容两种待遇。
90
+ *
91
+ * 24 行的依据:顾问 persona 明确要求「只讲最关键的 1-3 点、不复述历史」,一份合规的
92
+ * 评审装得下;真跑长了仍然折叠 + /show,刷屏边界没有放开。
93
+ */
94
+ const MAX_ADVISOR_PREVIEW_LINES = 24;
95
+
96
+ /** tool 结果的预览行数上限(纯函数,单独导出可测)。 */
97
+ export function toolPreviewCap(toolName?: string): number {
98
+ return toolName === 'Advisor' ? MAX_ADVISOR_PREVIEW_LINES : MAX_TOOL_PREVIEW_LINES;
99
+ }
100
+ /** 窄终端 reasoning 折叠阈值(行)—— 小于此值时 reasoning 只显示首 2 行 */
101
+ const NARROW_TERMINAL_ROWS = 28;
102
+ /** reasoning 超过此字符数截尾 —— 防撑爆历史 */
103
+ const REASONING_MAX_CHARS = 2000;
104
+
105
+ export interface AssistantDisplayLine {
106
+ /** prose = 可含 ANSI 的正文行;code = 原文逐字节;fence = ``` 围栏 */
107
+ content: string;
108
+ /**
109
+ * 行前缀。code/fence **恒为 ''**(复制安全断言点:代码顶格零装饰,
110
+ * 复制粘贴即原文);prose 首条非空行 '⏺ '、续行 ' '(视觉锚点,
111
+ * 普通文字复制场景少,保留过程锚更重要)。
112
+ */
113
+ prefix: '⏺ ' | ' ' | '';
114
+ kind: MdLineKind;
115
+ /** wrap='end':Ink 原样输出(不注入换行、不截断),超宽交终端软换行 */
116
+ wrap: 'end';
117
+ }
118
+
119
+ /**
120
+ * 把 assistant 最终正文拆成展示行:markdown 管线(src/ui/markdown.ts)产出
121
+ * prose/code/fence 三类行,⏺ 锚点落在**第一条非空 prose 行**——答案以代码块
122
+ * 开头时代码顶格在前、⏺ 顺延;全代码答案无 ⏺(代码纯净优先)。
123
+ * 纯文本无 markdown 特征时走快路径,与旧「按 \n 切 + wrap='end'」逐行一致。
124
+ */
125
+ export function buildAssistantDisplayLines(text: string): AssistantDisplayLine[] {
126
+ if (text.length === 0) return [];
127
+ let anchorPlaced = false;
128
+ return renderMarkdownLines(text).map((line) => {
129
+ if (line.kind !== 'prose') {
130
+ return { content: line.text, prefix: '' as const, kind: line.kind, wrap: 'end' as const };
131
+ }
132
+ if (!anchorPlaced && line.text.trim() !== '') {
133
+ anchorPlaced = true;
134
+ return { content: line.text, prefix: '⏺ ' as const, kind: line.kind, wrap: 'end' as const };
135
+ }
136
+ return { content: line.text, prefix: ' ' as const, kind: line.kind, wrap: 'end' as const };
137
+ });
138
+ }
139
+
140
+ /** 工具调用元信息:tool 消息头行的「人话描述」来源(从同 turn assistant 反查)。 */
141
+ export interface ToolCallMeta {
142
+ toolName: string;
143
+ friendly: string;
144
+ }
145
+
146
+ /**
147
+ * 扫 history 里全部 assistant.tool_calls,建 tool_call_id → 人话描述 索引。
148
+ * tool 消息本身只有 content + tool_call_id(不动 history 字节的前提下,
149
+ * 头行要显示「哪个工具在干什么」只能反查发起方)。<Static> 提交时序保证
150
+ * assistant(tool_calls) 恒先于对应 tool 消息入 history,索引命中无竞态。
151
+ */
152
+ export function buildToolCallIndex(history: Message[]): Map<string, ToolCallMeta> {
153
+ const map = new Map<string, ToolCallMeta>();
154
+ for (const m of history) {
155
+ if (m.role !== 'assistant' || !m.tool_calls) continue;
156
+ for (const tc of m.tool_calls) {
157
+ map.set(tc.id, {
158
+ toolName: tc.function.name,
159
+ friendly: friendlyToolDescription(tc.function.name, tc.function.arguments),
160
+ });
161
+ }
162
+ }
163
+ return map;
164
+ }
165
+
166
+ /**
167
+ * 从 Agent 子任务报告的统计尾巴(agent.ts 固定格式
168
+ * 「(子任务「x」[type]:N 轮 · M 次工具调用,K 次失败)」)解析统计数。
169
+ * 失败结果(错误文案)不匹配 → null,头行只显示描述不显示 ✓ 统计。
170
+ */
171
+ export function agentResultStats(
172
+ content: string,
173
+ ): { turns: number; tools: number; failed: number } | null {
174
+ const m = /(子任务「[^」]*」\[[^\]]+\]:(\d+) 轮 · (\d+) 次工具调用(?:,(\d+) 次失败)?)/.exec(
175
+ content,
176
+ );
177
+ if (!m) return null;
178
+ return { turns: Number(m[1]), tools: Number(m[2]), failed: m[3] ? Number(m[3]) : 0 };
179
+ }
180
+
181
+ /**
182
+ * 把 assistant message 的两个字符串型 reasoning 字段拼成单字符串。
183
+ * `reasoning_details` 是结构化数组,本期不展开。两个字段任一非空即返回拼接结果;
184
+ * 都空返回 ''。
185
+ *
186
+ * 导出供单测断言。
187
+ */
188
+ export function combineReasoning(
189
+ message: Extract<Message, { role: 'assistant' }>,
190
+ ): string {
191
+ const parts: string[] = [];
192
+ if (
193
+ typeof message.reasoning_content === 'string' &&
194
+ message.reasoning_content.length > 0
195
+ ) {
196
+ parts.push(message.reasoning_content);
197
+ }
198
+ if (typeof message.reasoning === 'string' && message.reasoning.length > 0) {
199
+ parts.push(message.reasoning);
200
+ }
201
+ return parts.join('\n');
202
+ }
203
+
204
+ /**
205
+ * 扫一遍 history,按 user message 切分 turn,返回每条 message 对应的 turnMeta。
206
+ * 返回数组长度 = history.length,与 history 一一对应:
207
+ * - user message(非中断)→ 该 turn 的 TurnMeta(turnN 递增,>=1)
208
+ * - 中断标记 user / assistant / tool / system → null
209
+ *
210
+ * 算法:
211
+ * 1) 先识别"turn 起点":所有非中断 user message
212
+ * 2) 每个 turn 起点 turnN 从 1 递增
213
+ * 3) turn 结束位置 = 下一个 turn 起点之前(或 history 末尾)
214
+ * 4) durationMs = turn 内最后一条 assistant/tool 的 timestamp - user.timestamp
215
+ * 5) toolsCount = turn 内所有 assistant.tool_calls?.length 累加
216
+ *
217
+ * 导出供单测断言。
218
+ */
219
+ export function summarizeTurn(history: Message[]): Array<TurnMeta | null> {
220
+ const result: Array<TurnMeta | null> = new Array(history.length).fill(null);
221
+ // 找出所有 turn 起点的 index
222
+ const turnStartIndices: number[] = [];
223
+ for (let i = 0; i < history.length; i++) {
224
+ const m = history[i];
225
+ if (
226
+ m.role === 'user' &&
227
+ contentToPlainText(m.content) !== INTERRUPT_USER_CONTENT
228
+ ) {
229
+ turnStartIndices.push(i);
230
+ }
231
+ }
232
+ for (let t = 0; t < turnStartIndices.length; t++) {
233
+ const startIdx = turnStartIndices[t];
234
+ const endIdx =
235
+ t + 1 < turnStartIndices.length
236
+ ? turnStartIndices[t + 1] - 1
237
+ : history.length - 1;
238
+ const startMsg = history[startIdx];
239
+ const userTimestamp =
240
+ (startMsg as { timestamp?: number }).timestamp ?? Date.now();
241
+
242
+ let lastResponseTs: number | null = null;
243
+ let toolsCount = 0;
244
+ for (let j = startIdx + 1; j <= endIdx; j++) {
245
+ const m = history[j];
246
+ if (m.role === 'assistant') {
247
+ toolsCount += m.tool_calls?.length ?? 0;
248
+ if (typeof m.timestamp === 'number') lastResponseTs = m.timestamp;
249
+ } else if (m.role === 'tool') {
250
+ if (typeof m.timestamp === 'number') lastResponseTs = m.timestamp;
251
+ }
252
+ }
253
+ const durationMs =
254
+ lastResponseTs !== null ? Math.max(0, lastResponseTs - userTimestamp) : 0;
255
+ result[startIdx] = {
256
+ turnN: t + 1,
257
+ durationMs,
258
+ toolsCount,
259
+ timestamp: userTimestamp,
260
+ };
261
+ }
262
+ return result;
263
+ }
264
+
265
+ /** 格式化 timestamp 为 HH:MM(本地时区) */
266
+ function formatTime(ts: number): string {
267
+ return new Date(ts).toTimeString().slice(0, 5);
268
+ }
269
+
270
+ /** 格式化耗时:<1s / 1.2s / 3m */
271
+ function formatDuration(ms: number): string {
272
+ if (ms < 1000) return '<1s';
273
+ if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
274
+ if (ms < 3_600_000) return `${Math.floor(ms / 60_000)}m`;
275
+ return `${Math.floor(ms / 3_600_000)}h`;
276
+ }
277
+
278
+ /**
279
+ * React.memo 包装。
280
+ *
281
+ * props 浅比较够用:
282
+ * - history 引用变化才需要重算 items(useMemo 依赖正确)
283
+ * - compactGeneration 数字
284
+ * - bannerText / terminalRows 都是基础类型 / 启动后稳定
285
+ *
286
+ * App.tsx 中用 useDeferredValue(chat.history) 把 history 推后到低优先级队列,
287
+ * 配合此 memo,streaming 期间高频 setStreamingText 不再带着 MessageList
288
+ * 重算 items + Static reconcile。
289
+ */
290
+ export const MessageList = React.memo(function MessageList({
291
+ history,
292
+ compactGeneration,
293
+ bannerText,
294
+ terminalRows,
295
+ }: Props): JSX.Element {
296
+ const items: StaticItem[] = React.useMemo(() => {
297
+ const turnMetas = summarizeTurn(history);
298
+ const toolIndex = buildToolCallIndex(history);
299
+ return [
300
+ ...(bannerText
301
+ ? [
302
+ {
303
+ kind: 'banner' as const,
304
+ text: bannerText,
305
+ key: `banner-${bannerText.slice(0, 10)}`,
306
+ },
307
+ ]
308
+ : []),
309
+ ...history.map((m, idx) => ({
310
+ kind: 'message' as const,
311
+ msg: m,
312
+ turnMeta: turnMetas[idx],
313
+ toolMeta: m.role === 'tool' ? (toolIndex.get(m.tool_call_id) ?? null) : null,
314
+ // 纯派生 key:有 id 用 id,没有就用 idx 兜底。绝不在 render 期 mutate 消息对象
315
+ // (<Static> 已 commit 的行永不重绘,key 仅在 append 对账时用,idx 已足够稳定)。
316
+ key: m.id ?? `message-${idx}`,
317
+ })),
318
+ ];
319
+ }, [bannerText, history]);
320
+
321
+ // 窄终端判定(terminalRows 缺失时默认不折叠,保留完整 reasoning)
322
+ const foldReasoning =
323
+ typeof terminalRows === 'number' && terminalRows < NARROW_TERMINAL_ROWS;
324
+
325
+ return (
326
+ <Static items={items} key={compactGeneration}>
327
+ {(item) => {
328
+ if (item.kind === 'banner') {
329
+ return <BannerRow key={item.key} text={item.text} />;
330
+ }
331
+ return (
332
+ <MessageRow
333
+ key={item.key}
334
+ message={item.msg}
335
+ turnMeta={item.turnMeta}
336
+ toolMeta={item.toolMeta}
337
+ foldReasoning={foldReasoning}
338
+ />
339
+ );
340
+ }}
341
+ </Static>
342
+ );
343
+ });
344
+
345
+ function BannerRow({ text }: { text: string }): JSX.Element {
346
+ // text 形如 "minimal-agent · provider/model · /new ... · Ctrl+C 中断"
347
+ // 拆出第一个 token "minimal-agent" 走加粗青色,剩下走暗灰。
348
+ const splitAt = text.indexOf(' ·');
349
+ const head = splitAt >= 0 ? text.slice(0, splitAt) : text;
350
+ const tail = splitAt >= 0 ? text.slice(splitAt) : '';
351
+ return (
352
+ <Box marginBottom={1}>
353
+ <Text color={theme.user} bold>
354
+ {head}
355
+ </Text>
356
+ {tail.length > 0 && <Text color={theme.subtle}>{tail}</Text>}
357
+ </Box>
358
+ );
359
+ }
360
+
361
+ function MessageRow({
362
+ message,
363
+ turnMeta,
364
+ toolMeta,
365
+ foldReasoning,
366
+ }: {
367
+ message: Message;
368
+ turnMeta: TurnMeta | null;
369
+ toolMeta?: ToolCallMeta | null;
370
+ foldReasoning: boolean;
371
+ }): JSX.Element | null {
372
+ switch (message.role) {
373
+ case 'system': {
374
+ // v2.x:ephemeral system message 是 UI 临时提示(slash 命令输出等),
375
+ // 用暗灰单行风格显示;正经 system prompt(_ephemeral=undefined)依然 hide。
376
+ const ephemeral =
377
+ (message as { _ephemeral?: true })._ephemeral === true;
378
+ if (!ephemeral) return null;
379
+ const lines = message.content.split('\n');
380
+ return (
381
+ <Box marginBottom={1} paddingLeft={2}>
382
+ <Box flexDirection="column">
383
+ {lines.map((line, i) => (
384
+ <Text key={`eph-${i}`} color={theme.subtle} dimColor wrap="end">
385
+ {line}
386
+ </Text>
387
+ ))}
388
+ </Box>
389
+ </Box>
390
+ );
391
+ }
392
+ case 'user': {
393
+ const rule = figures.hr.repeat(6);
394
+ const headerLine =
395
+ turnMeta !== null
396
+ ? `${rule} Turn ${turnMeta.turnN} · ${formatTime(turnMeta.timestamp)} · ${formatDuration(turnMeta.durationMs)} · ${turnMeta.toolsCount} tools ${rule}`
397
+ : null;
398
+ return (
399
+ <Box flexDirection="column" marginBottom={1}>
400
+ {headerLine !== null && (
401
+ <Text color={theme.subtle} dimColor>
402
+ {headerLine}
403
+ </Text>
404
+ )}
405
+ <Box>
406
+ <Text color={theme.user} bold>
407
+ {'> '}
408
+ </Text>
409
+ {/* 用户问题正文也染青色:整条青 = "你的输入",与答案"绿 ⏺ + 默认亮色正文"、
410
+ 思维链"灰 💭"形成 青/灰/绿 三色分明的视觉层次。纯 Static,零刷屏风险。 */}
411
+ <Text color={theme.user} wrap="end">
412
+ {contentToDisplayText(message.content)}
413
+ </Text>
414
+ </Box>
415
+ </Box>
416
+ );
417
+ }
418
+ case 'assistant': {
419
+ // assistant 可能只有 tool_calls 没有 content(content 为 null)
420
+ const raw = message.content ?? '';
421
+ // 展示层切分内联 <think>(MiniMax 等把思维链写进 content,内核有意不剥):
422
+ // thought 并入 💭 块(灰 dim + 折叠,与结构化 reasoning 同款待遇),
423
+ // 正文只显示净化后的部分。纯展示层——history 字节零改动(KV 缓存约束)。
424
+ const { thought, answer } = splitThinkBlocks(raw);
425
+ const tcCount = message.tool_calls?.length ?? 0;
426
+
427
+ // reasoning 拼接:结构化字段在前,内联 <think> 在后
428
+ let reasoningText = [combineReasoning(message), thought]
429
+ .filter(Boolean)
430
+ .join('\n');
431
+ let truncatedCharsHint = '';
432
+ if (reasoningText.length > REASONING_MAX_CHARS) {
433
+ const omitted = reasoningText.length - REASONING_MAX_CHARS;
434
+ reasoningText = reasoningText.slice(0, REASONING_MAX_CHARS);
435
+ truncatedCharsHint = `...(${omitted} 字省略)`;
436
+ }
437
+ const reasoningLines =
438
+ reasoningText.length > 0 ? reasoningText.split('\n') : [];
439
+ const visibleReasoningLines = foldReasoning
440
+ ? reasoningLines.slice(0, 2)
441
+ : reasoningLines;
442
+ const hiddenReasoningCount =
443
+ reasoningLines.length - visibleReasoningLines.length;
444
+
445
+ return (
446
+ <Box flexDirection="column" marginBottom={1}>
447
+ {visibleReasoningLines.length > 0 && (
448
+ <Box flexDirection="column">
449
+ {/* wrap='end':Ink 默认 wrap 会经 wrapAnsi(hard:true) 注入真实换行,
450
+ 从思维链复制文本会被硬断行——统一 end(原样输出,交终端软换行) */}
451
+ {visibleReasoningLines.map((line, i) => (
452
+ <Text key={`r-${i}`} color={theme.subtle} dimColor wrap="end">
453
+ {i === 0 ? `${figures.thought} ${line}` : ` ${line}`}
454
+ </Text>
455
+ ))}
456
+ {hiddenReasoningCount > 0 && (
457
+ <Text color={theme.subtle} dimColor>
458
+ {` ${figures.fold} ${hiddenReasoningCount} 行 reasoning 折叠(终端高度 < ${NARROW_TERMINAL_ROWS})`}
459
+ </Text>
460
+ )}
461
+ {truncatedCharsHint.length > 0 && (
462
+ <Text color={theme.subtle} dimColor>
463
+ {` ${truncatedCharsHint}`}
464
+ </Text>
465
+ )}
466
+ </Box>
467
+ )}
468
+ {/* 最终答案:markdown 管线格式化(标题/列表/行内代码/引用),⏺ 锚点落
469
+ 第一条非空 prose 行 + 默认亮色正文(答案是主角)。代码块「零装饰」:
470
+ fence 行 dim、code 行顶格无前缀无染色逐字节原文 —— 复制粘贴即原文。
471
+ 纯 <Static> 渲染(write-once),零刷屏风险,不碰 LiveArea 的 INV-1/INV-2。 */}
472
+ {buildAssistantDisplayLines(answer).map((line, i) => {
473
+ if (line.kind === 'fence') {
474
+ return (
475
+ <Text key={`a-${i}`} color={theme.subtle} dimColor wrap={line.wrap}>
476
+ {line.content}
477
+ </Text>
478
+ );
479
+ }
480
+ if (line.kind === 'code') {
481
+ return (
482
+ <Text key={`a-${i}`} wrap={line.wrap}>
483
+ {line.content.length > 0 ? line.content : ' '}
484
+ </Text>
485
+ );
486
+ }
487
+ return line.prefix === '⏺ ' ? (
488
+ <Box key={`a-${i}`}>
489
+ <Text color={theme.anchor} bold wrap={line.wrap}>
490
+ {line.prefix}
491
+ </Text>
492
+ <Text wrap={line.wrap}>
493
+ {line.content.length > 0 ? line.content : ' '}
494
+ </Text>
495
+ </Box>
496
+ ) : (
497
+ <Text key={`a-${i}`} wrap={line.wrap}>
498
+ {`${line.prefix}${line.content}`.length > 0
499
+ ? `${line.prefix}${line.content}`
500
+ : ' '}
501
+ </Text>
502
+ );
503
+ })}
504
+ {tcCount > 0 && (
505
+ <Text color={theme.warning} dimColor wrap="end">
506
+ {` ${figures.corner} 调用了 ${tcCount} 个工具:${(message.tool_calls ?? [])
507
+ .map((tc) => tc.function.name)
508
+ .join(', ')}`}
509
+ </Text>
510
+ )}
511
+ </Box>
512
+ );
513
+ }
514
+ case 'tool': {
515
+ const lines = message.content.split('\n');
516
+ // 顾问意见给更高的预览上限,并用默认亮色而非 dim 灰 —— 它是另一个模型说的话,
517
+ // 不该和 grep 输出一样被视觉降级(见 MAX_ADVISOR_PREVIEW_LINES)。
518
+ const isAdvisor = toolMeta?.toolName === 'Advisor';
519
+ const previewCap = toolPreviewCap(toolMeta?.toolName);
520
+ const previewLines = lines.slice(0, previewCap);
521
+ const hidden = Math.max(0, lines.length - previewCap);
522
+ const shortId = message.tool_call_id.slice(-8);
523
+ // 头行升级:有 toolMeta(发起方反查命中)显示人话描述;Agent 子任务
524
+ // 再解析统计尾巴显示 ✓ N 轮 · M 次工具。无 meta(历史重载等)兜底旧格式。
525
+ const stats =
526
+ toolMeta?.toolName === 'Agent' ? agentResultStats(message.content) : null;
527
+ const statsTail = stats
528
+ ? ` ${figures.ok} ${stats.turns} 轮 · ${stats.tools} 次工具${stats.failed > 0 ? `(${stats.failed} 次失败)` : ''}`
529
+ : '';
530
+ // 顾问头行单独成文并去掉 dim:friendlyToolDescription 没有 Advisor 分支,
531
+ // 走兜底会显示成一串参数预览,看不出这是「第二个模型的独立意见」。
532
+ const headText = isAdvisor
533
+ ? `[顾问意见 · #${shortId}]`
534
+ : toolMeta
535
+ ? `[${toolMeta.friendly} · #${shortId}]${statsTail}`
536
+ : `[tool result · #${shortId}]`;
537
+ return (
538
+ <Box marginBottom={1} paddingLeft={2}>
539
+ <Box flexDirection="column">
540
+ <Text color={theme.subtle} {...(isAdvisor ? {} : { dimColor: true })} wrap="end">
541
+ {headText}
542
+ </Text>
543
+ {/* 逐行 wrap='end':整段默认 wrap 会注入真实换行,从预览复制会被硬断行 */}
544
+ {previewLines.map((line, i) => (
545
+ <Text key={`p-${i}`} {...(isAdvisor ? {} : { color: theme.subtle })} wrap="end">
546
+ {line.length > 0 ? line : ' '}
547
+ </Text>
548
+ ))}
549
+ {hidden > 0 && (
550
+ <Text color={theme.subtle} dimColor>
551
+ {`... ${figures.fold} 完整 ${lines.length} 行(输入 /show ${shortId} 查看)`}
552
+ </Text>
553
+ )}
554
+ </Box>
555
+ </Box>
556
+ );
557
+ }
558
+ }
559
+ }
@@ -0,0 +1,107 @@
1
+ import React, { useState } from 'react';
2
+ import { Box, Text, useInput } from 'ink';
3
+
4
+ import type { ModelConfig } from '../config/configFile.js';
5
+ import { theme } from './theme.js';
6
+
7
+ interface ModelPickerProps {
8
+ models: ModelConfig[];
9
+ currentModelId: string | null;
10
+ title: string;
11
+ /** null 选项标签(用于顾问模型"不使用");不传则不显示 null 选项 */
12
+ nullOptionLabel?: string;
13
+ onSelect: (modelId: string | null) => void;
14
+ onCancel: () => void;
15
+ }
16
+
17
+ export function ModelPicker({
18
+ models,
19
+ currentModelId,
20
+ title,
21
+ nullOptionLabel,
22
+ onSelect,
23
+ onCancel,
24
+ }: ModelPickerProps): JSX.Element {
25
+ const enabledModels = models.filter((m) => m.enabled && m.apiKey);
26
+ const hasNullOption = Boolean(nullOptionLabel);
27
+ const nullIndex = hasNullOption ? enabledModels.length : -1;
28
+ const listLength = enabledModels.length + (hasNullOption ? 1 : 0);
29
+
30
+ const initialIdx = (() => {
31
+ const idx = enabledModels.findIndex((m) => m.id === currentModelId);
32
+ if (idx >= 0) return idx;
33
+ if (hasNullOption && !currentModelId) return nullIndex;
34
+ return 0;
35
+ })();
36
+
37
+ const [idx, setIdx] = useState(Math.max(0, Math.min(initialIdx, listLength - 1)));
38
+
39
+ useInput((input, key) => {
40
+ if (key.escape || (key.ctrl && input === 'c')) {
41
+ onCancel();
42
+ return;
43
+ }
44
+ if (key.upArrow) {
45
+ setIdx((i) => (i - 1 + listLength) % listLength);
46
+ return;
47
+ }
48
+ if (key.downArrow) {
49
+ setIdx((i) => (i + 1) % listLength);
50
+ return;
51
+ }
52
+ if (key.return) {
53
+ if (hasNullOption && idx === nullIndex) {
54
+ onSelect(null);
55
+ } else {
56
+ const target = enabledModels[idx];
57
+ if (target) onSelect(target.id);
58
+ }
59
+ }
60
+ });
61
+
62
+ return (
63
+ <Box flexDirection="column" paddingX={1}>
64
+ <Box marginBottom={1}>
65
+ <Text color={theme.user} bold>{title}</Text>
66
+ </Box>
67
+ <Text color={theme.subtle}>在已配置的模型中选择;选择后立即生效,后续请求使用新模型。</Text>
68
+ <Box flexDirection="column" marginTop={1}>
69
+ {enabledModels.map((m, i) => {
70
+ const active = i === idx;
71
+ const isCurrent = m.id === currentModelId;
72
+ const displayName = m.displayName || m.model;
73
+ return (
74
+ <Box key={m.id} flexDirection="column">
75
+ <Text color={active ? theme.user : undefined}>
76
+ {active ? '> ' : ' '}
77
+ {isCurrent ? '✓ ' : ' '}
78
+ <Text color={active ? theme.user : undefined} bold={isCurrent}>{displayName}</Text>
79
+ {isCurrent ? <Text color={theme.subtle}> (当前)</Text> : null}
80
+ </Text>
81
+ <Text color={theme.subtle}>
82
+ {' '}
83
+ {m.model} · {formatContextWindow(m.contextWindow)}
84
+ </Text>
85
+ </Box>
86
+ );
87
+ })}
88
+ {hasNullOption && (
89
+ <Text color={idx === nullIndex ? theme.user : theme.subtle}>
90
+ {idx === nullIndex ? '> ' : ' '}
91
+ {!currentModelId ? '✓ ' : ' '}
92
+ {nullOptionLabel}
93
+ </Text>
94
+ )}
95
+ </Box>
96
+ <Box marginTop={1}>
97
+ <Text color={theme.subtle}>↑↓ 选择 · Enter 确认 · ESC 取消</Text>
98
+ </Box>
99
+ </Box>
100
+ );
101
+ }
102
+
103
+ function formatContextWindow(tokens: number): string {
104
+ if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(1)}M context`;
105
+ if (tokens >= 1_000) return `${Math.round(tokens / 1_000)}K context`;
106
+ return `${tokens} context`;
107
+ }