@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.
- package/README.md +266 -79
- package/README.zh-CN.md +254 -67
- package/package.json +2 -2
- package/server/api/admin.js +7 -0
- package/server/api/assets/docx-page.js +5 -4
- package/server/api/assets/helpers.js +26 -0
- package/server/api/assets/notes.js +50 -0
- package/server/api/assets.js +31 -38
- package/server/api/board.js +29 -1
- package/server/api/browse.js +3 -2
- package/server/api/chatai.js +9 -8
- package/server/api/exports/cards.js +7 -6
- package/server/api/exports/docx-pdf.js +4 -1
- package/server/api/instruction.js +3 -3
- package/server/api/local.js +10 -8
- package/server/api/projects.js +19 -3
- package/server/api/publish.js +3 -2
- package/server/api/sessions-rewind.js +220 -0
- package/server/api/sessions.js +58 -194
- package/server/api/stage.js +35 -0
- package/server/api/turn-model-switch.js +109 -0
- package/server/api/turn.js +52 -88
- package/server/auth/middleware.js +29 -6
- package/server/auth/tier.js +27 -7
- package/server/auth/users-store.js +14 -1
- package/server/engine/agent/actor-trail.js +110 -0
- package/server/engine/agent/agent-shared.js +22 -52
- package/server/engine/agent/beat-state.js +61 -0
- package/server/engine/agent/cast.js +322 -0
- package/server/engine/agent/events.js +18 -1
- package/server/engine/agent/hooks/beat-gate.js +73 -0
- package/server/engine/agent/hooks/failure.js +11 -1
- package/server/engine/agent/hooks/lifecycle.js +16 -41
- package/server/engine/agent/hooks/post-guidance.js +4 -38
- package/server/engine/agent/hooks/post-trim.js +1 -0
- package/server/engine/agent/hooks/pre-board-dirty.js +29 -0
- package/server/engine/agent/hooks/pre-defaults.js +206 -2
- package/server/engine/agent/hooks/pre-injectors.js +19 -2
- package/server/engine/agent/hooks/pre-peer-guard.js +88 -0
- package/server/engine/agent/hooks/pre-unknown-params.js +82 -0
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +33 -0
- package/server/engine/agent/hooks/resident-role-lifecycle.js +100 -0
- package/server/engine/agent/hooks/slot-alias.js +30 -0
- package/server/engine/agent/hooks/user-prompt-submit.js +208 -47
- package/server/engine/agent/hooks.js +80 -14
- package/server/engine/agent/isolation.js +25 -2
- package/server/engine/agent/memory-config.js +87 -0
- package/server/engine/agent/model-context.js +102 -18
- package/server/engine/agent/model-table.js +361 -58
- package/server/engine/agent/prompts/nodesign-prelude.md +579 -70
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +1 -1
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +26 -9
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +1 -1
- package/server/engine/agent/prompts/tools/site-reference.md +15 -4
- package/server/engine/agent/role-card.js +175 -0
- package/server/engine/agent/rp-mode.js +17 -0
- package/server/engine/agent/session-loop.js +55 -32
- package/server/engine/agent/stage-status.js +73 -0
- package/server/engine/agent/subagent-flight.js +73 -0
- package/server/engine/agent/system-prompts.js +41 -0
- package/server/engine/agent/tool-input-stream.js +165 -0
- package/server/engine/agents/explorer.md +1 -1
- package/server/engine/agents/index.js +1 -1
- package/server/engine/mcp/actor.js +59 -0
- package/server/engine/mcp/capability-gate.js +21 -2
- package/server/engine/mcp/index.js +118 -33
- package/server/engine/mcp/mode-profile.js +139 -0
- package/server/engine/mcp/server-name.js +19 -0
- package/server/engine/mcp/tools/artifact-session.js +2 -1
- package/server/engine/mcp/tools/browse-computer.js +2 -1
- package/server/engine/mcp/tools/browse-find-batch.js +95 -16
- package/server/engine/mcp/tools/browse.js +25 -3
- package/server/engine/mcp/tools/cast-role.js +167 -0
- package/server/engine/mcp/tools/crystallize-skill.js +1 -1
- package/server/engine/mcp/tools/draw-trend.js +104 -0
- package/server/engine/mcp/tools/edit-board-schema.js +56 -0
- package/server/engine/mcp/tools/edit-board.js +569 -0
- package/server/engine/mcp/tools/generate-image.js +50 -70
- package/server/engine/mcp/tools/helpers/codex-imagegen.js +139 -0
- package/server/engine/mcp/tools/helpers/rembg.js +7 -2
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +109 -0
- package/server/engine/mcp/tools/list-pages.js +11 -5
- package/server/engine/mcp/tools/look-at-board.js +4 -3
- package/server/engine/mcp/tools/open-sheet.js +242 -0
- package/server/engine/mcp/tools/organize-board.js +45 -6
- package/server/engine/mcp/tools/paint-still.js +1 -1
- package/server/engine/mcp/tools/pin-to-board.js +103 -19
- package/server/engine/mcp/tools/read-board.js +123 -16
- package/server/engine/mcp/tools/read-tavern-json.js +87 -19
- package/server/engine/mcp/tools/report-issue.js +15 -7
- package/server/engine/mcp/tools/role-memory.js +83 -0
- package/server/engine/mcp/tools/roll-dice.js +82 -0
- package/server/engine/mcp/tools/roll-film.js +1 -1
- package/server/engine/mcp/tools/screenshot.js +19 -9
- package/server/engine/mcp/tools/set-vars.js +150 -0
- package/server/engine/mcp/tools/sheet-replan.js +46 -0
- package/server/engine/mcp/tools/web-search.js +12 -16
- package/server/engine/mcp/tools/write-on-board-flow.js +108 -0
- package/server/engine/mcp/tools/write-on-board-place.js +211 -0
- package/server/engine/mcp/tools/write-on-board-role-anchor.js +48 -0
- package/server/engine/mcp/tools/write-on-board-schema.js +125 -0
- package/server/engine/mcp/tools/write-on-board.js +568 -99
- package/server/engine/plugins/nodesign/skills/blackboard-rp/SKILL.md +215 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +14 -7
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +44 -5
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +16 -0
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +24 -8
- package/server/engine/plugins/nodesign/skills/story-craft/SKILL.md +94 -0
- package/server/engine/plugins/nodesign/skills/story-import/SKILL.md +112 -0
- package/server/engine/plugins/nodesign/skills/story-intimacy/SKILL.md +72 -0
- package/server/engine/plugins/nodesign/skills/story-voice/SKILL.md +66 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//345/206/267/347/241/254.md +33 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/203/205/346/204/237/347/233/264/347/273/231.md +42 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/204/217/350/257/206/346/265/201.md +42 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//346/255/246/344/276/240/347/231/275/350/257/235.md +34 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//347/224/237/346/264/273/346/265/201.md +35 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//347/231/275/346/217/217.md +37 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//350/210/236/345/217/260/345/211/247.md +32 -0
- package/server/engine/plugins/nodesign/skills/story-voice/voices//351/230/266/346/256/265/345/210/207/346/215/242.md +42 -0
- package/server/engine/runs/board-seater.js +272 -0
- package/server/engine/runs/board-tasklist.js +90 -48
- package/server/engine/runs/turn-relay.js +24 -2
- package/server/engine/runs/user-chalk-echo.js +44 -0
- package/server/index.js +2 -2
- package/server/lib/artifact-target.js +31 -14
- package/server/lib/board-anchor.js +49 -0
- package/server/lib/board-dirty.js +93 -0
- package/server/lib/board-excerpt.js +80 -0
- package/server/lib/board-follow.js +111 -0
- package/server/lib/board-groups.js +1 -5
- package/server/lib/board-hero.js +3 -1
- package/server/lib/board-kind-sizes.js +38 -3
- package/server/lib/board-lanes.js +58 -0
- package/server/lib/board-obstacles.js +80 -0
- package/server/lib/board-relations.js +5 -1
- package/server/lib/board-sheets.js +467 -0
- package/server/lib/board-shelf.js +86 -0
- package/server/lib/board-transform.js +62 -0
- package/server/lib/canvas-id.js +46 -4
- package/server/lib/chalk-flow.js +88 -0
- package/server/lib/chalk-rewrite.js +63 -0
- package/server/lib/chalk-size-pref.js +48 -0
- package/server/lib/chalk.js +42 -3
- package/server/lib/export-collect.js +3 -1
- package/server/lib/ingress/forward-openai-chat.js +16 -4
- package/server/lib/ingress/openai-chat.js +23 -12
- package/server/lib/ingress/session-routes.js +3 -3
- package/server/lib/ingress/stream-watchdog.js +53 -0
- package/server/lib/ingress/upstream-billing.js +15 -0
- package/server/lib/ingress/upstream-error-hints.js +36 -0
- package/server/lib/issues-store.js +3 -1
- package/server/lib/json-preview.js +58 -0
- package/server/lib/kinds/docx.js +11 -4
- package/server/lib/kinds/index.js +6 -16
- package/server/lib/kinds/site.js +40 -7
- package/server/lib/model-ingress.js +26 -1
- package/server/lib/moderation.js +10 -5
- package/server/lib/quota.js +26 -6
- package/server/lib/rect.js +50 -0
- package/server/lib/rewrite-refs.js +110 -0
- package/server/lib/screen.js +36 -0
- package/server/lib/sheet-state.js +19 -0
- package/server/lib/sketch-array.js +118 -0
- package/server/lib/sketch-hatch.js +99 -0
- package/server/lib/sketch-layout.js +269 -69
- package/server/lib/sketch-shapes.js +152 -0
- package/server/lib/sketch-stencils.js +170 -0
- package/server/lib/ssrf-guard.js +9 -0
- package/server/lib/state-table.js +237 -0
- package/server/lib/state-trend.js +94 -0
- package/server/lib/state-triggers.js +199 -0
- package/server/lib/task-scan.js +18 -0
- package/server/lib/tavern-json.js +48 -0
- package/server/lib/user-uploads.js +88 -0
- package/server/projects/board-limits.js +5 -3
- package/server/projects/board-sanitize.js +220 -10
- package/server/projects/board-store.js +137 -7
- package/server/projects/memory-migration.js +85 -0
- package/server/projects/move-entry.js +11 -1
- package/server/projects/session-jsonl.js +110 -0
- package/server/projects/store.js +56 -3
- package/server/projects/ui-config.js +2 -1
- package/server/projects/viewpoint-store.js +35 -3
- package/server/projects/workspace-slots.js +30 -0
- package/server/projects/workspace-templates.js +42 -10
- package/server/projects/workspace.js +25 -26
- package/server/runtime/capabilities.js +6 -2
- package/server/services/rembg-launcher.js +5 -2
- package/server/shared/locales.js +70 -0
- package/server/shared/messages-en.js +69 -0
- package/server/shared/messages.js +65 -0
- package/server/ws/broker.js +3 -0
- package/server/ws/index.js +43 -3
- package/web/README.md +1 -1
- package/web/dist/assets/{BrowserWindow-P_vIdh-G.js → BrowserWindow-CcaGRWz5.js} +1 -1
- package/web/dist/assets/DeckWindow-BIr2KGFT.js +6 -0
- package/web/dist/assets/{DocxWindow-DSAU1-tD.js → DocxWindow-B9KZmU5z.js} +2 -2
- package/web/dist/assets/{MermaidBlock-DR1GikCv.js → MermaidBlock-DrfRnz8E.js} +43 -43
- package/web/dist/assets/{SiteWindow-92uBuRRR.js → SiteWindow-CJOjCF7d.js} +6 -6
- package/web/dist/assets/{abnfDiagram-VCTEODGH-uA8Esix4.js → abnfDiagram-VCTEODGH-CUZbQGIg.js} +1 -1
- package/web/dist/assets/{arc-D_khH_xN.js → arc-C1-HSUny.js} +1 -1
- package/web/dist/assets/{architectureDiagram-5GKGNRK7-DydJHqOt.js → architectureDiagram-5GKGNRK7-B5nwvu60.js} +2 -2
- package/web/dist/assets/{blockDiagram-NRAW4CY4-B7wBbKiW.js → blockDiagram-NRAW4CY4-uPPuFLwR.js} +1 -1
- package/web/dist/assets/{c4Diagram-UCG6FXSJ-6mrH0x7Y.js → c4Diagram-UCG6FXSJ-C0mKBFlz.js} +1 -1
- package/web/dist/assets/channel-BxpWNOGU.js +1 -0
- package/web/dist/assets/{chunk-2Q5K7J3B-Cuikho5Q.js → chunk-2Q5K7J3B-Cs0rlZ4q.js} +1 -1
- package/web/dist/assets/{chunk-5VM5RSS4-3-KZ5AUz.js → chunk-5VM5RSS4-DtR63oYb.js} +1 -1
- package/web/dist/assets/{chunk-F27PBJKO-Bkwq2gGU.js → chunk-F27PBJKO-B3MRdezJ.js} +1 -1
- package/web/dist/assets/{chunk-G27WJ6UU-BLyQzdGE.js → chunk-G27WJ6UU-BtwntwpS.js} +1 -1
- package/web/dist/assets/{chunk-JWPE2WC7-B7HxxQds.js → chunk-JWPE2WC7-BHsTe5PD.js} +1 -1
- package/web/dist/assets/{chunk-LCL6LL3I-CtXrnUwF.js → chunk-LCL6LL3I-knY15Qdm.js} +1 -1
- package/web/dist/assets/{chunk-POPQ4Y6H-dxddZlXt.js → chunk-POPQ4Y6H-C51tgK_I.js} +1 -1
- package/web/dist/assets/{chunk-SVP7TREG-DpkmrgHn.js → chunk-SVP7TREG-CnwRZmF_.js} +1 -1
- package/web/dist/assets/{chunk-XXDRQBXY-BwZ7RbpQ.js → chunk-XXDRQBXY-BtXzVF-_.js} +1 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-DUEKmDfo.js +1 -0
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-DUEKmDfo.js +1 -0
- package/web/dist/assets/{cose-bilkent-JH36ORCC-DGFOsMUt.js → cose-bilkent-JH36ORCC-CzWlGP6i.js} +1 -1
- package/web/dist/assets/{cynefin-OW5HDTMX-DUwn3ehS.js → cynefin-OW5HDTMX-vK64EGSv.js} +1 -1
- package/web/dist/assets/{cynefinDiagram-5FMLGOSQ-DlLAOZ9x.js → cynefinDiagram-5FMLGOSQ-Bs3g1Gvh.js} +1 -1
- package/web/dist/assets/{dagre-3AP2YEHR-CyourLfz.js → dagre-3AP2YEHR-BhgkYe8V.js} +1 -1
- package/web/dist/assets/{diagram-S7CK7UJ4-BTMbx2np.js → diagram-S7CK7UJ4-C_6mvD2V.js} +1 -1
- package/web/dist/assets/{diagram-UQ7AKVKN-UK94_1-P.js → diagram-UQ7AKVKN-qW7tssUW.js} +1 -1
- package/web/dist/assets/{diagram-VSXAHHWV-C7zS-v9v.js → diagram-VSXAHHWV-txJPHpvG.js} +1 -1
- package/web/dist/assets/{diagram-VX7I27RA-DKJRa6CK.js → diagram-VX7I27RA-CQTMsCnL.js} +1 -1
- package/web/dist/assets/{diagram-Z3DM3KII-kUbAfGAp.js → diagram-Z3DM3KII--cb1hoE6.js} +1 -1
- package/web/dist/assets/{ebnfDiagram-PWID7BFC-CVW16_vb.js → ebnfDiagram-PWID7BFC-Da6lDU77.js} +1 -1
- package/web/dist/assets/{erDiagram-SSCWMZ5O-BRQMFQ5y.js → erDiagram-SSCWMZ5O-DGCMrsQn.js} +1 -1
- package/web/dist/assets/{flowDiagram-A5DVABFB-S34LpLtK.js → flowDiagram-A5DVABFB-GdOL_qno.js} +1 -1
- package/web/dist/assets/{ganttDiagram-EL5Y4UJY-BFiqk1-G.js → ganttDiagram-EL5Y4UJY-Cx1F_jBK.js} +4 -4
- package/web/dist/assets/{gitGraphDiagram-WWUBYQGX-LZsrdyyv.js → gitGraphDiagram-WWUBYQGX-B5_6JhR3.js} +1 -1
- package/web/dist/assets/hand-CuvZZQGW.webp +0 -0
- package/web/dist/assets/index-BYKfMnmv.js +2759 -0
- package/web/dist/assets/index-CW7VsrVg.css +1 -0
- package/web/dist/assets/{infoDiagram-RXCK75RN-0DvN6KIp.js → infoDiagram-RXCK75RN-CjvKpSPA.js} +1 -1
- package/web/dist/assets/{ishikawaDiagram-5VMMS53U-CaY3A-Bx.js → ishikawaDiagram-5VMMS53U-CxLoAPhx.js} +1 -1
- package/web/dist/assets/{journeyDiagram-EYS64GPL-BBeiaCrj.js → journeyDiagram-EYS64GPL-BfvS1upJ.js} +1 -1
- package/web/dist/assets/{kanban-definition-3QL26DDD-ux42DRXQ.js → kanban-definition-3QL26DDD-DKPPu0w4.js} +1 -1
- package/web/dist/assets/{layout-CnXiF_NB.js → layout-Dhgck-Zo.js} +1 -1
- package/web/dist/assets/{linear-BMW0u6G6.js → linear-DMSQ-aUw.js} +1 -1
- package/web/dist/assets/lxgw-nd-app-bold-D1pKA1bR.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-app-regular-BDe3d26v.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-bold-Bufvlg03.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-DQwoI4OP.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-100-DLPe7tEt.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-101-d3VsZKWT.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-102-ClzJuLq6.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-103-DuDnQ6K-.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-104-kPGW3Eem.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-105-BzD54h-0.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-106-BQ7fIzlx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-107-D8tDfFHI.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-108-tGhkpwep.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-109-7SMVSrly.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-110-_uDKrFed.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-111-Bio825RE.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-112-D2ee0i-C.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-113-JPFVDrga.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-114-BcfxYA4w.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-115-B1HeeOsd.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-116-hY12tXpq.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-117-BvuZwmre.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-118-tzvs_H7j.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-119-J2DWuYDN.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-21-pDubCJV5.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-22-ChAPMpqp.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-23-BBx3oNpT.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-24-CYt-Oagk.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-25-Cx3j-ZUv.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-26-B9AfZ3LL.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-27-Bh2s9MNE.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-28-C02JPoKP.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-29-T_tbUGhk.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-30-DMfyNzhz.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-31-DFfdzbkg.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-32-Bs1fz2iW.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-33-cpB96OkZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-34-BhinPYWX.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-35-D_njRQqA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-36-DqXlLE3t.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-37-j5LbvMph.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-38-oO4jWdKm.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-39-CabsX4rw.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-4-PatS3YHh.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-40-yrLg3C3e.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-41-DBXmCQhc.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-42-Bqij_UiQ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-43-D3kp0Ntc.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-44-B_BNGvK4.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-45-CZc0cQWs.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-46-CHQX5Kd9.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-47-Ci8gLrHA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-48-run3ugD3.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-49-DgfiUb6u.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-5-B0CtGbX_.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-50-CdCG-POr.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-51-L2okeQZ2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-52-BZAqyGJW.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-53-B0xWqLeU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-54-BZVQX-EA.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-55-093mkIUJ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-56-CbhWG8G-.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-57-pLQuvo6c.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-58-BHhoY50x.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-59-D1_6DuIQ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-6-BnAelmYU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-60-HDBIuLTZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-61-C-c13SeU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-62-kHRVOhZ2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-63-C0MCsvFv.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-64-DZekguCj.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-65-CtP6c3IU.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-66-DLVWoMSf.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-67-BVuOnayH.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-68-DpxQ6Mnx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-69-qesBxLmC.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-70-BBt1xN7h.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-71-BWqZacwB.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-72-B1wP_Yst.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-73-C99T7jNZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-74-DrUTsWBe.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-75-C4M5XXQe.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-76-Bq8spVWF.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-77-D9vNupE2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-78-D6th7Gnu.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-79-bezM85cS.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-80-DD8pnhG2.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-81-N6jtSyBs.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-82-D6c180bZ.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-83-em17AadM.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-84-CJ8gdrMK.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-85-9PPGO1eB.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-86-CjWPf-u5.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-87-8D_X_yOC.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-88-9vtKFPBx.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-89-BhE5jAKX.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-90-D2Npfm5E.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-91-C6Ft1-Eu.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-97-CaExyQP9.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-98-CPoVbsUd.woff2 +0 -0
- package/web/dist/assets/lxgwwenkaiscreen-subset-99-ZUQZGVgH.woff2 +0 -0
- package/web/dist/assets/{mindmap-definition-FBJOCRG2-hUxSpdKn.js → mindmap-definition-FBJOCRG2-CalfL_Kn.js} +1 -1
- package/web/dist/assets/{pegDiagram-XKGWAZYB-aCaQPH0w.js → pegDiagram-XKGWAZYB-D9mVWz7l.js} +1 -1
- package/web/dist/assets/pending-edit-apply-kwMFWsUe.js +31 -0
- package/web/dist/assets/{pieDiagram-E7YTZNPT-CmOEZ8zg.js → pieDiagram-E7YTZNPT-DELzbZNS.js} +1 -1
- package/web/dist/assets/{quadrantDiagram-AXDQQJYC-VW6fAhsV.js → quadrantDiagram-AXDQQJYC-TZqp4mfA.js} +1 -1
- package/web/dist/assets/{railroadDiagram-O6MQD6OU-CKJ7Ba5K.js → railroadDiagram-O6MQD6OU-DxX59f0i.js} +1 -1
- package/web/dist/assets/{requirementDiagram-EFPCY7ZU-pNGkq9Vw.js → requirementDiagram-EFPCY7ZU-DFdsJLIe.js} +1 -1
- package/web/dist/assets/{rotate-cw-KAfChbGV.js → rotate-cw-62liz7LX.js} +1 -1
- package/web/dist/assets/{sankeyDiagram-P5KCCOFB-BIMmDTfZ.js → sankeyDiagram-P5KCCOFB-ByYcd3fG.js} +1 -1
- package/web/dist/assets/{sequenceDiagram-WJ2MYXX4-BBW7wzKY.js → sequenceDiagram-WJ2MYXX4-Bj-ml1jS.js} +1 -1
- package/web/dist/assets/{sizeCapture-X5ZJPWSS-DdkXmLYR.js → sizeCapture-X5ZJPWSS-KhsXThyD.js} +1 -1
- package/web/dist/assets/{square-dashed-mouse-pointer-CDUlhUP7.js → square-dashed-mouse-pointer-D_VrqVpy.js} +1 -1
- package/web/dist/assets/{stateDiagram-HBIQ2CUA-W313Uz16.js → stateDiagram-HBIQ2CUA-BJlpHfWU.js} +1 -1
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-CR2kYAH2.js +1 -0
- package/web/dist/assets/{swimlanes-XN3QIQJK-C29TL26K.js → swimlanes-XN3QIQJK-DUB9OJVs.js} +1 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BitOxnG2.js +8 -0
- package/web/dist/assets/{timeline-definition-24CTP7MA-CF0JMObX.js → timeline-definition-24CTP7MA-C03NX82A.js} +1 -1
- package/web/dist/assets/{vennDiagram-4TSXK5OY-DrDFsaH3.js → vennDiagram-4TSXK5OY-B9pjzngp.js} +1 -1
- package/web/dist/assets/{wardleyDiagram-VM6X3IG4-CVrnRIS_.js → wardleyDiagram-VM6X3IG4-UqWq_mcT.js} +1 -1
- package/web/dist/assets/{xychartDiagram-S5SC5T6Z-BvkN1zsT.js → xychartDiagram-S5SC5T6Z-D2pDKtOv.js} +1 -1
- package/web/dist/index.html +2 -2
- package/server/api/memory.js +0 -185
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +0 -583
- package/server/engine/mcp/tools/arrange-on-board.js +0 -124
- package/server/engine/mcp/tools/create-on-board.js +0 -152
- package/server/engine/mcp/tools/edit-sketch.js +0 -167
- package/server/engine/mcp/tools/record-decision.js +0 -140
- package/server/engine/mcp/tools/relate-on-board.js +0 -112
- package/server/engine/mcp/tools/sketch-on-board.js +0 -337
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +0 -235
- 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
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +0 -268
- package/web/dist/assets/DeckWindow-BpLv5082.js +0 -16
- package/web/dist/assets/channel-DGkccMO1.js +0 -1
- package/web/dist/assets/classDiagram-DTDB5LWJ-BGscw3OD.js +0 -1
- package/web/dist/assets/classDiagram-v2-JRS7N3AN-BGscw3OD.js +0 -1
- package/web/dist/assets/index-C0KUf6Wd.css +0 -1
- package/web/dist/assets/index-GAamh1p4.js +0 -2152
- package/web/dist/assets/lxgw-nd-bold-DbRfiTgo.woff2 +0 -0
- package/web/dist/assets/lxgw-nd-regular-CpMw9Ehg.woff2 +0 -0
- package/web/dist/assets/pending-edit-apply-AcdOQ9Ij.js +0 -31
- package/web/dist/assets/stateDiagram-v2-4QOOHH4V-DDhJBZzf.js +0 -1
- package/web/dist/assets/swimlanesDiagram-VK2B7HYN-BfN-Xdmu.js +0 -8
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
**`linkedToEditId` follow-up comment 的优先级最高**:用户填了"重排 OK" / "保持完全不动" 等明确指令,按 comment 来。没填走默认(你自己判断"尽量不动")
|
|
54
54
|
- **pending-style** —— 按 `styleDelta` 改 inline style 或对应 Tailwind class(如 styleDelta.marginLeft 改 `ml-*`)。
|
|
55
55
|
- **自由模式(free position)—— 最小改动原则**:
|
|
56
|
-
- `styleDelta` 只含 `{position:'absolute', left, top}` ——
|
|
56
|
+
- `styleDelta` 只含 `{position:'absolute', left, top}` —— 这是用户的真实意图(他把东西摆到了哪里)
|
|
57
57
|
- **不要无脑写整个 source 的样式**。默认只改这 3 个定位字段,**保留 source 原本的 CSS class 行为**(响应式、flex/grid 分配的尺寸、margin、transform 等)
|
|
58
58
|
- 看 `aiContext.parentNeedsRelative` —— true 时把 `parentAnchor` 父元素加 `position:relative`
|
|
59
59
|
- **`aiContext.preDragLayout` 是关键决策上下文**——source 拖前的 computed style 快照,让你判断是否需要追加补救:
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
> 注入,每 session 只注一次。SKILL.md 已含精简版核心要点,本文是深度参考。
|
|
5
5
|
>
|
|
6
6
|
> ⚠️ Gemini gateway 时代的参数段(imageSize / thinkingLevel / model 路由 flash-pro /
|
|
7
|
-
> Image Search Grounding / PDF 文档输入 / NB2 model card
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
> `NODESIGN_IMAGE_PROVIDER=gateway` 重开时再把那份取回来。
|
|
7
|
+
> Image Search Grounding / PDF 文档输入 / NB2 model card 失败模式)**已整体移出本文**
|
|
8
|
+
> (那份存档 08-24 已删:全仓无引用的孤儿文件;要复活 gateway 从 git 历史里捞)。
|
|
9
|
+
> 那些旋钮在当前后端上传了不报错也没效果,留在这里只会让你花心思拨一个不存在的开关。
|
|
11
10
|
|
|
12
11
|
## 前置 · 后端事实 —— 先读这节,它决定下面所有写法
|
|
13
12
|
|
|
@@ -44,7 +43,7 @@
|
|
|
44
43
|
|
|
45
44
|
| 主体 | 来源 | 触发动作 |
|
|
46
45
|
|---|---|---|
|
|
47
|
-
|
|
|
46
|
+
| 用户上传素材(`用户内容/*.png\|jpg`,老项目在 `assets/`)| 直接用 | 把路径喂 `referenceImages[]` |
|
|
48
47
|
| 模型脑里有的著名实体(Apple Park / Wong Kar-wai 风 / 艺术流派)| 不必 reference | prompt 直接点名 |
|
|
49
48
|
| **真实存在但模型不熟**(最新发布产品 / 小众品牌 / 用户自有 IP / 特定型号设备)| **`web_search { include_images:true }`** | 工具自动翻英文 + 下载到 `assets/references/`;选 1-2 张最切题的 `local_path` 喂 `referenceImages[]` |
|
|
50
49
|
| 抽象概念 / 装饰 / 隐喻 | 不需要 reference | 直接 prompt |
|
|
@@ -242,7 +241,7 @@ Avoid: <底线级禁令>
|
|
|
242
241
|
|---|---|
|
|
243
242
|
| **风格一致(跨页锚)** | 第 1 张 cover 当种子,后续 hero / section-divider 都引它 → 整篇像同一部片子 |
|
|
244
243
|
| **角色一致(多页叙事)** | portrait 跨页引 + 给角色起名("Maya, the woman in Reference 1")|
|
|
245
|
-
| **logo / brand 嵌入** | 用户上传 logo 进
|
|
244
|
+
| **logo / brand 嵌入** | 用户上传 logo 进 `用户内容/`,prompt 写 "Place the logo from Reference 1 etched into the bottle in Reference 2" |
|
|
246
245
|
| **精修而非重画** | `screenshot_canvas` 截当前页当 reference,配 § G 的模板 |
|
|
247
246
|
| **真实主体锚定** | `web_search { include_images: true }` 拿真图 → 喂进来 + "Use the product in Reference 1 as the subject; render it in [场景]" |
|
|
248
247
|
|
|
@@ -344,6 +343,24 @@ Step 3: 给角色起名锚定("Maya, character from Reference 1")
|
|
|
344
343
|
|
|
345
344
|
每次只改一处,改完再看,别一轮里改五件事。
|
|
346
345
|
|
|
346
|
+
### G1. variationOf 模式 —— 系列图一致性用参数,别手写咒语
|
|
347
|
+
|
|
348
|
+
上面那套 "Change only… Keep… unchanged" 的骨架,工具已经内置成结构化参数:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{ "variationOf": "assets/generated/A1.png",
|
|
352
|
+
"change": "裙子换成薄荷绿短裙,长度到膝盖以上",
|
|
353
|
+
"preserve": ["pose","framing","face","hair","top","shoes"] }
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
- `variationOf` 就是编辑基底(自动成为第 1 张 reference),`change` 是唯一要动的那处,
|
|
357
|
+
`preserve` 是必须一致的方面(**省略 = 全保,最安全**)。工具会展开成完整骨架并
|
|
358
|
+
自动追加通用禁止项(不凭空加背带/发饰、缺席也要保持 —— 都是真实翻过车的)。
|
|
359
|
+
- 何时用它而不是手写 § G 模板:**做系列**(换装矩阵、多页同角色、多帧同场景)。
|
|
360
|
+
组织成一棵树:先出一张锚图,之后每次调用只沿一个轴变一处。
|
|
361
|
+
- 手写 prompt 仍然适合单次精修(改光线、删元素这类一次性编辑)。
|
|
362
|
+
- 此模式下 `prompt` 可省略;传了就当补充说明附在骨架后。
|
|
363
|
+
|
|
347
364
|
## H. 连续帧的能力边界(实测,别重新踩)
|
|
348
365
|
|
|
349
366
|
| 用法 | 结果 |
|
|
@@ -390,9 +407,9 @@ Step 3: 给角色起名锚定("Maya, character from Reference 1")
|
|
|
390
407
|
|
|
391
408
|
## J. 调完必做
|
|
392
409
|
|
|
393
|
-
1.
|
|
394
|
-
|
|
395
|
-
|
|
410
|
+
1. **记进记忆** —— 定案的 anchor(prompt 骨架 + role + 产物路径 + 用户评价)写一条
|
|
411
|
+
`type: project` 记忆(`记忆/` 主题文件 + MEMORY.md 一行)。重生时能查回,
|
|
412
|
+
用户在画布上也看得到。
|
|
396
413
|
2. **关键节点的反馈循环**:cover / 第一个 portrait / logo 嵌入这种会被当跨页种子的 anchor,
|
|
397
414
|
生完在自然回话里邀请用户看一眼方向(生图的 image block 已自动渲染在 chat,他直接看得到)。
|
|
398
415
|
种子早定早收益。
|
|
@@ -21,12 +21,22 @@
|
|
|
21
21
|
|
|
22
22
|
(旧项目里工作区根上直接放 index.html 的「根站」仍被识别和支持,但**新站一律入夹**。)
|
|
23
23
|
|
|
24
|
-
**构建型站点**(Astro / 11ty / 自写 build
|
|
25
|
-
`dist/`(或 `out/` `build/` `_site/`,有 index.html
|
|
26
|
-
|
|
24
|
+
**构建型站点**(Vite / React / Vue / Astro / 11ty / 自写 build 脚本):源随便组织,
|
|
25
|
+
构建产物落 `dist/`(或 `out/` `build/` `_site/` `public/`,有 index.html 的那个自动
|
|
26
|
+
被认作**产物根**)。现代工程源码里的 `index.html`(引 `/src/main.tsx` 那种 dev 入口)
|
|
27
|
+
**不会**被误当产物 —— 构建完系统自动优先 dist。`.nd-project.json` 的 `"root": "<目录>"`
|
|
28
|
+
可显式指定产物根:**放站点文件夹里,路径相对该文件夹**(如 `观察日志/.nd-project.json`
|
|
29
|
+
写 `{"root":"dist"}`);根站的放工作区根。预览、截图、`list_pages`、
|
|
27
30
|
导出、发布看的**全是产物根** —— 改完源必须重新构建,否则用户看到的还是旧的。
|
|
31
|
+
构建必须用绝对路径或 `(cd <站点文件夹> && npm run build)` 子 shell 跑,**build 完
|
|
32
|
+
`ls` 确认产物真的落在了站点文件夹里的 dist/**(cwd 漂了会把 dist 建到别处)。
|
|
28
33
|
`node_modules` / 构建缓存系统永远不扫,不用进 .ndignore。
|
|
29
34
|
|
|
35
|
+
WebGL/Three.js(含 R3F)的一个静默坑:渲染器属性(如剖切要的
|
|
36
|
+
`localClippingEnabled`)传给 `<Canvas gl={{…}}>` 构造参数会被**静默忽略**(无报错
|
|
37
|
+
无效果),要在 `onCreated={({gl}) => { gl.localClippingEnabled = true }}` 里设;
|
|
38
|
+
`clippingPlanes` 裁掉的是法线**负**侧,"看起来没剖"先怀疑法线方向。
|
|
39
|
+
|
|
30
40
|
手写起手:`mkdir 观察日志`,然后 Write `观察日志/index.html`(文件夹名换成真站名;
|
|
31
41
|
没有起手模板,骨架从需求推、样式从风格名推)。**唯一硬要求是产物根上有 `index.html`**;
|
|
32
42
|
样式文件怎么拆怎么叫你定,按站的体量来。
|
|
@@ -103,4 +113,5 @@
|
|
|
103
113
|
- **字体链少了 CJK 那段**:`'Inter', sans-serif` 换台机器中文就掉到系统默认字体。
|
|
104
114
|
每段 latin family 后面必须跟 `'PingFang SC', 'Noto Sans SC'`
|
|
105
115
|
- **改了 style.css 预览没变**:不会。html/css/js 走 `no-cache`,写完即时刷新
|
|
106
|
-
-
|
|
116
|
+
- **标点跟正文主体语言走**:中文正文里的 `,` `:` 很扎眼,用全角 `,` `:`;
|
|
117
|
+
英文正文反过来,全角标点混进去一样扎眼,用半角加空格
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* engine/agent/role-card.js —— 读角色文件(2026-08-26)
|
|
3
|
+
*
|
|
4
|
+
* 角色文件(`.claude/agents/rp-*.md`)**是模型可写的**:`cast_role` 是正门,但主 agent
|
|
5
|
+
* 手里有 Write/Edit/Bash,`.claude/agents/` 就在工作区内,workspace-scope-guard 不拦它。
|
|
6
|
+
* 所以「角色只能拿板上工具」这条教义如果只写在 cast_role 里,等于没写 —— 绕过它只要
|
|
7
|
+
* 手写一份 md。
|
|
8
|
+
*
|
|
9
|
+
* 收口在**派发时**(见 hooks/pre-defaults.js 的 rp 分支):不管这份文件是谁写的、
|
|
10
|
+
* 怎么落的盘,派它之前都按同一张白名单核一遍。
|
|
11
|
+
*
|
|
12
|
+
* ## 三种要拦的写法
|
|
13
|
+
*
|
|
14
|
+
* tools: mcp__nodesign__publish_site 点名要外发工具
|
|
15
|
+
* tools: mcp__nodesign **server 通配** = 这个 server 的全部工具
|
|
16
|
+
* (压根没有 tools 行) SDK 语义 = **继承父代理的全部工具**
|
|
17
|
+
*
|
|
18
|
+
* 最后那条最容易漏:少写一行比多写一行危险。所以缺失 = 不合法,不是"用默认值"。
|
|
19
|
+
*
|
|
20
|
+
* ## 解析为什么是保守的
|
|
21
|
+
*
|
|
22
|
+
* frontmatter 真的走 YAML 解析(CLI 侧 `Bun.YAML.parse`),合法写法不止一种
|
|
23
|
+
* (`a, b` / `[a, b]` / 多行 `- a`)。这里三种都认;认不出的形状一律**当不合法**
|
|
24
|
+
* 而不是当空 —— 判据看不懂的东西不能默认放行。角色文件本来就该由 cast_role 写,
|
|
25
|
+
* 手写出奇怪形状时 deny 会指回正门。
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import path from 'node:path';
|
|
29
|
+
import { promises as fs } from 'node:fs';
|
|
30
|
+
import { isResidentRole, isSlotType, safeRoleLabel } from './cast.js';
|
|
31
|
+
|
|
32
|
+
const AGENTS_DIR = '.claude/agents';
|
|
33
|
+
const REGISTRY_REL = '.nd/cast.json';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 演员位登记表(2026-08-28 重构):cast_role 写的 slug → {name, duty, pen, card}。
|
|
37
|
+
* 没有/坏了 = 空表 —— 登记是展示层,fail-soft,坏了角色照样上场(署名退回 slug)。
|
|
38
|
+
*/
|
|
39
|
+
export async function readCastRegistry(workspaceRoot) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = await fs.readFile(path.join(workspaceRoot, REGISTRY_REL), 'utf8');
|
|
42
|
+
const data = JSON.parse(raw);
|
|
43
|
+
return data && typeof data.roles === 'object' && data.roles ? data : { version: 1, roles: {} };
|
|
44
|
+
} catch { return { version: 1, roles: {} }; }
|
|
45
|
+
}
|
|
46
|
+
const FM_RE = /^---\r?\n([\s\S]{0,4000}?)\r?\n---/;
|
|
47
|
+
|
|
48
|
+
/** 从 frontmatter 文本里取一个键的原始值(只认顶层、单行键) */
|
|
49
|
+
function rawValue(fm, key) {
|
|
50
|
+
const re = new RegExp(`^${key}:[ \\t]*(.*)$`, 'm');
|
|
51
|
+
const m = re.exec(fm);
|
|
52
|
+
return m ? m[1] : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 解析 tools 声明。
|
|
57
|
+
* @returns {{ kind: 'list', tools: string[] } | { kind: 'missing' } | { kind: 'unparsable' }}
|
|
58
|
+
*/
|
|
59
|
+
export function parseToolsDeclaration(fm) {
|
|
60
|
+
const raw = rawValue(fm, 'tools');
|
|
61
|
+
if (raw === null) return { kind: 'missing' };
|
|
62
|
+
const inline = raw.trim();
|
|
63
|
+
|
|
64
|
+
if (inline === '') {
|
|
65
|
+
// 多行数组:tools:\n - a\n - b
|
|
66
|
+
const after = fm.slice(fm.indexOf('\ntools:') + 1).split('\n').slice(1);
|
|
67
|
+
const items = [];
|
|
68
|
+
for (const line of after) {
|
|
69
|
+
if (/^\s*-\s+/.test(line)) { items.push(line.replace(/^\s*-\s+/, '').trim().replace(/^["']|["']$/g, '')); continue; }
|
|
70
|
+
if (/^\S/.test(line)) break; // 下一个顶层键
|
|
71
|
+
if (line.trim() === '') continue;
|
|
72
|
+
return { kind: 'unparsable' };
|
|
73
|
+
}
|
|
74
|
+
return items.length ? { kind: 'list', tools: items } : { kind: 'unparsable' };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const flow = /^\[(.*)\]$/.exec(inline);
|
|
78
|
+
const body = flow ? flow[1] : inline;
|
|
79
|
+
const tools = body.split(',').map((t) => t.trim().replace(/^["']|["']$/g, '')).filter(Boolean);
|
|
80
|
+
return tools.length ? { kind: 'list', tools } : { kind: 'unparsable' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 读一份角色文件。
|
|
85
|
+
* @returns {Promise<{ slug, displayName: string|null, description: string|null,
|
|
86
|
+
* toolsDecl: ReturnType<typeof parseToolsDeclaration> } | null>}
|
|
87
|
+
*/
|
|
88
|
+
export async function readRoleCard(workspaceRoot, slug) {
|
|
89
|
+
if (!workspaceRoot || !isResidentRole(slug)) return null;
|
|
90
|
+
const file = path.join(workspaceRoot, AGENTS_DIR, `${slug}.md`);
|
|
91
|
+
let raw;
|
|
92
|
+
try { raw = await fs.readFile(file, 'utf8'); } catch { return null; }
|
|
93
|
+
const m = FM_RE.exec(raw);
|
|
94
|
+
if (!m) return { slug, displayName: null, description: null, toolsDecl: { kind: 'unparsable' } };
|
|
95
|
+
const fm = m[1];
|
|
96
|
+
const description = rawValue(fm, 'description');
|
|
97
|
+
const descPlain = description
|
|
98
|
+
? description.trim().replace(/^"([\s\S]*)"$/, '$1').replace(/\\"/g, '"')
|
|
99
|
+
: null;
|
|
100
|
+
const nameMatch = descPlain ? /^RP 角色「(.+?)」/.exec(descPlain) : null;
|
|
101
|
+
return {
|
|
102
|
+
slug,
|
|
103
|
+
// ⚠️ 展示名是文件里的**自称**,不是实证 —— 角色文件模型可写,一个角色能自称
|
|
104
|
+
// 「用户」或顶替别的角色。只拿它当展示层,归属的锚在名册(harness 亲眼所见的派发)。
|
|
105
|
+
// 保留字闸(safeRoleLabel)在**这个出口**过(08-28 下沉):此前只在 listRoleNames
|
|
106
|
+
// 过,直接消费 readRoleCard().displayName 的新调用点会重蹈「三个渲染面漏一个」。
|
|
107
|
+
displayName: nameMatch ? safeRoleLabel(slug, nameMatch[1]) : null,
|
|
108
|
+
description: descPlain,
|
|
109
|
+
toolsDecl: parseToolsDeclaration(fm),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 从派发 prompt 里推断实例名(2026-08-28 晚,glm 撞闸案)。
|
|
115
|
+
*
|
|
116
|
+
* name 闸的原设计假设「deny 一次模型就会补参数」—— 对 sonnet 成立(探针实证),
|
|
117
|
+
* 对免费行 glm 不成立:proj_mtd7d4et 里它连撞九次,第六次把 `name: rp-izumi`
|
|
118
|
+
* 写进了 prompt 正文 —— 它想给,但参数就是发不出来。弱模型够不着的参数不能当
|
|
119
|
+
* 硬前置,所以闸学会自己认,按可靠度分层:
|
|
120
|
+
*
|
|
121
|
+
* 1. prompt 头部的 `name: rp-xxx` 行(模型明说的)
|
|
122
|
+
* 2. 登记表匹配:prompt 里出现某个已登记角色的卡路径(cast_role 的配方就是
|
|
123
|
+
* 「第一行写卡路径」,正常照做的派发天然命中)
|
|
124
|
+
* 3. prompt 里出现的、登记过的 rp- slug
|
|
125
|
+
*
|
|
126
|
+
* 推出来的名字跟模型传参同一信任级(名字本来就是模型起的),不碰任何安全判据
|
|
127
|
+
* (工具白名单在演员位文件上,收件人闸认的是名册登记)。taken 是已在场名册 ——
|
|
128
|
+
* 在场的不再当候选(那是"续演该用 SendMessage"的情形,不是新派)。
|
|
129
|
+
*/
|
|
130
|
+
export async function inferRoleNameFromPrompt(workspaceRoot, prompt, { taken = new Set(), isSlot = () => false } = {}) {
|
|
131
|
+
const head = String(prompt || '').slice(0, 1200);
|
|
132
|
+
const ok = (s) => s && /^rp-[A-Za-z0-9][A-Za-z0-9_-]{0,60}$/.test(s) && !isSlot(s) && !taken.has(s);
|
|
133
|
+
const explicit = /name[::]\s*["'「]?(rp-[A-Za-z0-9][A-Za-z0-9_-]*)/.exec(head);
|
|
134
|
+
if (explicit && ok(explicit[1])) return explicit[1];
|
|
135
|
+
const reg = await readCastRegistry(workspaceRoot);
|
|
136
|
+
const byCard = Object.entries(reg.roles)
|
|
137
|
+
.filter(([slug, r]) => ok(slug) && typeof r?.card === 'string' && r.card && head.includes(r.card));
|
|
138
|
+
if (byCard.length === 1) return byCard[0][0];
|
|
139
|
+
const mentioned = Object.keys(reg.roles).filter((slug) => ok(slug) && head.includes(slug));
|
|
140
|
+
if (mentioned.length === 1) return mentioned[0];
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 列出这个工作区里所有角色的展示名(slug → 展示名)。
|
|
146
|
+
*
|
|
147
|
+
* 只当**展示层**用:展示名取自角色文件,而那份文件模型能写 —— 一个角色可以自称
|
|
148
|
+
* 「用户」。所以这张表不许用来做判断(路由、权限、归属),只用来把 slug 渲染得好看。
|
|
149
|
+
* 判断一律用 slug 本身(harness 盖的章)。
|
|
150
|
+
*/
|
|
151
|
+
export async function listRoleNames(workspaceRoot) {
|
|
152
|
+
const out = new Map();
|
|
153
|
+
if (!workspaceRoot) return out;
|
|
154
|
+
// 旧式一角一定义(会话启动前就存在的 .claude/agents/rp-*.md):仍然认,
|
|
155
|
+
// 但演员位本身(rp-actor / rp-narrator)是位置不是人,不进名册。
|
|
156
|
+
let names;
|
|
157
|
+
try { names = await fs.readdir(path.join(workspaceRoot, AGENTS_DIR)); } catch { names = []; }
|
|
158
|
+
for (const n of names) {
|
|
159
|
+
if (!n.endsWith('.md')) continue;
|
|
160
|
+
const slug = n.slice(0, -3);
|
|
161
|
+
if (!isResidentRole(slug) || isSlotType(slug)) continue;
|
|
162
|
+
const card = await readRoleCard(workspaceRoot, slug);
|
|
163
|
+
// 保留字闸已下沉到 readRoleCard 出口(08-28)—— 这里拿到的已是洗过的名字。
|
|
164
|
+
if (card?.displayName) out.set(slug, card.displayName);
|
|
165
|
+
}
|
|
166
|
+
// 登记表(08-28 演员位重构的主源):同 slug 时登记表赢 —— cast_role 是正门。
|
|
167
|
+
// 登记表也是模型可写的(.nd 在工作区里),展示名同样只当展示层、过保留字闸。
|
|
168
|
+
const reg = await readCastRegistry(workspaceRoot);
|
|
169
|
+
for (const [slug, r] of Object.entries(reg.roles)) {
|
|
170
|
+
if (!isResidentRole(slug) || isSlotType(slug)) continue;
|
|
171
|
+
const label = safeRoleLabel(slug, r?.name);
|
|
172
|
+
if (label) out.set(slug, label);
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* engine/agent/rp-mode.js —— 「RP 编排机械开不开」的唯一谓词(2026-08-28 归一)
|
|
3
|
+
*
|
|
4
|
+
* 此前这个判断散在三处、口径不一:stage-broadcast 判 mode==='rp',scene.js 和
|
|
5
|
+
* roles.js 不判 —— design 项目手动 cast_role 之后进入「半条腿」状态:say 能开轮、
|
|
6
|
+
* onRoleWait 能推进,广播却整机不转,没有任何一处报错。
|
|
7
|
+
*
|
|
8
|
+
* 归一口径(08-28):**编排机械(台上广播 / 轮次机 / set_scene)只在 rp 模式活**;
|
|
9
|
+
* 直投通道(say 私语、cue_role、SendMessage 召回)任何模式都通 —— design 项目里
|
|
10
|
+
* 轻度 RP(blackboard-rp skill + cast_role)照样能跟角色说话,只是没有机器替它转。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { getProject } from '../../projects/store.js';
|
|
14
|
+
|
|
15
|
+
export function isRpProject(projectId) {
|
|
16
|
+
return !!projectId && (getProject(projectId)?.mode || 'design') === 'rp';
|
|
17
|
+
}
|
|
@@ -31,6 +31,7 @@ import { AgentContext, freshTurnCounters } from './context.js';
|
|
|
31
31
|
import { Events } from './events.js';
|
|
32
32
|
import { createRun, markRunStarted, markRunSucceeded, markRunFailed, mergeRunMetadata, setRunMetrics, setRunModelUsage } from '../runs/store.js';
|
|
33
33
|
import { getProject } from '../../projects/store.js';
|
|
34
|
+
import { modeSkillsFor } from '../mcp/mode-profile.js';
|
|
34
35
|
import { randomUUID } from 'node:crypto';
|
|
35
36
|
import {
|
|
36
37
|
registerQuerySession,
|
|
@@ -47,6 +48,7 @@ import {
|
|
|
47
48
|
} from '../runs/active-runs.js';
|
|
48
49
|
import {
|
|
49
50
|
promoteNextPendingRunId, claimRunByUuid, releaseCurrentTurnRunId, getPendingRunCount,
|
|
51
|
+
isBackgroundTurnOpener,
|
|
50
52
|
closeMergedRun, publishQueueDepth, pushUnclaimedMessage,
|
|
51
53
|
} from '../runs/turn-relay.js';
|
|
52
54
|
// skill 起手文件拷贝已挪 hooks.js PreToolUse(Skill/Bash)(2026-07-27),
|
|
@@ -54,8 +56,12 @@ import {
|
|
|
54
56
|
import { loadInstalledPlugins } from './plugin-loader.js';
|
|
55
57
|
import { createHooks } from './hooks.js';
|
|
56
58
|
import { buildIsolationOptions, prepareAgentDirs, sandboxShimEnv } from './isolation.js';
|
|
59
|
+
import { MEMORY_EXTRA_GUIDELINES, mergeAgentSettings } from './memory-config.js';
|
|
57
60
|
import { createNodesignMcpServer } from '../mcp/index.js';
|
|
61
|
+
import { MCP_SERVER_NAME } from '../mcp/server-name.js';
|
|
58
62
|
import { assertInitContract } from './init-contract.js';
|
|
63
|
+
import { clearSessionFlights } from './subagent-flight.js'; import { clearStageStatus } from './stage-status.js'; import { clearBeatState } from './beat-state.js';
|
|
64
|
+
import { createRoleRoster } from './cast.js';
|
|
59
65
|
import { createAgents, resolveDefaultFastModel } from '../agents/index.js';
|
|
60
66
|
import { resolveSdkSpoofModel, pickThinkingConfig, resolveModelRoute, isUncensoredModel } from './model-context.js';
|
|
61
67
|
import { resolveSessionModel } from './session-model.js';
|
|
@@ -118,11 +124,6 @@ import { levelFor } from '../../lib/moderation.js';
|
|
|
118
124
|
* 旁路事件**不算** —— 通知之后 SDK 不一定真的唤起模型,铸了 run 却等不来
|
|
119
125
|
* result 收尾就是僵尸 run。
|
|
120
126
|
*/
|
|
121
|
-
function isBackgroundTurnOpener(message) {
|
|
122
|
-
return message?.type === 'assistant'
|
|
123
|
-
|| message?.type === 'stream_event'
|
|
124
|
-
|| message?.type === 'user';
|
|
125
|
-
}
|
|
126
127
|
|
|
127
128
|
export async function runSession({
|
|
128
129
|
sessionId,
|
|
@@ -324,13 +325,29 @@ export async function runSession({
|
|
|
324
325
|
|
|
325
326
|
// MCP server 实例落变量:开局契约自检要从**传给 query 的同一个实例**上取预期
|
|
326
327
|
// 工具名(server.toolNames,见 mcp/index.js)——不另立第二份清单。
|
|
327
|
-
|
|
328
|
-
|
|
328
|
+
// 常驻角色名册:一会话一份,hooks 与 MCP 工具共用同一引用(见 cast.js createRoleRoster)
|
|
329
|
+
const roleRoster = createRoleRoster();
|
|
330
|
+
// 项目模式(08-27):启动时读一次(切换下个会话生效),同一读数喂工具面(mode-profile.js)和提示词面(nd:mode 分区),两面不岔开
|
|
331
|
+
const projectMode = (projectId ? getProject(projectId)?.mode : null) || 'design';
|
|
332
|
+
const nodesignServer = createNodesignMcpServer({ workspaceRoot: wsRoot, sharedRoot, projectId, sessionId, ctx: sharedCtx, roleRoster, projectMode });
|
|
329
333
|
// npm 缓存 + 沙盒可写 tmp($TMPDIR / pip 缓存):细节与教训见 isolation.js
|
|
330
334
|
const agentDirs = await prepareAgentDirs({ dataRoot: PROJECTS_DATA_ROOT, projectId, sessionId });
|
|
331
335
|
|
|
336
|
+
// ⛔ 服务器自己的运行姿态不许漏进 agent 环境(08-24 案):
|
|
337
|
+
// - NODE_ENV=production(pm2 注的)会让 agent 沙盒里的 npm install 静默跳过
|
|
338
|
+
// devDependencies —— 返回 0 还报 "up to date",vite/typescript 根本没装上,
|
|
339
|
+
// 构建道当场断。npm_config_production / npm_config_omit 是同一开关的旁路。
|
|
340
|
+
// - PWD 是 pm2 进程的 cwd(仓库根);spawn({cwd}) 不更新它,bash 会自校正但
|
|
341
|
+
// python/node/构建工具读 $PWD 拿到的就是错目录 ——"cwd 被重置到父目录"的
|
|
342
|
+
// 另一半病根。下面显式钉成 cwdRoot。
|
|
343
|
+
const {
|
|
344
|
+
NODE_ENV: _dropNodeEnv, npm_config_production: _dropNpmProd,
|
|
345
|
+
npm_config_omit: _dropNpmOmit, OLDPWD: _dropOldpwd,
|
|
346
|
+
...inheritedEnv
|
|
347
|
+
} = process.env;
|
|
332
348
|
const sdkEnv = {
|
|
333
|
-
...
|
|
349
|
+
...inheritedEnv,
|
|
350
|
+
PWD: cwdRoot,
|
|
334
351
|
ANTHROPIC_BASE_URL: baseUrlForBinary,
|
|
335
352
|
// 订阅模型:apiKeyForBinary = process.env 原值(通常 undefined)——binary 见到
|
|
336
353
|
// ANTHROPIC_API_KEY 会弃用 ~/.claude 订阅 OAuth,所以订阅路绝不能注入。
|
|
@@ -341,6 +358,8 @@ export async function runSession({
|
|
|
341
358
|
// auto-memory 强制开启分支(binary gate:DISABLE 置 falsy 值 = force on,
|
|
342
359
|
// 绕过 CLAUDE_CODE_SIMPLE 等后置门;前置门 U$/zl 若拦住则此招无效 → 走自建 B 计划)
|
|
343
360
|
CLAUDE_CODE_DISABLE_AUTO_MEMORY: '0',
|
|
361
|
+
// 记忆指导追加段(保留 SDK 原合同只追加产品口径,理由见 memory-config.js)
|
|
362
|
+
CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES: MEMORY_EXTRA_GUIDELINES,
|
|
344
363
|
// 工具搜索:非 alwaysLoad 的 MCP 工具延迟加载(省 ~25-30k 常驻 schema tokens),
|
|
345
364
|
// agent 用 ToolSearch 按需取。白名单见 mcp/index.js ALWAYS_LOAD_TOOLS
|
|
346
365
|
ENABLE_TOOL_SEARCH: 'true',
|
|
@@ -405,8 +424,8 @@ export async function runSession({
|
|
|
405
424
|
systemPrompt: {
|
|
406
425
|
type: 'preset',
|
|
407
426
|
preset: 'claude_code',
|
|
408
|
-
// 成人段随项目 owner 的外审档联动(
|
|
409
|
-
//
|
|
427
|
+
// 成人段随项目 owner 的外审档联动(renderPrelude);无主项目落 tier.js 的 strict,绝不落 off。
|
|
428
|
+
// ⚠️ 08-30 默认档按通路拆成两栏(订阅 strict / 非订阅 off)→ 同一个项目换个模型跑成人段就换一档。
|
|
410
429
|
//
|
|
411
430
|
// uncensored 走模型表的标记位(model-context.isUncensoredModel),不在这里
|
|
412
431
|
// 判模型名 —— 那是模型属性,写在这儿就是给那张表开第二个真相源。为 true 的
|
|
@@ -416,13 +435,18 @@ export async function runSession({
|
|
|
416
435
|
const owner = projectId ? getUserById(getProject(projectId)?.ownerId) : null;
|
|
417
436
|
// 档位按模型通路取旋钮(08-20 两旋钮:订阅 / 本地与中转),model 是上面已解析的会话模型
|
|
418
437
|
// 无主项目 fail-closed 到 tier.js 的默认(strict),别落 loose(生产 08-21 实查 0 个无主项目)
|
|
438
|
+
// locale:项目 owner 在账号上记的界面语言。null(没表过态)时 renderPrelude
|
|
439
|
+
// 落中文默认 —— 服务端拿不到浏览器语言,这里不猜,猜错比给中文更糟。
|
|
419
440
|
return renderPrelude(owner ? levelFor(owner, model) : defaultModerationLevel(null), {
|
|
420
441
|
uncensored: isUncensoredModel(model),
|
|
442
|
+
locale: owner?.locale || undefined,
|
|
443
|
+
// 项目模式分区(nd:mode 标记块)—— 跟工具面用的是同一次读数,两面不会岔开
|
|
444
|
+
mode: projectMode,
|
|
421
445
|
});
|
|
422
446
|
})(),
|
|
423
447
|
},
|
|
424
448
|
plugins: installed.plugins,
|
|
425
|
-
skills: installed.skills,
|
|
449
|
+
skills: modeSkillsFor(installed.skills, projectMode), // 按模式筛+对账(拆件见 mode-profile)
|
|
426
450
|
|
|
427
451
|
// 2026-05-18 安全:关 inline shell execution。SDK 默认允许 skill / slash command 内
|
|
428
452
|
// inline shell 命令(Anthropic 标准 skill 协议的一部分,如 setup script)—— 但 NoDesign
|
|
@@ -530,22 +554,6 @@ export async function runSession({
|
|
|
530
554
|
persistSession: true,
|
|
531
555
|
settingSources: ['project'],
|
|
532
556
|
|
|
533
|
-
// skipWebFetchPreflight 来自 runtime/platform.js(gateway key 模式永远关)
|
|
534
|
-
// 详细因果链见 platform.js 的 skipWebFetchPreflight 注释
|
|
535
|
-
settings: {
|
|
536
|
-
skipWebFetchPreflight: platform.skipWebFetchPreflight,
|
|
537
|
-
// 项目 memory(2026-07-28 正式启用):骑 SDK 原生 auto-memory ——
|
|
538
|
-
// 二级索引(MEMORY.md 常驻 + 主题文件按需召回)、召回监督器
|
|
539
|
-
// (run.memory_recall 前端已渲染)、auto-dream 后台固化全由 SDK 包办。
|
|
540
|
-
// ⚠️ 目录必须显式指到项目共享区:默认按 cwd 派生,而 cwd 是会话级的,
|
|
541
|
-
// 不指定则每个会话一座记忆孤岛;指定后全项目所有会话共享一套。
|
|
542
|
-
...(sharedRoot ? {
|
|
543
|
-
autoMemoryEnabled: true,
|
|
544
|
-
// 落在 .claude/agent-memory/auto —— 跟前端记忆卡读的是同一棵树
|
|
545
|
-
// (2026-07-28 修:之前指到 shared/agent-memory/auto,SDK 记了前端也看不到)
|
|
546
|
-
autoMemoryDirectory: path.join(sharedRoot, '.claude', 'agent-memory', 'auto'),
|
|
547
|
-
} : {}),
|
|
548
|
-
},
|
|
549
557
|
|
|
550
558
|
includePartialMessages: STREAMING_ENABLED,
|
|
551
559
|
// 子代理时间轴(2026-07-28):转发子代理完整对话(text/thinking 也带
|
|
@@ -582,7 +590,19 @@ export async function runSession({
|
|
|
582
590
|
|
|
583
591
|
// 隔离两道闸(sandbox 管 Bash,permissions.deny 管 Read/Write 这类进程内工具)
|
|
584
592
|
// 全在 agent/isolation.js 里,改之前读那份文件头上的四条实测教训。
|
|
585
|
-
|
|
593
|
+
// ⛔ settings 必须与 isolationOptions.settings 深合并 —— 08-15 起两处 settings
|
|
594
|
+
// 静默互吞八天(autoMemory*/skipWebFetchPreflight 全丢)。合并+出口断言在
|
|
595
|
+
// memory-config.js 的 mergeAgentSettings。
|
|
596
|
+
...(() => {
|
|
597
|
+
const isolation = buildIsolationOptions({ cwdRoot, sharedRoot, ...agentDirs, dataRoot: PROJECTS_DATA_ROOT, env: sdkEnv });
|
|
598
|
+
return {
|
|
599
|
+
...isolation,
|
|
600
|
+
settings: mergeAgentSettings(isolation.settings, {
|
|
601
|
+
skipWebFetchPreflight: platform.skipWebFetchPreflight, sharedRoot,
|
|
602
|
+
crossSessionInbound: 'refuse', // 理由见 memory-config.js 那个键的注释
|
|
603
|
+
}),
|
|
604
|
+
};
|
|
605
|
+
})(),
|
|
586
606
|
|
|
587
607
|
toolConfig: {
|
|
588
608
|
askUserQuestion: { previewFormat: 'html' },
|
|
@@ -590,10 +610,12 @@ export async function runSession({
|
|
|
590
610
|
|
|
591
611
|
// projectId 要传:PostToolUseFailure 记问题库时用它标归属(漏传的话
|
|
592
612
|
// issues 行的 project_id 全是 null,事后追不回是哪个项目踩的)
|
|
593
|
-
hooks: createHooks({ ctx: sharedCtx, workspaceRoot: wsRoot, sharedRoot, sessionId, projectId }),
|
|
613
|
+
hooks: createHooks({ ctx: sharedCtx, workspaceRoot: wsRoot, sharedRoot, sessionId, projectId, roleRoster }),
|
|
594
614
|
|
|
595
615
|
mcpServers: {
|
|
596
|
-
|
|
616
|
+
// 键名 = 模型眼里的 `mcp__<名>__<工具>` 前缀,也是 isolation.js 那条
|
|
617
|
+
// permissions.allow 规则要匹配的名字 —— 两个读者,收在 mcp/server-name.js
|
|
618
|
+
[MCP_SERVER_NAME]: nodesignServer,
|
|
597
619
|
},
|
|
598
620
|
|
|
599
621
|
// mainModel = appModel ('kimi-k2.6'),sdkModel = SDK 视角 alias ('claude-opus-4-7[1m]')。
|
|
@@ -728,8 +750,8 @@ export async function runSession({
|
|
|
728
750
|
await commitWorkspace(projectId, sessionId, `turn ${status}: ${new Date().toISOString()}`, { author: 'agent' })
|
|
729
751
|
.catch((err) => console.warn('[git] turn commit failed:', err.message));
|
|
730
752
|
|
|
731
|
-
// 黑板草稿兜底落定(2026-08-23):agent 这一轮
|
|
732
|
-
// 物件,没调
|
|
753
|
+
// 黑板草稿兜底落定(2026-08-23):agent 这一轮 write_on_board 画图留下的 staging
|
|
754
|
+
// 物件,没调 edit_board commit 也在回合结束时变实 —— 草稿态是"正在画"的信号,
|
|
733
755
|
// 回合都结束了还半透明就是幽灵。取消/出错同样落定:画了就是画了。
|
|
734
756
|
if (projectId) {
|
|
735
757
|
try {
|
|
@@ -981,6 +1003,7 @@ export async function runSession({
|
|
|
981
1003
|
unregisterIngressSession(sessionId); // API 会话的 fast 兜底路由配对注销(订阅会话 noop)
|
|
982
1004
|
unregisterSessionNotice(sessionId, noticeHandler); // ingress → 会话的通知通道配对注销(按身份,别删掉新会话的)
|
|
983
1005
|
takeUpstreamTruncation(sessionId); // 半截标记跟会话同生命周期,别留
|
|
1006
|
+
clearSessionFlights(sessionId); clearStageStatus(projectId); clearBeatState(sessionId); // 在飞台账 + 台上一览 + 收尾闸记账,都跟会话同寿命
|
|
984
1007
|
// 带 token 比对:sid 若已被新 register 占用(closeQuerySession 已同步让位 +
|
|
985
1008
|
// 用户重发起新 runSession),unregister 看到 _token 不匹配 → noop 不误删新 entry
|
|
986
1009
|
unregisterQuerySession(sessionId, sessionToken);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* engine/agent/stage-status.js —— 「台上现在有谁、谁在写、谁刚写完」(2026-08-29)
|
|
3
|
+
*
|
|
4
|
+
* ## 它补的洞
|
|
5
|
+
*
|
|
6
|
+
* 主持人此前对角色的状态是半盲的:派发的返回、SendMessage 的返回、角色结束时的
|
|
7
|
+
* task-notification —— 全是**事件的瞬间**,没有任何地方能回答「现在怎么样了」。
|
|
8
|
+
* 2026-08-28 真会话里用户自己问出了这个洞(「感觉说书人是不是都需要重启一下?」),
|
|
9
|
+
* 而主持人手里最接近的工具 read_scene 只会背一遍它自己声明过的东西。
|
|
10
|
+
*
|
|
11
|
+
* ## 判据只用 harness 盖的章
|
|
12
|
+
*
|
|
13
|
+
* 在写 SubagentStart 到了、SubagentStop 还没到
|
|
14
|
+
* 刚写完 SubagentStop(`last_assistant_message` 是它收笔时说的话,SDK 现成给)
|
|
15
|
+
*
|
|
16
|
+
* 两个都不是模型能写的(feedback-verify-the-instrument)。这里**不记**「它写了什么
|
|
17
|
+
* 板书」—— 那份真相在画布上,每回合状态块本来就注入最近板书,重复一份就会有两份
|
|
18
|
+
* 各执一词的账。
|
|
19
|
+
*
|
|
20
|
+
* 生命周期:内存态、跟会话走,runSession 的 finally 里清。
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const stages = new Map(); // projectId → Map<slug, { writing, startedAt, finishedAt, lastLine }>
|
|
24
|
+
|
|
25
|
+
function tableFor(projectId) {
|
|
26
|
+
if (!stages.has(projectId)) stages.set(projectId, new Map());
|
|
27
|
+
return stages.get(projectId);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 角色起飞(SubagentStart,名字由别名桥解析出来) */
|
|
31
|
+
export function noteRoleStart(projectId, slug) {
|
|
32
|
+
if (!projectId || !slug) return;
|
|
33
|
+
const t = tableFor(projectId);
|
|
34
|
+
const prev = t.get(slug) || {};
|
|
35
|
+
t.set(slug, { ...prev, writing: true, startedAt: Date.now(), finishedAt: null });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 角色收笔(SubagentStop)。lastLine = 它这一轮最后说的话,截短存 */
|
|
39
|
+
export function noteRoleFinish(projectId, slug, lastLine = null) {
|
|
40
|
+
if (!projectId || !slug) return;
|
|
41
|
+
const t = tableFor(projectId);
|
|
42
|
+
const prev = t.get(slug) || {};
|
|
43
|
+
t.set(slug, {
|
|
44
|
+
...prev,
|
|
45
|
+
writing: false,
|
|
46
|
+
finishedAt: Date.now(),
|
|
47
|
+
lastLine: lastLine ? String(lastLine).replace(/\s+/g, ' ').trim().slice(0, 120) : (prev.lastLine || null),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 台上一览(给主持人的每回合状态块用)。
|
|
53
|
+
* @returns {Array<{slug, writing, lastLine, idleMs}>} 按「在写的排前面」排序
|
|
54
|
+
*/
|
|
55
|
+
export function stageStatus(projectId) {
|
|
56
|
+
const t = stages.get(projectId);
|
|
57
|
+
if (!t || !t.size) return [];
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
return [...t.entries()]
|
|
60
|
+
.map(([slug, v]) => ({
|
|
61
|
+
slug,
|
|
62
|
+
writing: !!v.writing,
|
|
63
|
+
lastLine: v.lastLine || null,
|
|
64
|
+
idleMs: v.finishedAt ? now - v.finishedAt : 0,
|
|
65
|
+
}))
|
|
66
|
+
.sort((a, b) => Number(b.writing) - Number(a.writing) || a.slug.localeCompare(b.slug));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** 会话收摊 */
|
|
70
|
+
export function clearStageStatus(projectId) { stages.delete(projectId); }
|
|
71
|
+
|
|
72
|
+
/** 测试用 */
|
|
73
|
+
export function _resetStageStatus() { stages.clear(); }
|