minimal-agent 0.9.0 → 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 (467) hide show
  1. package/README.md +11 -8
  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 +2 -1
  20. package/plugins/workflow-runner/src/agentCommon.js +172 -9
  21. package/plugins/workflow-runner/src/dslManifest.js +72 -0
  22. package/plugins/workflow-runner/src/expressions.js +150 -13
  23. package/plugins/workflow-runner/src/index.js +39 -8
  24. package/plugins/workflow-runner/src/loader.js +216 -64
  25. package/plugins/workflow-runner/src/multimodal.js +37 -0
  26. package/plugins/workflow-runner/src/recovery.js +26 -24
  27. package/plugins/workflow-runner/src/runner.js +693 -287
  28. package/plugins/workflow-runner/src/stepExecutors/agent.js +126 -37
  29. package/plugins/workflow-runner/src/stepExecutors/assert.js +3 -2
  30. package/plugins/workflow-runner/src/stepExecutors/llm.js +38 -30
  31. package/plugins/workflow-runner/src/stepExecutors/skill.js +27 -38
  32. package/plugins/workflow-runner/src/stepExecutors/tool.js +56 -3
  33. package/plugins/workflow-runner/src/stepExecutors/verify.js +19 -10
  34. package/plugins/workflow-runner/src/taskIdentity.js +185 -30
  35. package/plugins/workflow-runner/src/types.js +18 -7
  36. package/plugins/workflow-runner/src/workflowState.js +355 -55
  37. package/skills/ai-image/SKILL.md +89 -0
  38. package/skills/ai-image/scripts/generate.mjs +497 -0
  39. package/skills/ai-image/scripts/generate_from_plan.mjs +469 -0
  40. package/skills/ai-image/scripts/generate_from_plan_test.mjs +223 -0
  41. package/skills/ai-image/scripts/generate_test.mjs +74 -0
  42. package/skills/browser-automation/SKILL.md +47 -0
  43. package/skills/docx/SKILL.md +25 -3
  44. package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
  45. package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
  46. package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
  47. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
  48. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
  49. package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
  50. package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
  51. package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
  52. package/skills/docx/scripts/opc_check.py +253 -0
  53. package/skills/image-gen-openrouter/SKILL.md +10 -2
  54. package/skills/image-stats/SKILL.md +65 -0
  55. package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
  56. package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
  57. package/skills/image-stats/scripts/image_stats.py +368 -0
  58. package/skills/image-stats/scripts/image_stats_test.py +181 -0
  59. package/skills/pptx/SKILL.md +123 -95
  60. package/skills/pptx/design.md +82 -0
  61. package/skills/pptx/examples/README.md +29 -0
  62. package/skills/pptx/examples/hybrid_example.mjs +107 -0
  63. package/skills/pptx/examples/hybrid_example.pptx +0 -0
  64. package/skills/pptx/pptxgenjs.md +41 -0
  65. package/skills/pptx/scripts/deck_helpers.mjs +224 -0
  66. package/skills/pptx/scripts/layout_lint.py +293 -0
  67. package/skills/pptx/scripts/layout_lint_test.py +160 -0
  68. package/skills/pptx/scripts/opc_check.py +339 -0
  69. package/skills/pptx/scripts/svg_fallback.mjs +145 -0
  70. package/skills/pptx/svg.md +67 -0
  71. package/skills/tts-zh/SKILL.md +19 -2
  72. package/skills/ui-design/SKILL.md +158 -0
  73. package/skills/ui-design/claude_design.md +309 -0
  74. package/skills/ui-design/evals/evals.json +254 -0
  75. package/skills/ui-design/references/aesthetic-direction.md +83 -0
  76. package/skills/ui-design/references/claude-design-protocol.md +36 -0
  77. package/skills/ui-design/references/design-context.md +53 -0
  78. package/skills/ui-design/references/design-execution-playbook.md +82 -0
  79. package/skills/ui-design/references/export-pptx.md +124 -0
  80. package/skills/ui-design/references/layout-composition.md +654 -0
  81. package/skills/ui-design/references/preview-feedback.md +37 -0
  82. package/skills/ui-design/references/source-image-layout.md +189 -0
  83. package/skills/ui-design/references/starter-components.md +78 -0
  84. package/skills/ui-design/references/vi-aegis-vector.md +144 -0
  85. package/skills/ui-design/references/vi-amber-prism.md +133 -0
  86. package/skills/ui-design/references/vi-aqua-glass.md +200 -0
  87. package/skills/ui-design/references/vi-comfort-vision.md +147 -0
  88. package/skills/ui-design/references/vi-ember-prism.md +140 -0
  89. package/skills/ui-design/references/vi-nebula-console.md +151 -0
  90. package/skills/ui-design/references/vi-obsidian-optics.md +139 -0
  91. package/skills/ui-design/references/vi-obsidian-xui.md +143 -0
  92. package/skills/ui-design/references/vi-precision-horizon-dark.md +117 -0
  93. package/skills/ui-design/references/vi-precision-horizon.md +215 -0
  94. package/skills/ui-design/references/vi-solar-relay.md +130 -0
  95. package/skills/ui-design/references/vi-template-workflow.md +48 -0
  96. package/skills/ui-design/references/vi-verdant-grid.md +141 -0
  97. package/skills/ui-design/references/vi-violet-meridian.md +172 -0
  98. package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
  99. package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
  100. package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
  101. package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
  102. package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
  103. package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
  104. package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
  105. package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
  106. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
  107. package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
  108. package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
  109. package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
  110. package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
  111. package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
  112. package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
  113. package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
  114. package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
  115. package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
  116. package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
  117. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
  118. package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
  119. package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
  120. package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
  121. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
  122. package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
  123. package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
  124. package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
  125. package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
  126. package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
  127. package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
  128. package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
  129. package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
  130. package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
  131. package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
  132. package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
  133. package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
  134. package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
  135. package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
  136. package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
  137. package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
  138. package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
  139. package/skills/ui-design/scripts/assemble_material.py +217 -0
  140. package/skills/ui-design/scripts/assemble_material_test.py +149 -0
  141. package/skills/ui-design/scripts/browser_check_ui_artifact.mjs +221 -0
  142. package/skills/ui-design/scripts/chart_sanity_check.py +261 -0
  143. package/skills/ui-design/scripts/chart_sanity_check_test.py +165 -0
  144. package/skills/ui-design/scripts/check_language_policy.py +351 -0
  145. package/skills/ui-design/scripts/check_ui_artifact.py +435 -0
  146. package/skills/ui-design/scripts/check_ui_artifact_pptx_test.py +251 -0
  147. package/skills/ui-design/scripts/deck_pages_check.py +158 -0
  148. package/skills/ui-design/scripts/deck_pages_check_test.py +134 -0
  149. package/skills/ui-design/scripts/deck_visual_style_check.py +767 -0
  150. package/skills/ui-design/scripts/deck_visual_style_check_test.py +713 -0
  151. package/skills/ui-design/scripts/design_budget_check.py +271 -0
  152. package/skills/ui-design/scripts/design_budget_check_test.py +183 -0
  153. package/skills/ui-design/scripts/design_repair_hints.py +162 -0
  154. package/skills/ui-design/scripts/design_repair_hints_test.py +54 -0
  155. package/skills/ui-design/scripts/design_style_check.py +397 -0
  156. package/skills/ui-design/scripts/design_style_check_test.py +233 -0
  157. package/skills/ui-design/scripts/draft_coverage_check.py +184 -0
  158. package/skills/ui-design/scripts/draft_coverage_check_test.py +131 -0
  159. package/skills/ui-design/scripts/export_pptx_from_html.mjs +2367 -0
  160. package/skills/ui-design/scripts/extract_source_assets.py +607 -0
  161. package/skills/ui-design/scripts/extract_source_assets_test.py +256 -0
  162. package/skills/ui-design/scripts/generated_image_review_collect.py +206 -0
  163. package/skills/ui-design/scripts/generated_image_review_collect_test.py +91 -0
  164. package/skills/ui-design/scripts/image_plan_lint.py +571 -0
  165. package/skills/ui-design/scripts/image_plan_lint_test.py +515 -0
  166. package/skills/ui-design/scripts/image_prompt_rewrite.py +713 -0
  167. package/skills/ui-design/scripts/image_prompt_rewrite_test.py +966 -0
  168. package/skills/ui-design/scripts/layout_balance.py +881 -0
  169. package/skills/ui-design/scripts/layout_balance_test.py +927 -0
  170. package/skills/ui-design/scripts/normalize_design_json.py +272 -0
  171. package/skills/ui-design/scripts/normalize_design_json_test.py +119 -0
  172. package/skills/ui-design/scripts/plan_source_layout.py +1450 -0
  173. package/skills/ui-design/scripts/plan_source_layout_test.py +1136 -0
  174. package/skills/ui-design/scripts/resolve_source_input.py +270 -0
  175. package/skills/ui-design/scripts/resolve_source_input_test.py +98 -0
  176. package/skills/ui-design/scripts/scaffold_ui_artifact.py +377 -0
  177. package/skills/ui-design/scripts/source_usage_check.py +581 -0
  178. package/skills/ui-design/scripts/source_usage_check_test.py +427 -0
  179. package/skills/ui-design/scripts/text_readability_pixel.py +340 -0
  180. package/skills/ui-design/scripts/text_readability_pixel_test.py +219 -0
  181. package/skills/ui-design/scripts/visual_quality_gate.py +301 -0
  182. package/skills/ui-design/scripts/visual_quality_gate_test.py +233 -0
  183. package/skills/ui-design/tests/e2e/prompts/deck.md +19 -0
  184. package/skills/ui-design/tests/e2e/prompts/webpage.md +17 -0
  185. package/skills/ui-design/tests/e2e/render_check.mjs +101 -0
  186. package/skills/ui-design/tests/e2e/rubric.json +54 -0
  187. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/browser-check.json +49 -0
  188. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/language-check.txt +12 -0
  189. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/checks/static-check.txt +15 -0
  190. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/outputs/index.html +837 -0
  191. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/prompt.md +12 -0
  192. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/review.md +21 -0
  193. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/desktop.png +0 -0
  194. package/skills/ui-design/tests/e2e/runs/aegis-vector-preview-1/screenshots/mobile.png +0 -0
  195. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/homepage.html +373 -0
  196. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/outputs/platform.html +378 -0
  197. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/prompt.md +13 -0
  198. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/review.md +26 -0
  199. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  200. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  201. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  202. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  203. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  204. package/skills/ui-design/tests/e2e/runs/amber-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  205. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/checks/static-check.txt +2 -0
  206. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/outputs/index.html +656 -0
  207. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/prompt.md +12 -0
  208. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-1/render_check.mjs +67 -0
  209. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/checks/static-check.txt +2 -0
  210. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/outputs/index.html +587 -0
  211. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/prompt.md +12 -0
  212. package/skills/ui-design/tests/e2e/runs/aqua-glass-iteration-2/review.md +36 -0
  213. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/browser-check.json +49 -0
  214. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/language-check.txt +8 -0
  215. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/checks/static-check.txt +16 -0
  216. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/outputs/index.html +739 -0
  217. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/prompt.md +11 -0
  218. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/review.md +22 -0
  219. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/desktop.png +0 -0
  220. package/skills/ui-design/tests/e2e/runs/aqua-glass-volcano-refresh-1/screenshots/mobile.png +0 -0
  221. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/checks/browser-check.json +52 -0
  222. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/outputs/index.html +641 -0
  223. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/desktop.png +0 -0
  224. package/skills/ui-design/tests/e2e/runs/comfort-vision-preview-1/screenshots/mobile.png +0 -0
  225. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/outputs/index.html +467 -0
  226. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/browser-check.json +49 -0
  227. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/desktop.png +0 -0
  228. package/skills/ui-design/tests/e2e/runs/ember-prism-red-preview-1/screenshots/mobile.png +0 -0
  229. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/homepage.html +373 -0
  230. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/outputs/platform.html +378 -0
  231. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/prompt.md +13 -0
  232. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/review.md +26 -0
  233. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/browser-check.json +49 -0
  234. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/desktop.png +0 -0
  235. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/homepage/mobile.png +0 -0
  236. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/browser-check.json +49 -0
  237. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/desktop.png +0 -0
  238. package/skills/ui-design/tests/e2e/runs/ember-prism-screenshot-check-1/screenshots/platform/mobile.png +0 -0
  239. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/browser-check.json +49 -0
  240. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/language-check.txt +4 -0
  241. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/checks/static-check.txt +2 -0
  242. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/execution_notes.md +10 -0
  243. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/outputs/index.html +587 -0
  244. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/prompt.md +14 -0
  245. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/review.md +13 -0
  246. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/desktop.png +0 -0
  247. package/skills/ui-design/tests/e2e/runs/empty-agent-protocol-check/screenshots/mobile.png +0 -0
  248. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/browser-check.json +24 -0
  249. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/checks/static-check.txt +2 -0
  250. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/execution_notes.md +21 -0
  251. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/outputs/deck.html +451 -0
  252. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-01.png +0 -0
  253. package/skills/ui-design/tests/e2e/runs/iteration-1/deck/screenshots/slide-02.png +0 -0
  254. package/skills/ui-design/tests/e2e/runs/iteration-1/review.md +79 -0
  255. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/browser-check.json +26 -0
  256. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/checks/static-check.txt +2 -0
  257. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/execution_notes.md +20 -0
  258. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/outputs/index.html +534 -0
  259. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/desktop.png +0 -0
  260. package/skills/ui-design/tests/e2e/runs/iteration-1/webpage/screenshots/mobile.png +0 -0
  261. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/browser-check.json +64 -0
  262. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/language-check.txt +7 -0
  263. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/quick-validate.txt +1 -0
  264. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-console.txt +2 -0
  265. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/checks/static-homepage.txt +2 -0
  266. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/console.html +600 -0
  267. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/outputs/homepage.html +556 -0
  268. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/prompt.md +29 -0
  269. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/review.md +57 -0
  270. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-desktop.png +0 -0
  271. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/console-mobile.png +0 -0
  272. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-desktop.png +0 -0
  273. package/skills/ui-design/tests/e2e/runs/nebula-console-iteration-1/screenshots/homepage-mobile.png +0 -0
  274. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/browser-check.json +33 -0
  275. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/language-check.txt +6 -0
  276. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/quick-validate.txt +1 -0
  277. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/checks/static-check.txt +2 -0
  278. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/outputs/index.html +682 -0
  279. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/prompt.md +13 -0
  280. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/review.md +59 -0
  281. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/desktop.png +0 -0
  282. package/skills/ui-design/tests/e2e/runs/obsidian-optics-iteration-1/screenshots/mobile.png +0 -0
  283. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-01.html +466 -0
  284. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/outputs/page-02.html +433 -0
  285. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/prompt.md +13 -0
  286. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/browser-check.json +49 -0
  287. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/desktop.png +0 -0
  288. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-01/mobile.png +0 -0
  289. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/browser-check.json +49 -0
  290. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/desktop.png +0 -0
  291. package/skills/ui-design/tests/e2e/runs/obsidian-xui-screenshot-check-1/screenshots/page-02/mobile.png +0 -0
  292. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/browser-check.json +49 -0
  293. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/language-check.txt +3 -0
  294. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/checks/static-check.txt +2 -0
  295. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/execution_notes.md +24 -0
  296. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/outputs/index.html +723 -0
  297. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/prompt.md +15 -0
  298. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/review.md +26 -0
  299. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/desktop.png +0 -0
  300. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-1/screenshots/mobile.png +0 -0
  301. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-dark.json +49 -0
  302. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/browser-light.json +49 -0
  303. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/language-check.txt +11 -0
  304. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-dark.txt +2 -0
  305. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/checks/static-light.txt +3 -0
  306. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/execution_notes.md +23 -0
  307. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/dark.html +392 -0
  308. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/outputs/light.html +476 -0
  309. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/prompt.md +23 -0
  310. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/review.md +41 -0
  311. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/desktop.png +0 -0
  312. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/dark/mobile.png +0 -0
  313. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/desktop.png +0 -0
  314. package/skills/ui-design/tests/e2e/runs/precision-horizon-iteration-2/screenshots/light/mobile.png +0 -0
  315. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/browser-check.json +54 -0
  316. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/language-check.txt +5 -0
  317. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/quick-validate.txt +1 -0
  318. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/checks/static-check.txt +2 -0
  319. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/outputs/index.html +765 -0
  320. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/prompt.md +15 -0
  321. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/review.md +59 -0
  322. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/desktop.png +0 -0
  323. package/skills/ui-design/tests/e2e/runs/solar-relay-iteration-1/screenshots/mobile.png +0 -0
  324. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/outputs/index.html +508 -0
  325. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/browser-check.json +49 -0
  326. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/desktop.png +0 -0
  327. package/skills/ui-design/tests/e2e/runs/verdant-grid-preview-1/screenshots/mobile.png +0 -0
  328. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/browser-check.json +54 -0
  329. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/language-check.txt +6 -0
  330. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/quick-validate.txt +1 -0
  331. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/checks/static-check.txt +2 -0
  332. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/outputs/index.html +788 -0
  333. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/prompt.md +14 -0
  334. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/review.md +60 -0
  335. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/desktop.png +0 -0
  336. package/skills/ui-design/tests/e2e/runs/violet-meridian-iteration-1/screenshots/mobile.png +0 -0
  337. package/skills/video-compose/SKILL.md +8 -1
  338. package/skills/web-crawler/SKILL.md +70 -0
  339. package/skills/web-crawler/scripts/crawl.mjs +538 -0
  340. package/skills/web-crawler/scripts/crawl.test.mjs +204 -0
  341. package/skills/xlsx/SKILL.md +12 -0
  342. package/skills/xlsx/scripts/opc_check.py +253 -0
  343. package/src/advisorState.js +22 -0
  344. package/src/askBridge.js +87 -3
  345. package/src/bootstrap/workingDir.js +12 -1
  346. package/src/cli/args.js +17 -1
  347. package/src/cli/configMenu.js +236 -0
  348. package/src/cli/configWizard.js +7 -5
  349. package/src/cli/print.js +37 -4
  350. package/src/cli/streamJson.js +77 -24
  351. package/src/cli/wizards/advisorWizard.js +147 -0
  352. package/src/cli/wizards/fieldForm.js +149 -0
  353. package/src/cli/wizards/imageWizard.js +60 -0
  354. package/src/cli/wizards/openRouterWizard.js +23 -0
  355. package/src/cli/wizards/providerPresets.js +95 -0
  356. package/src/cli/wizards/providerWizard.js +219 -0
  357. package/src/cli/wizards/searchWizard.js +23 -0
  358. package/src/cli/wizards/ttsWizard.js +34 -0
  359. package/src/config/configFile.js +441 -83
  360. package/src/config/configService.js +446 -0
  361. package/src/config.js +24 -64
  362. package/src/context/agentHome.js +9 -0
  363. package/src/context/archive.js +3 -2
  364. package/src/context/compact.js +8 -5
  365. package/src/context/contextLock.js +81 -0
  366. package/src/context/persistContext.js +30 -11
  367. package/src/context/reactiveCompact.js +4 -2
  368. package/src/context/recentDirs.js +21 -61
  369. package/src/context/sessionPath.js +97 -23
  370. package/src/context/sessionRegistry.js +303 -24
  371. package/src/context/tokenCounter.js +18 -0
  372. package/src/context/workspaceIndex.js +731 -0
  373. package/src/infra/verification.js +3 -2
  374. package/src/llm/client.js +293 -206
  375. package/src/llm/providers/anthropicFormat.js +241 -0
  376. package/src/llm/providers/openaiFormat.js +190 -0
  377. package/src/loop.js +211 -27
  378. package/src/main.js +61 -11
  379. package/src/messageQueue.js +97 -0
  380. package/src/multimodal.js +203 -0
  381. package/src/plugin-sdk.js +15 -0
  382. package/src/plugins/commandRouter.js +3 -0
  383. package/src/plugins/hookEngine.js +5 -1
  384. package/src/plugins/pluginLifecycle.js +28 -0
  385. package/src/plugins/pluginRunner.js +40 -0
  386. package/src/plugins/transcript.js +4 -3
  387. package/src/prompts/pluginList.js +2 -2
  388. package/src/prompts/projectInstructions.js +3 -3
  389. package/src/prompts/promptAssembly.js +133 -109
  390. package/src/prompts/skillList.js +12 -22
  391. package/src/session/runAgentTurn.js +70 -0
  392. package/src/session/runTurn.js +41 -6
  393. package/src/toolGate.js +17 -0
  394. package/src/tools/advisor/advisor.js +379 -0
  395. package/src/tools/agent/agent.js +555 -0
  396. package/src/tools/agent/agentTypes.js +59 -0
  397. package/src/tools/agent/coordinator.js +93 -0
  398. package/src/tools/agent/worktree.js +270 -0
  399. package/src/tools/ask/ask.js +7 -2
  400. package/src/tools/bash/bash.js +112 -18
  401. package/src/tools/download/download.js +144 -0
  402. package/src/tools/edit/edit.js +1 -0
  403. package/src/tools/edit/multi-edit.js +1 -0
  404. package/src/tools/genimage/genimage.js +242 -0
  405. package/src/tools/genspeech/genspeech.js +67 -0
  406. package/src/tools/glob/glob.js +1 -0
  407. package/src/tools/grep/grep.js +3 -0
  408. package/src/tools/grep/rgPath.js +2 -0
  409. package/src/tools/index.js +25 -2
  410. package/src/tools/read/read.js +1 -0
  411. package/src/tools/shared/killTree.js +52 -0
  412. package/src/tools/toolProgressBridge.js +48 -0
  413. package/src/tools/webbrowser/agentBrowserPath.js +258 -0
  414. package/src/tools/webbrowser/browser.js +59 -42
  415. package/src/tools/webbrowser/browserControl.js +118 -0
  416. package/src/tools/webbrowser/browserDaemon.js +97 -0
  417. package/src/tools/webbrowser/browserService.js +27 -0
  418. package/src/tools/webbrowser/browserStream.js +119 -0
  419. package/src/tools/webbrowser/webbrowser.js +699 -130
  420. package/src/tools/webfetch/webfetch.js +1 -0
  421. package/src/tools/websearch/websearch.js +134 -51
  422. package/src/tools/write/write.js +1 -0
  423. package/src/ui/App.js +249 -22
  424. package/src/ui/AskQuestion.js +19 -17
  425. package/src/ui/InputBox.js +34 -9
  426. package/src/ui/LiveArea.js +112 -19
  427. package/src/ui/MessageList.js +100 -14
  428. package/src/ui/ModelPicker.js +56 -0
  429. package/src/ui/QueuedMessagesBanner.js +14 -0
  430. package/src/ui/Root.js +18 -10
  431. package/src/ui/SessionPicker.js +102 -0
  432. package/src/ui/StatusLine.js +13 -4
  433. package/src/ui/WorkspacePicker.js +55 -0
  434. package/src/ui/figures.js +44 -0
  435. package/src/ui/hooks/askSlot.js +82 -0
  436. package/src/ui/hooks/useChat.js +345 -64
  437. package/src/ui/hooks/usePasteHandler.js +11 -1
  438. package/src/ui/hooks/useTextBuffer.js +1 -3
  439. package/src/ui/liveViewport.js +5 -1
  440. package/src/ui/markdown.js +206 -0
  441. package/src/ui/textBuffer.js +0 -7
  442. package/src/ui/theme.js +32 -0
  443. package/src/utils/debugLog.js +0 -4
  444. package/src/utils/resourcePaths.js +2 -2
  445. package/src/utils/stripThink.js +50 -0
  446. package/src/web/browserBridge.js +447 -0
  447. package/src/web/protocol.js +19 -0
  448. package/workflows/adaptive-research.yaml +9 -2
  449. package/workflows/book-review-short.yaml +31 -7
  450. package/workflows/e2e-write-greet.yaml +3 -0
  451. package/workflows/illustrate.yaml +227 -0
  452. package/workflows/plan.yaml +437 -207
  453. package/workflows/ppt.yaml +1659 -0
  454. package/workflows/ralph-loop.yaml +142 -112
  455. package/workflows/schema.json +81 -23
  456. package/workflows/webppt.yaml +496 -0
  457. package/workflows/word.yaml +710 -0
  458. package/workflows/youtube-shorts.yaml +38 -10
  459. package/MINIMAL-TRACE.md +0 -77
  460. package/plugins/workflow-runner/src/voteHelpers.js +0 -110
  461. package/skills/config/SKILL.md +0 -123
  462. package/skills/frontend-design/LICENSE.txt +0 -177
  463. package/skills/frontend-design/SKILL.md +0 -42
  464. package/skills/pptx/scripts/office/__pycache__/soffice.cpython-314.pyc +0 -0
  465. package/src/ui/ProgressPanel.js +0 -54
  466. package/src/ui/StreamingBlock.js +0 -11
  467. package/src/ui/ToolStatus.js +0 -11
