@xiaobuyu/nodesign 0.0.6 → 0.0.8

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 (383) hide show
  1. package/README.md +266 -79
  2. package/README.zh-CN.md +254 -67
  3. package/package.json +2 -2
  4. package/server/api/admin.js +7 -0
  5. package/server/api/assets/docx-page.js +5 -4
  6. package/server/api/assets/helpers.js +26 -0
  7. package/server/api/assets/notes.js +50 -0
  8. package/server/api/assets.js +31 -38
  9. package/server/api/board.js +29 -1
  10. package/server/api/browse.js +3 -2
  11. package/server/api/chatai.js +9 -8
  12. package/server/api/exports/cards.js +7 -6
  13. package/server/api/exports/docx-pdf.js +4 -1
  14. package/server/api/instruction.js +3 -3
  15. package/server/api/local.js +10 -8
  16. package/server/api/projects.js +19 -3
  17. package/server/api/publish.js +3 -2
  18. package/server/api/sessions-rewind.js +220 -0
  19. package/server/api/sessions.js +58 -194
  20. package/server/api/stage.js +35 -0
  21. package/server/api/turn-model-switch.js +109 -0
  22. package/server/api/turn.js +52 -88
  23. package/server/auth/middleware.js +29 -6
  24. package/server/auth/tier.js +27 -7
  25. package/server/auth/users-store.js +14 -1
  26. package/server/engine/agent/actor-trail.js +110 -0
  27. package/server/engine/agent/agent-shared.js +22 -52
  28. package/server/engine/agent/beat-state.js +61 -0
  29. package/server/engine/agent/cast.js +322 -0
  30. package/server/engine/agent/events.js +18 -1
  31. package/server/engine/agent/hooks/beat-gate.js +73 -0
  32. package/server/engine/agent/hooks/failure.js +11 -1
  33. package/server/engine/agent/hooks/lifecycle.js +16 -41
  34. package/server/engine/agent/hooks/post-guidance.js +4 -38
  35. package/server/engine/agent/hooks/post-trim.js +1 -0
  36. package/server/engine/agent/hooks/pre-board-dirty.js +29 -0
  37. package/server/engine/agent/hooks/pre-defaults.js +206 -2
  38. package/server/engine/agent/hooks/pre-injectors.js +19 -2
  39. package/server/engine/agent/hooks/pre-peer-guard.js +88 -0
  40. package/server/engine/agent/hooks/pre-unknown-params.js +82 -0
  41. package/server/engine/agent/hooks/pre-workspace-scope-guard.js +33 -0
  42. package/server/engine/agent/hooks/resident-role-lifecycle.js +100 -0
  43. package/server/engine/agent/hooks/slot-alias.js +30 -0
  44. package/server/engine/agent/hooks/user-prompt-submit.js +208 -47
  45. package/server/engine/agent/hooks.js +80 -14
  46. package/server/engine/agent/isolation.js +25 -2
  47. package/server/engine/agent/memory-config.js +87 -0
  48. package/server/engine/agent/model-context.js +102 -18
  49. package/server/engine/agent/model-table.js +361 -58
  50. package/server/engine/agent/prompts/nodesign-prelude.md +579 -70
  51. package/server/engine/agent/prompts/tools/direct-edit-protocol.md +1 -1
  52. package/server/engine/agent/prompts/tools/generate-image-cookbook.md +26 -9
  53. package/server/engine/agent/prompts/tools/paint-still-cookbook.md +1 -1
  54. package/server/engine/agent/prompts/tools/site-reference.md +15 -4
  55. package/server/engine/agent/role-card.js +175 -0
  56. package/server/engine/agent/rp-mode.js +17 -0
  57. package/server/engine/agent/session-loop.js +55 -32
  58. package/server/engine/agent/stage-status.js +73 -0
  59. package/server/engine/agent/subagent-flight.js +73 -0
  60. package/server/engine/agent/system-prompts.js +41 -0
  61. package/server/engine/agent/tool-input-stream.js +165 -0
  62. package/server/engine/agents/explorer.md +1 -1
  63. package/server/engine/agents/index.js +1 -1
  64. package/server/engine/mcp/actor.js +59 -0
  65. package/server/engine/mcp/capability-gate.js +21 -2
  66. package/server/engine/mcp/index.js +118 -33
  67. package/server/engine/mcp/mode-profile.js +139 -0
  68. package/server/engine/mcp/server-name.js +19 -0
  69. package/server/engine/mcp/tools/artifact-session.js +2 -1
  70. package/server/engine/mcp/tools/browse-computer.js +2 -1
  71. package/server/engine/mcp/tools/browse-find-batch.js +95 -16
  72. package/server/engine/mcp/tools/browse.js +25 -3
  73. package/server/engine/mcp/tools/cast-role.js +167 -0
  74. package/server/engine/mcp/tools/crystallize-skill.js +1 -1
  75. package/server/engine/mcp/tools/draw-trend.js +104 -0
  76. package/server/engine/mcp/tools/edit-board-schema.js +56 -0
  77. package/server/engine/mcp/tools/edit-board.js +569 -0
  78. package/server/engine/mcp/tools/generate-image.js +50 -70
  79. package/server/engine/mcp/tools/helpers/codex-imagegen.js +139 -0
  80. package/server/engine/mcp/tools/helpers/rembg.js +7 -2
  81. package/server/engine/mcp/tools/helpers/shot-pipeline.js +109 -0
  82. package/server/engine/mcp/tools/list-pages.js +11 -5
  83. package/server/engine/mcp/tools/look-at-board.js +4 -3
  84. package/server/engine/mcp/tools/open-sheet.js +242 -0
  85. package/server/engine/mcp/tools/organize-board.js +45 -6
  86. package/server/engine/mcp/tools/paint-still.js +1 -1
  87. package/server/engine/mcp/tools/pin-to-board.js +103 -19
  88. package/server/engine/mcp/tools/read-board.js +123 -16
  89. package/server/engine/mcp/tools/read-tavern-json.js +87 -19
  90. package/server/engine/mcp/tools/report-issue.js +15 -7
  91. package/server/engine/mcp/tools/role-memory.js +83 -0
  92. package/server/engine/mcp/tools/roll-dice.js +82 -0
  93. package/server/engine/mcp/tools/roll-film.js +1 -1
  94. package/server/engine/mcp/tools/screenshot.js +19 -9
  95. package/server/engine/mcp/tools/set-vars.js +150 -0
  96. package/server/engine/mcp/tools/sheet-replan.js +46 -0
  97. package/server/engine/mcp/tools/web-search.js +12 -16
  98. package/server/engine/mcp/tools/write-on-board-flow.js +108 -0
  99. package/server/engine/mcp/tools/write-on-board-place.js +211 -0
  100. package/server/engine/mcp/tools/write-on-board-role-anchor.js +48 -0
  101. package/server/engine/mcp/tools/write-on-board-schema.js +125 -0
  102. package/server/engine/mcp/tools/write-on-board.js +568 -99
  103. package/server/engine/plugins/nodesign/skills/blackboard-rp/SKILL.md +215 -0
  104. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +14 -7
  105. package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +44 -5
  106. package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +16 -0
  107. package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +24 -8
  108. package/server/engine/plugins/nodesign/skills/story-craft/SKILL.md +94 -0
  109. package/server/engine/plugins/nodesign/skills/story-import/SKILL.md +112 -0
  110. package/server/engine/plugins/nodesign/skills/story-intimacy/SKILL.md +72 -0
  111. package/server/engine/plugins/nodesign/skills/story-voice/SKILL.md +66 -0
  112. package/server/engine/plugins/nodesign/skills/story-voice/voices//345/206/267/347/241/254.md +33 -0
  113. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/203/205/346/204/237/347/233/264/347/273/231.md +42 -0
  114. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/204/217/350/257/206/346/265/201.md +42 -0
  115. package/server/engine/plugins/nodesign/skills/story-voice/voices//346/255/246/344/276/240/347/231/275/350/257/235.md +34 -0
  116. package/server/engine/plugins/nodesign/skills/story-voice/voices//347/224/237/346/264/273/346/265/201.md +35 -0
  117. package/server/engine/plugins/nodesign/skills/story-voice/voices//347/231/275/346/217/217.md +37 -0
  118. package/server/engine/plugins/nodesign/skills/story-voice/voices//350/210/236/345/217/260/345/211/247.md +32 -0
  119. package/server/engine/plugins/nodesign/skills/story-voice/voices//351/230/266/346/256/265/345/210/207/346/215/242.md +42 -0
  120. package/server/engine/runs/board-seater.js +272 -0
  121. package/server/engine/runs/board-tasklist.js +90 -48
  122. package/server/engine/runs/turn-relay.js +24 -2
  123. package/server/engine/runs/user-chalk-echo.js +44 -0
  124. package/server/index.js +2 -2
  125. package/server/lib/artifact-target.js +31 -14
  126. package/server/lib/board-anchor.js +49 -0
  127. package/server/lib/board-dirty.js +93 -0
  128. package/server/lib/board-excerpt.js +80 -0
  129. package/server/lib/board-follow.js +111 -0
  130. package/server/lib/board-groups.js +1 -5
  131. package/server/lib/board-hero.js +3 -1
  132. package/server/lib/board-kind-sizes.js +38 -3
  133. package/server/lib/board-lanes.js +58 -0
  134. package/server/lib/board-obstacles.js +80 -0
  135. package/server/lib/board-relations.js +5 -1
  136. package/server/lib/board-sheets.js +467 -0
  137. package/server/lib/board-shelf.js +86 -0
  138. package/server/lib/board-transform.js +62 -0
  139. package/server/lib/canvas-id.js +46 -4
  140. package/server/lib/chalk-flow.js +88 -0
  141. package/server/lib/chalk-rewrite.js +63 -0
  142. package/server/lib/chalk-size-pref.js +48 -0
  143. package/server/lib/chalk.js +42 -3
  144. package/server/lib/export-collect.js +3 -1
  145. package/server/lib/ingress/forward-openai-chat.js +16 -4
  146. package/server/lib/ingress/openai-chat.js +23 -12
  147. package/server/lib/ingress/session-routes.js +3 -3
  148. package/server/lib/ingress/stream-watchdog.js +53 -0
  149. package/server/lib/ingress/upstream-billing.js +15 -0
  150. package/server/lib/ingress/upstream-error-hints.js +36 -0
  151. package/server/lib/issues-store.js +3 -1
  152. package/server/lib/json-preview.js +58 -0
  153. package/server/lib/kinds/docx.js +11 -4
  154. package/server/lib/kinds/index.js +6 -16
  155. package/server/lib/kinds/site.js +40 -7
  156. package/server/lib/model-ingress.js +26 -1
  157. package/server/lib/moderation.js +10 -5
  158. package/server/lib/quota.js +26 -6
  159. package/server/lib/rect.js +50 -0
  160. package/server/lib/rewrite-refs.js +110 -0
  161. package/server/lib/screen.js +36 -0
  162. package/server/lib/sheet-state.js +19 -0
  163. package/server/lib/sketch-array.js +118 -0
  164. package/server/lib/sketch-hatch.js +99 -0
  165. package/server/lib/sketch-layout.js +269 -69
  166. package/server/lib/sketch-shapes.js +152 -0
  167. package/server/lib/sketch-stencils.js +170 -0
  168. package/server/lib/ssrf-guard.js +9 -0
  169. package/server/lib/state-table.js +237 -0
  170. package/server/lib/state-trend.js +94 -0
  171. package/server/lib/state-triggers.js +199 -0
  172. package/server/lib/task-scan.js +18 -0
  173. package/server/lib/tavern-json.js +48 -0
  174. package/server/lib/user-uploads.js +88 -0
  175. package/server/projects/board-limits.js +5 -3
  176. package/server/projects/board-sanitize.js +220 -10
  177. package/server/projects/board-store.js +137 -7
  178. package/server/projects/memory-migration.js +85 -0
  179. package/server/projects/move-entry.js +11 -1
  180. package/server/projects/session-jsonl.js +110 -0
  181. package/server/projects/store.js +56 -3
  182. package/server/projects/ui-config.js +2 -1
  183. package/server/projects/viewpoint-store.js +35 -3
  184. package/server/projects/workspace-slots.js +30 -0
  185. package/server/projects/workspace-templates.js +42 -10
  186. package/server/projects/workspace.js +25 -26
  187. package/server/runtime/capabilities.js +6 -2
  188. package/server/services/rembg-launcher.js +5 -2
  189. package/server/shared/locales.js +70 -0
  190. package/server/shared/messages-en.js +69 -0
  191. package/server/shared/messages.js +65 -0
  192. package/server/ws/broker.js +3 -0
  193. package/server/ws/index.js +43 -3
  194. package/web/README.md +1 -1
  195. package/web/dist/assets/{BrowserWindow-P_vIdh-G.js → BrowserWindow-CcaGRWz5.js} +1 -1
  196. package/web/dist/assets/DeckWindow-BIr2KGFT.js +6 -0
  197. package/web/dist/assets/{DocxWindow-DSAU1-tD.js → DocxWindow-B9KZmU5z.js} +2 -2
  198. package/web/dist/assets/{MermaidBlock-DR1GikCv.js → MermaidBlock-DrfRnz8E.js} +43 -43
  199. package/web/dist/assets/{SiteWindow-92uBuRRR.js → SiteWindow-CJOjCF7d.js} +6 -6
  200. package/web/dist/assets/{abnfDiagram-VCTEODGH-uA8Esix4.js → abnfDiagram-VCTEODGH-CUZbQGIg.js} +1 -1
  201. package/web/dist/assets/{arc-D_khH_xN.js → arc-C1-HSUny.js} +1 -1
  202. package/web/dist/assets/{architectureDiagram-5GKGNRK7-DydJHqOt.js → architectureDiagram-5GKGNRK7-B5nwvu60.js} +2 -2
  203. package/web/dist/assets/{blockDiagram-NRAW4CY4-B7wBbKiW.js → blockDiagram-NRAW4CY4-uPPuFLwR.js} +1 -1
  204. package/web/dist/assets/{c4Diagram-UCG6FXSJ-6mrH0x7Y.js → c4Diagram-UCG6FXSJ-C0mKBFlz.js} +1 -1
  205. package/web/dist/assets/channel-BxpWNOGU.js +1 -0
  206. package/web/dist/assets/{chunk-2Q5K7J3B-Cuikho5Q.js → chunk-2Q5K7J3B-Cs0rlZ4q.js} +1 -1
  207. package/web/dist/assets/{chunk-5VM5RSS4-3-KZ5AUz.js → chunk-5VM5RSS4-DtR63oYb.js} +1 -1
  208. package/web/dist/assets/{chunk-F27PBJKO-Bkwq2gGU.js → chunk-F27PBJKO-B3MRdezJ.js} +1 -1
  209. package/web/dist/assets/{chunk-G27WJ6UU-BLyQzdGE.js → chunk-G27WJ6UU-BtwntwpS.js} +1 -1
  210. package/web/dist/assets/{chunk-JWPE2WC7-B7HxxQds.js → chunk-JWPE2WC7-BHsTe5PD.js} +1 -1
  211. package/web/dist/assets/{chunk-LCL6LL3I-CtXrnUwF.js → chunk-LCL6LL3I-knY15Qdm.js} +1 -1
  212. package/web/dist/assets/{chunk-POPQ4Y6H-dxddZlXt.js → chunk-POPQ4Y6H-C51tgK_I.js} +1 -1
  213. package/web/dist/assets/{chunk-SVP7TREG-DpkmrgHn.js → chunk-SVP7TREG-CnwRZmF_.js} +1 -1
  214. package/web/dist/assets/{chunk-XXDRQBXY-BwZ7RbpQ.js → chunk-XXDRQBXY-BtXzVF-_.js} +1 -1
  215. package/web/dist/assets/classDiagram-DTDB5LWJ-DUEKmDfo.js +1 -0
  216. package/web/dist/assets/classDiagram-v2-JRS7N3AN-DUEKmDfo.js +1 -0
  217. package/web/dist/assets/{cose-bilkent-JH36ORCC-DGFOsMUt.js → cose-bilkent-JH36ORCC-CzWlGP6i.js} +1 -1
  218. package/web/dist/assets/{cynefin-OW5HDTMX-DUwn3ehS.js → cynefin-OW5HDTMX-vK64EGSv.js} +1 -1
  219. package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js → cynefinDiagram-5FMLGOSQ-Bs3g1Gvh.js} +1 -1
  220. package/web/dist/assets/{dagre-3AP2YEHR-CyourLfz.js → dagre-3AP2YEHR-BhgkYe8V.js} +1 -1
  221. package/web/dist/assets/{diagram-S7CK7UJ4-BTMbx2np.js → diagram-S7CK7UJ4-C_6mvD2V.js} +1 -1
  222. package/web/dist/assets/{diagram-UQ7AKVKN-UK94_1-P.js → diagram-UQ7AKVKN-qW7tssUW.js} +1 -1
  223. package/web/dist/assets/{diagram-VSXAHHWV-C7zS-v9v.js → diagram-VSXAHHWV-txJPHpvG.js} +1 -1
  224. package/web/dist/assets/{diagram-VX7I27RA-DKJRa6CK.js → diagram-VX7I27RA-CQTMsCnL.js} +1 -1
  225. package/web/dist/assets/{diagram-Z3DM3KII-kUbAfGAp.js → diagram-Z3DM3KII--cb1hoE6.js} +1 -1
  226. package/web/dist/assets/{ebnfDiagram-PWID7BFC-CVW16_vb.js → ebnfDiagram-PWID7BFC-Da6lDU77.js} +1 -1
  227. package/web/dist/assets/{erDiagram-SSCWMZ5O-BRQMFQ5y.js → erDiagram-SSCWMZ5O-DGCMrsQn.js} +1 -1
  228. package/web/dist/assets/{flowDiagram-A5DVABFB-S34LpLtK.js → flowDiagram-A5DVABFB-GdOL_qno.js} +1 -1
  229. package/web/dist/assets/{ganttDiagram-EL5Y4UJY-BFiqk1-G.js → ganttDiagram-EL5Y4UJY-Cx1F_jBK.js} +4 -4
  230. package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-LZsrdyyv.js → gitGraphDiagram-WWUBYQGX-B5_6JhR3.js} +1 -1
  231. package/web/dist/assets/hand-CuvZZQGW.webp +0 -0
  232. package/web/dist/assets/index-BYKfMnmv.js +2759 -0
  233. package/web/dist/assets/index-CW7VsrVg.css +1 -0
  234. package/web/dist/assets/{infoDiagram-RXCK75RN-0DvN6KIp.js → infoDiagram-RXCK75RN-CjvKpSPA.js} +1 -1
  235. package/web/dist/assets/{ishikawaDiagram-5VMMS53U-CaY3A-Bx.js → ishikawaDiagram-5VMMS53U-CxLoAPhx.js} +1 -1
  236. package/web/dist/assets/{journeyDiagram-EYS64GPL-BBeiaCrj.js → journeyDiagram-EYS64GPL-BfvS1upJ.js} +1 -1
  237. package/web/dist/assets/{kanban-definition-3QL26DDD-ux42DRXQ.js → kanban-definition-3QL26DDD-DKPPu0w4.js} +1 -1
  238. package/web/dist/assets/{layout-CnXiF_NB.js → layout-Dhgck-Zo.js} +1 -1
  239. package/web/dist/assets/{linear-BMW0u6G6.js → linear-DMSQ-aUw.js} +1 -1
  240. package/web/dist/assets/lxgw-nd-app-bold-D1pKA1bR.woff2 +0 -0
  241. package/web/dist/assets/lxgw-nd-app-regular-BDe3d26v.woff2 +0 -0
  242. package/web/dist/assets/lxgw-nd-bold-Bufvlg03.woff2 +0 -0
  243. package/web/dist/assets/lxgw-nd-regular-DQwoI4OP.woff2 +0 -0
  244. package/web/dist/assets/lxgwwenkaiscreen-subset-100-DLPe7tEt.woff2 +0 -0
  245. package/web/dist/assets/lxgwwenkaiscreen-subset-101-d3VsZKWT.woff2 +0 -0
  246. package/web/dist/assets/lxgwwenkaiscreen-subset-102-ClzJuLq6.woff2 +0 -0
  247. package/web/dist/assets/lxgwwenkaiscreen-subset-103-DuDnQ6K-.woff2 +0 -0
  248. package/web/dist/assets/lxgwwenkaiscreen-subset-104-kPGW3Eem.woff2 +0 -0
  249. package/web/dist/assets/lxgwwenkaiscreen-subset-105-BzD54h-0.woff2 +0 -0
  250. package/web/dist/assets/lxgwwenkaiscreen-subset-106-BQ7fIzlx.woff2 +0 -0
  251. package/web/dist/assets/lxgwwenkaiscreen-subset-107-D8tDfFHI.woff2 +0 -0
  252. package/web/dist/assets/lxgwwenkaiscreen-subset-108-tGhkpwep.woff2 +0 -0
  253. package/web/dist/assets/lxgwwenkaiscreen-subset-109-7SMVSrly.woff2 +0 -0
  254. package/web/dist/assets/lxgwwenkaiscreen-subset-110-_uDKrFed.woff2 +0 -0
  255. package/web/dist/assets/lxgwwenkaiscreen-subset-111-Bio825RE.woff2 +0 -0
  256. package/web/dist/assets/lxgwwenkaiscreen-subset-112-D2ee0i-C.woff2 +0 -0
  257. package/web/dist/assets/lxgwwenkaiscreen-subset-113-JPFVDrga.woff2 +0 -0
  258. package/web/dist/assets/lxgwwenkaiscreen-subset-114-BcfxYA4w.woff2 +0 -0
  259. package/web/dist/assets/lxgwwenkaiscreen-subset-115-B1HeeOsd.woff2 +0 -0
  260. package/web/dist/assets/lxgwwenkaiscreen-subset-116-hY12tXpq.woff2 +0 -0
  261. package/web/dist/assets/lxgwwenkaiscreen-subset-117-BvuZwmre.woff2 +0 -0
  262. package/web/dist/assets/lxgwwenkaiscreen-subset-118-tzvs_H7j.woff2 +0 -0
  263. package/web/dist/assets/lxgwwenkaiscreen-subset-119-J2DWuYDN.woff2 +0 -0
  264. package/web/dist/assets/lxgwwenkaiscreen-subset-21-pDubCJV5.woff2 +0 -0
  265. package/web/dist/assets/lxgwwenkaiscreen-subset-22-ChAPMpqp.woff2 +0 -0
  266. package/web/dist/assets/lxgwwenkaiscreen-subset-23-BBx3oNpT.woff2 +0 -0
  267. package/web/dist/assets/lxgwwenkaiscreen-subset-24-CYt-Oagk.woff2 +0 -0
  268. package/web/dist/assets/lxgwwenkaiscreen-subset-25-Cx3j-ZUv.woff2 +0 -0
  269. package/web/dist/assets/lxgwwenkaiscreen-subset-26-B9AfZ3LL.woff2 +0 -0
  270. package/web/dist/assets/lxgwwenkaiscreen-subset-27-Bh2s9MNE.woff2 +0 -0
  271. package/web/dist/assets/lxgwwenkaiscreen-subset-28-C02JPoKP.woff2 +0 -0
  272. package/web/dist/assets/lxgwwenkaiscreen-subset-29-T_tbUGhk.woff2 +0 -0
  273. package/web/dist/assets/lxgwwenkaiscreen-subset-30-DMfyNzhz.woff2 +0 -0
  274. package/web/dist/assets/lxgwwenkaiscreen-subset-31-DFfdzbkg.woff2 +0 -0
  275. package/web/dist/assets/lxgwwenkaiscreen-subset-32-Bs1fz2iW.woff2 +0 -0
  276. package/web/dist/assets/lxgwwenkaiscreen-subset-33-cpB96OkZ.woff2 +0 -0
  277. package/web/dist/assets/lxgwwenkaiscreen-subset-34-BhinPYWX.woff2 +0 -0
  278. package/web/dist/assets/lxgwwenkaiscreen-subset-35-D_njRQqA.woff2 +0 -0
  279. package/web/dist/assets/lxgwwenkaiscreen-subset-36-DqXlLE3t.woff2 +0 -0
  280. package/web/dist/assets/lxgwwenkaiscreen-subset-37-j5LbvMph.woff2 +0 -0
  281. package/web/dist/assets/lxgwwenkaiscreen-subset-38-oO4jWdKm.woff2 +0 -0
  282. package/web/dist/assets/lxgwwenkaiscreen-subset-39-CabsX4rw.woff2 +0 -0
  283. package/web/dist/assets/lxgwwenkaiscreen-subset-4-PatS3YHh.woff2 +0 -0
  284. package/web/dist/assets/lxgwwenkaiscreen-subset-40-yrLg3C3e.woff2 +0 -0
  285. package/web/dist/assets/lxgwwenkaiscreen-subset-41-DBXmCQhc.woff2 +0 -0
  286. package/web/dist/assets/lxgwwenkaiscreen-subset-42-Bqij_UiQ.woff2 +0 -0
  287. package/web/dist/assets/lxgwwenkaiscreen-subset-43-D3kp0Ntc.woff2 +0 -0
  288. package/web/dist/assets/lxgwwenkaiscreen-subset-44-B_BNGvK4.woff2 +0 -0
  289. package/web/dist/assets/lxgwwenkaiscreen-subset-45-CZc0cQWs.woff2 +0 -0
  290. package/web/dist/assets/lxgwwenkaiscreen-subset-46-CHQX5Kd9.woff2 +0 -0
  291. package/web/dist/assets/lxgwwenkaiscreen-subset-47-Ci8gLrHA.woff2 +0 -0
  292. package/web/dist/assets/lxgwwenkaiscreen-subset-48-run3ugD3.woff2 +0 -0
  293. package/web/dist/assets/lxgwwenkaiscreen-subset-49-DgfiUb6u.woff2 +0 -0
  294. package/web/dist/assets/lxgwwenkaiscreen-subset-5-B0CtGbX_.woff2 +0 -0
  295. package/web/dist/assets/lxgwwenkaiscreen-subset-50-CdCG-POr.woff2 +0 -0
  296. package/web/dist/assets/lxgwwenkaiscreen-subset-51-L2okeQZ2.woff2 +0 -0
  297. package/web/dist/assets/lxgwwenkaiscreen-subset-52-BZAqyGJW.woff2 +0 -0
  298. package/web/dist/assets/lxgwwenkaiscreen-subset-53-B0xWqLeU.woff2 +0 -0
  299. package/web/dist/assets/lxgwwenkaiscreen-subset-54-BZVQX-EA.woff2 +0 -0
  300. package/web/dist/assets/lxgwwenkaiscreen-subset-55-093mkIUJ.woff2 +0 -0
  301. package/web/dist/assets/lxgwwenkaiscreen-subset-56-CbhWG8G-.woff2 +0 -0
  302. package/web/dist/assets/lxgwwenkaiscreen-subset-57-pLQuvo6c.woff2 +0 -0
  303. package/web/dist/assets/lxgwwenkaiscreen-subset-58-BHhoY50x.woff2 +0 -0
  304. package/web/dist/assets/lxgwwenkaiscreen-subset-59-D1_6DuIQ.woff2 +0 -0
  305. package/web/dist/assets/lxgwwenkaiscreen-subset-6-BnAelmYU.woff2 +0 -0
  306. package/web/dist/assets/lxgwwenkaiscreen-subset-60-HDBIuLTZ.woff2 +0 -0
  307. package/web/dist/assets/lxgwwenkaiscreen-subset-61-C-c13SeU.woff2 +0 -0
  308. package/web/dist/assets/lxgwwenkaiscreen-subset-62-kHRVOhZ2.woff2 +0 -0
  309. package/web/dist/assets/lxgwwenkaiscreen-subset-63-C0MCsvFv.woff2 +0 -0
  310. package/web/dist/assets/lxgwwenkaiscreen-subset-64-DZekguCj.woff2 +0 -0
  311. package/web/dist/assets/lxgwwenkaiscreen-subset-65-CtP6c3IU.woff2 +0 -0
  312. package/web/dist/assets/lxgwwenkaiscreen-subset-66-DLVWoMSf.woff2 +0 -0
  313. package/web/dist/assets/lxgwwenkaiscreen-subset-67-BVuOnayH.woff2 +0 -0
  314. package/web/dist/assets/lxgwwenkaiscreen-subset-68-DpxQ6Mnx.woff2 +0 -0
  315. package/web/dist/assets/lxgwwenkaiscreen-subset-69-qesBxLmC.woff2 +0 -0
  316. package/web/dist/assets/lxgwwenkaiscreen-subset-70-BBt1xN7h.woff2 +0 -0
  317. package/web/dist/assets/lxgwwenkaiscreen-subset-71-BWqZacwB.woff2 +0 -0
  318. package/web/dist/assets/lxgwwenkaiscreen-subset-72-B1wP_Yst.woff2 +0 -0
  319. package/web/dist/assets/lxgwwenkaiscreen-subset-73-C99T7jNZ.woff2 +0 -0
  320. package/web/dist/assets/lxgwwenkaiscreen-subset-74-DrUTsWBe.woff2 +0 -0
  321. package/web/dist/assets/lxgwwenkaiscreen-subset-75-C4M5XXQe.woff2 +0 -0
  322. package/web/dist/assets/lxgwwenkaiscreen-subset-76-Bq8spVWF.woff2 +0 -0
  323. package/web/dist/assets/lxgwwenkaiscreen-subset-77-D9vNupE2.woff2 +0 -0
  324. package/web/dist/assets/lxgwwenkaiscreen-subset-78-D6th7Gnu.woff2 +0 -0
  325. package/web/dist/assets/lxgwwenkaiscreen-subset-79-bezM85cS.woff2 +0 -0
  326. package/web/dist/assets/lxgwwenkaiscreen-subset-80-DD8pnhG2.woff2 +0 -0
  327. package/web/dist/assets/lxgwwenkaiscreen-subset-81-N6jtSyBs.woff2 +0 -0
  328. package/web/dist/assets/lxgwwenkaiscreen-subset-82-D6c180bZ.woff2 +0 -0
  329. package/web/dist/assets/lxgwwenkaiscreen-subset-83-em17AadM.woff2 +0 -0
  330. package/web/dist/assets/lxgwwenkaiscreen-subset-84-CJ8gdrMK.woff2 +0 -0
  331. package/web/dist/assets/lxgwwenkaiscreen-subset-85-9PPGO1eB.woff2 +0 -0
  332. package/web/dist/assets/lxgwwenkaiscreen-subset-86-CjWPf-u5.woff2 +0 -0
  333. package/web/dist/assets/lxgwwenkaiscreen-subset-87-8D_X_yOC.woff2 +0 -0
  334. package/web/dist/assets/lxgwwenkaiscreen-subset-88-9vtKFPBx.woff2 +0 -0
  335. package/web/dist/assets/lxgwwenkaiscreen-subset-89-BhE5jAKX.woff2 +0 -0
  336. package/web/dist/assets/lxgwwenkaiscreen-subset-90-D2Npfm5E.woff2 +0 -0
  337. package/web/dist/assets/lxgwwenkaiscreen-subset-91-C6Ft1-Eu.woff2 +0 -0
  338. package/web/dist/assets/lxgwwenkaiscreen-subset-97-CaExyQP9.woff2 +0 -0
  339. package/web/dist/assets/lxgwwenkaiscreen-subset-98-CPoVbsUd.woff2 +0 -0
  340. package/web/dist/assets/lxgwwenkaiscreen-subset-99-ZUQZGVgH.woff2 +0 -0
  341. package/web/dist/assets/{mindmap-definition-FBJOCRG2-hUxSpdKn.js → mindmap-definition-FBJOCRG2-CalfL_Kn.js} +1 -1
  342. package/web/dist/assets/{pegDiagram-XKGWAZYB-aCaQPH0w.js → pegDiagram-XKGWAZYB-D9mVWz7l.js} +1 -1
  343. package/web/dist/assets/pending-edit-apply-kwMFWsUe.js +31 -0
  344. package/web/dist/assets/{pieDiagram-E7YTZNPT-CmOEZ8zg.js → pieDiagram-E7YTZNPT-DELzbZNS.js} +1 -1
  345. package/web/dist/assets/{quadrantDiagram-AXDQQJYC-VW6fAhsV.js → quadrantDiagram-AXDQQJYC-TZqp4mfA.js} +1 -1
  346. package/web/dist/assets/{railroadDiagram-O6MQD6OU-CKJ7Ba5K.js → railroadDiagram-O6MQD6OU-DxX59f0i.js} +1 -1
  347. package/web/dist/assets/{requirementDiagram-EFPCY7ZU-pNGkq9Vw.js → requirementDiagram-EFPCY7ZU-DFdsJLIe.js} +1 -1
  348. package/web/dist/assets/{rotate-cw-KAfChbGV.js → rotate-cw-62liz7LX.js} +1 -1
  349. package/web/dist/assets/{sankeyDiagram-P5KCCOFB-BIMmDTfZ.js → sankeyDiagram-P5KCCOFB-ByYcd3fG.js} +1 -1
  350. package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-BBW7wzKY.js → sequenceDiagram-WJ2MYXX4-Bj-ml1jS.js} +1 -1
  351. package/web/dist/assets/{sizeCapture-X5ZJPWSS-DdkXmLYR.js → sizeCapture-X5ZJPWSS-KhsXThyD.js} +1 -1
  352. package/web/dist/assets/{square-dashed-mouse-pointer-CDUlhUP7.js → square-dashed-mouse-pointer-D_VrqVpy.js} +1 -1
  353. package/web/dist/assets/{stateDiagram-HBIQ2CUA-W313Uz16.js → stateDiagram-HBIQ2CUA-BJlpHfWU.js} +1 -1
  354. package/web/dist/assets/stateDiagram-v2-4QOOHH4V-CR2kYAH2.js +1 -0
  355. package/web/dist/assets/{swimlanes-XN3QIQJK-C29TL26K.js → swimlanes-XN3QIQJK-DUB9OJVs.js} +1 -1
  356. package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BitOxnG2.js +8 -0
  357. package/web/dist/assets/{timeline-definition-24CTP7MA-CF0JMObX.js → timeline-definition-24CTP7MA-C03NX82A.js} +1 -1
  358. package/web/dist/assets/{vennDiagram-4TSXK5OY-DrDFsaH3.js → vennDiagram-4TSXK5OY-B9pjzngp.js} +1 -1
  359. package/web/dist/assets/{wardleyDiagram-VM6X3IG4-CVrnRIS_.js → wardleyDiagram-VM6X3IG4-UqWq_mcT.js} +1 -1
  360. package/web/dist/assets/{xychartDiagram-S5SC5T6Z-BvkN1zsT.js → xychartDiagram-S5SC5T6Z-D2pDKtOv.js} +1 -1
  361. package/web/dist/index.html +2 -2
  362. package/server/api/memory.js +0 -185
  363. package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +0 -583
  364. package/server/engine/mcp/tools/arrange-on-board.js +0 -124
  365. package/server/engine/mcp/tools/create-on-board.js +0 -152
  366. package/server/engine/mcp/tools/edit-sketch.js +0 -167
  367. package/server/engine/mcp/tools/record-decision.js +0 -140
  368. package/server/engine/mcp/tools/relate-on-board.js +0 -112
  369. package/server/engine/mcp/tools/sketch-on-board.js +0 -337
  370. package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +0 -235
  371. package/server/engine/plugins/nodesign/skills/rp-craft/patterns//346/274/224/345/207/272/351/241/265-/346/234/200/345/260/217/345/256/236/347/216/260.html +0 -78
  372. package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +0 -268
  373. package/web/dist/assets/DeckWindow-BpLv5082.js +0 -16
  374. package/web/dist/assets/channel-DGkccMO1.js +0 -1
  375. package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +0 -1
  376. package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +0 -1
  377. package/web/dist/assets/index-C0KUf6Wd.css +0 -1
  378. package/web/dist/assets/index-GAamh1p4.js +0 -2152
  379. package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
  380. package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
  381. package/web/dist/assets/pending-edit-apply-AcdOQ9Ij.js +0 -31
  382. package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +0 -1
  383. package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +0 -8
