2sagaco48 1.0.0
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/diagnostics.mjs +141 -0
- package/dist/compute.worker-j5ahIpF-.js +1 -0
- package/dist/graph.worker-DG6iGCB9.js +1 -0
- package/dist/index.html +2314 -0
- package/flatten-guide.md +248 -0
- package/index.html +12 -0
- package/package.json +38 -0
- package/src/App.orig.tsx +204 -0
- package/src/App.orig1.tsx +27 -0
- package/src/App.orig2.tsx +25 -0
- package/src/App.tsx +3 -0
- package/src/BaseApp.tsx +18 -0
- package/src/INTEGRATION_LEDGER.md +22 -0
- package/src/PERSIST_CANARY.orig.txt +1 -0
- package/src/PERSIST_CANARY.txt +1 -0
- package/src/REGRESSION_LEDGER.md +651 -0
- package/src/TEMPLATE_PIPELINE_AUDIT.md +208 -0
- package/src/WILLIAMS_PERSONA_GUIDE.md +62 -0
- package/src/chat/VeritasChat.tsx +69 -0
- package/src/chat/synthesis.ts +31 -0
- package/src/chat/tier.ts +45 -0
- package/src/components/AdaptersPage.orig.tsx +99 -0
- package/src/components/AdaptersPage.tsx +3 -0
- package/src/components/AdversarialPanel.tsx +136 -0
- package/src/components/AnchorBaselinePanel.tsx +75 -0
- package/src/components/AnswerPanel.tsx +60 -0
- package/src/components/ArtifactPanel.tsx +43 -0
- package/src/components/ChatApp.orig.tsx +1582 -0
- package/src/components/ChatApp.orig1.tsx +722 -0
- package/src/components/ChatApp.tsx +95 -0
- package/src/components/ChatAugmentPanels.tsx +73 -0
- package/src/components/CitationLedgerPanel.tsx +313 -0
- package/src/components/ComputeSandboxPanel.tsx +130 -0
- package/src/components/ConfigPanel.tsx +110 -0
- package/src/components/ControlPlanePage.tsx +195 -0
- package/src/components/CreativeTreeLifePage.tsx +234 -0
- package/src/components/CreativeTreeOfLifePanel.tsx +149 -0
- package/src/components/DebugTracePanel.tsx +137 -0
- package/src/components/DeepReasoningTrace.tsx +48 -0
- package/src/components/EntitySheetPanel.tsx +51 -0
- package/src/components/FailureModesPanel.tsx +104 -0
- package/src/components/GBSDashboard.tsx +239 -0
- package/src/components/GraphView.tsx +116 -0
- package/src/components/HUD.tsx +125 -0
- package/src/components/HypothesisPanel.tsx +67 -0
- package/src/components/InnovationGenomeEngine.tsx +792 -0
- package/src/components/InnovationPersonaGuide.tsx +348 -0
- package/src/components/InnovationPersonaPanel.tsx +289 -0
- package/src/components/LiveResourceHUD.tsx +109 -0
- package/src/components/LongWriterPanel.tsx +112 -0
- package/src/components/MainPipelineV10Bridge.tsx +72 -0
- package/src/components/MarkdownLite.tsx +63 -0
- package/src/components/MemoryInspector.tsx +38 -0
- package/src/components/MemoryMonitor.tsx +52 -0
- package/src/components/MemoryStressPanel.tsx +176 -0
- package/src/components/ModulesPage.tsx +171 -0
- package/src/components/PipelineDebugConsole.orig.tsx +1856 -0
- package/src/components/PipelineDebugConsole.tsx +3 -0
- package/src/components/PreFlightHUD.tsx +67 -0
- package/src/components/PrismaFetchTracePanel.tsx +87 -0
- package/src/components/ReportOSPanel.tsx +165 -0
- package/src/components/ResourceEstimatorPage.tsx +327 -0
- package/src/components/RichText.tsx +181 -0
- package/src/components/SagaOsPanel.tsx +605 -0
- package/src/components/SagaOsReference.tsx +421 -0
- package/src/components/SagaOsV2Panel.tsx +594 -0
- package/src/components/SagaStyleTab.tsx +382 -0
- package/src/components/SageBlueprintPanel.tsx +416 -0
- package/src/components/SharedChatInput.tsx +77 -0
- package/src/components/StatFinancePanel.tsx +190 -0
- package/src/components/StylePersonaPanel.tsx +128 -0
- package/src/components/TemplatesPage.tsx +300 -0
- package/src/components/TestPanel.tsx +63 -0
- package/src/components/TraceLog.tsx +89 -0
- package/src/components/V15CalibrationAugment.tsx +1134 -0
- package/src/components/V15CalibrationDialog.tsx +1871 -0
- package/src/components/V15Overlay.orig.tsx +120 -0
- package/src/components/V15Overlay.tsx +4 -0
- package/src/components/V15OverlayWrapper.tsx +362 -0
- package/src/components/V15Toggle.tsx +34 -0
- package/src/console/DiagnosticRecoveryPanel.tsx +358 -0
- package/src/console/EngineeringConsole.tsx +103 -0
- package/src/console/FictionSystemPanels.tsx +882 -0
- package/src/console/InspectorPanels.tsx +254 -0
- package/src/console/ManuscriptGraderPanel.tsx +182 -0
- package/src/console/RecursiveReviserPanel.tsx +245 -0
- package/src/console/SeedControlPanel.tsx +135 -0
- package/src/console/StoryControllerPanel.tsx +443 -0
- package/src/console/engineBridge.ts +258 -0
- package/src/console/gemini.ts +106 -0
- package/src/console/grading.ts +250 -0
- package/src/console/util.ts +45 -0
- package/src/fiction/continuity.ts +372 -0
- package/src/fiction/crash-protection.ts +111 -0
- package/src/fiction/diagnostics.ts +208 -0
- package/src/fiction/diff.ts +74 -0
- package/src/fiction/entity-state-machine.ts +214 -0
- package/src/fiction/nive-engine.ts +404 -0
- package/src/fiction/pipeline-selftest.ts +132 -0
- package/src/fiction/seed-control.ts +193 -0
- package/src/fiction/story-engine.ts +326 -0
- package/src/fiction/types.ts +68 -0
- package/src/flatten-guide.md +379 -0
- package/src/index.css +14 -0
- package/src/index.orig.css +13 -0
- package/src/lib/academic-sources.base.ts +188 -0
- package/src/lib/academic-sources.orig.ts +28 -0
- package/src/lib/academic-sources.ts +10 -0
- package/src/lib/advanced-math.ts +119 -0
- package/src/lib/adversarial-engine.base.ts +409 -0
- package/src/lib/adversarial-engine.ts +82 -0
- package/src/lib/ai-revision.ts +623 -0
- package/src/lib/app-state.tsx +426 -0
- package/src/lib/artifact-registry.ts +97 -0
- package/src/lib/artifacts.ts +180 -0
- package/src/lib/atlas-dr.ts +167 -0
- package/src/lib/browser-mtls.ts +127 -0
- package/src/lib/browser-queue.ts +221 -0
- package/src/lib/browser-search-scraper.base.ts +382 -0
- package/src/lib/browser-search-scraper.orig.ts +90 -0
- package/src/lib/browser-search-scraper.ts +10 -0
- package/src/lib/calc-interceptor.ts +82 -0
- package/src/lib/citation-lane-tap.ts +78 -0
- package/src/lib/citation-ledger-store.ts +369 -0
- package/src/lib/citation-ledger.ts +203 -0
- package/src/lib/compute-sandbox.ts +215 -0
- package/src/lib/compute.worker.ts +153 -0
- package/src/lib/connectors/gemini.ts +120 -0
- package/src/lib/connectors/jina.ts +233 -0
- package/src/lib/connectors/marketdata.ts +51 -0
- package/src/lib/connectors/prismafetch.base.ts +175 -0
- package/src/lib/connectors/prismafetch.ts +1 -0
- package/src/lib/connectors/serpapi.ts +65 -0
- package/src/lib/connectors/wikidata.ts +44 -0
- package/src/lib/constraints.ts +331 -0
- package/src/lib/continuation-detector.base.ts +153 -0
- package/src/lib/continuation-detector.ts +48 -0
- package/src/lib/contradraft.ts +168 -0
- package/src/lib/cors-proxy.ts +120 -0
- package/src/lib/coverage.ts +81 -0
- package/src/lib/debug/architecture-prescription.ts +556 -0
- package/src/lib/debug/covea-repair.ts +543 -0
- package/src/lib/debug/helios-ground.ts +319 -0
- package/src/lib/debug/intent-decomposer.ts +281 -0
- package/src/lib/debug/intent-lattice.ts +349 -0
- package/src/lib/debug/pipeline-diagnosis.ts +1149 -0
- package/src/lib/debug/pipeline-trace-bus.ts +424 -0
- package/src/lib/debug/prompt-forge.ts +678 -0
- package/src/lib/debug/repair-sites.ts +513 -0
- package/src/lib/debug/research-phase.ts +266 -0
- package/src/lib/debug/retrieval-context.ts +76 -0
- package/src/lib/debug/retrieval-hardener.ts +142 -0
- package/src/lib/debug/scraper-debug-runner.ts +576 -0
- package/src/lib/debug/scraper-forensics.ts +418 -0
- package/src/lib/debug/scraper-lane-roadmap.ts +136 -0
- package/src/lib/debug/self-test.ts +415 -0
- package/src/lib/debug/step-attribution.ts +211 -0
- package/src/lib/debug/template-rubric.ts +301 -0
- package/src/lib/debug/unified-innovation.ts +126 -0
- package/src/lib/debug/veritas-hybrid-scraper.ts +335 -0
- package/src/lib/defense-registry.ts +104 -0
- package/src/lib/defenses.ts +162 -0
- package/src/lib/deterministic-citation-ledger.ts +174 -0
- package/src/lib/elo-registry.base.ts +117 -0
- package/src/lib/elo-registry.ts +1 -0
- package/src/lib/entity-resolver.ts +291 -0
- package/src/lib/failure-modes.ts +267 -0
- package/src/lib/feature-registry.ts +69 -0
- package/src/lib/flaw-registry.ts +143 -0
- package/src/lib/flaws/_template.ts +52 -0
- package/src/lib/flaws/builtins.ts +63 -0
- package/src/lib/flaws/finance.ts +169 -0
- package/src/lib/flaws/fixers.ts +10 -0
- package/src/lib/flaws/index.ts +32 -0
- package/src/lib/flaws/legal.ts +157 -0
- package/src/lib/flaws/medical.ts +188 -0
- package/src/lib/flaws/original-defenses-pack.ts +81 -0
- package/src/lib/flaws/sample-declarative-pack.json +38 -0
- package/src/lib/flaws/selftest.ts +201 -0
- package/src/lib/flaws/software-extended.ts +125 -0
- package/src/lib/flaws/software-rn-webgl.ts +182 -0
- package/src/lib/flaws/statistics-advanced.ts +46 -0
- package/src/lib/flaws/statistics.ts +61 -0
- package/src/lib/gbse/config.ts +20 -0
- package/src/lib/gbse/engine.ts +226 -0
- package/src/lib/gbse/graph.ts +118 -0
- package/src/lib/gbse/graph.worker.ts +21 -0
- package/src/lib/gbse/tests.ts +117 -0
- package/src/lib/gbse/tiers.ts +65 -0
- package/src/lib/gbse/types.ts +94 -0
- package/src/lib/innovation-genome-engine-v2.orig.ts +1001 -0
- package/src/lib/innovation-genome-engine-v2.ts +3 -0
- package/src/lib/innovation-genome-engine.orig.ts +330 -0
- package/src/lib/innovation-genome-engine.ts +3 -0
- package/src/lib/innovation-genome-v10.orig.ts +314 -0
- package/src/lib/innovation-genome-v10.ts +2 -0
- package/src/lib/innovation-genome-v3.orig.ts +2015 -0
- package/src/lib/innovation-genome-v3.ts +2 -0
- package/src/lib/innovation-genome-v4.orig.ts +1959 -0
- package/src/lib/innovation-genome-v4.ts +2 -0
- package/src/lib/innovation-genome-v5.orig.ts +1210 -0
- package/src/lib/innovation-genome-v5.ts +2 -0
- package/src/lib/innovation-genome-v7.orig.ts +2549 -0
- package/src/lib/innovation-genome-v7.ts +2 -0
- package/src/lib/innovation-genome-v8.orig.ts +1485 -0
- package/src/lib/innovation-genome-v8.ts +2 -0
- package/src/lib/innovation-genome-v9.orig.ts +643 -0
- package/src/lib/innovation-genome-v9.ts +2 -0
- package/src/lib/jina.base.ts +293 -0
- package/src/lib/jina.ts +1 -0
- package/src/lib/live-telemetry.ts +61 -0
- package/src/lib/longwriter.ts +84 -0
- package/src/lib/manuscript-grade-v2.ts +298 -0
- package/src/lib/memory-governor.ts +173 -0
- package/src/lib/memory-stress-tests.ts +248 -0
- package/src/lib/model-intelligence.ts +48 -0
- package/src/lib/model-rotator.ts +75 -0
- package/src/lib/models.orig.ts +513 -0
- package/src/lib/models.ts +151 -0
- package/src/lib/n-deep.base.ts +592 -0
- package/src/lib/n-deep.ts +51 -0
- package/src/lib/nih-simulator.ts +56 -0
- package/src/lib/nih-vulnerability-fixes.ts +460 -0
- package/src/lib/omega-templates.base.ts +294 -0
- package/src/lib/omega-templates.ts +225 -0
- package/src/lib/omni-nexus.ts +64 -0
- package/src/lib/oracle-adapters.ts +67 -0
- package/src/lib/oracle-registry.ts +163 -0
- package/src/lib/orchestrator.ts +357 -0
- package/src/lib/output-boundary.ts +235 -0
- package/src/lib/overrides/vite-native-chaos-harness.ts +154 -0
- package/src/lib/overrides/vite-native-contract-plane.ts +245 -0
- package/src/lib/overrides/vite-native-doctor-plane.ts +208 -0
- package/src/lib/overrides/vite-native-knowledge-store.ts +267 -0
- package/src/lib/overrides/vite-native-policy-plane.ts +190 -0
- package/src/lib/overrides/vite-native-replay-plane.ts +197 -0
- package/src/lib/overrides/vite-native-runtime-plane.ts +106 -0
- package/src/lib/overrides/vite-native-scraper.ts +848 -0
- package/src/lib/overrides/vite-native-selftest-functional.ts +217 -0
- package/src/lib/overrides/vite-native-selftest.ts +183 -0
- package/src/lib/overrides/vite-native-snapshot-plane.ts +123 -0
- package/src/lib/philosophy-toolkit.ts +75 -0
- package/src/lib/pipeline.orig.ts +659 -0
- package/src/lib/pipeline.ts +42 -0
- package/src/lib/precache.ts +128 -0
- package/src/lib/py-sandbox.ts +205 -0
- package/src/lib/quality-score.ts +96 -0
- package/src/lib/quant-engine.ts +231 -0
- package/src/lib/quant-lib.ts +80 -0
- package/src/lib/reportos.ts +175 -0
- package/src/lib/research-os.ts +306 -0
- package/src/lib/resource-estimator.ts +114 -0
- package/src/lib/rpm-governor.ts +93 -0
- package/src/lib/runtime-estimator.ts +135 -0
- package/src/lib/sage/empty-space-v6.ts +560 -0
- package/src/lib/sage/empty-space-v7.ts +222 -0
- package/src/lib/sage/empty-space.ts +733 -0
- package/src/lib/sage/engine.ts +786 -0
- package/src/lib/sage/index.ts +17 -0
- package/src/lib/sage/judge.ts +246 -0
- package/src/lib/sage/prng.ts +53 -0
- package/src/lib/sage/reality-grounding.ts +380 -0
- package/src/lib/sage/repetition.ts +151 -0
- package/src/lib/sage/saga-os-v2-engines.ts +1870 -0
- package/src/lib/sage/saga-os-v2.ts +274 -0
- package/src/lib/sage/saga-os-v3-core.ts +1491 -0
- package/src/lib/sage/saga-os-v3-release.ts +739 -0
- package/src/lib/sage/saga-os-v4-aureate.ts +1161 -0
- package/src/lib/sage/saga-os-v4.ts +212 -0
- package/src/lib/sage/saga-os-v5.ts +205 -0
- package/src/lib/sage/saga-os-v6.ts +346 -0
- package/src/lib/sage/saga-os-v7.ts +314 -0
- package/src/lib/sage/saga-os-v8.ts +505 -0
- package/src/lib/sage/saga-os.ts +329 -0
- package/src/lib/sage/taxonomy.ts +403 -0
- package/src/lib/sage/unified-roll.ts +524 -0
- package/src/lib/sage/worked-sketches.ts +44 -0
- package/src/lib/sage.ts +166 -0
- package/src/lib/scraper-debug-bus.ts +53 -0
- package/src/lib/scraper-enhanced.orig.ts +153 -0
- package/src/lib/scraper-enhanced.ts +10 -0
- package/src/lib/scraper-hardener.base.orig.ts +155 -0
- package/src/lib/scraper-hardener.base.ts +317 -0
- package/src/lib/scraper-hardener.ts +1 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.orig.ts +419 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.ts +13 -0
- package/src/lib/scraper-vnext/arbiter-omega.orig.ts +688 -0
- package/src/lib/scraper-vnext/arbiter-omega.ts +13 -0
- package/src/lib/scraper-vnext/canonical-portfolio-augments.ts +479 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.orig.ts +1529 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.ts +13 -0
- package/src/lib/scraper-vnext/conclave-omega.ts +1830 -0
- package/src/lib/scraper-vnext/content-extractor-v2.ts +226 -0
- package/src/lib/scraper-vnext/diagnostics-suite.ts +36 -0
- package/src/lib/scraper-vnext/epistemic-packer.ts +97 -0
- package/src/lib/scraper-vnext/fusion-v2.ts +65 -0
- package/src/lib/scraper-vnext/hydra-reader.orig.ts +1216 -0
- package/src/lib/scraper-vnext/hydra-reader.ts +13 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.orig.ts +91 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.ts +13 -0
- package/src/lib/scraper-vnext/nexus-consensus.orig.ts +905 -0
- package/src/lib/scraper-vnext/nexus-consensus.ts +13 -0
- package/src/lib/scraper-vnext/portfolio-consensus-adjudicator.ts +804 -0
- package/src/lib/scraper-vnext/portfolio-consensus-memory.ts +377 -0
- package/src/lib/scraper-vnext/portfolio-terminal-governor.ts +548 -0
- package/src/lib/scraper-vnext/query-strategist.orig.ts +224 -0
- package/src/lib/scraper-vnext/query-strategist.ts +423 -0
- package/src/lib/scraper-vnext/retrieval-accelerator.ts +708 -0
- package/src/lib/scraper-vnext/retrieval-audit-augments.ts +477 -0
- package/src/lib/scraper-vnext/retrieval-control-plane.ts +904 -0
- package/src/lib/scraper-vnext/retrieval-policy-augments.ts +358 -0
- package/src/lib/scraper-vnext/safe-fetch-v2.ts +157 -0
- package/src/lib/scraper-vnext/sentinel-omega.ts +56 -0
- package/src/lib/scraper-vnext/sentinel-orchestrator.ts +69 -0
- package/src/lib/scraper-vnext/sibyl-oracle.orig.ts +413 -0
- package/src/lib/scraper-vnext/sibyl-oracle.ts +13 -0
- package/src/lib/scraper-vnext/smart-read-v2.ts +55 -0
- package/src/lib/scraper-vnext/spa-rescue-bridge.ts +608 -0
- package/src/lib/scraper-vnext/strata-engine.orig.ts +2666 -0
- package/src/lib/scraper-vnext/strata-engine.ts +13 -0
- package/src/lib/scraper-vnext/structured-source-adapter.orig.ts +369 -0
- package/src/lib/scraper-vnext/structured-source-adapter.ts +34 -0
- package/src/lib/scraper-vnext/terminal-complete.ts +224 -0
- package/src/lib/scraper-vnext/terminal-final.ts +277 -0
- package/src/lib/scraper-vnext/terminal-saturation.ts +345 -0
- package/src/lib/scraper-vnext/terminal-wire.ts +425 -0
- package/src/lib/scraper-vnext/vanguard-titanium.orig.ts +467 -0
- package/src/lib/scraper-vnext/vanguard-titanium.ts +17 -0
- package/src/lib/search-cache.ts +138 -0
- package/src/lib/sloop-runner.base.ts +223 -0
- package/src/lib/sloop-runner.ts +104 -0
- package/src/lib/sloop.ts +114 -0
- package/src/lib/small-model-adapters.ts +82 -0
- package/src/lib/sscp.ts +86 -0
- package/src/lib/system-map.ts +54 -0
- package/src/lib/uni-node.ts +86 -0
- package/src/lib/universal-rigor-guard.ts +433 -0
- package/src/lib/v15-gate-testbed.ts +274 -0
- package/src/lib/v15-gemini.base.ts +89 -0
- package/src/lib/v15-gemini.ts +1 -0
- package/src/lib/v15-grounding.orig.ts +241 -0
- package/src/lib/v15-grounding.ts +79 -0
- package/src/lib/v15-pipeline.base.ts +769 -0
- package/src/lib/v15-pipeline.orig.ts +1154 -0
- package/src/lib/v15-pipeline.ts +674 -0
- package/src/lib/v15-questions.ts +64 -0
- package/src/lib/v15-rate-limiter.orig.ts +91 -0
- package/src/lib/v15-rate-limiter.ts +48 -0
- package/src/lib/v15-state.base.ts +87 -0
- package/src/lib/v15-state.ts +28 -0
- package/src/lib/visual-table-generator.tsx +207 -0
- package/src/lib/wasm-runtime.ts +133 -0
- package/src/lib/williams-persona-guide.ts +265 -0
- package/src/lib/williams-style.base.ts +464 -0
- package/src/lib/williams-style.ts +59 -0
- package/src/lib/worker-pool.ts +96 -0
- package/src/lib/writing-tiers.ts +125 -0
- package/src/main.orig.tsx +10 -0
- package/src/main.tsx +10 -0
- package/src/utils/cn.orig.ts +6 -0
- package/src/utils/cn.ts +6 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +31 -0
- package/unify.mjs +537 -0
- package/vite.config.ts +18 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* engineBridge.ts — READ-ONLY same-origin bridge to the packaged VERITAS engine.
|
|
3
|
+
* ---------------------------------------------------------------------------
|
|
4
|
+
* The engine runs inside a `<iframe srcDoc={...}>`. A srcdoc iframe inherits the
|
|
5
|
+
* PARENT origin, so the durable workspace shell may legitimately read the
|
|
6
|
+
* engine's storage and DOM. This module NEVER mutates the engine — it only
|
|
7
|
+
* observes, so it cannot alter the initial-story pipeline or its prompt set.
|
|
8
|
+
*
|
|
9
|
+
* Storage keys are the engine's own persistence keys, verified against the
|
|
10
|
+
* packaged bundle (node_modules/dist/index.html):
|
|
11
|
+
* localStorage: veritas.keys.v3, veritas.model.v3, veritas.settings.v2,
|
|
12
|
+
* veritas.persona.seed.v1, veritas.custom.templates.v1
|
|
13
|
+
* sessionStorage: veritas.messages.v1, veritas.lastrun.v1
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export type EngineKeys = { gemini?: string; jina?: string; serpapi?: string; [k: string]: string | undefined };
|
|
17
|
+
|
|
18
|
+
const LS_KEYS = "veritas.keys.v3";
|
|
19
|
+
const LS_MODEL = "veritas.model.v3";
|
|
20
|
+
const LS_SETTINGS = "veritas.settings.v2";
|
|
21
|
+
const LS_PERSONA_SEED = "veritas.persona.seed.v1";
|
|
22
|
+
const LS_TEMPLATES = "veritas.custom.templates.v1";
|
|
23
|
+
const SS_MESSAGES = "veritas.messages.v1";
|
|
24
|
+
const SS_LASTRUN = "veritas.lastrun.v1";
|
|
25
|
+
|
|
26
|
+
type Frame = HTMLIFrameElement | null;
|
|
27
|
+
|
|
28
|
+
function fwin(iframe: Frame): Window | null {
|
|
29
|
+
try { return iframe?.contentWindow ?? null; } catch { return null; }
|
|
30
|
+
}
|
|
31
|
+
function fdoc(iframe: Frame): Document | null {
|
|
32
|
+
try { return iframe?.contentDocument ?? null; } catch { return null; }
|
|
33
|
+
}
|
|
34
|
+
function readLocal(iframe: Frame, key: string): string | null {
|
|
35
|
+
try { const v = window.localStorage.getItem(key); if (v != null) return v; } catch { /* noop */ }
|
|
36
|
+
try { return fwin(iframe)?.localStorage.getItem(key) ?? null; } catch { return null; }
|
|
37
|
+
}
|
|
38
|
+
function readSession(iframe: Frame, key: string): string | null {
|
|
39
|
+
try { const v = fwin(iframe)?.sessionStorage.getItem(key); if (v != null) return v; } catch { /* noop */ }
|
|
40
|
+
try { return window.sessionStorage.getItem(key); } catch { return null; }
|
|
41
|
+
}
|
|
42
|
+
function parse<T>(raw: string | null, fallback: T): T {
|
|
43
|
+
if (!raw) return fallback;
|
|
44
|
+
try { return JSON.parse(raw) as T; } catch { return fallback; }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isEngineReachable(iframe: Frame): boolean {
|
|
48
|
+
return !!fdoc(iframe)?.body;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getKeys(iframe: Frame): EngineKeys {
|
|
52
|
+
return parse<EngineKeys>(readLocal(iframe, LS_KEYS), {});
|
|
53
|
+
}
|
|
54
|
+
export function getGeminiKey(iframe: Frame): string {
|
|
55
|
+
return (getKeys(iframe).gemini || "").trim();
|
|
56
|
+
}
|
|
57
|
+
export function getActiveModel(iframe: Frame): string {
|
|
58
|
+
return parse<string>(readLocal(iframe, LS_MODEL), "");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type EngineSettings = {
|
|
62
|
+
templateEnabled?: boolean;
|
|
63
|
+
activeTemplateId?: string;
|
|
64
|
+
styleMode?: string;
|
|
65
|
+
deepResearch?: boolean;
|
|
66
|
+
searchDepth?: number;
|
|
67
|
+
nDeepEnabled?: boolean;
|
|
68
|
+
nDeepMaxPasses?: number;
|
|
69
|
+
clusterSearch?: boolean;
|
|
70
|
+
clusterSize?: number;
|
|
71
|
+
forceSloop?: boolean;
|
|
72
|
+
sloopPages?: number;
|
|
73
|
+
[k: string]: unknown;
|
|
74
|
+
};
|
|
75
|
+
export function getSettings(iframe: Frame): EngineSettings {
|
|
76
|
+
return parse<EngineSettings>(readLocal(iframe, LS_SETTINGS), {});
|
|
77
|
+
}
|
|
78
|
+
export function getPersonaSeed(iframe: Frame): number | null {
|
|
79
|
+
const v = parse<number | null>(readLocal(iframe, LS_PERSONA_SEED), null);
|
|
80
|
+
return typeof v === "number" ? v : null;
|
|
81
|
+
}
|
|
82
|
+
export function getCustomTemplateCount(iframe: Frame): number {
|
|
83
|
+
const t = parse<unknown[]>(readLocal(iframe, LS_TEMPLATES), []);
|
|
84
|
+
return Array.isArray(t) ? t.length : 0;
|
|
85
|
+
}
|
|
86
|
+
export function getLastRun(iframe: Frame): Record<string, unknown> | null {
|
|
87
|
+
return parse<Record<string, unknown> | null>(readSession(iframe, SS_LASTRUN), null);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type RawMsg = { role?: string; content?: string; text?: string; [k: string]: unknown };
|
|
91
|
+
export function getMessages(iframe: Frame): RawMsg[] {
|
|
92
|
+
const m = parse<RawMsg[]>(readSession(iframe, SS_MESSAGES), []);
|
|
93
|
+
return Array.isArray(m) ? m : [];
|
|
94
|
+
}
|
|
95
|
+
function msgText(m: RawMsg): string {
|
|
96
|
+
if (typeof m.content === "string") return m.content;
|
|
97
|
+
if (typeof m.text === "string") return m.text;
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
function isAssistant(m: RawMsg): boolean {
|
|
101
|
+
const r = (m.role || "").toLowerCase();
|
|
102
|
+
if (r === "user" || r === "system") return false;
|
|
103
|
+
return !!msgText(m);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type ManuscriptSource = "engine-messages" | "engine-dom" | "none";
|
|
107
|
+
export type ManuscriptResult = { text: string; source: ManuscriptSource; chapters: number; chars: number };
|
|
108
|
+
|
|
109
|
+
/** Assemble the FULL manuscript (whole book) — all assistant turns concatenated. */
|
|
110
|
+
export function assembleManuscript(iframe: Frame): ManuscriptResult {
|
|
111
|
+
const assistant = getMessages(iframe).filter(isAssistant).map(msgText).map((s) => s.trim()).filter(Boolean);
|
|
112
|
+
if (assistant.length) {
|
|
113
|
+
const text = assistant.join("\n\n\u2014\u2014\u2014 CHAPTER BREAK \u2014\u2014\u2014\n\n");
|
|
114
|
+
return { text, source: "engine-messages", chapters: assistant.length, chars: text.length };
|
|
115
|
+
}
|
|
116
|
+
const d = fdoc(iframe);
|
|
117
|
+
const txt = (d?.body?.innerText || "").trim();
|
|
118
|
+
if (txt.length > 200) return { text: txt, source: "engine-dom", chapters: 0, chars: txt.length };
|
|
119
|
+
return { text: "", source: "none", chapters: 0, chars: 0 };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Best-effort adopt-back: replace the LAST assistant message in the engine's
|
|
124
|
+
* persisted `veritas.messages.v1` (iframe sessionStorage first, window fallback).
|
|
125
|
+
* HONESTY: the live engine UI is driven by React state, not storage polling,
|
|
126
|
+
* so this write is visible after the engine re-reads storage (reload) or not at
|
|
127
|
+
* all in the current session. The caller MUST surface `detail` verbatim and
|
|
128
|
+
* offer clipboard copy as the guaranteed path. Returns ok=false with the real
|
|
129
|
+
* reason when the write cannot be performed.
|
|
130
|
+
*/
|
|
131
|
+
export function reloadEngineFrame(iframe: Frame): boolean {
|
|
132
|
+
try {
|
|
133
|
+
if (iframe?.contentWindow) {
|
|
134
|
+
iframe.contentWindow.location.reload();
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
} catch {}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function attemptAdoptToEngine(iframe: Frame, text: string): { ok: boolean; detail: string } {
|
|
142
|
+
if (!text.trim()) return { ok: false, detail: "adopt refused: empty text" };
|
|
143
|
+
try {
|
|
144
|
+
const rawF = (() => {
|
|
145
|
+
try {
|
|
146
|
+
return iframe?.contentWindow?.sessionStorage.getItem(SS_MESSAGES);
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
})();
|
|
151
|
+
const rawW = (() => {
|
|
152
|
+
try {
|
|
153
|
+
return window.sessionStorage.getItem(SS_MESSAGES);
|
|
154
|
+
} catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
const raw = rawF ?? rawW;
|
|
159
|
+
let msgs: RawMsg[] = [];
|
|
160
|
+
if (raw) {
|
|
161
|
+
try {
|
|
162
|
+
const parsed = JSON.parse(raw) as RawMsg[];
|
|
163
|
+
if (Array.isArray(parsed)) msgs = parsed;
|
|
164
|
+
} catch {
|
|
165
|
+
return { ok: false, detail: "engine message store is not valid JSON" };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Seed a first assistant message when the store is empty so chapter 1 of a
|
|
169
|
+
// fresh novel still lands in the main chat pipeline (no theatre: empty store
|
|
170
|
+
// used to silently fail adopt until the user had run something else first).
|
|
171
|
+
let idx = -1;
|
|
172
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
173
|
+
const r = (msgs[i].role || "").toLowerCase();
|
|
174
|
+
if (r !== "user" && r !== "system" && msgText(msgs[i])) {
|
|
175
|
+
idx = i;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const next = msgs.slice();
|
|
180
|
+
if (idx < 0) {
|
|
181
|
+
next.push({
|
|
182
|
+
id: `a-saga-${Date.now()}`,
|
|
183
|
+
role: "assistant",
|
|
184
|
+
content: text,
|
|
185
|
+
ts: Date.now(),
|
|
186
|
+
usedMultiPass: true,
|
|
187
|
+
} as RawMsg);
|
|
188
|
+
idx = next.length - 1;
|
|
189
|
+
} else {
|
|
190
|
+
const target = { ...next[idx] };
|
|
191
|
+
if (typeof target.content === "string") target.content = text;
|
|
192
|
+
else if (typeof target.text === "string") target.text = text;
|
|
193
|
+
else target.content = text;
|
|
194
|
+
next[idx] = target;
|
|
195
|
+
}
|
|
196
|
+
const serial = JSON.stringify(next);
|
|
197
|
+
let wrote = false;
|
|
198
|
+
try {
|
|
199
|
+
iframe?.contentWindow?.sessionStorage.setItem(SS_MESSAGES, serial);
|
|
200
|
+
wrote = true;
|
|
201
|
+
} catch {
|
|
202
|
+
/* fall through to window */
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
window.sessionStorage.setItem(SS_MESSAGES, serial);
|
|
206
|
+
wrote = true;
|
|
207
|
+
} catch {
|
|
208
|
+
/* ignore */
|
|
209
|
+
}
|
|
210
|
+
if (!wrote) return { ok: false, detail: "storage write blocked by the browser" };
|
|
211
|
+
return {
|
|
212
|
+
ok: true,
|
|
213
|
+
detail: `wrote assistant message #${idx + 1}/${next.length} to engine store (${text.length} chars); live UI updates on engine reload`,
|
|
214
|
+
};
|
|
215
|
+
} catch (e) {
|
|
216
|
+
return { ok: false, detail: e instanceof Error ? e.message : String(e) };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export type RuntimeSignal = { label: string; count: number };
|
|
221
|
+
const RUNTIME_KEYWORDS: Array<{ label: string; re: RegExp }> = [
|
|
222
|
+
{ label: "SAGE / Saga-OS", re: /\bsaga?[-\s]?os\b|\bsage\b/gi },
|
|
223
|
+
{ label: "Empty-Space", re: /empty[-\s]?space/gi },
|
|
224
|
+
{ label: "Reality grounding", re: /grounding|reality[-\s]?ground/gi },
|
|
225
|
+
{ label: "Judge / rubric", re: /\bjudge\b|rubric/gi },
|
|
226
|
+
{ label: "Innovation genome", re: /innovation|genome/gi },
|
|
227
|
+
{ label: "Blueprint", re: /blueprint/gi },
|
|
228
|
+
{ label: "Persona / archetype", re: /persona|archetype/gi },
|
|
229
|
+
{ label: "Adversarial", re: /adversar/gi },
|
|
230
|
+
{ label: "Retrieval / scraper", re: /scrap|retriev|grounded search/gi },
|
|
231
|
+
{ label: "Synthesis", re: /synthes/gi },
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
export type RuntimeSnapshot = { reachable: boolean; chapters: number; phase: string; signals: RuntimeSignal[]; traceChars: number };
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* DOM keyword census — a PROXY signal, not source-level instrumentation.
|
|
238
|
+
* Counts occurrences of subsystem terms visible in the engine DOM. This is an
|
|
239
|
+
* honest lower/loose bound on activation, explicitly labelled as a proxy.
|
|
240
|
+
*/
|
|
241
|
+
export function observeRuntime(iframe: Frame): RuntimeSnapshot {
|
|
242
|
+
const d = fdoc(iframe);
|
|
243
|
+
const text = d?.body?.innerText || "";
|
|
244
|
+
const signals = RUNTIME_KEYWORDS.map((k) => {
|
|
245
|
+
k.re.lastIndex = 0;
|
|
246
|
+
const matches = text.match(k.re);
|
|
247
|
+
return { label: k.label, count: matches ? matches.length : 0 };
|
|
248
|
+
});
|
|
249
|
+
const lastRun = getLastRun(iframe) as { phase?: string } | null;
|
|
250
|
+
const { chapters } = assembleManuscript(iframe);
|
|
251
|
+
return {
|
|
252
|
+
reachable: !!d?.body,
|
|
253
|
+
chapters,
|
|
254
|
+
phase: (lastRun && typeof lastRun.phase === "string" && lastRun.phase) || "(not exposed by engine build)",
|
|
255
|
+
signals,
|
|
256
|
+
traceChars: text.length,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gemini.ts — REAL Google Generative Language API client (browser fetch).
|
|
3
|
+
* ---------------------------------------------------------------------------
|
|
4
|
+
* Every call is an independent HTTP request with NO conversation history, so
|
|
5
|
+
* each grading / revision call runs in a genuinely fresh context window.
|
|
6
|
+
* CORS: generativelanguage.googleapis.com accepts browser calls with ?key=.
|
|
7
|
+
*
|
|
8
|
+
* HONESTY: if a call does not occur or fails, the result carries ok=false and
|
|
9
|
+
* the real error string. Nothing is fabricated.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Real, currently-served Gemini models (editable by the user in the UI).
|
|
13
|
+
export const DEFAULT_MODEL_ROTATION: string[] = [
|
|
14
|
+
"gemini-3.7-flash",
|
|
15
|
+
"gemini-3.6-flash",
|
|
16
|
+
"gemini-3.5-flash",
|
|
17
|
+
"gemini-3.5-flash-lite",
|
|
18
|
+
"gemini-3.1-flash-lite",
|
|
19
|
+
"gemini-3-flash-preview",
|
|
20
|
+
"gemini-2.5-pro",
|
|
21
|
+
"gemini-2.5-flash",
|
|
22
|
+
"gemini-2.5-flash-lite",
|
|
23
|
+
"gemma-4-31b-it",
|
|
24
|
+
"gemma-4-26b-it",
|
|
25
|
+
"gemma-3-27b-it",
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export function pickModel(pool: string[], exclude?: string): string {
|
|
29
|
+
const arr = pool.length ? pool : DEFAULT_MODEL_ROTATION;
|
|
30
|
+
const candidates = exclude ? arr.filter((m) => m !== exclude) : arr;
|
|
31
|
+
const src = candidates.length ? candidates : arr;
|
|
32
|
+
return src[Math.floor(Math.random() * src.length)];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type GeminiResult = { ok: boolean; text: string; model: string; error?: string; ms: number };
|
|
36
|
+
|
|
37
|
+
export type CallOpts = {
|
|
38
|
+
apiKey: string;
|
|
39
|
+
model: string;
|
|
40
|
+
prompt: string;
|
|
41
|
+
system?: string;
|
|
42
|
+
temperature?: number;
|
|
43
|
+
maxOutputTokens?: number;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export async function callGemini(opts: CallOpts): Promise<GeminiResult> {
|
|
48
|
+
const started = Date.now();
|
|
49
|
+
const { apiKey, model, prompt, system, temperature = 0.7, maxOutputTokens = 8192, signal } = opts;
|
|
50
|
+
if (!apiKey) return { ok: false, text: "", model, error: "Missing Gemini API key (set it in the engine's Chat → Keys panel).", ms: 0 };
|
|
51
|
+
const url =
|
|
52
|
+
"https://generativelanguage.googleapis.com/v1beta/models/" +
|
|
53
|
+
encodeURIComponent(model) +
|
|
54
|
+
":generateContent?key=" +
|
|
55
|
+
encodeURIComponent(apiKey);
|
|
56
|
+
const body: Record<string, unknown> = {
|
|
57
|
+
contents: [{ role: "user", parts: [{ text: prompt }] }],
|
|
58
|
+
generationConfig: { temperature, maxOutputTokens },
|
|
59
|
+
};
|
|
60
|
+
if (system) body.systemInstruction = { parts: [{ text: system }] };
|
|
61
|
+
try {
|
|
62
|
+
const res = await fetch(url, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: { "Content-Type": "application/json" },
|
|
65
|
+
body: JSON.stringify(body),
|
|
66
|
+
signal,
|
|
67
|
+
});
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
const t = await res.text().catch(() => "");
|
|
70
|
+
return { ok: false, text: "", model, error: "HTTP " + res.status + ": " + t.slice(0, 400), ms: Date.now() - started };
|
|
71
|
+
}
|
|
72
|
+
const data = await res.json();
|
|
73
|
+
const cand = data?.candidates?.[0];
|
|
74
|
+
const parts = cand?.content?.parts;
|
|
75
|
+
const text = Array.isArray(parts) ? parts.map((p: { text?: string }) => p?.text || "").join("") : "";
|
|
76
|
+
if (!text) {
|
|
77
|
+
const reason = cand?.finishReason || data?.promptFeedback?.blockReason || "empty";
|
|
78
|
+
return { ok: false, text: "", model, error: "No text returned (finishReason: " + reason + ")", ms: Date.now() - started };
|
|
79
|
+
}
|
|
80
|
+
return { ok: true, text, model, ms: Date.now() - started };
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return { ok: false, text: "", model, error: e instanceof Error ? e.message : String(e), ms: Date.now() - started };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Random model per call + one resilient fallback to a DIFFERENT random model on
|
|
88
|
+
* failure. Preserves "random model each call" while surviving a rate-limited or
|
|
89
|
+
* unavailable model. Returns which model actually produced the output.
|
|
90
|
+
*/
|
|
91
|
+
export async function callGeminiResilient(
|
|
92
|
+
pool: string[],
|
|
93
|
+
exclude: string | undefined,
|
|
94
|
+
opts: Omit<CallOpts, "model">,
|
|
95
|
+
): Promise<GeminiResult> {
|
|
96
|
+
let excl = exclude;
|
|
97
|
+
let last: GeminiResult | null = null;
|
|
98
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
99
|
+
const model = pickModel(pool, excl);
|
|
100
|
+
const res = await callGemini({ ...opts, model });
|
|
101
|
+
if (res.ok) return res;
|
|
102
|
+
last = res;
|
|
103
|
+
excl = model;
|
|
104
|
+
}
|
|
105
|
+
return last ?? { ok: false, text: "", model: "", error: "No model available", ms: 0 };
|
|
106
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* grading.ts — Manuscript grading rubric, prompt builders, parser, and the
|
|
3
|
+
* recursive-revision prompt builder.
|
|
4
|
+
*
|
|
5
|
+
* The weighted rubric matches the user-specified headings/weights exactly.
|
|
6
|
+
* A supplementary set + a line-level rewrite-suggestion table are added.
|
|
7
|
+
* NOTE (honesty): the uploaded "additional criteria" image was NOT provided to
|
|
8
|
+
* this build, so the supplementary set + rewrite-table columns use a standard
|
|
9
|
+
* professional editorial format compatible with the described rubric. This is
|
|
10
|
+
* disclosed in the UI.
|
|
11
|
+
*/
|
|
12
|
+
import { clampNum, round2 } from "./util";
|
|
13
|
+
|
|
14
|
+
export type Criterion = { key: string; label: string; weight: number };
|
|
15
|
+
|
|
16
|
+
export const CRITERIA: Criterion[] = [
|
|
17
|
+
{ key: "theme", label: "Theme & Conceptual Ambition", weight: 0.15 },
|
|
18
|
+
{ key: "world", label: "Worldbuilding & Setting Depth", weight: 0.15 },
|
|
19
|
+
{ key: "prose", label: "Prose Style & Syntactic Craft", weight: 0.15 },
|
|
20
|
+
{ key: "voice", label: "Character Voice & Coherence", weight: 0.2 },
|
|
21
|
+
{ key: "plot", label: "Plot Architecture & Pacing", weight: 0.2 },
|
|
22
|
+
{ key: "clean", label: "Cleanliness & Narrative Logic", weight: 0.15 },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export const SUPPLEMENTARY_LABELS: string[] = [
|
|
26
|
+
"Dialogue Authenticity",
|
|
27
|
+
"Emotional Resonance",
|
|
28
|
+
"Originality & Trope Subversion",
|
|
29
|
+
"Sensory Immersion",
|
|
30
|
+
"Scene-Level Momentum",
|
|
31
|
+
"Line Economy & Redundancy",
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export type CriterionScore = {
|
|
35
|
+
key: string;
|
|
36
|
+
label: string;
|
|
37
|
+
weight: number;
|
|
38
|
+
score: number;
|
|
39
|
+
weighted: number;
|
|
40
|
+
grade: string;
|
|
41
|
+
justification: string;
|
|
42
|
+
};
|
|
43
|
+
export type Supp = { label: string; score: number; note: string };
|
|
44
|
+
export type Rewrite = { location: string; excerpt: string; diagnosis: string; suggestedRewrite: string; rationale: string };
|
|
45
|
+
|
|
46
|
+
export type GradeReport = {
|
|
47
|
+
criteria: CriterionScore[];
|
|
48
|
+
supplementary: Supp[];
|
|
49
|
+
rewrites: Rewrite[];
|
|
50
|
+
composite: number;
|
|
51
|
+
compositeGrade: string;
|
|
52
|
+
narrative: string;
|
|
53
|
+
weaknesses: string[];
|
|
54
|
+
model: string;
|
|
55
|
+
ms: number;
|
|
56
|
+
raw: string;
|
|
57
|
+
parseError?: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export function letterGrade(s: number): string {
|
|
61
|
+
if (s >= 93) return "A";
|
|
62
|
+
if (s >= 90) return "A-";
|
|
63
|
+
if (s >= 87) return "B+";
|
|
64
|
+
if (s >= 83) return "B";
|
|
65
|
+
if (s >= 80) return "B-";
|
|
66
|
+
if (s >= 77) return "C+";
|
|
67
|
+
if (s >= 73) return "C";
|
|
68
|
+
if (s >= 70) return "C-";
|
|
69
|
+
if (s >= 67) return "D+";
|
|
70
|
+
if (s >= 63) return "D";
|
|
71
|
+
if (s >= 60) return "D-";
|
|
72
|
+
return "F";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function buildGradingPrompt(manuscript: string): { system: string; prompt: string } {
|
|
76
|
+
const system =
|
|
77
|
+
"You are a ruthless, calibrated literary editor and manuscript grader. You evaluate the ENTIRE manuscript provided (the whole book, all chapters), never a summary. You never inflate scores. A truncated, contradictory, or reset plot MUST score in the F range for Plot Architecture & Pacing and for Cleanliness & Narrative Logic. You output STRICT JSON only — no markdown, no code fences, no prose outside the JSON object.";
|
|
78
|
+
|
|
79
|
+
const prompt = [
|
|
80
|
+
"Grade the manuscript with this WEIGHTED rubric. Score each criterion 1-100:",
|
|
81
|
+
CRITERIA.map((c) => "- " + c.label + " (" + Math.round(c.weight * 100) + "%) [key: " + c.key + "]").join("\n"),
|
|
82
|
+
"",
|
|
83
|
+
"Also score these SUPPLEMENTARY dimensions 1-100 (informational, unweighted):",
|
|
84
|
+
SUPPLEMENTARY_LABELS.map((l) => "- " + l).join("\n"),
|
|
85
|
+
"",
|
|
86
|
+
"Then produce 5-12 line-level REWRITE SUGGESTIONS. Each must quote a SHORT VERBATIM excerpt (<=25 words) taken from the manuscript, diagnose the specific problem, propose a concrete suggested rewrite that PRESERVES the author's voice and vibe, and give a one-line rationale.",
|
|
87
|
+
"",
|
|
88
|
+
"Return ONLY this JSON object (fill every field, keep the exact keys):",
|
|
89
|
+
"{",
|
|
90
|
+
' "criteria": [ {"key": "theme", "score": 0, "qualitativeGrade": "", "justification": ""} ],',
|
|
91
|
+
' "supplementary": [ {"label": "", "score": 0, "note": ""} ],',
|
|
92
|
+
' "rewriteSuggestions": [ {"location": "", "excerpt": "", "diagnosis": "", "suggestedRewrite": "", "rationale": ""} ],',
|
|
93
|
+
' "narrative": "2-4 sentence overall assessment",',
|
|
94
|
+
' "topWeaknesses": ["", "", ""]',
|
|
95
|
+
"}",
|
|
96
|
+
"There must be exactly one criteria entry per key: " + CRITERIA.map((c) => c.key).join(", ") + ".",
|
|
97
|
+
"",
|
|
98
|
+
"MANUSCRIPT:",
|
|
99
|
+
"<<<BEGIN_MANUSCRIPT>>>",
|
|
100
|
+
manuscript,
|
|
101
|
+
"<<<END_MANUSCRIPT>>>",
|
|
102
|
+
].join("\n");
|
|
103
|
+
|
|
104
|
+
return { system, prompt };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function extractJson(raw: string): Record<string, unknown> | null {
|
|
108
|
+
const t = raw.replace(/```json/gi, "").replace(/```/g, "").trim();
|
|
109
|
+
const a = t.indexOf("{");
|
|
110
|
+
const b = t.lastIndexOf("}");
|
|
111
|
+
if (a < 0 || b < 0 || b <= a) return null;
|
|
112
|
+
try {
|
|
113
|
+
return JSON.parse(t.slice(a, b + 1)) as Record<string, unknown>;
|
|
114
|
+
} catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function parseGrade(raw: string, model: string, ms: number): GradeReport {
|
|
120
|
+
const j = extractJson(raw);
|
|
121
|
+
if (!j) {
|
|
122
|
+
return {
|
|
123
|
+
criteria: CRITERIA.map((c) => ({ ...c, score: 0, weighted: 0, grade: "F", justification: "(unparsed)" })),
|
|
124
|
+
supplementary: [],
|
|
125
|
+
rewrites: [],
|
|
126
|
+
composite: 0,
|
|
127
|
+
compositeGrade: "F",
|
|
128
|
+
narrative: "Model output could not be parsed as JSON. The raw output is shown below for inspection.",
|
|
129
|
+
weaknesses: [],
|
|
130
|
+
model,
|
|
131
|
+
ms,
|
|
132
|
+
raw,
|
|
133
|
+
parseError: "JSON parse failed",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
type MC = { key?: string; label?: string; score?: unknown; justification?: string; note?: string };
|
|
138
|
+
const modelCrit: MC[] = Array.isArray(j.criteria) ? (j.criteria as MC[]) : [];
|
|
139
|
+
const firstWord = (s: string) => s.toLowerCase().split(/\s+/)[0];
|
|
140
|
+
|
|
141
|
+
const criteria: CriterionScore[] = CRITERIA.map((c) => {
|
|
142
|
+
const found =
|
|
143
|
+
modelCrit.find((m) => (m?.key || "").toLowerCase() === c.key) ||
|
|
144
|
+
modelCrit.find((m) => String(m?.label || "").toLowerCase().includes(firstWord(c.label)));
|
|
145
|
+
const score = clampNum(found?.score, 1, 100, 1);
|
|
146
|
+
return {
|
|
147
|
+
key: c.key,
|
|
148
|
+
label: c.label,
|
|
149
|
+
weight: c.weight,
|
|
150
|
+
score,
|
|
151
|
+
weighted: round2(score * c.weight),
|
|
152
|
+
grade: letterGrade(score),
|
|
153
|
+
justification: String(found?.justification || found?.note || ""),
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const composite = round2(criteria.reduce((sum, c) => sum + c.weighted, 0));
|
|
158
|
+
|
|
159
|
+
type MS = { label?: string; score?: unknown; note?: string };
|
|
160
|
+
const supplementary: Supp[] = Array.isArray(j.supplementary)
|
|
161
|
+
? (j.supplementary as MS[]).slice(0, 12).map((s) => ({
|
|
162
|
+
label: String(s?.label || ""),
|
|
163
|
+
score: clampNum(s?.score, 1, 100, 0),
|
|
164
|
+
note: String(s?.note || ""),
|
|
165
|
+
}))
|
|
166
|
+
: [];
|
|
167
|
+
|
|
168
|
+
type MR = { location?: string; excerpt?: string; diagnosis?: string; suggestedRewrite?: string; rationale?: string };
|
|
169
|
+
const rewrites: Rewrite[] = Array.isArray(j.rewriteSuggestions)
|
|
170
|
+
? (j.rewriteSuggestions as MR[]).slice(0, 12).map((r) => ({
|
|
171
|
+
location: String(r?.location || ""),
|
|
172
|
+
excerpt: String(r?.excerpt || ""),
|
|
173
|
+
diagnosis: String(r?.diagnosis || ""),
|
|
174
|
+
suggestedRewrite: String(r?.suggestedRewrite || ""),
|
|
175
|
+
rationale: String(r?.rationale || ""),
|
|
176
|
+
}))
|
|
177
|
+
: [];
|
|
178
|
+
|
|
179
|
+
const weaknesses: string[] = Array.isArray(j.topWeaknesses)
|
|
180
|
+
? (j.topWeaknesses as unknown[]).map((w) => String(w)).filter(Boolean).slice(0, 8)
|
|
181
|
+
: [];
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
criteria,
|
|
185
|
+
supplementary,
|
|
186
|
+
rewrites,
|
|
187
|
+
composite,
|
|
188
|
+
compositeGrade: letterGrade(composite),
|
|
189
|
+
narrative: String(j.narrative || ""),
|
|
190
|
+
weaknesses,
|
|
191
|
+
model,
|
|
192
|
+
ms,
|
|
193
|
+
raw,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Fixed-width ASCII report matching the requested table layout (copy/export). */
|
|
198
|
+
export function buildAsciiReport(r: GradeReport): string {
|
|
199
|
+
const bar = "=".repeat(98);
|
|
200
|
+
const dash = "-".repeat(98);
|
|
201
|
+
const head =
|
|
202
|
+
"EVALUATION CRITERIA (WEIGHT)".padEnd(42) +
|
|
203
|
+
"SCORE (1-100)".padEnd(16) +
|
|
204
|
+
"WEIGHTED".padEnd(16) +
|
|
205
|
+
"QUALITATIVE GRADE";
|
|
206
|
+
const rows = r.criteria.map((c) => {
|
|
207
|
+
const name = (c.label + " (" + Math.round(c.weight * 100) + "%)").padEnd(42);
|
|
208
|
+
const score = (c.score + " / 100").padEnd(16);
|
|
209
|
+
const wtd = c.weighted.toFixed(2).padEnd(16);
|
|
210
|
+
return name + score + wtd + c.grade;
|
|
211
|
+
});
|
|
212
|
+
const composite =
|
|
213
|
+
"WEIGHTED COMPOSITE SCORE (100%)".padEnd(42) +
|
|
214
|
+
(r.composite.toFixed(2) + " / 100").padEnd(32) +
|
|
215
|
+
"GRADE: " + r.compositeGrade;
|
|
216
|
+
return [bar, head, dash, ...rows, dash, composite, bar, "", "Model: " + r.model, "Assessment: " + r.narrative].join("\n");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function buildRevisionPrompt(
|
|
220
|
+
manuscript: string,
|
|
221
|
+
report: GradeReport,
|
|
222
|
+
target: number,
|
|
223
|
+
): { system: string; prompt: string } {
|
|
224
|
+
const system =
|
|
225
|
+
"You are a master literary reviser. Your job is to raise a manuscript's grade while changing as FEW words as possible. You preserve the original voice, vibe, tense, diction, and style. You keep every chapter heading and the same story, characters, and events. You make minimal, surgical edits — prioritising pacing, narrative logic, coherence, and cleanliness. You NEVER add meta-commentary, notes, JSON, or code fences.";
|
|
226
|
+
|
|
227
|
+
const lowest = report.criteria.slice().sort((a, b) => a.score - b.score).slice(0, 3);
|
|
228
|
+
const prompt = [
|
|
229
|
+
"Target composite score: " + target + "/100. Current composite: " + report.composite + "/100 (" + report.compositeGrade + ").",
|
|
230
|
+
"Apply the smallest possible set of edits (add / change / delete as few words as possible) to reach the target. Do NOT rewrite wholesale. Keep the same plot and voice.",
|
|
231
|
+
"",
|
|
232
|
+
"LOWEST-SCORING WEIGHTED CRITERIA (fix these first):",
|
|
233
|
+
lowest.map((c) => "- " + c.label + ": " + c.score + "/100 — " + c.justification).join("\n"),
|
|
234
|
+
"",
|
|
235
|
+
"TOP WEAKNESSES:",
|
|
236
|
+
(report.weaknesses.length ? report.weaknesses : ["(none provided)"]).map((w) => "- " + w).join("\n"),
|
|
237
|
+
"",
|
|
238
|
+
"LINE-LEVEL REWRITE SUGGESTIONS (apply where they help):",
|
|
239
|
+
(report.rewrites.length ? report.rewrites : []).map((r) => '- "' + r.excerpt + '" => ' + r.suggestedRewrite + " (" + r.diagnosis + ")").join("\n") || "- (none provided)",
|
|
240
|
+
"",
|
|
241
|
+
"Output ONLY the full revised manuscript text (all chapters, in order). No commentary.",
|
|
242
|
+
"",
|
|
243
|
+
"MANUSCRIPT:",
|
|
244
|
+
"<<<BEGIN_MANUSCRIPT>>>",
|
|
245
|
+
manuscript,
|
|
246
|
+
"<<<END_MANUSCRIPT>>>",
|
|
247
|
+
].join("\n");
|
|
248
|
+
|
|
249
|
+
return { system, prompt };
|
|
250
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Small shared helpers for the Engineering Console overlay.
|
|
2
|
+
// Pure, dependency-free, framework-agnostic.
|
|
3
|
+
|
|
4
|
+
export function cx(...parts: Array<string | false | null | undefined>): string {
|
|
5
|
+
return parts.filter(Boolean).join(" ");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function round2(n: number): number {
|
|
9
|
+
return Math.round(n * 100) / 100;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function clampNum(v: unknown, lo: number, hi: number, fallback: number): number {
|
|
13
|
+
const n = typeof v === "number" ? v : Number(v);
|
|
14
|
+
if (!Number.isFinite(n)) return fallback;
|
|
15
|
+
return Math.min(hi, Math.max(lo, n));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function download(name: string, text: string): void {
|
|
19
|
+
try {
|
|
20
|
+
const blob = new Blob([text], { type: "text/plain;charset=utf-8" });
|
|
21
|
+
const url = URL.createObjectURL(blob);
|
|
22
|
+
const a = document.createElement("a");
|
|
23
|
+
a.href = url;
|
|
24
|
+
a.download = name;
|
|
25
|
+
document.body.appendChild(a);
|
|
26
|
+
a.click();
|
|
27
|
+
document.body.removeChild(a);
|
|
28
|
+
URL.revokeObjectURL(url);
|
|
29
|
+
} catch {
|
|
30
|
+
/* ignore */
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function copyText(text: string): Promise<boolean> {
|
|
35
|
+
try {
|
|
36
|
+
await navigator.clipboard.writeText(text);
|
|
37
|
+
return true;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function nowStamp(): string {
|
|
44
|
+
return new Date().toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z");
|
|
45
|
+
}
|