@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,835 @@
|
|
|
1
|
+
import { Type, type Static } from "@mariozechner/pi-ai";
|
|
2
|
+
import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "@mariozechner/pi-agent-core";
|
|
3
|
+
import type { PipelineRunner } from "../pipeline/runner.js";
|
|
4
|
+
import { type ReviseMode } from "../agents/reviser.js";
|
|
5
|
+
import { readFile, writeFile, readdir, stat } from "node:fs/promises";
|
|
6
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
7
|
+
import { StateManager } from "../state/manager.js";
|
|
8
|
+
import { assertSafeTruthFileName, createInteractionToolsFromDeps } from "../interaction/project-tools.js";
|
|
9
|
+
import { writeExportArtifact } from "../interaction/export-artifact.js";
|
|
10
|
+
import { assertSafeBookId, deriveBookIdFromTitle } from "../utils/book-id.js";
|
|
11
|
+
import { safeChildPath } from "../utils/path-safety.js";
|
|
12
|
+
import { normalizePlatformId, normalizePlatformOrOther } from "../models/book.js";
|
|
13
|
+
import { generateShortFictionCover, runShortFictionProduction } from "../pipeline/short-fiction-runner.js";
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Helpers
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
function textResult(text: string): AgentToolResult<undefined>;
|
|
20
|
+
function textResult<T>(text: string, details: T): AgentToolResult<T>;
|
|
21
|
+
function textResult<T = undefined>(text: string, details?: T): AgentToolResult<T> {
|
|
22
|
+
return { content: [{ type: "text", text }], details: details as T };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a user-supplied relative path against the books root and guard
|
|
27
|
+
* against path-traversal (../ etc.).
|
|
28
|
+
*/
|
|
29
|
+
function safeBooksPath(booksRoot: string, relativePath: string): string {
|
|
30
|
+
return safeChildPath(booksRoot, relativePath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveToolBookId(
|
|
34
|
+
toolName: string,
|
|
35
|
+
paramsBookId: string | undefined,
|
|
36
|
+
activeBookId: string | null,
|
|
37
|
+
): string {
|
|
38
|
+
const resolvedBookId = paramsBookId ?? activeBookId ?? undefined;
|
|
39
|
+
if (!resolvedBookId) {
|
|
40
|
+
throw new Error(`${toolName} requires bookId when there is no active book.`);
|
|
41
|
+
}
|
|
42
|
+
const safeBookId = assertSafeBookId(resolvedBookId, `${toolName}.bookId`);
|
|
43
|
+
if (paramsBookId && activeBookId && safeBookId !== activeBookId) {
|
|
44
|
+
throw new Error(`${toolName}.bookId must match the active book.`);
|
|
45
|
+
}
|
|
46
|
+
return safeBookId;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function createDeterministicInteractionTools(pipeline: PipelineRunner, projectRoot: string) {
|
|
50
|
+
const state = new StateManager(projectRoot);
|
|
51
|
+
return createInteractionToolsFromDeps(pipeline, state);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// 1. SubAgentTool (sub_agent)
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
const SubAgentParams = Type.Object({
|
|
59
|
+
agent: Type.Union([
|
|
60
|
+
Type.Literal("architect"),
|
|
61
|
+
Type.Literal("writer"),
|
|
62
|
+
Type.Literal("auditor"),
|
|
63
|
+
Type.Literal("reviser"),
|
|
64
|
+
Type.Literal("exporter"),
|
|
65
|
+
]),
|
|
66
|
+
instruction: Type.String({ description: "Natural language instruction for the sub-agent" }),
|
|
67
|
+
bookId: Type.Optional(Type.String({
|
|
68
|
+
description: "Optional book ID. In active-book sessions, omit it to use the current active book; if provided, it must match the current active book. For architect creation, this optionally sets the new book ID.",
|
|
69
|
+
})),
|
|
70
|
+
chapterNumber: Type.Optional(Type.Number({ description: "auditor/reviser: target chapter number. Omit to use the latest chapter." })),
|
|
71
|
+
// -- architect params --
|
|
72
|
+
title: Type.Optional(Type.String({ description: "architect only: explicit book title. Required when creating a book." })),
|
|
73
|
+
genre: Type.Optional(Type.String({ description: "architect only: genre (xuanhuan, urban, mystery, romance, scifi, fantasy, wuxia, general, etc.)" })),
|
|
74
|
+
platform: Type.Optional(Type.Union([
|
|
75
|
+
Type.Literal("tomato"),
|
|
76
|
+
Type.Literal("qidian"),
|
|
77
|
+
Type.Literal("feilu"),
|
|
78
|
+
Type.Literal("other"),
|
|
79
|
+
], { description: "architect only: target platform. Default: other" })),
|
|
80
|
+
language: Type.Optional(Type.Union([
|
|
81
|
+
Type.Literal("zh"),
|
|
82
|
+
Type.Literal("en"),
|
|
83
|
+
], { description: "architect only: writing language. Default: zh" })),
|
|
84
|
+
targetChapters: Type.Optional(Type.Number({ description: "architect only: total chapter count. Default: 200" })),
|
|
85
|
+
chapterWordCount: Type.Optional(Type.Number({ description: "architect/writer: words per chapter. Default: 3000" })),
|
|
86
|
+
revise: Type.Optional(Type.Boolean({
|
|
87
|
+
description: "architect only: true 表示在当前 active book 上重新生成架构稿,而不是新建书籍。no-book creation sessions cannot revise an existing book.",
|
|
88
|
+
})),
|
|
89
|
+
feedback: Type.Optional(Type.String({
|
|
90
|
+
description: "architect only: revise 模式下的调整要求。举例:把架构稿从条目式升级成段落式架构稿、某个角色设定需要重新设计、主线冲突表达太弱需要加强等。如果是架构稿评审未通过要求重写的场景,把评审意见的 overallFeedback 原样传入即可",
|
|
91
|
+
})),
|
|
92
|
+
// -- reviser params --
|
|
93
|
+
mode: Type.Optional(Type.Union([
|
|
94
|
+
Type.Literal("spot-fix"),
|
|
95
|
+
Type.Literal("polish"),
|
|
96
|
+
Type.Literal("rewrite"),
|
|
97
|
+
Type.Literal("rework"),
|
|
98
|
+
Type.Literal("anti-detect"),
|
|
99
|
+
], { description: "reviser only: revision mode. Default: spot-fix" })),
|
|
100
|
+
// -- exporter params --
|
|
101
|
+
format: Type.Optional(Type.Union([
|
|
102
|
+
Type.Literal("txt"),
|
|
103
|
+
Type.Literal("md"),
|
|
104
|
+
Type.Literal("epub"),
|
|
105
|
+
], { description: "exporter only: export format. Default: txt" })),
|
|
106
|
+
approvedOnly: Type.Optional(Type.Boolean({ description: "exporter only: export only approved chapters. Default: false" })),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
type SubAgentParamsType = Static<typeof SubAgentParams>;
|
|
110
|
+
|
|
111
|
+
function prepareSubAgentArguments(args: unknown): SubAgentParamsType {
|
|
112
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) {
|
|
113
|
+
return args as SubAgentParamsType;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const prepared = { ...(args as Record<string, unknown>) };
|
|
117
|
+
if ("platform" in prepared) {
|
|
118
|
+
const platform = normalizePlatformId(prepared.platform);
|
|
119
|
+
if (platform) {
|
|
120
|
+
prepared.platform = platform;
|
|
121
|
+
} else {
|
|
122
|
+
delete prepared.platform;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return prepared as SubAgentParamsType;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function createSubAgentTool(
|
|
129
|
+
pipeline: PipelineRunner,
|
|
130
|
+
activeBookId: string | null,
|
|
131
|
+
projectRoot?: string,
|
|
132
|
+
): AgentTool<typeof SubAgentParams> {
|
|
133
|
+
return {
|
|
134
|
+
name: "sub_agent",
|
|
135
|
+
description:
|
|
136
|
+
"Delegate a heavy operation to a specialised sub-agent. " +
|
|
137
|
+
"Use agent='architect' to initialise a new book, 'writer' to write the next chapter, " +
|
|
138
|
+
"'auditor' to audit quality, 'reviser' to revise a chapter, 'exporter' to export.",
|
|
139
|
+
label: "Sub-Agent",
|
|
140
|
+
parameters: SubAgentParams,
|
|
141
|
+
prepareArguments: prepareSubAgentArguments,
|
|
142
|
+
async execute(
|
|
143
|
+
_toolCallId: string,
|
|
144
|
+
params: SubAgentParamsType,
|
|
145
|
+
_signal?: AbortSignal,
|
|
146
|
+
onUpdate?: AgentToolUpdateCallback,
|
|
147
|
+
): Promise<AgentToolResult<unknown>> {
|
|
148
|
+
const { agent, instruction, bookId, title, chapterNumber, genre, platform, language, targetChapters, chapterWordCount, revise, feedback, mode, format, approvedOnly } = params;
|
|
149
|
+
|
|
150
|
+
const progress = (msg: string) => {
|
|
151
|
+
onUpdate?.(textResult(msg));
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
if (!activeBookId && agent !== "architect") {
|
|
156
|
+
return textResult("No active book. Only the architect agent can create a book from this session.");
|
|
157
|
+
}
|
|
158
|
+
if (activeBookId && agent === "architect" && !revise) {
|
|
159
|
+
return textResult("当前已有书籍,不需要建书。如果你想创建新书,请先回到首页。");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
switch (agent) {
|
|
163
|
+
case "architect": {
|
|
164
|
+
if (revise) {
|
|
165
|
+
if (!activeBookId) {
|
|
166
|
+
return textResult("Open the book first before revising its foundation.");
|
|
167
|
+
}
|
|
168
|
+
const targetBookId = resolveToolBookId("architect", bookId, activeBookId);
|
|
169
|
+
progress(`Revising foundation for "${targetBookId}"...`);
|
|
170
|
+
await pipeline.reviseFoundation(targetBookId, feedback ?? instruction);
|
|
171
|
+
progress(`Foundation revised for "${targetBookId}".`);
|
|
172
|
+
return textResult(
|
|
173
|
+
`Book "${targetBookId}" 架构稿已按要求重写。原书的条目式架构稿已备份到 story/.backup-phase4-<时间戳>/。`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
const resolvedTitle = title?.trim();
|
|
177
|
+
if (!resolvedTitle) {
|
|
178
|
+
return textResult('Error: title is required for the architect agent.');
|
|
179
|
+
}
|
|
180
|
+
const id = bookId
|
|
181
|
+
? assertSafeBookId(bookId, "architect.bookId")
|
|
182
|
+
: deriveBookIdFromTitle(resolvedTitle) || `book-${Date.now().toString(36)}`;
|
|
183
|
+
const now = new Date().toISOString();
|
|
184
|
+
progress(`Starting architect for book "${id}"...`);
|
|
185
|
+
await pipeline.initBook(
|
|
186
|
+
{
|
|
187
|
+
id,
|
|
188
|
+
title: resolvedTitle,
|
|
189
|
+
genre: genre ?? "general",
|
|
190
|
+
platform: normalizePlatformOrOther(platform),
|
|
191
|
+
language: (language ?? "zh") as any,
|
|
192
|
+
status: "outlining" as any,
|
|
193
|
+
targetChapters: targetChapters ?? 200,
|
|
194
|
+
chapterWordCount: chapterWordCount ?? 3000,
|
|
195
|
+
createdAt: now,
|
|
196
|
+
updatedAt: now,
|
|
197
|
+
},
|
|
198
|
+
{ externalContext: instruction },
|
|
199
|
+
);
|
|
200
|
+
progress(`Architect finished — book "${id}" foundation created.`);
|
|
201
|
+
return textResult(
|
|
202
|
+
`Book "${resolvedTitle}" (${id}) initialised successfully. Foundation files are ready.`,
|
|
203
|
+
{ kind: "book_created", bookId: id, title: resolvedTitle },
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case "writer": {
|
|
208
|
+
const targetBookId = resolveToolBookId("writer", bookId, activeBookId);
|
|
209
|
+
progress(`Writing next chapter for "${targetBookId}"...`);
|
|
210
|
+
const result = await pipeline.writeNextChapter(targetBookId, chapterWordCount);
|
|
211
|
+
progress(`Writer finished chapter for "${targetBookId}".`);
|
|
212
|
+
return textResult(
|
|
213
|
+
`Chapter written for "${targetBookId}". ` +
|
|
214
|
+
`Word count: ${(result as any).wordCount ?? "unknown"}.`,
|
|
215
|
+
{
|
|
216
|
+
kind: "chapter_written",
|
|
217
|
+
bookId: targetBookId,
|
|
218
|
+
chapterNumber: (result as any).chapterNumber,
|
|
219
|
+
title: (result as any).title,
|
|
220
|
+
wordCount: (result as any).wordCount,
|
|
221
|
+
status: (result as any).status,
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
case "auditor": {
|
|
227
|
+
const targetBookId = resolveToolBookId("auditor", bookId, activeBookId);
|
|
228
|
+
progress(`Auditing chapter ${chapterNumber ?? "latest"} for "${targetBookId}"...`);
|
|
229
|
+
const audit = await pipeline.auditDraft(targetBookId, chapterNumber);
|
|
230
|
+
progress(`Audit complete for "${targetBookId}".`);
|
|
231
|
+
const issueLines = (audit.issues ?? [])
|
|
232
|
+
.map((i: any) => `[${i.severity}] ${i.description}`)
|
|
233
|
+
.join("\n");
|
|
234
|
+
return textResult(
|
|
235
|
+
`Audit chapter ${audit.chapterNumber}: ${audit.passed ? "PASSED" : "FAILED"}, ${(audit.issues ?? []).length} issue(s).` +
|
|
236
|
+
(issueLines ? `\n${issueLines}` : ""),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
case "reviser": {
|
|
241
|
+
const targetBookId = resolveToolBookId("reviser", bookId, activeBookId);
|
|
242
|
+
const resolvedMode: ReviseMode = (mode as ReviseMode) ?? "spot-fix";
|
|
243
|
+
progress(`Revising "${targetBookId}" chapter ${chapterNumber ?? "latest"} in ${resolvedMode} mode...`);
|
|
244
|
+
await pipeline.reviseDraft(targetBookId, chapterNumber, resolvedMode);
|
|
245
|
+
progress(`Revision complete for "${targetBookId}".`);
|
|
246
|
+
return textResult(`Revision (${resolvedMode}) complete for "${targetBookId}" chapter ${chapterNumber ?? "latest"}.`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
case "exporter": {
|
|
250
|
+
const targetBookId = resolveToolBookId("exporter", bookId, activeBookId);
|
|
251
|
+
if (!projectRoot) return textResult("Error: exporter requires projectRoot.");
|
|
252
|
+
const inferredFormat = format ?? (/epub/i.test(instruction)
|
|
253
|
+
? "epub"
|
|
254
|
+
: /markdown|\bmd\b/i.test(instruction)
|
|
255
|
+
? "md"
|
|
256
|
+
: "txt");
|
|
257
|
+
const exportApprovedOnly = approvedOnly ?? /approved|已通过|通过章节/.test(instruction);
|
|
258
|
+
const state = new StateManager(projectRoot);
|
|
259
|
+
const result = await writeExportArtifact(state, targetBookId, {
|
|
260
|
+
format: inferredFormat,
|
|
261
|
+
approvedOnly: exportApprovedOnly,
|
|
262
|
+
});
|
|
263
|
+
return textResult(
|
|
264
|
+
`Exported "${targetBookId}": ${result.chaptersExported} chapters, ${result.totalWords} words → ${result.outputPath}`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
default:
|
|
269
|
+
return textResult(`Unknown agent: ${agent}`);
|
|
270
|
+
}
|
|
271
|
+
} catch (err: any) {
|
|
272
|
+
console.error(`[sub_agent] "${agent}" failed:`, err);
|
|
273
|
+
throw err;
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ---------------------------------------------------------------------------
|
|
280
|
+
// 2. Standalone Short Fiction Tool
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
|
|
283
|
+
const ShortFictionRunParams = Type.Object({
|
|
284
|
+
direction: Type.String({
|
|
285
|
+
description: "Required short fiction direction, e.g. 女频短篇 婚姻背叛 证据反杀. Include genre, protagonist pressure, conflict, and desired payoff when known.",
|
|
286
|
+
}),
|
|
287
|
+
reference: Type.Optional(Type.String({
|
|
288
|
+
description: "Optional user-provided reference notes or constraints. Do not paste copyrighted source text unless the user explicitly provided it.",
|
|
289
|
+
})),
|
|
290
|
+
storyId: Type.Optional(Type.String({
|
|
291
|
+
description: "Optional output id under shorts/. Leave empty to derive from the generated title.",
|
|
292
|
+
})),
|
|
293
|
+
chapters: Type.Optional(Type.Number({
|
|
294
|
+
description: "Target complete short chapter count, 12-18. Default 12.",
|
|
295
|
+
})),
|
|
296
|
+
chars: Type.Optional(Type.Number({
|
|
297
|
+
description: "Target Chinese characters per chapter, 900-1200. Default 1000.",
|
|
298
|
+
})),
|
|
299
|
+
cover: Type.Optional(Type.Boolean({
|
|
300
|
+
description: "Whether to attempt cover image generation after synopsis and cover prompt. Default true; use false if the user only wants text assets.",
|
|
301
|
+
})),
|
|
302
|
+
coverBaseUrl: Type.Optional(Type.String({
|
|
303
|
+
description: "Optional OpenAI-compatible Responses API base URL for cover generation.",
|
|
304
|
+
})),
|
|
305
|
+
coverEndpoint: Type.Optional(Type.String({
|
|
306
|
+
description: "Optional exact Responses endpoint for cover generation. Overrides coverBaseUrl.",
|
|
307
|
+
})),
|
|
308
|
+
coverModel: Type.Optional(Type.String({
|
|
309
|
+
description: "Optional image-capable Responses model. Default gpt-image-2.",
|
|
310
|
+
})),
|
|
311
|
+
coverSize: Type.Optional(Type.String({
|
|
312
|
+
description: "Optional image size, default 1024x1360.",
|
|
313
|
+
})),
|
|
314
|
+
coverApiKeyEnv: Type.Optional(Type.String({
|
|
315
|
+
description: "Optional env var containing the cover API key. Default INKOS_COVER_API_KEY.",
|
|
316
|
+
})),
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
type ShortFictionRunParamsType = Static<typeof ShortFictionRunParams>;
|
|
320
|
+
|
|
321
|
+
export function createShortFictionRunTool(
|
|
322
|
+
pipeline: PipelineRunner,
|
|
323
|
+
projectRoot: string,
|
|
324
|
+
): AgentTool<typeof ShortFictionRunParams> {
|
|
325
|
+
return {
|
|
326
|
+
name: "short_fiction_run",
|
|
327
|
+
description:
|
|
328
|
+
"Create a standalone short fiction project from a direction. " +
|
|
329
|
+
"Runs outline -> outline review/revision -> full draft -> draft review/revision -> synopsis/selling points/cover prompt -> optional cover image. " +
|
|
330
|
+
"Uses the user's direction and optional reference notes as input.",
|
|
331
|
+
label: "Short Fiction",
|
|
332
|
+
parameters: ShortFictionRunParams,
|
|
333
|
+
async execute(
|
|
334
|
+
_toolCallId: string,
|
|
335
|
+
params: ShortFictionRunParamsType,
|
|
336
|
+
_signal?: AbortSignal,
|
|
337
|
+
onUpdate?: AgentToolUpdateCallback,
|
|
338
|
+
): Promise<AgentToolResult<unknown>> {
|
|
339
|
+
const progress = (message: string) => onUpdate?.(textResult(message));
|
|
340
|
+
const result = await runShortFictionProduction({
|
|
341
|
+
projectRoot,
|
|
342
|
+
direction: params.direction,
|
|
343
|
+
runtimes: {
|
|
344
|
+
planner: pipeline.createAgentContext("short-outline"),
|
|
345
|
+
outlineReview: pipeline.createAgentContext("short-outline-review"),
|
|
346
|
+
writer: pipeline.createAgentContext("short-writer"),
|
|
347
|
+
draftReview: pipeline.createAgentContext("short-draft-review"),
|
|
348
|
+
revise: pipeline.createAgentContext("short-revise"),
|
|
349
|
+
package: pipeline.createAgentContext("short-package"),
|
|
350
|
+
},
|
|
351
|
+
...(params.reference ? { reference: { text: params.reference } } : {}),
|
|
352
|
+
storyId: params.storyId,
|
|
353
|
+
chapterCount: params.chapters,
|
|
354
|
+
charsPerChapter: params.chars,
|
|
355
|
+
cover: params.cover,
|
|
356
|
+
coverBaseUrl: params.coverBaseUrl,
|
|
357
|
+
coverEndpoint: params.coverEndpoint,
|
|
358
|
+
coverModel: params.coverModel,
|
|
359
|
+
coverSize: params.coverSize,
|
|
360
|
+
coverApiKeyEnv: params.coverApiKeyEnv,
|
|
361
|
+
onProgress: progress,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
return textResult(
|
|
365
|
+
[
|
|
366
|
+
`Short fiction "${result.storyId}" completed.`,
|
|
367
|
+
`Final: ${result.finalMarkdownPath}`,
|
|
368
|
+
`Sales package: ${result.salesPackagePath}`,
|
|
369
|
+
`Cover prompt: ${result.coverPromptPath}`,
|
|
370
|
+
result.coverImagePath
|
|
371
|
+
? `Cover image: ${result.coverImagePath}`
|
|
372
|
+
: [
|
|
373
|
+
"Cover image: not generated.",
|
|
374
|
+
`Cover image reason: ${summarizeCoverGenerationError(result.coverError)}`,
|
|
375
|
+
"The short fiction draft, synopsis, selling points, and cover prompt were still written successfully.",
|
|
376
|
+
].join("\n"),
|
|
377
|
+
].join("\n"),
|
|
378
|
+
{ kind: "short_fiction_created", ...result },
|
|
379
|
+
);
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function summarizeCoverGenerationError(error: string | undefined): string {
|
|
385
|
+
const text = (error ?? "not generated").trim();
|
|
386
|
+
if (text.includes("HTTP 503")) {
|
|
387
|
+
return "cover provider returned HTTP 503; retry later or switch the Studio cover provider/model.";
|
|
388
|
+
}
|
|
389
|
+
if (text.includes("HTTP 502")) {
|
|
390
|
+
return "cover provider returned HTTP 502; retry later or switch the Studio cover provider/model.";
|
|
391
|
+
}
|
|
392
|
+
if (/API key is required|api key/i.test(text)) {
|
|
393
|
+
return "cover API key is missing; configure it in Studio service settings.";
|
|
394
|
+
}
|
|
395
|
+
return text.slice(0, 300);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ---------------------------------------------------------------------------
|
|
399
|
+
// 3. Standalone Cover Tool
|
|
400
|
+
// ---------------------------------------------------------------------------
|
|
401
|
+
|
|
402
|
+
const GenerateCoverParams = Type.Object({
|
|
403
|
+
title: Type.String({
|
|
404
|
+
description: "Required book or short-fiction title. Use the real story title when regenerating an existing cover.",
|
|
405
|
+
}),
|
|
406
|
+
intro: Type.Optional(Type.String({
|
|
407
|
+
description: "Optional synopsis or one-paragraph story hook to guide the cover.",
|
|
408
|
+
})),
|
|
409
|
+
sellingPoints: Type.Optional(Type.String({
|
|
410
|
+
description: "Optional selling points separated by semicolons or new lines, e.g. 婚姻背叛;证据反杀;女主冷笑.",
|
|
411
|
+
})),
|
|
412
|
+
coverPrompt: Type.Optional(Type.String({
|
|
413
|
+
description: "Optional concrete or revised visual direction. Use this when the user changes the cover prompt through chat. Keep it short and commercial; do not paste the whole story.",
|
|
414
|
+
})),
|
|
415
|
+
outputDir: Type.Optional(Type.String({
|
|
416
|
+
description: "Optional project-relative directory for cover-prompt.md and cover.png. For an existing short or cover prompt revision, use its existing final/cover directory to overwrite that cover.",
|
|
417
|
+
})),
|
|
418
|
+
coverBaseUrl: Type.Optional(Type.String({
|
|
419
|
+
description: "Optional image API base URL. Usually omit and use Studio cover config.",
|
|
420
|
+
})),
|
|
421
|
+
coverEndpoint: Type.Optional(Type.String({
|
|
422
|
+
description: "Optional exact image endpoint. Overrides coverBaseUrl.",
|
|
423
|
+
})),
|
|
424
|
+
coverModel: Type.Optional(Type.String({
|
|
425
|
+
description: "Optional image model. Usually omit and use Studio cover config.",
|
|
426
|
+
})),
|
|
427
|
+
coverSize: Type.Optional(Type.String({
|
|
428
|
+
description: "Optional image size, default 1024x1360.",
|
|
429
|
+
})),
|
|
430
|
+
coverApiKeyEnv: Type.Optional(Type.String({
|
|
431
|
+
description: "Optional env var containing the cover API key. Usually omit and use Studio cover config.",
|
|
432
|
+
})),
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
type GenerateCoverParamsType = Static<typeof GenerateCoverParams>;
|
|
436
|
+
|
|
437
|
+
export function createGenerateCoverTool(
|
|
438
|
+
projectRoot: string,
|
|
439
|
+
): AgentTool<typeof GenerateCoverParams> {
|
|
440
|
+
return {
|
|
441
|
+
name: "generate_cover",
|
|
442
|
+
description:
|
|
443
|
+
"Generate only a cover image and cover prompt from a title/synopsis/visual direction. " +
|
|
444
|
+
"Use this when the user asks to create/regenerate a cover or revise the cover prompt through chat, without rerunning story generation.",
|
|
445
|
+
label: "Generate Cover",
|
|
446
|
+
parameters: GenerateCoverParams,
|
|
447
|
+
async execute(
|
|
448
|
+
_toolCallId: string,
|
|
449
|
+
params: GenerateCoverParamsType,
|
|
450
|
+
_signal?: AbortSignal,
|
|
451
|
+
onUpdate?: AgentToolUpdateCallback,
|
|
452
|
+
): Promise<AgentToolResult<unknown>> {
|
|
453
|
+
onUpdate?.(textResult("Generating cover image..."));
|
|
454
|
+
const result = await generateShortFictionCover({
|
|
455
|
+
projectRoot,
|
|
456
|
+
title: params.title,
|
|
457
|
+
intro: params.intro,
|
|
458
|
+
sellingPoints: params.sellingPoints,
|
|
459
|
+
coverPrompt: params.coverPrompt,
|
|
460
|
+
outputDir: params.outputDir,
|
|
461
|
+
coverBaseUrl: params.coverBaseUrl,
|
|
462
|
+
coverEndpoint: params.coverEndpoint,
|
|
463
|
+
coverModel: params.coverModel,
|
|
464
|
+
coverSize: params.coverSize,
|
|
465
|
+
coverApiKeyEnv: params.coverApiKeyEnv,
|
|
466
|
+
});
|
|
467
|
+
return textResult(
|
|
468
|
+
[
|
|
469
|
+
`Cover generated for "${result.title}".`,
|
|
470
|
+
`Cover prompt: ${result.coverPromptPath}`,
|
|
471
|
+
`Cover image: ${result.coverImagePath}`,
|
|
472
|
+
].join("\n"),
|
|
473
|
+
{ kind: "cover_generated", ...result },
|
|
474
|
+
);
|
|
475
|
+
},
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ---------------------------------------------------------------------------
|
|
480
|
+
// 4. Deterministic writing tools
|
|
481
|
+
// ---------------------------------------------------------------------------
|
|
482
|
+
|
|
483
|
+
const WriteTruthFileParams = Type.Object({
|
|
484
|
+
bookId: Type.Optional(Type.String({ description: "Book ID. Omit to use the active book." })),
|
|
485
|
+
fileName: Type.String({ description: "Truth file name under story/, e.g. story_bible.md or current_focus.md." }),
|
|
486
|
+
content: Type.String({ description: "Full replacement content for the truth file." }),
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
export function createWriteTruthFileTool(
|
|
490
|
+
pipeline: PipelineRunner,
|
|
491
|
+
projectRoot: string,
|
|
492
|
+
activeBookId: string | null,
|
|
493
|
+
): AgentTool<typeof WriteTruthFileParams> {
|
|
494
|
+
const tools = createDeterministicInteractionTools(pipeline, projectRoot);
|
|
495
|
+
return {
|
|
496
|
+
name: "write_truth_file",
|
|
497
|
+
description: "Replace a truth/control file under story/ using deterministic project tools.",
|
|
498
|
+
label: "Write Truth File",
|
|
499
|
+
parameters: WriteTruthFileParams,
|
|
500
|
+
async execute(_toolCallId, params): Promise<AgentToolResult<undefined>> {
|
|
501
|
+
try {
|
|
502
|
+
const bookId = resolveToolBookId("write_truth_file", params.bookId, activeBookId);
|
|
503
|
+
const fileName = assertSafeTruthFileName(params.fileName);
|
|
504
|
+
await tools.writeTruthFile(bookId, fileName, params.content);
|
|
505
|
+
return textResult(`Updated "${fileName}" for "${bookId}".`);
|
|
506
|
+
} catch (err: any) {
|
|
507
|
+
return textResult(`write_truth_file failed: ${err?.message ?? String(err)}`);
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const RenameEntityParams = Type.Object({
|
|
514
|
+
bookId: Type.Optional(Type.String({ description: "Book ID. Omit to use the active book." })),
|
|
515
|
+
oldValue: Type.String({ description: "Current entity name." }),
|
|
516
|
+
newValue: Type.String({ description: "New entity name." }),
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
export function createRenameEntityTool(
|
|
520
|
+
pipeline: PipelineRunner,
|
|
521
|
+
projectRoot: string,
|
|
522
|
+
activeBookId: string | null,
|
|
523
|
+
): AgentTool<typeof RenameEntityParams> {
|
|
524
|
+
const tools = createDeterministicInteractionTools(pipeline, projectRoot);
|
|
525
|
+
return {
|
|
526
|
+
name: "rename_entity",
|
|
527
|
+
description: "Rename an entity across truth files and chapters using deterministic edit control.",
|
|
528
|
+
label: "Rename Entity",
|
|
529
|
+
parameters: RenameEntityParams,
|
|
530
|
+
async execute(_toolCallId, params): Promise<AgentToolResult<undefined>> {
|
|
531
|
+
const bookId = resolveToolBookId("rename_entity", params.bookId, activeBookId);
|
|
532
|
+
const result = await tools.renameEntity(bookId, params.oldValue, params.newValue) as {
|
|
533
|
+
readonly __interaction?: { readonly responseText?: string };
|
|
534
|
+
};
|
|
535
|
+
const summary = result.__interaction?.responseText ?? `Renamed "${params.oldValue}" to "${params.newValue}" in "${bookId}".`;
|
|
536
|
+
return textResult(summary);
|
|
537
|
+
},
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const PatchChapterTextParams = Type.Object({
|
|
542
|
+
bookId: Type.Optional(Type.String({ description: "Book ID. Omit to use the active book." })),
|
|
543
|
+
chapterNumber: Type.Number({ description: "Chapter number to patch." }),
|
|
544
|
+
targetText: Type.String({ description: "Exact text to replace." }),
|
|
545
|
+
replacementText: Type.String({ description: "Replacement text." }),
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
export function createPatchChapterTextTool(
|
|
549
|
+
pipeline: PipelineRunner,
|
|
550
|
+
projectRoot: string,
|
|
551
|
+
activeBookId: string | null,
|
|
552
|
+
): AgentTool<typeof PatchChapterTextParams> {
|
|
553
|
+
const tools = createDeterministicInteractionTools(pipeline, projectRoot);
|
|
554
|
+
return {
|
|
555
|
+
name: "patch_chapter_text",
|
|
556
|
+
description: "Apply a deterministic local text patch to a chapter and mark it for review.",
|
|
557
|
+
label: "Patch Chapter",
|
|
558
|
+
parameters: PatchChapterTextParams,
|
|
559
|
+
async execute(_toolCallId, params): Promise<AgentToolResult<undefined>> {
|
|
560
|
+
const bookId = resolveToolBookId("patch_chapter_text", params.bookId, activeBookId);
|
|
561
|
+
const result = await tools.patchChapterText(
|
|
562
|
+
bookId,
|
|
563
|
+
params.chapterNumber,
|
|
564
|
+
params.targetText,
|
|
565
|
+
params.replacementText,
|
|
566
|
+
) as {
|
|
567
|
+
readonly __interaction?: { readonly responseText?: string };
|
|
568
|
+
};
|
|
569
|
+
const summary = result.__interaction?.responseText ?? `Patched chapter ${params.chapterNumber} for "${bookId}".`;
|
|
570
|
+
return textResult(summary);
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ---------------------------------------------------------------------------
|
|
576
|
+
// 3. Read Tool
|
|
577
|
+
// ---------------------------------------------------------------------------
|
|
578
|
+
|
|
579
|
+
const ReadParams = Type.Object({
|
|
580
|
+
path: Type.String({ description: "File path relative to books/, or an absolute path when system path reading is enabled." }),
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
export interface ReadToolOptions {
|
|
584
|
+
readonly allowSystemPaths?: boolean;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function resolveReadPath(booksRoot: string, requestedPath: string, options: ReadToolOptions): string {
|
|
588
|
+
if (options.allowSystemPaths && isAbsolute(requestedPath)) {
|
|
589
|
+
return resolve(requestedPath);
|
|
590
|
+
}
|
|
591
|
+
return safeBooksPath(booksRoot, requestedPath);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export function createReadTool(
|
|
595
|
+
projectRoot: string,
|
|
596
|
+
options: ReadToolOptions = {},
|
|
597
|
+
): AgentTool<typeof ReadParams> {
|
|
598
|
+
const booksRoot = join(projectRoot, "books");
|
|
599
|
+
const description = options.allowSystemPaths
|
|
600
|
+
? "Read a file. Relative paths resolve under books/; absolute paths read from the system filesystem."
|
|
601
|
+
: "Read a file from the book directory. Path is relative to books/.";
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
name: "read",
|
|
605
|
+
description,
|
|
606
|
+
label: "Read File",
|
|
607
|
+
parameters: ReadParams,
|
|
608
|
+
async execute(
|
|
609
|
+
_toolCallId: string,
|
|
610
|
+
params: Static<typeof ReadParams>,
|
|
611
|
+
): Promise<AgentToolResult<undefined>> {
|
|
612
|
+
try {
|
|
613
|
+
const filePath = resolveReadPath(booksRoot, params.path, options);
|
|
614
|
+
let content = await readFile(filePath, "utf-8");
|
|
615
|
+
if (content.length > 10_000) {
|
|
616
|
+
content = content.slice(0, 10_000) + "\n\n... [truncated at 10 000 chars]";
|
|
617
|
+
}
|
|
618
|
+
return textResult(content);
|
|
619
|
+
} catch (err: any) {
|
|
620
|
+
return textResult(`Failed to read "${params.path}": ${err?.message ?? String(err)}`);
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// ---------------------------------------------------------------------------
|
|
627
|
+
// 3. Edit Tool
|
|
628
|
+
// ---------------------------------------------------------------------------
|
|
629
|
+
|
|
630
|
+
const EditParams = Type.Object({
|
|
631
|
+
path: Type.String({ description: "File path relative to books/" }),
|
|
632
|
+
old_string: Type.String({ description: "Exact string to find in the file" }),
|
|
633
|
+
new_string: Type.String({ description: "Replacement string" }),
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
export function createEditTool(projectRoot: string): AgentTool<typeof EditParams> {
|
|
637
|
+
const booksRoot = join(projectRoot, "books");
|
|
638
|
+
|
|
639
|
+
return {
|
|
640
|
+
name: "edit",
|
|
641
|
+
description:
|
|
642
|
+
"Edit a file under books/ via exact string replacement. " +
|
|
643
|
+
"old_string must appear exactly once in the file. " +
|
|
644
|
+
"For chapter text use patch_chapter_text; for canonical truth files (story_bible/volume_outline/book_rules/current_focus) prefer write_truth_file; " +
|
|
645
|
+
"to rewrite or polish a whole chapter call sub_agent with agent=\"reviser\".",
|
|
646
|
+
label: "Edit File",
|
|
647
|
+
parameters: EditParams,
|
|
648
|
+
async execute(
|
|
649
|
+
_toolCallId: string,
|
|
650
|
+
params: Static<typeof EditParams>,
|
|
651
|
+
): Promise<AgentToolResult<undefined>> {
|
|
652
|
+
try {
|
|
653
|
+
const filePath = safeBooksPath(booksRoot, params.path);
|
|
654
|
+
const content = await readFile(filePath, "utf-8");
|
|
655
|
+
const idx = content.indexOf(params.old_string);
|
|
656
|
+
if (idx === -1) {
|
|
657
|
+
return textResult(`old_string not found in "${params.path}".`);
|
|
658
|
+
}
|
|
659
|
+
if (content.indexOf(params.old_string, idx + 1) !== -1) {
|
|
660
|
+
return textResult(`old_string appears more than once in "${params.path}". Provide a more specific match.`);
|
|
661
|
+
}
|
|
662
|
+
const updated = content.slice(0, idx) + params.new_string + content.slice(idx + params.old_string.length);
|
|
663
|
+
await writeFile(filePath, updated, "utf-8");
|
|
664
|
+
return textResult(`File "${params.path}" updated successfully.`);
|
|
665
|
+
} catch (err: any) {
|
|
666
|
+
return textResult(`Failed to edit "${params.path}": ${err?.message ?? String(err)}`);
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// ---------------------------------------------------------------------------
|
|
673
|
+
// 4. Write Tool
|
|
674
|
+
// ---------------------------------------------------------------------------
|
|
675
|
+
|
|
676
|
+
const WriteFileParams = Type.Object({
|
|
677
|
+
path: Type.String({ description: "File path relative to books/" }),
|
|
678
|
+
content: Type.String({ description: "Full file content to write" }),
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
export function createWriteFileTool(projectRoot: string): AgentTool<typeof WriteFileParams> {
|
|
682
|
+
const booksRoot = join(projectRoot, "books");
|
|
683
|
+
|
|
684
|
+
return {
|
|
685
|
+
name: "write",
|
|
686
|
+
description:
|
|
687
|
+
"Create a new file, or fully replace an existing file's content under books/. " +
|
|
688
|
+
"Parent directories are created automatically. Existing content is overwritten silently — " +
|
|
689
|
+
"for canonical truth files prefer write_truth_file; " +
|
|
690
|
+
"for whole-chapter rewrites/polishing call sub_agent with agent=\"reviser\".",
|
|
691
|
+
label: "Write File",
|
|
692
|
+
parameters: WriteFileParams,
|
|
693
|
+
async execute(
|
|
694
|
+
_toolCallId: string,
|
|
695
|
+
params: Static<typeof WriteFileParams>,
|
|
696
|
+
): Promise<AgentToolResult<undefined>> {
|
|
697
|
+
try {
|
|
698
|
+
const filePath = safeBooksPath(booksRoot, params.path);
|
|
699
|
+
const parentDir = resolve(filePath, "..");
|
|
700
|
+
const { mkdir } = await import("node:fs/promises");
|
|
701
|
+
await mkdir(parentDir, { recursive: true });
|
|
702
|
+
await writeFile(filePath, params.content, "utf-8");
|
|
703
|
+
return textResult(`File "${params.path}" written successfully.`);
|
|
704
|
+
} catch (err: any) {
|
|
705
|
+
return textResult(`Failed to write "${params.path}": ${err?.message ?? String(err)}`);
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// ---------------------------------------------------------------------------
|
|
712
|
+
// 5. Grep Tool
|
|
713
|
+
// ---------------------------------------------------------------------------
|
|
714
|
+
|
|
715
|
+
const GrepParams = Type.Object({
|
|
716
|
+
bookId: Type.String({ description: "Book ID to search within" }),
|
|
717
|
+
pattern: Type.String({ description: "Search pattern (plain text or regex)" }),
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
export function createGrepTool(projectRoot: string): AgentTool<typeof GrepParams> {
|
|
721
|
+
const booksRoot = join(projectRoot, "books");
|
|
722
|
+
|
|
723
|
+
return {
|
|
724
|
+
name: "grep",
|
|
725
|
+
description:
|
|
726
|
+
"Search for a text pattern across a book's story/ and chapters/ directories. Returns matching lines.",
|
|
727
|
+
label: "Search",
|
|
728
|
+
parameters: GrepParams,
|
|
729
|
+
async execute(
|
|
730
|
+
_toolCallId: string,
|
|
731
|
+
params: Static<typeof GrepParams>,
|
|
732
|
+
): Promise<AgentToolResult<undefined>> {
|
|
733
|
+
try {
|
|
734
|
+
const bookDir = safeBooksPath(booksRoot, params.bookId);
|
|
735
|
+
const regex = new RegExp(params.pattern, "gi");
|
|
736
|
+
const results: string[] = [];
|
|
737
|
+
|
|
738
|
+
async function searchDir(dir: string, prefix: string) {
|
|
739
|
+
let entries: string[];
|
|
740
|
+
try {
|
|
741
|
+
entries = await readdir(dir);
|
|
742
|
+
} catch {
|
|
743
|
+
return; // directory doesn't exist
|
|
744
|
+
}
|
|
745
|
+
for (const entry of entries) {
|
|
746
|
+
const fullPath = join(dir, entry);
|
|
747
|
+
const entryStat = await stat(fullPath);
|
|
748
|
+
if (entryStat.isDirectory()) {
|
|
749
|
+
await searchDir(fullPath, `${prefix}${entry}/`);
|
|
750
|
+
} else if (entry.endsWith(".md") || entry.endsWith(".txt") || entry.endsWith(".json")) {
|
|
751
|
+
const content = await readFile(fullPath, "utf-8");
|
|
752
|
+
const lines = content.split("\n");
|
|
753
|
+
for (let i = 0; i < lines.length; i++) {
|
|
754
|
+
if (regex.test(lines[i])) {
|
|
755
|
+
results.push(`${prefix}${entry}:${i + 1}: ${lines[i]}`);
|
|
756
|
+
regex.lastIndex = 0; // reset for next test
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
await Promise.all([
|
|
764
|
+
searchDir(join(bookDir, "story"), "story/"),
|
|
765
|
+
searchDir(join(bookDir, "chapters"), "chapters/"),
|
|
766
|
+
]);
|
|
767
|
+
|
|
768
|
+
if (results.length === 0) {
|
|
769
|
+
return textResult(`No matches for "${params.pattern}" in book "${params.bookId}".`);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const truncated = results.length > 100
|
|
773
|
+
? results.slice(0, 100).join("\n") + `\n\n... [${results.length - 100} more matches]`
|
|
774
|
+
: results.join("\n");
|
|
775
|
+
|
|
776
|
+
return textResult(truncated);
|
|
777
|
+
} catch (err: any) {
|
|
778
|
+
return textResult(`Grep failed: ${err?.message ?? String(err)}`);
|
|
779
|
+
}
|
|
780
|
+
},
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// ---------------------------------------------------------------------------
|
|
785
|
+
// 5. Ls Tool
|
|
786
|
+
// ---------------------------------------------------------------------------
|
|
787
|
+
|
|
788
|
+
const LsParams = Type.Object({
|
|
789
|
+
bookId: Type.String({ description: "Book ID" }),
|
|
790
|
+
subdir: Type.Optional(
|
|
791
|
+
Type.String({ description: "Subdirectory within the book, e.g. 'story', 'chapters', 'story/runtime'" }),
|
|
792
|
+
),
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
export function createLsTool(projectRoot: string): AgentTool<typeof LsParams> {
|
|
796
|
+
const booksRoot = join(projectRoot, "books");
|
|
797
|
+
|
|
798
|
+
return {
|
|
799
|
+
name: "ls",
|
|
800
|
+
description: "List files in a book directory. Optionally specify a subdirectory like 'story' or 'chapters'.",
|
|
801
|
+
label: "List Files",
|
|
802
|
+
parameters: LsParams,
|
|
803
|
+
async execute(
|
|
804
|
+
_toolCallId: string,
|
|
805
|
+
params: Static<typeof LsParams>,
|
|
806
|
+
): Promise<AgentToolResult<undefined>> {
|
|
807
|
+
try {
|
|
808
|
+
const base = safeBooksPath(booksRoot, params.bookId);
|
|
809
|
+
const target = params.subdir ? safeBooksPath(base, params.subdir) : base;
|
|
810
|
+
|
|
811
|
+
const entries = await readdir(target);
|
|
812
|
+
const details: string[] = [];
|
|
813
|
+
|
|
814
|
+
for (const entry of entries) {
|
|
815
|
+
const fullPath = join(target, entry);
|
|
816
|
+
try {
|
|
817
|
+
const entryStat = await stat(fullPath);
|
|
818
|
+
const suffix = entryStat.isDirectory() ? "/" : ` (${entryStat.size} bytes)`;
|
|
819
|
+
details.push(`${entry}${suffix}`);
|
|
820
|
+
} catch {
|
|
821
|
+
details.push(entry);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (details.length === 0) {
|
|
826
|
+
return textResult(`Directory is empty: ${params.bookId}/${params.subdir ?? ""}`);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return textResult(details.join("\n"));
|
|
830
|
+
} catch (err: any) {
|
|
831
|
+
return textResult(`Failed to list "${params.bookId}/${params.subdir ?? ""}": ${err?.message ?? String(err)}`);
|
|
832
|
+
}
|
|
833
|
+
},
|
|
834
|
+
};
|
|
835
|
+
}
|