minimal-agent 0.8.2 → 0.10.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 (484) hide show
  1. package/README.md +14 -12
  2. package/package.json +34 -1
  3. package/plugins/cron-runner/.claude-plugin/plugin.json +5 -0
  4. package/plugins/cron-runner/commands/cron.md +26 -0
  5. package/plugins/cron-runner/commands/crons.md +7 -0
  6. package/plugins/cron-runner/plugin.js +34 -0
  7. package/plugins/cron-runner/src/commands.js +348 -0
  8. package/plugins/cron-runner/src/cronTime.js +148 -0
  9. package/plugins/cron-runner/src/lock.js +68 -0
  10. package/plugins/cron-runner/src/runs.js +89 -0
  11. package/plugins/cron-runner/src/scheduleEngine.js +72 -0
  12. package/plugins/cron-runner/src/scheduler.js +355 -0
  13. package/plugins/cron-runner/src/schema.js +140 -0
  14. package/plugins/cron-runner/src/service.js +167 -0
  15. package/plugins/cron-runner/src/store.js +298 -0
  16. package/plugins/cron-runner/src/types.js +1 -0
  17. package/plugins/workflow-runner/commands/workflow.md +4 -4
  18. package/plugins/workflow-runner/commands/workflows.md +1 -1
  19. package/plugins/workflow-runner/plugin.js +3 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +249 -51
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +198 -15
  23. package/plugins/workflow-runner/src/index.js +40 -8
  24. package/plugins/workflow-runner/src/jsonPayload.js +1 -1
  25. package/plugins/workflow-runner/src/loader.js +286 -72
  26. package/plugins/workflow-runner/src/multimodal.js +37 -0
  27. package/plugins/workflow-runner/src/recovery.js +49 -39
  28. package/plugins/workflow-runner/src/runner.js +802 -287
  29. package/plugins/workflow-runner/src/stepExecutors/agent.js +157 -39
  30. package/plugins/workflow-runner/src/stepExecutors/assert.js +16 -3
  31. package/plugins/workflow-runner/src/stepExecutors/llm.js +60 -111
  32. package/plugins/workflow-runner/src/stepExecutors/skill.js +47 -47
  33. package/plugins/workflow-runner/src/stepExecutors/tool.js +76 -10
  34. package/plugins/workflow-runner/src/stepExecutors/verify.js +27 -10
  35. package/plugins/workflow-runner/src/taskIdentity.js +296 -0
  36. package/plugins/workflow-runner/src/types.js +19 -9
  37. package/plugins/workflow-runner/src/workflowState.js +516 -33
  38. package/skills/adversarial-review/SKILL.md +0 -1
  39. package/skills/ai-image/SKILL.md +89 -0
  40. package/skills/ai-image/scripts/generate.mjs +497 -0
  41. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  42. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  43. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  44. package/skills/browser-automation/SKILL.md +47 -0
  45. package/skills/docx/SKILL.md +25 -3
  46. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  53. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  54. package/skills/docx/scripts/opc_check.py +253 -0
  55. package/skills/image-gen-openrouter/SKILL.md +10 -2
  56. package/skills/image-stats/SKILL.md +65 -0
  57. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  58. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  59. package/skills/image-stats/scripts/image_stats.py +368 -0
  60. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  61. package/skills/pptx/SKILL.md +123 -95
  62. package/skills/pptx/design.md +82 -0
  63. package/skills/pptx/examples/README.md +29 -0
  64. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  65. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  66. package/skills/pptx/pptxgenjs.md +41 -0
  67. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  68. package/skills/pptx/scripts/layout_lint.py +293 -0
  69. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  70. package/skills/pptx/scripts/opc_check.py +339 -0
  71. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  72. package/skills/pptx/svg.md +67 -0
  73. package/skills/tts-zh/SKILL.md +19 -2
  74. package/skills/ui-design/SKILL.md +158 -0
  75. package/skills/ui-design/claude_design.md +309 -0
  76. package/skills/ui-design/evals/evals.json +254 -0
  77. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  78. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  79. package/skills/ui-design/references/design-context.md +53 -0
  80. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  81. package/skills/ui-design/references/export-pptx.md +124 -0
  82. package/skills/ui-design/references/layout-composition.md +654 -0
  83. package/skills/ui-design/references/preview-feedback.md +37 -0
  84. package/skills/ui-design/references/source-image-layout.md +189 -0
  85. package/skills/ui-design/references/starter-components.md +78 -0
  86. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  87. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  88. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  89. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  90. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  91. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  92. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  93. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  94. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  95. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  96. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  97. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  98. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  99. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  100. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  140. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  141. package/skills/ui-design/scripts/assemble_material.py +217 -0
  142. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  143. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  144. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  145. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  146. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  147. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  148. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  149. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  150. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  151. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  152. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  153. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  154. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  155. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  156. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  157. package/skills/ui-design/scripts/design_style_check.py +397 -0
  158. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  159. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  160. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  161. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  162. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  163. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  164. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  165. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  166. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  167. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  168. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  169. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  170. package/skills/ui-design/scripts/layout_balance.py +881 -0
  171. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  172. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  173. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  174. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  175. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  176. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  177. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  178. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  179. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  180. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  181. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  182. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  183. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  184. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  185. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  186. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  187. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  188. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  195. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  196. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  205. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  206. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  221. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  222. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  225. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  226. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  239. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  240. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  248. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  249. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  261. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  262. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  275. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  292. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  293. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  316. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  324. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  325. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  328. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  329. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  337. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  338. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  339. package/skills/video-compose/SKILL.md +8 -1
  340. package/skills/web-crawler/SKILL.md +70 -0
  341. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  342. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  343. package/skills/xlsx/SKILL.md +12 -0
  344. package/skills/xlsx/scripts/opc_check.py +253 -0
  345. package/src/advisorState.js +22 -0
  346. package/src/askBridge.js +97 -3
  347. package/src/bootstrap/workingDir.js +12 -1
  348. package/src/cli/args.js +17 -1
  349. package/src/cli/configMenu.js +236 -0
  350. package/src/cli/configWizard.js +7 -5
  351. package/src/cli/print.js +37 -4
  352. package/src/cli/streamJson.js +77 -24
  353. package/src/cli/wizards/advisorWizard.js +147 -0
  354. package/src/cli/wizards/fieldForm.js +149 -0
  355. package/src/cli/wizards/imageWizard.js +60 -0
  356. package/src/cli/wizards/openRouterWizard.js +23 -0
  357. package/src/cli/wizards/providerPresets.js +95 -0
  358. package/src/cli/wizards/providerWizard.js +219 -0
  359. package/src/cli/wizards/searchWizard.js +23 -0
  360. package/src/cli/wizards/ttsWizard.js +34 -0
  361. package/src/config/configFile.js +441 -83
  362. package/src/config/configService.js +446 -0
  363. package/src/config.js +24 -64
  364. package/src/context/agentHome.js +9 -0
  365. package/src/context/archive.js +7 -2
  366. package/src/context/compact.js +8 -5
  367. package/src/context/contextLock.js +81 -0
  368. package/src/context/persistContext.js +30 -11
  369. package/src/context/reactiveCompact.js +4 -2
  370. package/src/context/recentDirs.js +21 -61
  371. package/src/context/sessionPath.js +97 -23
  372. package/src/context/sessionRegistry.js +303 -24
  373. package/src/context/tokenCounter.js +18 -0
  374. package/src/context/workspaceIndex.js +731 -0
  375. package/src/infra/verification.js +28 -15
  376. package/src/llm/client.js +394 -159
  377. package/src/llm/providers/anthropicFormat.js +241 -0
  378. package/src/llm/providers/openaiFormat.js +190 -0
  379. package/src/loop.js +219 -32
  380. package/src/main.js +61 -11
  381. package/src/messageQueue.js +97 -0
  382. package/src/multimodal.js +203 -0
  383. package/src/plugin-sdk.js +15 -0
  384. package/src/plugins/commandRouter.js +3 -0
  385. package/src/plugins/hookEngine.js +5 -1
  386. package/src/plugins/pluginLifecycle.js +28 -0
  387. package/src/plugins/pluginLoader.js +2 -1
  388. package/src/plugins/pluginRunner.js +49 -21
  389. package/src/plugins/transcript.js +4 -3
  390. package/src/prompts/pluginList.js +5 -5
  391. package/src/prompts/projectInstructions.js +3 -3
  392. package/src/prompts/promptAssembly.js +133 -144
  393. package/src/prompts/skillList.js +12 -22
  394. package/src/prompts/system.js +0 -1
  395. package/src/session/runAgentTurn.js +70 -0
  396. package/src/session/runTurn.js +41 -6
  397. package/src/toolGate.js +17 -0
  398. package/src/tools/advisor/advisor.js +379 -0
  399. package/src/tools/agent/agent.js +555 -0
  400. package/src/tools/agent/agentTypes.js +59 -0
  401. package/src/tools/agent/coordinator.js +93 -0
  402. package/src/tools/agent/worktree.js +270 -0
  403. package/src/tools/ask/ask.js +7 -2
  404. package/src/tools/bash/bash.js +112 -18
  405. package/src/tools/download/download.js +144 -0
  406. package/src/tools/edit/edit.js +1 -0
  407. package/src/tools/edit/multi-edit.js +1 -0
  408. package/src/tools/genimage/genimage.js +242 -0
  409. package/src/tools/genspeech/genspeech.js +67 -0
  410. package/src/tools/glob/glob.js +1 -0
  411. package/src/tools/grep/grep.js +3 -0
  412. package/src/tools/grep/rgPath.js +2 -0
  413. package/src/tools/index.js +25 -2
  414. package/src/tools/read/read.js +1 -0
  415. package/src/tools/shared/killTree.js +52 -0
  416. package/src/tools/toolProgressBridge.js +48 -0
  417. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  418. package/src/tools/webbrowser/browser.js +59 -42
  419. package/src/tools/webbrowser/browserControl.js +118 -0
  420. package/src/tools/webbrowser/browserDaemon.js +97 -0
  421. package/src/tools/webbrowser/browserService.js +27 -0
  422. package/src/tools/webbrowser/browserStream.js +119 -0
  423. package/src/tools/webbrowser/webbrowser.js +699 -130
  424. package/src/tools/webfetch/webfetch.js +1 -0
  425. package/src/tools/websearch/websearch.js +134 -51
  426. package/src/tools/write/write.js +1 -0
  427. package/src/ui/App.js +249 -22
  428. package/src/ui/AskQuestion.js +29 -10
  429. package/src/ui/InputBox.js +104 -12
  430. package/src/ui/LiveArea.js +112 -19
  431. package/src/ui/MessageList.js +100 -14
  432. package/src/ui/ModelPicker.js +56 -0
  433. package/src/ui/QueuedMessagesBanner.js +14 -0
  434. package/src/ui/Root.js +18 -10
  435. package/src/ui/SessionPicker.js +102 -0
  436. package/src/ui/StatusLine.js +13 -4
  437. package/src/ui/WorkspacePicker.js +55 -0
  438. package/src/ui/figures.js +44 -0
  439. package/src/ui/hooks/askSlot.js +82 -0
  440. package/src/ui/hooks/useChat.js +358 -64
  441. package/src/ui/hooks/usePasteHandler.js +11 -1
  442. package/src/ui/hooks/useTextBuffer.js +1 -3
  443. package/src/ui/liveViewport.js +5 -1
  444. package/src/ui/markdown.js +206 -0
  445. package/src/ui/textBuffer.js +0 -7
  446. package/src/ui/theme.js +32 -0
  447. package/src/utils/debugLog.js +0 -4
  448. package/src/utils/resourcePaths.js +2 -2
  449. package/src/utils/stripThink.js +50 -0
  450. package/src/web/browserBridge.js +447 -0
  451. package/src/web/protocol.js +19 -0
  452. package/workflows/adaptive-research.yaml +13 -5
  453. package/workflows/book-review-short.yaml +36 -10
  454. package/workflows/e2e-write-greet.yaml +6 -2
  455. package/workflows/illustrate.yaml +227 -0
  456. package/workflows/plan.yaml +460 -179
  457. package/workflows/ppt.yaml +1659 -0
  458. package/workflows/ralph-loop.yaml +199 -0
  459. package/workflows/schema.json +81 -23
  460. package/workflows/webppt.yaml +496 -0
  461. package/workflows/word.yaml +710 -0
  462. package/workflows/youtube-shorts.yaml +46 -15
  463. package/MINIMAL-TRACE.md +0 -77
  464. package/plugins/HOW-TO-WRITE-A-PLUGIN.md +0 -186
  465. package/plugins/ralph-wiggum/.claude-plugin/plugin.json +0 -9
  466. package/plugins/ralph-wiggum/README.md +0 -197
  467. package/plugins/ralph-wiggum/commands/cancel-ralph.md +0 -18
  468. package/plugins/ralph-wiggum/commands/help.md +0 -137
  469. package/plugins/ralph-wiggum/commands/ralph-loop.md +0 -85
  470. package/plugins/ralph-wiggum/hooks/hooks.json +0 -15
  471. package/plugins/ralph-wiggum/hooks/stop-hook.sh +0 -191
  472. package/plugins/ralph-wiggum/plugin.js +0 -280
  473. package/plugins/ralph-wiggum/src/goalState.js +0 -258
  474. package/plugins/ralph-wiggum/src/sentinels.js +0 -76
  475. package/plugins/ralph-wiggum/src/stopHookRunner.js +0 -104
  476. package/plugins/ralph-wiggum/src/verificationGate.js +0 -202
  477. package/plugins/workflow-runner/src/miniReAct.js +0 -223
  478. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  479. package/skills/config/SKILL.md +0 -123
  480. package/skills/frontend-design/LICENSE.txt +0 -177
  481. package/skills/frontend-design/SKILL.md +0 -42
  482. package/src/ui/ProgressPanel.js +0 -54
  483. package/src/ui/StreamingBlock.js +0 -11
  484. package/src/ui/ToolStatus.js +0 -11
