@xiaobuyu/nodesign 0.0.1
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/LICENSE +661 -0
- package/README.md +140 -0
- package/bin/nodesign.js +176 -0
- package/package.json +104 -0
- package/server/api/README.md +45 -0
- package/server/api/_guard.js +75 -0
- package/server/api/admin.js +187 -0
- package/server/api/assets/docx-page.js +103 -0
- package/server/api/assets/helpers.js +22 -0
- package/server/api/assets/notes.js +144 -0
- package/server/api/assets.js +896 -0
- package/server/api/board.js +57 -0
- package/server/api/browse.js +138 -0
- package/server/api/canvas.js +444 -0
- package/server/api/chatai.js +324 -0
- package/server/api/exports/build-standalone.js +980 -0
- package/server/api/exports/cards.js +118 -0
- package/server/api/exports/docx-pdf.js +46 -0
- package/server/api/exports/export-page.js +142 -0
- package/server/api/exports/handoff.js +147 -0
- package/server/api/exports.js +690 -0
- package/server/api/instruction.js +75 -0
- package/server/api/local.js +115 -0
- package/server/api/me.js +116 -0
- package/server/api/memory.js +185 -0
- package/server/api/pending-changes.js +366 -0
- package/server/api/plugins.js +164 -0
- package/server/api/projects.js +220 -0
- package/server/api/publish.js +64 -0
- package/server/api/recent.js +73 -0
- package/server/api/sessions.js +589 -0
- package/server/api/skills.js +77 -0
- package/server/api/standalone-fit.js +168 -0
- package/server/api/turn-compose.js +180 -0
- package/server/api/turn-inflight.js +44 -0
- package/server/api/turn.js +669 -0
- package/server/auth/README.md +17 -0
- package/server/auth/middleware.js +138 -0
- package/server/auth/origin-guard.js +71 -0
- package/server/auth/session.js +108 -0
- package/server/auth/tier.js +113 -0
- package/server/auth/users-store.js +348 -0
- package/server/edit/README.md +29 -0
- package/server/engine/README.md +113 -0
- package/server/engine/agent/agent-shared.js +550 -0
- package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -0
- package/server/engine/agent/auto-mode-rules.js +78 -0
- package/server/engine/agent/context.js +418 -0
- package/server/engine/agent/events.js +464 -0
- package/server/engine/agent/hooks/canvas-validate.js +199 -0
- package/server/engine/agent/hooks/failure.js +120 -0
- package/server/engine/agent/hooks/file-events.js +75 -0
- package/server/engine/agent/hooks/lifecycle.js +233 -0
- package/server/engine/agent/hooks/post-canvas-focus.js +80 -0
- package/server/engine/agent/hooks/post-guidance.js +206 -0
- package/server/engine/agent/hooks/post-subagent-report.js +104 -0
- package/server/engine/agent/hooks/post-trim.js +58 -0
- package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -0
- package/server/engine/agent/hooks/pre-defaults.js +84 -0
- package/server/engine/agent/hooks/pre-injectors.js +277 -0
- package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -0
- package/server/engine/agent/hooks/pre-starter-files.js +61 -0
- package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -0
- package/server/engine/agent/hooks/site-validate.js +94 -0
- package/server/engine/agent/hooks/tool-prompts.js +31 -0
- package/server/engine/agent/hooks/turn-state-memory.js +56 -0
- package/server/engine/agent/hooks/user-prompt-submit.js +215 -0
- package/server/engine/agent/hooks.js +354 -0
- package/server/engine/agent/init-contract.js +69 -0
- package/server/engine/agent/isolation.js +159 -0
- package/server/engine/agent/model-context.js +359 -0
- package/server/engine/agent/model-table.js +292 -0
- package/server/engine/agent/plugin-loader.js +266 -0
- package/server/engine/agent/prompts/nodesign-prelude.md +336 -0
- package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -0
- package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -0
- package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -0
- package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -0
- package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -0
- package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -0
- package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -0
- package/server/engine/agent/prompts/tools/site-reference.md +106 -0
- package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -0
- package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -0
- package/server/engine/agent/session-loop.js +1024 -0
- package/server/engine/agent/session-model.js +164 -0
- package/server/engine/agent/skill.js +204 -0
- package/server/engine/agent/system-prompts.js +86 -0
- package/server/engine/agent/task-events.js +78 -0
- package/server/engine/agents/ds-extractor.md +132 -0
- package/server/engine/agents/explorer.md +190 -0
- package/server/engine/agents/index.js +230 -0
- package/server/engine/agents/schemas/design-system.json +156 -0
- package/server/engine/agents/schemas/tweak-schema.json +113 -0
- package/server/engine/agents/tweak-proposer.md +127 -0
- package/server/engine/agents/vision-checker.md +254 -0
- package/server/engine/browse/capture.js +422 -0
- package/server/engine/browse/card.js +137 -0
- package/server/engine/browse/handover.js +59 -0
- package/server/engine/browse/page-digest.js +119 -0
- package/server/engine/browse/refs.js +196 -0
- package/server/engine/browse/registry.js +307 -0
- package/server/engine/browse/screencast.js +170 -0
- package/server/engine/browse/state.js +121 -0
- package/server/engine/chatai/chat-log.js +79 -0
- package/server/engine/chatai/index.js +193 -0
- package/server/engine/chatai/openai-compat.js +152 -0
- package/server/engine/chatai/orchestrate.js +309 -0
- package/server/engine/chatai/perform.js +40 -0
- package/server/engine/chatai/summarize.js +79 -0
- package/server/engine/mcp/capability-gate.js +52 -0
- package/server/engine/mcp/index.js +339 -0
- package/server/engine/mcp/param-sanitizer.js +142 -0
- package/server/engine/mcp/tools/arrange-on-board.js +124 -0
- package/server/engine/mcp/tools/artifact-session.js +278 -0
- package/server/engine/mcp/tools/browse-computer.js +421 -0
- package/server/engine/mcp/tools/browse-find-batch.js +183 -0
- package/server/engine/mcp/tools/browse-screenshot.js +157 -0
- package/server/engine/mcp/tools/browse.js +480 -0
- package/server/engine/mcp/tools/build-docx.js +101 -0
- package/server/engine/mcp/tools/clear-pending-changes.js +99 -0
- package/server/engine/mcp/tools/create-on-board.js +155 -0
- package/server/engine/mcp/tools/crystallize-skill.js +168 -0
- package/server/engine/mcp/tools/deliver-files.js +0 -0
- package/server/engine/mcp/tools/explain-style.js +237 -0
- package/server/engine/mcp/tools/export-handoff.js +133 -0
- package/server/engine/mcp/tools/expose-tweaks.js +149 -0
- package/server/engine/mcp/tools/generate-image.js +842 -0
- package/server/engine/mcp/tools/get-computed-styles.js +164 -0
- package/server/engine/mcp/tools/get-pending-changes.js +205 -0
- package/server/engine/mcp/tools/h3box-ssh.js +84 -0
- package/server/engine/mcp/tools/helpers/acquire-page.js +82 -0
- package/server/engine/mcp/tools/helpers/motion-lab.js +554 -0
- package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -0
- package/server/engine/mcp/tools/helpers/perception-page.js +215 -0
- package/server/engine/mcp/tools/helpers/reference-download.js +103 -0
- package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -0
- package/server/engine/mcp/tools/helpers/rembg.js +273 -0
- package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -0
- package/server/engine/mcp/tools/highlight.js +68 -0
- package/server/engine/mcp/tools/list-pages.js +222 -0
- package/server/engine/mcp/tools/lookup-tags.js +59 -0
- package/server/engine/mcp/tools/navigate-to-page.js +60 -0
- package/server/engine/mcp/tools/organize-board.js +64 -0
- package/server/engine/mcp/tools/paint-still.js +434 -0
- package/server/engine/mcp/tools/pin-to-board.js +130 -0
- package/server/engine/mcp/tools/preview-deck.js +130 -0
- package/server/engine/mcp/tools/profile-scroll.js +305 -0
- package/server/engine/mcp/tools/publish-site.js +102 -0
- package/server/engine/mcp/tools/query-elements.js +195 -0
- package/server/engine/mcp/tools/read-board.js +103 -0
- package/server/engine/mcp/tools/read-document.js +93 -0
- package/server/engine/mcp/tools/read-page.js +264 -0
- package/server/engine/mcp/tools/read-tavern-json.js +143 -0
- package/server/engine/mcp/tools/record-decision.js +140 -0
- package/server/engine/mcp/tools/relate-on-board.js +110 -0
- package/server/engine/mcp/tools/remove-background.js +391 -0
- package/server/engine/mcp/tools/report-issue.js +115 -0
- package/server/engine/mcp/tools/roll-film.js +266 -0
- package/server/engine/mcp/tools/screenshot-docx.js +138 -0
- package/server/engine/mcp/tools/screenshot-url.js +195 -0
- package/server/engine/mcp/tools/screenshot.js +583 -0
- package/server/engine/mcp/tools/tier-gate.js +96 -0
- package/server/engine/mcp/tools/trace-motion.js +215 -0
- package/server/engine/mcp/tools/web-search.js +548 -0
- package/server/engine/motion/inventory.js +349 -0
- package/server/engine/perception/session.js +235 -0
- package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +192 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -0
- package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -0
- package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +162 -0
- package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -0
- package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -0
- package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +228 -0
- 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 +78 -0
- package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -0
- package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +323 -0
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -0
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -0
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -0
- package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -0
- package/server/engine/runs/active-runs.js +698 -0
- package/server/engine/runs/live-turn.js +252 -0
- package/server/engine/runs/store.js +364 -0
- package/server/engine/runs/turn-relay.js +217 -0
- package/server/engine/runtime/workspace.js +164 -0
- package/server/index.js +235 -0
- package/server/lib/artifact-file-path.js +80 -0
- package/server/lib/artifact-target.js +284 -0
- package/server/lib/asset-refs.js +239 -0
- package/server/lib/async-queue.js +104 -0
- package/server/lib/auto-relations.js +130 -0
- package/server/lib/binding-types.js +60 -0
- package/server/lib/board-kind-sizes.js +62 -0
- package/server/lib/board-relations.js +133 -0
- package/server/lib/browse-proxy.js +180 -0
- package/server/lib/canvas-id.js +36 -0
- package/server/lib/cover.js +227 -0
- package/server/lib/danbooru-tags.js +291 -0
- package/server/lib/doc-extract.js +156 -0
- package/server/lib/docx/build-from-source.js +260 -0
- package/server/lib/docx/build.js +490 -0
- package/server/lib/docx/dump-styles.js +287 -0
- package/server/lib/docx/fonts/nodesign-cjk.conf +164 -0
- package/server/lib/docx/merge-runs.js +129 -0
- package/server/lib/docx/numbering.js +218 -0
- package/server/lib/docx/order.js +152 -0
- package/server/lib/docx/rawzip.js +172 -0
- package/server/lib/docx/render.js +92 -0
- package/server/lib/docx/text-lint.js +225 -0
- package/server/lib/docx/tokens.js +335 -0
- package/server/lib/docx/units.js +29 -0
- package/server/lib/docx/xml.js +291 -0
- package/server/lib/docx-pages.js +187 -0
- package/server/lib/export-collect.js +289 -0
- package/server/lib/export-package.js +210 -0
- package/server/lib/html-srcset.js +145 -0
- package/server/lib/image-variant.js +460 -0
- package/server/lib/ingress/forward-openai-chat.js +305 -0
- package/server/lib/ingress/openai-chat.js +491 -0
- package/server/lib/ingress/session-notice.js +66 -0
- package/server/lib/ingress/session-routes.js +68 -0
- package/server/lib/ingress/slot-probe.js +130 -0
- package/server/lib/ingress/upstream-billing.js +52 -0
- package/server/lib/ingress/upstream-fail-streak.js +72 -0
- package/server/lib/ingress/upstream-truncation.js +48 -0
- package/server/lib/issues-store.js +182 -0
- package/server/lib/kinds/deck.js +72 -0
- package/server/lib/kinds/docx.js +283 -0
- package/server/lib/kinds/file-kinds.js +113 -0
- package/server/lib/kinds/index.js +202 -0
- package/server/lib/kinds/site.js +161 -0
- package/server/lib/model-ingress.js +543 -0
- package/server/lib/moderation.js +255 -0
- package/server/lib/notice-store.js +103 -0
- package/server/lib/plugin-install.js +104 -0
- package/server/lib/plugin-validator.js +721 -0
- package/server/lib/publish-store.js +115 -0
- package/server/lib/quick-summary.js +52 -0
- package/server/lib/quota.js +270 -0
- package/server/lib/rate-window.js +29 -0
- package/server/lib/reference-assets.js +92 -0
- package/server/lib/region-shot.js +135 -0
- package/server/lib/safe-path.js +73 -0
- package/server/lib/sdk-session.js +33 -0
- package/server/lib/showcase-store.js +92 -0
- package/server/lib/site-publish.js +465 -0
- package/server/lib/ssrf-guard.js +432 -0
- package/server/lib/task-scan.js +158 -0
- package/server/lib/tavern-json.js +154 -0
- package/server/lib/video-variant.js +237 -0
- package/server/lib/workspace-path.js +33 -0
- package/server/ops/fix-sdk-musl.mjs +40 -0
- package/server/ops/install-macos-fonts.sh +114 -0
- package/server/ops/macos-fonts.conf +336 -0
- package/server/ops/sandbox-shim/bwrap +57 -0
- package/server/projects/assets-summary.js +119 -0
- package/server/projects/auto-name.js +53 -0
- package/server/projects/board-store.js +640 -0
- package/server/projects/move-entry.js +103 -0
- package/server/projects/store.js +270 -0
- package/server/projects/ui-config.js +54 -0
- package/server/projects/workspace-templates.js +53 -0
- package/server/projects/workspace.js +1025 -0
- package/server/runtime/capabilities.js +153 -0
- package/server/runtime/local-config.js +177 -0
- package/server/runtime/local-env.js +88 -0
- package/server/runtime/platform.js +342 -0
- package/server/runtime/profile.js +72 -0
- package/server/services/rembg-launcher.js +313 -0
- package/server/services/rembg-service.py +334 -0
- package/server/shared/README.md +25 -0
- package/server/shared/deck.js +54 -0
- package/server/shared/time.js +49 -0
- package/server/style-pipeline/README.md +53 -0
- package/server/ws/broker.js +36 -0
- package/server/ws/browse-channel.js +177 -0
- package/server/ws/index.js +386 -0
- package/web/README.md +89 -0
- package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +11 -0
- package/web/dist/assets/DeckWindow-4G0xX7P3.js +16 -0
- package/web/dist/assets/DocxWindow-BXQsoPkh.js +16 -0
- package/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/web/dist/assets/SiteWindow-D6eS-2gM.js +18 -0
- package/web/dist/assets/again-D8tfgKKO.webp +0 -0
- package/web/dist/assets/caveat-nd-CP6HlsNg.woff2 +0 -0
- package/web/dist/assets/clock-xJhvfYpS.webp +0 -0
- package/web/dist/assets/film-cut-BBKMwH7q.webp +0 -0
- package/web/dist/assets/film-still-BZ_DL9Tt.webp +0 -0
- package/web/dist/assets/index-C0KUf6Wd.css +1 -0
- package/web/dist/assets/index-Cbl3ATvp.js +2125 -0
- package/web/dist/assets/ink-desk-BRbxeDsR.webp +0 -0
- package/web/dist/assets/ink-night-Bz4BT3XH.webp +0 -0
- package/web/dist/assets/ink-portrait-C0DM4V8Q.webp +0 -0
- package/web/dist/assets/longcang-regular-C0uvTuKa.woff2 +0 -0
- 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-DTjo2geW.js +31 -0
- package/web/dist/assets/question-8H4HSqxh.webp +0 -0
- package/web/dist/assets/reject-BaPJqElj.webp +0 -0
- package/web/dist/assets/rotate-cw-LMT3x1Zj.js +11 -0
- package/web/dist/assets/rp-portrait-CbrGpew2.webp +0 -0
- package/web/dist/assets/rp-street-C4jVKjxx.webp +0 -0
- package/web/dist/assets/square-dashed-mouse-pointer-CBoAkfhi.js +11 -0
- package/web/dist/assets/tangle-DIxHVNuT.webp +0 -0
- package/web/dist/assets/thisone-BylZKglV.webp +0 -0
- package/web/dist/assets/thumb-DO1wfK1I.webp +0 -0
- package/web/dist/index.html +18 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse 行为引导族 —— 工具跑完后注 additionalContext 引导 agent 下一步
|
|
3
|
+
* (截图后自检 / 交付后告知 / 重生看门狗 / 风格锚落盘提醒)。
|
|
4
|
+
* (2026-08-14 可维护性行动:从 hooks.js 原样迁出,语义零改动)
|
|
5
|
+
*
|
|
6
|
+
* makePostToolUseRecordDecisionHandler — 已移除(git 历史可查)。
|
|
7
|
+
* 之前注 "继续做用户的当前任务" 跟 SDK preset 'claude_code' 教的内容重复,
|
|
8
|
+
* 让 agent 行为像被牵着走。删除后 agent 记完决策自己判断下一步,更接近
|
|
9
|
+
* SDK 默认行为。如未来观察到 agent 反复 record_decision 信号稀释,再考虑
|
|
10
|
+
* 加回(那时改成更精准的 anti-loop 检测,不是无脑注引导)。
|
|
11
|
+
*/
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* PostToolUse(screenshot_canvas) handler — agent 截图后引导它做视觉自检。
|
|
16
|
+
*
|
|
17
|
+
* input: PostToolUseHookInput (sdk.d.ts:1926)
|
|
18
|
+
* - tool_name / tool_input / tool_response / tool_use_id / duration_ms?
|
|
19
|
+
*
|
|
20
|
+
* output: PostToolUseHookSpecificOutput (sdk.d.ts:1938)
|
|
21
|
+
* - additionalContext?: string 注入下一轮 prompt
|
|
22
|
+
* - updatedToolOutput?: unknown 替换 tool 输出(不用)
|
|
23
|
+
*
|
|
24
|
+
* 注意:tool_response 里包含 image content block(base64)。agent 收到这条
|
|
25
|
+
* additionalContext 时已经能"看到"图(multimodal)—— 我们只是用文字提示
|
|
26
|
+
* 它接下来该做什么,不替换 image。
|
|
27
|
+
*
|
|
28
|
+
* 截图后的引导(2026-07-28;硬上限已撤销):
|
|
29
|
+
* 实测一个真实会话 9-33 张截图,每张 0.6 倍光栅后 ≈1k vision tokens,且
|
|
30
|
+
* **永久留在上下文里**(SDK 不能回改历史工具输出)。
|
|
31
|
+
*
|
|
32
|
+
* 曾经加过"整会话超 12 张就把图换成文字"的硬闸,撤掉了:那等于在 agent 检查
|
|
33
|
+
* 自己作品的时候把它的眼睛蒙上,而且蒙得悄无声息 —— 它只会以为"看起来 OK"。
|
|
34
|
+
* 省下来的几十 k 换不来这个代价。现在只报数、给建议,看不看由它自己判断;
|
|
35
|
+
* 真正的省是 0.6 倍光栅(每张 1.85k→1.0k)和压缩阈值,不是拦着不让看。
|
|
36
|
+
*/
|
|
37
|
+
const SCREENSHOT_BUSY_HINT_AT = 6; // 累到这个数开始提醒"大面积检查交给子代理"
|
|
38
|
+
|
|
39
|
+
export function makePostToolUseScreenshotHandler({ ctx }) {
|
|
40
|
+
let takenInSession = 0;
|
|
41
|
+
let takenInTurn = 0;
|
|
42
|
+
let lastTurn = -1;
|
|
43
|
+
// 不 emit run.screenshot_taken —— mcp/tools/screenshot.js:114 已经 emit
|
|
44
|
+
// 完整字段(sizeBytes / viewport / fullPage)。hook 只负责注 additionalContext
|
|
45
|
+
// 引导 agent 行为,业务事件由 MCP 工具内部负责。
|
|
46
|
+
return async (input, _toolUseId, _options) => {
|
|
47
|
+
const args = input?.tool_input || {};
|
|
48
|
+
const wasFullPage = args.fullPage === true;
|
|
49
|
+
const wasPerPage = typeof args.pageIndex === 'number';
|
|
50
|
+
const turn = ctx?.counters?.turns ?? 0;
|
|
51
|
+
if (turn !== lastTurn) { lastTurn = turn; takenInTurn = 0; }
|
|
52
|
+
takenInTurn += 1;
|
|
53
|
+
takenInSession += 1;
|
|
54
|
+
|
|
55
|
+
// fullPage 截图体积是 viewport 的 N×(N=页数),且会留在 context 多 turn
|
|
56
|
+
// 直到 autoCompact。push agent 下次整 deck 自检走 vision-checker subagent,
|
|
57
|
+
// subagent context 是隔离的,主线只收文字 critique,几 K vs 几百 K 的差距。
|
|
58
|
+
// 08-21 起这里只注**状态**(张数计数),不再每张截图注一段"点 3 个问题 / 派 vision-checker"
|
|
59
|
+
// 的说教 —— 那些住 prelude(做完之前先自己看 / 何时请外援),每张都说是 N 倍重复,而且
|
|
60
|
+
// "整 deck 自检请派 vision-checker"跟 prelude 定的触发条件(自检两轮用户仍不满意)矛盾。
|
|
61
|
+
void wasFullPage; void wasPerPage;
|
|
62
|
+
if (takenInSession < SCREENSHOT_BUSY_HINT_AT) return {};
|
|
63
|
+
return {
|
|
64
|
+
hookSpecificOutput: {
|
|
65
|
+
hookEventName: 'PostToolUse',
|
|
66
|
+
additionalContext:
|
|
67
|
+
`**上下文提示**:本轮 ${takenInTurn} 张、本会话累计 ${takenInSession} 张截图(每张约 1k tokens,进了上下文不会释放)。没有额度上限,该看就看。`,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* PostToolUse(export_handoff) handler — agent 打交付包后引导它告知用户路径。
|
|
75
|
+
*/
|
|
76
|
+
export function makePostToolUseExportHandler({ ctx: _ctx }) {
|
|
77
|
+
// 不 emit run.export_built —— mcp/tools/export-handoff.js:83 已经 emit
|
|
78
|
+
// 完整字段(format / path / sizeBytes / notes)。hook 从 tool_response 字符串
|
|
79
|
+
// substring 拼出来的 path 反而不准。hook 只负责注 additionalContext。
|
|
80
|
+
// 08-21:只在本 session 第一次打包时提醒一次;"收尾消息要自足"prelude 已讲,每次都注是重复
|
|
81
|
+
let said = false;
|
|
82
|
+
return async (_input, _toolUseId, _options) => {
|
|
83
|
+
if (said) return {};
|
|
84
|
+
said = true;
|
|
85
|
+
return {
|
|
86
|
+
hookSpecificOutput: {
|
|
87
|
+
hookEventName: 'PostToolUse',
|
|
88
|
+
additionalContext: '已生成交付包。把打包文件路径告诉用户(他从 UI 下载);同一个交付只打包一次。',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Phase Image-4:generate_image 重生看门狗 ──
|
|
95
|
+
//
|
|
96
|
+
// agent 同 outputName 调 generate_image 第 3 次起,注 systemMessage 建议:
|
|
97
|
+
// - 直接 chat 邀请用户在最近 2-3 张候选里选最好的(generate_image 已返 image
|
|
98
|
+
// content block,前端 chat 自动渲染,用户能直接看到)
|
|
99
|
+
// - 或 accept 当前最好的一版继续后续工作
|
|
100
|
+
// 防"agent 闷头改 5-10 次同 prompt 浪费 token + 用户也得不到更好版本"。
|
|
101
|
+
//
|
|
102
|
+
// 计数策略:
|
|
103
|
+
// - in-memory Map(key: outputName 去 timestamp 的 base,value: count)
|
|
104
|
+
// - 进程重启清;session 内累积;不区分 session(agent 进程同步 hook)
|
|
105
|
+
// - 阈值固定 3,可后续 env 化
|
|
106
|
+
//
|
|
107
|
+
// outputName base 提取:
|
|
108
|
+
// - "deck-cover-v1" → "deck-cover"(去掉 -v\d / -\d / -draft 等 suffix)
|
|
109
|
+
// - 同 base 不同 suffix 仍计入同一组(避免 agent 改名绕过 watchdog)
|
|
110
|
+
export function makePostToolUseGenerateImageRegenWatchdog() {
|
|
111
|
+
let nudgedOnce = false; // 08-21:邀请反馈的判断准则每 session 说一次就够(以前每个 base 组第一张都说)
|
|
112
|
+
const REGEN_THRESHOLD = 3;
|
|
113
|
+
const counts = new Map();
|
|
114
|
+
|
|
115
|
+
function extractBase(outputName) {
|
|
116
|
+
if (!outputName || typeof outputName !== 'string') return null;
|
|
117
|
+
return outputName
|
|
118
|
+
.replace(/-(?:v\d+|draft\d*|final|new|old|alt|\d+)$/i, '')
|
|
119
|
+
.replace(/[-_]+/g, '-')
|
|
120
|
+
.toLowerCase();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return async (input, _toolUseId, _options) => {
|
|
124
|
+
try {
|
|
125
|
+
const outputName = input?.tool_input?.outputName;
|
|
126
|
+
const base = extractBase(outputName);
|
|
127
|
+
if (!base) return {};
|
|
128
|
+
|
|
129
|
+
const next = (counts.get(base) || 0) + 1;
|
|
130
|
+
counts.set(base, next);
|
|
131
|
+
|
|
132
|
+
// 第 1 次:邀请反馈 nudge(按 SKILL.md 高代价 / 低代价节点判断)
|
|
133
|
+
if (next === 1 && !nudgedOnce) {
|
|
134
|
+
nudgedOnce = true;
|
|
135
|
+
return {
|
|
136
|
+
hookSpecificOutput: {
|
|
137
|
+
hookEventName: 'PostToolUse',
|
|
138
|
+
additionalContext:
|
|
139
|
+
`<system-reminder>\n[image-feedback-nudge] 这是本组(base="${base}")第 1 张图。\n\n`
|
|
140
|
+
+ `如果是 cover / portrait / 跨页 anchor 这类高代价节点(会被当 referenceImages 种子用于全 deck),可以在 chat 里自然邀请用户确认一下方向("这个 cover 当全 deck 视觉锚 OK 吗?"),收到反馈再做后续;section-divider / decoration / icon 这类单张可直接继续,工具 caption 已自动在 chat 显示。\n\n`
|
|
141
|
+
+ `判断诀窍:错了会不会导致全 deck 重生?会 → 邀请反馈;不会 → 继续。\n`
|
|
142
|
+
+ `</system-reminder>`,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (next < REGEN_THRESHOLD) return {};
|
|
148
|
+
|
|
149
|
+
// ≥ 3 次同 base outputName → 注 systemMessage
|
|
150
|
+
return {
|
|
151
|
+
hookSpecificOutput: {
|
|
152
|
+
hookEventName: 'PostToolUse',
|
|
153
|
+
additionalContext:
|
|
154
|
+
`<system-reminder>\n[regen-watchdog] 你已经对 outputName base "${base}" 调 generate_image ${next} 次。\n\n`
|
|
155
|
+
+ `如果是 conversational editing 微调("再暖一点 / 换日落色"),可以继续;\n`
|
|
156
|
+
+ `如果在反复尝试不同方向(每次 prompt 大改),**强烈建议**:\n`
|
|
157
|
+
+ ` 1. 直接在 chat 里邀请用户从最近 2-3 张候选选最好的(image content block 已自动在 chat 渲染)\n`
|
|
158
|
+
+ ` 2. 或 accept 当前最满意的那张,专心后续工作\n`
|
|
159
|
+
+ `理由:reroll 同 prompt 越多次 token 浪费越大,且用户也未必能在第 N 张里看出明显差别。\n`
|
|
160
|
+
+ `</system-reminder>`,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.warn(`[hooks/regen-watchdog] threw:`, err.message);
|
|
165
|
+
return {};
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* PostToolUse(record_decision) —— 锚定风格那一笔之后提醒落两处长期资产
|
|
172
|
+
* (2026-07-28,配合"记忆归 SDK / 品牌归我们 / 指引归用户"的分工)
|
|
173
|
+
*
|
|
174
|
+
* 只在这一笔上注、每 session 一次:
|
|
175
|
+
* - 品牌档案 `agent-memory/brand/memory.md` —— 前端 BrandCard 会把色板 /
|
|
176
|
+
* 字体渲染出来,是结构化资产,agent 不写就永远空着
|
|
177
|
+
* - 项目指引 `.claude/CLAUDE.md` —— SDK 每次 session 自动读进 system prompt,
|
|
178
|
+
* 但只有用户能决定要不要固化,所以是"问一句"不是"直接写"
|
|
179
|
+
*
|
|
180
|
+
* 通用偏好不在这儿管:那是 SDK 自动记忆的活(autoMemoryDirectory 已指到
|
|
181
|
+
* .claude/agent-memory/auto,前端记忆卡直接显示)。
|
|
182
|
+
*/
|
|
183
|
+
export function makePostToolUseStyleAnchorNudge({ sharedRoot }) {
|
|
184
|
+
let nudged = false;
|
|
185
|
+
const ANCHOR_RE = /(style-anchor|风格锚|锚定|视觉基调|palette|配色方案)/i;
|
|
186
|
+
return async (input, _toolUseId, _options) => {
|
|
187
|
+
if (nudged) return {};
|
|
188
|
+
const t = input?.tool_input || {};
|
|
189
|
+
const blob = `${t.topic || ''} ${t.title || ''} ${t.decision || ''} ${t.rationale || ''}`;
|
|
190
|
+
if (!ANCHOR_RE.test(blob)) return {};
|
|
191
|
+
nudged = true;
|
|
192
|
+
const guidePath = sharedRoot ? path.join(sharedRoot, '.claude', 'CLAUDE.md') : '.claude/CLAUDE.md';
|
|
193
|
+
return {
|
|
194
|
+
hookSpecificOutput: {
|
|
195
|
+
hookEventName: 'PostToolUse',
|
|
196
|
+
additionalContext:
|
|
197
|
+
'这一笔看起来是在锚定这个项目的视觉方向。顺手做两件事(都只做一次):\n'
|
|
198
|
+
+ '1. 把这版风格写进 `.claude/agent-memory/brand/memory.md`(色号 / 字体链 / 版式语言 / 动效预算;⚠️ 带 .claude/ 前缀,写错位置 BrandCard 读不到),'
|
|
199
|
+
+ '前端品牌档案卡会把色板和字体渲染出来给用户看,不写就一直空着。\n'
|
|
200
|
+
+ `2. 如果这次定下来的还包含**项目级约束**(不只这一个 deck 适用,比如"这个项目一律不用 emoji"),`
|
|
201
|
+
+ `在收尾时问用户一句要不要写进项目指引(${guidePath},SDK 每次 session 自动读它)。用户点头你再写。\n`
|
|
202
|
+
+ '用户的通用偏好不用你手动记,系统的自动记忆会管。',
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hooks/post-subagent-report.js — 子代理报告丢了的时候别整轮重跑(2026-08-18)
|
|
3
|
+
*
|
|
4
|
+
* ## 问题
|
|
5
|
+
*
|
|
6
|
+
* SDK 的子代理触到 `maxTurns` 时返回的是 SDKResultError,**那个类型没有 result
|
|
7
|
+
* 字段** —— 最后一条 assistant 消息(也就是报告本身)直接消失,主 agent 拿到的
|
|
8
|
+
* 是空的或者只有开场白。工作已经做完、token 已经烧掉,结果拿不到。
|
|
9
|
+
*
|
|
10
|
+
* 2026-08-05 真实记录:两次派 explorer,一次 59292 tokens / 16 轮工具调用 / 110s,
|
|
11
|
+
* 回来只有一句开场白;一次 31228 tokens / 17 轮,回来 "(returned no output.)"。
|
|
12
|
+
* 中间轮次少的那次正常。主 agent 的绕行只能是"拆小重派"或"放弃子代理" ——
|
|
13
|
+
* 等于放弃了子代理隔离上下文的全部价值,长研究任务反而不敢派了。
|
|
14
|
+
*
|
|
15
|
+
* ## 做法
|
|
16
|
+
*
|
|
17
|
+
* 我们改不了 SDK 那个类型,但 `task_notification` 上带着 **`output_file`** ——
|
|
18
|
+
* 子代理的完整转录 JSONL。所以:报告看起来是空的时候,把转录路径递给主 agent,
|
|
19
|
+
* 让它 Read 一遍把结论捞出来。**从"整轮白烧"变成"多读一个文件"。**
|
|
20
|
+
*
|
|
21
|
+
* ⚠️ 判据故意保守(只在摘要短得不像报告时才提示):把这句话贴在每次正常的
|
|
22
|
+
* 子代理返回后面是噪音,而噪音会训练 agent 忽略提示。
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** tool_use_id → 最近一次 task_notification(agent-shared 在收到时写进来) */
|
|
26
|
+
const lastNotification = new Map();
|
|
27
|
+
const MAX_TRACKED = 200;
|
|
28
|
+
|
|
29
|
+
export function recordTaskNotification(msg) {
|
|
30
|
+
if (!msg?.tool_use_id) return;
|
|
31
|
+
lastNotification.set(msg.tool_use_id, {
|
|
32
|
+
status: msg.status,
|
|
33
|
+
summary: msg.summary || '',
|
|
34
|
+
outputFile: msg.output_file || null,
|
|
35
|
+
toolUses: msg.usage?.tool_uses ?? null,
|
|
36
|
+
tokens: msg.usage?.total_tokens ?? null,
|
|
37
|
+
});
|
|
38
|
+
// 别无限长:一个会话里子代理数量有限,但进程是长命的
|
|
39
|
+
if (lastNotification.size > MAX_TRACKED) {
|
|
40
|
+
const first = lastNotification.keys().next().value;
|
|
41
|
+
lastNotification.delete(first);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 已经消费过的 id(用来区分"没记上"和"记过又用掉了",见下面那段) */
|
|
46
|
+
const consumed = new Set();
|
|
47
|
+
let missing = 0;
|
|
48
|
+
|
|
49
|
+
/** 报告"看起来是空的"的判据 —— 短于这个就不像一份报告 */
|
|
50
|
+
const SUSPICIOUS_LEN = 200;
|
|
51
|
+
|
|
52
|
+
export function makePostToolUseSubagentReportRecovery() {
|
|
53
|
+
return async (input) => {
|
|
54
|
+
const id = input?.tool_use_id;
|
|
55
|
+
if (!id) return {};
|
|
56
|
+
const note = lastNotification.get(id);
|
|
57
|
+
if (!note) {
|
|
58
|
+
// ⚠️ 这条分支是「静默死」的经典形状:`recordTaskNotification` 必须**先于**
|
|
59
|
+
// 这个 hook 跑,否则整个 handler 什么都不做而且一声不响。这个仓库为
|
|
60
|
+
// 「hook 静默死」付过三次账(最贵一次 19 个会话 0 触发、38k tokens 白跑),
|
|
61
|
+
// 所以宁可留一行日志:真出问题时它会成堆出现,而不是一片安静。
|
|
62
|
+
// 正常也会走到这儿(同一个 id 第二次进来,note 已被一次性消费掉),
|
|
63
|
+
// 所以只在**从来没见过这个 id** 时才记。
|
|
64
|
+
if (!consumed.has(id)) {
|
|
65
|
+
missing += 1;
|
|
66
|
+
console.warn(`[subagent-report] tool_use_id ${id} 没有对应的 task_notification`
|
|
67
|
+
+ `(累计 ${missing} 次)—— 如果这个数字在涨,说明 recordTaskNotification`
|
|
68
|
+
+ ' 没有跑在这个 hook 之前,兜底其实是死的。');
|
|
69
|
+
}
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
consumed.add(id);
|
|
73
|
+
if (consumed.size > MAX_TRACKED) consumed.delete(consumed.values().next().value);
|
|
74
|
+
lastNotification.delete(id); // 一次性
|
|
75
|
+
|
|
76
|
+
const looksEmpty = note.summary.trim().length < SUSPICIOUS_LEN;
|
|
77
|
+
// 摘要有内容就什么都不说 —— 报告回来了,再贴一段提示是噪音,
|
|
78
|
+
// 而噪音会训练 agent 忽略提示。
|
|
79
|
+
if (!looksEmpty) return {};
|
|
80
|
+
const didRealWork = (note.toolUses ?? 0) >= 4 || (note.tokens ?? 0) >= 8000;
|
|
81
|
+
|
|
82
|
+
const lines = [
|
|
83
|
+
`⚠️ 这个子代理回来的内容只有 ${note.summary.trim().length} 个字符`
|
|
84
|
+
+ `(status=${note.status}${note.toolUses != null ? `,跑了 ${note.toolUses} 次工具调用` : ''}`
|
|
85
|
+
+ `${note.tokens != null ? `,${note.tokens} tokens` : ''})。`,
|
|
86
|
+
];
|
|
87
|
+
if (didRealWork) {
|
|
88
|
+
lines.push('它**确实干了活**,但最终报告没回传 —— SDK 的子代理触到轮次上限时'
|
|
89
|
+
+ '返回的错误类型不带 result 字段,最后那条消息会整个丢掉。');
|
|
90
|
+
}
|
|
91
|
+
if (note.outputFile) {
|
|
92
|
+
lines.push(`完整转录在 \`${note.outputFile}\`。**Read 它把结论捞出来,别整轮重派** ——`
|
|
93
|
+
+ 'token 已经烧掉了,重跑一遍只是再烧一次。转录是 JSONL,从后往前读最快。');
|
|
94
|
+
} else {
|
|
95
|
+
lines.push('没有转录路径可捞。要重派的话把任务拆小(工具调用轮次少的时候不会丢)。');
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
hookSpecificOutput: {
|
|
99
|
+
hookEventName: 'PostToolUse',
|
|
100
|
+
additionalContext: lines.join('\n'),
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse(Edit|Write) handler —— 干掉 tool_response.originalFile 防上下文累积。
|
|
3
|
+
* (2026-08-14 可维护性行动:从 hooks.js 原样迁出,语义零改动)
|
|
4
|
+
*
|
|
5
|
+
* 背景:
|
|
6
|
+
* FileEditOutput.originalFile / FileWriteOutput.originalFile (sdk-tools.d.ts:2270, 2328)
|
|
7
|
+
* 是完整原文件内容。canvas.html 25KB 一次 Edit 在 tool_result 里等于 6k tokens。
|
|
8
|
+
* 30 turn 累积 ≈ 180k tokens → 跟 Kimi 256k 上限挤爆(用户实测 418k 报错)。
|
|
9
|
+
*
|
|
10
|
+
* structuredPatch(diff 行)是模型理解改动所需的全部信息;oldString/newString
|
|
11
|
+
* 是模型自己刚才传的 input,本来就在上下文里。originalFile 对模型基本无用 —
|
|
12
|
+
* 要看完整文件后续再 Read 即可。
|
|
13
|
+
*
|
|
14
|
+
* 行为:
|
|
15
|
+
* updatedToolOutput 是 SDK 提供的"改写发给 model 的 tool_result"通道
|
|
16
|
+
* (sdk.d.ts:1944)。**只影响 model 视图**,jsonl 持久化仍是原 tool_response。
|
|
17
|
+
* 也就是 forkSession / 断线恢复看到的还是完整产物 — 不丢数据。
|
|
18
|
+
*
|
|
19
|
+
* 保留字段:filePath / oldString / newString / structuredPatch / type / gitDiff
|
|
20
|
+
* 清掉字段:originalFile(替换为 null,保持类型 string|null)
|
|
21
|
+
*
|
|
22
|
+
* 非 Edit/Write 的 tool_response 形态(如 input 不带 originalFile) noop。
|
|
23
|
+
*
|
|
24
|
+
* input: PostToolUseHookInput (sdk.d.ts:1926)
|
|
25
|
+
* output: PostToolUseHookSpecificOutput (sdk.d.ts:1938)
|
|
26
|
+
*/
|
|
27
|
+
export function makePostToolUseEditWriteTrimHandler({ ctx }) {
|
|
28
|
+
return async (input, _toolUseId, _options) => {
|
|
29
|
+
try {
|
|
30
|
+
const resp = input?.tool_response;
|
|
31
|
+
if (!resp || typeof resp !== 'object') return {};
|
|
32
|
+
if (!('originalFile' in resp)) return {};
|
|
33
|
+
const originalSize = typeof resp.originalFile === 'string' ? resp.originalFile.length : 0;
|
|
34
|
+
if (originalSize === 0) return {}; // 新建文件 originalFile 本就是 null
|
|
35
|
+
|
|
36
|
+
const trimmed = { ...resp, originalFile: null };
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
ctx.emit({
|
|
40
|
+
type: 'run.tool_response_trimmed',
|
|
41
|
+
tool: input?.tool_name || 'Edit/Write',
|
|
42
|
+
field: 'originalFile',
|
|
43
|
+
savedChars: originalSize,
|
|
44
|
+
});
|
|
45
|
+
} catch { /* emit fail-safe */ }
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
hookSpecificOutput: {
|
|
49
|
+
hookEventName: 'PostToolUse',
|
|
50
|
+
updatedToolOutput: trimmed,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.warn(`[hooks/PostToolUse Edit|Write trim] handler threw:`, err.message);
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreToolUse(Read|Edit|Write) —— 关系线邻域注入(2026-08-14 切片③)。
|
|
3
|
+
*
|
|
4
|
+
* agent 正要摸的文件身上如果连着线(用户手画的标注、改自/对照谱系),这一刻
|
|
5
|
+
* 就是它最相关的时刻。每个文件一个会话只注一次(线是慢变数据,重复注是噪音);
|
|
6
|
+
* fail-soft —— 注不上不能挡工具。
|
|
7
|
+
*
|
|
8
|
+
* UserPromptSubmit 的全图摘要截断后,这里做精确补充。
|
|
9
|
+
*/
|
|
10
|
+
import { fileNeighborhood } from '../../../lib/board-relations.js';
|
|
11
|
+
import { toWorkspaceRel } from '../../../lib/workspace-path.js';
|
|
12
|
+
|
|
13
|
+
export function makePreToolUseBoardNeighborhoodInjector({ workspaceRoot, projectId }) {
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
return async (input) => {
|
|
16
|
+
try {
|
|
17
|
+
if (!projectId) return {};
|
|
18
|
+
const fp = input?.tool_input?.file_path;
|
|
19
|
+
if (typeof fp !== 'string' || !fp) return {};
|
|
20
|
+
const rel = toWorkspaceRel(fp, workspaceRoot);
|
|
21
|
+
if (!rel || seen.has(rel)) return {};
|
|
22
|
+
seen.add(rel);
|
|
23
|
+
const brief = await fileNeighborhood(projectId, rel, { limit: 6 });
|
|
24
|
+
if (!brief) return {};
|
|
25
|
+
return {
|
|
26
|
+
hookSpecificOutput: {
|
|
27
|
+
hookEventName: 'PreToolUse',
|
|
28
|
+
permissionDecision: 'allow',
|
|
29
|
+
additionalContext: `画布上连着 ${rel} 的关系线:\n${brief}`,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
} catch { return {}; }
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreToolUse 默认值矫正族 —— 透明改 tool_input,agent 无感,结果更有用。
|
|
3
|
+
* (2026-08-14 可维护性行动:从 hooks.js 原样迁出,语义零改动)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* PreToolUse(Agent) 强制前台 —— 透明改 input,不 hard deny。
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ 2026-08-03 修:**默认值翻了面,这个 hook 之前形同虚设。**
|
|
11
|
+
*
|
|
12
|
+
* 老 SDK:`run_in_background` 不传 = 前台,所以只需拦 `=== true`。
|
|
13
|
+
* 新 SDK(sdk-tools.d.ts AgentInput 原文):"Agents run in the background by
|
|
14
|
+
* default; you will be notified when one completes. **Set to false** to run this
|
|
15
|
+
* agent synchronously when you need its result before continuing."
|
|
16
|
+
*
|
|
17
|
+
* 也就是说**不传 = 后台**。模型自然写法就是不传(探针实测 bg=undefined),于是:
|
|
18
|
+
* 主 agent 只拿到一句 "Async agent launched successfully",报告永远不回来。
|
|
19
|
+
* 真实事故:2026-08-03 一个 explorer 烧了 38k tokens / 20 次工具调用 / 108 秒查
|
|
20
|
+
* 完时局资料,主 agent 收到的 tool_result 里一个字都没有,只好自己重搜四轮,
|
|
21
|
+
* 还跟用户说了句"研究员跑完了但报告没回传到我这儿"。
|
|
22
|
+
*
|
|
23
|
+
* 所以判据从"等于 true 才改"改成"**不是显式 false 就补 false**"。
|
|
24
|
+
* 显式传了 false 的(模型自己知道要前台)原样放过,不重复改也不发提示。
|
|
25
|
+
*
|
|
26
|
+
* 为什么 NoDesign 一定要前台:创作的核心反馈环是 agent 看 explorer /
|
|
27
|
+
* vision-checker 传回的素材 URL 与 critique → 据此改产物 → 再自检。
|
|
28
|
+
* fire-and-forget 等于把这个环剪断,agent 拿不到结果只能盲写。
|
|
29
|
+
* forwardSubagentText 已开,前台等的时候用户看得见子代理实时进度,不会卡死。
|
|
30
|
+
*
|
|
31
|
+
* 兜底另有一层:DEFAULT_TOOL_ALLOWLIST 里挂了 `TaskOutput`,万一还是漏成后台
|
|
32
|
+
* (比如 isolation:'remote' 强制后台),主 agent 能凭 task_id 把报告捞回来。
|
|
33
|
+
*/
|
|
34
|
+
export function makePreToolUseAgentForceForegroundHandler() {
|
|
35
|
+
return async (input) => {
|
|
36
|
+
const t = input?.tool_input;
|
|
37
|
+
if (!t || typeof t !== 'object') return {};
|
|
38
|
+
// 显式前台,不动
|
|
39
|
+
if (t.run_in_background === false) return {};
|
|
40
|
+
return {
|
|
41
|
+
hookSpecificOutput: {
|
|
42
|
+
hookEventName: 'PreToolUse',
|
|
43
|
+
permissionDecision: 'allow',
|
|
44
|
+
updatedInput: { ...t, run_in_background: false },
|
|
45
|
+
// 一句事实就够:为什么要前台 prelude 硬规则里已经讲了,每次派遣重复一遍是 N 倍说教
|
|
46
|
+
additionalContext: '已改为前台执行(run_in_background: false),报告会在这次 tool_result 里回来。',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* PreToolUse(Grep) handler:把缺省 output_mode 改成 'content'。
|
|
54
|
+
*
|
|
55
|
+
* SDK Grep 工具默认 output_mode='files_with_matches' —— 只返回匹配到的文件名
|
|
56
|
+
* 列表,不返回行内容。Agent 拿到文件名后还得再 Read 一遍,多一个 turn,浪费
|
|
57
|
+
* tokens 和时延。NoDesign 设计场景下 agent grep 几乎都是想看实际文本(CSS
|
|
58
|
+
* 类名定义在哪、某个 token 怎么用),'content' 是更合理的默认。
|
|
59
|
+
*
|
|
60
|
+
* 拦截规则:
|
|
61
|
+
* - 没传 output_mode 或传了空字符串 → updatedInput 改成 'content'
|
|
62
|
+
* - 显式传 'files_with_matches' / 'count' → 不动(agent 知道自己在做什么)
|
|
63
|
+
*
|
|
64
|
+
* ⚠️ Grep 的输入改写只许有这一个 handler:两个 handler 各返回 updatedInput,
|
|
65
|
+
* 后跑的那个是拿原始 tool_input 拼的,会把前一个的改动抹掉(2026-08-15 演出
|
|
66
|
+
* 隐私闸想在这儿加排除 glob 时踩明白的,那版已按用户决定撤回)。
|
|
67
|
+
*
|
|
68
|
+
* 不发 additionalContext —— agent 不需要知道这个变换,行为对它透明,结果
|
|
69
|
+
* 直接更有用。
|
|
70
|
+
*/
|
|
71
|
+
export function makePreToolUseGrepContentDefaultHandler() {
|
|
72
|
+
return async (input) => {
|
|
73
|
+
const t = input?.tool_input;
|
|
74
|
+
if (!t || typeof t !== 'object') return {};
|
|
75
|
+
if (t.output_mode && t.output_mode !== '') return {};
|
|
76
|
+
return {
|
|
77
|
+
hookSpecificOutput: {
|
|
78
|
+
hookEventName: 'PreToolUse',
|
|
79
|
+
permissionDecision: 'allow',
|
|
80
|
+
updatedInput: { ...t, output_mode: 'content' },
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
}
|