@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,469 @@
|
|
|
1
|
+
import { readFile, readdir, mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { BaseAgent } from "./base.js";
|
|
4
|
+
import type { BookConfig } from "../models/book.js";
|
|
5
|
+
import {
|
|
6
|
+
ContextPackageSchema,
|
|
7
|
+
type ChapterTrace,
|
|
8
|
+
type ContextPackage,
|
|
9
|
+
type RuleStack,
|
|
10
|
+
} from "../models/input-governance.js";
|
|
11
|
+
import type { PlanChapterOutput } from "./planner.js";
|
|
12
|
+
import {
|
|
13
|
+
parseChapterSummariesMarkdown,
|
|
14
|
+
retrieveMemorySelection,
|
|
15
|
+
} from "../utils/memory-retrieval.js";
|
|
16
|
+
import {
|
|
17
|
+
buildGovernedRuleStack,
|
|
18
|
+
buildGovernedTrace,
|
|
19
|
+
} from "../utils/context-assembly.js";
|
|
20
|
+
import { writeGovernedRuntimeArtifacts } from "../utils/runtime-writer.js";
|
|
21
|
+
|
|
22
|
+
export interface ComposeChapterInput {
|
|
23
|
+
readonly book: BookConfig;
|
|
24
|
+
readonly bookDir: string;
|
|
25
|
+
readonly chapterNumber: number;
|
|
26
|
+
readonly plan: PlanChapterOutput;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ComposeChapterOutput {
|
|
30
|
+
readonly contextPackage: ContextPackage;
|
|
31
|
+
readonly ruleStack: RuleStack;
|
|
32
|
+
readonly trace: ChapterTrace;
|
|
33
|
+
readonly contextPath: string;
|
|
34
|
+
readonly ruleStackPath: string;
|
|
35
|
+
readonly tracePath: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function composeGovernedChapter(input: ComposeChapterInput): Promise<ComposeChapterOutput> {
|
|
39
|
+
const storyDir = join(input.bookDir, "story");
|
|
40
|
+
const runtimeDir = join(storyDir, "runtime");
|
|
41
|
+
await mkdir(runtimeDir, { recursive: true });
|
|
42
|
+
|
|
43
|
+
const selectedContext = await collectSelectedContext(
|
|
44
|
+
storyDir,
|
|
45
|
+
input.plan,
|
|
46
|
+
input.book.language ?? "zh",
|
|
47
|
+
);
|
|
48
|
+
const contextPackage = ContextPackageSchema.parse({
|
|
49
|
+
chapter: input.chapterNumber,
|
|
50
|
+
selectedContext,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const ruleStack = buildGovernedRuleStack(input.plan, input.chapterNumber);
|
|
54
|
+
const trace = buildGovernedTrace({
|
|
55
|
+
chapterNumber: input.chapterNumber,
|
|
56
|
+
plan: input.plan,
|
|
57
|
+
contextPackage,
|
|
58
|
+
composerInputs: [input.plan.runtimePath],
|
|
59
|
+
});
|
|
60
|
+
const {
|
|
61
|
+
contextPath,
|
|
62
|
+
ruleStackPath,
|
|
63
|
+
tracePath,
|
|
64
|
+
} = await writeGovernedRuntimeArtifacts({
|
|
65
|
+
runtimeDir,
|
|
66
|
+
chapterNumber: input.chapterNumber,
|
|
67
|
+
contextPackage,
|
|
68
|
+
ruleStack,
|
|
69
|
+
trace,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
contextPackage,
|
|
74
|
+
ruleStack,
|
|
75
|
+
trace,
|
|
76
|
+
contextPath,
|
|
77
|
+
ruleStackPath,
|
|
78
|
+
tracePath,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export class ComposerAgent extends BaseAgent {
|
|
83
|
+
get name(): string {
|
|
84
|
+
return "composer";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async composeChapter(input: ComposeChapterInput): Promise<ComposeChapterOutput> {
|
|
88
|
+
return composeGovernedChapter(input);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function collectSelectedContext(
|
|
93
|
+
storyDir: string,
|
|
94
|
+
plan: PlanChapterOutput,
|
|
95
|
+
language: "zh" | "en",
|
|
96
|
+
): Promise<ContextPackage["selectedContext"]> {
|
|
97
|
+
const retrievalHints = deriveRetrievalHints(plan);
|
|
98
|
+
const memoBodyExcerpt = plan.memo.body.trim();
|
|
99
|
+
const chapterMemoEntry = memoBodyExcerpt.length > 0
|
|
100
|
+
? [{
|
|
101
|
+
source: "runtime/chapter_memo",
|
|
102
|
+
reason: "Carry the planner's chapter memo into governed writing.",
|
|
103
|
+
excerpt: [
|
|
104
|
+
`goal=${plan.memo.goal}`,
|
|
105
|
+
plan.memo.isGoldenOpening ? "golden-opening=true" : undefined,
|
|
106
|
+
memoBodyExcerpt,
|
|
107
|
+
].filter(Boolean).join(" | "),
|
|
108
|
+
}]
|
|
109
|
+
: [{
|
|
110
|
+
source: "runtime/chapter_memo",
|
|
111
|
+
reason: "Carry the planner's chapter memo into governed writing.",
|
|
112
|
+
excerpt: `goal=${plan.memo.goal}`,
|
|
113
|
+
}];
|
|
114
|
+
|
|
115
|
+
const entries = await Promise.all([
|
|
116
|
+
maybeContextSource(storyDir, "current_focus.md", "Current task focus for this chapter."),
|
|
117
|
+
maybeContextSource(
|
|
118
|
+
storyDir,
|
|
119
|
+
"audit_drift.md",
|
|
120
|
+
"Carry forward audit drift guidance from the previous chapter without polluting hard state facts.",
|
|
121
|
+
),
|
|
122
|
+
maybeContextSource(
|
|
123
|
+
storyDir,
|
|
124
|
+
"current_state.md",
|
|
125
|
+
"Preserve hard state facts referenced by the active chapter brief or hard constraints.",
|
|
126
|
+
retrievalHints,
|
|
127
|
+
),
|
|
128
|
+
maybeContextSource(
|
|
129
|
+
storyDir,
|
|
130
|
+
"outline/story_frame.md",
|
|
131
|
+
"Preserve canon constraints referenced by the active chapter brief or hard constraints.",
|
|
132
|
+
retrievalHints,
|
|
133
|
+
),
|
|
134
|
+
maybeContextSource(
|
|
135
|
+
storyDir,
|
|
136
|
+
"outline/volume_map.md",
|
|
137
|
+
"Anchor the default planning node for this chapter.",
|
|
138
|
+
plan.intent.outlineNode ? [plan.intent.outlineNode] : [],
|
|
139
|
+
),
|
|
140
|
+
maybeContextSource(
|
|
141
|
+
storyDir,
|
|
142
|
+
"parent_canon.md",
|
|
143
|
+
"Preserve parent canon constraints for governed continuation or fanfic writing.",
|
|
144
|
+
),
|
|
145
|
+
maybeContextSource(
|
|
146
|
+
storyDir,
|
|
147
|
+
"fanfic_canon.md",
|
|
148
|
+
"Preserve extracted fanfic canon constraints for governed writing.",
|
|
149
|
+
),
|
|
150
|
+
]);
|
|
151
|
+
const trailEntries = await buildRecentChapterTrailEntries(storyDir, plan.intent.chapter);
|
|
152
|
+
|
|
153
|
+
const memorySelection = await retrieveMemorySelection({
|
|
154
|
+
bookDir: dirname(storyDir),
|
|
155
|
+
chapterNumber: plan.intent.chapter,
|
|
156
|
+
goal: plan.intent.goal,
|
|
157
|
+
outlineNode: plan.intent.outlineNode,
|
|
158
|
+
mustKeep: retrievalHints,
|
|
159
|
+
});
|
|
160
|
+
const hookDebtEntries = await buildHookDebtEntries(
|
|
161
|
+
storyDir,
|
|
162
|
+
plan,
|
|
163
|
+
memorySelection.activeHooks,
|
|
164
|
+
language,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const summaryEntries = memorySelection.summaries.map((summary) => ({
|
|
168
|
+
source: `story/chapter_summaries.md#${summary.chapter}`,
|
|
169
|
+
reason: "Relevant episodic memory retrieved for the current chapter goal.",
|
|
170
|
+
excerpt: [summary.title, summary.events, summary.stateChanges, summary.hookActivity]
|
|
171
|
+
.filter(Boolean)
|
|
172
|
+
.join(" | "),
|
|
173
|
+
}));
|
|
174
|
+
const factEntries = memorySelection.facts.map((fact) => ({
|
|
175
|
+
source: `story/current_state.md#${toFactAnchor(fact.predicate)}`,
|
|
176
|
+
reason: "Relevant current-state fact retrieved for the current chapter goal.",
|
|
177
|
+
excerpt: `${fact.predicate} | ${fact.object}`,
|
|
178
|
+
}));
|
|
179
|
+
const hookEntries = memorySelection.hooks.map((hook) => ({
|
|
180
|
+
source: `story/pending_hooks.md#${hook.hookId}`,
|
|
181
|
+
reason: "Carry forward unresolved hooks that match the chapter focus.",
|
|
182
|
+
excerpt: [hook.type, hook.status, hook.expectedPayoff, hook.payoffTiming, hook.notes]
|
|
183
|
+
.filter(Boolean)
|
|
184
|
+
.join(" | "),
|
|
185
|
+
}));
|
|
186
|
+
const volumeSummaryEntries = memorySelection.volumeSummaries.map((summary) => ({
|
|
187
|
+
source: `story/volume_summaries.md#${summary.anchor}`,
|
|
188
|
+
reason: "Carry forward long-span arc memory compressed from earlier volumes.",
|
|
189
|
+
excerpt: `${summary.heading} | ${summary.content}`,
|
|
190
|
+
}));
|
|
191
|
+
|
|
192
|
+
return [
|
|
193
|
+
...chapterMemoEntry,
|
|
194
|
+
...entries.filter((entry): entry is NonNullable<typeof entry> => entry !== null),
|
|
195
|
+
...trailEntries,
|
|
196
|
+
...hookDebtEntries,
|
|
197
|
+
...factEntries,
|
|
198
|
+
...summaryEntries,
|
|
199
|
+
...volumeSummaryEntries,
|
|
200
|
+
...hookEntries,
|
|
201
|
+
];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function deriveRetrievalHints(plan: PlanChapterOutput): string[] {
|
|
205
|
+
return [
|
|
206
|
+
plan.intent.goal,
|
|
207
|
+
plan.intent.outlineNode,
|
|
208
|
+
...plan.memo.threadRefs,
|
|
209
|
+
].filter((value): value is string => Boolean(value));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function buildRecentChapterTrailEntries(
|
|
213
|
+
storyDir: string,
|
|
214
|
+
chapterNumber: number,
|
|
215
|
+
): Promise<ContextPackage["selectedContext"]> {
|
|
216
|
+
const content = await readFileOrDefault(join(storyDir, "chapter_summaries.md"));
|
|
217
|
+
if (!content || content === "(文件尚未创建)") {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const recentSummaries = parseChapterSummariesMarkdown(content)
|
|
222
|
+
.filter((summary) => summary.chapter < chapterNumber)
|
|
223
|
+
.sort((left, right) => right.chapter - left.chapter)
|
|
224
|
+
.slice(0, 5);
|
|
225
|
+
if (recentSummaries.length === 0) {
|
|
226
|
+
return [];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const entries: ContextPackage["selectedContext"] = [];
|
|
230
|
+
const recentTitles = recentSummaries
|
|
231
|
+
.map((summary) => [summary.chapter, summary.title].filter(Boolean).join(": "))
|
|
232
|
+
.filter(Boolean)
|
|
233
|
+
.join(" | ");
|
|
234
|
+
if (recentTitles) {
|
|
235
|
+
entries.push({
|
|
236
|
+
source: "story/chapter_summaries.md#recent_titles",
|
|
237
|
+
reason: "Keep recent title history visible to avoid repetitive chapter naming.",
|
|
238
|
+
excerpt: recentTitles,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const moodTrail = recentSummaries
|
|
243
|
+
.filter((summary) => summary.mood || summary.chapterType)
|
|
244
|
+
.map((summary) => `${summary.chapter}: ${summary.mood || "(none)"} / ${summary.chapterType || "(none)"}`)
|
|
245
|
+
.join(" | ");
|
|
246
|
+
if (moodTrail) {
|
|
247
|
+
entries.push({
|
|
248
|
+
source: "story/chapter_summaries.md#recent_mood_type_trail",
|
|
249
|
+
reason: "Keep recent mood and chapter-type cadence visible before writing the next chapter.",
|
|
250
|
+
excerpt: moodTrail,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const endingTrail = await buildRecentEndingTrail(storyDir, chapterNumber);
|
|
255
|
+
if (endingTrail) {
|
|
256
|
+
entries.push({
|
|
257
|
+
source: "story/chapters#recent_endings",
|
|
258
|
+
reason: "Show how recent chapters ended so the writer avoids structural repetition (e.g. 3 consecutive collapse endings).",
|
|
259
|
+
excerpt: endingTrail,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return entries;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function buildRecentEndingTrail(
|
|
267
|
+
storyDir: string,
|
|
268
|
+
chapterNumber: number,
|
|
269
|
+
): Promise<string | undefined> {
|
|
270
|
+
const chaptersDir = join(dirname(storyDir), "chapters");
|
|
271
|
+
try {
|
|
272
|
+
const files = await readdir(chaptersDir);
|
|
273
|
+
const chapterFiles = files
|
|
274
|
+
.filter((file) => file.endsWith(".md"))
|
|
275
|
+
.map((file) => ({ file, num: parseInt(file.slice(0, 4), 10) }))
|
|
276
|
+
.filter((entry) => Number.isFinite(entry.num) && entry.num < chapterNumber)
|
|
277
|
+
.sort((a, b) => b.num - a.num)
|
|
278
|
+
.slice(0, 3);
|
|
279
|
+
|
|
280
|
+
const endings: string[] = [];
|
|
281
|
+
for (const entry of chapterFiles.reverse()) {
|
|
282
|
+
const content = await readFile(join(chaptersDir, entry.file), "utf-8");
|
|
283
|
+
const lastLine = extractLastMeaningfulSentence(content);
|
|
284
|
+
if (lastLine) {
|
|
285
|
+
endings.push(`ch${entry.num}: ${lastLine}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return endings.length >= 2 ? endings.join(" | ") : undefined;
|
|
289
|
+
} catch {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function extractLastMeaningfulSentence(content: string): string | undefined {
|
|
295
|
+
const lines = content.split("\n").map((line) => line.trim()).filter((line) =>
|
|
296
|
+
line.length > 5 && !line.startsWith("#") && !line.startsWith("|") && !line.startsWith("==="),
|
|
297
|
+
);
|
|
298
|
+
const last = lines.at(-1);
|
|
299
|
+
if (!last) return undefined;
|
|
300
|
+
return last.length > 60 ? last.slice(0, 57) + "..." : last;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function buildHookDebtEntries(
|
|
304
|
+
storyDir: string,
|
|
305
|
+
plan: PlanChapterOutput,
|
|
306
|
+
activeHooks: ReadonlyArray<{
|
|
307
|
+
readonly hookId: string;
|
|
308
|
+
readonly startChapter: number;
|
|
309
|
+
readonly type: string;
|
|
310
|
+
readonly status: string;
|
|
311
|
+
readonly lastAdvancedChapter: number;
|
|
312
|
+
readonly expectedPayoff: string;
|
|
313
|
+
readonly payoffTiming?: string;
|
|
314
|
+
readonly notes: string;
|
|
315
|
+
}>,
|
|
316
|
+
language: "zh" | "en",
|
|
317
|
+
): Promise<ContextPackage["selectedContext"]> {
|
|
318
|
+
const targetHookIds = [...new Set(plan.memo.threadRefs)];
|
|
319
|
+
if (targetHookIds.length === 0) {
|
|
320
|
+
return [];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const summaries = parseChapterSummariesMarkdown(
|
|
324
|
+
await readFileOrDefault(join(storyDir, "chapter_summaries.md")),
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
return targetHookIds.flatMap((hookId) => {
|
|
328
|
+
const hook = activeHooks.find((entry) => entry.hookId === hookId);
|
|
329
|
+
if (!hook) {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const seedSummary = findHookSummary(summaries, hook.hookId, hook.startChapter, "seed");
|
|
334
|
+
const latestSummary = findHookSummary(summaries, hook.hookId, hook.lastAdvancedChapter, "latest");
|
|
335
|
+
const role = language === "en" ? "memo-referenced debt" : "备忘引用旧债";
|
|
336
|
+
const promise = hook.expectedPayoff || (language === "en" ? "(unspecified)" : "(未写明)");
|
|
337
|
+
const seedBeat = seedSummary
|
|
338
|
+
? renderHookDebtBeat(seedSummary)
|
|
339
|
+
: (hook.notes || promise);
|
|
340
|
+
const latestBeat = latestSummary && latestSummary !== seedSummary
|
|
341
|
+
? renderHookDebtBeat(latestSummary)
|
|
342
|
+
: undefined;
|
|
343
|
+
const age = Math.max(0, plan.intent.chapter - Math.max(1, hook.startChapter));
|
|
344
|
+
|
|
345
|
+
return [{
|
|
346
|
+
source: `runtime/hook_debt#${hook.hookId}`,
|
|
347
|
+
reason: language === "en"
|
|
348
|
+
? "Narrative debt brief with original seed text for this hook agenda target."
|
|
349
|
+
: "含原始种子文本的叙事债务简报。",
|
|
350
|
+
excerpt: language === "en"
|
|
351
|
+
? [
|
|
352
|
+
`${hook.hookId} (${hook.type}, ${role}, open ${age} chapters)`,
|
|
353
|
+
`reader promise: ${promise}`,
|
|
354
|
+
`original seed (ch${hook.startChapter}): ${seedBeat}`,
|
|
355
|
+
latestBeat ? `latest turn (ch${hook.lastAdvancedChapter}): ${latestBeat}` : undefined,
|
|
356
|
+
].filter(Boolean).join(" | ")
|
|
357
|
+
: [
|
|
358
|
+
`${hook.hookId}(${hook.type},${role},已开${age}章)`,
|
|
359
|
+
`读者承诺:${promise}`,
|
|
360
|
+
`种于第${hook.startChapter}章:${seedBeat}`,
|
|
361
|
+
latestBeat ? `推进于第${hook.lastAdvancedChapter}章:${latestBeat}` : undefined,
|
|
362
|
+
].filter(Boolean).join(" | "),
|
|
363
|
+
}];
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async function maybeContextSource(
|
|
368
|
+
storyDir: string,
|
|
369
|
+
fileName: string,
|
|
370
|
+
reason: string,
|
|
371
|
+
preferredExcerpts: ReadonlyArray<string> = [],
|
|
372
|
+
): Promise<ContextPackage["selectedContext"][number] | null> {
|
|
373
|
+
const path = join(storyDir, fileName);
|
|
374
|
+
let content = await readFileOrDefault(path);
|
|
375
|
+
let resolvedFileName = fileName;
|
|
376
|
+
|
|
377
|
+
if ((!content || content === "(文件尚未创建)")) {
|
|
378
|
+
// Phase 5 back-compat: the new outline/ files may be absent on legacy
|
|
379
|
+
// books. Fall back to the deprecated paths transparently.
|
|
380
|
+
const legacyFallback = outlineFallback(fileName);
|
|
381
|
+
if (legacyFallback) {
|
|
382
|
+
const legacyPath = join(storyDir, legacyFallback);
|
|
383
|
+
const legacyContent = await readFileOrDefault(legacyPath);
|
|
384
|
+
if (legacyContent && legacyContent !== "(文件尚未创建)") {
|
|
385
|
+
content = legacyContent;
|
|
386
|
+
resolvedFileName = legacyFallback;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (!content || content === "(文件尚未创建)") return null;
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
source: `story/${resolvedFileName}`,
|
|
395
|
+
reason,
|
|
396
|
+
excerpt: pickExcerpt(content, preferredExcerpts),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function outlineFallback(fileName: string): string | null {
|
|
401
|
+
if (fileName === "outline/story_frame.md") return "story_bible.md";
|
|
402
|
+
if (fileName === "outline/volume_map.md") return "volume_outline.md";
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function pickExcerpt(content: string, preferredExcerpts: ReadonlyArray<string>): string | undefined {
|
|
407
|
+
for (const preferred of preferredExcerpts) {
|
|
408
|
+
if (preferred && content.includes(preferred)) return preferred;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return content
|
|
412
|
+
.split("\n")
|
|
413
|
+
.map((line) => line.trim())
|
|
414
|
+
.find((line) => line.length > 0 && !line.startsWith("#"));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function toFactAnchor(predicate: string): string {
|
|
418
|
+
return predicate
|
|
419
|
+
.trim()
|
|
420
|
+
.toLowerCase()
|
|
421
|
+
.replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-")
|
|
422
|
+
.replace(/^-+|-+$/g, "")
|
|
423
|
+
|| "fact";
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function readFileOrDefault(path: string): Promise<string> {
|
|
427
|
+
try {
|
|
428
|
+
return await readFile(path, "utf-8");
|
|
429
|
+
} catch {
|
|
430
|
+
return "(文件尚未创建)";
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function findHookSummary(
|
|
435
|
+
summaries: ReadonlyArray<ReturnType<typeof parseChapterSummariesMarkdown>[number]>,
|
|
436
|
+
hookId: string,
|
|
437
|
+
chapter: number,
|
|
438
|
+
mode: "seed" | "latest",
|
|
439
|
+
) {
|
|
440
|
+
const directChapterHit = summaries.find((summary) => summary.chapter === chapter);
|
|
441
|
+
const hookMentions = summaries.filter((summary) => summaryMentionsHook(summary, hookId));
|
|
442
|
+
if (mode === "seed") {
|
|
443
|
+
return hookMentions.find((summary) => summary.chapter === chapter)
|
|
444
|
+
?? hookMentions.at(0)
|
|
445
|
+
?? directChapterHit;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return [...hookMentions].reverse().find((summary) => summary.chapter === chapter)
|
|
449
|
+
?? hookMentions.at(-1)
|
|
450
|
+
?? directChapterHit;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function summaryMentionsHook(
|
|
454
|
+
summary: ReturnType<typeof parseChapterSummariesMarkdown>[number],
|
|
455
|
+
hookId: string,
|
|
456
|
+
): boolean {
|
|
457
|
+
return [
|
|
458
|
+
summary.title,
|
|
459
|
+
summary.events,
|
|
460
|
+
summary.stateChanges,
|
|
461
|
+
summary.hookActivity,
|
|
462
|
+
].some((text) => text.includes(hookId));
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function renderHookDebtBeat(
|
|
466
|
+
summary: ReturnType<typeof parseChapterSummariesMarkdown>[number],
|
|
467
|
+
): string {
|
|
468
|
+
return `ch${summary.chapter} ${summary.title} - ${summary.events || summary.hookActivity || summary.stateChanges || "(none)"}`;
|
|
469
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { BaseAgent } from "./base.js";
|
|
2
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { readVolumeMap } from "../utils/outline-paths.js";
|
|
5
|
+
import {
|
|
6
|
+
parsePendingHooksMarkdown,
|
|
7
|
+
renderHookSnapshot,
|
|
8
|
+
} from "../utils/story-markdown.js";
|
|
9
|
+
import type { StoredHook } from "../state/memory-db.js";
|
|
10
|
+
|
|
11
|
+
export interface ConsolidationResult {
|
|
12
|
+
readonly volumeSummaries: string;
|
|
13
|
+
readonly archivedVolumes: number;
|
|
14
|
+
readonly retainedChapters: number;
|
|
15
|
+
/**
|
|
16
|
+
* Phase 7 hotfix 2: number of ledger hooks whose `promoted` flag flipped
|
|
17
|
+
* from false to true during this consolidation run (advanced_count rule).
|
|
18
|
+
* 0 when pending_hooks.md is absent or no hook crossed the threshold.
|
|
19
|
+
*/
|
|
20
|
+
readonly promotedHookCount: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Consolidates chapter summaries into volume-level narrative summaries.
|
|
25
|
+
* Reduces token usage for long books while preserving critical context.
|
|
26
|
+
*/
|
|
27
|
+
export class ConsolidatorAgent extends BaseAgent {
|
|
28
|
+
get name(): string {
|
|
29
|
+
return "consolidator";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Consolidate chapter summaries by volume.
|
|
34
|
+
* - Reads outline/volume_map.md (fallback: legacy volume_outline.md) to
|
|
35
|
+
* determine volume boundaries
|
|
36
|
+
* - For each completed volume, LLM compresses chapter summaries into a narrative paragraph
|
|
37
|
+
* - Archives detailed summaries, keeps only recent volume's per-chapter rows
|
|
38
|
+
*/
|
|
39
|
+
async consolidate(bookDir: string): Promise<ConsolidationResult> {
|
|
40
|
+
const storyDir = join(bookDir, "story");
|
|
41
|
+
const summariesPath = join(storyDir, "chapter_summaries.md");
|
|
42
|
+
const volumeSummariesPath = join(storyDir, "volume_summaries.md");
|
|
43
|
+
|
|
44
|
+
const [summariesRaw, outlineRaw] = await Promise.all([
|
|
45
|
+
readFile(summariesPath, "utf-8").catch(() => ""),
|
|
46
|
+
readVolumeMap(bookDir, ""),
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
// Phase 7 hotfix 2: pre-archive re-promotion pass. Runs independently of
|
|
50
|
+
// summary consolidation so a new book (no completed volumes yet) still
|
|
51
|
+
// flips the `promoted` flag whenever a seed's advanced_count crosses the
|
|
52
|
+
// threshold.
|
|
53
|
+
const promotedHookCount = await this.rerunAdvancedCountPromotion(storyDir);
|
|
54
|
+
|
|
55
|
+
if (!summariesRaw || !outlineRaw) {
|
|
56
|
+
return { volumeSummaries: "", archivedVolumes: 0, retainedChapters: 0, promotedHookCount };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Parse volume boundaries from outline
|
|
60
|
+
const volumeBoundaries = this.parseVolumeBoundaries(outlineRaw);
|
|
61
|
+
if (volumeBoundaries.length === 0) {
|
|
62
|
+
return { volumeSummaries: "", archivedVolumes: 0, retainedChapters: 0, promotedHookCount };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Parse chapter summaries into rows
|
|
66
|
+
const { header, rows } = this.parseSummaryTable(summariesRaw);
|
|
67
|
+
if (rows.length === 0) {
|
|
68
|
+
return { volumeSummaries: "", archivedVolumes: 0, retainedChapters: 0, promotedHookCount };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const maxChapter = Math.max(...rows.map((r) => r.chapter));
|
|
72
|
+
|
|
73
|
+
// Determine which volumes are "completed" (all chapters written)
|
|
74
|
+
const completedVolumes: Array<{ name: string; startCh: number; endCh: number; rows: typeof rows }> = [];
|
|
75
|
+
const currentVolumeRows: typeof rows = [];
|
|
76
|
+
|
|
77
|
+
for (const vol of volumeBoundaries) {
|
|
78
|
+
const volRows = rows.filter((r) => r.chapter >= vol.startCh && r.chapter <= vol.endCh);
|
|
79
|
+
if (vol.endCh <= maxChapter && volRows.length > 0) {
|
|
80
|
+
completedVolumes.push({ ...vol, rows: volRows });
|
|
81
|
+
} else {
|
|
82
|
+
// Current/incomplete volume — keep detailed rows
|
|
83
|
+
currentVolumeRows.push(...volRows);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Also keep any rows not covered by volume boundaries
|
|
88
|
+
const coveredChapters = new Set(volumeBoundaries.flatMap((v) => {
|
|
89
|
+
const chs: number[] = [];
|
|
90
|
+
for (let i = v.startCh; i <= v.endCh; i++) chs.push(i);
|
|
91
|
+
return chs;
|
|
92
|
+
}));
|
|
93
|
+
for (const r of rows) {
|
|
94
|
+
if (!coveredChapters.has(r.chapter)) currentVolumeRows.push(r);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (completedVolumes.length === 0) {
|
|
98
|
+
return {
|
|
99
|
+
volumeSummaries: "",
|
|
100
|
+
archivedVolumes: 0,
|
|
101
|
+
retainedChapters: currentVolumeRows.length,
|
|
102
|
+
promotedHookCount,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// LLM consolidation for each completed volume
|
|
107
|
+
const existingVolSummaries = await readFile(volumeSummariesPath, "utf-8").catch(() => "");
|
|
108
|
+
const newSummaries: string[] = existingVolSummaries ? [existingVolSummaries.trim()] : ["# Volume Summaries\n"];
|
|
109
|
+
|
|
110
|
+
for (const vol of completedVolumes) {
|
|
111
|
+
const volSummaryRows = vol.rows.map((r) => r.raw).join("\n");
|
|
112
|
+
|
|
113
|
+
const response = await this.chat([
|
|
114
|
+
{
|
|
115
|
+
role: "system",
|
|
116
|
+
content: `You are a narrative summarizer. Compress chapter-by-chapter summaries into a single coherent paragraph (max 500 words) that captures the key events, character developments, and plot progression of this volume. Preserve specific names, locations, and plot points. Write in the same language as the input.`,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
role: "user",
|
|
120
|
+
content: `Volume: ${vol.name} (Chapters ${vol.startCh}-${vol.endCh})\n\nChapter summaries:\n${header}\n${volSummaryRows}`,
|
|
121
|
+
},
|
|
122
|
+
], { temperature: 0.3 });
|
|
123
|
+
|
|
124
|
+
newSummaries.push(`\n## ${vol.name} (Ch.${vol.startCh}-${vol.endCh})\n\n${response.content.trim()}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Write volume summaries
|
|
128
|
+
await writeFile(volumeSummariesPath, newSummaries.join("\n"), "utf-8");
|
|
129
|
+
|
|
130
|
+
// Archive detailed summaries
|
|
131
|
+
const archiveDir = join(storyDir, "summaries_archive");
|
|
132
|
+
await mkdir(archiveDir, { recursive: true });
|
|
133
|
+
for (const vol of completedVolumes) {
|
|
134
|
+
const archivePath = join(archiveDir, `vol_${vol.startCh}-${vol.endCh}.md`);
|
|
135
|
+
await writeFile(archivePath, `# ${vol.name}\n\n${header}\n${vol.rows.map((r) => r.raw).join("\n")}`, "utf-8");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Rewrite chapter_summaries.md with only current volume rows
|
|
139
|
+
const retainedContent = currentVolumeRows.length > 0
|
|
140
|
+
? `${header}\n${currentVolumeRows.map((r) => r.raw).join("\n")}\n`
|
|
141
|
+
: `${header}\n`;
|
|
142
|
+
await writeFile(summariesPath, retainedContent, "utf-8");
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
volumeSummaries: newSummaries.join("\n"),
|
|
146
|
+
archivedVolumes: completedVolumes.length,
|
|
147
|
+
retainedChapters: currentVolumeRows.length,
|
|
148
|
+
promotedHookCount,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Phase 7 hotfix 2 — re-run promotion for seeds whose advancedCount has
|
|
154
|
+
* crossed the 2-chapter threshold since architect seed time. Delegates to
|
|
155
|
+
* the shared `rerunPromotionPass` in utils/hook-promotion.ts.
|
|
156
|
+
*
|
|
157
|
+
* Returns the number of hooks that flipped from promoted=false (or
|
|
158
|
+
* undefined) to promoted=true this run.
|
|
159
|
+
*/
|
|
160
|
+
private async rerunAdvancedCountPromotion(storyDir: string): Promise<number> {
|
|
161
|
+
const ledgerPath = join(storyDir, "pending_hooks.md");
|
|
162
|
+
const raw = await readFile(ledgerPath, "utf-8").catch(() => "");
|
|
163
|
+
if (!raw.trim()) return 0;
|
|
164
|
+
|
|
165
|
+
const hooks = parsePendingHooksMarkdown(raw);
|
|
166
|
+
if (hooks.length === 0) return 0;
|
|
167
|
+
|
|
168
|
+
const language: "zh" | "en" = /[\u4e00-\u9fff]/.test(raw) ? "zh" : "en";
|
|
169
|
+
const summariesRaw = await readFile(join(storyDir, "chapter_summaries.md"), "utf-8").catch(() => "");
|
|
170
|
+
|
|
171
|
+
const { rerunPromotionPass } = await import("../utils/hook-promotion.js");
|
|
172
|
+
const result = rerunPromotionPass(hooks, summariesRaw);
|
|
173
|
+
if (!result.updated) return 0;
|
|
174
|
+
|
|
175
|
+
await writeFile(ledgerPath, renderHookSnapshot([...result.hooks], language), "utf-8");
|
|
176
|
+
return result.flippedCount;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private parseVolumeBoundaries(outline: string): Array<{ name: string; startCh: number; endCh: number }> {
|
|
180
|
+
const volumes: Array<{ name: string; startCh: number; endCh: number }> = [];
|
|
181
|
+
const lines = outline.split("\n");
|
|
182
|
+
const volumeHeader = /^(第[一二三四五六七八九十百千万零〇\d]+卷|Volume\s+\d+)/i;
|
|
183
|
+
const rangePattern = /[((]\s*(?:第|[Cc]hapters?\s+)?(\d+)\s*[-–~~—]\s*(\d+)\s*(?:章)?\s*[))]|(?:第|[Cc]hapters?\s+)(\d+)\s*[-–~~—]\s*(\d+)\s*(?:章)?/i;
|
|
184
|
+
|
|
185
|
+
for (const rawLine of lines) {
|
|
186
|
+
const line = rawLine.replace(/^#+\s*/, "").trim();
|
|
187
|
+
if (!volumeHeader.test(line)) continue;
|
|
188
|
+
|
|
189
|
+
const rangeMatch = line.match(rangePattern);
|
|
190
|
+
if (!rangeMatch) continue;
|
|
191
|
+
|
|
192
|
+
const startCh = parseInt(rangeMatch[1] ?? rangeMatch[3] ?? "0", 10);
|
|
193
|
+
const endCh = parseInt(rangeMatch[2] ?? rangeMatch[4] ?? "0", 10);
|
|
194
|
+
if (startCh <= 0 || endCh <= 0) continue;
|
|
195
|
+
|
|
196
|
+
const rangeIndex = rangeMatch.index ?? line.length;
|
|
197
|
+
const name = line.slice(0, rangeIndex).replace(/[((]\s*$/, "").trim();
|
|
198
|
+
if (name.length > 0) {
|
|
199
|
+
volumes.push({ name, startCh, endCh });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return volumes;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private parseSummaryTable(raw: string): { header: string; rows: Array<{ chapter: number; raw: string }> } {
|
|
206
|
+
const lines = raw.split("\n");
|
|
207
|
+
const headerLines = lines.filter((l) => l.startsWith("|") && (l.includes("章节") || l.includes("Chapter") || l.includes("---")));
|
|
208
|
+
const dataLines = lines.filter((l) => l.startsWith("|") && !l.includes("章节") && !l.includes("Chapter") && !l.includes("---"));
|
|
209
|
+
|
|
210
|
+
const header = headerLines.join("\n");
|
|
211
|
+
const rows = dataLines.map((line) => {
|
|
212
|
+
const match = line.match(/\|\s*(\d+)\s*\|/);
|
|
213
|
+
return { chapter: match ? parseInt(match[1]!, 10) : 0, raw: line };
|
|
214
|
+
}).filter((r) => r.chapter > 0);
|
|
215
|
+
|
|
216
|
+
return { header, rows };
|
|
217
|
+
}
|
|
218
|
+
}
|