@@ -313,5 +313,6 @@ export const webfetchTool = {
313
313
  isReadOnly: true,
314
314
  isConcurrencySafe: true,
315
315
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
316
+ interruptBehavior: () => 'cancel',
316
317
  call,
317
318
  };
@@ -3,6 +3,8 @@
3
3
  * src/tools/websearch.ts —— WebSearch 工具(联网搜索)
4
4
  * ------------------------------------------------------------
5
5
  * 底层调用 Tavily Search API(https://tavily.com/)。
6
+ * 发请求带退避重试(见下「4b. 退避重试」):并发突发 + dev/免费 key 被前端切 socket
7
+ * 的瞬态不再直接当失败喂回 LLM。次数由 TAVILY_MAX_RETRIES 控(默认 3)。
6
8
  *
7
9
  * 为什么选 Tavily:
8
10
  * - LLM 友好:返回结构化 JSON,自带 content 摘要;不用我们再去抓 HTML
@@ -11,7 +13,7 @@
11
13
  *
12
14
  * Key 配置:
13
15
  * 在项目根 .env 里加:
14
- * TAVILY_API_KEY=tvly-xxxxxxxxxxxxxxxx
16
+ * TAVILY_API_KEY=
15
17
  * 没设的话,工具会返回友好错误(不影响 agent 启动)。
16
18
  *
17
19
  * 输入:
@@ -29,6 +31,7 @@
29
31
  * ============================================================
30
32
  */
31
33
  import { z } from 'zod';
34
+ import { configService } from '../../config/configService.js';
32
35
  import { DEFAULT_MAX_RESULT_SIZE_CHARS } from '../types.js';
33
36
  import { toToolParameters } from '../../utils/zodToJson.js';
34
37
  // ---------------- 1. Zod 输入 schema ----------------
@@ -62,17 +65,126 @@ const description = `- Searches the public web via the Tavily Search API and ret
62
65
  - Returns the top N results, each with a title, URL, and content snippet. With \`search_depth: "advanced"\` Tavily also returns a synthesized answer at the top.
63
66
  - Prefer specific natural-language queries over keyword soup (e.g. "how does Bun handle .env files in version 1.1").
64
67
  - Requires the TAVILY_API_KEY environment variable to be set; if missing the tool returns a friendly error.`;
68
+ // ---------------- 4b. 退避重试 ----------------
69
+ //
70
+ // 为什么需要(与单发 fetch 零重试的旧行为对比):WebSearch 是 isConcurrencySafe 工具,
71
+ // 主循环把同一轮的多个 WebSearch 调用 **并行**发火(loop.ts,Promise.all)。研究类
72
+ // prompt 又常让模型「围绕 3–6 个子问题分别检索」——于是一瞬间 3~6 条连接同时打向
73
+ // 同一个 Tavily key。dev / 免费 ティア 的并发/速率上限被触发时,CDN 前端常**直接切断
74
+ // socket** 而非返回干净的 429(错误落进 fetch 的 catch:「socket connection closed
75
+ // unexpectedly」)。旧代码单发 fetch、零重试,这类瞬态会被直接当失败喂回 LLM ——
76
+ // 表现为「突然连续搜索失败,但单发检索仍通」。
77
+ //
78
+ // 对策与 llm/client.ts 的自适应退避同思想(本地、有界、带抖动):
79
+ // 可重试:fetch 抛错(socket 切断/超时)+ HTTP 429 + 5xx(尊重 Retry-After)。
80
+ // 不重试:用户中断(signal)、其它 4xx(401 key 错 / 400 参数错,重试无意义)、
81
+ // JSON 解析失败(body 已到、重发大概率同样坏)。
82
+ // 次数:默认 3 次重试(共 4 次尝试),可用 TAVILY_MAX_RETRIES 覆盖。
83
+ // 抖动:±15% 把并发重试**散开**,避免落 socket 的几条同时再殺到、又一起被切。
84
+ const TAVILY_ENDPOINT = 'https://api.tavily.com/search';
85
+ function tavilyMaxRetries() {
86
+ const n = Number(process.env.TAVILY_MAX_RETRIES);
87
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 3;
88
+ }
89
+ /** 退避时长(ms):优先 Retry-After;否则指数 500·2^n 封顶 8s,再叠 ±15% 抖动。 */
90
+ function backoffDelayMs(attempt, retryAfterMs) {
91
+ if (retryAfterMs && retryAfterMs > 0)
92
+ return Math.min(retryAfterMs, 15000);
93
+ const capped = Math.min(500 * 2 ** attempt, 8000);
94
+ return Math.round(capped * (0.85 + Math.random() * 0.3));
95
+ }
96
+ /** Retry-After 头解析(秒数 或 HTTP-date)→ ms;无法解析返回 undefined。 */
97
+ function parseRetryAfter(h) {
98
+ if (!h)
99
+ return undefined;
100
+ const secs = Number(h);
101
+ if (Number.isFinite(secs))
102
+ return Math.max(0, secs * 1000);
103
+ const at = Date.parse(h);
104
+ return Number.isNaN(at) ? undefined : Math.max(0, at - Date.now());
105
+ }
106
+ /** 可中断退避:signal abort 时立即 resolve(让外层据 signal 收口,不在这里抛)。 */
107
+ function sleep(ms, signal) {
108
+ return new Promise((resolve) => {
109
+ if (signal?.aborted)
110
+ return resolve();
111
+ const onAbort = () => {
112
+ clearTimeout(timer);
113
+ resolve();
114
+ };
115
+ const timer = setTimeout(() => {
116
+ signal?.removeEventListener('abort', onAbort);
117
+ resolve();
118
+ }, ms);
119
+ signal?.addEventListener('abort', onAbort, { once: true });
120
+ });
121
+ }
122
+ /** 发 Tavily 请求并按上述策略重试。成功返回已解析的 data,失败返回 friendly error。 */
123
+ async function fetchTavilyWithRetry(body, signal) {
124
+ const maxRetries = tavilyMaxRetries();
125
+ let lastErr = '';
126
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
127
+ if (signal?.aborted)
128
+ return { ok: false, error: '搜索已被中断' };
129
+ let res;
130
+ try {
131
+ res = await fetch(TAVILY_ENDPOINT, {
132
+ method: 'POST',
133
+ headers: { 'content-type': 'application/json' },
134
+ body: JSON.stringify(body),
135
+ signal,
136
+ });
137
+ }
138
+ catch (e) {
139
+ if (signal?.aborted)
140
+ return { ok: false, error: '搜索已被中断' };
141
+ lastErr = `调用 Tavily 失败(网络):${e.message}`;
142
+ if (attempt < maxRetries) {
143
+ await sleep(backoffDelayMs(attempt), signal);
144
+ continue;
145
+ }
146
+ break;
147
+ }
148
+ // 429 / 5xx:服务端瞬态(限流 / 过载 / 切 socket 后偶发的 5xx)→ 可重试
149
+ if (res.status === 429 || res.status >= 500) {
150
+ const text = await res.text().catch(() => '');
151
+ lastErr = `Tavily HTTP ${res.status}:${text.slice(0, 300)}`;
152
+ if (attempt < maxRetries) {
153
+ const wait = backoffDelayMs(attempt, parseRetryAfter(res.headers.get('retry-after')));
154
+ await sleep(wait, signal);
155
+ continue;
156
+ }
157
+ break;
158
+ }
159
+ // 其它 4xx(401 key 无效 / 400 参数错…):重试无意义,立即返回
160
+ if (!res.ok) {
161
+ const text = await res.text().catch(() => '');
162
+ return { ok: false, error: `Tavily HTTP ${res.status}:${text.slice(0, 500)}` };
163
+ }
164
+ // 解析 JSON(坏响应不重试——body 已到、重发大概率同样坏)
165
+ try {
166
+ const data = (await res.json());
167
+ return { ok: true, data };
168
+ }
169
+ catch (e) {
170
+ return { ok: false, error: `Tavily 响应不是合法 JSON:${e.message}` };
171
+ }
172
+ }
173
+ return {
174
+ ok: false,
175
+ error: `${lastErr}(已重试 ${maxRetries} 次仍失败——并发突发下 dev/免费 key 易被前端切 socket;` +
176
+ `可减少同轮并发检索数、调大 TAVILY_MAX_RETRIES,或升级 Tavily key)`,
177
+ };
178
+ }
65
179
  // ---------------- 5. call() ----------------
