@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
@@ -46,6 +46,7 @@
46
46
  import {
47
47
  makePreToolUseAgentForceForegroundHandler,
48
48
  makePreToolUseGrepContentDefaultHandler,
49
+ makePreToolUseActorStamp,
49
50
  } from './hooks/pre-defaults.js';
50
51
  import {
51
52
  makePreToolUseGetPendingChangesProtocolInjector,
@@ -63,6 +64,13 @@ import {
63
64
  makePreToolUseBashStarterFilesFallback,
64
65
  } from './hooks/pre-starter-files.js';
65
66
  import { makePreToolUseBoardNeighborhoodInjector } from './hooks/pre-board-neighborhood.js';
67
+ import { makePreToolUseBoardDirtyInjector } from './hooks/pre-board-dirty.js';
68
+ import { makePreToolUseUnknownParamsProbe } from './hooks/pre-unknown-params.js';
69
+ import { makePreToolUseSendMessageRecipientGuard } from './hooks/pre-peer-guard.js';
70
+ import { createRoleRoster } from './cast.js';
71
+ import { makePostToolUseFailureRoleRelease, makeSubagentStopRoleNotice, makeSubagentStartRoleAlias } from './hooks/resident-role-lifecycle.js';
72
+ import { makePostToolUseBeatWrite, makePostToolUseBeatHandoff, makeStopBeatGate } from './hooks/beat-gate.js';
73
+ import { makePostToolUseSlotAliasHandler } from './hooks/slot-alias.js';
66
74
  import { makePreToolUsePerformanceLogGuard } from './hooks/pre-performance-log-guard.js';
67
75
  import { makePreToolUseWorkspaceScopeGuard } from './hooks/pre-workspace-scope-guard.js';
68
76
  import { PROJECTS_DATA_ROOT } from '../../projects/workspace.js';
@@ -84,7 +92,6 @@ import {
84
92
  makePostToolUseScreenshotHandler,
85
93
  makePostToolUseExportHandler,
86
94
  makePostToolUseGenerateImageRegenWatchdog,
87
- makePostToolUseStyleAnchorNudge,
88
95
  } from './hooks/post-guidance.js';
89
96
  import { makePostToolUseCanvasValidationHandler } from './hooks/canvas-validate.js';
90
97
  import { makePostToolUseSiteValidationHandler } from './hooks/site-validate.js';
@@ -100,7 +107,14 @@ import { makePostToolUseSubagentReportRecovery } from './hooks/post-subagent-rep
100
107
  * @param {string} [deps.projectId]
101
108
  * @returns {Partial<Record<string, Array<{ matcher?: string, hooks: Function[], timeout?: number }>>>}
102
109
  */
103
- export function createHooks({ ctx, workspaceRoot, sharedRoot, sessionId, projectId } = {}) {
110
+ export function createHooks({ ctx, workspaceRoot, sharedRoot, sessionId, projectId, roleRoster: injected = null } = {}) {
111
+ // 常驻角色名册:**一个会话一份**(闭包级,不是全局表)。派发时登记、收件人闸按它放行。
112
+ // 两个 handler 必须拿同一个引用 —— 各建各的等于闸永远看到空名册,症状是所有角色
113
+ // 都寄不出信(fail-closed,至少不静默漏)。见 cast.js createRoleRoster 的头注释。
114
+ // 名册由 session-loop 建好传进来(cast_role 也要用同一份 —— 它记"这一回合造的角色",
115
+ // 派发闸据此拦掉本回合内注定失败的派发)。没传就自建,单测/脚本直接调时仍成立。
116
+ const roleRoster = injected || createRoleRoster();
117
+
104
118
  return assertHooksWellFormed({
105
119
  // ── P0+ stage 1(不动)──
106
120
 
@@ -139,10 +153,31 @@ export function createHooks({ ctx, workspaceRoot, sharedRoot, sessionId, project
139
153
  }, {
140
154
  matcher: 'Task|Agent',
141
155
  hooks: [
142
- makePreToolUseAgentForceForegroundHandler(),
156
+ makePreToolUseAgentForceForegroundHandler({ roster: roleRoster, workspaceRoot, ctx }),
143
157
  // vision-checker 派遣 prompt 模板首次注入(仅当 subagent_type='vision-checker')
144
158
  makePreToolUseTaskVisionCheckerDispatchInjector(),
145
159
  ],
160
+ }, {
161
+ // SendMessage 收件人闸:只放行 main / 本项目常驻角色 / 本会话 agentId。
162
+ // 这个工具的另一半寻址空间是**同机其他 Claude 会话**(探针实测 ListAgents
163
+ // 列得出来),跨会话寄信 = 一个用户的 agent 给另一个人的 agent 递指令。
164
+ // SDK 的 crossSessionInbound 只管收不管发,发这侧得自己拦。见 pre-peer-guard.js
165
+ matcher: 'SendMessage',
166
+ hooks: [makePreToolUseSendMessageRecipientGuard({ roster: roleRoster })],
167
+ }, {
168
+ // 板上写入的署名:工具执行前把「这次是谁调的」记下来(MCP handler 自己拿不到
169
+ // agent 身份,hook 拿得到)。常驻角色写的板书要署它的名,不能全算主 agent 头上。
170
+ // 见 agent/actor-trail.js —— 权威是 harness 盖的章,不是角色文件里的自称。
171
+ //
172
+ // ⛔ 这里曾是手写工具名单,漏过两次,两次症状都是「闸装着、判据永远落 'agent'」:
173
+ // - 08-26 漏 await_user/check_inbox/read_board → 整条收件箱回路在生产里是死的
174
+ // - 08-28 审出漏场务四件 → 对真角色恒拒(它们永远被当成主控)
175
+ // (两族工具 08-29 随编排收敛一起退役,教训留着:通配挂法就是从这两次来的)
176
+ // 手写名单和「写死表家族」同病:加新工具必漏。改成全量盖章 —— 盖章本身只是往
177
+ // 500 容量的 LRU Map 记一条,多盖无副作用,凡是可能用 byOf 的工具永远在闸内。
178
+ // 装配层有测试钉着(hooks-assembly.test.js):别再收窄回名单。
179
+ matcher: 'mcp__nodesign__.*',
180
+ hooks: [makePreToolUseActorStamp()],
146
181
  }, {
147
182
  // ⚠️ Grep 的输入改写只许有这一个 handler——两个各返回 updatedInput 会互相抹掉
148
183
  matcher: 'Grep',
@@ -191,6 +226,16 @@ export function createHooks({ ctx, workspaceRoot, sharedRoot, sessionId, project
191
226
  // UserPromptSubmit 的全图摘要截断后,这里做精确补充。每个文件一个会话只注一次。
192
227
  matcher: 'Read|Edit|Write',
193
228
  hooks: [makePreToolUseBoardNeighborhoodInjector({ workspaceRoot, projectId })],
229
+ }, {
230
+ // 板上动静(2026-08-29 纸范式刀 4):用户回合中途拖动/搬家/擦组,agent 下一次
231
+ // 摸板前插话(按会话恰好一次,台账在 lib/board-dirty.js)。
232
+ matcher: 'mcp__nodesign__(write_on_board|edit_board|read_board|board_batch|open_sheet|organize_board|look_at_board)',
233
+ hooks: [makePreToolUseBoardDirtyInjector({ projectId, sessionId })],
234
+ }, {
235
+ // 未知参数探针(2026-08-30):zod 把 schema 里没有的键静默 strip 掉,工具自己
236
+ // 看不见 —— 只有钩子拿得到原始 tool_input。挂全部 nodesign 工具。
237
+ matcher: 'mcp__nodesign__.*',
238
+ hooks: [makePreToolUseUnknownParamsProbe()],
194
239
  }, {
195
240
  matcher: 'Write',
196
241
  hooks: [
@@ -205,9 +250,11 @@ export function createHooks({ ctx, workspaceRoot, sharedRoot, sessionId, project
205
250
  ],
206
251
  }],
207
252
 
208
- // Stop —— agent 准备结束 query 时触发,发自检事件给前端
253
+ // Stop —— agent 准备结束 query 时触发,发自检事件给前端。
254
+ // 第二个是演出模式的收尾闸:这一拍既没给按钮也没交给角色就不许收工
255
+ // (08-28 真会话的头号病:写完两千字正文玩家没有任何把手)。
209
256
  Stop: [{
210
- hooks: [makeStopReflectionHandler({ ctx, workspaceRoot })],
257
+ hooks: [makeStopReflectionHandler({ ctx, workspaceRoot }), makeStopBeatGate({ sessionId, projectId })],
211
258
  }],
212
259
 
213
260
  // PostCompact —— compact 后把摘要写入 spec.json 长期记忆
@@ -232,6 +279,18 @@ export function createHooks({ ctx, workspaceRoot, sharedRoot, sessionId, project
232
279
  // PostToolUse —— 按 MCP 工具名分别注 additionalContext,引导 agent 利用
233
280
  // 工具结果。matcher 字段是 SDK 标准(与 PreToolUse 'Bash' 同语义)。
234
281
  PostToolUse: [
282
+ // 演员位实例学名(2026-08-28 重构):hook input 没有实例名字段,名字只在
283
+ // 派发/唤醒的 tool_result 里露面 —— 从那里学 agentId→实例名(slot-alias.js)。
284
+ // 收件箱、板书署名、退场标记全靠这张表把 rp-actor 解析回具体角色。
285
+ {
286
+ matcher: 'Task|Agent|SendMessage',
287
+ hooks: [makePostToolUseSlotAliasHandler(), makePostToolUseBeatHandoff({ sessionId, projectId })],
288
+ },
289
+ // 收尾闸的另一半:主持人往画布上写了什么、带没带按钮(见 hooks/beat-gate.js)
290
+ {
291
+ matcher: 'mcp__nodesign__write_on_board|mcp__nodesign__board_batch',
292
+ hooks: [makePostToolUseBeatWrite({ sessionId, projectId })],
293
+ },
235
294
  // Edit/Write 后干掉 tool_response.originalFile:FileEditOutput/FileWriteOutput
236
295
  // 默认含完整原文件(sdk-tools.d.ts:2270, 2328),这是上下文累积大头。
237
296
  // canvas.html 25KB 一次 Edit ≈ 6k tokens,30 turn 累积可达 180k+ → 触发 256k 上限。
@@ -294,28 +353,35 @@ export function createHooks({ ctx, workspaceRoot, sharedRoot, sessionId, project
294
353
  makePostToolUseSiteValidationHandler({ workspaceRoot }),
295
354
  ],
296
355
  },
297
- // record_decision 一般**不注** additionalContext("继续主任务"那种跟 SDK
298
- // preset 重复,agent 自己懂)。唯一例外是"锚定风格"这一笔:那是项目级
299
- // 长期资产该落盘的时刻,而品牌档案 / 项目指引这两处 SDK 不知道。
300
- {
301
- matcher: 'mcp__nodesign__record_decision',
302
- hooks: [makePostToolUseStyleAnchorNudge({ sharedRoot })],
303
- },
356
+ // record_decision 及其 style-anchor nudge 2026-08-24 整条目拆除 ——
357
+ // 风格落盘的提醒并进 auto-memory 的追加指导(session-loop
358
+ // CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES)。删的是**整个 {matcher, hooks}
359
+ // 条目**,空壳条目会让 SDK initialize 静默灭门,见 assertHooksWellFormed。)
304
360
  ],
305
361
 
306
362
  // PostToolUseFailure —— 任意工具失败时统一处理:emit 事件 + 给 agent
307
363
  // 注入恢复建议(避免重试同样的错)。
308
364
  PostToolUseFailure: [{
309
365
  hooks: [makePostToolUseFailureHandler({ ctx, projectId, sessionId })],
366
+ }, {
367
+ // 常驻角色派发失败 → 把名字从名册里撤回。claim 在 PreToolUse(派发**之前**),
368
+ // 不撤的话这个名字整个会话被 brick,而且收件人闸会对一个并不存在的 agent 放行
369
+ // (裸名回落全机解析 = H1 那个洞在窄窗口里重开)。见 resident-role-lifecycle.js
370
+ matcher: 'Task|Agent',
371
+ hooks: [makePostToolUseFailureRoleRelease({ roster: roleRoster })],
310
372
  }],
311
373
 
312
374
  // SubagentStart / SubagentStop —— 主动捕子代理生命周期。当前只 emit 事件
313
375
  // 给上层观察;不阻塞流程。stage 2 真接通子代理时这里加调度逻辑。
314
376
  SubagentStart: [{
315
- hooks: [makeSubagentStartHandler({ ctx })],
377
+ // 第二个 handler 是署名的正门:agent_id ↔ 角色名在起飞这一刻绑定
378
+ // (见 resident-role-lifecycle.js;旧的"从 tool_result 抠 id"已退役)
379
+ hooks: [makeSubagentStartHandler({ ctx, sessionId }), makeSubagentStartRoleAlias({ projectId })],
316
380
  }],
317
381
  SubagentStop: [{
318
- hooks: [makeSubagentStopHandler({ ctx })],
382
+ // 常驻角色退场时补一句「怎么把它叫回来」给主控。放在通用 emit 之后:
383
+ // 那个只 emit 事件不返输出,两者不抢 systemMessage。见 resident-role-lifecycle.js
384
+ hooks: [makeSubagentStopHandler({ ctx, sessionId }), makeSubagentStopRoleNotice({ projectId })],
319
385
  }],
320
386
  });
321
387
  }
@@ -25,6 +25,7 @@ import os from 'node:os';
25
25
  import fs from 'node:fs/promises';
26
26
  import { platform } from '../../runtime/platform.js';
27
27
  import { autoModeSettings } from './auto-mode-rules.js';
28
+ import { MCP_ALLOW_RULE } from '../mcp/server-name.js';
28
29
 
29
30
  /**
30
31
  * 会话要用的缓存/临时目录一次备齐(2026-08-19 从 session-loop 下沉)。
@@ -129,7 +130,15 @@ export function buildIsolationOptions({ cwdRoot, sharedRoot, npmCacheDir, agentT
129
130
  // 没有它 Bash 的 tmp 只读:venv 静默失败、pip --user EROFS、npm 装不上
130
131
  ...(agentTmpDir ? [agentTmpDir] : []),
131
132
  ],
132
- denyWrite: ['/etc', '/usr', '/bin', '/sbin', '/private/etc'],
133
+ // 角色文件不许模型写(2026-08-26):它是派发闸的判据本身,模型能改就等于
134
+ // 自己给自己发工具权限(TOCTOU + 解析器分歧两条绕法,详见
135
+ // hooks/pre-workspace-scope-guard.js 那段注释)。
136
+ // 这半只管 Bash —— Write/Edit 是进程内工具**不进 bwrap**,那半在上面那道闸。
137
+ // 正门 cast_role 走服务端 fs,不经沙盒,照写不误。
138
+ denyWrite: [
139
+ '/etc', '/usr', '/bin', '/sbin', '/private/etc',
140
+ ...(cwdRoot ? [path.join(cwdRoot, '.claude', 'agents')] : []),
141
+ ],
133
142
  // 数据根整个盖住、再用 allowRead 给自己的工作区开天窗。
134
143
  // ⚠️ 口径要准(08-18 上生产时实测纠正):`ls 数据根` **看得见别的项目的
135
144
  // 目录名**(沙盒里实测列出了另一个 pid),拦住的是**进去读**——
@@ -149,7 +158,21 @@ export function buildIsolationOptions({ cwdRoot, sharedRoot, npmCacheDir, agentT
149
158
  },
150
159
  },
151
160
  settings: {
152
- permissions: { deny: platform.protectedPathRules({ dataRoot }) },
161
+ permissions: {
162
+ deny: platform.protectedPathRules({ dataRoot }),
163
+ // 本平台自己的 MCP 工具整服务放行,不过 auto 模式分类器(2026-08-25 用户拍板)。
164
+ //
165
+ // 为什么:这些工具是我们自己写的,每一件的边界在服务端**已经**有闸
166
+ // —— 出网走 ssrf-guard,publish_site / 本地产线走 owner + 档位闸,
167
+ // 生图走额度闸,路径走 permissions.deny + 沙盒。让一个模型分类器
168
+ // 再判一遍 `read_board` 安不安全,既判不出新东西,还多一次外部依赖。
169
+ // 真实账:真用户会话里分类器**从没拦下过一次真越界**(唯一那条是
170
+ // Stage 2 classifier error 误伤 deliver_files 交稿),却因为自身不可用
171
+ // 挡掉过 8 个会话的正经活(08-24 一天 15 次)。闸门的净效用是负的。
172
+ // Bash **不在**名单里:它跑的是任意命令,语义判断正是分类器的本职。
173
+ // 内置的 Read/Write/Edit/WebFetch 等也照旧不动。
174
+ allow: [MCP_ALLOW_RULE],
175
+ },
153
176
  // auto 模式的分类器规则(只在开了 auto 时注入,省得白占 settings)。
154
177
  // ⚠️ 按节替换不是追加 —— 为什么只覆盖 environment / hard_deny 两节,
155
178
  // 见 auto-mode-rules.js 文件头。
@@ -0,0 +1,87 @@
1
+ /**
2
+ * agent/memory-config.js — auto-memory 的产品化配置(2026-08-24 记忆体系改版)
3
+ *
4
+ * 记忆两层的分工(用户拍板):
5
+ * - `记忆/`(工作区根下,画布可见):SDK auto-memory 的家。提示词、写入、
6
+ * MEMORY.md 索引加载、权限放行全跟 autoMemoryDirectory 一个值走(二进制 yh(),
7
+ * 08-24 探明)。用户看得到每一条、也可能直接改。
8
+ * - 根 `CLAUDE.md`:人工筛选的项目档案(指引/风格/习惯),SDK 每会话
9
+ * 确定性全量注入(settingSources 'project' 原生行为,根目录与 .claude/ 两处都读)。
10
+ *
11
+ * ⚠️ EXTRA_GUIDELINES 是**追加**不是替换:SDK 自己的记忆合同(frontmatter 契约、
12
+ * 两步写入、200 行索引上限与截断提醒)原样保留。别换成 CLAUDE_COWORK_MEMORY_GUIDELINES
13
+ * 整段替换 —— 那会把截断提醒一起抹掉,而截断逻辑仍在跑,索引爆了没人说话。
14
+ */
15
+
16
+ import path from 'node:path';
17
+
18
+ /** 记忆目录名(工作区根相对)。画布可见是设计要求,不是巧合。 */
19
+ export const MEMORY_DIR_NAME = '记忆';
20
+
21
+ export function memoryDirFor(sharedRoot) {
22
+ return path.join(sharedRoot, MEMORY_DIR_NAME);
23
+ }
24
+
25
+ /** 追加进 SDK 记忆提示末尾的产品口径(env CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES) */
26
+ export const MEMORY_EXTRA_GUIDELINES = [
27
+ 'This memory directory (记忆/) is VISIBLE on the user\'s canvas — they can read',
28
+ 'every file and may edit them between sessions; treat edits as ground truth.',
29
+ 'Keep file names as short English kebab-case slugs (the SDK contract), but write',
30
+ 'descriptions and content in the user\'s language (中文 for this product).',
31
+ 'When a style decision is settled (palette / fonts / materials / art direction),',
32
+ 'record it as a `type: project` memory right away — style anchors are the most',
33
+ 'expensive thing to lose between sessions.',
34
+ 'Hard constraints and curated project guidance live in the workspace-root',
35
+ 'CLAUDE.md (deterministically injected every session) — put lasting rules there',
36
+ '(with the user\'s consent), and put evolving facts here.',
37
+ ].join(' ');
38
+
39
+ /**
40
+ * 把我们的 settings 跟 isolationOptions.settings **深合并**成 SDK 的最终 settings。
41
+ *
42
+ * ⛔ 存在的理由(08-24 案):08-15 起 buildIsolationOptions 的返回值里也有
43
+ * settings 键,query options 里对象展开写在后面,把独立写的 settings 整个覆盖 ——
44
+ * autoMemoryEnabled / autoMemoryDirectory / skipWebFetchPreflight 八天没送到
45
+ * SDK 手里(agent 照 SDK 默认路径写记忆被沙盒拒、~/.claude 下堆了 187 个空目录)。
46
+ * 「两处同名键静默互吞」没有任何报错,出口断言在这里兜。
47
+ */
48
+ export function mergeAgentSettings(isolationSettings, extra = {}) {
49
+ // ⚠️ 这个第二参是**显式白名单**(它存在的理由就是防 settings 互吞)。所以它有一个
50
+ // 结构性陷阱:往调用点里塞一个这里没解构的新键 = 那个键被静默丢弃,零症状。
51
+ // 2026-08-26 真踩:`crossSessionInbound: 'refuse'`(跨会话入向闸)在调用点写得好好的,
52
+ // 到了 SDK 手里根本不存在 —— 修互吞案的函数自己吞了新键。
53
+ // 所以未知键**当场炸**:白名单的代价必须由加键的人当场付,不能由线上静默付。
54
+ const { skipWebFetchPreflight, sharedRoot, crossSessionInbound, ...unknown } = extra;
55
+ const unknownKeys = Object.keys(unknown);
56
+ if (unknownKeys.length) {
57
+ throw new Error(
58
+ `[memory-config] mergeAgentSettings 收到不认识的键:${unknownKeys.join(', ')} —— `
59
+ + '这个第二参是显式白名单,加新键要同时改这里的解构和下面的出口断言,别指望它透传',
60
+ );
61
+ }
62
+
63
+ const settings = {
64
+ ...isolationSettings,
65
+ skipWebFetchPreflight,
66
+ // 跨会话入向(2026-08-26):实测——不是推断——Nodesign 的每个会话都会以 cwd
67
+ // 派生的名字注册进**本机 peer 名册**(生产上正在跑的用户会话当时显示为
68
+ // `shared-6d`),同机任何一个 Claude 会话 ListAgents 都看得见、都能按名字寄信。
69
+ // SDK 默认「权限模式相同就自动投递」(bypass↔bypass),而我们正是
70
+ // bypassPermissions → 本机任何 bypass 会话都能把文本注进用户会话的上下文。
71
+ // 出向那侧我们自己拦(hooks/pre-peer-guard.js),入向只有这一个旋钮。
72
+ // ⛔ 别写 'hold':那是「存起来等人批准」,而服务端没有人在批。
73
+ ...(crossSessionInbound ? { crossSessionInbound } : {}),
74
+ ...(sharedRoot ? {
75
+ autoMemoryEnabled: true,
76
+ autoMemoryDirectory: memoryDirFor(sharedRoot),
77
+ } : {}),
78
+ };
79
+ if (sharedRoot && !settings.autoMemoryDirectory) {
80
+ throw new Error('[memory-config] settings.autoMemoryDirectory 被吞了 —— 检查 settings 合并处');
81
+ }
82
+ // 出口断言按「传进来的每个键都要在出口活着」逐条对,不是只看一个样本键
83
+ if (crossSessionInbound && settings.crossSessionInbound !== crossSessionInbound) {
84
+ throw new Error('[memory-config] settings.crossSessionInbound 被吞了 —— 跨会话入向闸会静默失效');
85
+ }
86
+ return settings;
87
+ }
@@ -28,34 +28,43 @@
28
28
  * Opus 价虚高 30×)。repriceUsageDeltas 把 usage key 还原成 appModel、按行内
29
29
  * prices 重算 costUsd。行没填 prices = 沿用 SDK 虚价(接真流量前必须填价)。
30
30
  *
31
- * ⚠️ 硬约束:一个 sdkAlias 不能被两个 API 模型共用 —— 反查靠它,撞了整条
32
- * 路由和记账都错。模块加载断言兜底。
31
+ * ## sdkAlias 两种写法(08-25 起)
32
+ *
33
+ * - **不写(默认)**:派生时补上共用别名 SHARED_SDK_ALIAS,只按 id 进反查表,
34
+ * 靠会话级路由分辨(session-routes.resolveSessionWire)。加新行不用考虑别名。
35
+ * - **显式写 = 独占坑位**:alias 三种形态都进反查表,没会话也能按 alias 反查。
36
+ * ⚠️ 硬约束只对这种成立:一个独占 sdkAlias 不能被两个 API 模型共用 ——
37
+ * 反查靠它,撞了整条路由和记账都错。模块加载断言兜底。
33
38
  */
34
39
 
35
40
  import { can, localGenApproved, DENIAL } from '../../auth/tier.js';
36
41
  import { platform } from '../../runtime/platform.js';
37
- import { UPSTREAMS_BUILTIN, MODELS_BUILTIN, BRANDS } from './model-table.js';
42
+ import { UPSTREAMS_BUILTIN, MODELS_BUILTIN, BRANDS, SHARED_SDK_ALIAS } from './model-table.js';
38
43
  import { loadLocalConfig } from '../../runtime/local-config.js';
39
44
 
40
- export { BRANDS };
45
+ export { BRANDS, SHARED_SDK_ALIAS };
41
46
 
42
47
  // ── 内置表 + 用户插槽合并(08-22)──
43
- // 外部行(runtime/local-config.js)全部共用这一个 sdkAlias:表内唯一空着的 1M 订阅名。全表反查分不出它们,
44
- // 靠 ingress/session-routes.js 会话优先路由(一个会话只认自己那行)。所以外部行**不进 WIRE_LOOKUP 的 alias 键**,
45
- // 只按 id 可查;没注册会话的请求用这个 alias 发过来一律 502(探针要带会话前缀)。
46
- export const EXTERNAL_SDK_ALIAS = 'claude-sonnet-4-6[1m]';
47
48
  const external = loadLocalConfig();
48
49
  export const UPSTREAMS = Object.freeze({ ...UPSTREAMS_BUILTIN, ...external.upstreams });
49
- /** 配置条目 → 表行(字段名一一对应,见 local-config.js 文件头) */
50
+ /** 配置条目 → 表行(字段名一一对应,见 local-config.js 文件头;sdkAlias 不许手填 = 永远走下面的共用别名默认) */
50
51
  function toExternalRow(m) {
51
52
  const { id, label, desc, brand, window, uncensored, upstream, wireModel, fastModel, ...api } = m;
52
53
  return Object.freeze({
53
54
  id, window, brand, external: true, ...(uncensored ? { uncensored: true } : {}),
54
55
  select: Object.freeze({ label, desc }),
55
- api: Object.freeze({ upstream, wireModel, sdkAlias: EXTERNAL_SDK_ALIAS, fastModel: fastModel || id, ...api }),
56
+ api: Object.freeze({ upstream, wireModel, fastModel: fastModel || id, ...api }),
56
57
  });
57
58
  }
58
- const MODELS = Object.freeze([...MODELS_BUILTIN, ...external.models.map(toExternalRow)]);
59
+ // sdkAlias 可选(08-25 固化):API 行不写 = 补上共用别名 SHARED_SDK_ALIAS(内置行、外部插槽同一条路)。
60
+ // 共用别名的行**不进 WIRE_LOOKUP 的 alias 键**,只按 id 可查,靠 ingress/session-routes.js 会话优先路由
61
+ // 分辨(一个会话只认自己那行和自己的 fast 行);没注册会话的请求用这个 alias 发过来一律 502(探针要带
62
+ // 会话前缀)。独占别名的行才显式写 sdkAlias,语义见 model-table.js 字段说明。
63
+ function withDefaultAlias(row) {
64
+ if (!row.api || row.api.sdkAlias) return row;
65
+ return Object.freeze({ ...row, api: Object.freeze({ ...row.api, sdkAlias: SHARED_SDK_ALIAS }) });
66
+ }
67
+ const MODELS = Object.freeze([...MODELS_BUILTIN, ...external.models.map(toExternalRow)].map(withDefaultAlias));
59
68
  /** 外部插槽被整条丢掉的原因(启动日志一份、GET /api/local/config 一份,同一个数组) */
60
69
  export const MODEL_CONFIG_ERRORS = external.errors;
61
70
 
@@ -79,16 +88,25 @@ for (const row of MODELS) {
79
88
  if (BY_ID.has(row.id)) throw new Error(`[model-context] 模型 id 重复:${row.id}`);
80
89
  BY_ID.set(row.id, row);
81
90
  }
91
+ // 共用别名的本体必须始终是表内一条订阅 Claude 行:它是「不写 sdkAlias」的默认值,哪怕此刻没行在用,
92
+ // 删掉/改坏那条订阅行也要当场炸,不能等到下一条新行加进来才发现 SDK 不认识、窗口查不到。
93
+ {
94
+ const shared = BY_ID.get(SHARED_SDK_ALIAS);
95
+ if (!shared || shared.api) throw new Error(`[model-context] SHARED_SDK_ALIAS(${SHARED_SDK_ALIAS})必须是表内订阅模型行 —— 它是 sdkAlias 不写时的默认值`);
96
+ }
82
97
  for (const row of MODELS) {
83
98
  try { checkRow(row); } catch (err) {
84
99
  if (!row.external) throw err;
85
100
  BY_ID.delete(row.id); MODEL_CONFIG_ERRORS.push({ where: `models (${row.id})`, message: err.message }); continue;
86
101
  }
87
102
  if (!row.api) continue;
88
- const keys = row.external ? [row.id] : [row.id, row.api.sdkAlias, row.api.sdkAlias.replace(/\[1m\]$/i, '')];
103
+ // 共用别名的行(sdkAlias 没写、派生时补的默认值)只按 id 进反查表:那个别名同时属于好几行,
104
+ // 全表反查分不出谁是谁,只有会话知道(session-routes.resolveSessionWire 主行优先)。
105
+ const sharesAlias = row.api.sdkAlias === SHARED_SDK_ALIAS;
106
+ const keys = sharesAlias ? [row.id] : [row.id, row.api.sdkAlias, row.api.sdkAlias.replace(/\[1m\]$/i, '')];
89
107
  for (const k of keys) {
90
108
  const prev = WIRE_LOOKUP.get(k);
91
- if (prev && prev !== row) throw new Error(`[model-context] wire 名撞车:'${k}' 同时属于 ${prev.id} 和 ${row.id}(一个 sdkAlias 不能共用)`);
109
+ if (prev && prev !== row) throw new Error(`[model-context] wire 名撞车:'${k}' 同时属于 ${prev.id} 和 ${row.id}(独占 sdkAlias 不能共用;不想独占就别写 sdkAlias,让它走共用别名)`);
92
110
  WIRE_LOOKUP.set(k, row);
93
111
  }
94
112
  }
@@ -168,7 +186,7 @@ export function isModelLockedFor(user, appModel) {
168
186
  }
169
187
 
170
188
  /**
171
- * 这个用户没选过时用哪个:表里标 `default: true` 的行(08-21 = ox-alpha),它对该用户
189
+ * 这个用户没选过时用哪个:表里标 `default: true` 的行(08-26 = minimax-m3),它对该用户
172
190
  * 不可选时退到第一个可选的。前端 picker 与新会话的兜底都问这条,不再各自硬编码。
173
191
  */
174
192
  export function defaultModelFor(user) {
@@ -177,19 +195,76 @@ export function defaultModelFor(user) {
177
195
  }
178
196
 
179
197
  /**
180
- * 会话中途从 openai-chat 行(Ox)切到别的通路要拦(08-21 fable 评审 P3):转换层合成的 thinking 块
181
- * 没有 signature,CLI 会把它们原样回传给 Anthropic → 400 invalid signature。返回拒绝理由或 null
198
+ * 会话中途从 openai-chat 行(Ox / DeepSeek)切到别的通路要拦(08-21 fable 评审 P3):转换层合成的
199
+ * thinking 块没有 signature,CLI 会把它们原样回传给说 Anthropic 协议的那一头 → 400 invalid signature。
200
+ * 返回拒绝理由或 null。
201
+ *
202
+ * ⚠️ 拦的是**协议方向**不是"要不要 Claude":08-25 接了 MiniMax(Anthropic 原生透传)之后,
203
+ * 从 Ox 切到 MiniMax 同样是这条路,所以话里不许再写死"换到 Claude"。
182
204
  */
183
205
  export function crossLaneSwitchReason(fromModel, toModel) {
184
206
  if (!fromModel || !toModel || fromModel === toModel) return null;
185
207
  const from = resolveWireModel(fromModel);
186
208
  const to = resolveWireModel(toModel);
187
209
  if (from?.protocol === 'openai-chat' && to?.protocol !== 'openai-chat') {
188
- return '这个会话是在 Ox(免费)上开的,它的思考记录换到 Claude 会被拒收。想用 Claude 请新开一个会话';
210
+ const fromLabel = BY_ID.get(from.appModel)?.select?.label || from.appModel;
211
+ return `这个会话是在 ${fromLabel} 上开的,它的思考记录换到别的模型会被拒收。想换模型请新开一个会话`;
189
212
  }
190
213
  return null;
191
214
  }
192
215
 
216
+ /**
217
+ * **运行中**热切模型(POST /runs/:runId/model)额外要拦的一条:订阅 ↔ API 跨通路。
218
+ *
219
+ * 决定一条会话走订阅还是走 API 的是**起 query 那一刻注入的 env**(BASE_URL / API_KEY,
220
+ * 见 session-loop 的 route 分支),而 env 是 per-query 的,`setModel` 改不动它。所以跑到
221
+ * 一半跨通路切的真实后果是:
222
+ * - 订阅会话切到 API 行 → binary 手里没有 ingress 地址,会拿着 ~/.claude 的 OAuth 把
223
+ * **alias 名**(那都是真实存在的 Claude 模型)打到 anthropic.com —— 界面写着"免费",
224
+ * 烧的是订阅额度。⛔ 这是要花真钱的那种错。
225
+ * - API 会话切回订阅行 → 那个名字进了 ingress 反查不到,兜底到本会话的 fast 行,
226
+ * 等于"切了没生效"。
227
+ * 两边都不是用户想要的,所以运行中一律拒绝,让人等这轮跑完(PUT /sessions/:sid/model
228
+ * 那条等空闲重启 query,换的是新 env,不受这条限制)。
229
+ *
230
+ * 与 crossLaneSwitchReason 是两条**正交**的闸:那条管协议(openai-chat 的思考块没
231
+ * signature),这条管通路(env 定死在起 query 那一刻)。
232
+ */
233
+ export function hotSwitchLaneReason(fromModel, toModel) {
234
+ if (!fromModel || !toModel || fromModel === toModel) return null;
235
+ const from = resolveModelRoute(fromModel).mode;
236
+ const to = resolveModelRoute(toModel).mode;
237
+ if (from === to) return null;
238
+ return to === 'api'
239
+ ? '这一轮是用订阅模型开的,跑到一半换不成 API 模型 —— 网关地址和钥匙在起这一轮时就定死了,硬切会拿订阅额度去跑。等这轮跑完再换,或者新开一个会话'
240
+ : '这一轮是用 API 模型开的,跑到一半换不回订阅模型 —— 同样是网关地址起这一轮时就定死了。等这轮跑完再换,或者新开一个会话';
241
+ }
242
+
243
+ /**
244
+ * **换模型该不该拒**(null = 放行)。三条写模型的路共用这一个判断:turn.js 的 body.model、
245
+ * sessions.js 的 PUT /model、turn-model-switch.js 的运行中热切。
246
+ *
247
+ * 收成一份是因为 08-21 装的那条协议闸在两处都没真工作过(08-25 发现):sessions.js 那份把闸写在
248
+ * applySessionModel **之后**、又拿 apply 之后的模型当 from,等于自己跟自己比,恒返 null;turn.js 那份
249
+ * 带着 `override &&`,跑在全局默认上的会话整个逃过检查。同一个判断散成三份手写代码就是这个下场 ——
250
+ * 这个仓库为「同一件事有多个实例」付过最贵的学费。
251
+ *
252
+ * @param {object} p
253
+ * @param {string} p.from **改之前**的有效模型(⚠️ 不是刚写进去的那个 —— 那正是旧 bug)
254
+ * @param {string} p.to 要换成的模型(清覆盖时传全局默认那一行,别传 null)
255
+ * @param {boolean} [p.hasHistory] 这个会话跑过没有。没跑过就没有历史,协议闸不该拦(拦了只是让人换不了模型)
256
+ * @param {boolean} [p.running] 当前有没有正在跑的 query。跑着的话 env 已经定死,额外过通路闸
257
+ * @returns {string|null} 给用户看的拒绝理由
258
+ */
259
+ export function modelSwitchRejection({ from, to, hasHistory = true, running = false }) {
260
+ if (!from || !to || from === to) return null;
261
+ if (hasHistory) {
262
+ const why = crossLaneSwitchReason(from, to);
263
+ if (why) return why;
264
+ }
265
+ return running ? hotSwitchLaneReason(from, to) : null;
266
+ }
267
+
193
268
  /** 免费行(API 行且四价全 0):金额配额对它无意义,turn.js 改走按轮次的免费闸 */
194
269
  export function modelIsFree(appModel) {
195
270
  const p = BY_ID.get(appModel)?.api?.prices;
@@ -307,6 +382,11 @@ export function resolveWireModel(bodyModel) {
307
382
  // 预算 + 单发最长挂起(实测 185 秒)必须 < 600 秒(配了断言,见 upstream-truncation.test.js)。
308
383
  emptyRetries: Number.isFinite(row.api.emptyRetries) ? row.api.emptyRetries : null,
309
384
  retryBudgetMs: Number.isFinite(row.api.retryBudgetMs) ? row.api.retryBudgetMs : null,
385
+ // 上游要的额外顶层 body 字段(今天只有 merge 的 `vendor` 点名,见表里那行)。
386
+ // ⚠️ **这一份配置有两个读者**,因为两条腿各自序列化 body:Anthropic 透传在 transformForUpstream
387
+ // 里 Object.assign 到 parsed,openai-chat 在 toOpenAIChatRequest 里合进 out。加第三条腿要记得
388
+ // 带上它 —— 漏了不会报错,只是点名静默失效(model-ingress.test.js 两条腿各有一条断言)
389
+ bodyExtra: row.api.bodyExtra || null,
310
390
  };
311
391
  }
312
392
 
@@ -332,9 +412,13 @@ export function repriceUsageDeltas(deltas, sessionAppModel) {
332
412
  // helper 用 config 默认 Claude 名发的请求)必然被 ingress 的会话 fast 兜底
333
413
  // 承接 —— 归到 fastModel 头上是精确归因,不是猜测。
334
414
  const fastRow = BY_ID.get(sessionRow.api.fastModel);
415
+ // ⭐ 会话优先,跟入口路由同一个次序(session-routes.resolveSessionWire):SDK 报的 usage key 是
416
+ // **本会话的 sdkAlias**,而共用别名(SHARED_SDK_ALIAS)根本不在 WIRE_LOOKUP 里 —— 只按全表反查的话
417
+ // 主行那笔账会整个落到 fastModel 头上(计量按模型分组就全错了,钱对了数不对)。
418
+ const sessionNames = new Set(wireNamesOf(sessionRow.id));
335
419
  const out = {};
336
420
  for (const [key, d] of Object.entries(deltas)) {
337
- const row = WIRE_LOOKUP.get(key) || fastRow;
421
+ const row = (sessionNames.has(key) ? sessionRow : WIRE_LOOKUP.get(key)) || fastRow;
338
422
  const appKey = row ? row.id : key;
339
423
  const p = row?.api?.prices;
340
424
  const repriced = p ? {