@zwbigi/ink-xy 0.1.2 → 0.1.4
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +2 -2
- package/.next/build-manifest.json +2 -2
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/api/inkos/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/install/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/search/route.js.nft.json +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +1 -1
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app-paths-manifest.json +2 -2
- package/.next/server/chunks/162.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/trace +4 -4
- package/.next/trace-build +1 -1
- package/bin/pi-web.js +4 -1
- package/inkos/.env.example +20 -0
- package/inkos/.node-version +1 -0
- package/inkos/.nvmrc +1 -0
- package/inkos/CHANGELOG.md +787 -0
- package/inkos/CONTRIBUTING.md +89 -0
- package/inkos/LICENSE +661 -0
- package/inkos/README.en.md +483 -0
- package/inkos/README.ja.md +461 -0
- package/inkos/README.md +272 -0
- package/inkos/assets/15qun.jpg +0 -0
- package/inkos/assets/41777702961_.pic.jpg +0 -0
- package/inkos/assets/inkos-short-demo-cover.png +0 -0
- package/inkos/assets/inkos-text.svg +40 -0
- package/inkos/assets/logo.svg +47 -0
- package/inkos/assets/screenshot-chapters.png +0 -0
- package/inkos/assets/screenshot-pipeline.png +0 -0
- package/inkos/assets/screenshot-state.png +0 -0
- package/inkos/assets/screenshot-terminal.png +0 -0
- package/inkos/assets/wechat-group-v8.jpg +0 -0
- package/inkos/package.json +42 -0
- package/inkos/packages/cli/package.json +74 -0
- package/inkos/packages/cli/src/__tests__/analytics.test.ts +154 -0
- package/inkos/packages/cli/src/__tests__/cli-integration.test.ts +1031 -0
- package/inkos/packages/cli/src/__tests__/daemon.test.ts +93 -0
- package/inkos/packages/cli/src/__tests__/doctor.test.ts +36 -0
- package/inkos/packages/cli/src/__tests__/interact-command.test.ts +142 -0
- package/inkos/packages/cli/src/__tests__/interaction-tools.test.ts +107 -0
- package/inkos/packages/cli/src/__tests__/llm-overrides.test.ts +25 -0
- package/inkos/packages/cli/src/__tests__/localization.test.ts +121 -0
- package/inkos/packages/cli/src/__tests__/progress-text.test.ts +92 -0
- package/inkos/packages/cli/src/__tests__/project-bootstrap.test.ts +71 -0
- package/inkos/packages/cli/src/__tests__/publish-package.test.ts +272 -0
- package/inkos/packages/cli/src/__tests__/revision-command.test.ts +82 -0
- package/inkos/packages/cli/src/__tests__/runtime-requirements.test.ts +89 -0
- package/inkos/packages/cli/src/__tests__/short-fiction-command.test.ts +48 -0
- package/inkos/packages/cli/src/__tests__/studio-runtime.test.ts +142 -0
- package/inkos/packages/cli/src/__tests__/studio.test.ts +87 -0
- package/inkos/packages/cli/src/__tests__/tui-activity-state.test.ts +20 -0
- package/inkos/packages/cli/src/__tests__/tui-agent-session.test.ts +213 -0
- package/inkos/packages/cli/src/__tests__/tui-chat-depth.test.ts +27 -0
- package/inkos/packages/cli/src/__tests__/tui-chat-draft.test.ts +44 -0
- package/inkos/packages/cli/src/__tests__/tui-command.test.ts +86 -0
- package/inkos/packages/cli/src/__tests__/tui-composer-caret.test.ts +46 -0
- package/inkos/packages/cli/src/__tests__/tui-composer-display.test.ts +40 -0
- package/inkos/packages/cli/src/__tests__/tui-dashboard.test.tsx +219 -0
- package/inkos/packages/cli/src/__tests__/tui-effects-i18n.test.ts +29 -0
- package/inkos/packages/cli/src/__tests__/tui-i18n.test.ts +22 -0
- package/inkos/packages/cli/src/__tests__/tui-input-chrome.test.ts +10 -0
- package/inkos/packages/cli/src/__tests__/tui-input-history.test.ts +40 -0
- package/inkos/packages/cli/src/__tests__/tui-layout.test.ts +55 -0
- package/inkos/packages/cli/src/__tests__/tui-local-commands.test.ts +47 -0
- package/inkos/packages/cli/src/__tests__/tui-session-store.test.ts +81 -0
- package/inkos/packages/cli/src/__tests__/tui-setup-i18n.test.ts +31 -0
- package/inkos/packages/cli/src/__tests__/tui-slash-autocomplete.test.ts +33 -0
- package/inkos/packages/cli/src/commands/agent.ts +65 -0
- package/inkos/packages/cli/src/commands/analytics.ts +77 -0
- package/inkos/packages/cli/src/commands/audit.ts +52 -0
- package/inkos/packages/cli/src/commands/book.ts +260 -0
- package/inkos/packages/cli/src/commands/compose.ts +50 -0
- package/inkos/packages/cli/src/commands/config.ts +328 -0
- package/inkos/packages/cli/src/commands/consolidate.ts +50 -0
- package/inkos/packages/cli/src/commands/daemon.ts +121 -0
- package/inkos/packages/cli/src/commands/detect.ts +125 -0
- package/inkos/packages/cli/src/commands/doctor.ts +391 -0
- package/inkos/packages/cli/src/commands/draft.ts +43 -0
- package/inkos/packages/cli/src/commands/eval.ts +217 -0
- package/inkos/packages/cli/src/commands/export.ts +45 -0
- package/inkos/packages/cli/src/commands/fanfic.ts +183 -0
- package/inkos/packages/cli/src/commands/genre.ts +160 -0
- package/inkos/packages/cli/src/commands/import.ts +158 -0
- package/inkos/packages/cli/src/commands/init.ts +47 -0
- package/inkos/packages/cli/src/commands/interact.ts +109 -0
- package/inkos/packages/cli/src/commands/plan.ts +54 -0
- package/inkos/packages/cli/src/commands/radar.ts +60 -0
- package/inkos/packages/cli/src/commands/review.ts +253 -0
- package/inkos/packages/cli/src/commands/revise.ts +58 -0
- package/inkos/packages/cli/src/commands/short-fiction.ts +294 -0
- package/inkos/packages/cli/src/commands/status.ts +138 -0
- package/inkos/packages/cli/src/commands/studio.ts +194 -0
- package/inkos/packages/cli/src/commands/style.ts +99 -0
- package/inkos/packages/cli/src/commands/tui.ts +18 -0
- package/inkos/packages/cli/src/commands/update.ts +45 -0
- package/inkos/packages/cli/src/commands/write.ts +324 -0
- package/inkos/packages/cli/src/index.ts +5 -0
- package/inkos/packages/cli/src/interaction/tools.ts +49 -0
- package/inkos/packages/cli/src/localization.ts +215 -0
- package/inkos/packages/cli/src/program.ts +106 -0
- package/inkos/packages/cli/src/progress-text.ts +85 -0
- package/inkos/packages/cli/src/project-bootstrap.ts +175 -0
- package/inkos/packages/cli/src/runtime-requirements.ts +135 -0
- package/inkos/packages/cli/src/tui/__tests__/markdown.test.ts +64 -0
- package/inkos/packages/cli/src/tui/activity-state.ts +41 -0
- package/inkos/packages/cli/src/tui/agent-input.ts +264 -0
- package/inkos/packages/cli/src/tui/ansi.ts +72 -0
- package/inkos/packages/cli/src/tui/app.ts +130 -0
- package/inkos/packages/cli/src/tui/chat-depth.ts +22 -0
- package/inkos/packages/cli/src/tui/chat-draft.ts +42 -0
- package/inkos/packages/cli/src/tui/composer-caret.ts +22 -0
- package/inkos/packages/cli/src/tui/composer-display.ts +26 -0
- package/inkos/packages/cli/src/tui/dashboard-model.ts +164 -0
- package/inkos/packages/cli/src/tui/dashboard.tsx +544 -0
- package/inkos/packages/cli/src/tui/effects.ts +542 -0
- package/inkos/packages/cli/src/tui/i18n.ts +278 -0
- package/inkos/packages/cli/src/tui/input-history.ts +69 -0
- package/inkos/packages/cli/src/tui/local-commands.ts +55 -0
- package/inkos/packages/cli/src/tui/markdown.ts +64 -0
- package/inkos/packages/cli/src/tui/session-store.ts +6 -0
- package/inkos/packages/cli/src/tui/setup.ts +397 -0
- package/inkos/packages/cli/src/tui/slash-autocomplete.ts +62 -0
- package/inkos/packages/cli/src/tui/theme.ts +17 -0
- package/inkos/packages/cli/src/utils.ts +222 -0
- package/inkos/packages/cli/tsconfig.json +9 -0
- package/inkos/packages/core/genres/cozy.md +43 -0
- package/inkos/packages/core/genres/cultivation.md +42 -0
- package/inkos/packages/core/genres/dungeon-core.md +40 -0
- package/inkos/packages/core/genres/horror.md +51 -0
- package/inkos/packages/core/genres/isekai.md +43 -0
- package/inkos/packages/core/genres/litrpg.md +43 -0
- package/inkos/packages/core/genres/other.md +24 -0
- package/inkos/packages/core/genres/progression.md +41 -0
- package/inkos/packages/core/genres/romantasy.md +45 -0
- package/inkos/packages/core/genres/sci-fi.md +42 -0
- package/inkos/packages/core/genres/system-apocalypse.md +40 -0
- package/inkos/packages/core/genres/tower-climber.md +41 -0
- package/inkos/packages/core/genres/urban.md +53 -0
- package/inkos/packages/core/genres/xianxia.md +46 -0
- package/inkos/packages/core/genres/xuanhuan.md +64 -0
- package/inkos/packages/core/package.json +61 -0
- package/inkos/packages/core/src/__tests__/agent-max-tokens-policy.test.ts +29 -0
- package/inkos/packages/core/src/__tests__/agent-session.test.ts +866 -0
- package/inkos/packages/core/src/__tests__/agent-system-prompt.test.ts +167 -0
- package/inkos/packages/core/src/__tests__/agent-tools-params.test.ts +197 -0
- package/inkos/packages/core/src/__tests__/agent-tools.test.ts +421 -0
- package/inkos/packages/core/src/__tests__/ai-tells.test.ts +90 -0
- package/inkos/packages/core/src/__tests__/architect-phase5-consolidated.test.ts +445 -0
- package/inkos/packages/core/src/__tests__/architect-phase5.test.ts +455 -0
- package/inkos/packages/core/src/__tests__/architect-phase7.test.ts +210 -0
- package/inkos/packages/core/src/__tests__/architect.test.ts +859 -0
- package/inkos/packages/core/src/__tests__/audit-parse.test.ts +78 -0
- package/inkos/packages/core/src/__tests__/book-id.test.ts +26 -0
- package/inkos/packages/core/src/__tests__/book-session-store.test.ts +447 -0
- package/inkos/packages/core/src/__tests__/book-session.test.ts +113 -0
- package/inkos/packages/core/src/__tests__/chapter-analyzer.test.ts +574 -0
- package/inkos/packages/core/src/__tests__/chapter-memo-parser.test.ts +247 -0
- package/inkos/packages/core/src/__tests__/chapter-persistence.test.ts +198 -0
- package/inkos/packages/core/src/__tests__/chapter-review-cycle.test.ts +294 -0
- package/inkos/packages/core/src/__tests__/chapter-splitter.test.ts +156 -0
- package/inkos/packages/core/src/__tests__/chapter-state-recovery.test.ts +235 -0
- package/inkos/packages/core/src/__tests__/chapter-truth-validation.test.ts +253 -0
- package/inkos/packages/core/src/__tests__/composer.test.ts +627 -0
- package/inkos/packages/core/src/__tests__/config-loader.test.ts +325 -0
- package/inkos/packages/core/src/__tests__/config-migration.test.ts +102 -0
- package/inkos/packages/core/src/__tests__/consolidator.test.ts +32 -0
- package/inkos/packages/core/src/__tests__/context-filter.test.ts +60 -0
- package/inkos/packages/core/src/__tests__/context-transform.test.ts +108 -0
- package/inkos/packages/core/src/__tests__/continuity.test.ts +391 -0
- package/inkos/packages/core/src/__tests__/detection-insights.test.ts +59 -0
- package/inkos/packages/core/src/__tests__/detector.test.ts +86 -0
- package/inkos/packages/core/src/__tests__/draft-directive-parser.test.ts +386 -0
- package/inkos/packages/core/src/__tests__/edit-controller.test.ts +190 -0
- package/inkos/packages/core/src/__tests__/effective-llm-config.test.ts +486 -0
- package/inkos/packages/core/src/__tests__/fanfic-dimensions.test.ts +58 -0
- package/inkos/packages/core/src/__tests__/fanfic-models.test.ts +69 -0
- package/inkos/packages/core/src/__tests__/governed-working-set.test.ts +155 -0
- package/inkos/packages/core/src/__tests__/hook-arbiter.test.ts +124 -0
- package/inkos/packages/core/src/__tests__/hook-governance.test.ts +228 -0
- package/inkos/packages/core/src/__tests__/hook-health.test.ts +166 -0
- package/inkos/packages/core/src/__tests__/hook-ledger-validator.test.ts +236 -0
- package/inkos/packages/core/src/__tests__/hook-promotion.test.ts +192 -0
- package/inkos/packages/core/src/__tests__/hook-stale-detection.test.ts +136 -0
- package/inkos/packages/core/src/__tests__/index-notify-lazy.test.ts +20 -0
- package/inkos/packages/core/src/__tests__/interaction-chat-tokens.test.ts +170 -0
- package/inkos/packages/core/src/__tests__/interaction-models.test.ts +155 -0
- package/inkos/packages/core/src/__tests__/interaction-nl-router.test.ts +223 -0
- package/inkos/packages/core/src/__tests__/interaction-runtime.test.ts +633 -0
- package/inkos/packages/core/src/__tests__/interaction-tools.test.ts +343 -0
- package/inkos/packages/core/src/__tests__/length-metrics.test.ts +82 -0
- package/inkos/packages/core/src/__tests__/length-normalizer.test.ts +331 -0
- package/inkos/packages/core/src/__tests__/list-models.test.ts +109 -0
- package/inkos/packages/core/src/__tests__/llm-env.test.ts +31 -0
- package/inkos/packages/core/src/__tests__/logger.test.ts +175 -0
- package/inkos/packages/core/src/__tests__/long-span-fatigue.test.ts +160 -0
- package/inkos/packages/core/src/__tests__/memory-retrieval.test.ts +1303 -0
- package/inkos/packages/core/src/__tests__/models.test.ts +918 -0
- package/inkos/packages/core/src/__tests__/outline-paths.test.ts +97 -0
- package/inkos/packages/core/src/__tests__/path-safety.test.ts +22 -0
- package/inkos/packages/core/src/__tests__/persisted-governed-plan.test.ts +134 -0
- package/inkos/packages/core/src/__tests__/phase5-cleanup.test.ts +393 -0
- package/inkos/packages/core/src/__tests__/phase5-hotfix.test.ts +288 -0
- package/inkos/packages/core/src/__tests__/phase7-hotfix.test.ts +614 -0
- package/inkos/packages/core/src/__tests__/pipeline-agent.test.ts +354 -0
- package/inkos/packages/core/src/__tests__/pipeline-runner-memory-sync.test.ts +317 -0
- package/inkos/packages/core/src/__tests__/pipeline-runner.test.ts +5200 -0
- package/inkos/packages/core/src/__tests__/planner-context.test.ts +137 -0
- package/inkos/packages/core/src/__tests__/planner-prompts-ratio.test.ts +11 -0
- package/inkos/packages/core/src/__tests__/planner-prompts.test.ts +171 -0
- package/inkos/packages/core/src/__tests__/planner.test.ts +362 -0
- package/inkos/packages/core/src/__tests__/planning-materials.test.ts +90 -0
- package/inkos/packages/core/src/__tests__/polisher.test.ts +189 -0
- package/inkos/packages/core/src/__tests__/post-write-validator.test.ts +291 -0
- package/inkos/packages/core/src/__tests__/probe.test.ts +77 -0
- package/inkos/packages/core/src/__tests__/project-interaction.test.ts +241 -0
- package/inkos/packages/core/src/__tests__/provider.test.ts +953 -0
- package/inkos/packages/core/src/__tests__/providers-group.test.ts +34 -0
- package/inkos/packages/core/src/__tests__/providers-lookup.test.ts +81 -0
- package/inkos/packages/core/src/__tests__/providers-schema.test.ts +158 -0
- package/inkos/packages/core/src/__tests__/proxy-fetch.test.ts +75 -0
- package/inkos/packages/core/src/__tests__/revise-foundation.test.ts +514 -0
- package/inkos/packages/core/src/__tests__/reviser.test.ts +859 -0
- package/inkos/packages/core/src/__tests__/runtime-state-store.test.ts +388 -0
- package/inkos/packages/core/src/__tests__/scheduler.test.ts +123 -0
- package/inkos/packages/core/src/__tests__/secrets-migration.test.ts +71 -0
- package/inkos/packages/core/src/__tests__/secrets.test.ts +95 -0
- package/inkos/packages/core/src/__tests__/sensitive-words.test.ts +88 -0
- package/inkos/packages/core/src/__tests__/service-presets-regression.test.ts +73 -0
- package/inkos/packages/core/src/__tests__/service-resolver-regression.test.ts +75 -0
- package/inkos/packages/core/src/__tests__/service-resolver.test.ts +228 -0
- package/inkos/packages/core/src/__tests__/session-transcript-restore.test.ts +1311 -0
- package/inkos/packages/core/src/__tests__/session-transcript.test.ts +195 -0
- package/inkos/packages/core/src/__tests__/settler-delta-parser.test.ts +133 -0
- package/inkos/packages/core/src/__tests__/short-fiction-public.test.ts +241 -0
- package/inkos/packages/core/src/__tests__/spot-fix-patches.test.ts +104 -0
- package/inkos/packages/core/src/__tests__/state-manager.test.ts +1298 -0
- package/inkos/packages/core/src/__tests__/state-projections.test.ts +130 -0
- package/inkos/packages/core/src/__tests__/state-reducer.test.ts +372 -0
- package/inkos/packages/core/src/__tests__/state-validator-agent.test.ts +165 -0
- package/inkos/packages/core/src/__tests__/state-validator.test.ts +122 -0
- package/inkos/packages/core/src/__tests__/style-analyzer.test.ts +61 -0
- package/inkos/packages/core/src/__tests__/temperature-constraints.test.ts +57 -0
- package/inkos/packages/core/src/__tests__/v13-hotfix-round4.test.ts +343 -0
- package/inkos/packages/core/src/__tests__/verify-service.test.ts +77 -0
- package/inkos/packages/core/src/__tests__/webhook.test.ts +91 -0
- package/inkos/packages/core/src/__tests__/writer-parser.test.ts +348 -0
- package/inkos/packages/core/src/__tests__/writer-prompts.test.ts +269 -0
- package/inkos/packages/core/src/__tests__/writer.test.ts +1360 -0
- package/inkos/packages/core/src/agent/agent-session.ts +737 -0
- package/inkos/packages/core/src/agent/agent-system-prompt.ts +199 -0
- package/inkos/packages/core/src/agent/agent-tools.ts +835 -0
- package/inkos/packages/core/src/agent/context-transform.ts +85 -0
- package/inkos/packages/core/src/agent/index.ts +14 -0
- package/inkos/packages/core/src/agents/ai-tells.ts +161 -0
- package/inkos/packages/core/src/agents/architect.ts +1291 -0
- package/inkos/packages/core/src/agents/base.ts +100 -0
- package/inkos/packages/core/src/agents/chapter-analyzer.ts +634 -0
- package/inkos/packages/core/src/agents/composer.ts +469 -0
- package/inkos/packages/core/src/agents/consolidator.ts +218 -0
- package/inkos/packages/core/src/agents/continuity.ts +824 -0
- package/inkos/packages/core/src/agents/detection-insights.ts +72 -0
- package/inkos/packages/core/src/agents/detector.ts +224 -0
- package/inkos/packages/core/src/agents/en-prompt-sections.ts +129 -0
- package/inkos/packages/core/src/agents/fanfic-canon-importer.ts +146 -0
- package/inkos/packages/core/src/agents/fanfic-dimensions.ts +87 -0
- package/inkos/packages/core/src/agents/fanfic-prompt-sections.ts +109 -0
- package/inkos/packages/core/src/agents/foundation-reviewer.ts +204 -0
- package/inkos/packages/core/src/agents/length-normalizer.ts +218 -0
- package/inkos/packages/core/src/agents/observer-prompts.ts +127 -0
- package/inkos/packages/core/src/agents/planner-context.ts +297 -0
- package/inkos/packages/core/src/agents/planner-prompts.ts +404 -0
- package/inkos/packages/core/src/agents/planner.ts +783 -0
- package/inkos/packages/core/src/agents/polisher.ts +153 -0
- package/inkos/packages/core/src/agents/post-write-validator.ts +873 -0
- package/inkos/packages/core/src/agents/radar-source.ts +123 -0
- package/inkos/packages/core/src/agents/radar.ts +120 -0
- package/inkos/packages/core/src/agents/reviser.ts +701 -0
- package/inkos/packages/core/src/agents/rules-reader.ts +155 -0
- package/inkos/packages/core/src/agents/sensitive-words.ts +142 -0
- package/inkos/packages/core/src/agents/settler-delta-parser.ts +53 -0
- package/inkos/packages/core/src/agents/settler-parser.ts +38 -0
- package/inkos/packages/core/src/agents/settler-prompts.ts +230 -0
- package/inkos/packages/core/src/agents/short-fiction.ts +429 -0
- package/inkos/packages/core/src/agents/state-validator.ts +322 -0
- package/inkos/packages/core/src/agents/style-analyzer.ts +93 -0
- package/inkos/packages/core/src/agents/writer-parser.ts +178 -0
- package/inkos/packages/core/src/agents/writer-prompts.ts +899 -0
- package/inkos/packages/core/src/agents/writer.ts +1450 -0
- package/inkos/packages/core/src/index.ts +392 -0
- package/inkos/packages/core/src/interaction/book-session-store.ts +226 -0
- package/inkos/packages/core/src/interaction/draft-directive-parser.ts +266 -0
- package/inkos/packages/core/src/interaction/edit-controller.ts +270 -0
- package/inkos/packages/core/src/interaction/events.ts +41 -0
- package/inkos/packages/core/src/interaction/export-artifact.ts +151 -0
- package/inkos/packages/core/src/interaction/intents.ts +63 -0
- package/inkos/packages/core/src/interaction/modes.ts +13 -0
- package/inkos/packages/core/src/interaction/nl-router.ts +258 -0
- package/inkos/packages/core/src/interaction/project-control.ts +150 -0
- package/inkos/packages/core/src/interaction/project-session-store.ts +81 -0
- package/inkos/packages/core/src/interaction/project-tools.ts +704 -0
- package/inkos/packages/core/src/interaction/request-router.ts +5 -0
- package/inkos/packages/core/src/interaction/runtime.ts +1167 -0
- package/inkos/packages/core/src/interaction/session-transcript-legacy.ts +113 -0
- package/inkos/packages/core/src/interaction/session-transcript-restore.ts +607 -0
- package/inkos/packages/core/src/interaction/session-transcript-schema.ts +76 -0
- package/inkos/packages/core/src/interaction/session-transcript.ts +189 -0
- package/inkos/packages/core/src/interaction/session.ts +226 -0
- package/inkos/packages/core/src/interaction/truth-authority.ts +45 -0
- package/inkos/packages/core/src/llm/config-migration.ts +58 -0
- package/inkos/packages/core/src/llm/cover-providers.ts +45 -0
- package/inkos/packages/core/src/llm/provider.ts +1331 -0
- package/inkos/packages/core/src/llm/providers/endpoints/ai360.ts +42 -0
- package/inkos/packages/core/src/llm/providers/endpoints/anthropic.ts +82 -0
- package/inkos/packages/core/src/llm/providers/endpoints/astronCodingPlan.ts +30 -0
- package/inkos/packages/core/src/llm/providers/endpoints/baichuan.ts +28 -0
- package/inkos/packages/core/src/llm/providers/endpoints/bailian.ts +65 -0
- package/inkos/packages/core/src/llm/providers/endpoints/bailianCodingPlan.ts +30 -0
- package/inkos/packages/core/src/llm/providers/endpoints/custom.ts +22 -0
- package/inkos/packages/core/src/llm/providers/endpoints/deepseek.ts +35 -0
- package/inkos/packages/core/src/llm/providers/endpoints/giteeai.ts +41 -0
- package/inkos/packages/core/src/llm/providers/endpoints/githubCopilot.ts +43 -0
- package/inkos/packages/core/src/llm/providers/endpoints/glmCodingPlan.ts +28 -0
- package/inkos/packages/core/src/llm/providers/endpoints/google.ts +51 -0
- package/inkos/packages/core/src/llm/providers/endpoints/hunyuan.ts +42 -0
- package/inkos/packages/core/src/llm/providers/endpoints/infiniai.ts +72 -0
- package/inkos/packages/core/src/llm/providers/endpoints/internlm.ts +28 -0
- package/inkos/packages/core/src/llm/providers/endpoints/kimiCode.ts +23 -0
- package/inkos/packages/core/src/llm/providers/endpoints/kimiCodingPlan.ts +24 -0
- package/inkos/packages/core/src/llm/providers/endpoints/kkaiapi.ts +56 -0
- package/inkos/packages/core/src/llm/providers/endpoints/longcat.ts +25 -0
- package/inkos/packages/core/src/llm/providers/endpoints/minimax.ts +39 -0
- package/inkos/packages/core/src/llm/providers/endpoints/minimaxCodingPlan.ts +28 -0
- package/inkos/packages/core/src/llm/providers/endpoints/mistral.ts +40 -0
- package/inkos/packages/core/src/llm/providers/endpoints/modelscope.ts +30 -0
- package/inkos/packages/core/src/llm/providers/endpoints/moonshot.ts +39 -0
- package/inkos/packages/core/src/llm/providers/endpoints/newapi.ts +21 -0
- package/inkos/packages/core/src/llm/providers/endpoints/ollama.ts +73 -0
- package/inkos/packages/core/src/llm/providers/endpoints/openai.ts +77 -0
- package/inkos/packages/core/src/llm/providers/endpoints/opencodeCodingPlan.ts +30 -0
- package/inkos/packages/core/src/llm/providers/endpoints/openrouter.ts +87 -0
- package/inkos/packages/core/src/llm/providers/endpoints/ppio.ts +86 -0
- package/inkos/packages/core/src/llm/providers/endpoints/qiniu.ts +32 -0
- package/inkos/packages/core/src/llm/providers/endpoints/sensenova.ts +45 -0
- package/inkos/packages/core/src/llm/providers/endpoints/siliconcloud.ts +126 -0
- package/inkos/packages/core/src/llm/providers/endpoints/spark.ts +33 -0
- package/inkos/packages/core/src/llm/providers/endpoints/stepfun.ts +35 -0
- package/inkos/packages/core/src/llm/providers/endpoints/tencentcloud.ts +25 -0
- package/inkos/packages/core/src/llm/providers/endpoints/volcengine.ts +52 -0
- package/inkos/packages/core/src/llm/providers/endpoints/volcengineCodingPlan.ts +42 -0
- package/inkos/packages/core/src/llm/providers/endpoints/wenxin.ts +106 -0
- package/inkos/packages/core/src/llm/providers/endpoints/xai.ts +34 -0
- package/inkos/packages/core/src/llm/providers/endpoints/xiaomimimo.ts +26 -0
- package/inkos/packages/core/src/llm/providers/endpoints/zeroone.ts +34 -0
- package/inkos/packages/core/src/llm/providers/endpoints/zhipu.ts +61 -0
- package/inkos/packages/core/src/llm/providers/index.ts +71 -0
- package/inkos/packages/core/src/llm/providers/lookup.ts +70 -0
- package/inkos/packages/core/src/llm/providers/probe.ts +35 -0
- package/inkos/packages/core/src/llm/providers/types.ts +89 -0
- package/inkos/packages/core/src/llm/providers/verify.ts +104 -0
- package/inkos/packages/core/src/llm/secrets.ts +77 -0
- package/inkos/packages/core/src/llm/service-presets.ts +215 -0
- package/inkos/packages/core/src/llm/service-resolver.ts +91 -0
- package/inkos/packages/core/src/models/book-rules.ts +126 -0
- package/inkos/packages/core/src/models/book.ts +70 -0
- package/inkos/packages/core/src/models/chapter.ts +42 -0
- package/inkos/packages/core/src/models/detection.ts +25 -0
- package/inkos/packages/core/src/models/genre-profile.ts +36 -0
- package/inkos/packages/core/src/models/input-governance.ts +99 -0
- package/inkos/packages/core/src/models/length-governance.ts +46 -0
- package/inkos/packages/core/src/models/project.ts +161 -0
- package/inkos/packages/core/src/models/runtime-state.ts +144 -0
- package/inkos/packages/core/src/models/state.ts +52 -0
- package/inkos/packages/core/src/models/style-profile.ts +15 -0
- package/inkos/packages/core/src/notify/dispatcher.ts +96 -0
- package/inkos/packages/core/src/notify/feishu.ts +34 -0
- package/inkos/packages/core/src/notify/telegram.ts +25 -0
- package/inkos/packages/core/src/notify/webhook.ts +58 -0
- package/inkos/packages/core/src/notify/wechat-work.ts +22 -0
- package/inkos/packages/core/src/pipeline/agent.ts +691 -0
- package/inkos/packages/core/src/pipeline/chapter-persistence.ts +79 -0
- package/inkos/packages/core/src/pipeline/chapter-review-cycle.ts +324 -0
- package/inkos/packages/core/src/pipeline/chapter-state-recovery.ts +236 -0
- package/inkos/packages/core/src/pipeline/chapter-truth-validation.ts +145 -0
- package/inkos/packages/core/src/pipeline/detection-runner.ts +164 -0
- package/inkos/packages/core/src/pipeline/persisted-governed-plan.ts +216 -0
- package/inkos/packages/core/src/pipeline/runner.ts +3438 -0
- package/inkos/packages/core/src/pipeline/scheduler.ts +411 -0
- package/inkos/packages/core/src/pipeline/short-fiction-runner.ts +801 -0
- package/inkos/packages/core/src/prompts/index.ts +1 -0
- package/inkos/packages/core/src/prompts/short-fiction.ts +273 -0
- package/inkos/packages/core/src/state/manager.ts +560 -0
- package/inkos/packages/core/src/state/memory-db.ts +359 -0
- package/inkos/packages/core/src/state/runtime-state-store.ts +164 -0
- package/inkos/packages/core/src/state/state-bootstrap.ts +657 -0
- package/inkos/packages/core/src/state/state-projections.ts +255 -0
- package/inkos/packages/core/src/state/state-reducer.ts +260 -0
- package/inkos/packages/core/src/state/state-validator.ts +117 -0
- package/inkos/packages/core/src/utils/analytics.ts +92 -0
- package/inkos/packages/core/src/utils/book-id.ts +31 -0
- package/inkos/packages/core/src/utils/cadence-policy.ts +46 -0
- package/inkos/packages/core/src/utils/chapter-cadence.ts +211 -0
- package/inkos/packages/core/src/utils/chapter-memo-parser.ts +157 -0
- package/inkos/packages/core/src/utils/chapter-splitter.ts +80 -0
- package/inkos/packages/core/src/utils/config-loader.ts +29 -0
- package/inkos/packages/core/src/utils/context-assembly.ts +98 -0
- package/inkos/packages/core/src/utils/context-filter.ts +190 -0
- package/inkos/packages/core/src/utils/effective-llm-config.ts +529 -0
- package/inkos/packages/core/src/utils/governed-context.ts +101 -0
- package/inkos/packages/core/src/utils/governed-working-set.ts +395 -0
- package/inkos/packages/core/src/utils/hook-arbiter.ts +332 -0
- package/inkos/packages/core/src/utils/hook-governance.ts +199 -0
- package/inkos/packages/core/src/utils/hook-health.ts +189 -0
- package/inkos/packages/core/src/utils/hook-ledger-validator.ts +277 -0
- package/inkos/packages/core/src/utils/hook-lifecycle.ts +224 -0
- package/inkos/packages/core/src/utils/hook-policy.ts +115 -0
- package/inkos/packages/core/src/utils/hook-promotion.ts +313 -0
- package/inkos/packages/core/src/utils/hook-stale-detection.ts +168 -0
- package/inkos/packages/core/src/utils/length-metrics.ts +123 -0
- package/inkos/packages/core/src/utils/llm-endpoint-auth.ts +40 -0
- package/inkos/packages/core/src/utils/llm-env.ts +74 -0
- package/inkos/packages/core/src/utils/logger.ts +123 -0
- package/inkos/packages/core/src/utils/long-span-fatigue.ts +545 -0
- package/inkos/packages/core/src/utils/memory-retrieval.ts +527 -0
- package/inkos/packages/core/src/utils/narrative-control.ts +177 -0
- package/inkos/packages/core/src/utils/outline-paths.ts +275 -0
- package/inkos/packages/core/src/utils/path-safety.ts +11 -0
- package/inkos/packages/core/src/utils/planning-materials.ts +185 -0
- package/inkos/packages/core/src/utils/pov-filter.ts +149 -0
- package/inkos/packages/core/src/utils/proxy-fetch.ts +44 -0
- package/inkos/packages/core/src/utils/runtime-writer.ts +41 -0
- package/inkos/packages/core/src/utils/spot-fix-patches.ts +189 -0
- package/inkos/packages/core/src/utils/story-markdown.ts +346 -0
- package/inkos/packages/core/src/utils/web-search.ts +82 -0
- package/inkos/packages/core/src/utils/writing-methodology.ts +164 -0
- package/inkos/packages/core/tsconfig.json +8 -0
- package/inkos/packages/core/vitest.config.ts +7 -0
- package/inkos/packages/studio/components.json +25 -0
- package/inkos/packages/studio/index.html +13 -0
- package/inkos/packages/studio/package.json +72 -0
- package/inkos/packages/studio/postcss.config.js +3 -0
- package/inkos/packages/studio/src/App.test.ts +25 -0
- package/inkos/packages/studio/src/App.tsx +280 -0
- package/inkos/packages/studio/src/api/__tests__/normalize-base-url.test.ts +40 -0
- package/inkos/packages/studio/src/api/book-create.test.ts +104 -0
- package/inkos/packages/studio/src/api/book-create.ts +94 -0
- package/inkos/packages/studio/src/api/errors.ts +17 -0
- package/inkos/packages/studio/src/api/index.ts +30 -0
- package/inkos/packages/studio/src/api/lib/run-store.ts +177 -0
- package/inkos/packages/studio/src/api/lib/sse.ts +50 -0
- package/inkos/packages/studio/src/api/phase5-hotfix.test.ts +335 -0
- package/inkos/packages/studio/src/api/safety.ts +6 -0
- package/inkos/packages/studio/src/api/server.test.ts +3162 -0
- package/inkos/packages/studio/src/api/server.ts +3666 -0
- package/inkos/packages/studio/src/api/v13-hotfix-round4.test.ts +226 -0
- package/inkos/packages/studio/src/app-state.test.ts +8 -0
- package/inkos/packages/studio/src/app-state.ts +1 -0
- package/inkos/packages/studio/src/components/ConfirmDialog.tsx +95 -0
- package/inkos/packages/studio/src/components/ServiceConfigSourceCard.tsx +139 -0
- package/inkos/packages/studio/src/components/ServiceQuickLinks.tsx +65 -0
- package/inkos/packages/studio/src/components/Sidebar.tsx +652 -0
- package/inkos/packages/studio/src/components/ai-elements/code-block.tsx +562 -0
- package/inkos/packages/studio/src/components/ai-elements/confirmation.tsx +174 -0
- package/inkos/packages/studio/src/components/ai-elements/message.tsx +360 -0
- package/inkos/packages/studio/src/components/ai-elements/prompt-input.tsx +1457 -0
- package/inkos/packages/studio/src/components/ai-elements/reasoning.tsx +226 -0
- package/inkos/packages/studio/src/components/ai-elements/shimmer.tsx +77 -0
- package/inkos/packages/studio/src/components/ai-elements/tool.tsx +173 -0
- package/inkos/packages/studio/src/components/chat/BookSidebar.tsx +291 -0
- package/inkos/packages/studio/src/components/chat/ChatMessage.tsx +39 -0
- package/inkos/packages/studio/src/components/chat/QuickActions.tsx +73 -0
- package/inkos/packages/studio/src/components/chat/ToolExecutionSteps.tsx +320 -0
- package/inkos/packages/studio/src/components/chat/__tests__/ToolExecutionSteps.test.ts +114 -0
- package/inkos/packages/studio/src/components/chat-utils.ts +56 -0
- package/inkos/packages/studio/src/components/chatbar-state.test.ts +69 -0
- package/inkos/packages/studio/src/components/sidebar/ChaptersSection.tsx +66 -0
- package/inkos/packages/studio/src/components/sidebar/CharacterSection.tsx +129 -0
- package/inkos/packages/studio/src/components/sidebar/FoundationSection.tsx +61 -0
- package/inkos/packages/studio/src/components/sidebar/ProgressSection.tsx +124 -0
- package/inkos/packages/studio/src/components/sidebar/SidebarCard.tsx +30 -0
- package/inkos/packages/studio/src/components/sidebar/SummarySection.tsx +89 -0
- package/inkos/packages/studio/src/components/ui/alert.tsx +76 -0
- package/inkos/packages/studio/src/components/ui/badge.tsx +52 -0
- package/inkos/packages/studio/src/components/ui/button-group.tsx +87 -0
- package/inkos/packages/studio/src/components/ui/button.tsx +58 -0
- package/inkos/packages/studio/src/components/ui/collapsible.tsx +19 -0
- package/inkos/packages/studio/src/components/ui/command.tsx +194 -0
- package/inkos/packages/studio/src/components/ui/dialog.tsx +158 -0
- package/inkos/packages/studio/src/components/ui/dropdown-menu.tsx +266 -0
- package/inkos/packages/studio/src/components/ui/hover-card.tsx +51 -0
- package/inkos/packages/studio/src/components/ui/input-group.tsx +158 -0
- package/inkos/packages/studio/src/components/ui/input.tsx +20 -0
- package/inkos/packages/studio/src/components/ui/select.tsx +199 -0
- package/inkos/packages/studio/src/components/ui/separator.tsx +23 -0
- package/inkos/packages/studio/src/components/ui/spinner.tsx +10 -0
- package/inkos/packages/studio/src/components/ui/textarea.tsx +18 -0
- package/inkos/packages/studio/src/components/ui/tooltip.tsx +66 -0
- package/inkos/packages/studio/src/constants/service-groups.ts +29 -0
- package/inkos/packages/studio/src/hooks/use-api.test.ts +93 -0
- package/inkos/packages/studio/src/hooks/use-api.ts +189 -0
- package/inkos/packages/studio/src/hooks/use-book-activity.test.ts +129 -0
- package/inkos/packages/studio/src/hooks/use-book-activity.ts +180 -0
- package/inkos/packages/studio/src/hooks/use-colors.ts +27 -0
- package/inkos/packages/studio/src/hooks/use-hash-route.test.ts +101 -0
- package/inkos/packages/studio/src/hooks/use-hash-route.ts +89 -0
- package/inkos/packages/studio/src/hooks/use-i18n.ts +289 -0
- package/inkos/packages/studio/src/hooks/use-session-events.ts +64 -0
- package/inkos/packages/studio/src/hooks/use-sse.test.ts +52 -0
- package/inkos/packages/studio/src/hooks/use-sse.ts +92 -0
- package/inkos/packages/studio/src/hooks/use-theme.test.ts +31 -0
- package/inkos/packages/studio/src/hooks/use-theme.ts +75 -0
- package/inkos/packages/studio/src/index.css +323 -0
- package/inkos/packages/studio/src/lib/error-copy.test.ts +34 -0
- package/inkos/packages/studio/src/lib/error-copy.ts +37 -0
- package/inkos/packages/studio/src/lib/utils.ts +6 -0
- package/inkos/packages/studio/src/main.tsx +10 -0
- package/inkos/packages/studio/src/pages/Analytics.tsx +80 -0
- package/inkos/packages/studio/src/pages/BookCreate.tsx +895 -0
- package/inkos/packages/studio/src/pages/BookDetail.tsx +652 -0
- package/inkos/packages/studio/src/pages/ChapterReader.tsx +266 -0
- package/inkos/packages/studio/src/pages/ChatPage.tsx +521 -0
- package/inkos/packages/studio/src/pages/DaemonControl.tsx +116 -0
- package/inkos/packages/studio/src/pages/Dashboard.tsx +379 -0
- package/inkos/packages/studio/src/pages/DoctorView.tsx +82 -0
- package/inkos/packages/studio/src/pages/GenreManager.tsx +464 -0
- package/inkos/packages/studio/src/pages/ImportManager.tsx +216 -0
- package/inkos/packages/studio/src/pages/LanguageSelector.tsx +74 -0
- package/inkos/packages/studio/src/pages/LogViewer.tsx +82 -0
- package/inkos/packages/studio/src/pages/RadarView.tsx +157 -0
- package/inkos/packages/studio/src/pages/ServiceDetailPage.tsx +393 -0
- package/inkos/packages/studio/src/pages/ServiceListPage.tsx +463 -0
- package/inkos/packages/studio/src/pages/StyleManager.tsx +225 -0
- package/inkos/packages/studio/src/pages/TruthFiles.tsx +194 -0
- package/inkos/packages/studio/src/pages/chat-page-state.test.ts +206 -0
- package/inkos/packages/studio/src/pages/chat-page-state.ts +112 -0
- package/inkos/packages/studio/src/pages/page-state.test.ts +258 -0
- package/inkos/packages/studio/src/pages/service-detail-state.test.ts +294 -0
- package/inkos/packages/studio/src/pages/service-detail-state.ts +234 -0
- package/inkos/packages/studio/src/pages/style-manager-state.test.ts +22 -0
- package/inkos/packages/studio/src/pages/truth-files-state.test.ts +61 -0
- package/inkos/packages/studio/src/shared/contracts.ts +143 -0
- package/inkos/packages/studio/src/store/chat/__tests__/message-parts.test.ts +172 -0
- package/inkos/packages/studio/src/store/chat/index.ts +3 -0
- package/inkos/packages/studio/src/store/chat/initialState.ts +8 -0
- package/inkos/packages/studio/src/store/chat/message-policy.test.ts +16 -0
- package/inkos/packages/studio/src/store/chat/message-policy.ts +5 -0
- package/inkos/packages/studio/src/store/chat/parts-builder.ts +187 -0
- package/inkos/packages/studio/src/store/chat/selectors.ts +13 -0
- package/inkos/packages/studio/src/store/chat/slices/create/action.ts +10 -0
- package/inkos/packages/studio/src/store/chat/slices/create/initialState.ts +9 -0
- package/inkos/packages/studio/src/store/chat/slices/message/action.ts +417 -0
- package/inkos/packages/studio/src/store/chat/slices/message/initialState.ts +10 -0
- package/inkos/packages/studio/src/store/chat/slices/message/runtime.test.ts +21 -0
- package/inkos/packages/studio/src/store/chat/slices/message/runtime.ts +233 -0
- package/inkos/packages/studio/src/store/chat/slices/message/stream-events.ts +272 -0
- package/inkos/packages/studio/src/store/chat/store.ts +11 -0
- package/inkos/packages/studio/src/store/chat/types.ts +169 -0
- package/inkos/packages/studio/src/store/service/index.ts +2 -0
- package/inkos/packages/studio/src/store/service/store.ts +123 -0
- package/inkos/packages/studio/src/store/service/types.ts +50 -0
- package/inkos/packages/studio/tsconfig.json +24 -0
- package/inkos/packages/studio/tsconfig.server.json +11 -0
- package/inkos/packages/studio/vite.config.ts +34 -0
- package/inkos/packages/studio/vitest.config.ts +14 -0
- package/inkos/pnpm-lock.yaml +9569 -0
- package/inkos/pnpm-workspace.yaml +2 -0
- package/inkos/scripts/prepare-package-for-publish.mjs +135 -0
- package/inkos/scripts/restore-package-json.mjs +31 -0
- package/inkos/scripts/set-package-versions.mjs +74 -0
- package/inkos/scripts/verify-no-workspace-protocol.mjs +140 -0
- package/inkos/skills/SKILL.md +654 -0
- package/inkos/tsconfig.json +19 -0
- package/package.json +4 -3
- /package/.next/static/{F2hMZMf1IyCVAWpkbtRz7 → -3vIrBZXdQ0rp7Wa3Kz40}/_buildManifest.js +0 -0
- /package/.next/static/{F2hMZMf1IyCVAWpkbtRz7 → -3vIrBZXdQ0rp7Wa3Kz40}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { ArchitectAgent } from "../agents/architect.js";
|
|
6
|
+
import {
|
|
7
|
+
readCurrentStateWithFallback,
|
|
8
|
+
isCurrentStateSeedPlaceholder,
|
|
9
|
+
} from "../utils/outline-paths.js";
|
|
10
|
+
import type { BookConfig } from "../models/book.js";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Phase 5 consolidation invariants (7 sections → 5 sections).
|
|
14
|
+
//
|
|
15
|
+
// A brief side-trip restored current_state as a 6th "narrow env/era" section,
|
|
16
|
+
// but the bench run showed the LLM emits an empty block for 3 out of 3 books
|
|
17
|
+
// (修仙/玄幻/系统 genres have no real year; urban/period genres already weave
|
|
18
|
+
// the era into world-tonal-ground naturally). The section is back to optional:
|
|
19
|
+
// architect may omit it entirely, writeFoundationFiles still seeds
|
|
20
|
+
// current_state.md with a placeholder so the consolidator has a file to
|
|
21
|
+
// append to.
|
|
22
|
+
//
|
|
23
|
+
// These tests lock in the 5-section contract so future edits can't silently
|
|
24
|
+
// regress back to the 7-section layout that was causing gpt-5.4 to drop tail
|
|
25
|
+
// sections:
|
|
26
|
+
//
|
|
27
|
+
// 1. The architect prompt advertises exactly 5 SECTION headers — no
|
|
28
|
+
// current_state, no rhythm_principles.
|
|
29
|
+
// 2. The prompt FORBIDS duplication of protagonist-arc across story_frame
|
|
30
|
+
// and roles, and forbids re-emitting rhythm_principles / current_state.
|
|
31
|
+
// 3. It carries explicit per-section budget markers (NO current_state
|
|
32
|
+
// budget entry).
|
|
33
|
+
// 4. current_state section is NOT required in architect output — legacy
|
|
34
|
+
// outputs that still carry it are accepted.
|
|
35
|
+
// 5. book_rules prompt tells the LLM "YAML only, no prose".
|
|
36
|
+
// 6. rhythm principles prompt allows mix of universal + concrete (≥3
|
|
37
|
+
// concretized, rest may stay universal).
|
|
38
|
+
// 7. Legacy 7-section outputs still parse (backward compat).
|
|
39
|
+
// 8. writeFoundationFiles seeds current_state.md with a marker placeholder
|
|
40
|
+
// when architect produced no initial state.
|
|
41
|
+
// 9. readCurrentStateWithFallback derives a substitute block from
|
|
42
|
+
// roles/*.Current_State + pending_hooks startChapter=0 rows when the
|
|
43
|
+
// seed placeholder is still on disk.
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
const ZERO_USAGE = {
|
|
47
|
+
promptTokens: 0,
|
|
48
|
+
completionTokens: 0,
|
|
49
|
+
totalTokens: 0,
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
function buildAgent(): ArchitectAgent {
|
|
53
|
+
return new ArchitectAgent({
|
|
54
|
+
client: {
|
|
55
|
+
provider: "openai",
|
|
56
|
+
apiFormat: "chat",
|
|
57
|
+
stream: false,
|
|
58
|
+
defaults: {
|
|
59
|
+
temperature: 0.7,
|
|
60
|
+
maxTokens: 4096,
|
|
61
|
+
thinkingBudget: 0,
|
|
62
|
+
maxTokensCap: null,
|
|
63
|
+
extra: {},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
model: "test-model",
|
|
67
|
+
projectRoot: process.cwd(),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function baseBook(): BookConfig {
|
|
72
|
+
return {
|
|
73
|
+
id: "phase5-consolidated-book",
|
|
74
|
+
title: "Phase5 合并测试书",
|
|
75
|
+
platform: "other",
|
|
76
|
+
genre: "urban",
|
|
77
|
+
status: "active",
|
|
78
|
+
targetChapters: 60,
|
|
79
|
+
chapterWordCount: 2200,
|
|
80
|
+
language: "zh",
|
|
81
|
+
createdAt: "2026-04-15T00:00:00.000Z",
|
|
82
|
+
updatedAt: "2026-04-15T00:00:00.000Z",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const CONSOLIDATED_RESPONSE = [
|
|
87
|
+
"=== SECTION: story_frame ===",
|
|
88
|
+
"## 主题与基调",
|
|
89
|
+
"一段主题散文,结尾指向主角卡:本书主角是林辞,完整弧线详见 roles/主要角色/林辞.md。",
|
|
90
|
+
"## 核心冲突与对手定性",
|
|
91
|
+
"主角 vs 体制。对手有自己的逻辑。",
|
|
92
|
+
"## 世界观底色",
|
|
93
|
+
"湿冷的沿海城市,记录者与被记录者的对撞。含本书铁律:凡被记录的名字都会消失。",
|
|
94
|
+
"## 终局方向",
|
|
95
|
+
"最后一个镜头:码头日出。",
|
|
96
|
+
"",
|
|
97
|
+
"=== SECTION: volume_map ===",
|
|
98
|
+
"## 各卷主题与情绪曲线",
|
|
99
|
+
"三卷结构。",
|
|
100
|
+
"## 关键节点章",
|
|
101
|
+
"第 17 章让他回家。",
|
|
102
|
+
"## 卷间钩子与回收承诺",
|
|
103
|
+
"第 1 卷埋笔记本。",
|
|
104
|
+
"## 角色阶段性目标",
|
|
105
|
+
"卷一末:主角决定留下。",
|
|
106
|
+
"## 卷尾必须发生的改变",
|
|
107
|
+
"身份暴露。",
|
|
108
|
+
"## 节奏原则(具体化 + 通用)",
|
|
109
|
+
"1. 高潮间距:每 8-10 章一个大高潮。",
|
|
110
|
+
"2. 喘息频率:3 章高压后插 1 章喘息。",
|
|
111
|
+
"3. 钩子密度:每章章末 1 个主钩。",
|
|
112
|
+
"4. 信息释放节奏:前 1/3 释放 30%。",
|
|
113
|
+
"5. 爽点节奏:每 5 章一个智商爽点。",
|
|
114
|
+
"6. 情感节点递进:每 6 章一次。",
|
|
115
|
+
"",
|
|
116
|
+
"=== SECTION: roles ===",
|
|
117
|
+
"---ROLE---",
|
|
118
|
+
"tier: major",
|
|
119
|
+
"name: 林辞",
|
|
120
|
+
"---CONTENT---",
|
|
121
|
+
"## 核心标签",
|
|
122
|
+
"沉默、执拗",
|
|
123
|
+
"## 反差细节",
|
|
124
|
+
"会给流浪狗留罐头",
|
|
125
|
+
"## 人物小传",
|
|
126
|
+
"十五岁时失去父亲。",
|
|
127
|
+
"## 主角弧线(起点 → 终点 → 代价)",
|
|
128
|
+
"从沉默的旁观者走向沉默的见证人——代价是离开故乡。",
|
|
129
|
+
"## 当前现状",
|
|
130
|
+
"第 0 章时在码头边上的旧书店做账房,最近最烦心的是账本对不上。",
|
|
131
|
+
"## 关系网络",
|
|
132
|
+
"与沈默是旧友。",
|
|
133
|
+
"## 内在驱动",
|
|
134
|
+
"想知道父亲死前那一夜发生了什么。",
|
|
135
|
+
"## 成长弧光",
|
|
136
|
+
"从独行到托付。",
|
|
137
|
+
"---ROLE---",
|
|
138
|
+
"tier: major",
|
|
139
|
+
"name: 沈默",
|
|
140
|
+
"---CONTENT---",
|
|
141
|
+
"## 核心标签",
|
|
142
|
+
"精致、疏离",
|
|
143
|
+
"## 反差细节",
|
|
144
|
+
"唯独对林辞从不说谎",
|
|
145
|
+
"## 人物小传",
|
|
146
|
+
"体制内家庭。",
|
|
147
|
+
"## 当前现状",
|
|
148
|
+
"新任区域办公室副职,第 0 章刚上任一周。",
|
|
149
|
+
"## 关系网络",
|
|
150
|
+
"与林辞复杂。",
|
|
151
|
+
"## 内在驱动",
|
|
152
|
+
"在规则内做到最好。",
|
|
153
|
+
"## 成长弧光",
|
|
154
|
+
"被迫选择。",
|
|
155
|
+
"",
|
|
156
|
+
"=== SECTION: book_rules ===",
|
|
157
|
+
"---",
|
|
158
|
+
"version: \"1.0\"",
|
|
159
|
+
"protagonist:",
|
|
160
|
+
" name: 林辞",
|
|
161
|
+
" personalityLock: [沉默, 执拗]",
|
|
162
|
+
" behavioralConstraints: [不对长辈失礼]",
|
|
163
|
+
"prohibitions:",
|
|
164
|
+
" - 不得美化体制暴力",
|
|
165
|
+
"chapterTypesOverride: []",
|
|
166
|
+
"fatigueWordsOverride: []",
|
|
167
|
+
"additionalAuditDimensions: []",
|
|
168
|
+
"enableFullCastTracking: false",
|
|
169
|
+
"---",
|
|
170
|
+
"",
|
|
171
|
+
"=== SECTION: pending_hooks ===",
|
|
172
|
+
"| hook_id | 起始章节 | 类型 | 状态 | 最近推进 | 预期回收 | 回收节奏 | 上游依赖 | 回收卷 | 核心 | 半衰期 | 备注 |",
|
|
173
|
+
"| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |",
|
|
174
|
+
"| H01 | 1 | 主线 | 未开启 | 0 | 终章揭晓 | 终局 | 无 | 第3卷终章前 | 是 | 80 | 父亲的笔记本 |",
|
|
175
|
+
"| H02 | 0 | 初始世界 | 未开启 | 0 | 首卷中段 | 近期 | 无 | 第1卷中段 | 否 | 20 | 初始状态:体制已监视码头 |",
|
|
176
|
+
].join("\n");
|
|
177
|
+
|
|
178
|
+
describe("Phase 5 consolidation — 7→5 sections, prompt contract", () => {
|
|
179
|
+
it("the zh prompt advertises exactly 5 SECTION headers (NO current_state, NO rhythm_principles)", async () => {
|
|
180
|
+
const agent = buildAgent();
|
|
181
|
+
const chat = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
182
|
+
.mockResolvedValue({ content: CONSOLIDATED_RESPONSE, usage: ZERO_USAGE });
|
|
183
|
+
|
|
184
|
+
await agent.generateFoundation(baseBook());
|
|
185
|
+
|
|
186
|
+
const messages = chat.mock.calls[0]?.[0] as Array<{ role: string; content: string }>;
|
|
187
|
+
const system = messages[0]?.content ?? "";
|
|
188
|
+
|
|
189
|
+
const headers = [...system.matchAll(/^=== SECTION: ([a-z_]+) ===$/gim)]
|
|
190
|
+
.map((match) => match[1]);
|
|
191
|
+
expect(headers).toEqual([
|
|
192
|
+
"story_frame",
|
|
193
|
+
"volume_map",
|
|
194
|
+
"roles",
|
|
195
|
+
"book_rules",
|
|
196
|
+
"pending_hooks",
|
|
197
|
+
]);
|
|
198
|
+
// rhythm_principles is explicitly NOT a standalone section — it still
|
|
199
|
+
// lives inside the last paragraph of volume_map.
|
|
200
|
+
expect(headers).not.toContain("rhythm_principles");
|
|
201
|
+
// current_state is no longer emitted by the architect either — era/setting
|
|
202
|
+
// context, when relevant, lives inside story_frame.世界观底色.
|
|
203
|
+
expect(headers).not.toContain("current_state");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("the prompt forbids duplication across sections (dedup rule)", async () => {
|
|
207
|
+
const agent = buildAgent();
|
|
208
|
+
const chat = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
209
|
+
.mockResolvedValue({ content: CONSOLIDATED_RESPONSE, usage: ZERO_USAGE });
|
|
210
|
+
|
|
211
|
+
await agent.generateFoundation(baseBook());
|
|
212
|
+
const system = (chat.mock.calls[0]?.[0] as Array<{ content: string }>)[0]?.content ?? "";
|
|
213
|
+
|
|
214
|
+
// Protagonist arc: authoritative home is roles
|
|
215
|
+
expect(system).toContain("主角弧线只写在 roles");
|
|
216
|
+
// World hard rules: authoritative home is story_frame.世界观底色
|
|
217
|
+
expect(system).toContain("世界铁律只写在 story_frame.世界观底色");
|
|
218
|
+
// Rhythm principles: authoritative home is volume_map's closing paragraph
|
|
219
|
+
expect(system).toContain("节奏原则只写在 volume_map 最后一段");
|
|
220
|
+
// Era/setting guidance: weave into story_frame.世界观底色 for year-anchored
|
|
221
|
+
// genres, omit entirely for others. NOT a separate current_state section.
|
|
222
|
+
expect(system).toContain("年代文");
|
|
223
|
+
expect(system).not.toContain("只写在 current_state");
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("the prompt carries explicit per-section char budget markers (NO current_state budget)", async () => {
|
|
227
|
+
const agent = buildAgent();
|
|
228
|
+
const chat = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
229
|
+
.mockResolvedValue({ content: CONSOLIDATED_RESPONSE, usage: ZERO_USAGE });
|
|
230
|
+
|
|
231
|
+
await agent.generateFoundation(baseBook());
|
|
232
|
+
const system = (chat.mock.calls[0]?.[0] as Array<{ content: string }>)[0]?.content ?? "";
|
|
233
|
+
|
|
234
|
+
expect(system).toContain("story_frame ≤ 3000 chars");
|
|
235
|
+
expect(system).toContain("volume_map ≤ 5000 chars");
|
|
236
|
+
expect(system).toContain("roles 总 ≤ 8000 chars");
|
|
237
|
+
expect(system).toContain("book_rules ≤ 500 chars");
|
|
238
|
+
expect(system).toContain("pending_hooks ≤ 2000 chars");
|
|
239
|
+
// current_state budget is gone — it's not a section any more.
|
|
240
|
+
expect(system).not.toContain("current_state 500-800 chars");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("the rhythm principles prompt allows a mix of universal + concrete (≥3 concretized, rest may stay universal)", async () => {
|
|
244
|
+
const agent = buildAgent();
|
|
245
|
+
const chat = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
246
|
+
.mockResolvedValue({ content: CONSOLIDATED_RESPONSE, usage: ZERO_USAGE });
|
|
247
|
+
|
|
248
|
+
await agent.generateFoundation(baseBook());
|
|
249
|
+
const system = (chat.mock.calls[0]?.[0] as Array<{ content: string }>)[0]?.content ?? "";
|
|
250
|
+
|
|
251
|
+
// Header renamed to signal the mix is legal
|
|
252
|
+
expect(system).toContain("节奏原则(具体化 + 通用)");
|
|
253
|
+
// Rule: at least 3 of 6 must be concretized to this book
|
|
254
|
+
expect(system).toContain("至少 3 条必须具体化到本书");
|
|
255
|
+
// Universal principles are explicitly allowed as examples
|
|
256
|
+
expect(system).toContain("拒绝机械降神");
|
|
257
|
+
// And the mix is explicitly called legal
|
|
258
|
+
expect(system).toContain("具体化 + 通用混合是合法的");
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("the English prompt also carries the 5-section / dedup / budget rules and rhythm universal allowance", async () => {
|
|
262
|
+
const agent = buildAgent();
|
|
263
|
+
const chat = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
264
|
+
.mockResolvedValue({ content: CONSOLIDATED_RESPONSE, usage: ZERO_USAGE });
|
|
265
|
+
|
|
266
|
+
const enBook: BookConfig = { ...baseBook(), language: "en" };
|
|
267
|
+
await agent.generateFoundation(enBook);
|
|
268
|
+
const system = (chat.mock.calls[0]?.[0] as Array<{ content: string }>)[0]?.content ?? "";
|
|
269
|
+
|
|
270
|
+
const headers = [...system.matchAll(/^=== SECTION: ([a-z_]+) ===$/gim)]
|
|
271
|
+
.map((match) => match[1]);
|
|
272
|
+
expect(headers).toEqual([
|
|
273
|
+
"story_frame",
|
|
274
|
+
"volume_map",
|
|
275
|
+
"roles",
|
|
276
|
+
"book_rules",
|
|
277
|
+
"pending_hooks",
|
|
278
|
+
]);
|
|
279
|
+
expect(system).toContain("story_frame ≤ 3000 chars");
|
|
280
|
+
expect(system).not.toContain("current_state 500-800 chars");
|
|
281
|
+
expect(system).toContain("YAML only");
|
|
282
|
+
// Rhythm universal allowance
|
|
283
|
+
expect(system).toContain("At least 3 must be concretized for this book");
|
|
284
|
+
expect(system).toContain("no deus ex machina");
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("book_rules prompt block instructs YAML only, no prose", async () => {
|
|
288
|
+
const agent = buildAgent();
|
|
289
|
+
const chat = vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
290
|
+
.mockResolvedValue({ content: CONSOLIDATED_RESPONSE, usage: ZERO_USAGE });
|
|
291
|
+
|
|
292
|
+
await agent.generateFoundation(baseBook());
|
|
293
|
+
const system = (chat.mock.calls[0]?.[0] as Array<{ content: string }>)[0]?.content ?? "";
|
|
294
|
+
|
|
295
|
+
expect(system).toContain("只输出 YAML frontmatter 一块——零散文");
|
|
296
|
+
// The pre-consolidation prompt used to ask for `## 叙事视角` AND
|
|
297
|
+
// `## 核心冲突驱动` prose blocks inside book_rules — those are gone.
|
|
298
|
+
expect(system).not.toMatch(/=== SECTION: book_rules ===[\s\S]*?## 叙事视角/);
|
|
299
|
+
expect(system).not.toMatch(/=== SECTION: book_rules ===[\s\S]*?## 核心冲突驱动/);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe("Phase 5 consolidation — parser accepts 5-section output (current_state is optional)", () => {
|
|
304
|
+
let bookDir: string;
|
|
305
|
+
|
|
306
|
+
beforeEach(async () => {
|
|
307
|
+
bookDir = await mkdtemp(join(tmpdir(), "inkos-phase5-cons-"));
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
afterEach(async () => {
|
|
311
|
+
await rm(bookDir, { recursive: true, force: true });
|
|
312
|
+
vi.restoreAllMocks();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("accepts a response with no current_state section and seeds the placeholder on disk", async () => {
|
|
316
|
+
const agent = buildAgent();
|
|
317
|
+
// CONSOLIDATED_RESPONSE already omits current_state — feed it straight in.
|
|
318
|
+
vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
319
|
+
.mockResolvedValue({ content: CONSOLIDATED_RESPONSE, usage: ZERO_USAGE });
|
|
320
|
+
|
|
321
|
+
const out = await agent.generateFoundation(baseBook());
|
|
322
|
+
await agent.writeFoundationFiles(bookDir, out, false, "zh");
|
|
323
|
+
|
|
324
|
+
// Architect output has empty currentState — there's no section to parse.
|
|
325
|
+
expect(out.currentState.trim()).toBe("");
|
|
326
|
+
|
|
327
|
+
// writeFoundationFiles still writes current_state.md, but as a seed
|
|
328
|
+
// placeholder the fallback reader can detect.
|
|
329
|
+
const onDisk = await readFile(join(bookDir, "story/current_state.md"), "utf-8");
|
|
330
|
+
expect(isCurrentStateSeedPlaceholder(onDisk)).toBe(true);
|
|
331
|
+
expect(onDisk).toContain("建书时占位");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it("preserves legacy 7-section input (current_state + rhythm_principles still present)", async () => {
|
|
335
|
+
const legacyResponse = [
|
|
336
|
+
"=== SECTION: story_frame ===",
|
|
337
|
+
"# frame",
|
|
338
|
+
"=== SECTION: volume_map ===",
|
|
339
|
+
"# map",
|
|
340
|
+
"=== SECTION: rhythm_principles ===",
|
|
341
|
+
"# legacy rhythm — accepted but no longer required",
|
|
342
|
+
"=== SECTION: roles ===",
|
|
343
|
+
"---ROLE---",
|
|
344
|
+
"tier: major",
|
|
345
|
+
"name: 林辞",
|
|
346
|
+
"---CONTENT---",
|
|
347
|
+
"## 核心标签",
|
|
348
|
+
"沉默",
|
|
349
|
+
"## 反差细节",
|
|
350
|
+
"会给流浪狗留罐头",
|
|
351
|
+
"## 人物小传",
|
|
352
|
+
"过往。",
|
|
353
|
+
"## 当前现状",
|
|
354
|
+
"码头账房。",
|
|
355
|
+
"## 关系网络",
|
|
356
|
+
"无。",
|
|
357
|
+
"## 内在驱动",
|
|
358
|
+
"查清真相。",
|
|
359
|
+
"## 成长弧光",
|
|
360
|
+
"从独行到托付。",
|
|
361
|
+
"=== SECTION: book_rules ===",
|
|
362
|
+
"---",
|
|
363
|
+
"version: \"1.0\"",
|
|
364
|
+
"---",
|
|
365
|
+
"",
|
|
366
|
+
"## 叙事视角",
|
|
367
|
+
"第三人称(legacy prose body — parser accepts but it no longer drives anything)",
|
|
368
|
+
"=== SECTION: current_state ===",
|
|
369
|
+
"| 字段 | 值 |",
|
|
370
|
+
"| --- | --- |",
|
|
371
|
+
"| 当前章节 | 0 |",
|
|
372
|
+
"| 当前位置 | 码头 |",
|
|
373
|
+
"=== SECTION: pending_hooks ===",
|
|
374
|
+
"| hook_id | 起始章节 | 类型 | 状态 | 最近推进 | 预期回收 | 回收节奏 | 备注 |",
|
|
375
|
+
"| --- | --- | --- | --- | --- | --- | --- | --- |",
|
|
376
|
+
"| H01 | 1 | 主线 | 未开启 | 0 | 终章 | 终局 | 父亲的笔记本 |",
|
|
377
|
+
].join("\n");
|
|
378
|
+
|
|
379
|
+
const agent = buildAgent();
|
|
380
|
+
vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
381
|
+
.mockResolvedValue({ content: legacyResponse, usage: ZERO_USAGE });
|
|
382
|
+
|
|
383
|
+
const out = await agent.generateFoundation(baseBook());
|
|
384
|
+
|
|
385
|
+
// Legacy content is preserved as-is
|
|
386
|
+
expect(out.currentState).toContain("当前位置");
|
|
387
|
+
expect(out.rhythmPrinciples).toContain("legacy rhythm");
|
|
388
|
+
expect((out.roles ?? []).length).toBeGreaterThanOrEqual(1);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe("Phase 5 consolidation — readCurrentStateWithFallback derives initial state", () => {
|
|
393
|
+
let bookDir: string;
|
|
394
|
+
|
|
395
|
+
beforeEach(async () => {
|
|
396
|
+
bookDir = await mkdtemp(join(tmpdir(), "inkos-phase5-fallback-"));
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
afterEach(async () => {
|
|
400
|
+
await rm(bookDir, { recursive: true, force: true });
|
|
401
|
+
vi.restoreAllMocks();
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it("returns a derived block built from roles/*.Current_State + seed hooks when current_state.md is a seed placeholder", async () => {
|
|
405
|
+
// CONSOLIDATED_RESPONSE emits no current_state section — writeFoundationFiles
|
|
406
|
+
// seeds the placeholder, which triggers the fallback reader.
|
|
407
|
+
const agent = buildAgent();
|
|
408
|
+
vi.spyOn(agent as unknown as { chat: (...args: unknown[]) => Promise<unknown> }, "chat")
|
|
409
|
+
.mockResolvedValue({ content: CONSOLIDATED_RESPONSE, usage: ZERO_USAGE });
|
|
410
|
+
|
|
411
|
+
const out = await agent.generateFoundation(baseBook());
|
|
412
|
+
await agent.writeFoundationFiles(bookDir, out, false, "zh");
|
|
413
|
+
|
|
414
|
+
const derived = await readCurrentStateWithFallback(bookDir, "(missing)");
|
|
415
|
+
// Derived block should mention the role names and their Current_State text.
|
|
416
|
+
expect(derived).toContain("初始状态");
|
|
417
|
+
expect(derived).toContain("林辞");
|
|
418
|
+
expect(derived).toContain("码头边上的旧书店做账房");
|
|
419
|
+
expect(derived).toContain("沈默");
|
|
420
|
+
expect(derived).toContain("新任区域办公室副职");
|
|
421
|
+
// Seed hook row startChapter=0 surfaces in the derived block.
|
|
422
|
+
expect(derived).toContain("H02");
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("returns the file content as-is when current_state.md already has runtime content", async () => {
|
|
426
|
+
const storyDir = join(bookDir, "story");
|
|
427
|
+
await mkdir(storyDir, { recursive: true });
|
|
428
|
+
const runtime = "# 当前状态\n\n- 第 5 章后主角正式加入合作社。\n- 与体制的关系:明面协作、暗中抵抗。\n";
|
|
429
|
+
await writeFile(join(storyDir, "current_state.md"), runtime, "utf-8");
|
|
430
|
+
|
|
431
|
+
const derived = await readCurrentStateWithFallback(bookDir, "(missing)");
|
|
432
|
+
expect(derived).toBe(runtime);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it("isCurrentStateSeedPlaceholder correctly identifies seeds vs real content", () => {
|
|
436
|
+
expect(isCurrentStateSeedPlaceholder("")).toBe(true);
|
|
437
|
+
expect(isCurrentStateSeedPlaceholder("# 当前状态\n\n> 建书时占位。后续章节补。\n")).toBe(true);
|
|
438
|
+
expect(isCurrentStateSeedPlaceholder("# Current State\n\n> Seeded at book creation.\n")).toBe(true);
|
|
439
|
+
// A real consolidator-appended block — no seed marker
|
|
440
|
+
expect(isCurrentStateSeedPlaceholder("# 当前状态\n\n- 主角现状一二三\n- 体制关系四五六\n")).toBe(false);
|
|
441
|
+
// A long file that happens to contain the seed marker in prose — NOT a seed
|
|
442
|
+
const longContent = "# 当前状态\n\n" + "一段很长的实际内容。".repeat(200) + "\n建书时占位\n";
|
|
443
|
+
expect(isCurrentStateSeedPlaceholder(longContent)).toBe(false);
|
|
444
|
+
});
|
|
445
|
+
});
|