@@ -18,10 +18,14 @@
18
18
  * - listSessions 同时扫新旧格式,保证迁移期不丢
19
19
  * ============================================================
20
20
  */
21
- import { createHash } from 'node:crypto';
22
- import { mkdir, readFile, readdir, rename, writeFile } from 'node:fs/promises';
21
+ import { createHash, randomUUID } from 'node:crypto';
22
+ import { basename } from 'node:path';
23
+ import { copyFile, mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from 'node:fs/promises';
23
24
  import { homedir } from 'node:os';
24
25
  import { join } from 'node:path';
26
+ import { resolveSessionFile, sessionFileFor, validateSessionId } from './sessionPath.js';
27
+ import { workspaceIndexService } from './workspaceIndex.js';
28
+ export { validateSessionId } from './sessionPath.js';
25
29
  /** 解析根目录,支持 MINIMAL_AGENT_HOME 测试覆盖 */
26
30
  function agentHome() {
27
31
  return process.env.MINIMAL_AGENT_HOME ?? homedir();
@@ -38,6 +42,30 @@ function sessionsDirPath() {
38
42
  function cwdHash(cwd) {
39
43
  return createHash('sha1').update(cwd).digest('hex').slice(0, 12);
40
44
  }
45
+ function metadataFilePath() {
46
+ return join(agentDir(), 'session-metadata.json');
47
+ }
48
+ async function loadMetadata() {
49
+ try {
50
+ const value = JSON.parse(await readFile(metadataFilePath(), 'utf8'));
51
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
52
+ }
53
+ catch {
54
+ return {};
55
+ }
56
+ }
57
+ async function saveMetadata(value) {
58
+ await mkdir(agentDir(), { recursive: true });
59
+ const target = metadataFilePath();
60
+ const tmp = `${target}.${process.pid}.${randomUUID()}.tmp`;
61
+ try {
62
+ await writeFile(tmp, JSON.stringify(value, null, 2), { encoding: 'utf8', mode: 0o600 });
63
+ await rename(tmp, target);
64
+ }
65
+ finally {
66
+ await unlink(tmp).catch(() => { });
67
+ }
68
+ }
41
69
  async function loadActiveMap() {
42
70
  try {
43
71
  const raw = await readFile(activeFilePath(), 'utf8');
@@ -51,13 +79,137 @@ async function loadActiveMap() {
51
79
  return {};
52
80
  }
53
81
  }
82
+ async function saveActiveMap(map) {
83
+ await mkdir(agentDir(), { recursive: true });
84
+ const target = activeFilePath();
85
+ const tmp = `${target}.${process.pid}.${randomUUID()}.tmp`;
86
+ try {
87
+ await writeFile(tmp, JSON.stringify(map), { encoding: 'utf8', mode: 0o600 });
88
+ await rename(tmp, target);
89
+ }
90
+ finally {
91
+ await unlink(tmp).catch(() => { });
92
+ }
93
+ }
94
+ /**
95
+ * 同进程内的串行互斥队列(`session-metadata.json` / `active-sessions.json` 都是全局
96
+ * 单文件,按 cwdHash 分组——两个并发调用方 load→改→save 会互相覆盖丢更新)。
97
+ * 照抄 `plugins/cron-runner/src/store.ts` 的 `enqueueMutation` 模式。
98
+ *
99
+ * 同进程先排队,队首操作再取得跨进程目录锁。CLI、Desktop Runtime 与 Cron 可以同时
100
+ * 使用同一个 MINIMAL_AGENT_HOME;仅靠 rename 只能避免半写文件,不能避免两个进程
101
+ * load→改→save 时后写覆盖先写。目录创建在各平台都是原子的,适合做短临界区互斥。
102
+ *
103
+ * 下面所有导出的可变函数(setActiveSession/createSession/updateSessionMetadata/
104
+ * copySession/deleteSession)都必须整体包一层 enqueueMutation;内部若需要调用
105
+ * 另一个可变操作的逻辑,必须调用其 *Unlocked 版本而不是导出版本——否则内层调用会
106
+ * 排到同一队列的外层调用之后,外层等内层、内层等外层排到自己,死锁。
107
+ */
108
+ let mutationQueue = Promise.resolve();
109
+ const LOCK_STALE_MS = 120_000;
110
+ const LOCK_WAIT_MS = 15_000;
111
+ function lockDirPath() {
112
+ return join(agentDir(), 'session-registry.lock');
113
+ }
114
+ function processIsAlive(pid) {
115
+ if (!Number.isInteger(pid) || pid <= 0)
116
+ return false;
117
+ try {
118
+ process.kill(pid, 0);
119
+ return true;
120
+ }
121
+ catch (error) {
122
+ return error.code === 'EPERM';
123
+ }
124
+ }
125
+ async function acquireRegistryLock() {
126
+ const lockDir = lockDirPath();
127
+ const ownerFile = join(lockDir, 'owner.json');
128
+ const startedAt = Date.now();
129
+ await mkdir(agentDir(), { recursive: true, mode: 0o700 });
130
+ while (true) {
131
+ try {
132
+ await mkdir(lockDir, { mode: 0o700 });
133
+ await writeFile(ownerFile, JSON.stringify({ pid: process.pid, createdAt: Date.now() }), {
134
+ encoding: 'utf8',
135
+ mode: 0o600,
136
+ });
137
+ return async () => { await rm(lockDir, { recursive: true, force: true }); };
138
+ }
139
+ catch (error) {
140
+ if (error.code !== 'EEXIST')
141
+ throw error;
142
+ let stale = false;
143
+ try {
144
+ const [ownerRaw, info] = await Promise.all([readFile(ownerFile, 'utf8'), stat(lockDir)]);
145
+ const owner = JSON.parse(ownerRaw);
146
+ stale = typeof owner.pid === 'number'
147
+ ? !processIsAlive(owner.pid)
148
+ : Date.now() - info.mtimeMs > LOCK_STALE_MS;
149
+ }
150
+ catch {
151
+ try {
152
+ stale = Date.now() - (await stat(lockDir)).mtimeMs > LOCK_STALE_MS;
153
+ }
154
+ catch {
155
+ continue;
156
+ }
157
+ }
158
+ if (stale) {
159
+ await rm(lockDir, { recursive: true, force: true });
160
+ continue;
161
+ }
162
+ if (Date.now() - startedAt > LOCK_WAIT_MS) {
163
+ throw new Error('等待 Session 注册表锁超时,请确认没有异常残留的 minimal-agent 进程');
164
+ }
165
+ await new Promise((resolve) => setTimeout(resolve, 25));
166
+ }
167
+ }
168
+ }
169
+ function enqueueMutation(mutate) {
170
+ const run = async () => {
171
+ const release = await acquireRegistryLock();
172
+ try {
173
+ return await mutate();
174
+ }
175
+ finally {
176
+ await release();
177
+ }
178
+ };
179
+ const operation = mutationQueue.then(run, run);
180
+ mutationQueue = operation.then(() => undefined, () => undefined);
181
+ return operation;
182
+ }
54
183
  /**
55
184
  * 获取当前 cwd 的激活 session id。
56
185
  * 无记录 → 返回 'default'。
57
186
  */
58
187
  export async function getActiveSession(cwd) {
188
+ const workspace = await workspaceIndexService.findByPath(cwd).catch(() => null);
189
+ if (workspace?.activeSessionId) {
190
+ try {
191
+ return validateSessionId(workspace.activeSessionId);
192
+ }
193
+ catch { /* fall through */ }
194
+ }
59
195
  const map = await loadActiveMap();
60
- return map[cwdHash(cwd)] ?? 'default';
196
+ try {
197
+ return validateSessionId(map[cwdHash(cwd)] ?? 'default');
198
+ }
199
+ catch {
200
+ return 'default';
201
+ }
202
+ }
203
+ async function setActiveSessionUnlocked(cwd, sessionId) {
204
+ const id = validateSessionId(sessionId);
205
+ const map = await loadActiveMap();
206
+ map[cwdHash(cwd)] = id;
207
+ await saveActiveMap(map);
208
+ // 索引是自愈便利层:权威存储(active-sessions.json)已落盘,索引写失败
209
+ // (锁超时/磁盘错误)绝不能反过来打断会话切换——尽力而为,下次 reconcile 自愈。
210
+ await workspaceIndexService.setActive(cwd, id).catch(async () => {
211
+ await workspaceIndexService.recordPendingMutation({ type: 'active', path: cwd, sessionId: id }).catch(() => { });
212
+ });
61
213
  }
62
214
  /**
63
215
  * 设置当前 cwd 的激活 session id。
@@ -66,39 +218,166 @@ export async function getActiveSession(cwd) {
66
218
  * 即使被中断也只会留临时文件,不破坏 target。
67
219
  */
68
220
  export async function setActiveSession(cwd, sessionId) {
69
- const map = await loadActiveMap();
70
- map[cwdHash(cwd)] = sessionId;
71
- await mkdir(agentDir(), { recursive: true });
72
- const target = activeFilePath();
73
- const tmp = `${target}.tmp`;
74
- await writeFile(tmp, JSON.stringify(map), 'utf8');
75
- await rename(tmp, target);
221
+ return enqueueMutation(() => setActiveSessionUnlocked(cwd, sessionId));
76
222
  }
77
223
  /**
78
- * 列出当前 cwd 下所有 session id(含 'default')。
224
+ * 列出当前 cwd 下所有真实存在的 session id。
79
225
  *
80
226
  * 扫描规则:
81
227
  * - 新格式 `<hash>--<id>.json` → 提取 id
82
228
  * - 旧格式 `<hash>.json` → 视作 'default'
83
- * - 都没有 → 返回 ['default'](让 UI 默认能 list 出至少一项)
229
+ * - default 只有存在历史文件或元数据时才返回;CLI 的隐式 default 写入兼容不变
84
230
  */
85
231
  export async function listSessions(cwd) {
86
232
  const dir = sessionsDirPath();
87
- const hash = cwdHash(cwd);
88
- let names;
233
+ const defaultName = basename(sessionFileFor(cwd, 'default'));
234
+ const prefix = defaultName.slice(0, -'default.json'.length);
235
+ let names = [];
89
236
  try {
90
237
  names = await readdir(dir);
91
238
  }
92
- catch {
93
- return ['default'];
94
- }
95
- const prefix = `${hash}--`;
239
+ catch { /* a new workspace has no files yet */ }
96
240
  const ids = names
97
- .filter((n) => n.startsWith(prefix) && n.endsWith('.json'))
98
- .map((n) => n.slice(prefix.length, -'.json'.length));
99
- const legacy = `${hash}.json`;
100
- if (names.includes(legacy) && !ids.includes('default')) {
101
- ids.unshift('default');
241
+ .filter((name) => name.startsWith(prefix) && name.endsWith('.json') && !name.endsWith('.tmp.json'))
242
+ .map((name) => name.slice(prefix.length, -'.json'.length))
243
+ .filter((id) => {
244
+ try {
245
+ validateSessionId(id);
246
+ return true;
247
+ }
248
+ catch {
249
+ return false;
250
+ }
251
+ });
252
+ const metadata = await loadMetadata();
253
+ ids.push(...Object.keys(metadata[cwdHash(cwd)] ?? {}).filter((id) => !ids.includes(id)));
254
+ if (!ids.includes('default')) {
255
+ try {
256
+ await stat(await resolveSessionFile(cwd, 'default'));
257
+ ids.push('default');
258
+ }
259
+ catch {
260
+ // A missing implicit default is not a visible conversation.
261
+ }
262
+ }
263
+ const unique = [...new Set(ids.filter((id) => id !== 'default'))].sort();
264
+ return ids.includes('default') ? ['default', ...unique] : unique;
265
+ }
266
+ export async function listSessionSummaries(cwd) {
267
+ const ids = await listSessions(cwd);
268
+ const metadata = await loadMetadata();
269
+ const group = metadata[cwdHash(cwd)] ?? {};
270
+ const indexed = await workspaceIndexService.findByPath(cwd).catch(() => null);
271
+ const indexedById = new Map((indexed?.sessions ?? []).map((session) => [session.id, session]));
272
+ for (const session of indexed?.sessions ?? []) {
273
+ if (!ids.includes(session.id))
274
+ ids.push(session.id);
102
275
  }
103
- return ids.length > 0 ? ids : ['default'];
276
+ return Promise.all(ids.map(async (id) => {
277
+ const existing = group[id] ?? indexedById.get(id);
278
+ let modified = existing?.updatedAt ?? Date.now();
279
+ try {
280
+ modified = (await stat(await resolveSessionFile(cwd, id))).mtimeMs;
281
+ }
282
+ catch { /* metadata-only draft */ }
283
+ return existing
284
+ ? { ...existing, updatedAt: Math.max(existing.updatedAt, modified) }
285
+ : {
286
+ id,
287
+ title: id === 'default' ? '默认会话' : id,
288
+ createdAt: modified,
289
+ updatedAt: modified,
290
+ };
291
+ }));
292
+ }
293
+ async function createSessionUnlocked(cwd, options) {
294
+ const id = validateSessionId(options.id ?? randomUUID());
295
+ const metadata = await loadMetadata();
296
+ const hash = cwdHash(cwd);
297
+ const group = metadata[hash] ?? {};
298
+ if (group[id] || (await listSessions(cwd)).includes(id))
299
+ throw new Error(`session 已存在: ${id}`);
300
+ const now = Date.now();
301
+ const summary = {
302
+ id,
303
+ title: options.title?.trim() || '新会话',
304
+ createdAt: now,
305
+ updatedAt: now,
306
+ ...(options.modelId?.trim() ? { modelId: options.modelId.trim() } : {}),
307
+ };
308
+ metadata[hash] = { ...group, [id]: summary };
309
+ await saveMetadata(metadata);
310
+ // 权威元数据已落盘;索引只是便利层,失败不阻断建会话(见 setActiveSessionUnlocked)。
311
+ await workspaceIndexService.touchSession(cwd, summary).catch(async () => {
312
+ await workspaceIndexService.recordPendingMutation({ type: 'touch', path: cwd, session: summary }).catch(() => { });
313
+ });
314
+ return summary;
315
+ }
316
+ export async function createSession(cwd, options = {}) {
317
+ return enqueueMutation(() => createSessionUnlocked(cwd, options));
318
+ }
319
+ async function updateSessionMetadataUnlocked(cwd, sessionId, patch) {
320
+ const id = validateSessionId(sessionId);
321
+ const metadata = await loadMetadata();
322
+ const hash = cwdHash(cwd);
323
+ const group = metadata[hash] ?? {};
324
+ const now = Date.now();
325
+ const previous = group[id] ?? { id, title: id === 'default' ? '默认会话' : id, createdAt: now, updatedAt: now };
326
+ const next = {
327
+ ...previous,
328
+ ...(patch.title !== undefined ? { title: patch.title.trim() || previous.title } : {}),
329
+ ...(patch.lastMessagePreview !== undefined ? { lastMessagePreview: patch.lastMessagePreview.slice(0, 240) } : {}),
330
+ ...(patch.modelId === null ? { modelId: undefined } : patch.modelId !== undefined ? { modelId: patch.modelId.trim() } : {}),
331
+ updatedAt: now,
332
+ };
333
+ metadata[hash] = { ...group, [id]: next };
334
+ await saveMetadata(metadata);
335
+ // 权威元数据已落盘;索引只是便利层,失败不阻断元数据更新。
336
+ await workspaceIndexService.touchSession(cwd, next).catch(async () => {
337
+ await workspaceIndexService.recordPendingMutation({ type: 'touch', path: cwd, session: next }).catch(() => { });
338
+ });
339
+ return next;
340
+ }
341
+ export async function updateSessionMetadata(cwd, sessionId, patch) {
342
+ return enqueueMutation(() => updateSessionMetadataUnlocked(cwd, sessionId, patch));
343
+ }
344
+ export async function copySession(cwd, sourceSessionId, title) {
345
+ return enqueueMutation(async () => {
346
+ const sourceId = validateSessionId(sourceSessionId);
347
+ const source = (await listSessionSummaries(cwd)).find((session) => session.id === sourceId);
348
+ const target = await createSessionUnlocked(cwd, { title: title ?? '会话副本', ...(source?.modelId ? { modelId: source.modelId } : {}) });
349
+ try {
350
+ await mkdir(sessionsDirPath(), { recursive: true });
351
+ await copyFile(await resolveSessionFile(cwd, sourceId), sessionFileFor(cwd, target.id));
352
+ }
353
+ catch {
354
+ // An empty source session is still valid; its metadata is the useful clone.
355
+ }
356
+ return target;
357
+ });
358
+ }
359
+ export async function deleteSession(cwd, sessionId) {
360
+ return enqueueMutation(async () => {
361
+ const id = validateSessionId(sessionId);
362
+ if (id === 'default')
363
+ throw new Error('默认会话不能删除,可以归档后清空');
364
+ await unlink(await resolveSessionFile(cwd, id)).catch(() => { });
365
+ const metadata = await loadMetadata();
366
+ const hash = cwdHash(cwd);
367
+ if (metadata[hash]) {
368
+ delete metadata[hash][id];
369
+ await saveMetadata(metadata);
370
+ }
371
+ // 权威元数据已删除;索引只是便利层,失败不阻断删会话。
372
+ await workspaceIndexService.removeSession(cwd, id).catch(async () => {
373
+ await workspaceIndexService.recordPendingMutation({ type: 'remove-session', path: cwd, sessionId: id }).catch(() => { });
374
+ });
375
+ // 注意:这里故意直接读 loadActiveMap + 调 *Unlocked,不调导出的
376
+ // getActiveSession/setActiveSession——同一个已在队列里的 mutation
377
+ // 不能再排一次同一队列,否则自己等自己,死锁。
378
+ const activeMap = await loadActiveMap();
379
+ if ((activeMap[cwdHash(cwd)] ?? 'default') === id) {
380
+ await setActiveSessionUnlocked(cwd, 'default');
381
+ }
382
+ });
104
383
  }
@@ -62,6 +62,20 @@ export function estimateToolsTokens(tools) {
62
62
  }
63
63
  return total;
64
64
  }
65
+ function estimateContentPartTokens(part) {
66
+ if (part.type === 'text')
67
+ return countTextTokens(part.text);
68
+ if (part.type === 'image_url') {
69
+ const detail = part.image_url.detail ?? 'default';
70
+ if (detail === 'low')
71
+ return 600;
72
+ if (detail === 'high')
73
+ return 9000;
74
+ return 3000;
75
+ }
76
+ // v1 未正式暴露视频入口;保守估算,防压缩阈值低估。
77
+ return 15_000;
78
+ }
65
79
  /** 估算整段历史的 token 数(包含 role / 工具调用结构的开销) */
66
80
  export function countMessagesTokens(messages) {
67
81
  let total = 0;
@@ -71,6 +85,10 @@ export function countMessagesTokens(messages) {
71
85
  if (typeof m.content === 'string') {
72
86
  total += countTextTokens(m.content);
73
87
  }
88
+ else if (Array.isArray(m.content)) {
89
+ for (const part of m.content)
90
+ total += estimateContentPartTokens(part);
91
+ }
74
92
  else if (m.content === null) {
75
93
  // assistant 的 content 可以是 null(只有 tool_calls 时)
76
94
  }