66
- async function call(input, signal) {
180
+ async function call(input, signal, getApiKey = async () => (await configService.tool('webSearch'))?.apiKey) {
67
181
  // 5a. 取 key
68
- const apiKey = process.env.TAVILY_API_KEY;
182
+ const apiKey = await getApiKey();
69
183
  if (!apiKey) {
70
184
  return {
71
185
  ok: false,
72
- error: 'WebSearch 不可用:环境变量 TAVILY_API_KEY 未设置。\n' +
73
- '请在项目根 .env 里加:\n' +
74
- ' TAVILY_API_KEY=tvly-你的key\n' +
75
- '免费 key 申请:https://tavily.com/',
186
+ error: 'WebSearch 不可用:尚未配置 Tavily API Key。\n' +
187
+ '请在 KakadeAI 设置 → 工具 → 联网搜索中配置,或在 env 模式提供 TAVILY_API_KEY。',
76
188
  };
77
189
  }
78
190
  // 5b. 自动追加当前日期到 query(强制时间限制,防止搜索到过时信息)
@@ -89,40 +201,12 @@ async function call(input, signal) {
89
201
  // 让 advanced 模式自动给 answer;basic 不会有
90
202
  include_answer: input.search_depth === 'advanced',
91
203
  };
92
- // 5c. 发请求
93
- let res;
94
- try {
95
- res = await fetch('https://api.tavily.com/search', {
96
- method: 'POST',
97
- headers: { 'content-type': 'application/json' },
98
- body: JSON.stringify(body),
99
- signal,
100
- });
101
- }
102
- catch (e) {
103
- return {
104
- ok: false,
105
- error: `调用 Tavily 失败(网络):${e.message}`,
106
- };
107
- }
108
- if (!res.ok) {
109
- const text = await res.text().catch(() => '');
110
- return {
111
- ok: false,
112
- error: `Tavily HTTP ${res.status}:${text.slice(0, 500)}`,
113
- };
114
- }
115
- // 5d. 解析
116
- let data;
117
- try {
118
- data = (await res.json());
119
- }
120
- catch (e) {
121
- return {
122
- ok: false,
123
- error: `Tavily 响应不是合法 JSON:${e.message}`,
124
- };
125
- }
204
+ // 5c. 发请求(带退避重试,吸收 dev/免费 key 在并发突发下的 socket 切断 + 429/5xx)
205
+ const fetched = await fetchTavilyWithRetry(body, signal);
206
+ if (!fetched.ok)
207
+ return { ok: false, error: fetched.error };
208
+ const data = fetched.data;
209
+ // 5d. 取结果
126
210
  const results = data.results ?? [];
127
211
  if (results.length === 0) {
128
212
  return {
@@ -149,13 +233,12 @@ async function call(input, signal) {
149
233
  return { ok: true, content };
150
234
  }
151
235
  // ---------------- 6. 导出 ----------------
152
- export const webSearchTool = {
153
- name: 'WebSearch',
154
- description,
155
- inputSchema,
156
- parameters,
157
- isReadOnly: true,
158
- isConcurrencySafe: true,
159
- maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
160
- call,
161
- };
236
+ export function createWebSearchTool(getApiKey = async () => (await configService.tool('webSearch'))?.apiKey) {
237
+ return {
238
+ name: 'WebSearch', description, inputSchema, parameters, isReadOnly: true,
239
+ isConcurrencySafe: true, maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
240
+ interruptBehavior: () => 'cancel',
241
+ call: (input, signal) => call(input, signal, getApiKey),
242
+ };
243
+ }
244
+ export const webSearchTool = createWebSearchTool();
@@ -129,5 +129,6 @@ export const writeTool = {
129
129
  isReadOnly: false,
130
130
  isConcurrencySafe: false,
131
131
  maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
132
+ interruptBehavior: () => 'block',
132
133
  call,
133
134
  };
package/src/ui/App.js CHANGED
@@ -10,15 +10,26 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
10
  import React, { useDeferredValue } from 'react';
11
11
  import { Box, Text } from 'ink';
12
12
  import { getWorkingDir } from '../bootstrap/workingDir.js';
13
+ import { ConfigMenu } from '../cli/configMenu.js';
14
+ import { configService } from '../config/configService.js';
13
15
  import { listArchives, restoreArchive } from '../context/archive.js';
14
16
  import { saveContext } from '../context/persistContext.js';
15
17
  import { sessionFileFor } from '../context/sessionPath.js';
16
- import { listSessions } from '../context/sessionRegistry.js';
18
+ import { workspaceIndexService } from '../context/workspaceIndex.js';
19
+ import { createSession, listSessionSummaries, updateSessionMetadata, } from '../context/sessionRegistry.js';
20
+ import { activatePlugins } from '../plugins/pluginLifecycle.js';
21
+ import { runAgentTurn } from '../session/runAgentTurn.js';
22
+ import { runAdvisor } from '../tools/advisor/advisor.js';
17
23
  import { AskQuestion } from './AskQuestion.js';
18
24
  import { InputBox } from './InputBox.js';
19
25
  import { LiveArea } from './LiveArea.js';
26
+ import { QueuedMessagesBanner } from './QueuedMessagesBanner.js';
20
27
  import { MessageList } from './MessageList.js';
28
+ import { ModelPicker } from './ModelPicker.js';
29
+ import { SessionPicker } from './SessionPicker.js';
30
+ import { WorkspacePicker } from './WorkspacePicker.js';
21
31
  import { StatusLine } from './StatusLine.js';
32
+ import { theme } from './theme.js';
22
33
  import { useChat } from './hooks/useChat.js';
23
34
  import { useTerminalSize } from './hooks/useTerminalSize.js';
24
35
  /**
@@ -33,6 +44,13 @@ import { useTerminalSize } from './hooks/useTerminalSize.js';
33
44
  * 走 Ink 渲染管线,否则会破坏 log-update 计数。
34
45
  */
35
46
  const CLEAR_SCREEN = '\x1b[2J\x1b[H';
47
+ function formatProviderName(apiFormat) {
48
+ if (apiFormat === 'anthropic')
49
+ return 'Anthropic';
50
+ if (apiFormat === 'openai')
51
+ return 'OpenAI兼容';
52
+ return apiFormat;
53
+ }
36
54
  export function App({ provider, initialHistory }) {
37
55
  // v2 multi-session:持久化路径完全由 useChat 内部管控(按 activeSessionId 选文件)。
38
56
  // App 不再传 onPersist —— 之前 App 端调 saveContext(messages) 会污染 default session。
@@ -44,6 +62,27 @@ export function App({ provider, initialHistory }) {
44
62
  // 终端尺寸感知:rows → MessageList 决定窄终端是否折叠 reasoning + LiveArea 收敛预览高;
45
63
  // cols → LiveArea 逐行截断防换行
46
64
  const { rows: terminalRows, cols: terminalCols } = useTerminalSize();
65
+ React.useEffect(() => {
66
+ const controller = new AbortController();
67
+ let cleanup;
68
+ void activatePlugins({
69
+ provider,
70
+ signal: controller.signal,
71
+ runAgentTurn,
72
+ }).then((fn) => {
73
+ if (controller.signal.aborted) {
74
+ fn();
75
+ return;
76
+ }
77
+ cleanup = fn;
78
+ }).catch((err) => {
79
+ console.warn(`[minimal-agent] plugin activation failed: ${err instanceof Error ? err.message : String(err)}`);
80
+ });
81
+ return () => {
82
+ controller.abort();
83
+ cleanup?.();
84
+ };
85
+ }, [provider]);
47
86
  /**
48
87
  * 用 useDeferredValue 把 history 推后到低优先级渲染队列。
49
88
  *
@@ -73,6 +112,88 @@ export function App({ provider, initialHistory }) {
73
112
  React.useEffect(() => {
74
113
  historyRef.current = chat.history;
75
114
  }, [chat.history]);
115
+ // /session /sessions /resume(裸命令)打开的选择器数据;非 null 时顶替 InputBox。
116
+ // 纯本地 UI 态,不需要塞进 useChat——不像 pendingAsk 那样由 askBridge 从外部注入,
117
+ // 这里完全由 handleSubmit 自己的分支触发/清空。
118
+ const [sessionPicker, setSessionPicker] = React.useState(null);
119
+ const [workspacePicker, setWorkspacePicker] = React.useState(null);
120
+ // /config —— 硬编码菜单覆盖层,纯本地 UI 态(与 sessionPicker 同构)。
121
+ const [configMenuOpen, setConfigMenuOpen] = React.useState(false);
122
+ // /model —— 基座模型选择器;/advisor select —— 顾问模型选择器
123
+ const [baseModelPicker, setBaseModelPicker] = React.useState(null);
124
+ const [advisorModelPicker, setAdvisorModelPicker] = React.useState(null);
125
+ // 强制刷新计数,用于切换模型后触发 StatusLine 重渲染
126
+ const [, forceRerender] = React.useState(0);
127
+ const handleSessionSelect = React.useCallback(async (id) => {
128
+ setSessionPicker(null);
129
+ await chat.switchSession(id);
130
+ chat.ephemeralLine(`[/session] 已切换到 session "${id}"`);
131
+ }, [chat.switchSession, chat.ephemeralLine]);
132
+ const handleSessionCreate = React.useCallback(async (title) => {
133
+ setSessionPicker(null);
134
+ const summary = await createSession(getWorkingDir(), title ? { title } : {});
135
+ await chat.switchSession(summary.id);
136
+ chat.ephemeralLine(`[/session] 已新建并切换到 session "${summary.id}"(${summary.title})`);
137
+ }, [chat.switchSession, chat.ephemeralLine]);
138
+ const openWorkspacePicker = React.useCallback(async () => {
139
+ if (chat.isLoading)
140
+ return;
141
+ setSessionPicker(null);
142
+ setConfigMenuOpen(false);
143
+ setWorkspacePicker(await workspaceIndexService.list());
144
+ }, [chat.isLoading]);
145
+ const handleWorkspaceSelect = React.useCallback(async (cwd, sessionId) => {
146
+ const switched = await chat.switchWorkspace(cwd, sessionId);
147
+ setWorkspacePicker(null);
148
+ if (switched) {
149
+ chat.ephemeralLine(`[/workspaces] 已切换到 ${cwd} · ${sessionId}`);
150
+ }
151
+ }, [chat.switchWorkspace, chat.ephemeralLine]);
152
+ const handleBaseModelSelect = React.useCallback(async (modelId) => {
153
+ setBaseModelPicker(null);
154
+ if (!modelId)
155
+ return;
156
+ const snap = await configService.getSnapshot();
157
+ const model = snap.config?.models.find((m) => m.id === modelId);
158
+ if (!model) {
159
+ chat.ephemeralLine(`[/model] 未找到模型 "${modelId}"`);
160
+ return;
161
+ }
162
+ const newProvider = await configService.providerForModel(modelId);
163
+ if (newProvider) {
164
+ await updateSessionMetadata(getWorkingDir(), chat.activeSessionId, { modelId });
165
+ chat.setProvider(newProvider);
166
+ forceRerender((v) => v + 1);
167
+ chat.ephemeralLine(`[/model] 当前 Session 已切换为 ${model.displayName || model.model}(${formatProviderName(model.apiFormat)}),下一轮对话生效。`);
168
+ }
169
+ }, [chat.activeSessionId, chat.setProvider, chat.ephemeralLine]);
170
+ React.useEffect(() => {
171
+ let cancelled = false;
172
+ void listSessionSummaries(getWorkingDir()).then(async (sessions) => {
173
+ const modelId = sessions.find((session) => session.id === chat.activeSessionId)?.modelId;
174
+ const provider = await configService.providerForModel(modelId ?? null)
175
+ ?? await configService.providerForModel(null);
176
+ if (!cancelled && provider)
177
+ chat.setProvider(provider);
178
+ }).catch(() => { });
179
+ return () => { cancelled = true; };
180
+ }, [chat.activeSessionId, chat.setProvider]);
181
+ const handleAdvisorModelSelect = React.useCallback(async (modelId) => {
182
+ setAdvisorModelPicker(null);
183
+ if (modelId === null) {
184
+ await configService.savePatch({ advisor: { modelId: null, enabled: false } });
185
+ chat.ephemeralLine('[/advisor] 已关闭顾问模型。');
186
+ return;
187
+ }
188
+ const snap = await configService.getSnapshot();
189
+ const model = snap.config?.models.find((m) => m.id === modelId);
190
+ if (!model) {
191
+ chat.ephemeralLine(`[/advisor] 未找到模型 "${modelId}"`);
192
+ return;
193
+ }
194
+ await configService.savePatch({ advisor: { modelId, enabled: true } });
195
+ chat.ephemeralLine(`[/advisor] 已设置顾问模型为 ${model.displayName || model.model}(${formatProviderName(model.apiFormat)})。`);
196
+ }, [chat.ephemeralLine]);
76
197
  /**
77
198
  * v2 slash 命令拦截层。
78
199
  *
@@ -85,31 +206,36 @@ export function App({ provider, initialHistory }) {
85
206
  * previousLineCount 计数,下一帧 eraseLines 用过期 N 去清会留下脏行。
86
207
  *
87
208
  * 支持:
88
- * /session <name> — 切到指定 session(同 cwd)
89
- * /sessions — 列当前 cwd 下所有 session
209
+ * /session /sessions /resume(裸命令) — 列出当前 cwd 下所有 session,弹选择器
210
+ * /session <id> /resume <id> — 直接切换到指定 session
211
+ * /session new [title] — 新建 session 并切过去
90
212
  * /list-archive — 列归档
91
213
  * /restore <id> — 恢复归档为当前 session 历史(覆盖+重读)
92
214
  * /show <id-suffix> — 打印 tool_call_id 末尾匹配的完整 tool result
93
215
  */
94
216
  const handleSubmit = React.useCallback(async (text) => {
95
- // /session <name>
96
- if (text.startsWith('/session ')) {
97
- const id = text.slice('/session '.length).trim();
98
- if (id.length === 0) {
99
- chat.ephemeralLine('[/session] 用法:/session <name>');
100
- return;
101
- }
102
- await chat.switchSession(id);
103
- chat.ephemeralLine(`[/session] 已切换到 session "${id}"`);
217
+ const trimmed = text.trim();
218
+ const isBareSessionCmd = trimmed === '/session' || trimmed === '/sessions' || trimmed === '/resume';
219
+ const sessionArgMatch = /^\/(?:session|resume)\s+(.+)$/.exec(trimmed);
220
+ if (trimmed === '/workspaces') {
221
+ await openWorkspacePicker();
104
222
  return;
105
223
  }
106
- // /sessions
107
- if (text.trim() === '/sessions') {
108
- const ids = await listSessions(getWorkingDir());
109
- const lines = ids
110
- .map((id) => (id === chat.activeSessionId ? `* ${id}` : ` ${id}`))
111
- .join('\n');
112
- chat.ephemeralLine(`[/sessions] 当前 cwd 下的 session(* = 激活):\n${lines}`);
224
+ if (isBareSessionCmd) {
225
+ const sessions = await listSessionSummaries(getWorkingDir());
226
+ setSessionPicker(sessions);
227
+ return;
228
+ }
229
+ if (sessionArgMatch) {
230
+ const arg = sessionArgMatch[1].trim();
231
+ // "new" 必须是独立词(后面跟空格或到此为止),避免 "/session newton" 被误
232
+ // 拆成 "new" + 标题 "ton"——真实 session id 恰好以 "new" 开头时会撞上。
233
+ if (arg === 'new' || arg.startsWith('new ')) {
234
+ const title = arg.slice('new'.length).trim();
235
+ await handleSessionCreate(title);
236
+ return;
237
+ }
238
+ await handleSessionSelect(arg);
113
239
  return;
114
240
  }
115
241
  // /list-archive
@@ -173,6 +299,99 @@ export function App({ provider, initialHistory }) {
173
299
  void chat.submit(`/workflow plan "${goal}"`);
174
300
  return;
175
301
  }
302
+ // /model —— 列出已配置的基座模型并切换选择器;/model <id> 直接切换
303
+ const modelArgMatch = /^\/model(?:\s+(.+))?$/.exec(trimmed);
304
+ if (modelArgMatch) {
305
+ const arg = modelArgMatch[1]?.trim();
306
+ const models = await configService.models();
307
+ const enabledModels = models.filter((m) => m.enabled && m.apiKey);
308
+ if (enabledModels.length === 0) {
309
+ chat.ephemeralLine('[/model] 尚未配置任何模型,请先通过 Desktop 设置或 /config 添加模型。');
310
+ return;
311
+ }
312
+ if (arg) {
313
+ // 直接切换到指定模型(支持精确匹配 id 或模糊匹配 displayName/model)
314
+ const target = enabledModels.find((m) => m.id === arg || m.model === arg || m.displayName === arg);
315
+ if (!target) {
316
+ chat.ephemeralLine(`[/model] 未找到模型 "${arg}",可用模型:\n${enabledModels.map((m) => ` ${m.id} · ${m.displayName || m.model}`).join('\n')}`);
317
+ return;
318
+ }
319
+ await handleBaseModelSelect(target.id);
320
+ return;
321
+ }
322
+ // 无参数:打开交互选择器
323
+ const currentSession = (await listSessionSummaries(getWorkingDir())).find((session) => session.id === chat.activeSessionId);
324
+ const snap = await configService.getSnapshot();
325
+ setBaseModelPicker({ models: enabledModels, currentId: currentSession?.modelId ?? snap.config?.baseModelId ?? null });
326
+ return;
327
+ }
328
+ // /advisor status|on|off|ask|select —— 与 Desktop 共用统一配置。
329
+ const advisorArgMatch = /^\/advisor(?:\s+(status|on|off|ask|select)(?:\s+([\s\S]*))?)?$/.exec(trimmed);
330
+ if (advisorArgMatch) {
331
+ const sub = advisorArgMatch[1] ?? 'status';
332
+ const note = advisorArgMatch[2]?.trim();
333
+ const advisor = await configService.advisor();
334
+ if (sub === 'status') {
335
+ if (!advisor) {
336
+ chat.ephemeralLine('[/advisor] 未配置顾问模型,请在 Desktop 设置 → 模型中配置。');
337
+ }
338
+ else {
339
+ chat.ephemeralLine(`[/advisor] ${advisor.provider.name}/${advisor.provider.model} · ${advisor.enabled ? '开启' : '关闭'} · ${advisor.mode === 'auto' ? '智能自动' : '仅手动'}。`);
340
+ }
341
+ return;
342
+ }
343
+ if (sub === 'on') {
344
+ if (!advisor) {
345
+ chat.ephemeralLine('[/advisor] 未配置顾问模型,无法开启。');
346
+ }
347
+ else {
348
+ await configService.savePatch({ advisor: { enabled: true } });
349
+ chat.ephemeralLine(`[/advisor] 顾问模型已开启(${advisor.provider.name}/${advisor.provider.model})。`);
350
+ }
351
+ return;
352
+ }
353
+ if (sub === 'off') {
354
+ if (advisor)
355
+ await configService.savePatch({ advisor: { enabled: false } });
356
+ chat.ephemeralLine('[/advisor] 顾问模型已关闭。');
357
+ return;
358
+ }
359
+ if (sub === 'select') {
360
+ const models = await configService.models();
361
+ const enabledModels = models.filter((m) => m.enabled && m.apiKey);
362
+ if (enabledModels.length === 0) {
363
+ chat.ephemeralLine('[/advisor] 尚未配置任何模型,请先添加模型。');
364
+ return;
365
+ }
366
+ if (note) {
367
+ // /advisor select <id> 直接指定模型
368
+ const target = enabledModels.find((m) => m.id === note || m.model === note || m.displayName === note);
369
+ if (!target) {
370
+ chat.ephemeralLine(`[/advisor] 未找到模型 "${note}",可用模型:\n${enabledModels.map((m) => ` ${m.id} · ${m.displayName || m.model}`).join('\n')}`);
371
+ return;
372
+ }
373
+ await configService.savePatch({ advisor: { modelId: target.id, enabled: true } });
374
+ chat.ephemeralLine(`[/advisor] 已设置顾问模型为 ${target.displayName || target.model}(${formatProviderName(target.apiFormat)})。`);
375
+ }
376
+ else {
377
+ // 无参数:打开交互选择器(含"关闭"选项)
378
+ const snap = await configService.getSnapshot();
379
+ setAdvisorModelPicker({ models: enabledModels, currentId: snap.config?.advisor?.modelId ?? null });
380
+ }
381
+ return;
382
+ }
383
+ // sub === 'ask'(或无参数但有 note 文本):立即请求顾问意见
384
+ const result = await runAdvisor({ ...(note ? { note } : {}) }, undefined, { history: historyRef.current, advisorCalls: { used: 0, max: 1 }, advisorInvocation: 'manual' });
385
+ chat.ephemeralLine(result.ok ? `[/advisor] 专家意见:\n${result.content}` : `[/advisor] ${result.error}`);
386
+ return;
387
+ }
388
+ // /config —— 硬编码配置菜单(取代旧版 LLM 驱动的 skills/config/SKILL.md)。
389
+ // 精确匹配,不接受多余文字(与 /advisor 等硬拦截命令同规则)。仅交互式 TUI
390
+ // 支持;-p 非交互模式没有 Ink 树可渲染菜单,此命令在那里就是普通聊天文本。
391
+ if (trimmed === '/config') {
392
+ setConfigMenuOpen(true);
393
+ return;
394
+ }
176
395
  // 非命令 → 走正常 LLM 流程
177
396
  void chat.submit(text);
178
397
  },
@@ -185,12 +404,20 @@ export function App({ provider, initialHistory }) {
185
404
  chat.switchSession,
186
405
  chat.activeSessionId,
187
406
  chat.submit,
407
+ chat.setProvider,
408
+ handleSessionSelect,
409
+ handleSessionCreate,
410
+ openWorkspacePicker,
411
+ handleBaseModelSelect,
412
+ handleAdvisorModelSelect,
188
413
  ]);
189
414
  // banner 改作为 Static items[0] 走 MessageList。原因:Ink 5 的 Static 约定
190
415
  // 它必须是兄弟节点中第一个,否则 above-sibling 会跟 Static commit 一起被重打
191
416
  // 到 stdout(实测看到 banner 反复打印 N 次)。
192
- const bannerText = `minimal-agent · ${provider.name}/${provider.model} · /new 清空 · /compact 压缩 · /exit 退出 · Ctrl+C 中断`;
193
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MessageList, { history: deferredHistory, compactGeneration: chat.compactGeneration, bannerText: bannerText, terminalRows: terminalRows }), _jsxs(Box, { flexDirection: "column", width: Math.max(1, terminalCols - 1), children: [_jsx(LiveArea, { progressStage: chat.progressStage, reasoningPreview: chat.reasoningPreview, streamingText: chat.streamingText, runningTools: chat.runningTools, pluginProgress: chat.pluginProgress, terminalCols: terminalCols, terminalRows: terminalRows }), chat.error && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: "red", children: ["\u26A0 ", chat.error] }) })), chat.interrupted && (_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "yellow", children: "\u26A1 \u64CD\u4F5C\u88AB\u7528\u6237\u4E2D\u65AD\uFF0C\u7B49\u5F85\u65B0\u7684\u4EFB\u52A1\u8F93\u5165..." }), _jsx(Text, { dimColor: true, children: " \u8C03\u6574\u4E0A\u4E00\u6B21\u8BA1\u5212\uFF1A\u76F4\u63A5\u8BF4\u8C03\u6574\u5185\u5BB9 / \u91CD\u65B0\u8DD1 /plan \u76EE\u6807 / \u65B0\u4EFB\u52A1\u76F4\u63A5\u8BF4" })] })), chat.pendingAsk ? (_jsx(AskQuestion, { questions: chat.pendingAsk, onAnswer: chat.answerAsk, onCancel: chat.abort })) : (
417
+ // 使用 getCurrentProvider() 获取最新 provider(/model 切换后实时显示)
418
+ const currentProvider = chat.getCurrentProvider();
419
+ const bannerText = `minimal-agent · ${currentProvider.name}/${currentProvider.model} · /model 切模型 · /advisor 专家 · /new 清空 · /compact 压缩 · /exit 退出 · Ctrl+C 中断`;
420
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MessageList, { history: deferredHistory, compactGeneration: chat.compactGeneration, bannerText: bannerText, terminalRows: terminalRows }), _jsxs(Box, { flexDirection: "column", width: Math.max(1, terminalCols - 1), children: [_jsx(LiveArea, { progressStage: chat.progressStage, reasoningPreview: chat.reasoningPreview, streamingText: chat.streamingText, runningTools: chat.runningTools, pluginProgress: chat.pluginProgress, transientError: chat.transientError, terminalCols: terminalCols, terminalRows: terminalRows }), _jsx(QueuedMessagesBanner, { items: chat.queuedItems }), chat.error && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.error, children: ["\u26A0 ", chat.error] }) })), chat.interrupted && (_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: theme.warning, children: "\u26A1 \u64CD\u4F5C\u88AB\u7528\u6237\u4E2D\u65AD\uFF0C\u7B49\u5F85\u65B0\u7684\u4EFB\u52A1\u8F93\u5165..." }), _jsx(Text, { dimColor: true, children: " \u8C03\u6574\u4E0A\u4E00\u6B21\u8BA1\u5212\uFF1A\u76F4\u63A5\u8BF4\u8C03\u6574\u5185\u5BB9 / \u91CD\u65B0\u8DD1 /plan \u76EE\u6807 / \u65B0\u4EFB\u52A1\u76F4\u63A5\u8BF4" })] })), configMenuOpen ? (_jsx(ConfigMenu, { mode: "reconfigure", onDone: () => setConfigMenuOpen(false) })) : baseModelPicker ? (_jsx(ModelPicker, { models: baseModelPicker.models, currentModelId: baseModelPicker.currentId, title: "\u9009\u62E9\u57FA\u5EA7\u6A21\u578B", onSelect: handleBaseModelSelect, onCancel: () => setBaseModelPicker(null) })) : advisorModelPicker ? (_jsx(ModelPicker, { models: advisorModelPicker.models, currentModelId: advisorModelPicker.currentId, title: "\u9009\u62E9\u987E\u95EE\u6A21\u578B", nullOptionLabel: "\u25CB \u4E0D\u4F7F\u7528\u987E\u95EE\u6A21\u578B", onSelect: handleAdvisorModelSelect, onCancel: () => setAdvisorModelPicker(null) })) : workspacePicker ? (_jsx(WorkspacePicker, { workspaces: workspacePicker, currentPath: getWorkingDir(), currentSessionId: chat.activeSessionId, onSelect: handleWorkspaceSelect, onCancel: () => setWorkspacePicker(null) })) : sessionPicker ? (_jsx(SessionPicker, { sessions: sessionPicker, activeSessionId: chat.activeSessionId, onSelect: handleSessionSelect, onCreate: handleSessionCreate, onCancel: () => setSessionPicker(null) })) : chat.pendingAsk ? (_jsx(AskQuestion, { questions: chat.pendingAsk, source: chat.pendingAskSource, queuedCount: chat.pendingAskQueueCount, onAnswer: chat.answerAsk, onCancel: chat.cancelAsk })) : (
194
421
  /* handleSubmit 包装做 slash 命令拦截,非命令透传 chat.submit */
195
- _jsx(InputBox, { onSubmit: handleSubmit, disabled: chat.isLoading, onAbort: chat.abort, onClear: handleClear, onCompact: chat.compactNow })), _jsx(StatusLine, { provider: provider, history: chat.history, pluginProgress: chat.pluginProgress })] })] }));
422
+ _jsx(InputBox, { onSubmit: handleSubmit, disabled: chat.isLoading, onAbort: chat.abort, onClear: handleClear, onCompact: chat.compactNow, onWorkspaces: openWorkspacePicker })), _jsx(StatusLine, { provider: currentProvider, history: chat.history, pluginProgress: chat.pluginProgress, contextUsage: chat.contextUsage })] })] }));
196
423
  }