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