@@ -121,7 +121,7 @@ and reopens on the next call — the profile survives, so you stay logged in.
121
121
  The user has a browser card on their desktop and can watch, or take over.`,
122
122
  {
123
123
  url: z.string().min(4).describe('Absolute http(s) URL.'),
124
- waitUntil: z.enum(['domcontentloaded', 'load', 'networkidle']).optional()
124
+ waitUntil: z.enum(['commit', 'domcontentloaded', 'load', 'networkidle']).optional()
125
125
  .describe("How long to wait. Default 'domcontentloaded' — fastest and enough to read; measured on this machine, 'networkidle' costs an extra 1.6-4s per page, so do not reach for it by reflex. Use it only when a JS-built page comes back empty."),
126
126
  digest: z.boolean().optional()
127
127
  .describe('Include the page digest (default true). Set false only when you are navigating purely to screenshot and do not want the text.'),
@@ -175,9 +175,31 @@ The user has a browser card on their desktop and can watch, or take over.`,
175
175
  ].filter(Boolean).join('\n'), true);
176
176
  }
177
177
  if (navErr) {
178
+ // 超时 ≠ 全失败:commit 之后页面往往已有内容。半张页面也交回去 ——
179
+ // agent 拿到骨架就能决定继续还是换站(08-19 上报:原建议「试
180
+ // waitUntil:"load"」是往错方向推 —— load 比 domcontentloaded 严,
181
+ // 连 DOMContentLoaded 都没到换 load 只会更慢地再失败一次)
182
+ const timedOut = /Timeout/i.test(navErr);
183
+ let landedUrl = null;
184
+ try { const u = page.url(); if (timedOut && u !== before && /^https?:/.test(u)) landedUrl = u; } catch { /* */ }
185
+ if (landedUrl) {
186
+ let lines = [];
187
+ try {
188
+ const data = await collectPage(page);
189
+ if (!data.missing) lines = formatPage(data, { compact: true });
190
+ } catch { /* 半张页面读不出摘要也照样交回 */ }
191
+ try { ctx?.emit?.({ type: 'run.browser_opened', url: landedUrl, ts: new Date().toISOString() }); } catch { /* */ }
192
+ return asText([
193
+ `⚠️ 等待超时(${navErr}),但页面已部分加载 —— 当前状态如下,够用就继续(read/screenshot 都可),不够就换站:`,
194
+ ...lines,
195
+ gate,
196
+ ].filter(Boolean).join('\n'));
197
+ }
178
198
  return asText([
179
199
  `导航失败:${navErr}`,
180
- '如果是超时:站点可能慢或者在挡自动化访问;试 waitUntil:"load",或者换一个参考站。',
200
+ timedOut
201
+ ? '超时多半是广告/追踪脚本卡死或站点反自动化,不是"等得不够久"。别换 waitUntil:"load"(更严只会更慢地失败);可试 waitUntil:"commit"(一有响应就接手,配 read/screenshot 看拿到多少),或者直接换一个参考站。'
202
+ : '站点可能在挡自动化访问;换一个参考站。',
181
203
  gate,
182
204
  ].filter(Boolean).join('\n'), true);
183
205
  }
