feat(engine): merge cloudflare-migration — paradigm D engine, BYOK proxy, story persistence (#95)

Squash-merge the cloudflare-migration branch (7 commits by Kai ki) into
staging with conflict resolution, feature integration, and bug fixes.

Engine:
- Paradigm D: single-stream Writer replacing dual-phase Plan/Beats
- Delete Architect agent; story bible generated via Writer <plan> tag
- Modular prompt architecture (segments/registry/builder)
- StreamRouter for tagged stream splitting (<plan>/<story>/<choices>)

Infrastructure:
- Cloudflare Workers deployment (wrangler.jsonc, OpenNext adapter)
- D1 database schema + Drizzle ORM (scaffolded, not yet active)
- R2 storage helpers (scaffolded, not yet active)
- Story persistence API routes + client-side persistence

BYOK (Bring Your Own Key):
- /api/llm/user-proxy with SSRF-protected LLM proxy (+ requireUser auth)
- CORS-aware fetch in ai-client: auto-detect CORS failure, fallback to
  server proxy transparently via OpenAI SDK custom fetch
- BYO config support added to classify-freeform and vision routes
- SettingsModal CORS privacy notice (keys never logged/stored)

SSE streaming:
- engineClient.ts: fetchSSE helper for progressive scene events
- startSession/requestScene accept optional emit callback
- Fix SSE error event field name (error → message) in scene/start routes

i18n integration:
- Wire buildLanguageDirective into paradigm D's prompt builder
- Update corsNotice i18n keys (zh-CN/en/ja) with CORS proxy privacy text
- Preserve Session.language + LanguageSwitcher from i18n commit

Co-authored-by: Kai ki <155355644+zbf1009@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Zonghao Yuan
2026-06-18 18:05:38 +08:00
committed by GitHub
parent 05bd7e229c
commit 0e4c2ebef4
78 changed files with 7396 additions and 919 deletions
+10 -10
View File
@@ -107,6 +107,16 @@ export function DialogueHistoryModal({
</span>
)}
</div>
{item.narration && (
<p
className={`font-serif leading-[1.7] ${
item.body ? "mb-1" : ""
} ${portrait ? "text-[14px]" : "text-[12px]"}`}
style={{ color: "rgba(228,218,196,0.85)" }}
>
{item.narration}
</p>
)}
{item.body && (
<p
className={`font-serif leading-[1.75] ${
@@ -117,16 +127,6 @@ export function DialogueHistoryModal({
{item.body}
</p>
)}
{item.narration && (
<p
className={`mt-1 font-serif italic leading-[1.65] ${
portrait ? "text-[13px]" : "text-[12px]"
}`}
style={{ color: "rgba(200,185,155,0.72)" }}
>
{item.narration}
</p>
)}
{item.selectedChoice && (
<p className="mt-2 inline-flex max-w-full items-start gap-2 rounded-[5px] border border-[rgba(180,140,80,0.35)] bg-[rgba(180,140,60,0.10)] px-2.5 py-1.5 font-serif text-[12px] leading-snug text-cream-50/85">
<span className="shrink-0 text-[rgba(195,155,75,0.9)]">