@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,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 (v13) path resolution — prefer the new prose outline files, fall
|
|
3
|
+
* back to legacy paths so older books keep working during transition.
|
|
4
|
+
*
|
|
5
|
+
* Maps:
|
|
6
|
+
* story/outline/story_frame.md → preferred replacement for story_bible.md
|
|
7
|
+
* story/outline/volume_map.md → preferred replacement for volume_outline.md
|
|
8
|
+
* story/roles/主要角色/*.md +
|
|
9
|
+
* story/roles/次要角色/*.md → preferred replacement for character_matrix.md
|
|
10
|
+
*
|
|
11
|
+
* All helpers accept a bookDir (path to a book root, containing `story/`)
|
|
12
|
+
* and return a string — either the new-file content when it exists, or the
|
|
13
|
+
* legacy file content, or an empty default placeholder.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFile, readdir, access } from "node:fs/promises";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Detect whether a book uses the Phase 5 new layout (outline/story_frame.md
|
|
21
|
+
* exists on disk). If yes, story_bible.md / book_rules.md are compat shims.
|
|
22
|
+
* If no, those files ARE the authoritative source.
|
|
23
|
+
*/
|
|
24
|
+
export async function isNewLayoutBook(bookDir: string): Promise<boolean> {
|
|
25
|
+
try {
|
|
26
|
+
await access(join(bookDir, "story", "outline", "story_frame.md"));
|
|
27
|
+
return true;
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function readOr(path: string, fallback: string): Promise<string> {
|
|
34
|
+
try {
|
|
35
|
+
return await readFile(path, "utf-8");
|
|
36
|
+
} catch {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Read story_frame.md, falling back to legacy story_bible.md. */
|
|
42
|
+
export async function readStoryFrame(
|
|
43
|
+
bookDir: string,
|
|
44
|
+
fallbackPlaceholder: string = "",
|
|
45
|
+
): Promise<string> {
|
|
46
|
+
const newPath = join(bookDir, "story", "outline", "story_frame.md");
|
|
47
|
+
const legacyPath = join(bookDir, "story", "story_bible.md");
|
|
48
|
+
|
|
49
|
+
const newContent = await readOr(newPath, "");
|
|
50
|
+
if (newContent.trim()) return newContent;
|
|
51
|
+
|
|
52
|
+
return readOr(legacyPath, fallbackPlaceholder);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Read volume_map.md, falling back to legacy volume_outline.md. */
|
|
56
|
+
export async function readVolumeMap(
|
|
57
|
+
bookDir: string,
|
|
58
|
+
fallbackPlaceholder: string = "",
|
|
59
|
+
): Promise<string> {
|
|
60
|
+
const newPath = join(bookDir, "story", "outline", "volume_map.md");
|
|
61
|
+
const legacyPath = join(bookDir, "story", "volume_outline.md");
|
|
62
|
+
|
|
63
|
+
const newContent = await readOr(newPath, "");
|
|
64
|
+
if (newContent.trim()) return newContent;
|
|
65
|
+
|
|
66
|
+
return readOr(legacyPath, fallbackPlaceholder);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Read the rhythm principles file (zh or en variant). */
|
|
70
|
+
export async function readRhythmPrinciples(bookDir: string): Promise<string> {
|
|
71
|
+
const zhPath = join(bookDir, "story", "outline", "节奏原则.md");
|
|
72
|
+
const enPath = join(bookDir, "story", "outline", "rhythm_principles.md");
|
|
73
|
+
|
|
74
|
+
const zh = await readOr(zhPath, "");
|
|
75
|
+
if (zh.trim()) return zh;
|
|
76
|
+
return readOr(enPath, "");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface RoleCard {
|
|
80
|
+
readonly tier: "major" | "minor";
|
|
81
|
+
readonly name: string;
|
|
82
|
+
readonly content: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Read the roles/ directory. Returns [] when no roles are present (e.g. old
|
|
87
|
+
* books still on character_matrix.md).
|
|
88
|
+
*/
|
|
89
|
+
export async function readRoleCards(bookDir: string): Promise<ReadonlyArray<RoleCard>> {
|
|
90
|
+
const rolesRoot = join(bookDir, "story", "roles");
|
|
91
|
+
const majorDirZh = join(rolesRoot, "主要角色");
|
|
92
|
+
const minorDirZh = join(rolesRoot, "次要角色");
|
|
93
|
+
const majorDirEn = join(rolesRoot, "major");
|
|
94
|
+
const minorDirEn = join(rolesRoot, "minor");
|
|
95
|
+
|
|
96
|
+
const cards: RoleCard[] = [];
|
|
97
|
+
await Promise.all([
|
|
98
|
+
collectRoleDir(majorDirZh, "major", cards),
|
|
99
|
+
collectRoleDir(minorDirZh, "minor", cards),
|
|
100
|
+
collectRoleDir(majorDirEn, "major", cards),
|
|
101
|
+
collectRoleDir(minorDirEn, "minor", cards),
|
|
102
|
+
]);
|
|
103
|
+
return cards;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function collectRoleDir(
|
|
107
|
+
dir: string,
|
|
108
|
+
tier: "major" | "minor",
|
|
109
|
+
out: RoleCard[],
|
|
110
|
+
): Promise<void> {
|
|
111
|
+
let entries: string[];
|
|
112
|
+
try {
|
|
113
|
+
entries = await readdir(dir);
|
|
114
|
+
} catch {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const reads = entries
|
|
118
|
+
.filter((entry) => entry.endsWith(".md"))
|
|
119
|
+
.map(async (entry) => {
|
|
120
|
+
const content = await readOr(join(dir, entry), "");
|
|
121
|
+
if (!content.trim()) return;
|
|
122
|
+
out.push({
|
|
123
|
+
tier,
|
|
124
|
+
name: entry.replace(/\.md$/, ""),
|
|
125
|
+
content,
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
await Promise.all(reads);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Render role cards in a format compatible with downstream consumers that
|
|
133
|
+
* previously expected character_matrix.md prose. When no role cards exist,
|
|
134
|
+
* returns the legacy character_matrix.md content or the placeholder.
|
|
135
|
+
*/
|
|
136
|
+
export async function readCharacterContext(
|
|
137
|
+
bookDir: string,
|
|
138
|
+
fallbackPlaceholder: string = "",
|
|
139
|
+
): Promise<string> {
|
|
140
|
+
const cards = await readRoleCards(bookDir);
|
|
141
|
+
if (cards.length > 0) {
|
|
142
|
+
const groups: Record<"major" | "minor", RoleCard[]> = { major: [], minor: [] };
|
|
143
|
+
for (const card of cards) groups[card.tier].push(card);
|
|
144
|
+
|
|
145
|
+
const render = (tierCards: RoleCard[], heading: string): string => {
|
|
146
|
+
if (tierCards.length === 0) return "";
|
|
147
|
+
const sections = tierCards.map((card) => `### ${card.name}\n\n${card.content.trim()}`);
|
|
148
|
+
return `## ${heading}\n\n${sections.join("\n\n")}`;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const blocks = [
|
|
152
|
+
render(groups.major, "主要角色 / Major characters"),
|
|
153
|
+
render(groups.minor, "次要角色 / Minor characters"),
|
|
154
|
+
].filter(Boolean);
|
|
155
|
+
|
|
156
|
+
return blocks.join("\n\n");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Fallback: legacy character_matrix.md (may itself be a shim pointer).
|
|
160
|
+
const legacyPath = join(bookDir, "story", "character_matrix.md");
|
|
161
|
+
return readOr(legacyPath, fallbackPlaceholder);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// Phase 5 consolidation: current_state.md initial fallback
|
|
166
|
+
//
|
|
167
|
+
// After architect consolidation (7→5 sections), current_state.md is seeded
|
|
168
|
+
// with a tiny placeholder at book creation. Real content only arrives once
|
|
169
|
+
// the consolidator has appended output from chapter 1 onward. Readers that
|
|
170
|
+
// previously relied on architect-provided initial state (writer phase-1
|
|
171
|
+
// creative prompt, continuity, chapter-analyzer, reviser, composer) should
|
|
172
|
+
// substitute a derived initial-state block when the seed placeholder is all
|
|
173
|
+
// that's on disk — otherwise the "## 当前状态卡" block in prompts degenerates
|
|
174
|
+
// into a meta note about runtime append behaviour.
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Marker substring emitted by architect.writeFoundationFiles when seeding
|
|
179
|
+
* current_state.md. Its presence is how readers detect "nothing real yet".
|
|
180
|
+
*/
|
|
181
|
+
const CURRENT_STATE_SEED_MARKERS = [
|
|
182
|
+
"建书时占位",
|
|
183
|
+
"Seeded at book creation",
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
export function isCurrentStateSeedPlaceholder(raw: string): boolean {
|
|
187
|
+
const trimmed = raw.trim();
|
|
188
|
+
if (!trimmed) return true;
|
|
189
|
+
// Heuristic: short file AND contains one of the seed markers.
|
|
190
|
+
if (trimmed.length > 600) return false;
|
|
191
|
+
return CURRENT_STATE_SEED_MARKERS.some((marker) => trimmed.includes(marker));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function extractCurrentStateFromRole(content: string): string | null {
|
|
195
|
+
// Accept both zh (`## 当前现状`) and en (`## Current_State` / `## Current State`).
|
|
196
|
+
const pattern = /^##\s*(?:当前现状|Current[_\s]?State)[^\n]*$/im;
|
|
197
|
+
const match = content.match(pattern);
|
|
198
|
+
if (!match || match.index === undefined) return null;
|
|
199
|
+
const after = content.slice(match.index + match[0].length);
|
|
200
|
+
// Cut at next `## ` heading (same or higher level).
|
|
201
|
+
const nextHeading = after.search(/^##\s/m);
|
|
202
|
+
const raw = nextHeading >= 0 ? after.slice(0, nextHeading) : after;
|
|
203
|
+
const text = raw.trim();
|
|
204
|
+
return text.length > 0 ? text : null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function extractSeedHooksFromPendingHooks(raw: string): string[] {
|
|
208
|
+
if (!raw.trim()) return [];
|
|
209
|
+
const lines = raw.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
210
|
+
const seedRows: string[] = [];
|
|
211
|
+
for (const line of lines) {
|
|
212
|
+
if (!line.startsWith("|")) continue;
|
|
213
|
+
if (/^\|\s*-/.test(line)) continue;
|
|
214
|
+
const cells = line.split("|").slice(1, -1).map((cell) => cell.trim());
|
|
215
|
+
if (cells.length < 2) continue;
|
|
216
|
+
if (cells[0]?.toLowerCase() === "hook_id" || cells[0] === "hookId") continue;
|
|
217
|
+
const startCh = Number.parseInt(cells[1] ?? "", 10);
|
|
218
|
+
if (!Number.isFinite(startCh) || startCh !== 0) continue;
|
|
219
|
+
// cells[2] type, cells[5] expected payoff, last cell notes
|
|
220
|
+
const notes = cells[cells.length - 1] ?? "";
|
|
221
|
+
const summary = [cells[0], cells[2], notes].filter(Boolean).join(" · ");
|
|
222
|
+
if (summary) seedRows.push(summary);
|
|
223
|
+
}
|
|
224
|
+
return seedRows;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Read current_state.md; when the file is only a seed placeholder (chapter 0,
|
|
229
|
+
* before consolidator has appended anything), derive an initial-state block
|
|
230
|
+
* from roles/*.Current_State + pending_hooks startChapter=0 rows so callers
|
|
231
|
+
* still have substantive content to feed into writer / analyzer prompts.
|
|
232
|
+
*/
|
|
233
|
+
export async function readCurrentStateWithFallback(
|
|
234
|
+
bookDir: string,
|
|
235
|
+
fallbackPlaceholder: string = "",
|
|
236
|
+
): Promise<string> {
|
|
237
|
+
const storyDir = join(bookDir, "story");
|
|
238
|
+
const currentStatePath = join(storyDir, "current_state.md");
|
|
239
|
+
const raw = await readOr(currentStatePath, "");
|
|
240
|
+
|
|
241
|
+
if (!isCurrentStateSeedPlaceholder(raw)) {
|
|
242
|
+
return raw;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const [cards, pendingHooks] = await Promise.all([
|
|
246
|
+
readRoleCards(bookDir),
|
|
247
|
+
readOr(join(storyDir, "pending_hooks.md"), ""),
|
|
248
|
+
]);
|
|
249
|
+
|
|
250
|
+
const roleLines = cards
|
|
251
|
+
.map((card) => {
|
|
252
|
+
const state = extractCurrentStateFromRole(card.content);
|
|
253
|
+
if (!state) return null;
|
|
254
|
+
const tierLabel = card.tier === "major" ? "主要" : "次要";
|
|
255
|
+
return `- ${card.name}(${tierLabel}):${state.replace(/\s+/g, " ")}`;
|
|
256
|
+
})
|
|
257
|
+
.filter((line): line is string => line !== null);
|
|
258
|
+
|
|
259
|
+
const hookLines = extractSeedHooksFromPendingHooks(pendingHooks);
|
|
260
|
+
|
|
261
|
+
if (roleLines.length === 0 && hookLines.length === 0) {
|
|
262
|
+
return raw.trim() ? raw : fallbackPlaceholder;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const parts: string[] = ["# 初始状态(第 0 章,由 roles + 种子伏笔派生)"];
|
|
266
|
+
if (roleLines.length > 0) {
|
|
267
|
+
parts.push("\n## 角色初始位置 / 处境");
|
|
268
|
+
parts.push(...roleLines);
|
|
269
|
+
}
|
|
270
|
+
if (hookLines.length > 0) {
|
|
271
|
+
parts.push("\n## 种子伏笔(startChapter = 0)");
|
|
272
|
+
parts.push(...hookLines.map((line) => `- ${line}`));
|
|
273
|
+
}
|
|
274
|
+
return parts.join("\n");
|
|
275
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
2
|
+
|
|
3
|
+
export function safeChildPath(root: string, requestedPath: string): string {
|
|
4
|
+
const resolvedRoot = resolve(root);
|
|
5
|
+
const resolvedPath = resolve(resolvedRoot, requestedPath);
|
|
6
|
+
const rel = relative(resolvedRoot, resolvedPath);
|
|
7
|
+
if (rel === "" || (!rel.startsWith("..") && !isAbsolute(rel))) {
|
|
8
|
+
return resolvedPath;
|
|
9
|
+
}
|
|
10
|
+
throw new Error(`Path traversal blocked: ${requestedPath}`);
|
|
11
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import type { StoredHook, StoredSummary } from "../state/memory-db.js";
|
|
4
|
+
import {
|
|
5
|
+
parseChapterSummariesMarkdown,
|
|
6
|
+
retrieveMemorySelection,
|
|
7
|
+
type MemorySelection,
|
|
8
|
+
} from "./memory-retrieval.js";
|
|
9
|
+
import {
|
|
10
|
+
readStoryFrame,
|
|
11
|
+
readVolumeMap,
|
|
12
|
+
readCurrentStateWithFallback,
|
|
13
|
+
} from "./outline-paths.js";
|
|
14
|
+
|
|
15
|
+
export interface PlanningSeedMaterials {
|
|
16
|
+
readonly storyDir: string;
|
|
17
|
+
readonly authorIntent: string;
|
|
18
|
+
readonly currentFocus: string;
|
|
19
|
+
readonly storyBible: string;
|
|
20
|
+
readonly volumeOutline: string;
|
|
21
|
+
readonly bookRulesRaw: string;
|
|
22
|
+
readonly currentState: string;
|
|
23
|
+
readonly chapterSummariesRaw: string;
|
|
24
|
+
readonly brief: string;
|
|
25
|
+
readonly outlineNode?: string;
|
|
26
|
+
readonly recentSummaries: ReadonlyArray<StoredSummary>;
|
|
27
|
+
readonly previousEndingHook?: string;
|
|
28
|
+
readonly previousEndingExcerpt?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface PlanningMaterials extends PlanningSeedMaterials {
|
|
32
|
+
readonly activeHooks: ReadonlyArray<StoredHook>;
|
|
33
|
+
readonly memorySelection: MemorySelection;
|
|
34
|
+
readonly plannerInputs: ReadonlyArray<string>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function readFileOrDefault(path: string): Promise<string> {
|
|
38
|
+
try {
|
|
39
|
+
return await readFile(path, "utf-8");
|
|
40
|
+
} catch {
|
|
41
|
+
return "(文件尚未创建)";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function readBriefFile(path: string): Promise<string> {
|
|
46
|
+
try {
|
|
47
|
+
return await readFile(path, "utf-8");
|
|
48
|
+
} catch {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function readPreviousEndingExcerpt(
|
|
54
|
+
bookDir: string,
|
|
55
|
+
chapterNumber: number,
|
|
56
|
+
): Promise<string | undefined> {
|
|
57
|
+
const previousChapter = chapterNumber - 1;
|
|
58
|
+
if (previousChapter < 1) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const chaptersDir = join(bookDir, "chapters");
|
|
63
|
+
const padded = String(previousChapter).padStart(4, "0");
|
|
64
|
+
try {
|
|
65
|
+
const files = await readdir(chaptersDir);
|
|
66
|
+
const match = files.find((file) => file.startsWith(padded) && file.endsWith(".md"));
|
|
67
|
+
if (!match) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const markdown = await readFile(join(chaptersDir, match), "utf-8");
|
|
71
|
+
const body = markdown
|
|
72
|
+
.split("\n")
|
|
73
|
+
.slice(1)
|
|
74
|
+
.join("\n")
|
|
75
|
+
.trim();
|
|
76
|
+
if (!body) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
return body.slice(-320).trim();
|
|
80
|
+
} catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function loadPlanningSeedMaterials(params: {
|
|
86
|
+
readonly bookDir: string;
|
|
87
|
+
readonly chapterNumber: number;
|
|
88
|
+
}): Promise<PlanningSeedMaterials> {
|
|
89
|
+
const storyDir = join(params.bookDir, "story");
|
|
90
|
+
const sourcePaths = {
|
|
91
|
+
authorIntent: join(storyDir, "author_intent.md"),
|
|
92
|
+
currentFocus: join(storyDir, "current_focus.md"),
|
|
93
|
+
chapterSummaries: join(storyDir, "chapter_summaries.md"),
|
|
94
|
+
bookRules: join(storyDir, "book_rules.md"),
|
|
95
|
+
currentState: join(storyDir, "current_state.md"),
|
|
96
|
+
brief: join(storyDir, "brief.md"),
|
|
97
|
+
} as const;
|
|
98
|
+
|
|
99
|
+
// Phase 5: prefer the new prose outline files (outline/story_frame.md +
|
|
100
|
+
// outline/volume_map.md). Fall back to the legacy files transparently.
|
|
101
|
+
const placeholder = "(文件尚未创建)";
|
|
102
|
+
|
|
103
|
+
const [
|
|
104
|
+
authorIntent,
|
|
105
|
+
currentFocus,
|
|
106
|
+
storyBible,
|
|
107
|
+
volumeOutline,
|
|
108
|
+
chapterSummariesRaw,
|
|
109
|
+
bookRulesRaw,
|
|
110
|
+
currentState,
|
|
111
|
+
previousEndingExcerpt,
|
|
112
|
+
brief,
|
|
113
|
+
] = await Promise.all([
|
|
114
|
+
readFileOrDefault(sourcePaths.authorIntent),
|
|
115
|
+
readFileOrDefault(sourcePaths.currentFocus),
|
|
116
|
+
readStoryFrame(params.bookDir, placeholder),
|
|
117
|
+
readVolumeMap(params.bookDir, placeholder),
|
|
118
|
+
readFileOrDefault(sourcePaths.chapterSummaries),
|
|
119
|
+
readFileOrDefault(sourcePaths.bookRules),
|
|
120
|
+
// Phase 5 consolidation: derive initial state from roles + pending_hooks
|
|
121
|
+
// seed rows when current_state.md is still just the architect's placeholder.
|
|
122
|
+
readCurrentStateWithFallback(params.bookDir, placeholder),
|
|
123
|
+
readPreviousEndingExcerpt(params.bookDir, params.chapterNumber),
|
|
124
|
+
readBriefFile(sourcePaths.brief),
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
const chapterSummaries = parseChapterSummariesMarkdown(chapterSummariesRaw)
|
|
128
|
+
.filter((summary) => summary.chapter < params.chapterNumber)
|
|
129
|
+
.sort((left, right) => right.chapter - left.chapter);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
storyDir,
|
|
133
|
+
authorIntent,
|
|
134
|
+
currentFocus,
|
|
135
|
+
storyBible,
|
|
136
|
+
volumeOutline,
|
|
137
|
+
bookRulesRaw,
|
|
138
|
+
currentState,
|
|
139
|
+
chapterSummariesRaw,
|
|
140
|
+
brief,
|
|
141
|
+
recentSummaries: chapterSummaries.slice(0, 4).sort((left, right) => left.chapter - right.chapter),
|
|
142
|
+
previousEndingHook: chapterSummaries[0]?.hookActivity || undefined,
|
|
143
|
+
previousEndingExcerpt,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function gatherPlanningMaterials(params: {
|
|
148
|
+
readonly bookDir: string;
|
|
149
|
+
readonly chapterNumber: number;
|
|
150
|
+
readonly goal: string;
|
|
151
|
+
readonly outlineNode?: string;
|
|
152
|
+
readonly mustKeep?: ReadonlyArray<string>;
|
|
153
|
+
readonly seed?: PlanningSeedMaterials;
|
|
154
|
+
}): Promise<PlanningMaterials> {
|
|
155
|
+
const seed = params.seed ?? await loadPlanningSeedMaterials({
|
|
156
|
+
bookDir: params.bookDir,
|
|
157
|
+
chapterNumber: params.chapterNumber,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const memorySelection = await retrieveMemorySelection({
|
|
161
|
+
bookDir: params.bookDir,
|
|
162
|
+
chapterNumber: params.chapterNumber,
|
|
163
|
+
goal: params.goal,
|
|
164
|
+
outlineNode: params.outlineNode,
|
|
165
|
+
mustKeep: params.mustKeep,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
...seed,
|
|
170
|
+
outlineNode: params.outlineNode,
|
|
171
|
+
activeHooks: memorySelection.activeHooks,
|
|
172
|
+
memorySelection,
|
|
173
|
+
plannerInputs: [
|
|
174
|
+
join(seed.storyDir, "author_intent.md"),
|
|
175
|
+
join(seed.storyDir, "current_focus.md"),
|
|
176
|
+
join(seed.storyDir, "outline", "story_frame.md"),
|
|
177
|
+
join(seed.storyDir, "outline", "volume_map.md"),
|
|
178
|
+
join(seed.storyDir, "chapter_summaries.md"),
|
|
179
|
+
join(seed.storyDir, "book_rules.md"),
|
|
180
|
+
join(seed.storyDir, "current_state.md"),
|
|
181
|
+
join(seed.storyDir, "pending_hooks.md"),
|
|
182
|
+
...(memorySelection.dbPath ? [memorySelection.dbPath] : []),
|
|
183
|
+
],
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POV-aware context filtering.
|
|
3
|
+
*
|
|
4
|
+
* Filters truth file content based on the current POV character's
|
|
5
|
+
* information boundaries. Characters should only "see" information
|
|
6
|
+
* they've actually witnessed or been told about.
|
|
7
|
+
*
|
|
8
|
+
* Works with markdown-based truth files (no DB dependency).
|
|
9
|
+
* When MemoryDB is available, can do more precise queries.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extract the POV character from the volume outline for a given chapter.
|
|
14
|
+
* Looks for patterns like "POV: 角色名" or "视角: 角色名" or "POV: CharacterName"
|
|
15
|
+
* in the chapter's section of the outline.
|
|
16
|
+
*/
|
|
17
|
+
export function extractPOVFromOutline(volumeOutline: string, chapterNumber: number): string | null {
|
|
18
|
+
// Find the section for this chapter
|
|
19
|
+
const lines = volumeOutline.split("\n");
|
|
20
|
+
|
|
21
|
+
// Look for chapter reference near the chapter number
|
|
22
|
+
const chapterPatterns = [
|
|
23
|
+
new RegExp(`第${chapterNumber}章`),
|
|
24
|
+
new RegExp(`Chapter\\s+${chapterNumber}\\b`),
|
|
25
|
+
new RegExp(`\\b${chapterNumber}\\b.*章`),
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
let inChapterSection = false;
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
// Check if we're in the right chapter section
|
|
31
|
+
if (chapterPatterns.some((p) => p.test(line))) {
|
|
32
|
+
inChapterSection = true;
|
|
33
|
+
} else if (inChapterSection && /^[#-]/.test(line) && !line.includes(String(chapterNumber))) {
|
|
34
|
+
// Left the chapter section
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (inChapterSection) {
|
|
39
|
+
// Look for POV declaration
|
|
40
|
+
const povMatch = line.match(/(?:POV|视角|pov)[::\s]+([^\s,,。.、]+)/i);
|
|
41
|
+
if (povMatch) return povMatch[1]!;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Filter character_matrix information boundaries for the POV character.
|
|
50
|
+
* Returns only what the POV character knows — strips other characters' "known info".
|
|
51
|
+
*/
|
|
52
|
+
export function filterMatrixByPOV(characterMatrix: string, povCharacter: string): string {
|
|
53
|
+
if (!characterMatrix || characterMatrix === "(文件尚未创建)") return characterMatrix;
|
|
54
|
+
if (!povCharacter) return characterMatrix;
|
|
55
|
+
|
|
56
|
+
// Find the 信息边界 / Information Boundaries section
|
|
57
|
+
const sections = characterMatrix.split(/(?=^###)/m);
|
|
58
|
+
const filtered = sections.map((section) => {
|
|
59
|
+
const isInfoBoundary = /信息边界|Information\s+Boundar/i.test(section);
|
|
60
|
+
if (!isInfoBoundary) return section;
|
|
61
|
+
|
|
62
|
+
// In the info boundary table, keep only the POV character's row
|
|
63
|
+
// and add a note about what other characters know
|
|
64
|
+
const lines = section.split("\n");
|
|
65
|
+
const headerLines = lines.filter((l) =>
|
|
66
|
+
l.startsWith("|") && (l.includes("---") || l.includes("角色") || l.includes("Character") || l.includes("已知") || l.includes("Known")),
|
|
67
|
+
);
|
|
68
|
+
const dataLines = lines.filter((l) =>
|
|
69
|
+
l.startsWith("|") && !l.includes("---") && !l.includes("角色") && !l.includes("Character") && !l.includes("已知") && !l.includes("Known"),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Keep POV character's row + a summary note
|
|
73
|
+
const povRows = dataLines.filter((l) => l.includes(povCharacter));
|
|
74
|
+
const otherCharCount = dataLines.length - povRows.length;
|
|
75
|
+
|
|
76
|
+
const sectionHeader = lines.find((l) => l.startsWith("###"));
|
|
77
|
+
const result = [
|
|
78
|
+
sectionHeader ?? "### 信息边界",
|
|
79
|
+
`(当前视角:${povCharacter},其他 ${otherCharCount} 个角色的信息边界已隐藏)`,
|
|
80
|
+
...headerLines,
|
|
81
|
+
...povRows,
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
return result.join("\n");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return filtered.join("\n");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Filter pending_hooks by POV character's knowledge.
|
|
92
|
+
* Hooks planted in scenes where the POV character was NOT present are hidden.
|
|
93
|
+
*
|
|
94
|
+
* This is a heuristic: if the hook's chapter summary mentions the POV character,
|
|
95
|
+
* they likely know about it.
|
|
96
|
+
*/
|
|
97
|
+
export function filterHooksByPOV(
|
|
98
|
+
hooks: string,
|
|
99
|
+
povCharacter: string,
|
|
100
|
+
chapterSummaries: string,
|
|
101
|
+
): string {
|
|
102
|
+
if (!hooks || hooks === "(文件尚未创建)") return hooks;
|
|
103
|
+
if (!povCharacter) return hooks;
|
|
104
|
+
|
|
105
|
+
const lines = hooks.split("\n");
|
|
106
|
+
const headerLines = lines.filter((l) =>
|
|
107
|
+
l.startsWith("|") && (l.includes("hook_id") || l.includes("---")),
|
|
108
|
+
);
|
|
109
|
+
const dataLines = lines.filter((l) =>
|
|
110
|
+
l.startsWith("|") && !l.includes("hook_id") && !l.includes("---"),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Parse summary rows to find which chapters the POV character appeared in
|
|
114
|
+
const povChapters = new Set<number>();
|
|
115
|
+
if (chapterSummaries) {
|
|
116
|
+
for (const line of chapterSummaries.split("\n")) {
|
|
117
|
+
if (line.includes(povCharacter)) {
|
|
118
|
+
const match = line.match(/\|\s*(\d+)\s*\|/);
|
|
119
|
+
if (match) povChapters.add(parseInt(match[1]!, 10));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Keep hooks where:
|
|
125
|
+
// 1. The POV character was present in the source chapter, OR
|
|
126
|
+
// 2. The hook mentions the POV character directly, OR
|
|
127
|
+
// 3. We can't determine (keep to be safe)
|
|
128
|
+
const filtered = dataLines.filter((row) => {
|
|
129
|
+
// If hook directly mentions POV character, keep it
|
|
130
|
+
if (row.includes(povCharacter)) return true;
|
|
131
|
+
|
|
132
|
+
// Extract source chapter from hook row
|
|
133
|
+
const chapterMatch = row.match(/\|\s*(\d+)\s*\|/);
|
|
134
|
+
if (!chapterMatch) return true; // can't determine, keep
|
|
135
|
+
|
|
136
|
+
const sourceChapter = parseInt(chapterMatch[1]!, 10);
|
|
137
|
+
// If POV was in that chapter, they know about the hook
|
|
138
|
+
if (povChapters.has(sourceChapter)) return true;
|
|
139
|
+
|
|
140
|
+
// POV wasn't in that chapter — hide this hook
|
|
141
|
+
return false;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Fallback: if filtering removes everything, return original
|
|
145
|
+
if (filtered.length === 0 && dataLines.length > 0) return hooks;
|
|
146
|
+
|
|
147
|
+
const nonTableLines = lines.filter((l) => !l.startsWith("|"));
|
|
148
|
+
return [...nonTableLines, ...headerLines, ...filtered].join("\n");
|
|
149
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ProxyAgent } from "undici";
|
|
2
|
+
|
|
3
|
+
type ProxyEnv = Record<string, string | undefined>;
|
|
4
|
+
type FetchInitWithDispatcher = RequestInit & { dispatcher?: unknown };
|
|
5
|
+
|
|
6
|
+
export function resolveProxyUrl(explicitProxyUrl?: string, env: ProxyEnv = process.env): string | undefined {
|
|
7
|
+
const candidate = [
|
|
8
|
+
explicitProxyUrl,
|
|
9
|
+
env.INKOS_LLM_PROXY_URL,
|
|
10
|
+
env.HTTPS_PROXY,
|
|
11
|
+
env.https_proxy,
|
|
12
|
+
env.HTTP_PROXY,
|
|
13
|
+
env.http_proxy,
|
|
14
|
+
].find((value) => typeof value === "string" && value.trim().length > 0)?.trim();
|
|
15
|
+
|
|
16
|
+
if (!candidate) return undefined;
|
|
17
|
+
const parsed = new URL(candidate);
|
|
18
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
19
|
+
throw new Error(`Unsupported proxy protocol: ${parsed.protocol}`);
|
|
20
|
+
}
|
|
21
|
+
return candidate;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function buildProxyFetchInit(
|
|
25
|
+
init: RequestInit = {},
|
|
26
|
+
explicitProxyUrl?: string,
|
|
27
|
+
env: ProxyEnv = process.env,
|
|
28
|
+
): FetchInitWithDispatcher {
|
|
29
|
+
const proxyUrl = resolveProxyUrl(explicitProxyUrl, env);
|
|
30
|
+
if (!proxyUrl) return init;
|
|
31
|
+
return {
|
|
32
|
+
...init,
|
|
33
|
+
dispatcher: new ProxyAgent(proxyUrl) as any,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function fetchWithProxy(
|
|
38
|
+
input: Parameters<typeof fetch>[0],
|
|
39
|
+
init: RequestInit = {},
|
|
40
|
+
explicitProxyUrl?: string,
|
|
41
|
+
env: ProxyEnv = process.env,
|
|
42
|
+
): ReturnType<typeof fetch> {
|
|
43
|
+
return fetch(input, buildProxyFetchInit(init, explicitProxyUrl, env));
|
|
44
|
+
}
|