@@ -436,7 +458,7 @@ about why they were taken is landfill.`,
436
458
  normalize: normalizeShot,
437
459
  });
438
460
  for (const f of r.files) {
439
- try { ctx?.emit?.({ type: 'run.file_changed', path: f.rel, change: 'add' }); } catch { /* */ }
461
+ try { ctx?.emit?.({ type: 'run.file_changed', filePath: f.rel, event: 'add' }); } catch { /* */ }
440
462
  }
441
463
  const lines = [`从 ${r.data.title || r.data.url} 采下来了:`];
442
464
  for (const f of r.files) lines.push(` ${f.rel}(${(f.bytes / 1024).toFixed(0)} KB,${f.kind})`);
@@ -0,0 +1,167 @@
1
+ /**
2
+ * mcp/tools/cast-role.js — cast_role MCP tool(2026-08-26 建;2026-08-28 演员位重构重写)
3
+ *
4
+ * ## 重写前后
5
+ *
6
+ * 旧语义:写 `.claude/agents/rp-<id>.md`(一角色一个子代理定义),派发要等 CLI 重扫。
7
+ * ⛔ 那条路 08-28 勘定为生产不可靠:会话中途写入的定义文件只有**写入时活着的那个
8
+ * CLI 进程**认得(chokidar 事件),resume 换进程后永久 `not found`(三场真会话:
9
+ * 12 连败 / 恰好成功 / 永久失明)。等待、轮询、公告续接五版都救不了,全史见 git
10
+ * 里本文件的旧版头注。
11
+ *
12
+ * 新语义:角色卡是**数据**,子代理是**预注册的演员位**(rp-actor / rp-narrator,
13
+ * 建项目时就落盘,见 cast.js slotAgentFile)。cast_role 只做两件事:
14
+ * 1. 把人设写成 `角色/<名>/角色卡.md`(用户在画布上看得见、随时能改的文件夹范式)
15
+ * 2. 在 `.nd/cast.json` 登记 slug → 展示名/笔权/卡路径(板书署名与名册 API 的展示源)
16
+ * 写完**当回合就能派**:Agent(subagent_type: 演员位, name: "rp-<id>", prompt: 卡)。
17
+ *
18
+ * 登记不是上场的前置(不登记直接派也行),它换来的是:板上署名是「程晚」不是
19
+ * slug、重启后按卡重新开演有据可查、用户能在角色文件夹里改人设。
20
+ */
21
+
22
+ import path from 'node:path';
23
+ import fs from 'node:fs/promises';
24
+ import { tool } from '@anthropic-ai/claude-agent-sdk';
25
+ import { z } from 'zod';
26
+ import { ROLE_PREFIX, ROLE_SLOT, isSlotType, isValidRoleSlug } from '../../agent/cast.js';
27
+ import { readCastRegistry } from '../../agent/role-card.js';
28
+
29
+ /** 角色 id(不含 rp- 前缀):ASCII、能当文件名、能当 SendMessage 收件人名。
30
+ * ⚠️ 这是 cast.js `ROLE_SLUG_RE` 的**收紧子集**(这边只许小写、≥2 字符)——
31
+ * 两个判据的蕴含关系(ID_RE 过的 id 拼上前缀必被 ROLE_SLUG_RE 认)由
32
+ * cast-role.test.js 钉着;改任何一边先看那条测试。导出仅供测试。 */
33
+ export const ID_RE = /^[a-z0-9][a-z0-9_-]{1,40}$/;
34
+
35
+ const ROLES_DIR = '角色';
36
+ const REGISTRY_REL = '.nd/cast.json';
37
+ const PERSONA_MAX = 20000;
38
+
39
+ /** 自由文本压一行:控制字符/行终止符全清(进 JSON 与返回话术都安全) */
40
+ function oneLine(v, max) {
41
+ return String(v ?? '')
42
+ .replace(/[\r\n\u2028\u2029]+/g, ' ')
43
+ .replace(/[\u0000-\u001f\u007f]/g, '')
44
+ .trim().slice(0, max);
45
+ }
46
+
47
+ /** 展示名 → 文件夹名:剥路径分隔与点前缀,空了退回 slug(文件夹名坏不了路径) */
48
+ function folderNameFor(displayName, slug) {
49
+ const cleaned = oneLine(displayName, 40).replace(/[\/\\]/g, '').replace(/^\.+/, '').trim();
50
+ return cleaned || slug;
51
+ }
52
+
53
+ export function makeCastRoleTool({ workspaceRoot, sessionId = null, ctx, roster = null }) {
54
+ void sessionId; void roster; // 旧签名的调用方兼容位(公告续接与回合闸已随重构退役)
55
+ return tool(
56
+ 'cast_role',
57
+ `Register a character card so this person has a fixed identity to act from.
58
+
59
+ Writes the card to ${ROLES_DIR}/<name>/角色卡.md (user-visible, user-editable) and records
60
+ the display name. The card is DATA, and in this build it is a reference for YOU:
61
+ character subagents are disabled, so you write everyone on stage yourself.
62
+
63
+ Use it for anyone who has a name, will show up again, and whom the user may want to talk
64
+ to. Re-read the card right before you write that person's lines — that is the thing that
65
+ keeps several characters from all sounding like you. A walk-on with one line needs no card.
66
+
67
+ ⛔ Do NOT dispatch a role subagent after this: no Agent(subagent_type: "${ROLE_SLOT}"),
68
+ no SendMessage to a character. That path is being debugged; the cards you write now will
69
+ plug straight back in once it returns.
70
+ When the user hands you a ready-made character card, prefer copying it in verbatim over
71
+ rewriting it.`,
72
+ {
73
+ id: z.string().describe('ASCII slug, no prefix: lowercase letters/digits/_/- , 2-41 chars. Becomes the address (rp-<id>).'),
74
+ name: z.string().min(1).max(40).describe('Display name, any language — what this character is actually called. Board signatures use it.'),
75
+ duty: z.string().min(1).max(400).describe('One line: who this is and when you would talk to them.'),
76
+ persona: z.string().min(1).describe("The card itself: who this person is, how they speak, what they would never do, plus two or three short samples of their voice. You read this back before writing their lines, so write it for that use — no meta rules about who writes what."),
77
+ },
78
+ async (args) => {
79
+ const fail = (msg) => ({ content: [{ type: 'text', text: msg }], isError: true });
80
+ if (!workspaceRoot) return fail('没有工作区,cast_role 不可用。');
81
+
82
+ const id = String(args.id || '').trim();
83
+ if (!ID_RE.test(id)) {
84
+ return fail(`角色 id「${id}」不合法:只能用小写字母、数字、下划线、连字符,2-41 个字符,首字符是字母或数字。`
85
+ + `(这个 id 要当 SendMessage 的收件人名,收件人名不收中文和空格 —— 中文名字放 name 参数。)`);
86
+ }
87
+ const slug = `${ROLE_PREFIX}${id}`;
88
+ if (!isValidRoleSlug(slug) || isSlotType(slug)) return fail(`角色名「${slug}」过不了名册校验(演员位的名字不能当角色名)。`);
89
+
90
+ const displayName = oneLine(args.name, 40);
91
+ if (!displayName) return fail('name 清洗后是空的 —— 换个展示名。');
92
+ const persona = String(args.persona || '').trim();
93
+ if (!persona) return fail('persona 是空的 —— 角色卡就是这个角色的全部身份,不能空。');
94
+ if (persona.length > PERSONA_MAX) {
95
+ return fail(`persona 有 ${persona.length} 字符,超过 ${PERSONA_MAX} 上限。`
96
+ + `把大部头设定挪进世界书/设定文件让角色自己 grep,卡上留人设主干。`);
97
+ }
98
+
99
+ // 卡落盘:角色/<名>/角色卡.md(文件夹范式:这个文件夹就是该角色的家,
100
+ // 之后的记忆/日记等件都住这里;用户随时可改,改动对"下次派发/唤醒后重读卡"生效)
101
+ const folder = folderNameFor(displayName, slug);
102
+ const dir = path.join(workspaceRoot, ROLES_DIR, folder);
103
+ const file = path.join(dir, '角色卡.md');
104
+ if (!path.resolve(file).startsWith(path.resolve(workspaceRoot, ROLES_DIR) + path.sep)) {
105
+ return fail('角色卡路径异常,拒绝写入。');
106
+ }
107
+ // 一个家只住一个角色(2026-08-28 对账发现):文件夹按展示名取,两个不同的
108
+ // slug 用同一个展示名就会共用 角色/<名>/ —— 卡被后来者覆盖,**记忆.md 也共用**,
109
+ // 于是 A 的 jot_memory 写进 B 的记忆里,谁都不会发现。堵在写口,不是在
110
+ // roleHomeDir 那头兜(判据别建在模型可写的登记表上)。
111
+ // 同一个 slug 重登(改卡)不受影响 —— 那本来就是同一个人。
112
+ const claimedBy = Object.entries((await readCastRegistry(workspaceRoot)).roles || {})
113
+ .find(([s2, e2]) => s2 !== slug && typeof e2?.card === 'string'
114
+ && e2.card.split('/').slice(0, 2).join('/') === `${ROLES_DIR}/${folder}`);
115
+ if (claimedBy) {
116
+ return fail(`「${displayName}」这个家已经是 ${claimedBy[0]} 的了(角色/${folder}/)。`
117
+ + `同名两个角色会共用角色卡和记忆件 —— 换个展示名,`
118
+ + `或者你要的其实是同一个人的话,用 ${claimedBy[0]} 这个名字重登(改卡就是重登同一个 slug)。`);
119
+ }
120
+ let existed = false;
121
+ try { await fs.access(file); existed = true; } catch { /* 新角色 */ }
122
+ await fs.mkdir(dir, { recursive: true });
123
+ // 记忆件骨架(只在不存在时铺):角色 jot_memory 追加,用户/GM 可整理
124
+ const memFile = path.join(dir, '记忆.md');
125
+ try { await fs.access(memFile); } catch {
126
+ await fs.writeFile(memFile, `# 记忆\n\n<!-- ${slug} 的记忆:角色自己 jot_memory 追加,用户和 GM 可整理改写。还是空的。 -->\n`, 'utf8');
127
+ }
128
+ await fs.writeFile(file, [
129
+ `# ${displayName}`,
130
+ '',
131
+ `<!-- ${slug} · cast_role 登记。`,
132
+ ' 正文就是角色的人设,派发时全文随 prompt 进入角色 —— 想改人设直接改这里,',
133
+ ' 对已在场的角色用 SendMessage 告知,对下次开演自动生效。 -->',
134
+ '',
135
+ persona,
136
+ '',
137
+ ].join('\n'), 'utf8');
138
+
139
+ // 登记表:板书署名与名册 API 的展示名来源(fail-soft:登记坏了不拦上场)
140
+ const cardRel = path.join(ROLES_DIR, folder, '角色卡.md');
141
+ try {
142
+ const reg = await readCastRegistry(workspaceRoot);
143
+ reg.roles[slug] = { name: displayName, duty: oneLine(args.duty, 400), card: cardRel };
144
+ await fs.mkdir(path.join(workspaceRoot, '.nd'), { recursive: true });
145
+ await fs.writeFile(path.join(workspaceRoot, REGISTRY_REL), JSON.stringify(reg, null, 2), 'utf8');
146
+ } catch { /* 展示层,坏了角色照样能上场(署名退回 slug) */ }
147
+
148
+ try { ctx?.emit?.({ type: 'run.role_cast', slug, name: displayName }); } catch { /* 事件失败不挡正事 */ }
149
+
150
+ // ⚠️ 这段话和工具 description 是**同一条教义的两个读者**,改一头必须改另一头 ——
151
+ // 08-30 停用子代理那一刀只改了 description,这里还留着「现在就可以派它上场」,
152
+ // 而返回文案离模型的下一个动作更近,等于白改(fable 评审当场抓到)。
153
+ const lines = [
154
+ `${existed ? '改写' : '写好'}了角色卡「${displayName}」→ ${cardRel}(登记为 ${slug})`,
155
+ '',
156
+ `⛔ **不要派子代理**:这一版角色由你自己演,`
157
+ + `Agent(subagent_type: "${ROLE_SLOT}") 和 SendMessage 给角色这两条路正在调试。`,
158
+ `轮到「${displayName}」说话时,**先 Read 一遍 ${cardRel}** 把腔调找回来,再由你写他这一段。`,
159
+ `几个人同场就一个个来,每人开口前各读各的卡 —— 这是防止全场一个腔的正事。`,
160
+ ];
161
+ if (existed) {
162
+ lines.push('', '⚠️ 卡改了,下次写他的话之前重新 Read 一遍 —— 你上下文里那份是旧的。');
163
+ }
164
+ return { content: [{ type: 'text', text: lines.join('\n') }] };
165
+ },
166
+ );
167
+ }
@@ -9,7 +9,7 @@
9
9
  * (~/.nodesign/plugins/<userId>/<name>/),下次开新会话在**这个用户的所有项目**里
