@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
|
@@ -18,8 +18,11 @@
|
|
|
18
18
|
* - 自己出错就放行(fail-open)—— 闸崩了不该把整个会话堵死。
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
import fs from 'node:fs';
|
|
21
22
|
import os from 'node:os';
|
|
22
23
|
import path from 'node:path';
|
|
24
|
+
import { CHALK_DIR } from '../../../lib/chalk.js';
|
|
25
|
+
import { ROLE_SLUG_RE } from '../cast.js';
|
|
23
26
|
|
|
24
27
|
const TARGET_FIELDS = ['file_path', 'path', 'notebook_path'];
|
|
25
28
|
const WRITE_TOOLS = new Set(['Write', 'Edit', 'MultiEdit', 'NotebookEdit']);
|
|
@@ -46,6 +49,36 @@ export function checkWorkspaceScope(toolInput, { workspaceRoot, dataRoot, toolNa
|
|
|
46
49
|
const v = toolInput?.[field];
|
|
47
50
|
if (typeof v !== 'string' || !v) continue;
|
|
48
51
|
const abs = path.resolve(ws, v); // 相对路径按工作区解析
|
|
52
|
+
// ⛔ 角色文件是**判据本身**,不许模型手写(2026-08-26 fable 验收)。
|
|
53
|
+
//
|
|
54
|
+
// 派发闸靠读 `.claude/agents/<slug>.md` 的 tools 行决定放不放行,可那份文件
|
|
55
|
+
// 模型自己能改 —— 于是闸校验的和 CLI 真正使用的**不是同一份内容**:
|
|
56
|
+
// ① TOCTOU:写宽版 → 结束回合(CLI 缓存了宽版)→ 覆写成窄版 → 同回合派发。
|
|
57
|
+
// 我们现读磁盘拿窄版放行,CLI 用回合开工的宽版快照 → 角色真拿到外发/花钱工具。
|
|
58
|
+
// ② 解析器分歧:重复的 `tools:` 键,我们的正则取第一处、CLI 的 YAML 取最后一处。
|
|
59
|
+
// 两条都不是对主 agent 提权(它本来就有这些工具),但这条线的正常用法是**导入
|
|
60
|
+
// 酒馆卡/世界书**——外来文本能借主 agent 的手造出一个拿着外发工具的角色。
|
|
61
|
+
// 修法只能是「让判据不可被它改」:正门 cast_role 走服务端 fs(不过这道闸),
|
|
62
|
+
// 模型这侧一律拒。⚠️ Bash 不归这道闸管,那半靠沙盒(isolation.js)。
|
|
63
|
+
if (isWrite && insideDir(abs, path.join(ws, '.claude', 'agents'))) {
|
|
64
|
+
return '角色文件不能手写 —— 用 cast_role。'
|
|
65
|
+
+ '那个目录里的文件同时是「这个角色能用哪些工具」的判据,'
|
|
66
|
+
+ '手写等于自己给自己发权限,所以一律拒绝(改已有角色也走 cast_role)。';
|
|
67
|
+
}
|
|
68
|
+
// 接续权的文件面(2026-08-27 编排):角色写的板书文件,主 agent 不许 Edit/Write。
|
|
69
|
+
// 板上那半(reply_to/chain)在 write-on-board 闸;这里堵的是「直接改文件正文」——
|
|
70
|
+
// 改别人的台词比接别人的话头更越界。作者看文件自己的 by: 章(harness 盖的,
|
|
71
|
+
// 角色没有 Write 工具伪造不了)。读不到/没有章 → 放行(fail-open,主 agent 自己的板书本来就能改)。
|
|
72
|
+
if (isWrite && insideDir(abs, path.join(ws, ...CHALK_DIR.split('/')))) {
|
|
73
|
+
try {
|
|
74
|
+
const m = fs.readFileSync(abs, 'utf8').slice(0, 400).match(/^by:\s*(\S+)/m);
|
|
75
|
+
// 判据统一走 ROLE_SLUG_RE(08-27 审计):真角色名必过它
|
|
76
|
+
if (m && ROLE_SLUG_RE.test(m[1])) {
|
|
77
|
+
return `这份板书是角色「${m[1]}」写的,它的话不是你的稿子 —— 不改、不润色、不代笔。`
|
|
78
|
+
+ '想让它改:把意见寄给它(SendMessage)或让用户直接跟它说。';
|
|
79
|
+
}
|
|
80
|
+
} catch { /* 新建/读不到 → 放行 */ }
|
|
81
|
+
}
|
|
49
82
|
if (insideDir(abs, ws)) continue; // 自己的工作区,放行
|
|
50
83
|
if (isWrite) {
|
|
51
84
|
if (tempDirs().some(d => insideDir(abs, d))) continue; // 临时文件随便写
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hooks/resident-role-lifecycle.js —— 常驻角色名册的退场那一侧(2026-08-26)
|
|
3
|
+
*
|
|
4
|
+
* 进场在 `pre-defaults.js`(PreToolUse 的 rp 分支 `roster.claim()`),退场在这里。
|
|
5
|
+
* 两边分开是因为事件族不同,不是因为职责不同 —— 改任何一边都要看另一边。
|
|
6
|
+
*
|
|
7
|
+
* ## 为什么必须有退场
|
|
8
|
+
*
|
|
9
|
+
* claim 发生在 **Agent 工具执行之前**(PreToolUse)。派发要是失败了(最现实的路径:
|
|
10
|
+
* 模型在角色的 `.claude/agents/<name>.md` 写出来之前就先派了这个还不存在的类型),
|
|
11
|
+
* 名字已经登记进名册,而进程里根本没有这个 agent。后果两条,都很迷惑:
|
|
12
|
+
*
|
|
13
|
+
* 1. **收件人闸的洞在这个窄窗口里重开**:闸看名册说「在场」→ 放行 → 而本会话没有
|
|
14
|
+
* 同名 in-process agent → CLI 的裸名解析回落到**全机范围** → 信可能落到别人的会话。
|
|
15
|
+
* (这正是 H1 的病,名册修好了主干,这里是它的补角。)
|
|
16
|
+
* 2. **这个名字整个会话被 brick**:重派被硬 deny 且文案谎报「已经在场了」,
|
|
17
|
+
* SendMessage 又叫不醒,模型只剩换名一条路。
|
|
18
|
+
*
|
|
19
|
+
* claim 留在 PreToolUse 不动(它挡住了同一回合并发派两次同名的竞态,这点比挪到
|
|
20
|
+
* PostToolUse 更值),失败时在这里补一次 release —— 进场乐观、退场兜底。
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { isResidentRole, isSlotType } from '../cast.js';
|
|
24
|
+
import { agentNameOf, noteAgentName, takePendingRoleName } from '../actor-trail.js';
|
|
25
|
+
import { noteRoleStart, noteRoleFinish } from '../stage-status.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* SubagentStart:把这个 agent_id 认成刚派出去的那个角色(2026-08-29)。
|
|
29
|
+
*
|
|
30
|
+
* hook input 只有 `agent_id` + `agent_type`(演员位),没有实例名 —— 名字在派发闸
|
|
31
|
+
* 那一刻就知道了,所以闸把它排进队列(actor-trail.notePendingRoleName),这里取队头
|
|
32
|
+
* 配对。**这是署名的正门**:harness 在起飞那一刻亲手给的 id,不解析任何模型可写的文本。
|
|
33
|
+
*
|
|
34
|
+
* 只认演员位起飞:干活型子代理(vision-checker 那类)不进别名表。
|
|
35
|
+
*/
|
|
36
|
+
export function makeSubagentStartRoleAlias({ projectId = null } = {}) {
|
|
37
|
+
return async function subagentStartRoleAlias(input) {
|
|
38
|
+
if (!isSlotType(input?.agent_type) || !input?.agent_id) return {};
|
|
39
|
+
const known = agentNameOf(input.agent_id); // 已经认过(唤醒重入)
|
|
40
|
+
const name = known || takePendingRoleName();
|
|
41
|
+
if (!name) return {};
|
|
42
|
+
if (!known) noteAgentName(input.agent_id, name);
|
|
43
|
+
noteRoleStart(projectId, name); // 台上一览:它开始写了
|
|
44
|
+
return {};
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function makePostToolUseFailureRoleRelease({ roster = null } = {}) {
|
|
49
|
+
return async (input) => {
|
|
50
|
+
if (!roster) return {};
|
|
51
|
+
// 演员位派发失败:登记的是实例名(tool_input.name),撤的也是它
|
|
52
|
+
const type = isSlotType(input?.tool_input?.subagent_type)
|
|
53
|
+
? input?.tool_input?.name
|
|
54
|
+
: input?.tool_input?.subagent_type;
|
|
55
|
+
if (!isResidentRole(type)) return {};
|
|
56
|
+
if (!roster.release(type)) return {};
|
|
57
|
+
|
|
58
|
+
// 「还没拾取到」跟「角色不存在」是两回事,指引也不同。前者**必须原样重派**,
|
|
59
|
+
// 而模型不会自己这么做:2026-08-26 探针里它撞了 not found 之后既没重试,
|
|
60
|
+
// 还回了一句「已派」谎报成功 —— 所以这句话要说得比"你可以再派一次"硬。
|
|
61
|
+
const notFound = /not found/i.test(String(input?.error || ''));
|
|
62
|
+
return {
|
|
63
|
+
hookSpecificOutput: {
|
|
64
|
+
hookEventName: 'PostToolUseFailure',
|
|
65
|
+
// nd:rp-prompt
|
|
66
|
+
additionalContext: notFound
|
|
67
|
+
? `「${type}」没派出来:CLI 还没拾取到它的角色文件(监视器有几秒防抖)。`
|
|
68
|
+
+ `**原样再派一次**,别改参数、别跳过、更不要说"已派"—— 它现在还没上场。`
|
|
69
|
+
: `「${type}」没派出来,已经把这个名字从在场名册里撤了 —— 你可以再派一次。`
|
|
70
|
+
+ `(如果是因为这个角色还不存在,先用 cast_role 把它的角色文件写出来。)`,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* SubagentStop:角色收笔了,记一笔进台上一览(2026-08-26 建;08-29 改写)。
|
|
78
|
+
*
|
|
79
|
+
* 08-29 之前这里做两件事:标「散场」+ 用 systemMessage 教主控怎么召回。两件都随
|
|
80
|
+
* 常驻/散场概念一起退役 —— 新回路里角色本来就写一段结束一轮,「结束」是正常节拍
|
|
81
|
+
* 不是事故。而且 systemMessage 这条路 08-28 真会话里查无痕迹(SDK 的 SubagentStop
|
|
82
|
+
* 只有 additionalContext,且按类型定义那是**发给子代理**的),主持人真正收到的是
|
|
83
|
+
* SDK 自带的 task-notification。所以这里不再试图跟主持人说话,只记账 ——
|
|
84
|
+
* 状态由每回合状态块的「台上」一节统一注入(user-prompt-submit.js)。
|
|
85
|
+
*
|
|
86
|
+
* ⛔ **不 release 名字**:角色收笔 ≠ 不存在了。它的转录还在,SendMessage 叫得回来;
|
|
87
|
+
* 把名字放回去等于允许重派,而重派会新起一个失忆的同名角色顶掉它。
|
|
88
|
+
*/
|
|
89
|
+
export function makeSubagentStopRoleNotice({ projectId = null } = {}) {
|
|
90
|
+
return async (input) => {
|
|
91
|
+
// 角色位实例:agent_type 是位置(rp-role),实例名走别名表。别名没学到就跳过
|
|
92
|
+
// (不知道是谁收的笔 —— 别把 'rp-role' 当角色记进台上一览)。
|
|
93
|
+
const name = isSlotType(input?.agent_type)
|
|
94
|
+
? agentNameOf(input?.agent_id)
|
|
95
|
+
: input?.agent_type;
|
|
96
|
+
if (!name || !isResidentRole(name) || isSlotType(name)) return {};
|
|
97
|
+
noteRoleFinish(projectId, name, input?.last_assistant_message || null);
|
|
98
|
+
return {};
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hooks/slot-alias.js —— 唤醒时重新认一遍角色的 agent_id(2026-08-28 建;08-29 收窄)
|
|
3
|
+
*
|
|
4
|
+
* 派发那条路 08-29 搬去 `SubagentStart`(harness 亲手给 id,不解析文本,见
|
|
5
|
+
* resident-role-lifecycle.js 的 makeSubagentStartRoleAlias)。这里只剩**唤醒**一条:
|
|
6
|
+
* `SendMessage({to:"rp-x"})` 的结果 JSON 带 `resumedAgentId`,服务器重启后别名表
|
|
7
|
+
* 清零、而角色还活着时,靠这一条重新学回来。
|
|
8
|
+
*
|
|
9
|
+
* 匹配用正则怼序列化文本而不是解构对象:tool_response 的形状(数组/对象/字符串)
|
|
10
|
+
* 随 SDK 版本漂,id 的形状(十六进制串)比包装稳定得多。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { isSlotType, ROLE_SLUG_RE } from '../cast.js';
|
|
14
|
+
import { noteAgentName } from '../actor-trail.js';
|
|
15
|
+
|
|
16
|
+
// 引号写成 \\?":结果 JSON 常作为字符串再被包一层(stringify 转义成 \"),两层都认
|
|
17
|
+
const RESUMED_RE = /\\?"resumedAgentId\\?"\s*:\s*\\?"([0-9a-f]{8,32})\\?"/;
|
|
18
|
+
|
|
19
|
+
export function makePostToolUseSlotAliasHandler() {
|
|
20
|
+
return async function slotAlias(input) {
|
|
21
|
+
const t = input?.tool_input;
|
|
22
|
+
if (!t || typeof t !== 'object') return {};
|
|
23
|
+
// 按名寄信的恢复结果 → 重新学(覆盖重启后别名表清零的窗口)
|
|
24
|
+
if (typeof t.to === 'string' && ROLE_SLUG_RE.test(t.to) && !isSlotType(t.to)) {
|
|
25
|
+
const m = RESUMED_RE.exec(JSON.stringify(input?.tool_response ?? ''));
|
|
26
|
+
if (m) noteAgentName(m[1], t.to);
|
|
27
|
+
}
|
|
28
|
+
return {};
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UserPromptSubmit handler — 每次用户输入前注入工作区**状态**(2026-08-21 重做)
|
|
3
3
|
*
|
|
4
|
-
* 注的是状态不是指令:cwd
|
|
4
|
+
* 注的是状态不是指令:cwd、素材清单、便利贴、画布关系线、产物清单。
|
|
5
5
|
* 怎么用这些东西(路径表、"先看有没有现成的素材"、工具怎么选)住 prelude,那是缓存的
|
|
6
6
|
* system prompt;每轮再说一遍是上下文里的 N 倍重复。
|
|
7
7
|
*
|
|
@@ -23,11 +23,21 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import fs from 'node:fs/promises';
|
|
25
25
|
import path from 'node:path';
|
|
26
|
-
import { readUiConfigFile } from '../../../projects/ui-config.js';
|
|
26
|
+
import { readUiConfigFile, withUiDefaults } from '../../../projects/ui-config.js';
|
|
27
27
|
import { readAssetsSummary } from '../../../projects/assets-summary.js';
|
|
28
28
|
import { relationsDigest } from '../../../lib/board-relations.js';
|
|
29
29
|
import { getViewpoint, describeViewpoint } from '../../../projects/viewpoint-store.js';
|
|
30
|
+
import { sheetSummaries, latestSheetId, currentSheet, sheetOfPoint } from '../../../lib/board-sheets.js';
|
|
31
|
+
import { currentSheetIdOf } from '../../../lib/sheet-state.js';
|
|
32
|
+
import { dirtyEvents, describeDirty, splitDirtyByCharge } from '../../../lib/board-dirty.js';
|
|
33
|
+
import { fitFor } from '../../../lib/sketch-layout.js';
|
|
34
|
+
import { readStateVars } from '../../../lib/state-table.js';
|
|
35
|
+
import { parseTriggers, evalTriggers, readLatch, writeLatch } from '../../../lib/state-triggers.js';
|
|
30
36
|
import { recentChalk, CHALK_DIR } from '../../../lib/chalk.js';
|
|
37
|
+
import { listRoleNames } from '../role-card.js';
|
|
38
|
+
import { stageStatus } from '../stage-status.js';
|
|
39
|
+
import { resetBeat } from '../beat-state.js';
|
|
40
|
+
import { roleLabel } from '../../mcp/actor.js';
|
|
31
41
|
import { readBoard } from '../../../projects/board-store.js';
|
|
32
42
|
import { estimateSizeOn } from '../../../lib/board-kind-sizes.js';
|
|
33
43
|
import { layerOf } from '../../../lib/canvas-id.js';
|
|
@@ -37,11 +47,6 @@ import {
|
|
|
37
47
|
} from '../../../lib/artifact-target.js';
|
|
38
48
|
import { getTurnMemory, setTurnMemory, fingerprint, diffItems } from './turn-state-memory.js';
|
|
39
49
|
|
|
40
|
-
/** UserPromptSubmit hook 读取 spec.json 时的最大字节数 */
|
|
41
|
-
const SPEC_JSON_MAX_BYTES = 200 * 1024;
|
|
42
|
-
/** spec.json.decisions 注入摘要时取最近 N 条 */
|
|
43
|
-
const SPEC_DECISIONS_TAIL = 5;
|
|
44
|
-
|
|
45
50
|
/** PDF/Office 文档的解法(系统自带工具;python 包没装且装不上 —— 08-19 上报实锤)。只在首次出现二进制文档时说一遍。 */
|
|
46
51
|
const BINARY_DOC_HINT = 'PDF / PPTX / DOCX / XLSX 直接 Read 拿不到结构化内容(二进制或 zip 包)。用系统自带的工具解'
|
|
47
52
|
+ '(pdfplumber/PyPDF2/python-docx/openpyxl 这些 python 包**没装且装不上**,别试):'
|
|
@@ -72,25 +77,7 @@ async function collectSections({ workspaceRoot, sessionId, projectId }) {
|
|
|
72
77
|
}
|
|
73
78
|
} catch { /* 素材读不到就沉默 */ }
|
|
74
79
|
|
|
75
|
-
// spec.json
|
|
76
|
-
try {
|
|
77
|
-
const specPath = path.join(workspaceRoot, 'spec.json');
|
|
78
|
-
const stat = await fs.stat(specPath);
|
|
79
|
-
if (stat.size <= SPEC_JSON_MAX_BYTES) {
|
|
80
|
-
const spec = JSON.parse(await fs.readFile(specPath, 'utf8'));
|
|
81
|
-
const decisions = Array.isArray(spec?.decisions) ? spec.decisions : [];
|
|
82
|
-
if (decisions.length > 0) {
|
|
83
|
-
const recent = decisions.slice(-SPEC_DECISIONS_TAIL);
|
|
84
|
-
const lines = recent.map((d, i) => {
|
|
85
|
-
const idx = decisions.length - recent.length + i + 1;
|
|
86
|
-
const title = (d?.title || '(无标题)').slice(0, 80);
|
|
87
|
-
const rationale = (d?.rationale || '').slice(0, 200);
|
|
88
|
-
return ` ${idx}. ${title}${rationale ? ` — ${rationale}` : ''}`;
|
|
89
|
-
}).join('\n');
|
|
90
|
-
sections.push({ key: 'decisions', title: '旧决策档案', text: `旧决策档案(spec.json 遗产,共 ${decisions.length} 条,最近 ${recent.length} 条;新决策一律走 record_decision → 任务便利贴):\n${lines}` });
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
} catch { /* spec.json 不存在 / 解析失败:noop */ }
|
|
80
|
+
// (spec.json 决策档案注入 2026-08-24 拆除:决策体系退役,长期事实走 CLAUDE.md/记忆)
|
|
94
81
|
|
|
95
82
|
// 便利贴:metadata-not-content,只列文件和首行标题
|
|
96
83
|
try {
|
|
@@ -128,13 +115,99 @@ async function collectSections({ workspaceRoot, sessionId, projectId }) {
|
|
|
128
115
|
w: vp.camera.w, h: vp.camera.h,
|
|
129
116
|
} } : vp;
|
|
130
117
|
const line = describeViewpoint(q, rects);
|
|
131
|
-
|
|
118
|
+
// 纸(2026-08-29 纸范式):报当前有哪些纸、剩多少地 —— 空位建议/走向学习
|
|
119
|
+
// 那套启发式随落位引擎一起退役,agent 的空间账本现在是纸的清单。
|
|
120
|
+
let spot = null;
|
|
121
|
+
// 暂存架(2026-08-30):机器到货的默认座。点名 + 三个安置动词,agent 不动它们就一直在
|
|
122
|
+
const shelfSeats = Object.entries(board.objects || {})
|
|
123
|
+
.filter(([, e]) => e?.seat === 'shelf' && !e.zone && Number.isFinite(e?.x))
|
|
124
|
+
.map(([id]) => id);
|
|
125
|
+
const pendingSeats = Array.isArray(board.pending) ? board.pending : [];
|
|
126
|
+
try {
|
|
127
|
+
const ss = sheetSummaries(board);
|
|
128
|
+
// 「最新」按登记时间取(latestSheetId 一份算法)—— 不是数组末项那张最下面的
|
|
129
|
+
const latest = latestSheetId(board);
|
|
130
|
+
const cur = ss.find(s => s.id === latest) || ss[ss.length - 1];
|
|
131
|
+
if (cur) {
|
|
132
|
+
// 报「还能装下什么」不是裸数字(2026-08-30 容量线):行数是模型真正用来
|
|
133
|
+
// 决策的量纲 —— 顺手教它超过余量别赌一发,flow 会替它拆
|
|
134
|
+
const slots = cur.slots?.length
|
|
135
|
+
? `;版位 ${cur.slots.map(s => `${s.name} 剩 ~${s.freeLines} 行`).join('、')}`
|
|
136
|
+
: ';这张没规划版位';
|
|
137
|
+
spot = `板上 ${ss.length} 张纸;当前 ${cur.id}${cur.title ? `(${cur.title})` : ''} 还剩 ~${cur.freeH}px 高的空地${slots}`
|
|
138
|
+
+ '(内容眼看要超余量就别赌一发 —— 自己多切几个空位分段填(plan/replan 省掉 at 即竖排接放),'
|
|
139
|
+
+ '或兜底 flow:true;写满**不会**自动翻页,自己 open_sheet 规划下一页;新话题也是 open_sheet)';
|
|
140
|
+
} else {
|
|
141
|
+
spot = '板上还没铺过纸 —— 第一笔 write_on_board 会自动铺一张在他视口下,或先 open_sheet';
|
|
142
|
+
}
|
|
143
|
+
const unplaced = [...shelfSeats, ...pendingSeats];
|
|
144
|
+
if (unplaced.length) {
|
|
145
|
+
spot += `;📦 ${unplaced.length} 件在暂存架等你安置(${unplaced.slice(0, 3).map(r => r.split('/').pop()).join('、')}${unplaced.length > 3 ? '…' : ''})`
|
|
146
|
+
+ ' —— 给它们规划的地:open_sheet{plan:[…{for:"artifacts"}]} 或逐件 pin_to_board{path,slot} / edit_board move。'
|
|
147
|
+
+ '架不是版面,东西留在架上就是没摆';
|
|
148
|
+
}
|
|
149
|
+
} catch { /* 纸读不出就不占字 */ }
|
|
150
|
+
const dirs = null;
|
|
151
|
+
/**
|
|
152
|
+
* 手机 / 平板档的版式(2026-08-28 移动端第二轮,用户拍板「一件 = 一屏,纵向单列」)。
|
|
153
|
+
*
|
|
154
|
+
* ⚠️ 这段是**事前**说的,不是靠 write_on_board 的返回文案事后纠正 —— 一块
|
|
155
|
+
* 1700 宽的板书在 390 的屏上已经写出来了,再告诉它"下次窄一点"没有意义,
|
|
156
|
+
* 用户这一轮拿到的就是要横着滑四屏的东西。
|
|
157
|
+
*
|
|
158
|
+
* ⭐ 同时几何那边也在执行(resolvePlacement 的 column:左右侧一律降级成正下方)。
|
|
159
|
+
* 两处都做不是重复:提示词管"它主动写多宽",几何管"它没照做时版面还读得了"。
|
|
160
|
+
*/
|
|
161
|
+
const fit = fitFor(vp);
|
|
162
|
+
const laneLine = fit.column
|
|
163
|
+
? `⚠️ 他在${fit.lane === 'phone' ? '手机' : '平板'}上(屏幕 ${fit.screen?.w}x${fit.screen?.h}px)。`
|
|
164
|
+
+ `版面规矩:**一件 = 一屏,纵向单列**。每件宽度 ≤${fit.w}(超了就要横向滑动,手机上没人受得了),`
|
|
165
|
+
+ `高度到 ${fit.h} 都行(竖着滚是手机上读长内容的天然姿势)。`
|
|
166
|
+
+ `接着写就往**正下方**接,别用 side:'right'/'left' 并排 —— 并排的第二件在他屏幕外。`
|
|
167
|
+
+ `宁可多拆几件竖着排,也别把一件写宽。`
|
|
168
|
+
: '';
|
|
169
|
+
if (line) sections.push({ key: 'viewpoint', title: '用户视点', text: `用户此刻在画布上:${line}。${spot ? `${spot}。` : ''}${dirs ? `${dirs}。` : ''}${laneLine}他说「这个/这里/这张」多半指选中的 > 开着的窗 > 视口里的东西;写板走纸(at = 纸内坐标)。要看画面细节才调 read_user_view。` });
|
|
132
170
|
}
|
|
133
171
|
} catch { /* 视点读不到就沉默 */ }
|
|
172
|
+
// 板上动静(2026-08-29 纸范式刀 4):用户拖动/搬家/擦组此前完全静默,agent 只能
|
|
173
|
+
// 撞运气。这里报最近半小时的动静(指纹只在有新动静时变,未变随「未变」行收拢);
|
|
174
|
+
// 回合中途的动静另有 PreToolUse 注入器在 agent 摸板前插话。
|
|
175
|
+
try {
|
|
176
|
+
const evts = dirtyEvents(projectId, 0);
|
|
177
|
+
if (evts.length) {
|
|
178
|
+
const line = describeDirty(evts, { limit: 6 });
|
|
179
|
+
// 有限负责制(刀⑤ 2026-08-30):动静按纸分拣 —— 挪进你当前纸的要接手处理,
|
|
180
|
+
// 挪去别处的是用户自留地,只报不催(板整个是用户随便动,别拔河)。
|
|
181
|
+
let charge = '';
|
|
182
|
+
try {
|
|
183
|
+
const b = await readBoard(projectId);
|
|
184
|
+
const curId = currentSheet(b, currentSheetIdOf(sessionId))?.id || null;
|
|
185
|
+
const sheetOf = (id) => {
|
|
186
|
+
const e = b.objects?.[id];
|
|
187
|
+
if (!e || !Number.isFinite(e.x)) return null;
|
|
188
|
+
const sz = estimateSizeOn(b, id, e);
|
|
189
|
+
return sheetOfPoint(b, { x: e.x + sz.w / 2, y: e.y + sz.h / 2 })?.id || null;
|
|
190
|
+
};
|
|
191
|
+
const { inMine, elsewhere } = splitDirtyByCharge(evts, { sheetOf, currentSheetId: curId });
|
|
192
|
+
if (inMine.length) {
|
|
193
|
+
charge = `\n⚠️ 其中 ${inMine.slice(0, 4).join('、')}${inMine.length > 4 ? ` 等 ${inMine.length} 件` : ''} 现在落在**你正在写的纸(${curId})**上:`
|
|
194
|
+
+ '这块工作区的版面归你管 —— 若它挡了你的版位/内容,用 edit_board 给它挪个合适的位置(挪要挪得讲理,别甩出用户视野);用户明说过要放那儿的除外。';
|
|
195
|
+
}
|
|
196
|
+
if (elsewhere.length && inMine.length) {
|
|
197
|
+
charge += `\n其余 ${elsewhere.length} 件在你的纸外 —— 那是用户自留地,不要去动。`;
|
|
198
|
+
} else if (elsewhere.length) {
|
|
199
|
+
charge = '\n这些都在你当前的纸外 —— 用户自留地,看在眼里就好,不要去动。';
|
|
200
|
+
}
|
|
201
|
+
} catch { /* 分拣失败就退回不分拣的报法 */ }
|
|
202
|
+
sections.push({ key: 'boardDirty', title: '板上动静', text:
|
|
203
|
+
`用户最近亲手动过板面:${line}。这些位置以现状为准 —— 摆放前先 read_board,别按你记忆里的旧位置来。${charge}` });
|
|
204
|
+
}
|
|
205
|
+
} catch { /* 动静读不到就沉默 */ }
|
|
206
|
+
|
|
134
207
|
try {
|
|
135
208
|
const digest = await relationsDigest(projectId, { limit: 12 });
|
|
136
209
|
if (digest) {
|
|
137
|
-
sections.push({ key: 'relations', title: '画布关系线', text: `画布关系线(用户和你手动画的连线,端点跟着改名走;语义看线上的词):\n${digest}\n 产出新东西后记得用
|
|
210
|
+
sections.push({ key: 'relations', title: '画布关系线', text: `画布关系线(用户和你手动画的连线,端点跟着改名走;语义看线上的词):\n${digest}\n 产出新东西后记得用 edit_board add_edge 把「改自/对照/接着/取材」画上去。` });
|
|
138
211
|
}
|
|
139
212
|
} catch { /* 板读不到就沉默 */ }
|
|
140
213
|
|
|
@@ -160,35 +233,121 @@ async function collectSections({ workspaceRoot, sessionId, projectId }) {
|
|
|
160
233
|
}
|
|
161
234
|
} catch { /* 扫不动就不说 */ }
|
|
162
235
|
|
|
163
|
-
//
|
|
164
|
-
//
|
|
236
|
+
// 最近板书(2026-08-23;08-24 记忆改版时被误删,同日修回):你/用户在画布上
|
|
237
|
+
// 说过的最近几句 —— 对话在板上,得记得板上说了什么
|
|
165
238
|
try {
|
|
166
|
-
const
|
|
239
|
+
const all = await recentChalk(workspaceRoot, { limit: 24 });
|
|
240
|
+
const roleNames = await listRoleNames(workspaceRoot);
|
|
241
|
+
// 前 8 条 + 保底:每个角色最新那条就算被 GM 自己的章节/状态板挤出前 8 也要在 ——
|
|
242
|
+
// GM 需要知道台上角色说了什么(08-28 用户拍板),漏了它就会旁白转述/代笔补戏。
|
|
243
|
+
const recent = all.slice(0, 8);
|
|
244
|
+
const seenRole = new Set(recent.map(c => c.by));
|
|
245
|
+
for (const c of all.slice(8)) {
|
|
246
|
+
if (c.by && c.by !== 'agent' && c.by !== 'user' && !seenRole.has(c.by)) {
|
|
247
|
+
recent.push(c); seenRole.add(c.by);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
167
250
|
if (recent.length) {
|
|
168
|
-
|
|
251
|
+
// 这段是注给**主 agent** 的,所以「你」= 主 agent;角色写的板书报它的名字,
|
|
252
|
+
// 否则主 agent 会把角色写的东西当成自己写的(RP 线里那是大部分板书)。
|
|
253
|
+
const lines = recent.map(c => ` ${c.path}(${c.by === 'user' ? '用户' : c.by && c.by !== 'agent' ? roleLabel(c.by, roleNames.get(c.by)) : '你'}${c.anchor ? `,关于 ${c.anchor}` : ''}${c.replyTo ? `,回应 ${c.replyTo.replace(`${CHALK_DIR}/`, '')}` : ''})「${c.first}」`);
|
|
169
254
|
sections.push({ key: 'chalk', title: '最近板书', text: `画布上最近的板书(${CHALK_DIR}/,新在前;正文 Read 文件):\n${lines.join('\n')}`, items: recent.map(c => c.path) });
|
|
170
255
|
}
|
|
171
256
|
} catch { /* 板书读不到就沉默 */ }
|
|
172
|
-
|
|
257
|
+
|
|
258
|
+
// 状态表(2026-08-30):板上那张 `| 键 | 值 |` 的现值,每轮开头端到手边 ——
|
|
259
|
+
// 演出里"会变、要记得"的东西(好感度/时间/线索)不该靠模型自己在上下文里背。
|
|
260
|
+
//
|
|
261
|
+
// ⚠️ 这一节**不给 items**,走整节 hash 比对(renderTurnState 的默认分支)。
|
|
262
|
+
// 给了 items 会被 diffItems 渲染成「新增 好感度=4;移除 好感度=3」—— 信息对、
|
|
263
|
+
// 话是错的(那个渲染器被 9 个节共用,为一张小表去改它不值)。而且对读者来说,
|
|
264
|
+
// 变了的时候看到**整表现值**比看到增量有用:它按现值行事,不按 delta 行事。
|
|
265
|
+
//
|
|
266
|
+
// ⛔ 「解析不出来」必须出声。collectSections 的房规是"采不到就不出现",对普通节
|
|
267
|
+
// 是对的,对这一节就是坑:表被 set_text / 用户手改写坏之后会静默消失,而写口闸
|
|
268
|
+
// 只守得住 set_vars 那一路(另外两路是合法的写入方)。所以 broken 一律推一节。
|
|
173
269
|
try {
|
|
174
|
-
const
|
|
175
|
-
if (
|
|
270
|
+
const st = await readStateVars(workspaceRoot);
|
|
271
|
+
if (st.state === 'ok' && st.rows.length) {
|
|
272
|
+
const table = st.rows.map(r => ` ${r.key} = ${r.value}`).join('\n');
|
|
273
|
+
const parts = [];
|
|
274
|
+
|
|
275
|
+
// 条件触发器(2026-08-30):求值点 = 注入点。只在这里求一次,命中就写进
|
|
276
|
+
// 这一轮的状态块 —— 不在 set_vars 里求值攒到下一轮,那样中间一次进程重启
|
|
277
|
+
// 就静默吞掉一次触发。沿状态落 .nd/(派生记账,丢了只是"上膛不击发")。
|
|
278
|
+
let trig = null;
|
|
279
|
+
try {
|
|
280
|
+
const parsed = parseTriggers(st.body || '');
|
|
281
|
+
if (parsed.triggers.length || parsed.errors.length) {
|
|
282
|
+
const { latch, fresh } = await readLatch(workspaceRoot);
|
|
283
|
+
trig = evalTriggers(parsed.triggers, st.rows, latch, { fresh });
|
|
284
|
+
try { await writeLatch(workspaceRoot, trig.latch); } catch (e) {
|
|
285
|
+
console.warn('[vars] 沿状态写不进去,下一轮可能重复触发:', e.message);
|
|
286
|
+
}
|
|
287
|
+
if (trig.fired.length) {
|
|
288
|
+
parts.push(`⚡ 这一拍有 ${trig.fired.length} 个条件命中了(你之前挂的):\n`
|
|
289
|
+
+ trig.fired.map(f => ` · ${f.message}\n (条件:${f.raw})`).join('\n'));
|
|
290
|
+
}
|
|
291
|
+
const roster = [];
|
|
292
|
+
if (trig.armed) roster.push(`${trig.armed} 条挂着`);
|
|
293
|
+
if (trig.retired) roster.push(`${trig.retired} 条已退休`);
|
|
294
|
+
if (roster.length) parts.push(` 触发器:${roster.join('、')}(声明在 ${st.rel} 的 \`\`\`nd:triggers 围栏里,删一行就是撤一条)`);
|
|
295
|
+
for (const e of [...parsed.errors, ...trig.errors]) {
|
|
296
|
+
parts.push(` ⚠️ 触发器写错了,这条一直不会响:${e}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} catch (e) { console.warn('[vars] 触发器求值失败:', e.message); }
|
|
300
|
+
|
|
301
|
+
parts.push(`状态表现值(${st.rel},共 ${st.rows.length} 格):\n${table}\n`
|
|
302
|
+
+ ` 改数字用 set_vars(只动那一格);改表的结构/加说明文字才用 edit_board 的 set_text。`);
|
|
303
|
+
sections.push({ key: 'vars', title: '状态表', text: parts.join('\n') });
|
|
304
|
+
} else if (st.state === 'broken') {
|
|
305
|
+
sections.push({
|
|
306
|
+
key: 'vars',
|
|
307
|
+
title: '状态表',
|
|
308
|
+
text: `⚠️ 状态表读不出来了:${st.why}\n`
|
|
309
|
+
+ ` 在修好之前 set_vars 会一直拒绝(它不会"尽力写",那只会把表写得更坏)。`
|
|
310
|
+
+ `${st.rel ? ` Read 一下 ${st.rel} 看看表被改成什么样了。` : ''}`,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
} catch { /* 状态表这一节自己不能变成故障源 */ }
|
|
314
|
+
|
|
315
|
+
// 台上(2026-08-29):角色现在的状态。判据全是 harness 盖的章(SubagentStart/Stop,
|
|
316
|
+
// 见 stage-status.js)—— 此前主持人对这件事是半盲的:只在角色**结束**时收到一条
|
|
317
|
+
// 通知,中间既不知道谁在写,也不知道谁写完在等它接。08-28 真会话里用户替它问出了
|
|
318
|
+
// 这个洞(「说书人是不是要重启一下?」),而主持人手上没有任何工具能回答。
|
|
319
|
+
try {
|
|
320
|
+
const st = stageStatus(projectId);
|
|
321
|
+
if (st.length) {
|
|
322
|
+
const names = await listRoleNames(workspaceRoot);
|
|
323
|
+
const mins = (ms) => Math.round(ms / 60000);
|
|
324
|
+
const lines = st.map((r) => {
|
|
325
|
+
const who = roleLabel(r.slug, names.get(r.slug));
|
|
326
|
+
if (r.writing) return ` ${who}(${r.slug})正在写`;
|
|
327
|
+
const idle = r.idleMs > 90000 ? `,${mins(r.idleMs)} 分钟没动了` : '';
|
|
328
|
+
return ` ${who}(${r.slug})写完了这一段${idle}${r.lastLine ? `,收笔时说:「${r.lastLine}」` : ''}`;
|
|
329
|
+
});
|
|
330
|
+
sections.push({ key: 'stage', title: '台上', text:
|
|
331
|
+
`这场故事里的角色:\n${lines.join('\n')}\n`
|
|
332
|
+
+ `写完的角色这一轮已经结束了 —— 要它接着演,SendMessage 寄给它的名字(当场醒,记得之前所有事);`
|
|
333
|
+
+ `不用重新派(重派会新起一个失忆的同名角色顶掉它)。` });
|
|
334
|
+
}
|
|
335
|
+
} catch { /* 台上读不到就沉默 */ }
|
|
336
|
+
|
|
337
|
+
// 黑板模式(2026-08-23;08-24 起默认开 —— 没写过这个键的按开算,显式 false 才算关)
|
|
338
|
+
try {
|
|
339
|
+
const cfg = withUiDefaults(await readUiConfigFile(workspaceRoot));
|
|
340
|
+
if (cfg.blackboard_mode === true) {
|
|
176
341
|
sections.push({ key: 'blackboard', title: '黑板模式', text:
|
|
177
|
-
'
|
|
178
|
-
+ '
|
|
342
|
+
'【黑板模式:开】用户此刻在画布上专注思考。这一轮默认这么做:先有纸再动笔(新话题 open_sheet);'
|
|
343
|
+
+ '想事情就画成图(write_on_board 给 nodes/edges,小改动用 edit_board 原地改别重画);'
|
|
344
|
+
+ '做完一件东西在它旁边写一条板书(near= 连线说明它说的是谁);'
|
|
179
345
|
+ '用户标注了板上的东西就接在那条下面回(reply_to=)。侧栏照常回复,但板上已经写的别大段重复。'
|
|
180
|
-
+ '
|
|
346
|
+
+ '尺寸守规范(一张纸 = 一屏、正文 md 起、一条板书说一件事);画完 look_at_board 看一眼再收。' });
|
|
181
347
|
}
|
|
182
348
|
} catch { /* 读失败:不注入 */ }
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (cfg) {
|
|
186
|
-
const on = cfg?.tweaks_mode_enabled !== false;
|
|
187
|
-
sections.push({ key: 'tweaks', title: 'Tweaks 开关', text: on
|
|
188
|
-
? '【Tweaks 模式:启用】用户偏好"可调产品"——deck 形态稳定后,建议调 expose_tweaks 暴露核心微调参数(颜色 / 字号 / 排版密度等)让用户拖滑杆即时改样式。'
|
|
189
|
-
: '【Tweaks 模式:禁用】用户在 toolbar 关闭了 Tweaks——这次跳过 expose_tweaks,按对话方式让用户提需求你来 Edit。已暴露的 controls 保留不动,不新增 / 不重 expose。' });
|
|
190
|
-
}
|
|
191
|
-
} catch { /* 读失败:默认行为,不注入 */ }
|
|
349
|
+
|
|
350
|
+
// (tweaks 开关注入 2026-08-24 随 expose_tweaks 暂退役一起摘除;工具升级后再回来)
|
|
192
351
|
|
|
193
352
|
return sections;
|
|
194
353
|
}
|
|
@@ -241,6 +400,8 @@ export function renderTurnState(sections, prev) {
|
|
|
241
400
|
|
|
242
401
|
export function makeUserPromptSubmitHandler({ ctx: _ctx, workspaceRoot, sessionId, projectId }) {
|
|
243
402
|
return async (_input, _toolUseId, _options) => {
|
|
403
|
+
// 新的一轮:收尾闸重新记账(判的是「这一轮」,见 beat-state.js)
|
|
404
|
+
resetBeat(sessionId);
|
|
244
405
|
try {
|
|
245
406
|
if (!workspaceRoot) return {};
|
|
246
407
|
const sections = await collectSections({ workspaceRoot, sessionId, projectId });
|