10
10
  * 都能用(plugin 发现是 startup-time,当前会话不生效)。
11
11
  *
12
- * record_decision 的分工:决策贴是**这一件作品**的过程档案,随任务走;
12
+ * 跟长期记忆(记忆/ + 根 CLAUDE.md)的分工:记忆记的是**这个项目/这位用户**的持续状态,随项目走;
13
13
  * 这里是跨项目复用的方法论,随用户走。一个是日志,一个是沉淀。
14
14
  *
15
15
  * 归属:写到**项目 owner** 的目录,不是"当前请求者"——同一个项目谁跑都该产出同一
@@ -0,0 +1,104 @@
1
+ /**
2
+ * mcp/tools/draw-trend.js —— `draw_trend`:状态表历史 → 手绘趋势线
3
+ * (2026-08-30 画图能力线·「活图」第一块)
4
+ *
5
+ * 数据不用记:状态表住在板书文件里、文件进 git、**每回合落一个 commit** ——
6
+ * 「好感度随拍数怎么走」的序列是现成的,git 就是时间轴(lib/state-trend.js)。
7
+ *
8
+ * 重画语义:同 key 再调一次 = 原地重画(旧的擦掉、**位置保留** —— 用户拖过它
9
+ * 摆在哪就还在哪)。所以每拍 set_vars 之后想让曲线长一格,再调一次就行。
10
+ */
11
+
12
+ import { tool } from '@anthropic-ai/claude-agent-sdk';
13
+ import { z } from 'zod';
14
+ import { readBoard, patchBoard, removeByTag } from '../../../projects/board-store.js';
15
+ import { trendSeries, trendGeometry } from '../../../lib/state-trend.js';
16
+ import { roughFreePath, textBox } from '../../../lib/sketch-layout.js';
17
+ import { KEY_MAX } from '../../../lib/state-table.js';
18
+ import { currentSheet, toWorld } from '../../../lib/board-sheets.js';
19
+ import { currentSheetIdOf } from '../../../lib/sheet-state.js';
20
+ import { estimateSizeOn } from '../../../lib/board-kind-sizes.js';
21
+ import { Events } from '../../agent/events.js';
22
+
23
+ let seq = 0;
24
+ const stamp = () => `${Date.now().toString(36)}${(seq++ % 1000).toString(36)}`;
25
+
26
+ export function makeDrawTrendTool({ projectId, sharedRoot, sessionId, ctx }) {
27
+ return tool(
28
+ 'draw_trend',
29
+ `Draw a hand-inked trend line of one state-table key over the story so far — 好感度
30
+ climbing across eight beats, HP bleeding down. The series comes from the workspace's
31
+ git history (one commit per turn), so there is nothing to record: if the number lived
32
+ in the state table, its past is already there. Call it again after set_vars and the
33
+ chart redraws IN PLACE (position kept, even if the user dragged it). Needs ≥2 numeric
34
+ points — early beats will refuse loudly, that is normal.`,
35
+ {
36
+ key: z.string().min(1).max(KEY_MAX).describe('Which state-table key to chart (must hold numbers — 8/10 counts as 8)'),
37
+ at: z.object({ x: z.number().min(0).max(12000), y: z.number().min(0).max(12000) }).optional()
38
+ .describe('Sheet-local pixels for first placement. Omit it: lands under the state-table card (or where the previous chart of this key sits)'),
39
+ },
40
+ async (args) => {
41
+ const err = (t) => ({ content: [{ type: 'text', text: t }], isError: true });
42
+ if (!projectId) return err('No project bound.');
43
+ const s = await trendSeries(sharedRoot, args.key);
44
+ if (!s.ok) return err(`⛔ ${s.why}`);
45
+
46
+ const g = trendGeometry(s.points);
47
+ const tag = `trend-${args.key}`.slice(0, 40);
48
+ const board = await readBoard(projectId);
49
+
50
+ // 落点:旧图原位 > at(纸内像素) > 状态表卡正下方
51
+ let origin = null;
52
+ const prev = Object.entries(board.objects || {}).filter(([, e]) => e?.tag === tag && Number.isFinite(e?.x));
53
+ if (prev.length) {
54
+ origin = { x: Math.min(...prev.map(([, e]) => e.x)), y: Math.min(...prev.map(([, e]) => e.y)) };
55
+ await removeByTag(projectId, tag);
56
+ } else if (args.at) {
57
+ const sheet = currentSheet(board, currentSheetIdOf(sessionId));
58
+ if (!sheet) return err('还没有纸 —— 先 open_sheet,或者省掉 at 让它落在状态表卡下面。');
59
+ origin = toWorld(sheet, args.at);
60
+ } else {
61
+ const e = board.objects?.[s.rel];
62
+ if (!e || !Number.isFinite(e.x)) {
63
+ return err(`状态表那条板书(${s.rel})不在板上(没有座位)—— 传 at:{x,y}(纸内像素)指个位置。`);
64
+ }
65
+ const sz = estimateSizeOn(board, s.rel, e);
66
+ origin = { x: e.x, y: e.y + sz.h + 16 };
67
+ }
68
+
69
+ // 标签在图上方一行;曲线 ink、基线 pencil、现值小圈 red —— 全过同一支抖动笔
70
+ const label = `${args.key} ${s.points[0]} → ${s.points[s.points.length - 1]}(${s.points.length} 拍,${g.min}~${g.max})`;
71
+ const lb = textBox(label, 'sm');
72
+ const seed = `trend:${args.key}`;
73
+ const objects = {
74
+ [`text:a${stamp()}`]: {
75
+ x: Math.round(origin.x), y: Math.round(origin.y), w: lb.w, h: lb.h, z: 1, kind: 'text',
76
+ data: { t: label, font: 'kai', size: 'sm' }, zone: '', by: 'agent', seat: 'agent', tag,
77
+ },
78
+ [`scribble:a${stamp()}`]: {
79
+ x: Math.round(origin.x), y: Math.round(origin.y + lb.h + 4), w: g.w, h: g.h, z: 1, kind: 'scribble',
80
+ data: { d: roughFreePath(g.baselineD, `${seed}:base`), color: 'pencil', width: 1 }, zone: '', by: 'agent', seat: 'agent', tag,
81
+ },
82
+ [`scribble:b${stamp()}`]: {
83
+ x: Math.round(origin.x), y: Math.round(origin.y + lb.h + 4), w: g.w, h: g.h, z: 1, kind: 'scribble',
84
+ data: { d: roughFreePath(g.lineD, `${seed}:line`), color: 'ink', width: 2 }, zone: '', by: 'agent', seat: 'agent', tag,
85
+ },
86
+ [`scribble:c${stamp()}`]: {
87
+ x: Math.round(origin.x), y: Math.round(origin.y + lb.h + 4), w: g.w, h: g.h, z: 1, kind: 'scribble',
88
+ data: { d: g.dotD, color: 'red', width: 2 }, zone: '', by: 'agent', seat: 'agent', tag,
89
+ },
90
+ };
91
+ await patchBoard(projectId, { objects });
92
+ const rect = { x: Math.round(origin.x), y: Math.round(origin.y), w: g.w, h: g.h + lb.h + 4 };
93
+ try {
94
+ ctx?.emit?.({ type: 'board.updated', sessionId: null, summary: `画了 ${args.key} 的趋势线` });
95
+ ctx?.emit?.(Events.boardFocus(rect, { tag, layer: '', soft: true }));
96
+ } catch { /* fail-soft */ }
97
+ return { content: [{ type: 'text', text:
98
+ `Trend #${tag}: ${s.points.length} points (${s.points.join(' → ')}), range ${g.min}~${g.max}, `
99
+ + `at (${rect.x},${rect.y}) ${rect.w}x${rect.h}${prev.length ? ' — redrawn in place' : ''}.\n`
100
+ + `Call draw_trend again after set_vars to grow it; move/group it like any sketch (tag #${tag} — `
101
+ + `edit_board follow can pin it to the 状态板 group).` }] };
102
+ },
103
+ );
104
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * edit_board 的 ops schema(2026-08-30 从 edit-board.js 拆出 —— 行数棘轮)。
3
+ * 纯数据;弱模型方言垫片($text 剥壳)与坐标钳制都住在这里。
4
+ */
5
+
6
+ import { z } from 'zod';
7
+ import { BINDING_TYPE_IDS, BINDING_MATERIALS } from '../../../lib/binding-types.js';
8
+ import { SLOT } from './open-sheet.js';
9
+
10
+ const clampTo = (lo, hi) => (v) => (typeof v === 'number' && Number.isFinite(v) ? Math.max(lo, Math.min(hi, v)) : v);
11
+ const GAP = z.preprocess(clampTo(0, 400), z.number().min(0).max(400));
12
+ const PX_DELTA = z.preprocess(clampTo(-20000, 20000), z.number().min(-20000).max(20000));
13
+ /** 弱模型方言垫片:免费档模型给字符串字段裹 {$text:"…"} 壳(27/61 条真实错误,
14
+ * 且读不懂 zod 报文会原样重试到死)。单键 $text 自动剥壳,合法对象碰不到。 */
15
+ const unwrapText = (v) => (v && typeof v === 'object' && !Array.isArray(v)
16
+ && typeof v.$text === 'string' && Object.keys(v).length === 1) ? v.$text : v;
17
+ const ENDPOINT = z.preprocess(unwrapText, z.string().min(1).max(300));
18
+
19
+ const REL = z.object({
20
+ ref: z.string().min(1).max(300).describe('canvas id to place relative to'),
21
+ side: z.enum(['right', 'left', 'above', 'below']),
22
+ gap: GAP.optional().describe('PIXELS of breathing room between the two (default 24, max 400) — same unit as every position you read back'),
23
+ });
24
+ const DELTA = z.object({ dx: PX_DELTA, dy: PX_DELTA }).describe('PIXELS to shift by (+x right, +y down) — the same pixels read_board reports');
25
+ // 纸内绝对坐标(2026-08-29 纸范式):以某张纸版心左上为原点的像素。sheet 缺省 = 当前纸
26
+ const ABS = z.object({
27
+ x: z.preprocess((v) => (typeof v === 'number' && Number.isFinite(v) ? Math.max(0, Math.min(12000, v)) : v), z.number().min(0).max(12000)),
28
+ y: z.preprocess((v) => (typeof v === 'number' && Number.isFinite(v) ? Math.max(0, Math.min(12000, v)) : v), z.number().min(0).max(12000)),
29
+ sheet: z.string().max(40).optional().describe('which sheet these pixels are on (default the current one)'),
30
+ }).describe("PIXELS from a sheet's top-left writable corner — exact placement, you own the layout");
31
+ const TO = z.union([REL, ABS, DELTA]);
32
+
33
+ export const OP = z.discriminatedUnion('op', [
34
+ z.object({ op: z.literal('set_text'), id: z.string().min(1).max(300), text: z.string().min(1).max(8000).optional(), format: z.enum(['plain', 'md']).optional(), size: z.enum(['sm', 'md', 'lg', 'xl']).optional(), color: z.enum(['ink', 'red', 'pencil', 'brass']).optional(), font: z.enum(['pen', 'kai', 'sans', 'serif', 'mono']).optional() }),
35
+ z.object({ op: z.literal('move'), id: z.string().min(1).max(300), to: TO }),
36
+ z.object({ op: z.literal('move_group'), tag: z.string().min(1).max(40), to: TO }),
37
+ z.object({ op: z.literal('remove'), id: z.string().min(1).max(300) }),
38
+ z.object({ op: z.literal('add_node'), id: z.string().regex(/^[A-Za-z0-9_-]{1,24}$/).optional().describe('local handle for later ops of this call'), text: z.string().min(1).max(8000), format: z.enum(['plain', 'md']).optional(), size: z.enum(['sm', 'md', 'lg', 'xl']).optional(), font: z.enum(['pen', 'kai', 'sans', 'serif', 'mono']).optional(), color: z.enum(['ink', 'red', 'pencil', 'brass']).optional(), at: REL, tag: z.string().max(40).optional() }),
39
+ z.object({ op: z.literal('add_shape'), kind: z.enum(['rect', 'ellipse', 'circle', 'underline']), around: z.string().min(1).max(300).describe('canvas id to wrap/underline — the mark HUGS it and follows when it moves'), color: z.enum(['ink', 'red', 'pencil', 'brass']).optional(), width: z.number().min(1).max(12).optional(), tag: z.string().max(40).optional() }),
40
+ z.object({ op: z.literal('set_shape'), id: z.string().min(1).max(300), color: z.enum(['ink', 'red', 'pencil', 'brass']).optional(), width: z.number().min(1).max(12).optional() }),
41
+ z.object({ op: z.literal('add_edge'), from: ENDPOINT, to: ENDPOINT, type: z.enum(BINDING_TYPE_IDS).optional(), material: z.enum(BINDING_MATERIALS).optional(), label: z.string().max(60).optional(), tag: z.string().max(40).optional() }),
42
+ z.object({ op: z.literal('set_edge'), id: z.string().min(1).max(300), label: z.string().max(60).optional(), type: z.enum(BINDING_TYPE_IDS).optional(), material: z.enum(BINDING_MATERIALS).optional(), from: ENDPOINT.optional().describe('re-point the line: new source end'), to: ENDPOINT.optional().describe('re-point the line: new target end') }),
43
+ z.object({ op: z.literal('remove_edge'), id: z.string().min(1).max(300) }),
44
+ z.object({ op: z.literal('reflow'), tag: z.string().min(1).max(40), layout: z.enum(['column', 'row']).optional().describe('default column: restack the group in reading order with real sizes (use after set_text changes heights)') }),
45
+ z.object({ op: z.literal('follow'), group_tag: z.string().min(1).max(40).describe('the group that should follow (e.g. a status panel)'), target_tag: z.string().min(1).max(40).describe('whenever a new item with this tag lands, the group auto-moves beside it and the anchor line re-points'), side: z.enum(['right', 'left', 'above', 'below']).optional(), label: z.string().max(60).optional() }),
46
+ z.object({ op: z.literal('unfollow'), group_tag: z.string().min(1).max(40) }),
47
+ z.object({ op: z.literal('commit'), tag: z.string().max(40).optional().describe('make staging solid; omit tag = everything staging') }),
48
+ z.object({ op: z.literal('erase_group'), tag: z.string().min(1).max(40).describe('delete the whole tagged group (notes/shapes/lines; artifact cards only lose the tag)') }),
49
+ z.object({ op: z.literal('roll'), tag: z.string().min(1).max(40).describe('STOW a finished group: hides it behind a compact scroll card, everything kept in place (seats reserved, files intact, one click to unroll). For a scene/act/chapter that is DONE — use erase_group only to destroy'), label: z.string().max(60).optional().describe('scroll card title (default: the tag)') }),
50
+ z.object({ op: z.literal('unroll'), tag: z.string().min(1).max(40).describe('expand a rolled group back — everything returns to its original seat') }),
51
+ z.object({ op: z.literal('feature'), id: z.string().min(1).max(300).describe('make this the hero of the desktop') }),
52
+ z.object({ op: z.literal('unfeature') }),
53
+ z.object({ op: z.literal('transform_group'), tag: z.string().min(1).max(40), scale: z.number().min(0.3).max(3).optional(), rotate: z.number().min(-180).max(180).optional().describe('degrees clockwise') }),
54
+ z.object({ op: z.literal('replan'), sheet: z.string().max(40).optional().describe('which sheet (default: the current one)'), plan: z.array(SLOT).min(1).max(24).describe('slots to ADD or RESIZE on an existing sheet (merged by name; slots you do not mention stay). Same local-pixel coordinates as open_sheet{plan}') }),
55
+ z.object({ op: z.literal('chalk_edit'), on: z.boolean().describe('true = turn ON the user-side 改板书 toggle (notes become freely draggable/editable for the user); false = back to guarded mode') }),
56
+ ]);