From 765dbdfbd43426905465ec711b81511df34a6f82 Mon Sep 17 00:00:00 2001
From: Yaojia Wang
Date: Sat, 20 Jun 2026 10:39:58 +0200
Subject: [PATCH] =?UTF-8?q?feat:=20M4=20=E6=96=87=E9=A3=8E=20+=20M5=20?=
=?UTF-8?q?=E7=94=9F=E6=88=90/=E5=A4=9Aprovider/Skill=20+=20Kimi=20Code=20?=
=?UTF-8?q?=E8=AE=A2=E9=98=85=E6=8E=A5=E5=85=A5=20+=20=E6=9C=AC=E5=9C=B0?=
=?UTF-8?q?=E8=81=94=E8=B0=83=E4=BF=AE=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
M4(文风): style-auditor 双轨(提取指纹/漂移第四审)+ jobs 长任务框架(zombie reaper) + 回炉 refine + GET /style read-back。
M5(生成+扩展): worldbuilder/character-gen(入库 continuity 409 gate + partition_writes 白名单 + schema→JSONB 形变);
网关多 provider 回退链/熔断/能力降级(Anthropic/Gemini 适配器);Skill registry + 表权限沙箱 + 规则;
前端 角色生成器/世界观/Codex/规则页/技能库/⌘K 命令面板。
K1(Kimi Code 订阅接入): OAuth device-flow(kimi-code)+ 静态 Console key(kimi-code-key)两路径;
coding 端点 KimiCLI 伪造头(实测 UA allow-list 门禁,缺则 403)+ JSON 模式结构化(thinking ⊥ tool_choice)。
本地联调修复: CORS 中间件;assemble 注入 premise+「写第N章」指令(修空 prompt 400);
GET /outline·/draft read-back + 大纲/工作台/审稿页重载;写页 client/server 常量边界 + notFound 健壮化;
字数 toLocaleString locale 水合;审稿页终稿从已存草稿 seed(修 accept 422)。
门禁: backend ruff/mypy(157)/alembic 无漂移/pytest 451 · frontend lint/tsc/vitest/build。
Co-Authored-By: Claude Opus 4.8 (1M context)
---
.gitignore | 1 +
ARCHITECTURE.md | 1 +
CLAUDE.md | 4 +-
PRODUCT_SPEC.md | 1 +
PROGRESS.md | 102 +-
apps/api/tests/fakes_projects.py | 136 +-
apps/api/tests/fakes_providers.py | 30 +-
.../api/tests/test_gateway_fallback_wiring.py | 100 ++
apps/api/tests/test_generation.py | 506 ++++++++
apps/api/tests/test_job_runner.py | 153 +++
apps/api/tests/test_kimi_code_key_wiring.py | 77 ++
apps/api/tests/test_kimi_code_wiring.py | 125 ++
apps/api/tests/test_kimi_oauth_endpoints.py | 213 ++++
apps/api/tests/test_kimi_oauth_service.py | 184 +++
apps/api/tests/test_outline.py | 79 ++
apps/api/tests/test_projects.py | 37 +
apps/api/tests/test_rules.py | 87 ++
apps/api/tests/test_style.py | 394 ++++++
apps/api/ww_api/main.py | 29 +
apps/api/ww_api/routers/generation.py | 427 +++++++
apps/api/ww_api/routers/kimi_oauth.py | 190 +++
apps/api/ww_api/routers/outline.py | 43 +-
apps/api/ww_api/routers/projects.py | 36 +
apps/api/ww_api/routers/rules.py | 53 +
apps/api/ww_api/routers/style.py | 225 ++++
apps/api/ww_api/schemas/generation.py | 163 +++
apps/api/ww_api/schemas/kimi_oauth.py | 39 +
apps/api/ww_api/schemas/projects.py | 16 +
apps/api/ww_api/schemas/rules.py | 28 +
apps/api/ww_api/schemas/style.py | 47 +
apps/api/ww_api/services/credentials.py | 95 +-
apps/api/ww_api/services/job_runner.py | 97 ++
apps/api/ww_api/services/kimi_oauth.py | 240 ++++
apps/api/ww_api/services/project_deps.py | 279 +++-
apps/api/ww_api/services/provider_deps.py | 11 +
apps/web/app/layout.tsx | 6 +-
apps/web/app/projects/[id]/codex/page.tsx | 42 +
apps/web/app/projects/[id]/outline/page.tsx | 12 +-
apps/web/app/projects/[id]/review/page.tsx | 6 +-
apps/web/app/projects/[id]/rules/page.tsx | 20 +
apps/web/app/projects/[id]/skills/page.tsx | 20 +
apps/web/app/projects/[id]/style/page.tsx | 27 +
apps/web/app/projects/[id]/write/page.tsx | 25 +-
apps/web/app/settings/providers/page.tsx | 20 +-
apps/web/components/AppShell.tsx | 4 +-
apps/web/components/LeftNav.tsx | 36 +-
apps/web/components/codex/CodexPage.tsx | 167 +++
.../web/components/command/CommandPalette.tsx | 152 +++
.../command/CommandPaletteMount.tsx | 12 +
.../generation/CharacterCardItem.tsx | 97 ++
.../generation/CharacterGenerator.tsx | 171 +++
.../generation/ConflictAdjudication.tsx | 75 ++
.../components/generation/WorldGenerator.tsx | 78 ++
apps/web/components/review/ReviewReport.tsx | 56 +-
apps/web/components/rules/RulesPage.tsx | 107 ++
.../web/components/settings/KimiCodeOauth.tsx | 127 ++
.../components/settings/ProvidersSettings.tsx | 134 +-
apps/web/components/skills/SkillsPage.tsx | 77 ++
apps/web/components/style/FingerprintView.tsx | 65 +
apps/web/components/style/RefineView.tsx | 100 ++
apps/web/components/style/StylePage.tsx | 48 +
apps/web/components/style/StylePanel.tsx | 87 ++
apps/web/components/style/StyleUpload.tsx | 110 ++
apps/web/components/workbench/Workbench.tsx | 15 +-
apps/web/lib/api/schema.d.ts | 1124 ++++++++++++++++-
apps/web/lib/api/server.test.ts | 68 +
apps/web/lib/api/server.ts | 89 ++
apps/web/lib/api/types.ts | 47 +
apps/web/lib/command/palette.test.ts | 75 ++
apps/web/lib/command/palette.ts | 154 +++
apps/web/lib/generation/cards.test.ts | 199 +++
apps/web/lib/generation/cards.ts | Bin 0 -> 5668 bytes
apps/web/lib/generation/useCharacterGen.ts | 150 +++
apps/web/lib/generation/useWorldGen.ts | 58 +
apps/web/lib/jobs/job.test.ts | 150 +++
apps/web/lib/jobs/job.ts | 111 ++
apps/web/lib/jobs/useJobPoll.ts | 88 ++
apps/web/lib/review/sse.ts | 40 +
apps/web/lib/review/style-sse.test.ts | 70 +
apps/web/lib/review/useReviewStream.ts | 3 +
apps/web/lib/rules/rules.test.ts | 54 +
apps/web/lib/rules/rules.ts | 58 +
apps/web/lib/rules/useRules.ts | 62 +
apps/web/lib/settings/kimiOauth.test.ts | 191 +++
apps/web/lib/settings/kimiOauth.ts | 101 ++
apps/web/lib/settings/providers.test.ts | 107 ++
apps/web/lib/settings/providers.ts | 103 +-
apps/web/lib/settings/useKimiOauth.ts | 139 ++
apps/web/lib/skills/skills.test.ts | 38 +
apps/web/lib/skills/skills.ts | 44 +
apps/web/lib/style/style.test.ts | 127 ++
apps/web/lib/style/style.ts | 129 ++
apps/web/lib/style/useRefine.ts | 91 ++
apps/web/lib/style/useStyleLearn.ts | 123 ++
apps/web/lib/workbench/chapter.ts | 5 +
apps/web/tsconfig.tsbuildinfo | 2 +-
memory/contracts.md | 181 ++-
memory/decisions.md | 64 +
memory/gotchas.md | 34 +
.../agents/tests/test_generation_specs.py | 183 +++
packages/agents/tests/test_style_specs.py | 191 +++
packages/agents/ww_agents/__init__.py | 28 +
packages/agents/ww_agents/schemas.py | 151 +++
packages/agents/ww_agents/specs.py | 203 +++
packages/config/ww_config/settings.py | 14 +
packages/core/tests/test_generation_node.py | 375 ++++++
.../core/tests/test_generation_write_repos.py | 31 +
packages/core/tests/test_job_repo.py | 243 ++++
packages/core/tests/test_memory.py | 35 +
packages/core/tests/test_rules_repo.py | 46 +
.../core/tests/test_style_extract_node.py | 137 ++
packages/core/tests/test_style_review.py | 249 ++++
packages/core/ww_core/domain/__init__.py | 36 +
.../core/ww_core/domain/character_repo.py | 121 ++
packages/core/ww_core/domain/job_repo.py | 169 +++
packages/core/ww_core/domain/repositories.py | 24 +-
packages/core/ww_core/domain/rule_repo.py | 49 +
packages/core/ww_core/domain/style_repo.py | 108 ++
.../core/ww_core/domain/world_entity_repo.py | 75 ++
packages/core/ww_core/memory/assemble.py | 28 +-
.../core/ww_core/memory/sql_repositories.py | 44 +-
.../core/ww_core/orchestrator/__init__.py | 25 +
packages/core/ww_core/orchestrator/collect.py | 24 +-
.../ww_core/orchestrator/generation_node.py | 262 ++++
packages/core/ww_core/orchestrator/graph.py | 10 +-
packages/core/ww_core/orchestrator/sse.py | 31 +-
.../orchestrator/style_extract_node.py | 88 ++
...bd4d_provider_credentials_oauth_columns.py | 47 +
packages/db/ww_db/models.py | 4 +-
packages/llm_gateway/pyproject.toml | 3 +
.../llm_gateway/tests/fakes_resilience.py | 119 ++
.../tests/test_build_adapter_factory.py | 59 +
.../tests/test_capability_degradation.py | 109 ++
.../llm_gateway/tests/test_fallback_chain.py | 174 +++
.../tests/test_kimi_code_adapter.py | 139 ++
.../tests/test_kimi_code_factory.py | 62 +
.../tests/test_kimi_code_key_factory.py | 69 +
.../tests/test_multi_provider_accounting.py | 51 +
.../llm_gateway/tests/test_new_adapters.py | 198 +++
.../llm_gateway/ww_llm_gateway/__init__.py | 51 +-
.../ww_llm_gateway/adapters/anthropic.py | 184 +++
.../ww_llm_gateway/adapters/gemini.py | 127 ++
.../ww_llm_gateway/adapters/kimi_code.py | 190 +++
.../ww_llm_gateway/adapters/kimi_code_key.py | 64 +
.../ww_llm_gateway/adapters/openai_compat.py | 64 +-
packages/llm_gateway/ww_llm_gateway/errors.py | 22 +
.../llm_gateway/ww_llm_gateway/factory.py | 77 ++
.../llm_gateway/ww_llm_gateway/gateway.py | 267 +++-
.../llm_gateway/ww_llm_gateway/routing.py | 48 +-
packages/llm_gateway/ww_llm_gateway/types.py | 8 +-
packages/skills/pyproject.toml | 25 +
.../skills/tests/test_skill_permissions.py | 130 ++
packages/skills/tests/test_skill_registry.py | 103 ++
packages/skills/ww_skills/__init__.py | 35 +
.../skills/ww_skills/skill_permissions.py | 71 ++
packages/skills/ww_skills/skill_registry.py | 131 ++
pyproject.toml | 2 +
tests/test_k1_kimi_oauth_e2e.py | 510 ++++++++
tests/test_m4_e2e.py | 569 +++++++++
tests/test_m5_e2e.py | 514 ++++++++
uv.lock | 109 ++
161 files changed, 17330 insertions(+), 208 deletions(-)
create mode 100644 apps/api/tests/test_gateway_fallback_wiring.py
create mode 100644 apps/api/tests/test_generation.py
create mode 100644 apps/api/tests/test_job_runner.py
create mode 100644 apps/api/tests/test_kimi_code_key_wiring.py
create mode 100644 apps/api/tests/test_kimi_code_wiring.py
create mode 100644 apps/api/tests/test_kimi_oauth_endpoints.py
create mode 100644 apps/api/tests/test_kimi_oauth_service.py
create mode 100644 apps/api/tests/test_rules.py
create mode 100644 apps/api/tests/test_style.py
create mode 100644 apps/api/ww_api/routers/generation.py
create mode 100644 apps/api/ww_api/routers/kimi_oauth.py
create mode 100644 apps/api/ww_api/routers/rules.py
create mode 100644 apps/api/ww_api/routers/style.py
create mode 100644 apps/api/ww_api/schemas/generation.py
create mode 100644 apps/api/ww_api/schemas/kimi_oauth.py
create mode 100644 apps/api/ww_api/schemas/rules.py
create mode 100644 apps/api/ww_api/schemas/style.py
create mode 100644 apps/api/ww_api/services/job_runner.py
create mode 100644 apps/api/ww_api/services/kimi_oauth.py
create mode 100644 apps/web/app/projects/[id]/codex/page.tsx
create mode 100644 apps/web/app/projects/[id]/rules/page.tsx
create mode 100644 apps/web/app/projects/[id]/skills/page.tsx
create mode 100644 apps/web/app/projects/[id]/style/page.tsx
create mode 100644 apps/web/components/codex/CodexPage.tsx
create mode 100644 apps/web/components/command/CommandPalette.tsx
create mode 100644 apps/web/components/command/CommandPaletteMount.tsx
create mode 100644 apps/web/components/generation/CharacterCardItem.tsx
create mode 100644 apps/web/components/generation/CharacterGenerator.tsx
create mode 100644 apps/web/components/generation/ConflictAdjudication.tsx
create mode 100644 apps/web/components/generation/WorldGenerator.tsx
create mode 100644 apps/web/components/rules/RulesPage.tsx
create mode 100644 apps/web/components/settings/KimiCodeOauth.tsx
create mode 100644 apps/web/components/skills/SkillsPage.tsx
create mode 100644 apps/web/components/style/FingerprintView.tsx
create mode 100644 apps/web/components/style/RefineView.tsx
create mode 100644 apps/web/components/style/StylePage.tsx
create mode 100644 apps/web/components/style/StylePanel.tsx
create mode 100644 apps/web/components/style/StyleUpload.tsx
create mode 100644 apps/web/lib/api/server.test.ts
create mode 100644 apps/web/lib/command/palette.test.ts
create mode 100644 apps/web/lib/command/palette.ts
create mode 100644 apps/web/lib/generation/cards.test.ts
create mode 100644 apps/web/lib/generation/cards.ts
create mode 100644 apps/web/lib/generation/useCharacterGen.ts
create mode 100644 apps/web/lib/generation/useWorldGen.ts
create mode 100644 apps/web/lib/jobs/job.test.ts
create mode 100644 apps/web/lib/jobs/job.ts
create mode 100644 apps/web/lib/jobs/useJobPoll.ts
create mode 100644 apps/web/lib/review/style-sse.test.ts
create mode 100644 apps/web/lib/rules/rules.test.ts
create mode 100644 apps/web/lib/rules/rules.ts
create mode 100644 apps/web/lib/rules/useRules.ts
create mode 100644 apps/web/lib/settings/kimiOauth.test.ts
create mode 100644 apps/web/lib/settings/kimiOauth.ts
create mode 100644 apps/web/lib/settings/providers.test.ts
create mode 100644 apps/web/lib/settings/useKimiOauth.ts
create mode 100644 apps/web/lib/skills/skills.test.ts
create mode 100644 apps/web/lib/skills/skills.ts
create mode 100644 apps/web/lib/style/style.test.ts
create mode 100644 apps/web/lib/style/style.ts
create mode 100644 apps/web/lib/style/useRefine.ts
create mode 100644 apps/web/lib/style/useStyleLearn.ts
create mode 100644 apps/web/lib/workbench/chapter.ts
create mode 100644 packages/agents/tests/test_generation_specs.py
create mode 100644 packages/agents/tests/test_style_specs.py
create mode 100644 packages/core/tests/test_generation_node.py
create mode 100644 packages/core/tests/test_generation_write_repos.py
create mode 100644 packages/core/tests/test_job_repo.py
create mode 100644 packages/core/tests/test_rules_repo.py
create mode 100644 packages/core/tests/test_style_extract_node.py
create mode 100644 packages/core/tests/test_style_review.py
create mode 100644 packages/core/ww_core/domain/character_repo.py
create mode 100644 packages/core/ww_core/domain/job_repo.py
create mode 100644 packages/core/ww_core/domain/rule_repo.py
create mode 100644 packages/core/ww_core/domain/style_repo.py
create mode 100644 packages/core/ww_core/domain/world_entity_repo.py
create mode 100644 packages/core/ww_core/orchestrator/generation_node.py
create mode 100644 packages/core/ww_core/orchestrator/style_extract_node.py
create mode 100644 packages/db/migrations/versions/1f011c42bd4d_provider_credentials_oauth_columns.py
create mode 100644 packages/llm_gateway/tests/fakes_resilience.py
create mode 100644 packages/llm_gateway/tests/test_build_adapter_factory.py
create mode 100644 packages/llm_gateway/tests/test_capability_degradation.py
create mode 100644 packages/llm_gateway/tests/test_fallback_chain.py
create mode 100644 packages/llm_gateway/tests/test_kimi_code_adapter.py
create mode 100644 packages/llm_gateway/tests/test_kimi_code_factory.py
create mode 100644 packages/llm_gateway/tests/test_kimi_code_key_factory.py
create mode 100644 packages/llm_gateway/tests/test_multi_provider_accounting.py
create mode 100644 packages/llm_gateway/tests/test_new_adapters.py
create mode 100644 packages/llm_gateway/ww_llm_gateway/adapters/anthropic.py
create mode 100644 packages/llm_gateway/ww_llm_gateway/adapters/gemini.py
create mode 100644 packages/llm_gateway/ww_llm_gateway/adapters/kimi_code.py
create mode 100644 packages/llm_gateway/ww_llm_gateway/adapters/kimi_code_key.py
create mode 100644 packages/llm_gateway/ww_llm_gateway/errors.py
create mode 100644 packages/llm_gateway/ww_llm_gateway/factory.py
create mode 100644 packages/skills/pyproject.toml
create mode 100644 packages/skills/tests/test_skill_permissions.py
create mode 100644 packages/skills/tests/test_skill_registry.py
create mode 100644 packages/skills/ww_skills/skill_permissions.py
create mode 100644 packages/skills/ww_skills/skill_registry.py
create mode 100644 tests/test_k1_kimi_oauth_e2e.py
create mode 100644 tests/test_m4_e2e.py
create mode 100644 tests/test_m5_e2e.py
diff --git a/.gitignore b/.gitignore
index 91f5442..f7961c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ apps/web/lib/api/openapi.json
.claude/settings.local.json
# os
.DS_Store
+.gstack/
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index c240952..b87db76 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -774,6 +774,7 @@ def assemble(project_id, chapter_no):
| POST | `/projects/:id/characters/generate` | `{需求, count?, role?}` | 角色卡(预览,待入库) | 200 |
| POST | `/projects/:id/characters` | 角色卡 | 入库结果 | 201 |
| POST | `/projects/:id/style` | 样本(`mode=update?`) | `{job_id}`(异步见 7.4) | 202 |
+| GET | `/projects/:id/style` | — | 最新文风指纹 `{dimensions, evidence, version}` | 200 (无指纹 404) |
| GET | `/jobs/:id` | — | 任务状态/进度/结果 | 200 |
| POST | `/projects/:id/outline` | `{范围}` | 大纲(含伏笔窗口) | 200 |
| POST | `/projects/:id/chapters/:no/draft` | `{}` | **SSE 流**草稿 | 200(stream) |
diff --git a/CLAUDE.md b/CLAUDE.md
index 0492c6c..7222c4c 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -4,7 +4,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Repository status
-**This is a spec-only repository at the pre-implementation stage.** There is no application code yet — only four design documents. The next step is Phase 0 / T0.1 in `DEV_PLAN.md` (scaffold the monorepo). Do not assume any build/test tooling exists until you create it.
+**Phase 0–5 (M1–M5) are all shipped; there is no active phase.** The monorepo is scaffolded and the full MVP loop runs end-to-end: 立项 → 设定库 → 大纲 → 写章(SSE) → 四审(一致性/伏笔/文风/节奏) → 验收事务,底层带多 provider 网关(路由/回退/熔断) + 声明式 skill 注册表(§5.5) + Kimi Code OAuth(K1)。Last green gate: backend ruff/format clean · mypy **154 files** · alembic **no drift** · pytest **434 passed**; frontend lint/typecheck clean · vitest **155 passed** · build OK. **The build/test tooling exists — see §Toolchain below; do not re-scaffold.**
+
+`PROGRESS.md` is the authoritative status ledger (current phase + archived M1–M5 detail + changelog). Read it before any task. **Next work if resumed**: DEV_PLAN §4 P2/后续 (向量检索 / 真任务队列 / 全书一致性扫描 / 社区市场), or the 对标竞品功能补齐 plan (`~/.claude/plans/streamed-giggling-fiddle.md` — AI续写/扩写、AI拆书、通用生成器框架、细纲、模板库).
Product: an AI-assisted **Chinese web-novel writing workflow**, delivered as a web app. The core thesis (`PRODUCT_SPEC.md §1.5`) is to treat long-form novel writing like software engineering — establish architecture (worldbuilding/characters/outline), generate against spec, test every chapter, maintain a single source of truth.
diff --git a/PRODUCT_SPEC.md b/PRODUCT_SPEC.md
index 8fc77e6..ce68dc7 100644
--- a/PRODUCT_SPEC.md
+++ b/PRODUCT_SPEC.md
@@ -506,6 +506,7 @@ skills(id, scope, name, description, model, system_prompt,
| `POST /projects/:id/world/generate` | 设计世界观 | 立项/写作 | worldbuilder 生成/扩展世界观、力量体系、势力、地理 |
| `POST /projects/:id/characters/generate` | AI 生成角色 | 立项/写作 | 一句话生成完整角色卡,入库;批量传 `count` + 定位 |
| `POST /projects/:id/style` | 学文风 | 立项 | 学习作者文风,生成文风指纹(`mode=update` 增量补样本) |
+| `GET /projects/:id/style` | 查看文风 | 立项/写作 | 读取最新文风指纹(完整 16 维 + 原文证据 + 版本号;无指纹 404) |
| `POST /projects/:id/outline` | 排大纲 | 规划 | 生成/更新分卷分章大纲,提示伏笔回收窗口 |
| `POST /projects/:id/chapters/:no/draft` | 写本章 | 写作 | 注入记忆+文风,产出本章草稿(streaming) |
| `POST /projects/:id/chapters/:no/review` | 审稿 | 审稿 | 四审并行,输出冲突/漂移/节奏报告 |
diff --git a/PROGRESS.md b/PROGRESS.md
index 26b7e0e..950ad83 100644
--- a/PROGRESS.md
+++ b/PROGRESS.md
@@ -8,33 +8,60 @@
---
-## 当前阶段:Phase 3 (M3) · 伏笔 + 节奏
+## 当前阶段:K1 · Kimi Code OAuth(订阅 plan 接入)
-> 目标:伏笔账本/到期提醒/看板 + 节奏引擎 + 大纲。**契约先行**:C6 扩(foreshadow-analyst/pace-checker/outliner 三 spec + 输出 schema) → C4 扩(审图加两审分支 + outline 节点) → C3 扩(outline/foreshadow 端点)。
-> 执行波次(依赖驱动):A(T3.1‖T3.4) → B(T3.2‖T3.3) → C(T3.5) → D(T3.6) → E(T3.7)。
-> 计划已定的衔接决策:
-> **M3-a** `foreshadow` 表已在 T0.2 建齐(含 status/expected_close_from/to/progress/links 列)→ **M3 无新建表迁移**;T3.1 仅 Repository + 纯函数状态机,落 `core/ww_core/domain/`(@backend owned,同 T2.3 先例,故 T3.1 负责据实记 @backend)。
-> **M3-b** 到期扫描(T3.2)挂在**验收后**触发——接 M2 验收事务 §5.5 步骤 3 当前留的 `TODO(M3)`(人物 latest_state/伏笔更新)占位。
-> **M3-c** 第三审(foreshadow/pace)复用 M2 `build_review_graph` 的 `review_specs` 扩展点并入并行分支(C4 已稳定);**文风第四审属 M4/T4.2**,M3 先到三审齐。
-> **M3-d** 到期判据为纯函数确定性代码(非 Agent):`current_ch > expected_close_to AND status≠CLOSED → OVERDUE`。
+> **背景**:用户要求接入 Kimi Code 订阅 plan(device-flow OAuth)。**已知情接受 ToS/封号风险**——研究确认:用订阅 plan 调用需伪造官方客户端 `User-Agent` + `X-Msh-*` 头,Kimi ToS 视为违规、可能封停会员(见变更日志研究结论)。用户明确选择此路径(非合规的 API Key 路径)。与既有 API-key провайдер `kimi` **分离**,新增独立 provider `kimi-code` 以便切换。
+> **集成契约(研究确认,实现时对照 `github.com/picassio/pi-kimi-coder` 校验)**:
+> - device flow:`POST https://auth.kimi.com/api/oauth/device_authorization`(client_id `17e5f671-d194-4dfb-9706-5516cb48c098`,env `KIMI_CLIENT_ID` 可覆盖,scope `kimi-code`)→ 返回 user_code/verification_uri/device_code/interval;轮询 `POST https://auth.kimi.com/api/oauth/token`(grant device_code,处理 authorization_pending/slow_down)。refresh:同 token 端点 grant refresh_token。
+> - 推理:base `https://api.kimi.com/coding/v1`(OpenAI 兼容),model `kimi-for-coding`,`Authorization: Bearer ` + **必需伪造头**:`User-Agent`(如 `KimiCLI/1.x`) + `X-Msh-Platform`/`X-Msh-Version`/`X-Msh-Device-Name`/`X-Msh-Device-Model`/`X-Msh-Os-Version`/`X-Msh-Device-Id`(持久 UUID)。缺头→403。
+> - token:access ~15min / refresh ~30d → **建网关时按需刷新**(临近过期刷新并持久化)。存储:`provider_credentials` 加 `auth_type`(api_key|oauth) + `oauth_enc`(Fernet 加密 JSON `{access_token,refresh_token,expires_at}`)。
+> **执行波次**:A(@db 加列+迁移 ‖ @llm Kimi coding 适配器) → B(@backend OAuth device 服务 + 端点 + store + `_build_provider_adapter` 接线) → C(@frontend 设置页连接/轮询/断开 + 路由档位) → D(@qa E2E device-flow mock + 头断言)。
+> **测试纪律**:device flow 注入 fake httpx(零联网);适配器断言伪造头集;token 刷新逻辑单测;E2E mock auth+coding 两端点。
| 任务 | 状态 | 负责 | 依赖 | 契约 | 备注 |
|---|---|---|---|---|---|
-| T3.1 foreshadow Repository + 纯函数状态机 | ✅ @backend 2026-06-18 | @backend | T0.2✅ | — | `foreshadow_state.py`(ForeshadowStatus+transition/is_overdue/apply_overdue_scan/InvalidTransition)+`foreshadow_repo.py`(`ForeshadowLedger*`,register/list_by_status/transition/record_progress/scan_overdue,只 flush);37 测;写侧加 Ledger 前缀避撞 C5 读侧 |
-| T3.2 到期扫描(BackgroundTask) + 登记/状态接口 | ✅ @backend 2026-06-18 | @backend | T2.4✅, T3.1✅ | C3✅扩 | accept 后 BackgroundTask `run_overdue_scan`(自建 session 置 OVERDUE);`POST/PATCH .../foreshadow` 登记/转移(重复/非法→VALIDATION 422);9 测全仓 205 passed |
-| T3.3 foreshadow-analyst + pace-checker 节点 | ✅ @llm 2026-06-18 | @llm | T2.2✅ | C6✅扩/C4✅扩 | `foreshadow_spec`+`pace_spec`(+schema)并入 `REVIEW_SPECS`(三审齐);collect 分列落 conflicts/foreshadow_sug/pace;`normalize_review` 加 `foreshadow`/`pace` 事件;~30 测;文风审留 M4 |
-| T3.4 outliner Agent + 伏笔窗口 | ✅ @llm 2026-06-18 | @llm | T1.1✅ | C6✅扩 | `packages/agents/`(outliner_spec+OutlineResult/OutlineChapter/ForeshadowWindow)+`orchestrator/outline_node.py`(run_outline 只读不写库);20 测;C6 扩稳定 |
-| T3.5 API:outline + foreshadow | ✅ @backend 2026-06-18 | @backend | T3.1✅, T3.4✅ | C3✅扩 | `GET .../foreshadow?status=`(看板,续 foreshadow.router) + `POST .../outline`(独立 outline.py,run_outline analyst→upsert outline 表+commit);outline 写侧 repo;+15 测全仓 220 passed |
-| T3.6 伏笔看板 + 大纲编辑器 + 节奏报告 | ✅ @frontend 2026-06-18 | @frontend | T3.3✅, T3.5✅ | — | `/foreshadow`(四泳道+登记/转移) + `/outline`(beats+窗口徽标) + 审稿页扩消费 foreshadow/pace(节拍图▁▃▅);gen:api 纳 M3 端点;vitest 69(+24) |
-| T3.7 M3 E2E | ✅ @qa 2026-06-18 | @qa | T3.6✅ | — | `tests/test_m3_e2e.py` 3 用例真跑(非 skip):伏笔埋设→进展→验收后扫描 OVERDUE✓ + 大纲 upsert 含窗口✓ + 三审 SSE(**strict-xfail 钉住并发记账 bug**);DB 真源断言 |
-| T3.8 [bugfix] 并行审记账并发安全 | ✅ @llm 2026-06-18 | @llm | T3.7✅ | — | `SqlAlchemyLedgerSink.record` 改 add-only(去 `await flush`);并发回归测试(RED 已验旧实现炸);E2E case3 转 XPASS(三审齐落库 foreshadow_sug=2/pace✓)。**待 @qa 解钉 T3.7 strict-xfail 收尾** |
+| K1.1 provider_credentials 加 auth_type + oauth_enc 列 | ✅ | @db | — | C2扩 | 完成:models 加 auth_type/oauth_enc + api_key_enc 改 nullable;迁移 `1f011c42bd4d`;alembic check 无漂移。⚠️ K1.3 @backend 须把 `StoredCredential.api_key_enc` 改 `bytes\|None`(见下方请求 + memory)|
+| K1.2 Kimi coding 适配器(伪造头 + OAuth bearer) | ✅ | @llm | — | C1扩 | 完成:`adapters/kimi_code.py`(KimiCodeAdapter 子类化 OpenAICompatAdapter + 伪造头 + 稳定 device_id);`build_adapter` 加 `kimi-code` 分支;UA 验证为 `KimiCLI/1.5`(pi-kimi-coder 仅设 UA,X-Msh-* 为契约附带,见 C1扩 校正);11 测全绿 |
+| K1.3 OAuth device 服务 + 端点 + store + 接线 | ✅ | @backend | K1.1,K1.2 | C3扩 | 完成:`services/kimi_oauth.py`(start/poll/refresh,注入 httpx Protocol)+3 端点(start 202→jobs 后台轮询/disconnect/status,复用 run_job)+`StoredCredential` 收口(api_key_enc→nullable+auth_type/oauth_enc,K1.1 的 2 mypy red 已消)+store `upsert_oauth_credential`/`delete_credential`+`_build_provider_adapter` OAuth 按需刷新(300s 缓冲)。**token 不进 job 结果/日志/响应**。全仓门禁绿(ruff/format/mypy **154**/alembic 无漂移/pytest **430**)。OpenAPI 加 3 端点→**@frontend K1.4 须 `pnpm gen:api`** |
+| K1.4 设置页 Kimi Code 连接流 | ✅ | @frontend | K1.3 | — | 完成:`gen:api` 纳入 3 OAuth 端点;`KimiCodeOauth` 连接区(显 user_code+开授权页+轮询 job→已连接/断开)+ 可编辑档位路由(含 `kimi-code:kimi-for-coding`);纯逻辑 `lib/settings/{kimiOauth,providers}` + 25 新 vitest;前端门禁全绿(gen:api/lint/typecheck/vitest 155/build)。运行时验真 auth.kimi.com 留 K1.5 |
+| K1.5 K1 E2E | ✅ | @qa | K1.4 | — | `tests/test_k1_kimi_oauth_e2e.py`(4 用例,真 pg):device-flow(pending×2→成功)→加密 token 真落 pg(无明文+回环)→建网关断言 KimiCodeAdapter coding base+伪造头+bearer→refresh-on-build 更新存量 token(DB 真源)→disconnect 清除;token 全程不进 job 结果/状态/start 响应。全仓门禁绿(ruff/format/mypy **154**/alembic 无漂移/pytest **434**)。**K1 完成,可封板** |
-**M3 出口(DoD)✅ 全部达成**:伏笔账本/到期提醒/看板;大纲含回收窗口;节奏报告(三审齐真持久化)。后端门禁绿(ruff / mypy **111 文件** / pytest **228 passed** / alembic **无漂移**);前端门禁绿(gen:api / lint / tsc / vitest **69** / build);E2E 真实 DB + mock 网关零 token 走通 伏笔埋设→进展→验收后扫描 OVERDUE→看板 + 大纲含窗口 + 三审齐 SSE/留痕,DB 真源逐项断言。T3.7 E2E 暴露并发记账 bug → T3.8 add-only 修复 → 解钉 xfail。**M3 完成。** 下一阶段 M4(文风)见 DEV_PLAN,进入时拉入本表。
+**K1 出口(DoD)✅ 全部达成**:作者在设置页用 Kimi Code OAuth 登录(device flow,显 user_code + 开授权页 + 轮询 job)→ token Fernet 加密入库(`oauth_enc`,全程不进 job 结果/日志/响应)→ 把档位路由到 `kimi-code:kimi-for-coding` → 建网关时构 `KimiCodeAdapter`(coding base + 伪造 UA/X-Msh-* 头 + bearer)→ token 临近过期建网关时自动刷新并回写。后端门禁绿(ruff/format 干净 / mypy **154** / alembic **无漂移** / pytest **434**);前端门禁绿(gen:api/lint/tsc/vitest **155**/build);E2E(`tests/test_k1_kimi_oauth_e2e.py` 4 用例)device-flow mock 零联网走通,DB 真源逐项断言(含 token 不泄漏 + refresh-on-build + disconnect 清除)。**K1 完成。** **⚠️ 仍需用户用真账号对真 auth.kimi.com/api.kimi.com 做一次联网冒烟(device-flow 需伪造官方客户端 UA → ToS 视为违规、可能封停会员,用户已知情自担);E2E/单测仅验逻辑、不联网。**
+
+---
+
+## 状态:M1–M5 全部完成(含 M5 三项收尾余项已清理)
+
+> **当前无活跃阶段。** Phase 0–5(M1–M5)已全部交付,M5 的三项非阻塞收尾余项(①Codex 读端点、②Anthropic/Gemini 真实适配器选择、③`packages/skills` 纳入 workspace)亦已清理。各阶段详情见下方「已归档阶段」+「变更日志」。
+> 最终门禁(本次全绿):后端 ruff/format 干净、mypy **144 文件**、alembic **无漂移**、pytest **400 passed**;前端 lint/typecheck 干净、vitest **130 passed**、build OK。
+> **下一步若继续**:DEV_PLAN §4 的 P2/后续(向量检索 / 真任务队列 / 全书一致性扫描 / 社区市场),进入时把对应任务拉入本表。
+> **仍开放的非阻塞备注**:@devops 复核根 `uv.lock`(@backend 改了 `packages/skills/pyproject.toml` deps 后已 `uv sync` 重建 ww-skills);Anthropic/Gemini 真实回退已接线(`build_adapter` 工厂 + 按 provider 选适配器)但尚未对真 SDK 联网验证(单测/E2E 用 OpenAI 兼容 mock)。
---
## 已归档阶段
+Phase 5 · 生成 + 多 provider + 扩展 (M5) — ✅ 全部完成(2026-06-19)
+
+T5.1 worldbuilder + character-gen 节点(C6✅扩;群像防雷同注入已生成+已有 + 入库前 continuity 校验缝 `precheck_generated_cards`)@llm · T5.4 网关多 provider 韧性(C1✅扩;Anthropic/Gemini 适配器 + 回退链 + `CircuitBreaker` + tenacity 退避 + 能力协商/降级 `served_by.degraded`,无 OpenAPI 形变)@llm · T5.5 Skill 运行时 + 规则(C8 + C3✅扩;`SkillRegistry.load` 越权声明拒绝 + 表权限沙箱 filter_reads/partition_writes + `POST /rules`)@backend · T5.2 生成/入库 API(C3✅扩;world/characters generate 预览 + POST characters 入库双 gate:continuity 预检 409 + partition_writes 白名单 + schema→JSONB 形变 + GET rules/skills;`build_gateway_for_tier` 接线 chain_from_routing 回退)@backend · T5.3+T5.6 角色生成器 + 世界观设计器 + 设定库 Codex + 规则页 + 技能库 + 命令面板(⌘K)@frontend · T5.7 M5 E2E + 切 provider(真 Gateway deepseek 失败→openai 回退、DB ledger 记实际服务方、能力降级)@qa。
+出口达成:后端门禁绿(ruff/format 干净 / mypy 142 / pytest 391 / alembic 无漂移,复用 T0.2 既有表零建表迁移);前端门禁绿(gen:api / lint / tsc / vitest 125 / build);E2E 真 DB + mock 多 provider 零 token 走通。**M5 完成 → M1–M5 全部交付。** 三非阻塞余项(①Codex 读端点、②Anthropic/Gemini 真实适配器选择、③`packages/skills` 纳入 workspace)随后于同日全部清理(见变更日志 follow-up #1–#4)。
+
+
+
+Phase 4 · 文风 (M4) — ✅ 全部完成(2026-06-19)
+
+T4.2 style-auditor 双轨 + 第四审(C6✅扩/C4✅扩;`style_extract_spec`(analyst,16 维指纹带证据,不进 review 图) + `style_drift_spec`(name="style",light,第四审并入 REVIEW_SPECS) + `refiner_spec`(writer 纯文本),REVIEW_SPECS 四审齐 + collect/SSE 加 style)@llm · T4.1 jobs 长任务基建(C3.5✅;`JobRepo`/`SqlJobRepo` + `run_job` 独立 session + lifespan zombie reaper)@backend · T4.3 API style + refine(C3✅扩;`POST /style` 202+jobs BackgroundTask、`GET /style` 最新指纹、`POST /refine` 同步 diff 不写库)@backend · T4.4 文风页 + 漂移 + 回炉 + job 轮询(`useJobPoll` 状态机 + StyleUpload/FingerprintView + 审稿页第四审 StylePanel + RefineView diff 采纳合入)@frontend · T4.5 M4 E2E(学文风→202 job→后台跑完→指纹真落 pg version+1 + 第四审 SSE/`chapter_reviews.style` 真填 + 回炉不写库)@qa。
+出口达成:后端门禁绿(ruff/format 干净 / mypy 115 / pytest 296 / alembic 无漂移,全程零建表迁移,复用 T0.2 预留的 `style_fingerprint`/`chapter_reviews.style`/`jobs`);前端门禁绿(gen:api / lint / tsc / vitest 96 / build);E2E 真 DB + mock 网关零 token 走通 学文风→第四审打分→回炉,DB 真源逐项断言。@docs 已把 `GET /style` 回写 ARCH §7.2/PRODUCT_SPEC §6。
+
+
+
+Phase 3 · 伏笔 + 节奏 (M3) — ✅ 全部完成(2026-06-18)
+
+T3.1 foreshadow Repository + 纯函数状态机(OPEN/PARTIAL/CLOSED/OVERDUE 全转移 + 到期判据 + `ForeshadowLedger*` 写侧 repo)@backend · T3.4 outliner Agent + 伏笔窗口(C6✅扩;`outliner_spec` + `OutlineResult`(含 foreshadow_windows) + `run_outline` 只读节点)@llm · T3.3 foreshadow-analyst + pace-checker 节点(C6✅扩/C4✅扩;并入 REVIEW_SPECS 三审齐 + collect 分列 + SSE foreshadow/pace 事件)@llm · T3.2 到期扫描 BackgroundTask + 登记/状态接口(验收后 `run_overdue_scan` 自建 session 置 OVERDUE + `POST/PATCH .../foreshadow`,重复/非法→VALIDATION 422)@backend · T3.5 API outline + foreshadow(C3✅扩;`GET .../foreshadow?status=` 看板 + `POST .../outline` upsert outline 表含窗口)@backend · T3.6 伏笔看板 + 大纲编辑器 + 节奏报告(四泳道 + beats+窗口徽标 + 审稿页节拍图▁▃▅)@frontend · T3.7 M3 E2E(伏笔埋设→进展→验收后扫描 OVERDUE + 大纲含窗口 + 三审 SSE,strict-xfail 钉住并发记账 bug)@qa · T3.8 [bugfix] 并行审记账并发安全(`SqlAlchemyLedgerSink.record` 改 add-only 去 `await flush`,解 AsyncSession 并发 flush 冲突,xfail 转 XPASS 后解钉)@llm。
+出口达成:后端门禁绿(ruff / mypy 111 / pytest 228 passed(0 xfailed/0 failed)/ alembic 无漂移,`foreshadow` 表已 T0.2 建齐无新建表);前端门禁绿(gen:api / lint / tsc / vitest 69 / build);E2E 真 DB + mock 网关零 token 走通,DB 真源逐项断言。T3.7 暴露并发记账 bug → T3.8 修复 → 解钉 xfail。
+
+
+
Phase 2 · 一致性 + 验收 (M2) — ✅ 全部完成(2026-06-18)
T2.1 续审 AgentSpec+结构化输出契约+网关 instructor 接线(C6✅稳定,改 C1:`run()` 填 `parsed`)@llm · T2.2 LangGraph 并行审+collect 留痕+review SSE(C4✅扩)@llm · T2.3 摘要/审稿留痕 Repository+章节 version(@backend,表已 T0.2 建)· T2.4 验收事务+冲突 gate(单原子事务 + R2 事务外提炼 digest)@backend · T2.5 review API+历史(C3✅扩)@backend · T2.6 审稿报告页+冲突裁决+验收 gate @frontend · T2.7 M2 E2E(真 DB+多档位 mock 网关零 token)@qa。
@@ -63,6 +90,45 @@ T0.1 monorepo 骨架 ✅ @devops · T0.2 16 MVP 表迁移(无漂移,users st
> 格式:`- [YYYY-MM-DD] @skill 完成/进展 Txx — 一句话结果 + 影响的契约/文件`
+- [2026-06-20] @frontend — **✅ 接上两个新读端点:大纲页 / 写作工作台重访时回显已存内容**。`pnpm gen:api` 纳入 `GET .../outline` + `GET .../chapters/{no}/draft` + schema `DraftView`;`lib/api/types.ts` 加 `DraftView` 别名。新增两个 RSC 读 helper(仿 `fetchForeshadow`/`fetchProject` 服务端读、无 CORS):`fetchOutline(projectId)`→`OutlineChapterView[]`(200 解包 chapters,**任何错误/空降级为 `[]`** 不阻塞进页),`fetchDraft(projectId,chapterNo)`→`DraftView|null`(复用 `getJsonOrNull`,**404→null 当空编辑器**,不抛)。**大纲页**(`outline/page.tsx`) 进页 `fetchOutline` 作 `initialChapters` 种入 `OutlineEditor`(`useOutline` 已据非空初值置 `ready`)→ 重访显已生成大纲;生成流照常覆盖所显章节。**写作工作台**(`write/page.tsx` + `Workbench`) 进页 `fetchDraft` 拿 `content` 作编辑器初值(`useState(initialText)`)→ 重访重载已写正文;404/新章 → `""` 空编辑器;**SSE 流式写 + AbortController + PUT 自动保存全不动**(流起始空、仅 streaming/done/aborted 变化才覆盖,初值不反扑活跃流);导出 `WORKBENCH_CHAPTER_NO`(原 `M1_CHAPTER_NO`)供页面拉对应章草稿。新增 `lib/api/server.test.ts`(5 vitest,mock fetch 验解包/降级/404→null)。前端门禁全绿:gen:api / lint / typecheck / vitest **160**(155→+5) / build。文件:`lib/api/{types,server,server.test}.ts` · `app/projects/[id]/{outline,write}/page.tsx` · `components/workbench/Workbench.tsx`。消费 contracts C3扩(@backend 两读端点)。
+- [2026-06-20] @backend — **✅ `GET /projects/{id}/chapters/{no}/draft` 草稿读端点(补缺口,镜像 GET /outline)**。写作工作台先前只有 `POST .../draft`(SSE 流写)+`PUT .../draft`(自动保存) 无读端点 → 重访工作台编辑器空白、看似「未保存」(其实 `chapters` 行在库)。补对称读侧:`GET /projects/{id}/chapters/{no}/draft` → 200 `DraftView{project_id,chapter_no,volume,status,version,content,length}`(**比 PUT 的 `DraftResponse` 多 `content`**——读侧需正文重建编辑器);无草稿行/正文空白 → **404 `NOT_FOUND`**(工作台据此呈现空编辑器,仿其它缺资源端点)。**直接复用既有 `chapter_repo.get_draft`**(同续审 `_resolve_review_draft` 读缝,`ChapterDraftView` 已含全字段,无新 repo 方法/view);只读不写库;多版本固定返草稿版次(`DRAFT_VERSION=1`)。文件:`schemas/projects.py`(+`DraftView`) · `routers/projects.py`(+GET endpoint+import) · `tests/test_projects.py`(+2:回灌正文 / 无草稿 404)。**全仓门禁绿**:ruff/format 干净、mypy **154 Success**、alembic **无漂移**、pytest **444 passed**(442→+2)。OpenAPI 新增 1 端点 → **@frontend 须 `cd apps/web && pnpm gen:api`** + 工作台初次加载拉已保存草稿回灌编辑器。记 contracts C3扩。
+- [2026-06-20] @backend — **✅ `GET /projects/{id}/outline` 大纲读端点(补缺口,仿 Codex 读端点)**。大纲页先前只有 `POST .../outline`(生成+落库)无读端点 → 重访页面已落库大纲不回显、看似「未保存」。补对称读侧:`GET /projects/{id}/outline` → 200 `OutlineResponse{chapters:[OutlineChapterView{no,volume,beats:list[str],foreshadow_windows}]}`(与 POST **同形**,按 chapter_no 升序);项目存在无大纲 → **200 空列表**(非 404);项目不存在 → 404 `NOT_FOUND`。**复用 C5 读侧**:扩 `OutlineRepo`(domain) + `SqlOutlineRepo`(assemble 读侧) 加 `list_for_project`(仿 Character/WorldEntityRepo,order_by chapter_no,既有 `get` 不动);新注入缝 `get_outline_read_repo`(仿 `get_rules_read_repo`)。beats DB JSONB `{"beats":[...]}` → 解包裸 `list[str]`(同 POST)。文件:`routers/outline.py`(+GET) · `services/project_deps.py`(+dep) · `domain/repositories.py`(+protocol method) · `memory/sql_repositories.py`(+impl) · `tests/{test_outline.py(+3),fakes_projects.py(+FakeOutlineReadRepo)}` + 补 2 既有 outline fake(test_memory/test_projects)满足扩展后的 protocol。**全仓门禁绿**:ruff/format 干净、mypy **154 Success**、alembic **无漂移**、pytest **442 passed**(439→+3)。OpenAPI 新增 1 端点 → **@frontend 须 `cd apps/web && pnpm gen:api`** + 大纲页初次加载拉已持久化大纲。记 contracts C3扩。
+
+- [2026-06-19] @llm — **K1.2 修头校正:恢复 opencode 规范的完整 7 头(UA `KimiCLI/1.37.0`)**。先前误信 `picassio/pi-kimi-coder`(UA-only)把头集裁成仅 UA,是分歧/错误参考导致的误修——真源改为 `ooojustin/opencode-kimi`(`src/headers.ts`+`src/constants.ts`,1:1 镜像 kimi-cli v1.37.0,已重新拉取 master 核对)。`kimi_code_headers()` 现发 7 头:`User-Agent=KimiCLI/1.37.0` + `X-Msh-Platform=kimi_cli`(字面常量)+ `X-Msh-Version=1.37.0` + `X-Msh-Device-Name`(hostname ASCII 化)+ `X-Msh-Device-Model`(`kimi_device_model()`:macOS=`macOS {mac_ver} {machine}`)+ `X-Msh-Os-Version`(`platform.version()`)+ `X-Msh-Device-Id`(稳定 32 位无连字符 `uuid4().hex`:env `KIMI_DEVICE_ID` 优先→否则持久化 `~/.kimi/device_id`)。恢复 `kimi_device_id()`/`kimi_device_model()` 助手 + 重加 imports(os/uuid/socket/platform/pathlib)+ `__init__` 重导出。文件:`packages/llm_gateway/ww_llm_gateway/adapters/kimi_code.py` + `__init__.py` + 两测(断全 7 头键 + 固定字面值 + device-id 32 位小写 hex 稳定 + host 派生值非空 ASCII,env 注入保 CI 确定)。更 contracts C1扩(7 头 + opencode 真源)+ gotcha(pi-kimi-coder 分歧→回退)。门禁绿:ruff/format 干净、mypy 干净(27 文件)、pytest **60 passed**(packages/llm_gateway)。无 OpenAPI 形变。
+- [2026-06-19] @backend — **K1.3 修正:device authorization 重新带 `scope=kimi-code`**(实测 401 后修)。K1.5 联网实测登录拿到真 JWT 但调 `api.kimi.com/coding/v1` 回 `401 Invalid Authentication`——根因 token 缺 coding entitlement(省略了 scope)。`start_device_authorization` 请求体加 `scope=kimi-code`(=`{"client_id":,"scope":"kimi-code"}`,模块常量 `KIMI_CODE_SCOPE`);scope 只放 device-auth,token 交换/刷新不带。文件:`apps/api/ww_api/services/kimi_oauth.py` + 单测 `test_kimi_oauth_service.py`(`test_start_device_authorization_parses_response_and_sends_scope` 断 `data["scope"]=="kimi-code"`)。修正 C3扩 contract + decision「省略 scope」表述 + 加 gotcha。无 OpenAPI 形变,前端无需 re-gen。门禁绿:ruff/format 干净、mypy 干净、pytest **101 passed**(apps/api)。
+- [2026-06-19] @orchestrator — **🎉 K1 完成 + 独立复核(Kimi Code OAuth 订阅 plan 接入)**。波次 A(K1.1 @db 加列+迁移 ‖ K1.2 @llm KimiCodeAdapter)→B(K1.3 @backend OAuth device 服务+端点+store+接线)→C(K1.4 @frontend 连接流+可编辑路由)→D(K1.5 @qa E2E) 全绿,契约先行、零返工、无新 bug。研究先行(device flow at auth.kimi.com、client_id、伪造 UA/X-Msh-*、coding base、kimi-for-coding、access~15m/refresh~30d)+ 对照 pi-kimi-coder/ooojustin/lemon07r 校验(省略 scope 匹配 kimi-cli v1.41.0+)。最终独立复跑确认:后端 ruff/format 干净、mypy **154 文件**、alembic **无漂移**(首个新迁移=provider_credentials 加 auth_type/oauth_enc)、pytest **434 passed**;前端 vitest **155**/build。能力闭环:设置页 device-flow 登录→token Fernet 加密入库(`oauth_enc`,不泄漏)→路由 `kimi-code:kimi-for-coding`→网关构 KimiCodeAdapter(伪造头+bearer)→临过期自动刷新。**封号风险用户已知情接受**;与既有 API-key provider `kimi` 分离可切换。**唯一未尽**:对真 auth.kimi.com/api.kimi.com 的真账号联网冒烟(含封号风险,用户自担)——代码/E2E 仅验逻辑零联网。
+- [2026-06-19] @qa — **K1.5 ✅ K1 E2E(Kimi Code OAuth device-flow 全 mock → 真 pg)**。新增 `tests/test_k1_kimi_oauth_e2e.py`(4 用例,零联网,真 pg):① `POST .../oauth/start`→202 `{job_id,user_code,verification_uri,interval}`,fake httpx 跑 device-auth + token(pending×2→成功) 全程;后台 `run_job` 轮询跑在**真 e2e_sm session**(override `get_session_factory`→e2e_sm + monkeypatch 模块级 `kimi_oauth._default_http_client`→同一 scripted fake + `kimi_oauth.asyncio.sleep`→no-op;端点 device-auth 走 `_default_http_client` dep 同样指 fake)→ `GET /jobs/{id}` done,result `{connected:true,provider:"kimi-code"}`;**DB 真源**:`provider_credentials` 行 `auth_type="oauth"`/`oauth_enc` 非空/`api_key_enc` null,密文不含明文 token + 解密回环 access/refresh;`GET .../oauth/status` connected 无 token。② 存 oauth + `tier_routing` writer→`kimi-code:kimi-for-coding` → `build_gateway_for_tier` → `KimiCodeAdapter`,其 `_client` 带 coding base `https://api.kimi.com/coding/v1` + UA `KimiCLI/1.5` + 全 X-Msh-* + `api_key`(=bearer)=存量 access token。③ 临近过期 oauth 包 → `_build_provider_adapter` 触发刷新(fake `project_deps.kimi_refresh` + 字符串 target monkeypatch `project_deps.httpx.AsyncClient`)→ 适配器用新 token + `oauth_enc`(DB 真源)更新为新包。④ `POST .../oauth/disconnect`→凭据行清除(DB 真源无 oauth)。**token 全程负向断言:不进 job 结果/状态响应/start 响应/密文明文**。全仓门禁绿:ruff/format 干净、mypy **154 Success**、alembic 无漂移、pytest **434 passed**(430→+4)。memory 记 1 gotcha。**K1(Kimi Code OAuth 订阅 plan 接入)全 5 子任务 ✅,可封板。⚠️ 仍未对真 auth.kimi.com/api.kimi.com 联网验证(含封号风险,用户自担)。**
+- [2026-06-19] @frontend — **K1.4 ✅ 设置页 Kimi Code OAuth 连接流 + 可编辑档位路由**。`pnpm gen:api` 纳入 K1.3 的 3 端点(OAuthStart/Disconnect/StatusResponse),`lib/api/types.ts` 加 3 别名。纯逻辑(node-env vitest):`lib/settings/kimiOauth.ts`(`connectPhase` 状态机 idle/awaiting/connected/error + `toDeviceDisplay`/`authOpenUrl`/`toConnectionStatus`/`jobConnected`/`formatExpiresAt`,16 测)+ `lib/settings/providers.ts` 扩(`KNOWN_PROVIDERS` 加 `kimi-code`(auth:oauth, model:kimi-for-coding) + `API_KEY_PROVIDERS`/`TIERS`/`defaultModelFor` + 路由草稿纯函数 `toRoutingDrafts`/`applyProviderChange`/`draftsToRoutingInput`,9 测)。组件:`useKimiOauth` hook(**复用 `useJobPoll`**:connect=POST start→展示 user_code+`window.open` 授权页+轮询 job→done 拉 status;disconnect=POST→复位)+ `KimiCodeOauth.tsx`(连接区,user_code 用 `
) : (
-
+
)}
diff --git a/apps/web/components/AppShell.tsx b/apps/web/components/AppShell.tsx
index ef2afd5..5af827d 100644
--- a/apps/web/components/AppShell.tsx
+++ b/apps/web/components/AppShell.tsx
@@ -1,7 +1,7 @@
import Link from "next/link";
import type { ReactNode } from "react";
-import { LeftNav } from "./LeftNav";
+import { LeftNav, type ActiveNav } from "./LeftNav";
interface AppShellProps {
children: ReactNode;
@@ -11,7 +11,7 @@ interface AppShellProps {
// 项目内页给出 projectId → 左导航展开项目级入口(大纲/伏笔/审稿,UX §3.1)。
projectId?: string;
// 当前激活的项目级入口键(用于高亮)。
- activeNav?: "write" | "outline" | "foreshadow" | "review";
+ activeNav?: ActiveNav;
}
// 全局外壳:顶栏(64px) + 左导航 + 主区(UX §5.1)。
diff --git a/apps/web/components/LeftNav.tsx b/apps/web/components/LeftNav.tsx
index 376f17f..65fafb4 100644
--- a/apps/web/components/LeftNav.tsx
+++ b/apps/web/components/LeftNav.tsx
@@ -3,7 +3,15 @@
import Link from "next/link";
import { usePathname } from "next/navigation";
-export type ActiveNav = "write" | "outline" | "foreshadow" | "review";
+export type ActiveNav =
+ | "write"
+ | "outline"
+ | "foreshadow"
+ | "review"
+ | "style"
+ | "codex"
+ | "rules"
+ | "skills";
interface LeftNavProps {
// 项目内页传 projectId → 展开项目级入口(大纲/伏笔/审稿,UX §3.1)。
@@ -50,8 +58,30 @@ function projectItems(projectId: string): NavItem[] {
enabled: true,
key: "review",
},
- { href: "#codex", label: "设定库", enabled: false },
- { href: "#style", label: "文风", enabled: false },
+ {
+ href: `/projects/${projectId}/style`,
+ label: "文风",
+ enabled: true,
+ key: "style",
+ },
+ {
+ href: `/projects/${projectId}/codex`,
+ label: "设定库",
+ enabled: true,
+ key: "codex",
+ },
+ {
+ href: `/projects/${projectId}/rules`,
+ label: "规则",
+ enabled: true,
+ key: "rules",
+ },
+ {
+ href: `/projects/${projectId}/skills`,
+ label: "技能库",
+ enabled: true,
+ key: "skills",
+ },
];
}
diff --git a/apps/web/components/codex/CodexPage.tsx b/apps/web/components/codex/CodexPage.tsx
new file mode 100644
index 0000000..803cb8c
--- /dev/null
+++ b/apps/web/components/codex/CodexPage.tsx
@@ -0,0 +1,167 @@
+"use client";
+
+import { useMemo, useState } from "react";
+
+import { AppShell } from "@/components/AppShell";
+import { CharacterGenerator } from "@/components/generation/CharacterGenerator";
+import { WorldGenerator } from "@/components/generation/WorldGenerator";
+import type {
+ CharacterCardView,
+ ProjectResponse,
+ WorldEntityCardView,
+} from "@/lib/api/types";
+import {
+ mergeCharacterCards,
+ worldEntityRules,
+} from "@/lib/generation/cards";
+
+type CodexTab = "characters" | "world" | "timeline";
+
+interface CodexPageProps {
+ project: ProjectResponse;
+ // 后端读端点(GET .../characters / .../world_entities)拉来的跨会话全量真源。
+ initialCharacters: CharacterCardView[];
+ initialWorldEntities: WorldEntityCardView[];
+ // 进页可经 ?gen=character|world 直接打开对应生成器(命令面板动作跳转)。
+ initialTab?: CodexTab;
+}
+
+const TABS: { key: CodexTab; label: string }[] = [
+ { key: "characters", label: "人物" },
+ { key: "world", label: "世界观" },
+ { key: "timeline", label: "时间线" },
+];
+
+// 设定库 Codex(UX §6.5):管理人物 / 世界观 / 时间线。
+// 初始列表来自后端读端点(跨会话全量真源);本会话新入库的角色卡再合并补显,
+// 故刷新后不再为空、且不与真源重复(按 name 去重,见 mergeCharacterCards)。
+// 世界观本期无入库端点(仅生成预览)→ 展示真源 + 生成器预览。时间线为 P2/派生,暂占位。
+export function CodexPage({
+ project,
+ initialCharacters,
+ initialWorldEntities,
+ initialTab = "characters",
+}: CodexPageProps) {
+ const [tab, setTab] = useState(initialTab);
+ // 本会话内新入库的角色(即时回显,无需刷新即见)。
+ const [sessionCards, setSessionCards] = useState([]);
+
+ // 真源(初始全量)+ 本会话新卡,按 name 去重合并。
+ const characters = useMemo(
+ () => mergeCharacterCards(initialCharacters, sessionCards),
+ [initialCharacters, sessionCards],
+ );
+
+ return (
+
+
+
+ {TABS.map((t) => (
+
+ ))}
+
+
+
+ {tab === "characters" ? (
+
+
+
+ 已入库人物({characters.length})
+
+ {characters.length > 0 ? (
+
+ {characters.map((c, i) => (
+ -
+ {c.name}({c.role})
+
+ ))}
+
+ ) : (
+
+ 暂无入库人物,先在下方生成并入库。
+
+ )}
+
+
+ setSessionCards((prev) => [...prev, ...cards])
+ }
+ />
+
+ ) : null}
+
+ {tab === "world" ? (
+
+
+
+ 已入库世界观({initialWorldEntities.length})
+
+ {initialWorldEntities.length > 0 ? (
+
+ {initialWorldEntities.map((entity, i) => (
+
+
+
+ {entity.name}
+
+
+ {entity.type}
+
+
+ {worldEntityRules(entity).length > 0 ? (
+
+ {worldEntityRules(entity).map((rule, j) => (
+ - {rule}
+ ))}
+
+ ) : (
+ (无显式硬规则)
+ )}
+
+ ))}
+
+ ) : (
+
+ 暂无入库世界观,可在下方生成预览参考。
+
+ )}
+
+
+
+ ) : null}
+
+ {tab === "timeline" ? (
+
+ 时间线为派生视图(P2),将由章节摘要 + 伏笔窗口自动汇总,敬请期待。
+
+ ) : null}
+
+
+
+ );
+}
diff --git a/apps/web/components/command/CommandPalette.tsx b/apps/web/components/command/CommandPalette.tsx
new file mode 100644
index 0000000..786cf60
--- /dev/null
+++ b/apps/web/components/command/CommandPalette.tsx
@@ -0,0 +1,152 @@
+"use client";
+
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { useRouter } from "next/navigation";
+
+import {
+ filterCommands,
+ globalCommands,
+ moveHighlight,
+ projectCommands,
+ type Command,
+} from "@/lib/command/palette";
+
+// 从 pathname 抽取当前 projectId(/projects//...)。
+function projectIdFromPath(pathname: string): string | null {
+ const m = pathname.match(/^\/projects\/([^/]+)/);
+ return m ? m[1] : null;
+}
+
+interface CommandPaletteProps {
+ pathname: string;
+}
+
+// 命令面板(⌘K,UX §7):键盘触发的快速导航/动作。
+// 焦点管理(打开自动聚焦输入框、Esc 关闭、上下选择、回车执行)+ a11y(role=dialog/listbox)。
+export function CommandPalette({ pathname }: CommandPaletteProps) {
+ const router = useRouter();
+ const [open, setOpen] = useState(false);
+ const [query, setQuery] = useState("");
+ const [highlight, setHighlight] = useState(0);
+ const inputRef = useRef(null);
+
+ const projectId = projectIdFromPath(pathname);
+ const commands = useMemo(
+ () =>
+ projectId
+ ? [...projectCommands(projectId), ...globalCommands()]
+ : globalCommands(),
+ [projectId],
+ );
+ const results = useMemo(
+ () => filterCommands(commands, query),
+ [commands, query],
+ );
+
+ const close = useCallback((): void => {
+ setOpen(false);
+ setQuery("");
+ setHighlight(0);
+ }, []);
+
+ const run = useCallback(
+ (cmd: Command | undefined): void => {
+ if (!cmd) return;
+ close();
+ if (cmd.href) router.push(cmd.href);
+ },
+ [close, router],
+ );
+
+ // ⌘K / Ctrl+K 全局开关。
+ useEffect(() => {
+ const onKey = (e: KeyboardEvent): void => {
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
+ e.preventDefault();
+ setOpen((prev) => !prev);
+ }
+ };
+ window.addEventListener("keydown", onKey);
+ return () => window.removeEventListener("keydown", onKey);
+ }, []);
+
+ // 打开时聚焦输入框;切查询重置高亮。
+ useEffect(() => {
+ if (open) inputRef.current?.focus();
+ }, [open]);
+ useEffect(() => {
+ setHighlight(0);
+ }, [query]);
+
+ if (!open) return null;
+
+ const onInputKey = (e: React.KeyboardEvent): void => {
+ if (e.key === "Escape") {
+ e.preventDefault();
+ close();
+ } else if (e.key === "ArrowDown") {
+ e.preventDefault();
+ setHighlight((h) => moveHighlight(h, 1, results.length));
+ } else if (e.key === "ArrowUp") {
+ e.preventDefault();
+ setHighlight((h) => moveHighlight(h, -1, results.length));
+ } else if (e.key === "Enter") {
+ e.preventDefault();
+ run(results[highlight]);
+ }
+ };
+
+ return (
+
+
e.stopPropagation()}
+ >
+
setQuery(e.target.value)}
+ onKeyDown={onInputKey}
+ placeholder="搜索命令或页面…(写本章 / 审稿 / 生成角色 / 跳转伏笔 / 搜设定)"
+ aria-label="命令搜索"
+ aria-controls="command-list"
+ className="w-full border-b border-line bg-bg px-4 py-3 text-sm text-ink outline-none"
+ />
+
+ {results.length === 0 ? (
+ - 无匹配命令
+ ) : (
+ results.map((cmd, i) => (
+ - setHighlight(i)}
+ onClick={() => run(cmd)}
+ className={`flex cursor-pointer items-center justify-between px-4 py-2 text-sm ${
+ i === highlight
+ ? "bg-[var(--color-cinnabar-wash)] text-cinnabar"
+ : "text-ink"
+ }`}
+ >
+ {cmd.title}
+ {cmd.group}
+
+ ))
+ )}
+
+
+
+ );
+}
diff --git a/apps/web/components/command/CommandPaletteMount.tsx b/apps/web/components/command/CommandPaletteMount.tsx
new file mode 100644
index 0000000..986efd2
--- /dev/null
+++ b/apps/web/components/command/CommandPaletteMount.tsx
@@ -0,0 +1,12 @@
+"use client";
+
+import { usePathname } from "next/navigation";
+
+import { CommandPalette } from "./CommandPalette";
+
+// 全局挂载命令面板(⌘K):读当前 pathname 注入项目上下文。
+// 放在 RootLayout 内,对所有页面生效。
+export function CommandPaletteMount() {
+ const pathname = usePathname();
+ return ;
+}
diff --git a/apps/web/components/generation/CharacterCardItem.tsx b/apps/web/components/generation/CharacterCardItem.tsx
new file mode 100644
index 0000000..8228dd3
--- /dev/null
+++ b/apps/web/components/generation/CharacterCardItem.tsx
@@ -0,0 +1,97 @@
+"use client";
+
+import type { CharacterCardView } from "@/lib/api/types";
+
+interface CharacterCardItemProps {
+ card: CharacterCardView;
+ selected: boolean;
+ onToggle: () => void;
+ // 入库前作者就地编辑名/弧光(最小可编辑面,避免重型表单)。
+ onEdit?: (patch: Partial) => void;
+}
+
+// 单张角色预览卡(UX §6.6):勾选入库 + 关键字段就地编辑。
+export function CharacterCardItem({
+ card,
+ selected,
+ onToggle,
+ onEdit,
+}: CharacterCardItemProps) {
+ return (
+
+
+
+ {card.traits && card.traits.length > 0 ? (
+
+ 特质:
+ {card.traits.join("、")}
+
+ ) : null}
+
+
+ 背景:
+ {card.backstory}
+
+
+
+ 弧光:
+ {onEdit ? (
+ onEdit({ arc: e.target.value })}
+ aria-label="人物弧光"
+ className="flex-1 rounded border border-line bg-bg px-2 py-1 text-ink"
+ />
+ ) : (
+ {card.arc}
+ )}
+
+
+ {card.speech_tics && card.speech_tics.length > 0 ? (
+
+ 口癖:
+ {card.speech_tics.join("、")}
+
+ ) : null}
+
+ {card.relations && card.relations.length > 0 ? (
+
+ {card.relations.map((r, i) => (
+ -
+ {r.kind}:{r.name}
+ {r.note ? `(${r.note})` : ""}
+
+ ))}
+
+ ) : null}
+
+ );
+}
diff --git a/apps/web/components/generation/CharacterGenerator.tsx b/apps/web/components/generation/CharacterGenerator.tsx
new file mode 100644
index 0000000..2e3e509
--- /dev/null
+++ b/apps/web/components/generation/CharacterGenerator.tsx
@@ -0,0 +1,171 @@
+"use client";
+
+import { useCallback, useEffect, useMemo, useState } from "react";
+
+import type { CharacterCardView } from "@/lib/api/types";
+import {
+ MAX_CHARACTER_COUNT,
+ MIN_CHARACTER_COUNT,
+ updateCard,
+} from "@/lib/generation/cards";
+import { useCharacterGen } from "@/lib/generation/useCharacterGen";
+import { CharacterCardItem } from "./CharacterCardItem";
+import { ConflictAdjudication } from "./ConflictAdjudication";
+
+interface CharacterGeneratorProps {
+ projectId: string;
+ // 入库成功后回调(如刷新 Codex 本地列表)。
+ onIngested?: (created: string[], cards: CharacterCardView[]) => void;
+}
+
+// 角色生成器(UX §6.6):一句话需求 + 数量 + 定位 → 预览卡 → 选/改 → 入库(处理 409 裁决)。
+export function CharacterGenerator({
+ projectId,
+ onIngested,
+}: CharacterGeneratorProps) {
+ const gen = useCharacterGen();
+ const [brief, setBrief] = useState("");
+ const [count, setCount] = useState(MIN_CHARACTER_COUNT);
+ const [role, setRole] = useState("");
+ // 编辑态卡片(预览返回后拷贝进本地,允许就地改)。
+ const [drafts, setDrafts] = useState([]);
+ const [selected, setSelected] = useState>(new Set());
+
+ // 预览返回 → 同步进本地编辑态 + 默认全选。
+ const syncDrafts = useCallback((cards: CharacterCardView[]) => {
+ setDrafts(cards);
+ setSelected(new Set(cards.map((_, i) => i)));
+ }, []);
+
+ const onGenerate = useCallback(async () => {
+ await gen.generate({ projectId, brief, count, role });
+ }, [gen, projectId, brief, count, role]);
+
+ // gen.cards 变化(新预览)→ 重新同步本地草稿。
+ const previewKey = gen.cards.map((c) => c.name).join("|");
+ useEffect(() => {
+ if (gen.genStatus === "preview") syncDrafts(gen.cards);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [previewKey, gen.genStatus]);
+
+ const toggle = (i: number): void => {
+ setSelected((prev) => {
+ const next = new Set(prev);
+ if (next.has(i)) next.delete(i);
+ else next.add(i);
+ return next;
+ });
+ };
+
+ const selectedCards = useMemo(
+ () => drafts.filter((_, i) => selected.has(i)),
+ [drafts, selected],
+ );
+
+ const doIngest = useCallback(
+ async (acknowledge: boolean) => {
+ const ok = await gen.ingest(projectId, selectedCards, acknowledge);
+ if (ok) {
+ onIngested?.(gen.created, selectedCards);
+ setDrafts([]);
+ setSelected(new Set());
+ }
+ },
+ [gen, projectId, selectedCards, onIngested],
+ );
+
+ const generating = gen.genStatus === "generating";
+ const ingesting = gen.ingestStatus === "ingesting";
+
+ return (
+
+
+
+ {gen.genStatus === "preview" && drafts.length > 0 ? (
+
+
+ 已生成 {drafts.length} 张,选 {selectedCards.length} 张入库(可就地编辑名/弧光)。
+
+
+ {drafts.map((card, i) => (
+ toggle(i)}
+ onEdit={(patch) =>
+ setDrafts((prev) =>
+ prev.map((c, j) => (j === i ? updateCard(c, patch) : c)),
+ )
+ }
+ />
+ ))}
+
+
+ {gen.ingestStatus === "conflict" && gen.conflicts ? (
+
void doIngest(true)}
+ onCancel={() => gen.reset()}
+ />
+ ) : (
+
+ )}
+
+ ) : null}
+
+ {gen.ingestStatus === "done" && gen.created.length > 0 ? (
+ 已入库:{gen.created.join("、")}
+ ) : null}
+
+ );
+}
diff --git a/apps/web/components/generation/ConflictAdjudication.tsx b/apps/web/components/generation/ConflictAdjudication.tsx
new file mode 100644
index 0000000..c21a706
--- /dev/null
+++ b/apps/web/components/generation/ConflictAdjudication.tsx
@@ -0,0 +1,75 @@
+"use client";
+
+import type { IngestConflicts } from "@/lib/generation/cards";
+
+interface ConflictAdjudicationProps {
+ conflicts: IngestConflicts;
+ busy: boolean;
+ // 作者已查看冲突,确认带 acknowledge 重发入库。
+ onAcknowledge: () => void;
+ onCancel: () => void;
+}
+
+// 入库 409 CONFLICT_UNRESOLVED 裁决面板(UX §7 / 不变量#3):
+// 展示 continuity 预检冲突 → 作者裁决(确认入库 = acknowledge 重发 / 取消回去改卡)。
+export function ConflictAdjudication({
+ conflicts,
+ busy,
+ onAcknowledge,
+ onCancel,
+}: ConflictAdjudicationProps) {
+ return (
+
+
+ 发现 {conflicts.conflictCount} 处一致性冲突
+
+
+ 预检发现生成角色与既有真相源冲突。确认无碍可强制入库,或取消后调整角色卡。
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/components/generation/WorldGenerator.tsx b/apps/web/components/generation/WorldGenerator.tsx
new file mode 100644
index 0000000..cec24ff
--- /dev/null
+++ b/apps/web/components/generation/WorldGenerator.tsx
@@ -0,0 +1,78 @@
+"use client";
+
+import { useCallback, useState } from "react";
+
+import { worldEntityRules } from "@/lib/generation/cards";
+import { useWorldGen } from "@/lib/generation/useWorldGen";
+
+interface WorldGeneratorProps {
+ projectId: string;
+}
+
+// 世界观设计器(UX §6.5):一句话需求 → 预览实体卡(type/name/rules)。
+// 本期世界观入库端点未建(仅预览);预览结果供作者参考/复制。
+export function WorldGenerator({ projectId }: WorldGeneratorProps) {
+ const gen = useWorldGen();
+ const [brief, setBrief] = useState("");
+
+ const onGenerate = useCallback(async () => {
+ await gen.generate(projectId, brief);
+ }, [gen, projectId, brief]);
+
+ const generating = gen.status === "generating";
+
+ return (
+
+
+
世界观设计器
+
+
+
+
+ {gen.status === "done" && gen.entities.length > 0 ? (
+
+ {gen.entities.map((entity, i) => (
+
+
+
+ {entity.name}
+
+
+ {entity.type}
+
+
+ {worldEntityRules(entity).length > 0 ? (
+
+ {worldEntityRules(entity).map((rule, j) => (
+ - {rule}
+ ))}
+
+ ) : (
+ (无显式硬规则)
+ )}
+
+ ))}
+
+ ) : null}
+
+ );
+}
diff --git a/apps/web/components/review/ReviewReport.tsx b/apps/web/components/review/ReviewReport.tsx
index c978c9e..8cb1559 100644
--- a/apps/web/components/review/ReviewReport.tsx
+++ b/apps/web/components/review/ReviewReport.tsx
@@ -20,12 +20,16 @@ import {
} from "@/lib/review/history";
import { useAccept } from "@/lib/review/useAccept";
import { useReviewStream } from "@/lib/review/useReviewStream";
-import type { ReviewConflict } from "@/lib/review/sse";
+import type { ReviewConflict, StyleDriftSegment } from "@/lib/review/sse";
+import { normalizeStyleDrift } from "@/lib/style/style";
+import { useToast } from "@/components/Toast";
import { AcceptPanel } from "./AcceptPanel";
import { AnnotatedText } from "./AnnotatedText";
import { ConflictCard } from "./ConflictCard";
import { ForeshadowSuggestions } from "./ForeshadowSuggestions";
import { PacePanel } from "./PacePanel";
+import { StylePanel } from "@/components/style/StylePanel";
+import { RefineView } from "@/components/style/RefineView";
interface ReviewReportProps {
project: ProjectResponse;
@@ -45,6 +49,7 @@ export function ReviewReport({
}: ReviewReportProps) {
const review = useReviewStream();
const accept = useAccept();
+ const toast = useToast();
const [finalText, setFinalText] = useState(initialDraft);
const [drafts, setDrafts] = useState(() =>
@@ -52,6 +57,10 @@ export function ReviewReport({
);
const [focusedIndex, setFocusedIndex] = useState(null);
const [missing, setMissing] = useState>(new Set());
+ // 回炉中的漂移段(null=未打开 RefineView)。
+ const [refineSegment, setRefineSegment] = useState(
+ null,
+ );
const seededRef = useRef(false);
// 进页一次性把历史留痕(冲突 + 伏笔建议 + 节奏)种入流状态(无需重审即可裁决/查看)。
@@ -67,26 +76,33 @@ export function ReviewReport({
() => normalizePace(initialReview),
[initialReview],
);
+ const seededStyle = useMemo(
+ () => normalizeStyleDrift(initialReview),
+ [initialReview],
+ );
useEffect(() => {
if (seededRef.current) return;
seededRef.current = true;
const hasSeed =
seededConflicts.length > 0 ||
seededForeshadow.length > 0 ||
- seededPace !== null;
+ seededPace !== null ||
+ seededStyle !== null;
if (hasSeed) {
review.seed({
conflicts: seededConflicts,
foreshadow: seededForeshadow,
pace: seededPace,
+ style: seededStyle,
});
}
- }, [review, seededConflicts, seededForeshadow, seededPace]);
+ }, [review, seededConflicts, seededForeshadow, seededPace, seededStyle]);
// 当前生效冲突 = 流状态(重审后即时更新,否则种入的历史)。
const conflicts: ReviewConflict[] = review.state.conflicts;
const foreshadow = review.state.foreshadow;
const pace = review.state.pace;
+ const style = review.state.style;
const sectionStatus = (name: string): string | undefined =>
review.state.sections.find((s) => s.name === name)?.status;
@@ -147,6 +163,26 @@ export function ReviewReport({
}
};
+ // 漂移段 idx → 终稿对应段正文(按空行切段;越界则空串)。
+ const segmentText = (idx: number): string => {
+ const paras = finalText.split(/\n{2,}/);
+ return paras[idx]?.trim() ?? "";
+ };
+
+ // 采纳:把重写段替换终稿中的原段(首个匹配),合入终稿(经既有 draft 路径)。
+ const onAdopt = (original: string, refined: string): void => {
+ setFinalText((prev) => {
+ const idx = prev.indexOf(original);
+ if (idx === -1) {
+ toast("未在终稿中找到原段,请手动合入。", "error");
+ return prev;
+ }
+ return prev.slice(0, idx) + refined + prev.slice(idx + original.length);
+ });
+ setRefineSegment(null);
+ toast("已合入终稿,记得验收时复核。", "success");
+ };
+
const resolved = allResolved(drafts);
const unresolved = unresolvedIndices(drafts).length;
const reviewing = review.isReviewing;
@@ -279,6 +315,20 @@ export function ReviewReport({
pace={pace}
incomplete={sectionStatus("pace") === "incomplete"}
/>
+
+ {refineSegment !== null ? (
+ setRefineSegment(null)}
+ />
+ ) : null}
diff --git a/apps/web/components/rules/RulesPage.tsx b/apps/web/components/rules/RulesPage.tsx
new file mode 100644
index 0000000..6418636
--- /dev/null
+++ b/apps/web/components/rules/RulesPage.tsx
@@ -0,0 +1,107 @@
+"use client";
+
+import { useMemo, useState } from "react";
+
+import { AppShell } from "@/components/AppShell";
+import type { ProjectResponse, RuleView } from "@/lib/api/types";
+import {
+ RULE_LEVELS,
+ RULE_LEVEL_LABELS,
+ groupByLevel,
+ type RuleLevel,
+} from "@/lib/rules/rules";
+import { useRules } from "@/lib/rules/useRules";
+
+interface RulesPageProps {
+ project: ProjectResponse;
+ initialRules: RuleView[];
+}
+
+// 规则页(UX §7):四级规则列表 + 新增(乐观 + 回滚)。
+export function RulesPage({ project, initialRules }: RulesPageProps) {
+ const { items, busy, add } = useRules(initialRules);
+ const [level, setLevel] = useState("project");
+ const [content, setContent] = useState("");
+ const groups = useMemo(() => groupByLevel(items), [items]);
+
+ const onSubmit = async (e: React.FormEvent): Promise => {
+ e.preventDefault();
+ const ok = await add(project.id, level, content);
+ if (ok) setContent("");
+ };
+
+ return (
+
+
+
+
+
+ {RULE_LEVELS.map((lv) => (
+
+
+ {RULE_LEVEL_LABELS[lv]}
+
+ {groups[lv].length}
+
+
+ {groups[lv].length === 0 ? (
+ (暂无)
+ ) : (
+
+ {groups[lv].map((rule, i) => (
+ -
+ {rule.content}
+
+ ))}
+
+ )}
+
+ ))}
+
+
+
+ );
+}
diff --git a/apps/web/components/settings/KimiCodeOauth.tsx b/apps/web/components/settings/KimiCodeOauth.tsx
new file mode 100644
index 0000000..45175ed
--- /dev/null
+++ b/apps/web/components/settings/KimiCodeOauth.tsx
@@ -0,0 +1,127 @@
+"use client";
+
+import { authOpenUrl, formatExpiresAt } from "@/lib/settings/kimiOauth";
+import { useKimiOauth } from "@/lib/settings/useKimiOauth";
+
+interface KimiCodeOauthProps {
+ initialConnected: boolean;
+ initialExpiresAt: string | null;
+}
+
+// Kimi Code(OAuth 订阅 plan)连接区(K1.4)。
+// 与 API-key 凭据行分离:无 key 输入,连接经 device flow(显示 user_code + 开授权页 + 轮询 job)。
+export function KimiCodeOauth({
+ initialConnected,
+ initialExpiresAt,
+}: KimiCodeOauthProps) {
+ const oauth = useKimiOauth({ initialConnected, initialExpiresAt });
+ const { phase, device, expiresAt, busy, error } = oauth;
+ const expiresLabel = formatExpiresAt(expiresAt);
+
+ return (
+
+ Kimi Code(OAuth)
+
+ 订阅 plan 经 OAuth 设备授权连接(无需 API Key)。连接后可在上方档位路由里选用
+ kimi-code · kimi-for-coding。
+
+
+
+
+
+
+ {phase === "connected" ? "已连接" : "未连接"}
+
+ {phase === "connected" && expiresLabel ? (
+
+ 凭据有效至 {expiresLabel}(到期自动刷新)
+
+ ) : null}
+
+
+ {phase === "connected" ? (
+
+ ) : (
+
+ )}
+
+
+
+ {phase === "awaiting" && device ? (
+
+ ) : null}
+
+ {phase === "error" && error ? (
+
+ {error}
+
+ ) : null}
+
+
+ );
+}
+
+interface DeviceInstructionsProps {
+ userCode: string;
+ openUrl: string;
+}
+
+// 设备授权指引:突出展示 user_code(可读、可选、可聚焦)+ 打开授权页按钮。
+function DeviceInstructions({ userCode, openUrl }: DeviceInstructionsProps) {
+ const openAuth = (): void => {
+ if (typeof window !== "undefined") {
+ window.open(openUrl, "_blank", "noopener,noreferrer");
+ }
+ };
+
+ return (
+
+
+ 请在浏览器中打开授权页,并确认页面显示的设备码与下方一致:
+
+
+
+
+ 授权完成后本页会自动检测连接状态(设备码过期后请重新连接)。
+
+
+ );
+}
diff --git a/apps/web/components/settings/ProvidersSettings.tsx b/apps/web/components/settings/ProvidersSettings.tsx
index 837c0d4..c359bb9 100644
--- a/apps/web/components/settings/ProvidersSettings.tsx
+++ b/apps/web/components/settings/ProvidersSettings.tsx
@@ -4,15 +4,25 @@ import { useState } from "react";
import { api } from "@/lib/api/client";
import { useToast } from "@/components/Toast";
-import { KNOWN_PROVIDERS, TIER_LABELS } from "@/lib/settings/providers";
+import { KimiCodeOauth } from "@/components/settings/KimiCodeOauth";
+import {
+ API_KEY_PROVIDERS,
+ KNOWN_PROVIDERS,
+ TIER_LABELS,
+ applyProviderChange,
+ draftsToRoutingInput,
+ toRoutingDrafts,
+ type RoutingDraft,
+} from "@/lib/settings/providers";
import type {
CapabilitiesView,
ProvidersResponse,
- TierRoutingView,
} from "@/lib/api/types";
interface ProvidersSettingsProps {
initial: ProvidersResponse;
+ // Kimi Code OAuth 连接态(GET .../oauth/status,进页 Server Component 取)。
+ kimiOauth: { connected: boolean; expiresAt: string | null };
}
interface TestResult {
@@ -20,11 +30,17 @@ interface TestResult {
capabilities: CapabilitiesView;
}
-// 设置页主体(UX §6.10):档位路由(只读展示)+ 凭据行(脱敏 / 添加更新 / 测试连接)。
-export function ProvidersSettings({ initial }: ProvidersSettingsProps) {
+// 设置页主体(UX §6.10):档位路由(可编辑)+ API-key 凭据行 + Kimi Code OAuth 连接区。
+export function ProvidersSettings({
+ initial,
+ kimiOauth,
+}: ProvidersSettingsProps) {
const toast = useToast();
const [providers, setProviders] = useState(initial.providers ?? []);
- const tierRouting: TierRoutingView[] = initial.tier_routing ?? [];
+ const [routing, setRouting] = useState(
+ toRoutingDrafts(initial.tier_routing ?? []),
+ );
+ const [savingRouting, setSavingRouting] = useState(false);
const [drafts, setDrafts] = useState>({});
const [savingId, setSavingId] = useState(null);
const [testingId, setTestingId] = useState(null);
@@ -33,6 +49,35 @@ export function ProvidersSettings({ initial }: ProvidersSettingsProps) {
const maskedFor = (id: string): string | null =>
providers.find((p) => p.provider === id)?.masked_key ?? null;
+ const updateRouting = (tier: string, providerId: string): void => {
+ setRouting((prev) =>
+ prev.map((d) =>
+ d.tier === tier ? applyProviderChange(d, providerId) : d,
+ ),
+ );
+ };
+
+ const updateRoutingModel = (tier: string, model: string): void => {
+ setRouting((prev) =>
+ prev.map((d) => (d.tier === tier ? { ...d, model } : d)),
+ );
+ };
+
+ const saveRouting = async (): Promise => {
+ setSavingRouting(true);
+ const { data, error } = await api.PUT("/settings/providers", {
+ body: { tier_routing: draftsToRoutingInput(routing) },
+ });
+ setSavingRouting(false);
+ if (error || !data) {
+ toast("保存档位路由失败,请重试", "error");
+ return;
+ }
+ setProviders(data.providers ?? []);
+ setRouting(toRoutingDrafts(data.tier_routing ?? []));
+ toast("档位路由已保存", "success");
+ };
+
const saveCredential = async (providerId: string): Promise => {
const apiKey = (drafts[providerId] ?? "").trim();
if (!apiKey) {
@@ -74,34 +119,61 @@ export function ProvidersSettings({ initial }: ProvidersSettingsProps) {
+
+
提供商凭据
{providers.length === 0 ? (
@@ -111,7 +183,7 @@ export function ProvidersSettings({ initial }: ProvidersSettingsProps) {
) : null}
- {KNOWN_PROVIDERS.map((prov) => {
+ {API_KEY_PROVIDERS.map((prov) => {
const masked = maskedFor(prov.id);
const result = results[prov.id];
return (
diff --git a/apps/web/components/skills/SkillsPage.tsx b/apps/web/components/skills/SkillsPage.tsx
new file mode 100644
index 0000000..9dc3abb
--- /dev/null
+++ b/apps/web/components/skills/SkillsPage.tsx
@@ -0,0 +1,77 @@
+import { AppShell } from "@/components/AppShell";
+import type { ProjectResponse, SkillView } from "@/lib/api/types";
+import { groupByScope, scopeLabel, tierLabel } from "@/lib/skills/skills";
+
+interface SkillsPageProps {
+ project: ProjectResponse;
+ skills: SkillView[];
+}
+
+// 技能库(UX §7):只读注册表视图(name/scope/tier/reads/writes)。Server Component(纯读)。
+export function SkillsPage({ project, skills }: SkillsPageProps) {
+ const groups = groupByScope(skills);
+
+ return (
+
+
+
技能库
+
+ 声明式 Skill 注册表:每个技能声明能力档位与可读/写的表(沙箱白名单,越权产出丢弃并审计)。
+
+ {groups.length === 0 ? (
+
(暂无已注册技能)
+ ) : (
+ groups.map((group) => (
+
+
+ {scopeLabel(group.scope)}
+
+ {group.skills.length}
+
+
+
+ {group.skills.map((skill) => (
+ -
+
+ {skill.name}
+
+ {tierLabel(skill.tier)}
+
+ {skill.genre ? (
+
+ {skill.genre}
+
+ ) : null}
+
+
+
+ 读:
+ {skill.reads && skill.reads.length > 0
+ ? skill.reads.join("、")
+ : "—"}
+
+
+ 写:
+ {skill.writes && skill.writes.length > 0
+ ? skill.writes.join("、")
+ : "—"}
+
+
+
+ ))}
+
+
+ ))
+ )}
+
+
+ );
+}
diff --git a/apps/web/components/style/FingerprintView.tsx b/apps/web/components/style/FingerprintView.tsx
new file mode 100644
index 0000000..5e0cdec
--- /dev/null
+++ b/apps/web/components/style/FingerprintView.tsx
@@ -0,0 +1,65 @@
+"use client";
+
+import type { Fingerprint } from "@/lib/style/style";
+
+interface FingerprintViewProps {
+ fingerprint: Fingerprint | null;
+}
+
+// 文风指纹展示(UX §6.9):16 维行(维度名 + 值 + 可展开原文证据)。
+export function FingerprintView({ fingerprint }: FingerprintViewProps) {
+ if (fingerprint === null) {
+ return (
+
+ 尚未学习文风。在左侧粘贴样本正文(或选文件)后点「学习文风」。
+
+ );
+ }
+
+ return (
+
+
+
文风指纹
+
+ v{fingerprint.version} · {fingerprint.dimensions.length} 维
+
+
+ {fingerprint.dimensions.length === 0 ? (
+
指纹无维度数据。
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/apps/web/components/style/RefineView.tsx b/apps/web/components/style/RefineView.tsx
new file mode 100644
index 0000000..a226696
--- /dev/null
+++ b/apps/web/components/style/RefineView.tsx
@@ -0,0 +1,100 @@
+"use client";
+
+import { useRefine } from "@/lib/style/useRefine";
+import { useEffect } from "react";
+
+interface RefineViewProps {
+ projectId: string;
+ chapterNo: number;
+ // 选中漂移段的原文(从终稿按段切出;空则提示先编辑终稿)。
+ segment: string;
+ // 采纳:把 refined 合入终稿(经审稿页 finalText → 既有 draft 自动保存路径)。
+ onAdopt: (original: string, refined: string) => void;
+ onClose: () => void;
+}
+
+// 回炉 diff(UX §8.3):POST .../refine → 展示原段/重写段对比 → 采纳合入正文。
+// 不另写库(不变量#3);采纳经既有 draft 自动保存合入。
+export function RefineView({
+ projectId,
+ chapterNo,
+ segment,
+ onAdopt,
+ onClose,
+}: RefineViewProps) {
+ const refiner = useRefine();
+
+ // 进入即触发回炉(段非空)。
+ useEffect(() => {
+ if (segment.trim().length > 0) {
+ void refiner.refine(projectId, chapterNo, segment);
+ }
+ // 仅在段变化时触发。
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [segment, projectId, chapterNo]);
+
+ return (
+
+
+
回炉对比
+
+
+
+ {segment.trim().length === 0 ? (
+
该段在终稿中为空,请先在终稿中保留该段正文。
+ ) : refiner.status === "refining" ? (
+
+ 回炉中…
+
+ ) : refiner.status === "error" ? (
+
回炉失败,请稍后重试。
+ ) : refiner.result ? (
+
+
+
原段
+
+ {refiner.result.original}
+
+
+
+
重写段
+
+ {refiner.result.refined}
+
+
+
+
+
+
+
+ ) : null}
+
+ );
+}
diff --git a/apps/web/components/style/StylePage.tsx b/apps/web/components/style/StylePage.tsx
new file mode 100644
index 0000000..e9c4089
--- /dev/null
+++ b/apps/web/components/style/StylePage.tsx
@@ -0,0 +1,48 @@
+"use client";
+
+import { AppShell } from "@/components/AppShell";
+import type { ProjectResponse } from "@/lib/api/types";
+import { useStyleLearn } from "@/lib/style/useStyleLearn";
+import type { Fingerprint, StyleLearnMode } from "@/lib/style/style";
+import { FingerprintView } from "./FingerprintView";
+import { StyleUpload } from "./StyleUpload";
+
+interface StylePageProps {
+ project: ProjectResponse;
+ initialFingerprint: Fingerprint | null;
+}
+
+// 文风页(UX §6.9):左侧上传样本学文风,右侧展示 16 维指纹 + 证据。
+export function StylePage({ project, initialFingerprint }: StylePageProps) {
+ const learn = useStyleLearn(initialFingerprint);
+
+ const onLearn = (samples: string[], mode: StyleLearnMode): void => {
+ void learn.learn(project.id, samples, mode);
+ };
+
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/web/components/style/StylePanel.tsx b/apps/web/components/style/StylePanel.tsx
new file mode 100644
index 0000000..b796e8a
--- /dev/null
+++ b/apps/web/components/style/StylePanel.tsx
@@ -0,0 +1,87 @@
+"use client";
+
+import type { StyleDriftReport, StyleDriftSegment } from "@/lib/review/sse";
+
+interface StylePanelProps {
+ style: StyleDriftReport | null;
+ incomplete: boolean;
+ // 选中段做回炉(打开 RefineView)。
+ onRefine: (segment: StyleDriftSegment) => void;
+}
+
+// 整体相似度的四分圆字符档(◔◑◕●),按 score 映射(UX §6.4 ③)。
+const QUARTER_CHARS = ["○", "◔", "◑", "◕", "●"] as const;
+
+function quarterChar(score: number): string {
+ const clamped = Math.min(100, Math.max(0, score));
+ const idx = Math.round((clamped / 100) * (QUARTER_CHARS.length - 1));
+ return QUARTER_CHARS[idx] ?? QUARTER_CHARS[0];
+}
+
+// 文风审区(UX §6.4 ③):整体相似度 ◔% + 漂移段列表,每段可一键回炉。只读建议。
+export function StylePanel({ style, incomplete, onRefine }: StylePanelProps) {
+ return (
+
+
+ 文风 (style-auditor)
+
+ {incomplete ? (
+ ◌ 未完成
+ ) : style === null ? (
+
+ 无文风报告(学文风后第四审才能打分)。
+
+ ) : (
+
+
+
+ {quarterChar(style.score)}
+
+
+ 整体相似度 {style.score}%
+
+
+
+ {style.segments.length === 0 ? (
+
✓ 无明显文风漂移
+ ) : (
+
+ )}
+
+ )}
+
+ );
+}
diff --git a/apps/web/components/style/StyleUpload.tsx b/apps/web/components/style/StyleUpload.tsx
new file mode 100644
index 0000000..a13abd3
--- /dev/null
+++ b/apps/web/components/style/StyleUpload.tsx
@@ -0,0 +1,110 @@
+"use client";
+
+import { useCallback, useState, type ChangeEvent } from "react";
+
+import { hasUsableSamples, type StyleLearnMode } from "@/lib/style/style";
+import { useToast } from "@/components/Toast";
+
+interface StyleUploadProps {
+ busy: boolean;
+ pollStatus: "idle" | "polling" | "done" | "error";
+ progress: number;
+ // 是否已有指纹(决定默认 mode = update)。
+ hasFingerprint: boolean;
+ onLearn: (samples: string[], mode: StyleLearnMode) => void;
+}
+
+// 学文风输入(UX §6.9):多段文本粘贴 + 文件选择(在浏览器读成文本)。
+// 样本以正文文本入 body(无对象存储,确认决策)。
+export function StyleUpload({
+ busy,
+ pollStatus,
+ progress,
+ hasFingerprint,
+ onLearn,
+}: StyleUploadProps) {
+ const [text, setText] = useState("");
+ const toast = useToast();
+
+ // 读取选中的文本文件,追加到文本框(多文件用空行分隔)。
+ const onFiles = useCallback(
+ async (e: ChangeEvent): Promise => {
+ const files = Array.from(e.target.files ?? []);
+ if (files.length === 0) return;
+ try {
+ const contents = await Promise.all(files.map((f) => f.text()));
+ setText((prev) => {
+ const joined = contents.join("\n\n");
+ return prev.trim().length > 0 ? `${prev}\n\n${joined}` : joined;
+ });
+ } catch {
+ toast("读取文件失败,请改用粘贴。", "error");
+ } finally {
+ e.target.value = ""; // 允许重复选同一文件。
+ }
+ },
+ [toast],
+ );
+
+ const submit = (): void => {
+ // 以空行切分成多段样本(对齐后端 samples:list[str])。
+ const samples = text
+ .split(/\n{2,}/)
+ .map((s) => s.trim())
+ .filter((s) => s.length > 0);
+ if (!hasUsableSamples(samples)) {
+ toast("请粘贴或选择至少一段样本正文。", "error");
+ return;
+ }
+ onLearn(samples, hasFingerprint ? "update" : "create");
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ {pollStatus === "polling" ? (
+
+ 提取文风指纹中…({progress}%)
+
+ ) : pollStatus === "error" ? (
+
学文风失败,请稍后重试。
+ ) : null}
+
+ );
+}
diff --git a/apps/web/components/workbench/Workbench.tsx b/apps/web/components/workbench/Workbench.tsx
index e625337..2ed2b03 100644
--- a/apps/web/components/workbench/Workbench.tsx
+++ b/apps/web/components/workbench/Workbench.tsx
@@ -7,20 +7,25 @@ import { AppShell } from "@/components/AppShell";
import type { ProjectResponse } from "@/lib/api/types";
import { useAutosave } from "@/lib/autosave/useAutosave";
import { useDraftStream } from "@/lib/stream/useDraftStream";
+import { WORKBENCH_CHAPTER_NO } from "@/lib/workbench/chapter";
import { ChapterList } from "./ChapterList";
import { ChapterAssistant } from "./ChapterAssistant";
import { Editor } from "./Editor";
interface WorkbenchProps {
project: ProjectResponse;
+ // RSC 种入的已存草稿正文(GET .../draft);无草稿(404)→ ""(空编辑器)。
+ initialText?: string;
}
// M1:单章工作台。章节列表为占位(无章节列表端点);默认第 1 章。
-const M1_CHAPTER_NO = 1;
+// WORKBENCH_CHAPTER_NO 移到 lib/workbench/chapter.ts(非客户端模块),供 Server Component 安全导入。
-export function Workbench({ project }: WorkbenchProps) {
- const chapterNo = M1_CHAPTER_NO;
- const [text, setText] = useState("");
+export function Workbench({ project, initialText = "" }: WorkbenchProps) {
+ const chapterNo = WORKBENCH_CHAPTER_NO;
+ // 用已存草稿作初值;流式开始后由下方 effect 覆盖(流不会被初值反扑——
+ // stream.state.text 起始为空,仅当 streaming/done/aborted 且变化才写回)。
+ const [text, setText] = useState(initialText);
const autosave = useAutosave(project.id, chapterNo);
const stream = useDraftStream();
const lastStreamText = useRef("");
@@ -137,7 +142,7 @@ function Toolbar({
)}
- 字数 {wordCount.toLocaleString()}
+ 字数 {wordCount.toLocaleString("en-US")}
;
export interface components {
@@ -363,6 +624,141 @@ export interface components {
*/
thinking: boolean;
};
+ /**
+ * CharacterCardView
+ * @description 单张角色卡(贴 ww_agents.CharacterCard;预览 + 入库请求共用形)。
+ */
+ CharacterCardView: {
+ /**
+ * Name
+ * @description 角色名
+ */
+ name: string;
+ /**
+ * Role
+ * @description 角色定位
+ */
+ role: string;
+ /**
+ * Traits
+ * @description 性格特质清单
+ */
+ traits?: string[];
+ /**
+ * Backstory
+ * @description 背景故事
+ */
+ backstory: string;
+ /**
+ * Arc
+ * @description 人物弧光(一句话)
+ */
+ arc: string;
+ /**
+ * Speech Tics
+ * @description 口癖/语言风格
+ */
+ speech_tics?: string[];
+ /**
+ * Tags
+ * @description 人设标签/萌点
+ */
+ tags?: string[];
+ /**
+ * Relations
+ * @description 关系网
+ */
+ relations?: components["schemas"]["CharacterRelationView"][];
+ };
+ /**
+ * CharacterGenPreviewResponse
+ * @description 角色生成预览(不持久化;作者确认后经 POST /characters 入库)。
+ */
+ CharacterGenPreviewResponse: {
+ /** Cards */
+ cards?: components["schemas"]["CharacterCardView"][];
+ };
+ /**
+ * CharacterGenerateRequest
+ * @description POST /projects/:id/characters/generate:据需求生成角色卡(预览,群像防雷同)。
+ */
+ CharacterGenerateRequest: {
+ /**
+ * Brief
+ * @description 角色生成需求
+ */
+ brief: string;
+ /**
+ * Count
+ * @description 生成数量
+ * @default 1
+ */
+ count: number;
+ /**
+ * Role
+ * @description 角色定位(主角/CP/对手/导师/工具人 等)
+ */
+ role?: string | null;
+ };
+ /**
+ * CharacterIngestRequest
+ * @description POST /projects/:id/characters:把作者确认的角色卡入库。
+ *
+ * `acknowledge_conflicts`:作者已查看并接受预检冲突时置 `true`,越过 continuity gate
+ * (仿 accept 的冲突裁决:不静默入库,须作者确认;默认 false → 有冲突即 409,见端点)。
+ */
+ CharacterIngestRequest: {
+ /**
+ * Cards
+ * @description 待入库的角色卡(至少 1 张)
+ */
+ cards: components["schemas"]["CharacterCardView"][];
+ /**
+ * Acknowledge Conflicts
+ * @description 作者已知悉并接受 continuity 冲突 → 放行入库
+ * @default false
+ */
+ acknowledge_conflicts: boolean;
+ };
+ /**
+ * CharacterIngestResponse
+ * @description 角色入库结果(201):写入的角色 + 被白名单丢弃的越权表(审计)。
+ */
+ CharacterIngestResponse: {
+ /**
+ * Created
+ * @description 写入的角色名(按入参顺序)
+ */
+ created?: string[];
+ /**
+ * Rejected Tables
+ * @description 被权限白名单丢弃的越权写表名(审计;正常为空)
+ */
+ rejected_tables?: string[];
+ };
+ /**
+ * CharacterListResponse
+ * @description GET /projects/:id/characters:已入库角色全量列表(设定库 Codex 真源)。
+ *
+ * DB JSONB dict 列 → API list/str 反向解包(入库形变的逆向,见 character_repo):
+ * `{"items":[...]}`→list、`{"text":...}`→str;tags/relations 直落 list。
+ */
+ CharacterListResponse: {
+ /** Characters */
+ characters?: components["schemas"]["CharacterCardView"][];
+ };
+ /**
+ * CharacterRelationView
+ * @description 单条人物关系(贴 ww_agents.CharacterRelation)。
+ */
+ CharacterRelationView: {
+ /** Name */
+ name: string;
+ /** Kind */
+ kind: string;
+ /** Note */
+ note?: string | null;
+ };
/**
* ConflictDecision
* @description 对最近一次审稿留痕里**某个冲突**(按其在 conflicts 列表的下标定位)的裁决。
@@ -414,6 +810,32 @@ export interface components {
/** Text */
text: string;
};
+ /**
+ * DraftView
+ * @description GET .../draft:回灌已保存草稿(含正文,供工作台重载编辑器)。
+ *
+ * 与 PUT 的 `DraftResponse` 同元信息,**额外带 `content`**——读侧需要正文以重建编辑器,
+ * 保存侧不回灌正文故不带。`length` 仍按 content 字符数派生。
+ */
+ DraftView: {
+ /**
+ * Project Id
+ * Format: uuid
+ */
+ project_id: string;
+ /** Chapter No */
+ chapter_no: number;
+ /** Volume */
+ volume: number;
+ /** Status */
+ status: string;
+ /** Version */
+ version: number;
+ /** Content */
+ content: string;
+ /** Length */
+ length: number;
+ };
/**
* ForeshadowBoardResponse
* @description GET /projects/:id/foreshadow?status=:伏笔看板(四泳道 + OVERDUE 字段齐)。
@@ -536,6 +958,48 @@ export interface components {
/** Detail */
detail?: components["schemas"]["ValidationError"][];
};
+ /**
+ * OAuthDisconnectResponse
+ * @description 断开:是否删到凭据行。
+ */
+ OAuthDisconnectResponse: {
+ /** Disconnected */
+ disconnected: boolean;
+ };
+ /**
+ * OAuthStartResponse
+ * @description device flow 启动:返回 job_id(轮询 `GET /jobs/{id}`)+ 用户面展示信息。
+ *
+ * 前端展示 `user_code`、打开 `verification_uri`(或 `verification_uri_complete`),并按
+ * `interval` 轮询 job 直到 `done`(已连接)/`failed`(过期/拒绝)。**无 token**。
+ */
+ OAuthStartResponse: {
+ /**
+ * Job Id
+ * Format: uuid
+ */
+ job_id: string;
+ /** User Code */
+ user_code: string;
+ /** Verification Uri */
+ verification_uri: string;
+ /** Verification Uri Complete */
+ verification_uri_complete?: string | null;
+ /** Expires In */
+ expires_in: number;
+ /** Interval */
+ interval: number;
+ };
+ /**
+ * OAuthStatusResponse
+ * @description 连接状态:是否已连接 + access token 过期时刻(ISO8601;**无 token 本体**)。
+ */
+ OAuthStatusResponse: {
+ /** Connected */
+ connected: boolean;
+ /** Expires At */
+ expires_at?: string | null;
+ };
/**
* OutlineChapterView
* @description 单章大纲视图:章号 + 卷 + 节拍 + 伏笔窗口。
@@ -665,6 +1129,26 @@ export interface components {
/** Tier Routing */
tier_routing?: components["schemas"]["TierRoutingInput"][];
};
+ /**
+ * RefineRequest
+ * @description 回炉:重写选中段(可选改写指令)。
+ */
+ RefineRequest: {
+ /** Segment */
+ segment: string;
+ /** Instruction */
+ instruction?: string | null;
+ };
+ /**
+ * RefineResponse
+ * @description 回炉结果:原段 + 重写段(不写库,作者采纳经既有 draft 自动保存合入)。
+ */
+ RefineResponse: {
+ /** Original */
+ original: string;
+ /** Refined */
+ refined: string;
+ };
/**
* ReviewHistoryItem
* @description 单条审稿留痕(GET .../reviews 历史项;snake_case)。
@@ -725,6 +1209,114 @@ export interface components {
/** Draft */
draft?: string | null;
};
+ /**
+ * RuleCreateRequest
+ * @description POST /projects/:id/rules:新增一条规则。
+ */
+ RuleCreateRequest: {
+ /**
+ * Level
+ * @description 规则级别(global/genre/style/project,越具体越优先)
+ * @enum {string}
+ */
+ level: "global" | "genre" | "style" | "project";
+ /**
+ * Content
+ * @description 规则正文
+ */
+ content: string;
+ };
+ /**
+ * RuleListResponse
+ * @description GET /projects/:id/rules:规则列表(规则页)。
+ */
+ RuleListResponse: {
+ /** Rules */
+ rules?: components["schemas"]["RuleView"][];
+ };
+ /**
+ * RuleView
+ * @description 规则视图(创建后回显;snake_case)。
+ */
+ RuleView: {
+ /** Level */
+ level: string;
+ /** Content */
+ content: string;
+ };
+ /**
+ * SkillListResponse
+ * @description GET /skills:技能库列表(按 name 升序)。
+ */
+ SkillListResponse: {
+ /** Skills */
+ skills?: components["schemas"]["SkillView"][];
+ };
+ /**
+ * SkillView
+ * @description 单个 skill 的声明式视图(技能库 UI)。
+ */
+ SkillView: {
+ /** Name */
+ name: string;
+ /**
+ * Scope
+ * @description builtin / custom / community
+ */
+ scope: string;
+ /**
+ * Tier
+ * @description 能力档位(writer / analyst / light)
+ */
+ tier: string;
+ /** Reads */
+ reads?: string[];
+ /** Writes */
+ writes?: string[];
+ /** Genre */
+ genre?: string | null;
+ };
+ /**
+ * StyleFingerprintResponse
+ * @description 最新文风指纹(`GET /style`):完整 16 维 + 证据 + 版本(对齐 UX §6.9)。
+ */
+ StyleFingerprintResponse: {
+ /** Dimensions */
+ dimensions?: {
+ [key: string]: unknown;
+ };
+ /** Evidence */
+ evidence?: {
+ [key: string]: unknown;
+ };
+ /** Version */
+ version: number;
+ };
+ /**
+ * StyleLearnRequest
+ * @description 学文风:上传样本正文(前端可读文件转文本)+ 模式(首学 / 更新)。
+ */
+ StyleLearnRequest: {
+ /** Samples */
+ samples: string[];
+ /**
+ * Mode
+ * @default create
+ * @enum {string}
+ */
+ mode: "create" | "update";
+ };
+ /**
+ * StyleLearnResponse
+ * @description 学文风受理:返回 job_id(长任务,走 `GET /jobs/{id}` 轮询)。
+ */
+ StyleLearnResponse: {
+ /**
+ * Job Id
+ * Format: uuid
+ */
+ job_id: string;
+ };
/**
* TestConnectionRequest
* @description POST /test:最小探测请求。
@@ -785,6 +1377,56 @@ export interface components {
/** Context */
ctx?: Record;
};
+ /**
+ * WorldEntityCardView
+ * @description 单个世界观实体卡(贴 ww_agents.WorldEntityCard;预览 + 入库共用形)。
+ */
+ WorldEntityCardView: {
+ /**
+ * Type
+ * @description 实体类型(势力 / 地理 / 力量体系 / 物品 / 概念 等)
+ */
+ type: string;
+ /**
+ * Name
+ * @description 实体名
+ */
+ name: string;
+ /**
+ * Rules
+ * @description 该实体的硬规则清单
+ */
+ rules?: string[];
+ };
+ /**
+ * WorldEntityListResponse
+ * @description GET /projects/:id/world_entities:已入库世界观实体全量列表(设定库 Codex 真源)。
+ *
+ * DB JSONB dict 列 `{"rules":[...]}`→裸 list(worldbuilder 形变的逆向)。
+ */
+ WorldEntityListResponse: {
+ /** World Entities */
+ world_entities?: components["schemas"]["WorldEntityCardView"][];
+ };
+ /**
+ * WorldGenPreviewResponse
+ * @description 世界观生成预览(不持久化;作者确认后另走入库,本期入库端点为角色)。
+ */
+ WorldGenPreviewResponse: {
+ /** Entities */
+ entities?: components["schemas"]["WorldEntityCardView"][];
+ };
+ /**
+ * WorldGenerateRequest
+ * @description POST /projects/:id/world/generate:据需求生成世界观实体(预览)。
+ */
+ WorldGenerateRequest: {
+ /**
+ * Brief
+ * @description 世界观生成需求(题材/设定方向/约束)
+ */
+ brief: string;
+ };
};
responses: never;
parameters: never;
@@ -955,6 +1597,38 @@ export interface operations {
};
};
};
+ get_draft_projects__project_id__chapters__chapter_no__draft_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ chapter_no: number;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["DraftView"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
save_draft_projects__project_id__chapters__chapter_no__draft_put: {
parameters: {
query?: never;
@@ -1231,6 +1905,37 @@ export interface operations {
};
};
};
+ get_outline_projects__project_id__outline_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["OutlineResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
generate_outline_projects__project_id__outline_post: {
parameters: {
query?: never;
@@ -1266,6 +1971,361 @@ export interface operations {
};
};
};
+ list_rules_projects__project_id__rules_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["RuleListResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ create_rule_projects__project_id__rules_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["RuleCreateRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 201: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["RuleView"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ get_style_projects__project_id__style_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["StyleFingerprintResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ learn_style_projects__project_id__style_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["StyleLearnRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 202: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["StyleLearnResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ refine_segment_projects__project_id__chapters__chapter_no__refine_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ chapter_no: number;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["RefineRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["RefineResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ generate_world_projects__project_id__world_generate_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["WorldGenerateRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["WorldGenPreviewResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ generate_characters_projects__project_id__characters_generate_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["CharacterGenerateRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["CharacterGenPreviewResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_characters_projects__project_id__characters_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["CharacterListResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ ingest_characters_projects__project_id__characters_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": components["schemas"]["CharacterIngestRequest"];
+ };
+ };
+ responses: {
+ /** @description Successful Response */
+ 201: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["CharacterIngestResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_world_entities_projects__project_id__world_entities_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ project_id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["WorldEntityListResponse"];
+ };
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ list_skills_skills_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["SkillListResponse"];
+ };
+ };
+ };
+ };
list_providers_settings_providers_get: {
parameters: {
query?: never;
@@ -1352,4 +2412,64 @@ export interface operations {
};
};
};
+ start_oauth_settings_providers_kimi_code_oauth_start_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 202: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["OAuthStartResponse"];
+ };
+ };
+ };
+ };
+ disconnect_oauth_settings_providers_kimi_code_oauth_disconnect_post: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["OAuthDisconnectResponse"];
+ };
+ };
+ };
+ };
+ oauth_status_settings_providers_kimi_code_oauth_status_get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["OAuthStatusResponse"];
+ };
+ };
+ };
+ };
}
diff --git a/apps/web/lib/api/server.test.ts b/apps/web/lib/api/server.test.ts
new file mode 100644
index 0000000..658899f
--- /dev/null
+++ b/apps/web/lib/api/server.test.ts
@@ -0,0 +1,68 @@
+import { afterEach, describe, expect, it, vi } from "vitest";
+
+import { fetchDraft, fetchOutline } from "./server";
+
+// 纯加载/降级逻辑(mock fetch,无网络/DOM):大纲解包/降级、草稿 404→null。
+function mockFetch(status: number, body: unknown): void {
+ vi.stubGlobal(
+ "fetch",
+ vi.fn(async () =>
+ new Response(JSON.stringify(body), {
+ status,
+ headers: { "content-type": "application/json" },
+ }),
+ ),
+ );
+}
+
+afterEach(() => {
+ vi.unstubAllGlobals();
+});
+
+describe("fetchOutline", () => {
+ it("returns the persisted chapters on 200", async () => {
+ mockFetch(200, { chapters: [{ no: 1, volume: 1, beats: ["开场"] }] });
+
+ const chapters = await fetchOutline("p1");
+
+ expect(chapters).toHaveLength(1);
+ expect(chapters[0].no).toBe(1);
+ });
+
+ it("returns empty array when the project has no outline", async () => {
+ mockFetch(200, { chapters: [] });
+
+ expect(await fetchOutline("p1")).toEqual([]);
+ });
+
+ it("falls back to empty array on any error (does not throw)", async () => {
+ mockFetch(404, { error: { code: "NOT_FOUND", message: "no project" } });
+
+ expect(await fetchOutline("missing")).toEqual([]);
+ });
+});
+
+describe("fetchDraft", () => {
+ it("returns the saved draft view on 200", async () => {
+ mockFetch(200, {
+ project_id: "p1",
+ chapter_no: 1,
+ volume: 1,
+ status: "draft",
+ version: 2,
+ content: "第一章正文",
+ length: 5,
+ });
+
+ const draft = await fetchDraft("p1", 1);
+
+ expect(draft?.content).toBe("第一章正文");
+ expect(draft?.version).toBe(2);
+ });
+
+ it("returns null when no draft exists (404 → empty editor)", async () => {
+ mockFetch(404, { error: { code: "NOT_FOUND", message: "no draft" } });
+
+ expect(await fetchDraft("p1", 9)).toBeNull();
+ });
+});
diff --git a/apps/web/lib/api/server.ts b/apps/web/lib/api/server.ts
index 264b5d0..505ed00 100644
--- a/apps/web/lib/api/server.ts
+++ b/apps/web/lib/api/server.ts
@@ -1,10 +1,19 @@
import { serverApiBase } from "./config";
import type {
+ CharacterListResponse,
+ DraftView,
ForeshadowBoardResponse,
+ OutlineChapterView,
+ OutlineResponse,
ProjectListResponse,
ProjectResponse,
+ OAuthStatusResponse,
ProvidersResponse,
ReviewHistoryResponse,
+ RuleListResponse,
+ SkillListResponse,
+ StyleFingerprintResponse,
+ WorldEntityListResponse,
} from "./types";
// 服务端只读取数据(Server Components)。失败时抛出,由页面边界处理。
@@ -16,6 +25,16 @@ async function getJson(path: string): Promise {
return (await res.json()) as T;
}
+// 像上面一样读取,但 404(无指纹)返回 null 而非抛出(首学前页面照常渲染)。
+async function getJsonOrNull(path: string): Promise {
+ const res = await fetch(`${serverApiBase()}${path}`, { cache: "no-store" });
+ if (res.status === 404) return null;
+ if (!res.ok) {
+ throw new Error(`请求失败 ${res.status}: ${path}`);
+ }
+ return (await res.json()) as T;
+}
+
export async function fetchProjects(): Promise {
return getJson("/projects");
}
@@ -28,6 +47,13 @@ export async function fetchProviders(): Promise {
return getJson("/settings/providers");
}
+// Kimi Code OAuth 连接态(GET .../oauth/status;无 token 本体)。
+export async function fetchKimiOauthStatus(): Promise {
+ return getJson(
+ "/settings/providers/kimi-code/oauth/status",
+ );
+}
+
export async function fetchReviews(
projectId: string,
chapterNo: number,
@@ -43,3 +69,66 @@ export async function fetchForeshadow(
): Promise {
return getJson(`/projects/${projectId}/foreshadow`);
}
+
+// 最新文风指纹(GET /style);无指纹(404)→ null(首学前页面照常渲染)。
+export async function fetchStyleFingerprint(
+ projectId: string,
+): Promise {
+ return getJsonOrNull(
+ `/projects/${projectId}/style`,
+ );
+}
+
+// 规则列表(GET .../rules,规则页)。
+export async function fetchRules(
+ projectId: string,
+): Promise {
+ return getJson(`/projects/${projectId}/rules`);
+}
+
+// 技能库注册表(GET /skills,全局只读)。
+export async function fetchSkills(): Promise {
+ return getJson("/skills");
+}
+
+// 设定库 Codex:跨会话全量已入库角色(GET .../characters;无行→空列表,非 404)。
+export async function fetchCharacters(
+ projectId: string,
+): Promise {
+ return getJson(`/projects/${projectId}/characters`);
+}
+
+// 设定库 Codex:跨会话全量已入库世界观实体(GET .../world_entities;无行→空列表)。
+export async function fetchWorldEntities(
+ projectId: string,
+): Promise {
+ return getJson(
+ `/projects/${projectId}/world_entities`,
+ );
+}
+
+// 已存大纲(GET .../outline,与 POST 同形,按 chapter_no 排序)。
+// 无大纲→空列表;任何错误亦降级为空(进页不阻塞,生成流照常工作)。
+export async function fetchOutline(
+ projectId: string,
+): Promise {
+ try {
+ const res = await getJson(
+ `/projects/${projectId}/outline`,
+ );
+ return res.chapters ?? [];
+ } catch {
+ return [];
+ }
+}
+
+// 已存草稿(GET .../draft,含正文,供工作台重访重载编辑器)。
+// 404(尚无草稿,新章)→ null,由调用方当空编辑器处理,不抛错。
+export async function fetchDraft(
+ projectId: string,
+ chapterNo: number,
+): Promise {
+ return getJsonOrNull(
+ `/projects/${projectId}/chapters/${chapterNo}/draft`,
+ );
+}
diff --git a/apps/web/lib/api/types.ts b/apps/web/lib/api/types.ts
index 30ed013..f2a0f63 100644
--- a/apps/web/lib/api/types.ts
+++ b/apps/web/lib/api/types.ts
@@ -6,6 +6,8 @@ export type ProjectCreateRequest = components["schemas"]["ProjectCreateRequest"]
export type ProjectListResponse = components["schemas"]["ProjectListResponse"];
export type DraftSaveRequest = components["schemas"]["DraftSaveRequest"];
export type DraftResponse = components["schemas"]["DraftResponse"];
+// GET .../draft 读端点:含正文,供工作台重访时重载编辑器(404→空编辑器)。
+export type DraftView = components["schemas"]["DraftView"];
export type ProvidersResponse = components["schemas"]["ProvidersResponse"];
export type ProviderView = components["schemas"]["ProviderView"];
export type TierRoutingView = components["schemas"]["TierRoutingView"];
@@ -40,3 +42,48 @@ export type OutlineChapterView = components["schemas"]["OutlineChapterView"];
export type OutlineGenerateRequest =
components["schemas"]["OutlineGenerateRequest"];
export type OutlineResponse = components["schemas"]["OutlineResponse"];
+
+// M4 文风:学文风(jobs 异步)+ 最新指纹 + 回炉(C3 扩 T4.3)。
+export type StyleLearnRequest = components["schemas"]["StyleLearnRequest"];
+export type StyleLearnResponse = components["schemas"]["StyleLearnResponse"];
+export type StyleFingerprintResponse =
+ components["schemas"]["StyleFingerprintResponse"];
+export type RefineRequest = components["schemas"]["RefineRequest"];
+export type RefineResponse = components["schemas"]["RefineResponse"];
+
+// M5 生成 + 设定库(C3 扩 T5.2)。
+export type WorldGenerateRequest =
+ components["schemas"]["WorldGenerateRequest"];
+export type WorldGenPreviewResponse =
+ components["schemas"]["WorldGenPreviewResponse"];
+export type WorldEntityCardView =
+ components["schemas"]["WorldEntityCardView"];
+export type CharacterGenerateRequest =
+ components["schemas"]["CharacterGenerateRequest"];
+export type CharacterGenPreviewResponse =
+ components["schemas"]["CharacterGenPreviewResponse"];
+export type CharacterCardView = components["schemas"]["CharacterCardView"];
+export type CharacterRelationView =
+ components["schemas"]["CharacterRelationView"];
+export type CharacterIngestRequest =
+ components["schemas"]["CharacterIngestRequest"];
+export type CharacterIngestResponse =
+ components["schemas"]["CharacterIngestResponse"];
+// 设定库 Codex 读端点(C3 扩 M5 R1 follow-up):跨会话全量已入库角色/世界观。
+export type CharacterListResponse =
+ components["schemas"]["CharacterListResponse"];
+export type WorldEntityListResponse =
+ components["schemas"]["WorldEntityListResponse"];
+
+// K1 Kimi Code OAuth device-flow(C3 扩 K1.3)。
+export type OAuthStartResponse = components["schemas"]["OAuthStartResponse"];
+export type OAuthDisconnectResponse =
+ components["schemas"]["OAuthDisconnectResponse"];
+export type OAuthStatusResponse = components["schemas"]["OAuthStatusResponse"];
+
+// M5 规则 + 技能(C3 扩 T5.2 / T5.5)。
+export type RuleCreateRequest = components["schemas"]["RuleCreateRequest"];
+export type RuleView = components["schemas"]["RuleView"];
+export type RuleListResponse = components["schemas"]["RuleListResponse"];
+export type SkillView = components["schemas"]["SkillView"];
+export type SkillListResponse = components["schemas"]["SkillListResponse"];
diff --git a/apps/web/lib/command/palette.test.ts b/apps/web/lib/command/palette.test.ts
new file mode 100644
index 0000000..b6eb918
--- /dev/null
+++ b/apps/web/lib/command/palette.test.ts
@@ -0,0 +1,75 @@
+import { describe, expect, it } from "vitest";
+
+import {
+ clampHighlight,
+ filterCommands,
+ globalCommands,
+ moveHighlight,
+ projectCommands,
+} from "./palette";
+
+describe("projectCommands", () => {
+ it("includes nav + action commands wired to the project id", () => {
+ const cmds = projectCommands("p1");
+ const write = cmds.find((c) => c.id === "nav-write");
+ expect(write?.href).toBe("/projects/p1/write");
+ const genChar = cmds.find((c) => c.id === "action-gen-character");
+ expect(genChar?.kind).toBe("action");
+ expect(genChar?.href).toBe("/projects/p1/codex?gen=character");
+ });
+});
+
+describe("globalCommands", () => {
+ it("offers home + settings", () => {
+ expect(globalCommands().map((c) => c.id)).toEqual([
+ "nav-home",
+ "nav-settings",
+ ]);
+ });
+});
+
+describe("filterCommands", () => {
+ const cmds = projectCommands("p1");
+
+ it("returns all on empty query", () => {
+ expect(filterCommands(cmds, " ")).toHaveLength(cmds.length);
+ });
+
+ it("matches on title", () => {
+ const out = filterCommands(cmds, "审稿");
+ expect(out.map((c) => c.id)).toContain("nav-review");
+ });
+
+ it("matches on keyword case-insensitively", () => {
+ const out = filterCommands(cmds, "FORESHADOW");
+ expect(out.map((c) => c.id)).toContain("nav-foreshadow");
+ });
+
+ it("matches generation actions by 角色/世界观", () => {
+ expect(filterCommands(cmds, "角色").map((c) => c.id)).toContain(
+ "action-gen-character",
+ );
+ expect(filterCommands(cmds, "世界观").map((c) => c.id)).toContain(
+ "action-gen-world",
+ );
+ });
+
+ it("returns empty when nothing matches", () => {
+ expect(filterCommands(cmds, "zzzz-no-match")).toEqual([]);
+ });
+});
+
+describe("clampHighlight / moveHighlight", () => {
+ it("wraps within [0, len)", () => {
+ expect(clampHighlight(0, 3)).toBe(0);
+ expect(clampHighlight(3, 3)).toBe(0);
+ expect(clampHighlight(-1, 3)).toBe(2);
+ expect(clampHighlight(5, 0)).toBe(0);
+ });
+
+ it("moves up/down with wrap", () => {
+ expect(moveHighlight(0, 1, 3)).toBe(1);
+ expect(moveHighlight(2, 1, 3)).toBe(0);
+ expect(moveHighlight(0, -1, 3)).toBe(2);
+ });
+});
diff --git a/apps/web/lib/command/palette.ts b/apps/web/lib/command/palette.ts
new file mode 100644
index 0000000..a5cd320
--- /dev/null
+++ b/apps/web/lib/command/palette.ts
@@ -0,0 +1,154 @@
+// 命令面板(⌘K)纯逻辑:命令清单组装 + 模糊过滤 + 键盘选择状态机。
+// 对齐 UX §7(快速导航/动作)。纯逻辑,便于 node 环境单测(不依赖 DOM)。
+
+export type CommandKind = "navigate" | "action";
+
+export interface Command {
+ id: string;
+ // 展示标题(如「写本章」「跳转伏笔」)。
+ title: string;
+ // 用于匹配的关键词(拼音/别名),与 title 一起参与过滤。
+ keywords: string[];
+ kind: CommandKind;
+ // navigate:跳转路径;action:交由调用方按 id 分派。
+ href?: string;
+ group: string;
+}
+
+// 项目内可用命令(projectId 已知)。导航项给 href;动作项(生成角色等)给 id。
+export function projectCommands(projectId: string): Command[] {
+ const p = `/projects/${projectId}`;
+ return [
+ {
+ id: "nav-write",
+ title: "写本章",
+ keywords: ["write", "写作", "工作台", "起草"],
+ kind: "navigate",
+ href: `${p}/write`,
+ group: "导航",
+ },
+ {
+ id: "nav-review",
+ title: "审稿",
+ keywords: ["review", "质检", "冲突", "裁决"],
+ kind: "navigate",
+ href: `${p}/review`,
+ group: "导航",
+ },
+ {
+ id: "nav-outline",
+ title: "大纲",
+ keywords: ["outline", "节拍", "beats"],
+ kind: "navigate",
+ href: `${p}/outline`,
+ group: "导航",
+ },
+ {
+ id: "nav-foreshadow",
+ title: "跳转伏笔",
+ keywords: ["foreshadow", "伏笔", "看板", "线索"],
+ kind: "navigate",
+ href: `${p}/foreshadow`,
+ group: "导航",
+ },
+ {
+ id: "nav-style",
+ title: "文风",
+ keywords: ["style", "文风", "指纹", "漂移"],
+ kind: "navigate",
+ href: `${p}/style`,
+ group: "导航",
+ },
+ {
+ id: "nav-codex",
+ title: "搜设定",
+ keywords: ["codex", "设定库", "人物", "世界观", "角色"],
+ kind: "navigate",
+ href: `${p}/codex`,
+ group: "导航",
+ },
+ {
+ id: "nav-rules",
+ title: "规则",
+ keywords: ["rules", "规则", "硬规则"],
+ kind: "navigate",
+ href: `${p}/rules`,
+ group: "导航",
+ },
+ {
+ id: "nav-skills",
+ title: "技能库",
+ keywords: ["skills", "技能", "注册表"],
+ kind: "navigate",
+ href: `${p}/skills`,
+ group: "导航",
+ },
+ {
+ id: "action-gen-character",
+ title: "生成角色",
+ keywords: ["character", "角色", "生成器", "群像"],
+ kind: "action",
+ href: `${p}/codex?gen=character`,
+ group: "动作",
+ },
+ {
+ id: "action-gen-world",
+ title: "生成世界观",
+ keywords: ["world", "世界观", "设定", "设计器"],
+ kind: "action",
+ href: `${p}/codex?gen=world`,
+ group: "动作",
+ },
+ ];
+}
+
+// 全局命令(无 projectId 时;如作品库/设置)。
+export function globalCommands(): Command[] {
+ return [
+ {
+ id: "nav-home",
+ title: "作品库",
+ keywords: ["home", "作品", "library", "projects"],
+ kind: "navigate",
+ href: "/",
+ group: "导航",
+ },
+ {
+ id: "nav-settings",
+ title: "设置",
+ keywords: ["settings", "设置", "凭据", "provider", "档位"],
+ kind: "navigate",
+ href: "/settings/providers",
+ group: "导航",
+ },
+ ];
+}
+
+// 模糊过滤:空查询返回全部;否则在 title + keywords 上做大小写无关子串匹配(保持原顺序)。
+export function filterCommands(
+ commands: readonly Command[],
+ query: string,
+): Command[] {
+ const q = query.trim().toLowerCase();
+ if (!q) return [...commands];
+ return commands.filter((cmd) => {
+ if (cmd.title.toLowerCase().includes(q)) return true;
+ return cmd.keywords.some((kw) => kw.toLowerCase().includes(q));
+ });
+}
+
+// 键盘高亮索引在 [0, len) 内循环(空列表归 0)。
+export function clampHighlight(index: number, length: number): number {
+ if (length <= 0) return 0;
+ const wrapped = index % length;
+ return wrapped < 0 ? wrapped + length : wrapped;
+}
+
+// 上/下方向移动高亮(循环)。
+export function moveHighlight(
+ index: number,
+ delta: number,
+ length: number,
+): number {
+ return clampHighlight(index + delta, length);
+}
diff --git a/apps/web/lib/generation/cards.test.ts b/apps/web/lib/generation/cards.test.ts
new file mode 100644
index 0000000..2aa50c9
--- /dev/null
+++ b/apps/web/lib/generation/cards.test.ts
@@ -0,0 +1,199 @@
+import { describe, expect, it } from "vitest";
+
+import type {
+ CharacterCardView,
+ WorldEntityCardView,
+} from "@/lib/api/types";
+import {
+ buildCharacterGenerateRequest,
+ buildCharacterIngestRequest,
+ buildWorldGenerateRequest,
+ clampCharacterCount,
+ errorCode,
+ extractIngestConflicts,
+ generationErrorMessage,
+ MAX_CHARACTER_COUNT,
+ mergeCharacterCards,
+ mergeWorldEntities,
+ MIN_CHARACTER_COUNT,
+ updateCard,
+ worldEntityRules,
+} from "./cards";
+
+const card = (over: Partial = {}): CharacterCardView => ({
+ name: "甲",
+ role: "主角",
+ backstory: "出身寒门",
+ arc: "由怯转勇",
+ ...over,
+});
+
+describe("buildWorldGenerateRequest", () => {
+ it("trims brief", () => {
+ expect(buildWorldGenerateRequest(" 修真世界 ")).toEqual({
+ brief: "修真世界",
+ });
+ });
+});
+
+describe("clampCharacterCount", () => {
+ it("clamps to [MIN, MAX] and rounds", () => {
+ expect(clampCharacterCount(0)).toBe(MIN_CHARACTER_COUNT);
+ expect(clampCharacterCount(99)).toBe(MAX_CHARACTER_COUNT);
+ expect(clampCharacterCount(3.6)).toBe(4);
+ expect(clampCharacterCount(Number.NaN)).toBe(MIN_CHARACTER_COUNT);
+ });
+});
+
+describe("buildCharacterGenerateRequest", () => {
+ it("trims brief, clamps count, omits empty role", () => {
+ expect(buildCharacterGenerateRequest(" 剑修 ", 99)).toEqual({
+ brief: "剑修",
+ count: MAX_CHARACTER_COUNT,
+ });
+ });
+
+ it("includes trimmed role when present", () => {
+ expect(buildCharacterGenerateRequest("剑修", 2, " 对手 ")).toEqual({
+ brief: "剑修",
+ count: 2,
+ role: "对手",
+ });
+ });
+});
+
+describe("buildCharacterIngestRequest", () => {
+ it("copies cards and carries acknowledge flag", () => {
+ const cards = [card()];
+ const req = buildCharacterIngestRequest(cards, true);
+ expect(req.acknowledge_conflicts).toBe(true);
+ expect(req.cards).toEqual(cards);
+ expect(req.cards[0]).not.toBe(cards[0]); // immutable copy
+ });
+
+ it("defaults acknowledge to false", () => {
+ expect(buildCharacterIngestRequest([card()]).acknowledge_conflicts).toBe(
+ false,
+ );
+ });
+});
+
+describe("updateCard", () => {
+ it("returns a new card with patch applied (no mutation)", () => {
+ const original = card();
+ const next = updateCard(original, { name: "乙" });
+ expect(next.name).toBe("乙");
+ expect(original.name).toBe("甲");
+ expect(next).not.toBe(original);
+ });
+});
+
+describe("extractIngestConflicts", () => {
+ it("narrows CONFLICT_UNRESOLVED envelope details", () => {
+ const out = extractIngestConflicts({
+ error: {
+ code: "CONFLICT_UNRESOLVED",
+ details: {
+ conflicts: [
+ { type: "性格漂移", where: "第3章", refs: ["甲"], suggestion: "改" },
+ "junk",
+ ],
+ conflict_count: 2,
+ },
+ },
+ });
+ expect(out).toEqual({
+ conflictCount: 2,
+ conflicts: [
+ { type: "性格漂移", where: "第3章", refs: ["甲"], suggestion: "改" },
+ { type: "未分类", where: "", refs: [], suggestion: "" },
+ ],
+ });
+ });
+
+ it("returns null for non-conflict errors", () => {
+ expect(extractIngestConflicts({ error: { code: "LLM_UNAVAILABLE" } })).toBeNull();
+ expect(extractIngestConflicts(null)).toBeNull();
+ });
+
+ it("falls back conflict_count to conflicts length", () => {
+ const out = extractIngestConflicts({
+ error: {
+ code: "CONFLICT_UNRESOLVED",
+ details: { conflicts: [{ type: "设定违例" }] },
+ },
+ });
+ expect(out?.conflictCount).toBe(1);
+ });
+});
+
+describe("errorCode / generationErrorMessage", () => {
+ it("extracts code and maps 503 to settings prompt", () => {
+ expect(errorCode({ error: { code: "LLM_UNAVAILABLE" } })).toBe(
+ "LLM_UNAVAILABLE",
+ );
+ expect(generationErrorMessage({ error: { code: "LLM_UNAVAILABLE" } })).toContain(
+ "设置",
+ );
+ expect(generationErrorMessage({ error: { code: "INTERNAL" } })).toContain(
+ "重试",
+ );
+ });
+});
+
+describe("worldEntityRules", () => {
+ it("returns rules or empty array", () => {
+ expect(worldEntityRules({ type: "势力", name: "宗门", rules: ["a"] })).toEqual([
+ "a",
+ ]);
+ expect(worldEntityRules({ type: "势力", name: "宗门" })).toEqual([]);
+ });
+});
+
+describe("mergeCharacterCards", () => {
+ it("appends session cards not already in the persisted list", () => {
+ const persisted = [card({ name: "甲" })];
+ const session = [card({ name: "乙" })];
+ const out = mergeCharacterCards(persisted, session);
+ expect(out.map((c) => c.name)).toEqual(["甲", "乙"]);
+ });
+
+ it("dedups by name (persisted wins, no duplicate on reload echo)", () => {
+ const persisted = [card({ name: "甲", role: "主角" })];
+ const session = [card({ name: "甲", role: "对手" })];
+ const out = mergeCharacterCards(persisted, session);
+ expect(out).toHaveLength(1);
+ expect(out[0].role).toBe("主角"); // persisted version kept
+ });
+
+ it("returns persisted list when no session cards", () => {
+ const persisted = [card({ name: "甲" })];
+ expect(mergeCharacterCards(persisted, [])).toEqual(persisted);
+ });
+});
+
+describe("mergeWorldEntities", () => {
+ const entity = (
+ over: Partial = {},
+ ): WorldEntityCardView => ({ type: "势力", name: "宗门", ...over });
+
+ it("appends entities not already present (keyed by type+name)", () => {
+ const persisted = [entity({ name: "天剑宗" })];
+ const session = [entity({ name: "魔渊" })];
+ expect(mergeWorldEntities(persisted, session).map((e) => e.name)).toEqual([
+ "天剑宗",
+ "魔渊",
+ ]);
+ });
+
+ it("dedups by type+name; same name different type kept separate", () => {
+ const persisted = [entity({ type: "势力", name: "玄" })];
+ const session = [
+ entity({ type: "势力", name: "玄" }),
+ entity({ type: "地点", name: "玄" }),
+ ];
+ const out = mergeWorldEntities(persisted, session);
+ expect(out).toHaveLength(2);
+ expect(out.map((e) => e.type)).toEqual(["势力", "地点"]);
+ });
+});
diff --git a/apps/web/lib/generation/cards.ts b/apps/web/lib/generation/cards.ts
new file mode 100644
index 0000000000000000000000000000000000000000..547fdbc5d2d5ab28f748793fdca534632bae2547
GIT binary patch
literal 5668
zcmbtYZBtvv74Bz##ah!3(pV$q^+RQ2N>!4225<)xCo>G=T&`AdYw1e8_sYOvX23BZ
zYy_$U#Km9-$F8Z{AUl&dnAiW3Xs@JC{DqveyZ22RAZEdOk{C2$l{C<7$httEw^MuVkWBXs%Hty8+D~*|h`td(b4>#(SpY2z-
zt4Ckg4(`;yoBMTUmX)?^iwo@NesymKetz`c9|)WOq4r|dUVUAAap%{W+a2*ZVU;(H
zqcTZ;NbJRbgU0ckkx9kXF~v|+iy9^wr8+efi<0xiH>&BQ)U?1H*v4nZQuX+xy1z{H
zT#Ay~a)mwlij~&vpYEZy4lSF{85Xf7^OQ_=5R!DXBo!ldO{3#c|KO)m@c{jM0dxiC
zE_3z1na|Pe9^KNcN&jYi_{>JrByHrfB=pC4MjMGMc`c46nV}0o3&{Yz2}UJzdOD-2
z76~ph_+;EPDXc>z1w;1Q%f`Y={rC;=25<^^S$VnkpNe$&iA7k`1n8|+Ce8c
zQff{&Ez)gp!JdKZ$t$1r50Xyu{)Hx~?k`$NeE`O#3%Y7)Ih`oxRm%YC
z!Z#D7px@AQ<9d{&l}u(tQE$MWp-?12OfJF+GN@HBbWT_UCTPLL7vA<_i{33v5{
zj_DSPx(!2_^l5Xq9~$Ngv@{8y<3cgbl){k+k$Q)QFMvbHA2(xZEdw4I4o|=?6GSr|
zOFJ()7ES~#jBuNPnO|mB;XiSMNraai?r>mqQ5q>|nN%y!36B^WO*;sI6|IE)z)cak
zg8zvDu-R~gOgGsj9OoTIsTn1k_W~wY(5fnni`Y
z2X70%LcE33>EI51iZvE9KuJn2#c8BWCl_0JrAu6!oyF2ir0=Y&dP~pF`J6!K(8=fs
z66Rvl6kK7jIlvnxk^pizVB|8i%VCz>0w^oarg
z-f?4Q5vq)EPpl@qvOr-H1xQq!)5C|5J0O!}XlfL4{^}0o|N85@^^*nkBumo^kdw(M
zR1ipqya<(-8w(}20#ysEN9*u&VGm?_RQWIDq6323|yC+9T-3~gnIBos0x
zR$yMv*kaf&K7D7;FP%A)f-&5f%rx
z1e7!Z+eH^(2ewOSR9>;vu>|?6Vpy7zX({B731@8pH4Y0Xjp7%+;hBtwZjijv|4HxV
z>Gt
z`LYwDM5JQ3fl-G-c!UtB@);u|QCj2|=0l-jDIn~0=0)EDJL0e_<3E8T83rM5T#CBo
zyn3+QSp8?cQmLM7vzN2>N|~*!6R+>CSN>&xH3tc^vGquF;zFGKGJl_=-6|W>2@5;%
zCf5m5;N0-@+$11NSTo;z*%`*gsK>cv8yx_703QU-s1E%f5u(7GV1@sLx+#94XbQ`y
z>@9II{*wWUa-P5Y#N0sEx`)1kkKVFd*7!(N2aWiD!)xvT{1%*%ZrAebgF5y>8xc4fc|+OliU>
zH;5WD8<2!WVAQ^qK?Di-sqA_-n`Hc;gC
z<`y_$ks>bu9aqun-`
zj*9y@l+YebK7G_QmC^P9PkHn=u>tK9mYgOwg`aitbl#XNfgO-QtH%%7+WhI^5;U7^
zuIQTB*#4=0@(2#Zc3(r*6l0M|fP!Z!$cH4HvTKs6T5CojK}cCjKR
zRCaaoI2WCvyte`?Yf1Z4Tx4%v`+KG07JGirUYf1GnPW@qLJFw<
zv}X`IG2fdG5PY84oByriv(XlIm7)d3-g3iUS|qxXr3AnR*kA*9%j)3+AOUdYZEJan
zl}puwXN>C=>Z*JDY++v5-IzULb0t>X5W9m9Vhj7=xVpE@ia(=PXzn&2+xum_4m>Tv
z6yPtMuX?h@_E)$y&T%$(phbpvEYSqlx1YiCR*Yn+F-im7t%>b#9^SGv)1s+&UmZ;F
zZ18eyZr^#)Xw?l%dLiXOrZy?XtF%tX$yI8FT{HYw2{nd*M|_!D$>%AAbqZGct#O|~
z#1-TmZXO#`%&>n4=~hG7eLIKdy|}aKaJv}r`4vD9EpK)2F0hRr!5hXXH&Z%BTkQFx
z2={yq;-L-no{;-qC+=%HppyV(n6{MUm$@&0KSg7hf2V(k
l-|(E%UwRq+tt7(kZ>P>Vi_0G6Iz9GahcM93`TyV8`wig$?~ecg
literal 0
HcmV?d00001
diff --git a/apps/web/lib/generation/useCharacterGen.ts b/apps/web/lib/generation/useCharacterGen.ts
new file mode 100644
index 0000000..bcdb781
--- /dev/null
+++ b/apps/web/lib/generation/useCharacterGen.ts
@@ -0,0 +1,150 @@
+"use client";
+
+import { useCallback, useState } from "react";
+
+import { api } from "@/lib/api/client";
+import { useToast } from "@/components/Toast";
+import type {
+ CharacterCardView,
+ CharacterIngestResponse,
+} from "@/lib/api/types";
+import {
+ buildCharacterGenerateRequest,
+ buildCharacterIngestRequest,
+ extractIngestConflicts,
+ generationErrorMessage,
+ type IngestConflicts,
+} from "./cards";
+
+export type CharacterGenStatus = "idle" | "generating" | "preview" | "error";
+export type IngestStatus = "idle" | "ingesting" | "conflict" | "done" | "error";
+
+export interface CharacterGenInput {
+ projectId: string;
+ brief: string;
+ count: number;
+ role?: string | null;
+}
+
+export interface UseCharacterGen {
+ genStatus: CharacterGenStatus;
+ cards: CharacterCardView[];
+ ingestStatus: IngestStatus;
+ // 409 冲突详情(待作者裁决);裁决后带 acknowledge 重发。
+ conflicts: IngestConflicts | null;
+ created: string[];
+ generate: (input: CharacterGenInput) => Promise;
+ // 入库选中卡;acknowledge 用于过 409(已查看冲突后重发)。
+ ingest: (
+ projectId: string,
+ cards: readonly CharacterCardView[],
+ acknowledge?: boolean,
+ ) => Promise;
+ reset: () => void;
+}
+
+// 角色生成器(UX §6.6):生成预览 → 作者选/改 → 入库(处理 409 裁决流)。
+export function useCharacterGen(): UseCharacterGen {
+ const [genStatus, setGenStatus] = useState("idle");
+ const [cards, setCards] = useState([]);
+ const [ingestStatus, setIngestStatus] = useState("idle");
+ const [conflicts, setConflicts] = useState(null);
+ const [created, setCreated] = useState([]);
+ const toast = useToast();
+
+ const generate = useCallback(
+ async ({ projectId, brief, count, role }) => {
+ setGenStatus("generating");
+ setCards([]);
+ setConflicts(null);
+ setIngestStatus("idle");
+ try {
+ const { data, error } = await api.POST(
+ "/projects/{project_id}/characters/generate",
+ {
+ params: { path: { project_id: projectId } },
+ body: buildCharacterGenerateRequest(brief, count, role),
+ },
+ );
+ if (error || !data) {
+ setGenStatus("error");
+ toast(generationErrorMessage(error), "error");
+ return;
+ }
+ setCards(data.cards ?? []);
+ setGenStatus("preview");
+ } catch {
+ setGenStatus("error");
+ toast("生成请求异常,请检查网络。", "error");
+ }
+ },
+ [toast],
+ );
+
+ const ingest = useCallback(
+ async (projectId, ingestCards, acknowledge = false) => {
+ if (ingestCards.length === 0) {
+ toast("请至少选择一张角色卡。", "error");
+ return false;
+ }
+ setIngestStatus("ingesting");
+ try {
+ const { data, error } = await api.POST(
+ "/projects/{project_id}/characters",
+ {
+ params: { path: { project_id: projectId } },
+ body: buildCharacterIngestRequest(ingestCards, acknowledge),
+ },
+ );
+ if (error || !data) {
+ const conflict = extractIngestConflicts(error);
+ if (conflict) {
+ // 409 CONFLICT_UNRESOLVED:展示冲突让作者裁决,再带 acknowledge 重发。
+ setConflicts(conflict);
+ setIngestStatus("conflict");
+ return false;
+ }
+ setIngestStatus("error");
+ toast(generationErrorMessage(error), "error");
+ return false;
+ }
+ const result = data as CharacterIngestResponse;
+ setCreated(result.created ?? []);
+ setConflicts(null);
+ setIngestStatus("done");
+ toast(`已入库 ${result.created?.length ?? 0} 个角色`, "success");
+ if (result.rejected_tables && result.rejected_tables.length > 0) {
+ toast(
+ `越权写表被丢弃:${result.rejected_tables.join("、")}`,
+ "info",
+ );
+ }
+ return true;
+ } catch {
+ setIngestStatus("error");
+ toast("入库请求异常,请检查网络。", "error");
+ return false;
+ }
+ },
+ [toast],
+ );
+
+ const reset = useCallback((): void => {
+ setGenStatus("idle");
+ setCards([]);
+ setIngestStatus("idle");
+ setConflicts(null);
+ setCreated([]);
+ }, []);
+
+ return {
+ genStatus,
+ cards,
+ ingestStatus,
+ conflicts,
+ created,
+ generate,
+ ingest,
+ reset,
+ };
+}
diff --git a/apps/web/lib/generation/useWorldGen.ts b/apps/web/lib/generation/useWorldGen.ts
new file mode 100644
index 0000000..17daacf
--- /dev/null
+++ b/apps/web/lib/generation/useWorldGen.ts
@@ -0,0 +1,58 @@
+"use client";
+
+import { useCallback, useState } from "react";
+
+import { api } from "@/lib/api/client";
+import { useToast } from "@/components/Toast";
+import type { WorldEntityCardView } from "@/lib/api/types";
+import { buildWorldGenerateRequest, generationErrorMessage } from "./cards";
+
+export type WorldGenStatus = "idle" | "generating" | "done" | "error";
+
+export interface UseWorldGen {
+ status: WorldGenStatus;
+ entities: WorldEntityCardView[];
+ generate: (projectId: string, brief: string) => Promise;
+ reset: () => void;
+}
+
+// 世界观设计器:POST /world/generate → 预览实体(不入库)。
+export function useWorldGen(): UseWorldGen {
+ const [status, setStatus] = useState("idle");
+ const [entities, setEntities] = useState([]);
+ const toast = useToast();
+
+ const generate = useCallback(
+ async (projectId, brief) => {
+ setStatus("generating");
+ setEntities([]);
+ try {
+ const { data, error } = await api.POST(
+ "/projects/{project_id}/world/generate",
+ {
+ params: { path: { project_id: projectId } },
+ body: buildWorldGenerateRequest(brief),
+ },
+ );
+ if (error || !data) {
+ setStatus("error");
+ toast(generationErrorMessage(error), "error");
+ return;
+ }
+ setEntities(data.entities ?? []);
+ setStatus("done");
+ } catch {
+ setStatus("error");
+ toast("生成请求异常,请检查网络。", "error");
+ }
+ },
+ [toast],
+ );
+
+ const reset = useCallback((): void => {
+ setStatus("idle");
+ setEntities([]);
+ }, []);
+
+ return { status, entities, generate, reset };
+}
diff --git a/apps/web/lib/jobs/job.test.ts b/apps/web/lib/jobs/job.test.ts
new file mode 100644
index 0000000..dd3894c
--- /dev/null
+++ b/apps/web/lib/jobs/job.test.ts
@@ -0,0 +1,150 @@
+import { describe, expect, it } from "vitest";
+
+import {
+ initialPollState,
+ isTerminal,
+ narrowJob,
+ reducePoll,
+ styleLearnResult,
+ type JobView,
+} from "./job";
+
+describe("narrowJob", () => {
+ it("narrows a well-formed loose dict into JobView", () => {
+ const job = narrowJob({
+ id: "j1",
+ kind: "style_learn",
+ status: "running",
+ progress: 42,
+ result: null,
+ error: null,
+ });
+ expect(job).toEqual({
+ id: "j1",
+ kind: "style_learn",
+ status: "running",
+ progress: 42,
+ result: null,
+ error: null,
+ });
+ });
+
+ it("falls back safely for missing/wrong-typed fields", () => {
+ expect(narrowJob({})).toEqual({
+ id: "",
+ kind: "",
+ status: "queued",
+ progress: 0,
+ result: null,
+ error: null,
+ });
+ // unknown status → queued; out-of-range progress clamped.
+ expect(narrowJob({ status: "weird", progress: 250 }).status).toBe("queued");
+ expect(narrowJob({ progress: 250 }).progress).toBe(100);
+ expect(narrowJob({ progress: -5 }).progress).toBe(0);
+ });
+
+ it("keeps result only when an object (not array)", () => {
+ expect(narrowJob({ result: { version: 2 } }).result).toEqual({ version: 2 });
+ expect(narrowJob({ result: [1, 2] }).result).toBeNull();
+ });
+
+ it("narrows non-object input", () => {
+ expect(narrowJob(null).status).toBe("queued");
+ expect(narrowJob("nope").id).toBe("");
+ });
+});
+
+describe("isTerminal", () => {
+ it("done and failed are terminal", () => {
+ expect(isTerminal("done")).toBe(true);
+ expect(isTerminal("failed")).toBe(true);
+ expect(isTerminal("queued")).toBe(false);
+ expect(isTerminal("running")).toBe(false);
+ });
+});
+
+describe("reducePoll", () => {
+ const job = (over: Partial): JobView => ({
+ id: "j1",
+ kind: "style_learn",
+ status: "running",
+ progress: 0,
+ result: null,
+ error: null,
+ ...over,
+ });
+
+ it("stays polling for queued/running and tracks progress", () => {
+ const out = reducePoll(initialPollState, {
+ type: "tick",
+ job: job({ status: "running", progress: 30 }),
+ });
+ expect(out.status).toBe("polling");
+ expect(out.progress).toBe(30);
+ });
+
+ it("transitions to done with progress 100", () => {
+ const out = reducePoll(initialPollState, {
+ type: "tick",
+ job: job({ status: "done", result: { version: 2, dims_count: 16 } }),
+ });
+ expect(out.status).toBe("done");
+ expect(out.progress).toBe(100);
+ expect(out.job?.result).toEqual({ version: 2, dims_count: 16 });
+ });
+
+ it("transitions to error on failed, using job.error", () => {
+ const out = reducePoll(initialPollState, {
+ type: "tick",
+ job: job({ status: "failed", error: "网关爆炸", progress: 20 }),
+ });
+ expect(out.status).toBe("error");
+ expect(out.error).toBe("网关爆炸");
+ expect(out.progress).toBe(20);
+ });
+
+ it("defaults error text when failed without error string", () => {
+ const out = reducePoll(initialPollState, {
+ type: "tick",
+ job: job({ status: "failed", error: null }),
+ });
+ expect(out.error).toBe("任务失败");
+ });
+
+ it("handles a fail action (poll request failure)", () => {
+ const out = reducePoll(initialPollState, {
+ type: "fail",
+ error: "网络断了",
+ });
+ expect(out.status).toBe("error");
+ expect(out.error).toBe("网络断了");
+ });
+});
+
+describe("styleLearnResult", () => {
+ it("extracts version + dims_count from done result", () => {
+ const out = styleLearnResult({
+ id: "j",
+ kind: "style_learn",
+ status: "done",
+ progress: 100,
+ result: { version: 3, dims_count: 16 },
+ error: null,
+ });
+ expect(out).toEqual({ version: 3, dimsCount: 16 });
+ });
+
+ it("returns nulls for missing result fields", () => {
+ expect(
+ styleLearnResult({
+ id: "j",
+ kind: "style_learn",
+ status: "done",
+ progress: 100,
+ result: null,
+ error: null,
+ }),
+ ).toEqual({ version: null, dimsCount: null });
+ });
+});
diff --git a/apps/web/lib/jobs/job.ts b/apps/web/lib/jobs/job.ts
new file mode 100644
index 0000000..6010f2e
--- /dev/null
+++ b/apps/web/lib/jobs/job.ts
@@ -0,0 +1,111 @@
+// 长任务(jobs)轮询纯逻辑:把松散 `GET /jobs/{id}` 响应安全收窄成 JobView,
+// 并提供一个 setTimeout 轮询状态机的纯 reducer(C3.5 / ARCH §7.4)。
+// schema 把 GET /jobs/{id} 标成 {[k]:unknown}(T0.3 裸端点),这里安全narrow。
+
+// 后端 job 状态机(C3.5):queued → running → done|failed。
+export type JobStatus = "queued" | "running" | "done" | "failed";
+
+export interface JobView {
+ id: string;
+ kind: string;
+ status: JobStatus;
+ progress: number;
+ result: Record | null;
+ error: string | null;
+}
+
+function asString(v: unknown, fallback = ""): string {
+ return typeof v === "string" ? v : fallback;
+}
+
+function asStatus(v: unknown): JobStatus {
+ return v === "running" || v === "done" || v === "failed" || v === "queued"
+ ? v
+ : "queued";
+}
+
+function asProgress(v: unknown): number {
+ if (typeof v !== "number" || !Number.isFinite(v)) return 0;
+ return Math.min(100, Math.max(0, Math.round(v)));
+}
+
+function asResult(v: unknown): Record | null {
+ return typeof v === "object" && v !== null && !Array.isArray(v)
+ ? (v as Record)
+ : null;
+}
+
+// 把松散 GET /jobs/{id} 响应收窄成 JobView(缺字段给安全默认)。
+export function narrowJob(raw: unknown): JobView {
+ const dict =
+ typeof raw === "object" && raw !== null ? (raw as Record) : {};
+ return {
+ id: asString(dict["id"]),
+ kind: asString(dict["kind"]),
+ status: asStatus(dict["status"]),
+ progress: asProgress(dict["progress"]),
+ result: asResult(dict["result"]),
+ error: typeof dict["error"] === "string" ? (dict["error"] as string) : null,
+ };
+}
+
+// 学文风 job 完成态的 result:{version, dims_count}(C3 扩 work 摘要)。
+export interface StyleLearnResult {
+ version: number | null;
+ dimsCount: number | null;
+}
+
+export function styleLearnResult(job: JobView): StyleLearnResult {
+ const r = job.result ?? {};
+ const version = typeof r["version"] === "number" ? r["version"] : null;
+ const dimsCount =
+ typeof r["dims_count"] === "number" ? r["dims_count"] : null;
+ return { version, dimsCount };
+}
+
+// 轮询状态机:polling(queued/running)→ done | error(done/failed)。
+export type PollStatus = "polling" | "done" | "error";
+
+export interface PollState {
+ status: PollStatus;
+ progress: number;
+ job: JobView | null;
+ error: string | null;
+}
+
+export const initialPollState: PollState = {
+ status: "polling",
+ progress: 0,
+ job: null,
+ error: null,
+};
+
+export type PollAction =
+ | { type: "tick"; job: JobView }
+ | { type: "fail"; error: string };
+
+// 纯 reducer:吃一次轮询结果,映射成轮询状态。
+// done → 终态 done;failed → 终态 error(用 job.error 文案);其余 → 继续 polling。
+export function reducePoll(state: PollState, action: PollAction): PollState {
+ if (action.type === "fail") {
+ return { ...state, status: "error", error: action.error };
+ }
+ const { job } = action;
+ if (job.status === "done") {
+ return { status: "done", progress: 100, job, error: null };
+ }
+ if (job.status === "failed") {
+ return {
+ status: "error",
+ progress: job.progress,
+ job,
+ error: job.error ?? "任务失败",
+ };
+ }
+ return { status: "polling", progress: job.progress, job, error: null };
+}
+
+// 是否终态(停止轮询)。
+export function isTerminal(status: JobStatus): boolean {
+ return status === "done" || status === "failed";
+}
diff --git a/apps/web/lib/jobs/useJobPoll.ts b/apps/web/lib/jobs/useJobPoll.ts
new file mode 100644
index 0000000..70ad98d
--- /dev/null
+++ b/apps/web/lib/jobs/useJobPoll.ts
@@ -0,0 +1,88 @@
+"use client";
+
+import { useCallback, useEffect, useReducer, useRef } from "react";
+
+import { api } from "@/lib/api/client";
+import {
+ initialPollState,
+ isTerminal,
+ narrowJob,
+ reducePoll,
+ type PollState,
+} from "./job";
+
+const POLL_INTERVAL_MS = 1500;
+
+export interface UseJobPoll extends PollState {
+ // 开始轮询某个 job(替换正在轮询的 job)。
+ poll: (jobId: string) => void;
+ // 复位到初始(停止当前轮询)。
+ reset: () => void;
+}
+
+// 轮询 GET /jobs/{job_id}:setTimeout 循环,done/failed 终态停止,卸载清理。
+// 纯状态机在 job.ts(可单测);本 hook 只负责定时器 + fetch 编排。
+export function useJobPoll(): UseJobPoll {
+ const [state, dispatch] = useReducer(reducePoll, initialPollState);
+ const timerRef = useRef | null>(null);
+ const activeRef = useRef(null);
+
+ const clearTimer = useCallback((): void => {
+ if (timerRef.current !== null) {
+ clearTimeout(timerRef.current);
+ timerRef.current = null;
+ }
+ }, []);
+
+ const tick = useCallback(
+ async (jobId: string): Promise => {
+ if (activeRef.current !== jobId) return;
+ try {
+ const { data, error } = await api.GET("/jobs/{job_id}", {
+ params: { path: { job_id: jobId } },
+ });
+ if (activeRef.current !== jobId) return; // 中途切换/卸载。
+ if (error || !data) {
+ dispatch({ type: "fail", error: "轮询任务状态失败" });
+ return;
+ }
+ const job = narrowJob(data);
+ dispatch({ type: "tick", job });
+ if (isTerminal(job.status)) {
+ activeRef.current = null;
+ return;
+ }
+ timerRef.current = setTimeout(() => void tick(jobId), POLL_INTERVAL_MS);
+ } catch (err: unknown) {
+ if (activeRef.current !== jobId) return;
+ const message = err instanceof Error ? err.message : "未知网络错误";
+ dispatch({ type: "fail", error: message });
+ }
+ },
+ [],
+ );
+
+ const poll = useCallback(
+ (jobId: string): void => {
+ clearTimer();
+ activeRef.current = jobId;
+ void tick(jobId);
+ },
+ [clearTimer, tick],
+ );
+
+ const reset = useCallback((): void => {
+ clearTimer();
+ activeRef.current = null;
+ }, [clearTimer]);
+
+ // 卸载清理。
+ useEffect(() => {
+ return () => {
+ if (timerRef.current !== null) clearTimeout(timerRef.current);
+ activeRef.current = null;
+ };
+ }, []);
+
+ return { ...state, poll, reset };
+}
diff --git a/apps/web/lib/review/sse.ts b/apps/web/lib/review/sse.ts
index c299e5c..9d50190 100644
--- a/apps/web/lib/review/sse.ts
+++ b/apps/web/lib/review/sse.ts
@@ -42,6 +42,18 @@ export interface PaceReport {
beat_map: number[];
}
+// 文风漂移(第四审,C4 扩 T4.2):整体相似度 score + 段级漂移。
+export interface StyleDriftSegment {
+ idx: number;
+ score: number;
+ label: string | null;
+}
+
+export interface StyleDriftReport {
+ score: number;
+ segments: StyleDriftSegment[];
+}
+
export interface SectionEvent {
event: "section";
data: { name: string; status: SectionStatus };
@@ -68,6 +80,17 @@ export interface PaceEvent {
beat_map: number[];
};
}
+export interface StyleEvent {
+ event: "style";
+ data: {
+ score: number;
+ segments: {
+ idx: number;
+ score: number;
+ label?: string | null;
+ }[];
+ };
+}
export interface DoneEvent {
event: "done";
data: { length: number };
@@ -81,6 +104,7 @@ export type ReviewSseEvent =
| ConflictEvent
| ForeshadowEvent
| PaceEvent
+ | StyleEvent
| DoneEvent
| ErrorEvent;
@@ -89,6 +113,7 @@ const KNOWN_EVENTS = new Set([
"conflict",
"foreshadow",
"pace",
+ "style",
"done",
"error",
]);
@@ -160,6 +185,7 @@ export interface ReviewStreamState {
conflicts: ReviewConflict[];
foreshadow: ForeshadowSuggestion[];
pace: PaceReport | null;
+ style: StyleDriftReport | null;
error: { code: string; message: string; request_id?: string | null } | null;
}
@@ -169,6 +195,7 @@ export const initialReviewState: ReviewStreamState = {
conflicts: [],
foreshadow: [],
pace: null,
+ style: null,
error: null,
};
@@ -202,6 +229,19 @@ export function reduceReview(
phase: "reviewing",
pace: { ...event.data },
};
+ case "style":
+ return {
+ ...state,
+ phase: "reviewing",
+ style: {
+ score: event.data.score,
+ segments: event.data.segments.map((s) => ({
+ idx: s.idx,
+ score: s.score,
+ label: s.label ?? null,
+ })),
+ },
+ };
case "done":
return { ...state, phase: "done" };
case "error":
diff --git a/apps/web/lib/review/style-sse.test.ts b/apps/web/lib/review/style-sse.test.ts
new file mode 100644
index 0000000..ef0deb4
--- /dev/null
+++ b/apps/web/lib/review/style-sse.test.ts
@@ -0,0 +1,70 @@
+import { describe, expect, it } from "vitest";
+
+import {
+ initialReviewState,
+ parseReviewBlock,
+ reduceReview,
+ type ReviewSseEvent,
+} from "./sse";
+
+describe("parseReviewBlock — style (C4 扩 T4.2)", () => {
+ it("parses a style frame", () => {
+ const evt = parseReviewBlock(
+ 'event:style\ndata:{"score":87,"segments":[{"idx":3,"score":60,"label":"口语化"}]}',
+ );
+ expect(evt).toEqual({
+ event: "style",
+ data: {
+ score: 87,
+ segments: [{ idx: 3, score: 60, label: "口语化" }],
+ },
+ });
+ });
+
+ it("parses a degrade态 style frame (无指纹 score=100/空段)", () => {
+ expect(parseReviewBlock('event:style\ndata:{"score":100,"segments":[]}')).toEqual(
+ { event: "style", data: { score: 100, segments: [] } },
+ );
+ });
+});
+
+describe("reduceReview — style (replace-style like pace)", () => {
+ it("sets style report and marks reviewing", () => {
+ const evt: ReviewSseEvent = {
+ event: "style",
+ data: { score: 80, segments: [{ idx: 1, score: 50, label: "突兀" }] },
+ };
+ const out = reduceReview(initialReviewState, evt);
+ expect(out.phase).toBe("reviewing");
+ expect(out.style).toEqual({
+ score: 80,
+ segments: [{ idx: 1, score: 50, label: "突兀" }],
+ });
+ });
+
+ it("replaces (not accumulates) the style report and defaults label null", () => {
+ const first = reduceReview(initialReviewState, {
+ event: "style",
+ data: { score: 70, segments: [{ idx: 0, score: 40 }] },
+ });
+ const second = reduceReview(first, {
+ event: "style",
+ data: { score: 95, segments: [] },
+ });
+ expect(first.style?.segments[0]).toEqual({ idx: 0, score: 40, label: null });
+ expect(second.style).toEqual({ score: 95, segments: [] });
+ });
+
+ it("leaves style untouched on a section event", () => {
+ const seeded = reduceReview(initialReviewState, {
+ event: "style",
+ data: { score: 88, segments: [] },
+ });
+ const out = reduceReview(seeded, {
+ event: "section",
+ data: { name: "style", status: "done" },
+ });
+ expect(out.style).toEqual({ score: 88, segments: [] });
+ expect(out.sections).toEqual([{ name: "style", status: "done" }]);
+ });
+});
diff --git a/apps/web/lib/review/useReviewStream.ts b/apps/web/lib/review/useReviewStream.ts
index 887ffee..42c311e 100644
--- a/apps/web/lib/review/useReviewStream.ts
+++ b/apps/web/lib/review/useReviewStream.ts
@@ -11,12 +11,14 @@ import {
type PaceReport,
type ReviewConflict,
type ReviewStreamState,
+ type StyleDriftReport,
} from "./sse";
export interface ReviewSeed {
conflicts: ReviewConflict[];
foreshadow: ForeshadowSuggestion[];
pace: PaceReport | null;
+ style: StyleDriftReport | null;
}
type Action =
@@ -46,6 +48,7 @@ function reducer(state: ReviewStreamState, action: Action): ReviewStreamState {
conflicts: action.seed.conflicts,
foreshadow: action.seed.foreshadow,
pace: action.seed.pace,
+ style: action.seed.style,
};
default:
return state;
diff --git a/apps/web/lib/rules/rules.test.ts b/apps/web/lib/rules/rules.test.ts
new file mode 100644
index 0000000..37e2dee
--- /dev/null
+++ b/apps/web/lib/rules/rules.test.ts
@@ -0,0 +1,54 @@
+import { describe, expect, it } from "vitest";
+
+import type { RuleView } from "@/lib/api/types";
+import {
+ buildRuleCreateRequest,
+ groupByLevel,
+ hasUsableContent,
+ isRuleLevel,
+ RULE_LEVELS,
+} from "./rules";
+
+describe("isRuleLevel", () => {
+ it("accepts the four known levels only", () => {
+ for (const lv of RULE_LEVELS) expect(isRuleLevel(lv)).toBe(true);
+ expect(isRuleLevel("bogus")).toBe(false);
+ });
+});
+
+describe("groupByLevel", () => {
+ it("groups rules by level, unknown level falls to project", () => {
+ const rules: RuleView[] = [
+ { level: "global", content: "g1" },
+ { level: "project", content: "p1" },
+ { level: "weird", content: "x1" },
+ ];
+ const groups = groupByLevel(rules);
+ expect(groups.global).toEqual([{ level: "global", content: "g1" }]);
+ expect(groups.project).toEqual([
+ { level: "project", content: "p1" },
+ { level: "weird", content: "x1" },
+ ]);
+ expect(groups.genre).toEqual([]);
+ });
+
+ it("handles undefined", () => {
+ expect(groupByLevel(undefined).style).toEqual([]);
+ });
+});
+
+describe("buildRuleCreateRequest", () => {
+ it("trims content", () => {
+ expect(buildRuleCreateRequest("project", " 禁现代词 ")).toEqual({
+ level: "project",
+ content: "禁现代词",
+ });
+ });
+});
+
+describe("hasUsableContent", () => {
+ it("true only when non-blank", () => {
+ expect(hasUsableContent(" ")).toBe(false);
+ expect(hasUsableContent(" x ")).toBe(true);
+ });
+});
diff --git a/apps/web/lib/rules/rules.ts b/apps/web/lib/rules/rules.ts
new file mode 100644
index 0000000..02b429b
--- /dev/null
+++ b/apps/web/lib/rules/rules.ts
@@ -0,0 +1,58 @@
+// 规则页纯逻辑:四级合并展示、请求体组装、级别文案。
+// 对齐 C3 扩(GET/POST .../rules)。纯逻辑,便于 node 环境单测。
+
+import type { RuleCreateRequest, RuleView } from "@/lib/api/types";
+
+// 四级规则(global→genre→style→project,越具体越优先;对齐后端 RuleCreateRequest Literal)。
+export type RuleLevel = "global" | "genre" | "style" | "project";
+
+export const RULE_LEVELS: readonly RuleLevel[] = [
+ "global",
+ "genre",
+ "style",
+ "project",
+] as const;
+
+export const RULE_LEVEL_LABELS: Record = {
+ global: "全局",
+ genre: "题材",
+ style: "文风",
+ project: "本作",
+};
+
+export function isRuleLevel(v: string): v is RuleLevel {
+ return (
+ v === "global" || v === "genre" || v === "style" || v === "project"
+ );
+}
+
+export type RuleGroups = Record;
+
+function emptyGroups(): RuleGroups {
+ return { global: [], genre: [], style: [], project: [] };
+}
+
+// 按 level 分组(未知 level 落 project 兜底,保持服务端顺序)。
+export function groupByLevel(
+ rules: readonly RuleView[] | undefined,
+): RuleGroups {
+ const groups = emptyGroups();
+ for (const rule of rules ?? []) {
+ const level = isRuleLevel(rule.level) ? rule.level : "project";
+ groups[level] = [...groups[level], rule];
+ }
+ return groups;
+}
+
+// 组装新增规则请求体(去空白)。
+export function buildRuleCreateRequest(
+ level: RuleLevel,
+ content: string,
+): RuleCreateRequest {
+ return { level, content: content.trim() };
+}
+
+// 内容是否可提交(非空白)。
+export function hasUsableContent(content: string): boolean {
+ return content.trim().length > 0;
+}
diff --git a/apps/web/lib/rules/useRules.ts b/apps/web/lib/rules/useRules.ts
new file mode 100644
index 0000000..d7d5ec2
--- /dev/null
+++ b/apps/web/lib/rules/useRules.ts
@@ -0,0 +1,62 @@
+"use client";
+
+import { useCallback, useState } from "react";
+
+import { api } from "@/lib/api/client";
+import { useToast } from "@/components/Toast";
+import type { RuleView } from "@/lib/api/types";
+import { buildRuleCreateRequest, hasUsableContent, type RuleLevel } from "./rules";
+
+export interface UseRules {
+ items: RuleView[];
+ busy: boolean;
+ // 新增一条规则(乐观追加 + 失败回滚 + Toast)。
+ add: (
+ projectId: string,
+ level: RuleLevel,
+ content: string,
+ ) => Promise;
+}
+
+// 规则页(UX §7):列出 + 新增规则。乐观追加,失败回滚(仿 useForeshadow)。
+export function useRules(initial: RuleView[]): UseRules {
+ const [items, setItems] = useState(initial);
+ const [busy, setBusy] = useState(false);
+ const toast = useToast();
+
+ const add = useCallback(
+ async (projectId, level, content) => {
+ if (!hasUsableContent(content)) {
+ toast("请填写规则正文。", "error");
+ return false;
+ }
+ const snapshot = items;
+ const optimistic: RuleView = { level, content: content.trim() };
+ setItems((prev) => [...prev, optimistic]);
+ setBusy(true);
+ try {
+ const { data, error } = await api.POST(
+ "/projects/{project_id}/rules",
+ {
+ params: { path: { project_id: projectId } },
+ body: buildRuleCreateRequest(level, content),
+ },
+ );
+ if (error || !data) {
+ setItems(snapshot); // 回滚
+ toast("新增规则失败,请稍后重试。", "error");
+ return false;
+ }
+ // 用服务端权威行替换乐观行(去掉乐观项、追加返回项)。
+ setItems((prev) => [...prev.slice(0, snapshot.length), data]);
+ toast("已新增规则", "success");
+ return true;
+ } finally {
+ setBusy(false);
+ }
+ },
+ [items, toast],
+ );
+
+ return { items, busy, add };
+}
diff --git a/apps/web/lib/settings/kimiOauth.test.ts b/apps/web/lib/settings/kimiOauth.test.ts
new file mode 100644
index 0000000..c4944a8
--- /dev/null
+++ b/apps/web/lib/settings/kimiOauth.test.ts
@@ -0,0 +1,191 @@
+import { describe, expect, it } from "vitest";
+
+import type { JobView, PollState } from "@/lib/jobs/job";
+import {
+ authOpenUrl,
+ connectPhase,
+ formatExpiresAt,
+ jobConnected,
+ KIMI_CODE_MODEL,
+ KIMI_CODE_PROVIDER,
+ toConnectionStatus,
+ toDeviceDisplay,
+} from "./kimiOauth";
+
+const poll = (over: Partial): PollState => ({
+ status: "polling",
+ progress: 0,
+ job: null,
+ error: null,
+ ...over,
+});
+
+const job = (over: Partial): JobView => ({
+ id: "j1",
+ kind: "kimi_oauth",
+ status: "done",
+ progress: 100,
+ result: null,
+ error: null,
+ ...over,
+});
+
+describe("KIMI_CODE constants", () => {
+ it("uses the OAuth provider + coding model names", () => {
+ expect(KIMI_CODE_PROVIDER).toBe("kimi-code");
+ expect(KIMI_CODE_MODEL).toBe("kimi-for-coding");
+ });
+});
+
+describe("toDeviceDisplay", () => {
+ it("maps start response fields", () => {
+ const d = toDeviceDisplay({
+ job_id: "abc",
+ user_code: "WXYZ-1234",
+ verification_uri: "https://auth.kimi.com/device",
+ verification_uri_complete: "https://auth.kimi.com/device?code=WXYZ-1234",
+ expires_in: 600,
+ interval: 5,
+ });
+ expect(d).toEqual({
+ userCode: "WXYZ-1234",
+ verificationUri: "https://auth.kimi.com/device",
+ verificationUriComplete: "https://auth.kimi.com/device?code=WXYZ-1234",
+ expiresIn: 600,
+ interval: 5,
+ });
+ });
+
+ it("defaults missing complete uri to null", () => {
+ const d = toDeviceDisplay({
+ job_id: "abc",
+ user_code: "CODE",
+ verification_uri: "https://auth.kimi.com/device",
+ verification_uri_complete: null,
+ expires_in: 600,
+ interval: 5,
+ });
+ expect(d.verificationUriComplete).toBeNull();
+ });
+});
+
+describe("authOpenUrl", () => {
+ it("prefers the complete uri when present", () => {
+ expect(
+ authOpenUrl({
+ userCode: "C",
+ verificationUri: "https://auth.kimi.com/device",
+ verificationUriComplete: "https://auth.kimi.com/device?code=C",
+ expiresIn: 600,
+ interval: 5,
+ }),
+ ).toBe("https://auth.kimi.com/device?code=C");
+ });
+
+ it("falls back to the plain uri", () => {
+ expect(
+ authOpenUrl({
+ userCode: "C",
+ verificationUri: "https://auth.kimi.com/device",
+ verificationUriComplete: null,
+ expiresIn: 600,
+ interval: 5,
+ }),
+ ).toBe("https://auth.kimi.com/device");
+ });
+});
+
+describe("toConnectionStatus", () => {
+ it("narrows connected + expires_at", () => {
+ expect(
+ toConnectionStatus({ connected: true, expires_at: "2026-07-01T00:00:00Z" }),
+ ).toEqual({ connected: true, expiresAt: "2026-07-01T00:00:00Z" });
+ });
+
+ it("defaults non-string expires_at to null", () => {
+ expect(toConnectionStatus({ connected: false })).toEqual({
+ connected: false,
+ expiresAt: null,
+ });
+ });
+});
+
+describe("jobConnected", () => {
+ it("true only when result.connected === true (no token expected)", () => {
+ expect(jobConnected(job({ result: { connected: true, provider: "kimi-code" } }))).toBe(
+ true,
+ );
+ expect(jobConnected(job({ result: { connected: false } }))).toBe(false);
+ expect(jobConnected(job({ result: null }))).toBe(false);
+ });
+});
+
+describe("connectPhase", () => {
+ it("not started + not connected → idle", () => {
+ expect(
+ connectPhase({ started: false, connected: false, poll: poll({}) }),
+ ).toBe("idle");
+ });
+
+ it("not started + connected (status query) → connected", () => {
+ expect(
+ connectPhase({ started: false, connected: true, poll: poll({}) }),
+ ).toBe("connected");
+ });
+
+ it("started + polling → awaiting", () => {
+ expect(
+ connectPhase({
+ started: true,
+ connected: false,
+ poll: poll({ status: "polling" }),
+ }),
+ ).toBe("awaiting");
+ });
+
+ it("started + done + job.connected → connected", () => {
+ expect(
+ connectPhase({
+ started: true,
+ connected: false,
+ poll: poll({
+ status: "done",
+ job: job({ result: { connected: true, provider: "kimi-code" } }),
+ }),
+ }),
+ ).toBe("connected");
+ });
+
+ it("started + done but job not connected → error", () => {
+ expect(
+ connectPhase({
+ started: true,
+ connected: false,
+ poll: poll({ status: "done", job: job({ result: { connected: false } }) }),
+ }),
+ ).toBe("error");
+ });
+
+ it("started + poll error (expired/denied/network) → error", () => {
+ expect(
+ connectPhase({
+ started: true,
+ connected: false,
+ poll: poll({ status: "error", error: "授权已过期" }),
+ }),
+ ).toBe("error");
+ });
+});
+
+describe("formatExpiresAt", () => {
+ it("returns null for null/invalid input", () => {
+ expect(formatExpiresAt(null)).toBeNull();
+ expect(formatExpiresAt("not-a-date")).toBeNull();
+ });
+
+ it("formats a valid ISO timestamp to a non-empty string", () => {
+ const out = formatExpiresAt("2026-07-01T08:30:00Z");
+ expect(typeof out).toBe("string");
+ expect(out).not.toBe("");
+ });
+});
diff --git a/apps/web/lib/settings/kimiOauth.ts b/apps/web/lib/settings/kimiOauth.ts
new file mode 100644
index 0000000..0925b31
--- /dev/null
+++ b/apps/web/lib/settings/kimiOauth.ts
@@ -0,0 +1,101 @@
+// Kimi Code OAuth device-flow 连接态纯逻辑(C3 扩 K1.3,K1.4 前端)。
+// 把「连接状态查询」「device 启动响应」「job 轮询结果」收窄/归一成一个
+// 可渲染的连接阶段(connect phase),组件只读结果、不含分支逻辑。
+// 纯函数 + node-env 单测;token 永不出现(job 结果只 {connected, provider})。
+
+import type { JobView, PollState } from "@/lib/jobs/job";
+import type {
+ OAuthStartResponse,
+ OAuthStatusResponse,
+} from "@/lib/api/types";
+
+// Kimi Code 是 OAuth 订阅 plan 提供商(与 API-key provider `kimi` 分离)。
+export const KIMI_CODE_PROVIDER = "kimi-code";
+export const KIMI_CODE_MODEL = "kimi-for-coding";
+
+// 连接流阶段:
+// - idle:未发起且未连接(展示「连接」按钮)。
+// - connected:状态查询/job 完成显示已连接(展示「断开」)。
+// - awaiting:已拿 device code,正在等待用户在浏览器授权 + 轮询 job。
+// - error:device 启动失败 / 轮询失败 / 过期 / 拒绝(展示错误 + 允许重试)。
+export type ConnectPhase = "idle" | "awaiting" | "connected" | "error";
+
+// device 启动后用户面要展示的信息(无 token)。
+export interface DeviceDisplay {
+ userCode: string;
+ verificationUri: string;
+ verificationUriComplete: string | null;
+ expiresIn: number;
+ interval: number;
+}
+
+// 把 OAuthStartResponse 收窄成展示用 DeviceDisplay(缺字段给安全默认)。
+export function toDeviceDisplay(res: OAuthStartResponse): DeviceDisplay {
+ return {
+ userCode: res.user_code,
+ verificationUri: res.verification_uri,
+ verificationUriComplete: res.verification_uri_complete ?? null,
+ expiresIn: typeof res.expires_in === "number" ? res.expires_in : 0,
+ interval: typeof res.interval === "number" ? res.interval : 5,
+ };
+}
+
+// 优先打开的授权 URL:有 complete(带 user_code 预填)就用它,否则裸 verification_uri。
+export function authOpenUrl(device: DeviceDisplay): string {
+ return device.verificationUriComplete ?? device.verificationUri;
+}
+
+// 连接状态(GET .../oauth/status)收窄。
+export interface ConnectionStatus {
+ connected: boolean;
+ expiresAt: string | null;
+}
+
+export function toConnectionStatus(
+ res: OAuthStatusResponse,
+): ConnectionStatus {
+ return {
+ connected: res.connected === true,
+ expiresAt: typeof res.expires_at === "string" ? res.expires_at : null,
+ };
+}
+
+// kimi_oauth job 完成态的 result:{connected, provider}(**绝无 token**)。
+export function jobConnected(job: JobView): boolean {
+ return job.result?.["connected"] === true;
+}
+
+// 把「是否已发起连接 + 轮询状态」映射成连接阶段。
+// - 未发起(started=false):connected ? connected : idle。
+// - 已发起:done 且 job.connected → connected;error → error;否则 awaiting。
+export function connectPhase(args: {
+ started: boolean;
+ connected: boolean;
+ poll: PollState;
+}): ConnectPhase {
+ const { started, connected, poll } = args;
+ if (!started) {
+ return connected ? "connected" : "idle";
+ }
+ if (poll.status === "done") {
+ return poll.job !== null && jobConnected(poll.job) ? "connected" : "error";
+ }
+ if (poll.status === "error") {
+ return "error";
+ }
+ return "awaiting";
+}
+
+// 把 ISO8601 过期时刻格式化成可读文案(无效/缺省→null)。
+export function formatExpiresAt(iso: string | null): string | null {
+ if (!iso) return null;
+ const ms = Date.parse(iso);
+ if (Number.isNaN(ms)) return null;
+ return new Date(ms).toLocaleString("zh-CN", {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ });
+}
diff --git a/apps/web/lib/settings/providers.test.ts b/apps/web/lib/settings/providers.test.ts
new file mode 100644
index 0000000..2f2ad35
--- /dev/null
+++ b/apps/web/lib/settings/providers.test.ts
@@ -0,0 +1,107 @@
+import { describe, expect, it } from "vitest";
+
+import {
+ API_KEY_PROVIDERS,
+ KNOWN_PROVIDERS,
+ applyProviderChange,
+ defaultModelFor,
+ draftsToRoutingInput,
+ toRoutingDrafts,
+ type RoutingDraft,
+} from "./providers";
+
+describe("KNOWN_PROVIDERS", () => {
+ it("includes kimi-code as an OAuth provider with a fixed model", () => {
+ const kc = KNOWN_PROVIDERS.find((p) => p.id === "kimi-code");
+ expect(kc).toBeDefined();
+ expect(kc?.auth).toBe("oauth");
+ expect(kc?.defaultModel).toBe("kimi-for-coding");
+ });
+
+ it("API_KEY_PROVIDERS excludes the OAuth provider", () => {
+ expect(API_KEY_PROVIDERS.some((p) => p.id === "kimi-code")).toBe(false);
+ expect(API_KEY_PROVIDERS.some((p) => p.id === "deepseek")).toBe(true);
+ });
+
+ it("includes kimi-code-key as an api_key provider with a fixed model", () => {
+ const kck = KNOWN_PROVIDERS.find((p) => p.id === "kimi-code-key");
+ expect(kck).toBeDefined();
+ expect(kck?.auth).toBe("api_key");
+ expect(kck?.defaultModel).toBe("kimi-for-coding");
+ });
+
+ it("API_KEY_PROVIDERS includes the static-key Kimi Code provider", () => {
+ expect(API_KEY_PROVIDERS.some((p) => p.id === "kimi-code-key")).toBe(true);
+ });
+});
+
+describe("defaultModelFor", () => {
+ it("returns the OAuth provider's fixed model", () => {
+ expect(defaultModelFor("kimi-code")).toBe("kimi-for-coding");
+ });
+
+ it("returns the static-key Kimi Code provider's fixed model", () => {
+ expect(defaultModelFor("kimi-code-key")).toBe("kimi-for-coding");
+ });
+
+ it("returns empty string for api_key providers / unknown", () => {
+ expect(defaultModelFor("deepseek")).toBe("");
+ expect(defaultModelFor("nope")).toBe("");
+ });
+});
+
+describe("toRoutingDrafts", () => {
+ it("expands to all tiers, filling missing ones empty", () => {
+ const drafts = toRoutingDrafts([
+ { tier: "writer", provider: "deepseek", model: "deepseek-chat" },
+ ]);
+ expect(drafts.map((d) => d.tier)).toEqual(["writer", "analyst", "light"]);
+ expect(drafts[0]).toEqual({
+ tier: "writer",
+ provider: "deepseek",
+ model: "deepseek-chat",
+ });
+ expect(drafts[1]).toEqual({ tier: "analyst", provider: "", model: "" });
+ });
+});
+
+describe("applyProviderChange", () => {
+ it("auto-fills the OAuth provider's fixed model", () => {
+ const next = applyProviderChange(
+ { tier: "writer", provider: "", model: "" },
+ "kimi-code",
+ );
+ expect(next).toEqual({
+ tier: "writer",
+ provider: "kimi-code",
+ model: "kimi-for-coding",
+ });
+ });
+
+ it("clears the model when switching to an api_key provider", () => {
+ const next = applyProviderChange(
+ { tier: "writer", provider: "kimi-code", model: "kimi-for-coding" },
+ "deepseek",
+ );
+ expect(next).toEqual({ tier: "writer", provider: "deepseek", model: "" });
+ });
+
+ it("does not mutate the input draft", () => {
+ const draft: RoutingDraft = { tier: "writer", provider: "", model: "" };
+ applyProviderChange(draft, "kimi-code");
+ expect(draft).toEqual({ tier: "writer", provider: "", model: "" });
+ });
+});
+
+describe("draftsToRoutingInput", () => {
+ it("keeps only rows with both provider and model", () => {
+ const input = draftsToRoutingInput([
+ { tier: "writer", provider: "kimi-code", model: "kimi-for-coding" },
+ { tier: "analyst", provider: "", model: "" },
+ { tier: "light", provider: "deepseek", model: "" },
+ ]);
+ expect(input).toEqual([
+ { tier: "writer", provider: "kimi-code", model: "kimi-for-coding" },
+ ]);
+ });
+});
diff --git a/apps/web/lib/settings/providers.ts b/apps/web/lib/settings/providers.ts
index 3496567..8d0feac 100644
--- a/apps/web/lib/settings/providers.ts
+++ b/apps/web/lib/settings/providers.ts
@@ -1,16 +1,103 @@
+// 提供商鉴权方式:api_key(凭据行)或 oauth(device-flow 连接按钮,如 Kimi Code)。
+export type ProviderAuthKind = "api_key" | "oauth";
+
+export interface KnownProvider {
+ id: string;
+ label: string;
+ auth: ProviderAuthKind;
+ // OAuth 提供商在档位路由里用的默认 model(API-key 提供商由用户填写/后端默认)。
+ defaultModel?: string;
+}
+
// 已知提供商(UX §6.10)。后端按 provider 字符串识别;这里只是 UI 候选列表。
-export const KNOWN_PROVIDERS: { id: string; label: string }[] = [
- { id: "anthropic", label: "Anthropic" },
- { id: "deepseek", label: "DeepSeek" },
- { id: "kimi", label: "Kimi" },
- { id: "openai", label: "OpenAI" },
- { id: "qwen", label: "通义千问" },
- { id: "glm", label: "智谱 GLM" },
- { id: "gemini", label: "Gemini" },
+// `kimi-code` 是 OAuth 订阅 plan 提供商(device flow,伪造客户端头,有封号风险),与 API-key
+// 的 `kimi` 分离(K1.4)。`kimi-code-key` 是同一订阅 plan 的**静态 Console Key** 路径(ToS 合规、
+// 无伪造头)——普通 api_key 提供商,固定 model `kimi-for-coding`。
+export const KNOWN_PROVIDERS: KnownProvider[] = [
+ { id: "anthropic", label: "Anthropic", auth: "api_key" },
+ { id: "deepseek", label: "DeepSeek", auth: "api_key" },
+ { id: "kimi", label: "Kimi", auth: "api_key" },
+ { id: "openai", label: "OpenAI", auth: "api_key" },
+ { id: "qwen", label: "通义千问", auth: "api_key" },
+ { id: "glm", label: "智谱 GLM", auth: "api_key" },
+ { id: "gemini", label: "Gemini", auth: "api_key" },
+ {
+ id: "kimi-code-key",
+ label: "Kimi Code(订阅 Key)",
+ auth: "api_key",
+ defaultModel: "kimi-for-coding",
+ },
+ {
+ id: "kimi-code",
+ label: "Kimi Code(OAuth)",
+ auth: "oauth",
+ defaultModel: "kimi-for-coding",
+ },
];
+// API-key 凭据行只展示 api_key 提供商;OAuth 提供商有独立连接区。
+export const API_KEY_PROVIDERS: KnownProvider[] = KNOWN_PROVIDERS.filter(
+ (p) => p.auth === "api_key",
+);
+
export const TIER_LABELS: Record = {
writer: "写手档",
analyst: "分析档",
light: "轻量档",
};
+
+// 档位顺序(路由编辑器列出全部档位,缺省的也能配)。
+export const TIERS = ["writer", "analyst", "light"] as const;
+export type Tier = (typeof TIERS)[number];
+
+// 某 provider 在路由里使用的默认 model:OAuth 提供商有固定 model,其余给空串占位。
+export function defaultModelFor(providerId: string): string {
+ return (
+ KNOWN_PROVIDERS.find((p) => p.id === providerId)?.defaultModel ?? ""
+ );
+}
+
+// 单条档位路由的可编辑草稿(纯数据)。
+export interface RoutingDraft {
+ tier: string;
+ provider: string;
+ model: string;
+}
+
+export interface TierRouting {
+ tier: string;
+ provider: string;
+ model: string;
+ fallback?: string[] | null;
+}
+
+// 把已存路由(可能缺档位)展开成「全部档位」的可编辑草稿(缺的给空)。
+export function toRoutingDrafts(existing: TierRouting[]): RoutingDraft[] {
+ const byTier = new Map(existing.map((r) => [r.tier, r]));
+ return TIERS.map((tier) => {
+ const row = byTier.get(tier);
+ return {
+ tier,
+ provider: row?.provider ?? "",
+ model: row?.model ?? "",
+ };
+ });
+}
+
+// 选择 provider 时自动套用该 provider 的默认 model(OAuth 固定 model;否则保留已填值或清空)。
+export function applyProviderChange(
+ draft: RoutingDraft,
+ providerId: string,
+): RoutingDraft {
+ const def = defaultModelFor(providerId);
+ return { ...draft, provider: providerId, model: def !== "" ? def : "" };
+}
+
+// 草稿 → PUT body 的 tier_routing:只取选了 provider+model 的行(不可变)。
+export function draftsToRoutingInput(
+ drafts: RoutingDraft[],
+): { tier: string; provider: string; model: string }[] {
+ return drafts
+ .filter((d) => d.provider.trim() !== "" && d.model.trim() !== "")
+ .map((d) => ({ tier: d.tier, provider: d.provider, model: d.model }));
+}
diff --git a/apps/web/lib/settings/useKimiOauth.ts b/apps/web/lib/settings/useKimiOauth.ts
new file mode 100644
index 0000000..98b5476
--- /dev/null
+++ b/apps/web/lib/settings/useKimiOauth.ts
@@ -0,0 +1,139 @@
+"use client";
+
+import { useCallback, useEffect, useRef, useState } from "react";
+
+import { api } from "@/lib/api/client";
+import { useToast } from "@/components/Toast";
+import { useJobPoll } from "@/lib/jobs/useJobPoll";
+import {
+ authOpenUrl,
+ connectPhase,
+ toConnectionStatus,
+ toDeviceDisplay,
+ type ConnectPhase,
+ type DeviceDisplay,
+} from "./kimiOauth";
+
+const START = "/settings/providers/kimi-code/oauth/start";
+const DISCONNECT = "/settings/providers/kimi-code/oauth/disconnect";
+
+export interface UseKimiOauth {
+ // 派生连接阶段(idle/awaiting/connected/error),驱动 UI。
+ phase: ConnectPhase;
+ // device 启动后展示给用户的信息(user_code + 验证 URL);未启动→null。
+ device: DeviceDisplay | null;
+ // 已连接时的 access token 过期时刻(ISO8601;可能为 null)。
+ expiresAt: string | null;
+ // 任一在途请求(start/disconnect)或正在轮询。
+ busy: boolean;
+ // 错误文案(device 启动失败 / 轮询失败 / 过期 / 拒绝)。
+ error: string | null;
+ // 发起连接:POST start → 拿 user_code + job_id → 开浏览器 + 轮询 job。
+ connect: () => Promise;
+ // 断开:POST disconnect → 复位为未连接。
+ disconnect: () => Promise;
+}
+
+// 在浏览器打开授权页(device complete URL 优先)。SSR/无 window 时静默跳过。
+function openVerification(device: DeviceDisplay): void {
+ if (typeof window === "undefined") return;
+ window.open(authOpenUrl(device), "_blank", "noopener,noreferrer");
+}
+
+// Kimi Code OAuth device-flow 连接编排(K1.4)。
+// connect:POST .../oauth/start(202)→ 展示 user_code + 开浏览器 → 轮询 job 到 done/failed。
+// status 进页传入 initialConnected/initialExpiresAt(Server Component 取)。
+export function useKimiOauth(args: {
+ initialConnected: boolean;
+ initialExpiresAt: string | null;
+}): UseKimiOauth {
+ const [connected, setConnected] = useState(args.initialConnected);
+ const [expiresAt, setExpiresAt] = useState(
+ args.initialExpiresAt,
+ );
+ const [device, setDevice] = useState(null);
+ const [started, setStarted] = useState(false);
+ const [inFlight, setInFlight] = useState(false);
+ const poll = useJobPoll();
+ const toast = useToast();
+ const startedRef = useRef(false);
+
+ const phase = connectPhase({ started, connected, poll });
+
+ // 轮询终态:done 且 job.connected → 已连接(刷新状态);error → 提示。
+ useEffect(() => {
+ if (!startedRef.current) return;
+ if (poll.status === "done") {
+ void refreshStatus().then((ok) => {
+ if (ok) {
+ setConnected(true);
+ toast("已连接 Kimi Code。", "success");
+ }
+ });
+ }
+ if (poll.status === "error") {
+ toast(`连接失败:${poll.error ?? "授权未完成或已过期"}`, "error");
+ }
+ // 仅在 poll.status 变化时反应。
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [poll.status]);
+
+ const refreshStatus = useCallback(async (): Promise => {
+ const { data, error } = await api.GET(
+ "/settings/providers/kimi-code/oauth/status",
+ );
+ if (error || !data) return false;
+ const status = toConnectionStatus(data);
+ setExpiresAt(status.expiresAt);
+ return status.connected;
+ }, []);
+
+ const connect = useCallback(async () => {
+ setInFlight(true);
+ try {
+ const { data, error } = await api.POST(START, {});
+ if (error || !data) {
+ toast("发起 Kimi Code 连接失败,请稍后重试。", "error");
+ return;
+ }
+ const dev = toDeviceDisplay(data);
+ setDevice(dev);
+ setStarted(true);
+ startedRef.current = true;
+ openVerification(dev);
+ poll.poll(data.job_id);
+ } finally {
+ setInFlight(false);
+ }
+ }, [poll, toast]);
+
+ const disconnect = useCallback(async () => {
+ setInFlight(true);
+ try {
+ const { data, error } = await api.POST(DISCONNECT, {});
+ if (error || !data) {
+ toast("断开 Kimi Code 失败,请稍后重试。", "error");
+ return;
+ }
+ poll.reset();
+ startedRef.current = false;
+ setStarted(false);
+ setDevice(null);
+ setConnected(false);
+ setExpiresAt(null);
+ toast("已断开 Kimi Code。", "success");
+ } finally {
+ setInFlight(false);
+ }
+ }, [poll, toast]);
+
+ return {
+ phase,
+ device,
+ expiresAt,
+ busy: inFlight || (startedRef.current && poll.status === "polling"),
+ error: poll.status === "error" ? poll.error : null,
+ connect,
+ disconnect,
+ };
+}
diff --git a/apps/web/lib/skills/skills.test.ts b/apps/web/lib/skills/skills.test.ts
new file mode 100644
index 0000000..744e64e
--- /dev/null
+++ b/apps/web/lib/skills/skills.test.ts
@@ -0,0 +1,38 @@
+import { describe, expect, it } from "vitest";
+
+import type { SkillView } from "@/lib/api/types";
+import { groupByScope, scopeLabel, tierLabel } from "./skills";
+
+const skill = (over: Partial): SkillView => ({
+ name: "x",
+ scope: "builtin",
+ tier: "analyst",
+ ...over,
+});
+
+describe("scopeLabel / tierLabel", () => {
+ it("maps known values, passes through unknown", () => {
+ expect(scopeLabel("builtin")).toBe("内置");
+ expect(scopeLabel("mystery")).toBe("mystery");
+ expect(tierLabel("writer")).toBe("写手");
+ expect(tierLabel("xl")).toBe("xl");
+ });
+});
+
+describe("groupByScope", () => {
+ it("groups by scope in first-seen order, preserves within-group order", () => {
+ const skills: SkillView[] = [
+ skill({ name: "a", scope: "builtin" }),
+ skill({ name: "b", scope: "custom" }),
+ skill({ name: "c", scope: "builtin" }),
+ ];
+ const groups = groupByScope(skills);
+ expect(groups.map((g) => g.scope)).toEqual(["builtin", "custom"]);
+ expect(groups[0].skills.map((s) => s.name)).toEqual(["a", "c"]);
+ expect(groups[1].skills.map((s) => s.name)).toEqual(["b"]);
+ });
+
+ it("handles undefined", () => {
+ expect(groupByScope(undefined)).toEqual([]);
+ });
+});
diff --git a/apps/web/lib/skills/skills.ts b/apps/web/lib/skills/skills.ts
new file mode 100644
index 0000000..e1b017c
--- /dev/null
+++ b/apps/web/lib/skills/skills.ts
@@ -0,0 +1,44 @@
+// 技能库纯逻辑:按 scope 分组、档位文案。
+// 对齐 C3 扩(GET /skills,只读注册表视图)。纯逻辑,便于 node 环境单测。
+
+import type { SkillView } from "@/lib/api/types";
+
+export type SkillScope = "builtin" | "custom" | "community";
+
+export const SCOPE_LABELS: Record = {
+ builtin: "内置",
+ custom: "自定义",
+ community: "社区",
+};
+
+export const TIER_LABELS: Record = {
+ writer: "写手",
+ analyst: "分析",
+ light: "轻量",
+};
+
+export function scopeLabel(scope: string): string {
+ return SCOPE_LABELS[scope] ?? scope;
+}
+
+export function tierLabel(tier: string): string {
+ return TIER_LABELS[tier] ?? tier;
+}
+
+export type SkillGroups = { scope: string; skills: SkillView[] }[];
+
+// 按 scope 分组(保持服务端 name 升序);scope 顺序按首次出现。
+export function groupByScope(
+ skills: readonly SkillView[] | undefined,
+): SkillGroups {
+ const order: string[] = [];
+ const map = new Map();
+ for (const skill of skills ?? []) {
+ if (!map.has(skill.scope)) {
+ order.push(skill.scope);
+ map.set(skill.scope, []);
+ }
+ map.get(skill.scope)?.push(skill);
+ }
+ return order.map((scope) => ({ scope, skills: map.get(scope) ?? [] }));
+}
diff --git a/apps/web/lib/style/style.test.ts b/apps/web/lib/style/style.test.ts
new file mode 100644
index 0000000..ef52743
--- /dev/null
+++ b/apps/web/lib/style/style.test.ts
@@ -0,0 +1,127 @@
+import { describe, expect, it } from "vitest";
+
+import type {
+ ReviewHistoryItem,
+ StyleFingerprintResponse,
+} from "@/lib/api/types";
+import {
+ buildLearnRequest,
+ buildRefineRequest,
+ hasUsableSamples,
+ narrowStyleEvent,
+ normalizeFingerprint,
+ normalizeStyleDrift,
+} from "./style";
+
+const reviewItem = (over: Partial): ReviewHistoryItem => ({
+ id: "00000000-0000-0000-0000-000000000001",
+ project_id: "00000000-0000-0000-0000-000000000002",
+ chapter_no: 1,
+ ...over,
+});
+
+describe("normalizeFingerprint", () => {
+ it("aligns dims with evidence by name, preserving key order", () => {
+ const resp: StyleFingerprintResponse = {
+ dimensions: { 句长: "偏短", 比喻密度: "高" },
+ evidence: { 句长: ["他来了。"], 比喻密度: ["如龙似虎", "若即若离"] },
+ version: 2,
+ };
+ const fp = normalizeFingerprint(resp);
+ expect(fp).toEqual({
+ version: 2,
+ dimensions: [
+ { name: "句长", value: "偏短", evidence: ["他来了。"] },
+ { name: "比喻密度", value: "高", evidence: ["如龙似虎", "若即若离"] },
+ ],
+ });
+ });
+
+ it("coerces non-string dim values and missing evidence", () => {
+ const fp = normalizeFingerprint({
+ dimensions: { 节奏: 5, 视角: true },
+ evidence: { 节奏: ["x", 1] },
+ version: 1,
+ });
+ expect(fp?.dimensions).toEqual([
+ { name: "节奏", value: "5", evidence: ["x"] },
+ { name: "视角", value: "true", evidence: [] },
+ ]);
+ });
+
+ it("returns null when fingerprint is undefined", () => {
+ expect(normalizeFingerprint(undefined)).toBeNull();
+ });
+});
+
+describe("normalizeStyleDrift", () => {
+ it("tightens style dict into report, filtering bad segments", () => {
+ const out = normalizeStyleDrift(
+ reviewItem({
+ style: {
+ score: 87,
+ segments: [
+ { idx: 3, score: 60, label: "口语化" },
+ { idx: 5, score: 72 },
+ "junk",
+ ],
+ },
+ }),
+ );
+ expect(out).toEqual({
+ score: 87,
+ segments: [
+ { idx: 3, score: 60, label: "口语化" },
+ { idx: 5, score: 72, label: null },
+ ],
+ });
+ });
+
+ it("defaults score to 100 (degrade态) and returns null when missing", () => {
+ expect(normalizeStyleDrift(reviewItem({ style: {} }))).toEqual({
+ score: 100,
+ segments: [],
+ });
+ expect(normalizeStyleDrift(reviewItem({ style: null }))).toBeNull();
+ expect(normalizeStyleDrift(undefined)).toBeNull();
+ });
+});
+
+describe("narrowStyleEvent", () => {
+ it("narrows SSE style event data with label fallback", () => {
+ expect(
+ narrowStyleEvent({ score: 90, segments: [{ idx: 1, score: 50 }] }),
+ ).toEqual({ score: 90, segments: [{ idx: 1, score: 50, label: null }] });
+ });
+
+ it("returns degrade态 for non-object data", () => {
+ expect(narrowStyleEvent(null)).toEqual({ score: 100, segments: [] });
+ });
+});
+
+describe("buildLearnRequest", () => {
+ it("trims and drops empty samples; passes mode through", () => {
+ expect(buildLearnRequest([" 甲 ", "", "乙"], "update")).toEqual({
+ samples: ["甲", "乙"],
+ mode: "update",
+ });
+ });
+});
+
+describe("hasUsableSamples", () => {
+ it("true only when at least one non-blank sample", () => {
+ expect(hasUsableSamples(["", " "])).toBe(false);
+ expect(hasUsableSamples(["", "正文"])).toBe(true);
+ });
+});
+
+describe("buildRefineRequest", () => {
+ it("trims segment and omits empty instruction", () => {
+ expect(buildRefineRequest(" 这一段 ")).toEqual({ segment: "这一段" });
+ expect(buildRefineRequest("段", " ")).toEqual({ segment: "段" });
+ expect(buildRefineRequest("段", " 更紧凑 ")).toEqual({
+ segment: "段",
+ instruction: "更紧凑",
+ });
+ });
+});
diff --git a/apps/web/lib/style/style.ts b/apps/web/lib/style/style.ts
new file mode 100644
index 0000000..381e2e7
--- /dev/null
+++ b/apps/web/lib/style/style.ts
@@ -0,0 +1,129 @@
+// 文风纯逻辑:指纹归一(dims+evidence)、漂移归一(从 chapter_reviews.style)、
+// 学文风/回炉请求体组装。纯逻辑,便于 node 环境单测(C3 扩 T4.3 / C4 扩 T4.2)。
+
+import type {
+ ReviewHistoryItem,
+ StyleFingerprintResponse,
+ StyleLearnRequest,
+ RefineRequest,
+} from "@/lib/api/types";
+import type { StyleDriftReport, StyleDriftSegment } from "@/lib/review/sse";
+
+export type { StyleDriftReport, StyleDriftSegment } from "@/lib/review/sse";
+
+// ── 指纹(16 维 + 证据,UX §6.9)─────────────────────────────────
+// 后端 dimensions={name:value}、evidence={name:[quotes]}(松散 JSONB)。
+export interface FingerprintDimension {
+ name: string;
+ value: string;
+ evidence: string[];
+}
+
+export interface Fingerprint {
+ dimensions: FingerprintDimension[];
+ version: number;
+}
+
+function asStringArray(v: unknown): string[] {
+ if (!Array.isArray(v)) return [];
+ return v.filter((x): x is string => typeof x === "string");
+}
+
+function asDisplayValue(v: unknown): string {
+ if (typeof v === "string") return v;
+ if (typeof v === "number" || typeof v === "boolean") return String(v);
+ return "";
+}
+
+// 把松散 GET /style 响应收窄成 Fingerprint。
+// dimensions 的 key 顺序即维度顺序(身份);evidence 按维度名对齐。
+export function normalizeFingerprint(
+ resp: StyleFingerprintResponse | undefined,
+): Fingerprint | null {
+ if (!resp) return null;
+ const dims = resp.dimensions ?? {};
+ const evid = resp.evidence ?? {};
+ const names = Object.keys(dims);
+ const dimensions: FingerprintDimension[] = names.map((name) => ({
+ name,
+ value: asDisplayValue(dims[name]),
+ evidence: asStringArray(evid[name]),
+ }));
+ return { dimensions, version: resp.version };
+}
+
+// ── 漂移(第四审,C4 扩 T4.2)────────────────────────────────────
+// chapter_reviews.style = {score:int, segments:[{idx,score,label?}]}。
+// 类型 StyleDriftReport/StyleDriftSegment 在 lib/review/sse.ts(reduce 复用),上面已 re-export。
+
+function asInt(v: unknown, fallback = 0): number {
+ return typeof v === "number" && Number.isFinite(v) ? Math.round(v) : fallback;
+}
+
+function asOptionalString(v: unknown): string | null {
+ return typeof v === "string" ? v : null;
+}
+
+// 把松散 style dict 收窄成漂移报告;缺失/非 dict → null(不渲染)。
+export function normalizeStyleDrift(
+ item: ReviewHistoryItem | undefined,
+): StyleDriftReport | null {
+ const raw = item?.style;
+ if (typeof raw !== "object" || raw === null) return null;
+ const dict = raw as Record;
+ const segRaw = Array.isArray(dict["segments"]) ? dict["segments"] : [];
+ const segments: StyleDriftSegment[] = segRaw
+ .filter((s): s is Record => typeof s === "object" && s !== null)
+ .map((s) => ({
+ idx: asInt(s["idx"]),
+ score: asInt(s["score"], 100),
+ label: asOptionalString(s["label"]),
+ }));
+ // score 缺省 100(无指纹降级态,对齐后端默认)。
+ return { score: asInt(dict["score"], 100), segments };
+}
+
+// 同 style SSE 事件 data 也用此收窄(reduceReview case "style" 复用)。
+export function narrowStyleEvent(data: unknown): StyleDriftReport {
+ if (typeof data !== "object" || data === null) {
+ return { score: 100, segments: [] };
+ }
+ const dict = data as Record;
+ const segRaw = Array.isArray(dict["segments"]) ? dict["segments"] : [];
+ const segments: StyleDriftSegment[] = segRaw
+ .filter((s): s is Record => typeof s === "object" && s !== null)
+ .map((s) => ({
+ idx: asInt(s["idx"]),
+ score: asInt(s["score"], 100),
+ label: asOptionalString(s["label"]),
+ }));
+ return { score: asInt(dict["score"], 100), segments };
+}
+
+// ── 请求体组装 ──────────────────────────────────────────────────
+export type StyleLearnMode = "create" | "update";
+
+// 组装学文风请求体:去掉空白样本;mode 透传(首学/更新仅前端语义)。
+export function buildLearnRequest(
+ samples: readonly string[],
+ mode: StyleLearnMode,
+): StyleLearnRequest {
+ const cleaned = samples.map((s) => s.trim()).filter((s) => s.length > 0);
+ return { samples: cleaned, mode };
+}
+
+// 至少一条非空样本才可提交(对齐后端 min_length=1)。
+export function hasUsableSamples(samples: readonly string[]): boolean {
+ return samples.some((s) => s.trim().length > 0);
+}
+
+// 组装回炉请求体(段去空白;空指令省略)。
+export function buildRefineRequest(
+ segment: string,
+ instruction?: string | null,
+): RefineRequest {
+ const body: RefineRequest = { segment: segment.trim() };
+ const trimmed = instruction?.trim();
+ if (trimmed) body.instruction = trimmed;
+ return body;
+}
diff --git a/apps/web/lib/style/useRefine.ts b/apps/web/lib/style/useRefine.ts
new file mode 100644
index 0000000..57aeb35
--- /dev/null
+++ b/apps/web/lib/style/useRefine.ts
@@ -0,0 +1,91 @@
+"use client";
+
+import { useCallback, useState } from "react";
+
+import { api } from "@/lib/api/client";
+import { useToast } from "@/components/Toast";
+import type { RefineResponse } from "@/lib/api/types";
+import { buildRefineRequest } from "./style";
+
+export type RefineStatus = "idle" | "refining" | "done" | "error";
+
+export interface RefineResult {
+ original: string;
+ refined: string;
+}
+
+export interface UseRefine {
+ status: RefineStatus;
+ result: RefineResult | null;
+ // 回炉重写某段(可选改写指令)→ 返回 {original, refined} 或 null(失败)。
+ refine: (
+ projectId: string,
+ chapterNo: number,
+ segment: string,
+ instruction?: string,
+ ) => Promise;
+ reset: () => void;
+}
+
+function errorCode(error: unknown): string | undefined {
+ if (typeof error !== "object" || error === null) return undefined;
+ const env = error as { error?: { code?: unknown } };
+ return typeof env.error?.code === "string" ? env.error.code : undefined;
+}
+
+// 回炉(POST .../refine):同步返回新旧 diff,不写库(不变量#3)。
+// 503 LLM_UNAVAILABLE(无凭据)优雅提示去设置;其余失败 toast。
+export function useRefine(): UseRefine {
+ const [status, setStatus] = useState("idle");
+ const [result, setResult] = useState(null);
+ const toast = useToast();
+
+ const refine = useCallback(
+ async (projectId, chapterNo, segment, instruction) => {
+ setStatus("refining");
+ setResult(null);
+ try {
+ const { data, error } = await api.POST(
+ "/projects/{project_id}/chapters/{chapter_no}/refine",
+ {
+ params: {
+ path: { project_id: projectId, chapter_no: chapterNo },
+ },
+ body: buildRefineRequest(segment, instruction),
+ },
+ );
+ if (error || !data) {
+ setStatus("error");
+ const code = errorCode(error);
+ toast(
+ code === "LLM_UNAVAILABLE"
+ ? "未配置提供商,请先去设置页连一家。"
+ : "回炉失败,请稍后重试。",
+ "error",
+ );
+ return null;
+ }
+ const next = data as RefineResponse;
+ const outcome: RefineResult = {
+ original: next.original,
+ refined: next.refined,
+ };
+ setResult(outcome);
+ setStatus("done");
+ return outcome;
+ } catch {
+ setStatus("error");
+ toast("回炉请求异常,请检查网络。", "error");
+ return null;
+ }
+ },
+ [toast],
+ );
+
+ const reset = useCallback((): void => {
+ setStatus("idle");
+ setResult(null);
+ }, []);
+
+ return { status, result, refine, reset };
+}
diff --git a/apps/web/lib/style/useStyleLearn.ts b/apps/web/lib/style/useStyleLearn.ts
new file mode 100644
index 0000000..8e36ec1
--- /dev/null
+++ b/apps/web/lib/style/useStyleLearn.ts
@@ -0,0 +1,123 @@
+"use client";
+
+import { useCallback, useEffect, useRef, useState } from "react";
+
+import { api } from "@/lib/api/client";
+import { useToast } from "@/components/Toast";
+import type { StyleFingerprintResponse } from "@/lib/api/types";
+import { useJobPoll } from "@/lib/jobs/useJobPoll";
+import { styleLearnResult } from "@/lib/jobs/job";
+import {
+ buildLearnRequest,
+ normalizeFingerprint,
+ type Fingerprint,
+ type StyleLearnMode,
+} from "./style";
+
+export interface UseStyleLearn {
+ // 提交中(POST /style 受理)或轮询中。
+ busy: boolean;
+ pollStatus: ReturnType["status"] | "idle";
+ progress: number;
+ fingerprint: Fingerprint | null;
+ // 学文风:POST /style → 拿 job_id → 轮询 → done 后拉最新指纹。
+ learn: (
+ projectId: string,
+ samples: string[],
+ mode: StyleLearnMode,
+ ) => Promise;
+}
+
+function errorCode(error: unknown): string | undefined {
+ if (typeof error !== "object" || error === null) return undefined;
+ const env = error as { error?: { code?: unknown } };
+ return typeof env.error?.code === "string" ? env.error.code : undefined;
+}
+
+// 学文风编排:受理(202 job_id)→ useJobPoll 轮询 → done 拉 GET /style 展示指纹。
+export function useStyleLearn(initial: Fingerprint | null): UseStyleLearn {
+ const [fingerprint, setFingerprint] = useState(initial);
+ const [submitting, setSubmitting] = useState(false);
+ const [pollStatus, setPollStatus] = useState<
+ ReturnType["status"] | "idle"
+ >("idle");
+ const [projectId, setProjectId] = useState(null);
+ const poll = useJobPoll();
+ const toast = useToast();
+ // 仅在提交过一次学文风后才反映轮询状态(initialPollState.status 默认 "polling")。
+ const startedRef = useRef(false);
+
+ // 轮询完成 → 拉最新指纹;失败 → toast。
+ useEffect(() => {
+ if (!startedRef.current) return;
+ setPollStatus(poll.status);
+ if (poll.status === "done" && projectId) {
+ void refetchFingerprint(projectId).then((fp) => {
+ if (fp) setFingerprint(fp);
+ toast("文风指纹已更新。", "success");
+ });
+ }
+ if (poll.status === "error") {
+ toast(`学文风失败:${poll.error ?? "未知原因"}`, "error");
+ }
+ // 仅在 poll.status 变化时反应。
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [poll.status]);
+
+ const learn = useCallback(
+ async (pid, samples, mode) => {
+ setSubmitting(true);
+ setProjectId(pid);
+ try {
+ const { data, error } = await api.POST("/projects/{project_id}/style", {
+ params: { path: { project_id: pid } },
+ body: buildLearnRequest(samples, mode),
+ });
+ if (error || !data) {
+ const code = errorCode(error);
+ toast(
+ code === "LLM_UNAVAILABLE"
+ ? "未配置提供商,请先去设置页连一家。"
+ : "学文风受理失败,请稍后重试。",
+ "error",
+ );
+ return false;
+ }
+ startedRef.current = true;
+ poll.poll(data.job_id);
+ setPollStatus("polling");
+ return true;
+ } finally {
+ setSubmitting(false);
+ }
+ },
+ [poll, toast],
+ );
+
+ return {
+ busy: submitting || pollStatus === "polling",
+ pollStatus,
+ progress: poll.progress,
+ fingerprint,
+ learn,
+ };
+}
+
+// 客户端拉最新指纹(done 后刷新展示);404/失败 → null。
+async function refetchFingerprint(
+ projectId: string,
+): Promise {
+ const { data, error } = await api.GET("/projects/{project_id}/style", {
+ params: { path: { project_id: projectId } },
+ });
+ if (error || !data) return null;
+ return normalizeFingerprint(data as StyleFingerprintResponse);
+}
+
+// 仅供测试/复用:保证 job done result 的版本回显(不阻断 UI)。
+export function learnSummary(
+ poll: ReturnType,
+): { version: number | null; dimsCount: number | null } | null {
+ if (poll.status !== "done" || !poll.job) return null;
+ return styleLearnResult(poll.job);
+}
diff --git a/apps/web/lib/workbench/chapter.ts b/apps/web/lib/workbench/chapter.ts
new file mode 100644
index 0000000..dac1260
--- /dev/null
+++ b/apps/web/lib/workbench/chapter.ts
@@ -0,0 +1,5 @@
+// 工作台当前章号常量(纯模块,**非** "use client")。
+// 必须放在非客户端模块:Server Component(write/page.tsx)要在服务端用它拼 GET draft 的 URL;
+// 若从客户端组件(Workbench.tsx,"use client")导入,Next 会把它替换成客户端引用代理,
+// 服务端使用时变成抛错的函数 → draft URL 变成 `/chapters/function(){…}/draft`(422)。
+export const WORKBENCH_CHAPTER_NO = 1;
diff --git a/apps/web/tsconfig.tsbuildinfo b/apps/web/tsconfig.tsbuildinfo
index c32d512..327ff7f 100644
--- a/apps/web/tsconfig.tsbuildinfo
+++ b/apps/web/tsconfig.tsbuildinfo
@@ -1 +1 @@
-{"fileNames":["./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/global.d.ts","./node_modules/.pnpm/csstype@3.2.3/node_modules/csstype/index.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/amp.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/get-page-files.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/index.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/globals.typedarray.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/buffer.buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/globals.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/events.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/header.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/readable.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/file.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/fetch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/formdata.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/connector.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-origin.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool-stats.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/handlers.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/balanced-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-interceptor.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-handler.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/api.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/interceptors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/util.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cookies.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/patch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/websocket.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/eventsource.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/filereader.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/content-type.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cache.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/index.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/storage.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/assert.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/assert/strict.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/async_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/child_process.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/cluster.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/console.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/constants.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/crypto.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dgram.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dns.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dns/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/domain.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/events.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/fs.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/fs/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/http.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/http2.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/https.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/inspector.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/inspector.generated.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/module.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/net.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/os.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/path.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/perf_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/process.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/punycode.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/querystring.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/readline.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/readline/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/repl.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/sea.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/sqlite.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/consumers.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/web.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/string_decoder.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/test.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/timers.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/timers/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/tls.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/trace_events.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/tty.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/url.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/util.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/v8.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/vm.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/wasi.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/worker_threads.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/zlib.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/index.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/canary.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/experimental.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/index.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/canary.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/experimental.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/fallback.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/body-streams.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/revalidate.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/constants.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/require-hook.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/page-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-kind.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/render-result.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/load-components.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/with-router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/route-loader.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/page-loader.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/pages.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/render.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/next-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/adapter.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/clean-async-snapshot-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/clean-async-snapshot.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/jsx-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/client-page.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/search-params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/metadata-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/http.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/utils.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/export/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/export/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/after.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/after-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request-meta.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/cli/next-test.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/config-shared.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-http/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-http/node.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/.pnpm/sharp@0.33.5/node_modules/sharp/lib/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/next-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/trace.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/shared.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/swc/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/react-dev-overlay/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/telemetry/storage.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/next.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/.pnpm/@next+env@15.1.3/node_modules/@next/env/dist/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_app.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/app.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/runtime-config.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_document.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/document.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dynamic.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/head.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/head.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/image-component.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/link.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/link.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/not-found.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/navigation.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/navigation.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/script.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/script.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/connection.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types/global.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types/compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image-types/global.d.ts","./next-env.d.ts","./node_modules/.pnpm/source-map-js@1.2.1/node_modules/source-map-js/source-map.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/previous-map.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/input.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/declaration.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/root.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/warning.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/lazy-result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/no-work-result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/processor.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/document.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/rule.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/node.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/comment.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/container.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/at-rule.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/list.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/postcss.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/postcss.d.mts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/generated/corepluginlist.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/generated/colors.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/config.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/index.d.ts","./tailwind.config.ts","./node_modules/.pnpm/@types+estree@1.0.9/node_modules/@types/estree/index.d.ts","./node_modules/.pnpm/rollup@4.62.0/node_modules/rollup/dist/rollup.d.ts","./node_modules/.pnpm/rollup@4.62.0/node_modules/rollup/dist/parseast.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/hmrpayload.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/customevent.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/hot.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/types.d-agj9qkwt.d.ts","./node_modules/.pnpm/esbuild@0.21.5/node_modules/esbuild/lib/main.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/runtime.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/importglob.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/metadata.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/index.d.ts","./node_modules/.pnpm/@vitest+pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/types.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/index-c1cfc5e9.d.ts","./node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/index.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/tasks-3znpj1lr.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/types-bxe-2udy.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/types.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/error.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/environment.looobwuu.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/environment-ddx0edty.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/rawsnapshot-cpnkto81.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/environment.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/config.cy0c388z.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/trace-mapping.d-dlvdeqop.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/index-z0r8hvru.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/index.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/client.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/server.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/benchmark.geerunq4.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/reporters.nr4dxcka.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/vite.czkp4x9w.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/config.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/config.d.ts","./vitest.config.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/worker.tn5kgiih.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/worker.b9fxpcac.d.ts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/dist/chai.d.cts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/dist/index.d.ts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/index.d.ts","./node_modules/.pnpm/@vitest+spy@2.1.9/node_modules/@vitest/spy/dist/index.d.ts","./node_modules/.pnpm/@vitest+mocker@2.1.9_vite@5.4.21_@types+node@22.19.21_/node_modules/@vitest/mocker/dist/types-dzoqtgin.d.ts","./node_modules/.pnpm/@vitest+mocker@2.1.9_vite@5.4.21_@types+node@22.19.21_/node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/mocker.crtm890j.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/suite.b2jumifp.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/index.d.ts","./lib/api/client.test.ts","./node_modules/.pnpm/openapi-typescript-helpers@0.0.15/node_modules/openapi-typescript-helpers/index.d.ts","./node_modules/.pnpm/openapi-fetch@0.13.8/node_modules/openapi-fetch/dist/index.d.ts","./lib/api/schema.d.ts","./lib/api/config.ts","./lib/api/client.ts","./lib/api/types.ts","./lib/api/server.ts","./lib/autosave/autosave.ts","./lib/autosave/autosave.test.ts","./components/toast.tsx","./lib/autosave/useautosave.ts","./lib/foreshadow/board.ts","./lib/foreshadow/board.test.ts","./lib/foreshadow/useforeshadow.ts","./lib/outline/outline.ts","./lib/outline/outline.test.ts","./lib/outline/useoutline.ts","./lib/pace/beatmap.ts","./lib/pace/beatmap.test.ts","./lib/review/decisions.ts","./lib/review/decisions.test.ts","./lib/review/sse.ts","./lib/review/history.ts","./lib/review/history.test.ts","./lib/review/sse.test.ts","./lib/review/useaccept.ts","./lib/review/usereviewstream.ts","./lib/settings/providers.ts","./lib/stream/sse.ts","./lib/stream/sse.test.ts","./lib/stream/usedraftstream.ts","./lib/wizard/wizard.ts","./lib/wizard/wizard.test.ts","./app/layout.tsx","./components/leftnav.tsx","./components/appshell.tsx","./components/projectcard.tsx","./app/page.tsx","./components/foreshadow/foreshadowcard.tsx","./components/foreshadow/kanbancolumn.tsx","./components/foreshadow/registerform.tsx","./components/foreshadow/foreshadowboard.tsx","./app/projects/[id]/foreshadow/page.tsx","./components/outline/outlinechapterrow.tsx","./components/outline/outlineeditor.tsx","./app/projects/[id]/outline/page.tsx","./components/review/acceptpanel.tsx","./components/review/annotatedtext.tsx","./components/review/conflictcard.tsx","./components/review/foreshadowsuggestions.tsx","./components/review/beatmap.tsx","./components/review/pacepanel.tsx","./components/review/reviewreport.tsx","./app/projects/[id]/review/page.tsx","./components/workbench/chapterlist.tsx","./components/workbench/chapterassistant.tsx","./components/workbench/editor.tsx","./components/workbench/workbench.tsx","./app/projects/[id]/write/page.tsx","./components/projectwizard.tsx","./app/projects/new/page.tsx","./components/settings/providerssettings.tsx","./app/settings/providers/page.tsx","./.next/types/cache-life.d.ts","./.next/types/app/layout.ts","./.next/types/app/page.ts","./.next/types/app/projects/[id]/foreshadow/page.ts","./.next/types/app/projects/[id]/outline/page.ts","./.next/types/app/projects/[id]/review/page.ts","./.next/types/app/projects/[id]/write/page.ts","./.next/types/app/projects/new/page.ts","./.next/types/app/settings/providers/page.ts"],"fileIdsList":[[76,124,141,142,308,570],[76,124,141,142,308,574],[76,124,141,142,308,579],[76,124,141,142,308,582],[76,124,141,142,308,590],[76,124,141,142,308,595],[76,124,141,142,308,597],[76,124,141,142,308,599],[76,124,141,142,399,400,401,402],[76,124,141,142,447,546],[76,124,141,142,423,542,543,572,573],[76,124,141,142,431,543,578],[76,124,141,142,431,543,581],[76,124,141,142,431,543,559,589],[76,124,141,142,431,543,594],[76,124,141,142,572,596],[76,124,141,142,542,543,572,598],[62,76,124,141,142,423,571],[62,76,124,141,142,542,548,550,572,576,577],[62,76,124,141,142,542,548],[76,124,141,142,542,548,575],[62,76,124,141,142,548],[76,124,141,142,423,431],[76,124,141,142,542,551],[62,76,124,141,142,542,551,553,572,580],[76,124,141,142,423,542],[62,76,124,141,142,431,541,546,568],[76,124,141,142,542],[76,124,141,142,558],[76,124,141,142,554],[76,124,141,142,556,558],[76,124,141,142,558,587],[62,76,124,141,142,542,556,558,559,562,563,572,583,584,585,586,588],[62,76,124,141,142,541,542,546,564],[62,76,124,141,142],[76,124,141,142],[62,76,124,141,142,423,542,547,567,572,591,592,593],[76,124,141,142,535],[76,124,141,142,538,539,540],[76,124,141,142,540,542],[76,124,141,142,539],[76,124,141,142,535,544],[62,76,124,141,142,541,544,546],[76,124,141,142,535,542,548],[62,76,124,141,142,541,542,546,548],[76,124,141,142,535,542,551],[62,76,124,141,142,541,542,546],[76,124,141,142,535,554],[76,124,141,142,535,556],[76,124,141,142,535,542,559],[76,124,141,142,542,558],[76,124,141,142,535,558],[62,76,124,141,142,541,542,546,556],[62,76,124,141,142,540,558],[76,124,141,142,535,565],[62,76,124,141,142,540,565],[76,124,141,142,535,568],[76,124,141,142,447,448],[76,121,122,124,141,142],[76,123,124,141,142],[124,141,142],[76,124,129,141,142,159],[76,124,125,130,135,141,142,144,156,167],[76,124,125,126,135,141,142,144],[71,72,73,76,124,141,142],[76,124,127,141,142,168],[76,124,128,129,136,141,142,145],[76,124,129,141,142,156,164],[76,124,130,132,135,141,142,144],[76,123,124,131,141,142],[76,124,132,133,141,142],[76,124,134,135,141,142],[76,123,124,135,141,142],[76,124,135,136,137,141,142,156,167],[76,124,135,136,137,141,142,151,156,159],[76,117,124,132,135,138,141,142,144,156,167],[76,124,135,136,138,139,141,142,144,156,164,167],[76,124,138,140,141,142,156,164,167],[74,75,76,77,78,79,80,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],[76,124,135,141,142],[76,124,141,142,143,167],[76,124,132,135,141,142,144,156],[76,124,141,142,145],[76,124,141,142,146],[76,123,124,141,142,147],[76,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],[76,124,141,142,149],[76,124,141,142,150],[76,124,135,141,142,151,152],[76,124,141,142,151,153,168,170],[76,124,136,141,142],[76,124,135,141,142,156,157,159],[76,124,141,142,158,159],[76,124,141,142,156,157],[76,124,141,142,159],[76,124,141,142,160],[76,121,124,141,142,156,161,167],[76,124,135,141,142,162,163],[76,124,141,142,162,163],[76,124,129,141,142,144,156,164],[76,124,141,142,165],[76,124,141,142,144,166],[76,124,138,141,142,150,167],[76,124,129,141,142,168],[76,124,141,142,156,169],[76,124,141,142,143,170],[76,124,141,142,171],[76,117,124,141,142],[76,117,124,135,137,141,142,147,156,159,167,169,170,172],[76,124,141,142,156,173],[62,76,124,141,142,177,179],[62,76,124,141,142,177,178],[62,66,76,124,141,142,176,393,440],[62,66,76,124,141,142,175,393,440],[60,61,76,124,141,142],[76,124,141,142,491,492,495],[76,124,141,142,523],[76,124,141,142,526],[76,124,141,142,492,493,495,496,497],[76,124,141,142,492],[76,124,141,142,492,493,495],[76,124,141,142,492,493],[76,124,141,142,500],[76,124,141,142,487,500,501],[76,124,141,142,487,500],[76,124,141,142,487,494],[76,124,141,142,488],[76,124,141,142,487,488,489,491],[76,124,141,142,487],[76,124,141,142,530,531],[76,124,141,142,530,531,532,533],[76,124,141,142,530,532],[76,124,141,142,530],[68,76,124,141,142],[76,124,141,142,397],[76,124,141,142,404],[76,124,141,142,183,196,197,198,200,357],[76,124,141,142,183,187,189,190,191,192,346,357,359],[76,124,141,142,357],[76,124,141,142,197,213,290,337,353],[76,124,141,142,183],[76,124,141,142,377],[76,124,141,142,357,359,376],[76,124,141,142,276,290,318,445],[76,124,141,142,283,300,337,352],[76,124,141,142,238],[76,124,141,142,341],[76,124,141,142,340,341,342],[76,124,141,142,340],[70,76,124,138,141,142,180,183,190,193,194,195,197,201,269,274,320,328,338,348,357,393],[76,124,141,142,183,199,227,272,357,373,374,445],[76,124,141,142,199,445],[76,124,141,142,272,273,274,357,445],[76,124,141,142,445],[76,124,141,142,183,199,200,445],[76,124,141,142,193,339,345],[76,124,141,142,150,291,353],[76,124,141,142,291,353],[62,76,124,141,142,291],[62,76,124,141,142,270,291,292],[76,124,141,142,218,236,353,429],[76,124,141,142,334,424,425,426,427,428],[76,124,141,142,333],[76,124,141,142,333,334],[76,124,141,142,191,215,216,270],[76,124,141,142,217,218,270],[76,124,141,142,270],[62,76,124,141,142,184,418],[62,76,124,141,142,167],[62,76,124,141,142,199,225],[62,76,124,141,142,199],[76,124,141,142,223,228],[62,76,124,141,142,224,396],[62,66,76,124,138,141,142,174,175,176,393,438,439],[76,124,136,138,141,142,187,213,241,259,270,343,357,358,445],[76,124,141,142,328,344],[76,124,141,142,393],[76,124,141,142,182],[76,124,141,142,150,276,288,309,311,352,353],[76,124,141,142,150,276,288,308,309,310,352,353],[76,124,141,142,302,303,304,305,306,307],[76,124,141,142,304],[76,124,141,142,308],[62,76,124,141,142,224,291,396],[62,76,124,141,142,291,394,396],[62,76,124,141,142,291,396],[76,124,141,142,259,349],[76,124,141,142,349],[76,124,138,141,142,358,396],[76,124,141,142,296],[76,123,124,141,142,295],[76,124,141,142,209,210,212,242,270,283,284,285,287,320,352,355,358],[76,124,141,142,286],[76,124,141,142,210,218,270],[76,124,141,142,283,352],[76,124,141,142,283,292,293,294,296,297,298,299,300,301,312,313,314,315,316,317,352,353,445],[76,124,141,142,281],[76,124,138,141,142,150,187,208,210,212,213,214,218,246,259,268,269,320,348,357,358,359,393,445],[76,124,141,142,352],[76,123,124,141,142,197,212,269,285,300,348,350,351,358],[76,124,141,142,283],[76,123,124,141,142,208,242,262,277,278,279,280,281,282],[76,124,138,141,142,262,263,277,358,359],[76,124,141,142,197,259,269,270,285,348,352,358],[76,124,138,141,142,357,359],[76,124,138,141,142,156,355,358,359],[76,124,138,141,142,150,167,180,187,199,209,210,212,213,214,219,241,242,243,245,246,249,250,252,255,256,257,258,270,347,348,353,355,357,358,359],[76,124,138,141,142,156],[76,124,141,142,183,184,185,187,194,355,356,393,396,445],[76,124,138,141,142,156,167,203,375,377,378,379,445],[76,124,141,142,150,167,180,203,213,242,243,250,259,267,270,348,353,355,360,361,367,373,389,390],[76,124,141,142,193,194,269,328,339,348,357],[76,124,138,141,142,167,184,242,355,357,365],[76,124,141,142,275],[76,124,138,141,142,386,387,388],[76,124,141,142,355,357],[76,124,141,142,187,212,242,347,396],[76,124,138,141,142,150,250,259,355,361,367,369,373,389,392],[76,124,138,141,142,193,328,373,382],[76,124,141,142,183,219,347,357,384],[76,124,138,141,142,199,219,357,368,369,380,381,383,385],[70,76,124,141,142,210,211,212,393,396],[76,124,138,141,142,150,167,187,193,201,209,213,214,242,243,245,246,258,259,267,270,328,347,348,353,354,355,360,361,362,364,366,396],[76,124,138,141,142,156,193,355,367,386,391],[76,124,141,142,323,324,325,326,327],[76,124,141,142,249,251],[76,124,141,142,253],[76,124,141,142,251],[76,124,141,142,253,254],[76,124,138,141,142,187,208,358],[62,76,124,138,141,142,150,182,184,187,209,210,212,213,214,240,355,359,393,396],[76,124,138,141,142,150,167,186,191,242,354,358],[76,124,141,142,277],[76,124,141,142,278],[76,124,141,142,279],[76,124,141,142,202,206],[76,124,138,141,142,187,202,209],[76,124,141,142,205,206],[76,124,141,142,207],[76,124,141,142,202,203],[76,124,141,142,202,220],[76,124,141,142,202],[76,124,141,142,248,249,354],[76,124,141,142,247],[76,124,141,142,203,353,354],[76,124,141,142,244,354],[76,124,141,142,203,353],[76,124,141,142,320],[76,124,141,142,204,209,211,242,270,276,285,288,289,319,355,358],[76,124,141,142,218,229,232,233,234,235,236],[76,124,141,142,336],[76,124,141,142,197,211,212,263,270,283,296,300,329,330,331,332,334,335,338,347,352,357],[76,124,141,142,218],[76,124,141,142,240],[76,124,138,141,142,209,211,221,237,239,241,355,393,396],[76,124,141,142,218,229,230,231,232,233,234,235,236,394],[76,124,141,142,203],[76,124,141,142,263,264,267,348],[76,124,138,141,142,249,357],[76,124,138,141,142],[76,124,141,142,262,283],[76,124,141,142,261],[76,124,141,142,258,263],[76,124,141,142,260,262,357],[76,124,138,141,142,186,263,264,265,266,357,358],[62,76,124,141,142,215,217,270],[76,124,141,142,271],[62,76,124,141,142,184],[62,76,124,141,142,353],[62,70,76,124,141,142,212,214,393,396],[76,124,141,142,184,418,419],[62,76,124,141,142,228],[62,76,124,141,142,150,167,182,222,224,226,227,396],[76,124,141,142,199,353,358],[76,124,141,142,353,363],[62,76,124,136,138,141,142,150,182,228,272,393,394,395],[62,76,124,141,142,175,176,393,440],[62,63,64,65,66,76,124,141,142],[76,124,129,141,142],[76,124,141,142,370,371,372],[76,124,141,142,370],[62,66,76,124,138,140,141,142,150,174,175,176,177,179,180,182,246,308,359,392,396,440],[76,124,141,142,406],[76,124,141,142,408],[76,124,141,142,410],[76,124,141,142,412],[76,124,141,142,414,415,416],[76,124,141,142,420],[67,69,76,124,141,142,398,403,405,407,409,411,413,417,421,423,431,432,434,443,444,445,446],[76,124,141,142,422],[76,124,141,142,430],[76,124,141,142,224],[76,124,141,142,433],[76,123,124,141,142,263,264,265,267,299,353,435,436,437,440,441,442],[76,124,141,142,174],[76,124,141,142,537],[76,124,141,142,465],[76,124,141,142,463,465],[76,124,141,142,454,462,463,464,466,468],[76,124,141,142,452],[76,124,141,142,455,460,465,468],[76,124,141,142,451,468],[76,124,141,142,455,456,459,460,461,468],[76,124,141,142,455,456,457,459,460,468],[76,124,141,142,452,453,454,455,456,460,461,462,464,465,466,468],[76,124,141,142,468],[76,124,141,142,450,452,453,454,455,456,457,459,460,461,462,463,464,465,466,467],[76,124,141,142,450,468],[76,124,141,142,455,457,458,460,461,468],[76,124,141,142,459,468],[76,124,141,142,460,461,465,468],[76,124,141,142,453,463],[76,124,141,142,476,485],[76,124,141,142,475,476],[76,124,141,142,156,174],[76,124,141,142,470,471],[76,124,141,142,469,472],[76,124,141,142,490],[76,89,93,124,141,142,167],[76,89,124,141,142,156,167],[76,84,124,141,142],[76,86,89,124,141,142,164,167],[76,124,141,142,144,164],[76,84,124,141,142,174],[76,86,89,124,141,142,144,167],[76,81,82,85,88,124,135,141,142,156,167],[76,89,96,124,141,142],[76,81,87,124,141,142],[76,89,110,111,124,141,142],[76,85,89,124,141,142,159,167,174],[76,110,124,141,142,174],[76,83,84,124,141,142,174],[76,89,124,141,142],[76,83,84,85,86,87,88,89,90,91,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,111,112,113,114,115,116,124,141,142],[76,89,104,124,141,142],[76,89,96,97,124,141,142],[76,87,89,97,98,124,141,142],[76,88,124,141,142],[76,81,84,89,124,141,142],[76,89,93,97,98,124,141,142],[76,93,124,141,142],[76,87,89,92,124,141,142,167],[76,81,86,89,96,124,141,142],[76,124,141,142,156],[76,84,89,110,124,141,142,172,174],[76,124,141,142,505,506],[76,124,141,142,505],[76,124,141,142,486,505,506,516],[76,124,135,136,138,139,140,141,142,144,156,164,167,173,174,469,476,477,478,479,480,481,482,483,484,485],[76,124,141,142,478,479,480,481],[76,124,141,142,478,479,480],[76,124,141,142,478],[76,124,141,142,479],[76,124,141,142,476],[76,124,141,142,517],[76,124,141,142,498,511,512,535],[76,124,141,142,487,498,502,503,535],[76,124,141,142,527],[76,124,136,141,142,156,486,487,492,498,499,502,504,507,508,509,510,513,514,516,522,535],[76,124,141,142,498,511,512,513,535],[76,124,141,142,486,515],[76,124,141,142,172,520],[76,124,141,142,498,499,502,504,507,535],[76,124,136,141,142,156,486,487,492,498,499,502,503,504,507,508,509,510,511,512,513,514,515,516,522,535],[76,124,136,141,142,156,172,486,487,492,495,498,499,502,503,504,507,508,509,510,511,512,513,514,515,516,520,521,522,524,525,527,528,529,534,535],[76,124,141,142,473],[76,124,141,142,146,518]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"a305ee2f90e34e9e70aba9a9e9a154ce20c4d5cd1499cd21b8dc3617e1e5c810","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"9018e9493596e2d041fc531f5101a0d05f9d2e1311f4ae3c74345b1766158c08","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"865a2612f5ec073dd48d454307ccabb04c48f8b96fda9940c5ebfe6b4b451f51","impliedFormat":1},{"version":"89e7fd23f6e6ced38596054161f5fb88737018909c6529c946cb349b74b95275","impliedFormat":1},{"version":"115b2ad73fa7d175cd71a5873d984c21593b2a022f1a2036cc39d9f53629e5dc","impliedFormat":1},{"version":"1be330b3a0b00590633f04c3b35db7fa618c9ee079258e2b24c137eb4ffcd728","impliedFormat":1},{"version":"bb7a61dd55dc4b9422d13da3a6bb9cc5e89be888ef23bbcf6558aa9726b89a1c","impliedFormat":1},{"version":"3da0083607976261730c44908eab1b6262f727747ef3230a65ecd0153d9e8639","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"dd721e5707f241e4ef4ab36570d9e2a79f66aad63a339e3cbdbac7d9164d2431","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"3849a7f92d0e11b785f6ae7bedb25d9aad8d1234b3f1cf530a4e7404be26dd0a","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"57d6ac03382e30e9213641ff4f18cf9402bb246b77c13c8e848c0b1ca2b7ef92","impliedFormat":1},{"version":"f040772329d757ecd38479991101ef7bc9bf8d8f4dd8ee5d96fe00aa264f2a2b","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"57e47d02e88abef89d214cdf52b478104dc17997015746e288cbb580beaef266","impliedFormat":1},{"version":"04a2d0bd8166f057cc980608bd5898bfc91198636af3c1eb6cb4eb5e8652fbea","impliedFormat":1},{"version":"376c21ad92ca004531807ea4498f90a740fd04598b45a19335a865408180eddd","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9af0e608929aaf9ce96bd7a7b99c9360636c31d73670e4af09a09950df97841","impliedFormat":1},{"version":"48d37b90a04e753a925228f50304d02c4f95d57bf682f8bb688621c3cd9d32ec","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"b68c4ed987ef5693d3dccd85222d60769463aca404f2ffca1c4c42781dce388e","impliedFormat":1},{"version":"cfb5b5d514eb4ad0ee25f313b197f3baa493eee31f27613facd71efb68206720","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"9715fe982fccf375c88ac4d3cc8f6a126a7b7596be8d60190a0c7d22b45b4be4","impliedFormat":1},{"version":"1fe24e25a00c7dd689cb8c0fb4f1048b4a6d1c50f76aaca2ca5c6cdb44e01442","impliedFormat":1},{"version":"672f293c53a07b8c1c1940797cd5c7984482a0df3dd9c1f14aaee8d3474c2d83","impliedFormat":1},{"version":"0a66cb2511fa8e3e0e6ba9c09923f664a0a00896f486e6f09fc11ff806a12b0c","impliedFormat":1},{"version":"d703f98676a44f90d63b3ffc791faac42c2af0dd2b4a312f4afdb5db471df3de","impliedFormat":1},{"version":"0cfe1d0b90d24f5c105db5a2117192d082f7d048801d22a9ea5c62fae07b80a0","impliedFormat":1},{"version":"ef61792acbfa8c27c9bd113f02731e66229f7d3a169e3c1993b508134f1a58e0","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"414cc05e215b7fc5a4a6ece431985e05e03762c8eb5bf1e0972d477f97832956","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"5c2e5ca7d53236bbf483a81ae283e2695e291fe69490cd139b33fa9e71838a69","impliedFormat":1},{"version":"a73bee51e3820392023252c36348e62dd72e6bae30a345166e9c78360f1aba7e","impliedFormat":1},{"version":"6ea68b3b7d342d1716cc4293813410d3f09ff1d1ca4be14c42e6d51e810962e1","impliedFormat":1},{"version":"c319e82ac16a5a5da9e28dfdefdad72cebb5e1e67cbdcc63cce8ae86be1e454f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a020158a317c07774393974d26723af551e569f1ba4d6524e8e245f10e11b976","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"8bba776476c48b0e319d243f353190f24096057acede3c2f620fee17ff885dba","impliedFormat":1},{"version":"a3abe92070fbd33714bd837806030b39cfb1f8283a98c7c1f55fffeea388809e","impliedFormat":1},{"version":"ceb6696b98a72f2dae802260c5b0940ea338de65edd372ff9e13ab0a410c3a88","impliedFormat":1},{"version":"2cd914e04d403bdc7263074c63168335d44ce9367e8a74f6896c77d4d26a1038","impliedFormat":1},{"version":"ac60bbee0d4235643cc52b57768b22de8c257c12bd8c2039860540cab1fa1d82","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"3bc8605900fd1668f6d93ce8e14386478b6caa6fda41be633ee0fe4d0c716e62","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"9f31420a5040dbfb49ab94bcaaa5103a9a464e607cabe288958f53303f1da32e","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"f11d0dcaa4a1cba6d6513b04ceb31a262f223f56e18b289c0ba3133b4d3cd9a6","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"56013416784a6b754f3855f8f2bf6ce132320679b8a435389aca0361bce4df6b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"9c066f3b46cf016e5d072b464821c5b21cc9adcc44743de0f6c75e2509a357ab","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c51641ab4bfa31b7a50a0ca37edff67f56fab3149881024345b13f2b48b7d2de","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"52abbd5035a97ebfb4240ec8ade2741229a7c26450c84eb73490dc5ea048b911","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"4360ad4de54de2d5c642c4375d5eab0e7fe94ebe8adca907e6c186bbef75a54d","impliedFormat":1},{"version":"c338dff3233675f87a3869417aaea8b8bf590505106d38907dc1d0144f6402ef","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"9c9cae45dc94c2192c7d25f80649414fa13c425d0399a2c7cb2b979e4e50af42","impliedFormat":1},{"version":"068f063c2420b20f8845afadb38a14c640aed6bb01063df224edb24af92b4550","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"b8719d4483ebef35e9cb67cd5677b7e0103cf2ed8973df6aba6fdd02896ddc6e","impliedFormat":1},{"version":"643672ce383e1c58ea665a92c5481f8441edbd3e91db36e535abccbc9035adeb","impliedFormat":1},{"version":"6dd9bcf10678b889842d467706836a0ab42e6c58711e33918ed127073807ee65","impliedFormat":1},{"version":"8fa022ea514ce0ea78ac9b7092a9f97f08ead20c839c779891019e110fce8307","impliedFormat":1},{"version":"c93235337600b786fd7d0ff9c71a00f37ca65c4d63e5d695fc75153be2690f09","impliedFormat":1},{"version":"10179c817a384983f6925f778a2dac2c9427817f7d79e27d3e9b1c8d0564f1f4","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"c0a666b005521f52e2db0b685d659d7ee9b0b60bc0d347dfc5e826c7957bdb83","impliedFormat":1},{"version":"807d38d00ce6ab9395380c0f64e52f2f158cc804ac22745d8f05f0efdec87c33","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"d05fb434f4ba073aed74b6c62eff1723c835de2a963dbb091e000a2decb5a691","impliedFormat":1},{"version":"10e6166be454ddb8c81000019ce1069b476b478c316e7c25965a91904ec5c1e3","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"703989a003790524b4e34a1758941d05c121d5d352bccca55a5cfb0c76bca592","impliedFormat":1},{"version":"a58abf1f5c8feb335475097abeddd32fd71c4dc2065a3d28cf15cacabad9654a","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"671aeae7130038566a8d00affeb1b3e3b131edf93cbcfff6f55ed68f1ca4c1b3","impliedFormat":1},{"version":"f0f05149debcf31b3a717ce8dd16e0323a789905cb9e27239167b604153b8885","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"955c69dde189d5f47a886ed454ff50c69d4d8aaec3a454c9ab9c3551db727861","impliedFormat":1},{"version":"cec8b16ff98600e4f6777d1e1d4ddf815a5556a9c59bc08cc16db4fd4ae2cf00","impliedFormat":1},{"version":"9e21f8e2c0cfea713a4a372f284b60089c0841eb90bf3610539d89dbcd12d65a","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"c226288bda11cee97850f0149cc4ff5a244d42ed3f5a9f6e9b02f1162bf1e3f4","impliedFormat":1},{"version":"210a4ec6fd58f6c0358e68f69501a74aef547c82deb920c1dec7fa04f737915a","impliedFormat":1},{"version":"8eea4cc42d04d26bcbcaf209366956e9f7abaf56b0601c101016bb773730c5fe","impliedFormat":1},{"version":"f5319e38724c54dff74ee734950926a745c203dcce00bb0343cb08fbb2f6b546","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"12b8dfed70961bea1861e5d39e433580e71323abb5d33da6605182ec569db584","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"7e560f533aaf88cf9d3b427dcf6c112dd3f2ee26d610e2587583b6c354c753db","impliedFormat":1},{"version":"71e0082342008e4dfb43202df85ea0986ef8e003c921a1e49999d0234a3019da","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"e71e103fb212e015394def7f1379706fce637fec9f91aa88410a73b7c5cbd4e3","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"794998dc1c5a19ce77a75086fe829fb9c92f2fd07b5631c7d5e0d04fd9bc540c","impliedFormat":1},{"version":"2b0b12d0ee52373b1e7b09226eae8fbf6a2043916b7c19e2c39b15243f32bde2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"bdc5fd605a6d315ded648abf2c691a22d0b0c774b78c15512c40ddf138e51950","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"6cd4b0986c638d92f7204d1407b1cb3e0a79d7a2d23b0f141c1a0829540ce7ef","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"d58265e159fc3cb30aa8878ba5e986a314b1759c824ff66d777b9fe42117231a","impliedFormat":1},{"version":"ff8fccaae640b0bb364340216dcc7423e55b6bb182ca2334837fee38636ad32e","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"59ee66cf96b093b18c90a8f6dbb3f0e3b65c758fba7b8b980af9f2726c32c1a2","impliedFormat":1},{"version":"c590195790d7fa35b4abed577a605d283b8336b9e01fa9bf4ae4be49855940f9","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"026a43d8239b8f12d2fc4fa5a7acbc2ad06dd989d8c71286d791d9f57ca22b78","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"14cf3683955f914b4695e92c93aae5f3fe1e60f3321d712605164bfe53b34334","impliedFormat":1},{"version":"12f0fb50e28b9d48fe5b7580580efe7cc0bd38e4b8c02d21c175aa9a4fd839b0","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"7cd657e359eac7829db5f02c856993e8945ffccc71999cdfb4ab3bf801a1bbc6","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"29c2aa0712786a4a504fce3acd50928f086027276f7490965cb467d2ce638bae","impliedFormat":1},{"version":"f14e63395b54caecc486f00a39953ab00b7e4d428a4e2c38325154b08eb5dcc2","impliedFormat":1},{"version":"e749bbd37dadf82c9833278780527c717226e1e2c9bc7b2576c8ec1c40ec5647","impliedFormat":1},{"version":"46e4e179b295f08d0bd0176fe44cf6c89558c9091d3cb3894f9eaaa42ea1add1","impliedFormat":1},{"version":"08f52a9edaabeda3b2ea19a54730174861ceed637c5ca1c1b0c39459fdc0853e","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"29164fb428c851bc35b632761daad3ae075993a0bf9c43e9e3bc6468b32d9aa5","impliedFormat":1},{"version":"3c01539405051bffccacffd617254c8d0f665cdce00ec568c6f66ccb712b734f","impliedFormat":1},{"version":"ef9021bdfe54f4df005d0b81170bd2da9bfd86ef552cde2a049ba85c9649658f","impliedFormat":1},{"version":"17a1a0d1c492d73017c6e9a8feb79e9c8a2d41ef08b0fe51debc093a0b2e9459","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"96e1caae9b78cde35c62fee46c1ec9fa5f12c16bc1e2ab08d48e5921e29a6958","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"9e0327857503a958348d9e8e9dd57ed155a1e6ec0071eb5eb946fe06ccdf7680","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"01aa917531e116485beca44a14970834687b857757159769c16b228eb1e49c5f","impliedFormat":1},{"version":"397f568f996f8ffcf12d9156342552b0da42f6571eadba6bce61c99e1651977d","impliedFormat":1},{"version":"e2fd426f3cbc5bbff7860378784037c8fa9c1644785eed83c47c902b99b6cda9","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"a52674bc98da7979607e0f44d4c015c59c1b1d264c83fc50ec79ff2cfea06723","impliedFormat":1},{"version":"bcca16e60015db8bbf6bd117e88c5f7269337aebb05fc2b0701ae658a458c9c3","impliedFormat":1},{"version":"5e1246644fab20200cdc7c66348f3c861772669e945f2888ef58b461b81e1cd8","impliedFormat":1},{"version":"eb39550e2485298d91099e8ab2a1f7b32777d9a5ba34e9028ea8df2e64891172","impliedFormat":1},{"version":"e108f38a04a607f9386d68a4c6f3fdae1b712960f11f6482c6f1769bab056c2e","impliedFormat":1},{"version":"a3128a84a9568762a2996df79717d92154d18dd894681fc0ab3a098fa7f8ee3b","affectsGlobalScope":true,"impliedFormat":1},{"version":"347791f3792f436950396dd6171d6450234358001ae7c94ca209f1406566ccbf","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"714d8ebb298c7acc9bd1f34bd479c57d12b73371078a0c5a1883a68b8f1b9389","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"51bf55bb6eb80f11b3aa59fb0a9571565a7ea304a19381f6da5630f4b2e206c4","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"02f8ef78d46c5b27f108dbb56709daa0aff625c20247abb0e6bb67cd73439f9f","impliedFormat":1},{"version":"b7fff2d004c5879cae335db8f954eb1d61242d9f2d28515e67902032723caeab","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"6812502cc640de74782ce9121592ae3765deb1c5c8e795b179736b308dd65e90","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"2b664c3cc544d0e35276e1fb2d4989f7d4b4027ffc64da34ec83a6ccf2e5c528","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"b10bc147143031b250dc36815fd835543f67278245bf2d0a46dca765f215124e","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"1e4c6ac595b6d734c056ac285b9ee50d27a2c7afe7d15bd14ed16210e71593b0","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"3c7b3aecd652169787b3c512d8f274a3511c475f84dcd6cead164e40cad64480","impliedFormat":1},{"version":"9a01f12466488eccd8d9eafc8fecb9926c175a4bf4a8f73a07c3bcf8b3363282","impliedFormat":1},{"version":"b80f624162276f24a4ec78b8e86fbee80ca255938e12f8b58e7a8f1a6937120b","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"b95a6f019095dd1d48fd04965b50dfd63e5743a6e75478343c46d2582a5132bf","impliedFormat":99},{"version":"c2008605e78208cfa9cd70bd29856b72dda7ad89df5dc895920f8e10bcb9cd0a","impliedFormat":99},{"version":"b97cb5616d2ab82a98ec9ada7b9e9cabb1f5da880ec50ea2b8dc5baa4cbf3c16","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"00b0f43b3770f66aa1e105327980c0ff17a868d0e5d9f5689f15f8d6bf4fb1f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"272a7e7dbe05e8aaba1662ef1a16bbd57975cc352648b24e7a61b7798f3a0ad7","affectsGlobalScope":true,"impliedFormat":1},{"version":"a1219ee18b9282b4c6a31f1f0bcc9255b425e99363268ba6752a932cf76662f0","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"f2b3bca04d1bfe583daae1e1f798c92ec24bb6693bd88d0a09ba6802dee362a8",{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"370bde134aa8c2abc926d0e99d3a4d5d5dba65c6ee65459137e4f02670cbf841","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"e3cf0611709328b449ec13f8c436712d62003620ce480139fae46ce001c2ee9f","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"b558c9a18ea4e6e4157124465c3ef1063e64640da139e67be5edb22f534f2f08","impliedFormat":1},{"version":"01374379f82be05d25c08d2f30779fa4a4c41895a18b93b33f14aeef51768692","impliedFormat":1},{"version":"b0dee183d4e65cf938242efaf3d833c6b645afb35039d058496965014f158141","impliedFormat":1},{"version":"c0bbbf84d3fbd85dd60d040c81e8964cc00e38124a52e9c5dcdedf45fea3f213","impliedFormat":1},"8ab2020159a945e311929abd23acd6b9e2ad9b665bf31a7247f2e12ccad7662d",{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"282f98006ed7fa9bb2cd9bdbe2524595cfc4bcd58a0bb3232e4519f2138df811","impliedFormat":1},{"version":"6222e987b58abfe92597e1273ad7233626285bc2d78409d4a7b113d81a83496b","impliedFormat":1},{"version":"cbe726263ae9a7bf32352380f7e8ab66ee25b3457137e316929269c19e18a2be","impliedFormat":1},{"version":"8b96046bf5fb0a815cba6b0880d9f97b7f3a93cf187e8dcfe8e2792e97f38f87","impliedFormat":99},{"version":"bacf2c84cf448b2cd02c717ad46c3d7fd530e0c91282888c923ad64810a4d511","affectsGlobalScope":true,"impliedFormat":1},{"version":"82e687ebd99518bc63ea04b0c3810fb6e50aa6942decd0ca6f7a56d9b9a212a6","impliedFormat":99},{"version":"7f698624bbbb060ece7c0e51b7236520ebada74b747d7523c7df376453ed6fea","impliedFormat":1},{"version":"8f07f2b6514744ac96e51d7cb8518c0f4de319471237ea10cf688b8d0e9d0225","impliedFormat":1},{"version":"257b83faa134d971c738a6b9e4c47e59bb7b23274719d92197580dd662bfafc3","impliedFormat":99},{"version":"d2e64a6f25013b099e83bfadb2c388d7bef3e8f3fdb25528225bbc841e7e7e3a","impliedFormat":99},{"version":"369ba5259e66ca8c7d35e3234f7a2a0863a770fdb8266505747c65cf346a0804","impliedFormat":99},{"version":"64d984f55025daf604f670b7dfd090ea765f2098aee871174ef2ee3e94479098","impliedFormat":99},{"version":"f147b6710441cf3ec3234adf63b0593ce5e8c9b692959d21d3babc8454bcf743","impliedFormat":99},{"version":"e96d5373a66c2cfbbc7e6642cf274055aa2c7ff6bd37be7480c66faf9804db6d","impliedFormat":99},{"version":"02bcdd7a76c5c1c485cbf05626d24c86ac8f9a1d8dc31f8924108bbaa4cf3ba9","impliedFormat":99},{"version":"c874ab6feac6e0fdf9142727c9a876065777a5392f14b0bbcf869b1e69eb46b5","impliedFormat":99},{"version":"7c553fc9e34773ddbaabe0fa1367d4b109101d0868a008f11042bee24b5a925d","impliedFormat":99},{"version":"9962ce696fbdce2421d883ca4b062a54f982496625437ae4d3633376c5ad4a80","impliedFormat":99},{"version":"e3ea467c4a7f743f3548c9ed61300591965b1d12c08c8bb9aaff8a002ba95fce","impliedFormat":99},{"version":"4c17183a07a63bea2653fbfc0a942b027160ddbee823024789a415f9589de327","impliedFormat":99},{"version":"3e2203c892297ea44b87470fde51b3d48cfe3eeb6901995de429539462894464","impliedFormat":99},{"version":"c84bf7a4abc5e7fdf45971a71b25b0e0d34ccd5e720a866dd78bb71d60d41a3f","impliedFormat":99},{"version":"e01ea380015ed698c3c0e2ccd0db72f3fc3ef1abc4519f122aa1c1a8d419a505","impliedFormat":99},{"version":"5ada1f8a9580c0f7478fe03ae3e07e958f0b79bdfb9dd50eeb98c1324f40011b","impliedFormat":99},{"version":"a8301dc90b4bd9fba333226ee0f1681aeeff1bd90233a8f647e687cb4b7d3521","impliedFormat":99},{"version":"e3225dc0bec183183509d290f641786245e6652bc3dce755f7ef404060693c35","impliedFormat":99},{"version":"09a03870ed8c55d7453bc9ad684df88965f2f770f987481ca71b8a09be5205bc","impliedFormat":99},{"version":"e6233e1c976265e85aa8ad76c3881febe6264cb06ae3136f0257e1eab4a6cc5a","impliedFormat":99},{"version":"2cdd50ddc49e2d608ee848fc4ab0db9a2716624fabb4209c7c683d87e54d79c5","impliedFormat":99},{"version":"e431d664338b8470abb1750d699c7dfcebb1a25434559ef85bb96f1e82de5972","impliedFormat":99},{"version":"2c4254139d037c3caca66ce291c1308c1b5092cfcb151eb25980db932dd3b01a","impliedFormat":99},{"version":"970ae00ed018cb96352dc3f37355ef9c2d9f8aa94d7174ccd6d0ed855e462097","impliedFormat":99},{"version":"d2f8dee457ef7660b604226d471d55d927c3051766bdd80353553837492635c3","impliedFormat":99},{"version":"110a503289a2ef76141ffff3ffceb9a1c3662c32748eb9f6777a2bd0866d6fb1","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"310e6b62c493ce991624169a1c1904015769d947be88dc67e00adc7ebebcfa87","impliedFormat":99},{"version":"62fefda288160bf6e435b21cc03d3fbac11193d8d3bd0e82d86623cca7691c29","impliedFormat":99},{"version":"fcc46a8bcbf9bef21023bba1995160a25f0bc590ca3563ec44c315b4f4c1b18a","impliedFormat":99},{"version":"669573548930fb7d0a0761b827e203dc623581e21febf0be80fb02414f217d74","impliedFormat":99},{"version":"f974db5be6d7428044e19c2848e72495a9b56a8d33b6fcab392e7fb5328eb8df","impliedFormat":99},{"version":"aa348c4fb2f8ac77df855f07fb66281c9f6e71746fdff3b13c7932aa7642b788","impliedFormat":99},{"version":"262d4c61b446d096f0615b5acb0a09007b2a425c6a0fd38b66e404e7b6021812","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"0309a01650023994ed96edbd675ea4fdc3779a823ce716ad876cc77afb792b62","impliedFormat":99},{"version":"f13d7beeea58e219daef3a40e0dc4f2bd7d9581ac04cedec236102a12dfd2090","impliedFormat":99},{"version":"48c411efce1848d1ed55de41d7deb93cbf7c04080912fd87aa517ed25ef42639","affectsGlobalScope":true,"impliedFormat":1},{"version":"a094636c05f3e75cb072684dd42cd25a4c1324bec4a866706c85c04cecd49613","affectsGlobalScope":true,"impliedFormat":99},{"version":"fe2d63fcfdde197391b6b70daf7be8c02a60afa90754a5f4a04bdc367f62793d","impliedFormat":99},{"version":"9a3e2c85ec1ab7a0874a19814cc73c691b716282cb727914093089c5a8475955","impliedFormat":99},{"version":"cbdc781d2429935c9c42acd680f2a53a9f633e8de03290ec6ea818e4f7bff19a","impliedFormat":99},{"version":"9f6d9f5dd710922f82f69abf9a324e28122b5f31ae6f6ce78427716db30a377e","impliedFormat":99},{"version":"ac2414a284bdecfd6ab7b87578744ab056cd04dd574b17853cd76830ef5b72f2","impliedFormat":99},{"version":"c3f921bbc9d2e65bd503a56fbc66da910e68467baedb0b9db0cc939e1876c0d7","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"0cc99fbb161d78729d71fad66c6c363e3095862d6277160f29fa960744b785c6","affectsGlobalScope":true,"impliedFormat":99},"129a86faf021cbd7dd75371cef3025e3eb2137128b7cd35ea06b566f3413315c",{"version":"61de421bab744b4e1e105a40e0409221ba5729868cde52fbdbe3e21a1fb2c69c","impliedFormat":99},{"version":"6c84d9410cb39c418e76c74200dc943c8909863efe798df05c552933801dc42f","impliedFormat":99},"296e4d9489ff6aefc93528063dcc384338b4d6e555fbb73cdec2fee8b6744b54",{"version":"3aa67bb15606c55fdd705fc720d1c5550078cb83f21355806a72495e8f59cdfa","signature":"07415405713faaca7975ad1bfbf9b412ba297f47a6923fe9cebafd7f4dcb7395"},"6a9a61013d77df9d48372dd9f6da3b2b7395ca4675b4fb4cfbce80060beb8d86",{"version":"4f0aad57d90b3b60f110236f8aeee57237ed2c083b947e687e4412ee3040b7f6","signature":"d241dd8d3506b3a6df117db8d62114d1d2d4663baaeb0f5e72cc0e38f72725d1"},{"version":"d6cf83ebe1ae55f96a9e79a748723cdea14ad985a72f6585dc53412e32c78a86","signature":"adf2277f61af2e1981a1e63f463cb2a8fc39964908863180a4e52a88482da9e2"},{"version":"0f407ed5c25d2fb2c97a216d175a3efc0fe21fd0faf4de3cca00931016375991","signature":"46f78a03a7c6287bd7eaa736b490ad45be142f90896a3d02a739501b3d5b2de0"},{"version":"ce2c97f3e5b6a222f19f005b6ef4886d89bf4c16bc1903658802d04a552fc9d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cdec8f8e76fb1dfd3a6fdca604aae2bf3f51c1110f7a0012dc5604fdbb12f689","signature":"e79eaa521c0e7a44cd476d4c4ec7a2150ffd2800d3f4a92e0c4d32eee476849b"},"af7de680832f9a99aa122282a09b27ce3ca195b913239ed42775e10797136a9d",{"version":"d3b8f898932a894fcf3a2f0137add132d5345f85c97e58cb6974a3b5f80b6127","signature":"fe68f8d72e2ba80e4e6cd7d14dc27c6463905a100d806ff4357dc083b46b60c0"},{"version":"fe03821ef6e674b9bd68b3c957a27e0eec9f2c3da4afd8174eb2a1640b2a0a3b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"306cc4071730e408e1e6df19d616bc3b4bf5dcec55343057834231cea7e2c9b8","signature":"93ea245c6b41d0516d8e9f79df02214396c126cb7129c44a1419f591e9b0149d"},{"version":"ce71392a988633a5400e5c42a22e14144c582f2c099ac74fa6e7327728343da5","signature":"af98905d9b626d3e1e58a247fcf12eeee6dc6ab75fd66f495cfb3f53fc60442f"},{"version":"1341b7ef069321245b63656c887094a74e28ad9a4f0579d55d50b9c8ea227262","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"f046a76e6c2b6bc3a9c5cc53b99da7340b63ebd7f84e13a8a53e60f99d7c85a5","signature":"937982033f30be6d1131b84b6e07e898b512dc3c301f8fd13fbbf7fc726fc42f"},{"version":"68448904d6c46e4a04f229602ea4fe77ebe567283b1e370aefe127c03c4ae630","signature":"146b47847e0270185276ad42ff155ebe162ecf46e15dbcf5148818abd6edbb89"},{"version":"a5d5c5023804437603b51acbf146f6d7bfa9585c6addb8e45488f0e14f8ddbcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"59dd6a66b9027174bc2de83cd568fc135fa912f806adc7a2fe2eee15bdb869b8","51d79ddaeeb46de8db2aa5928bf15a830b33ff0b91050c548a2ea82831010b60",{"version":"de1ffa1e3bb7fa3341e883ec6079ba6dd6f73199354948d2f7ded7dc4df74b8c","signature":"e26bccafe194370d7a6064acaa4860cf362e8b05feed9c5c0e938b2d87a7d51a"},{"version":"1dd7fa4fda5354d9a835ff15de140db2664c9ad4bba57f4fa0c8ac7b25b2f0b6","signature":"68cf7e37be5cb67a476fcf97cee522bf6991ff292ce99736568c7cf552c33f12"},{"version":"030d523e29ac43dfe92303dc59be3348b02a6d3a1769e17fde3951bdfc56b464","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"3e0fc0ac00cc2be442d8ca11f3737d52118bf68c5b54e7857cffe8c663f52b12","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"4caa4cac2a1e939f9a5d8b39dd20a2f318f01881a3f3864a5c5af3a1529db9c1",{"version":"a41537fbd75a9b00364517e9f9a7dedb617a1b7d70529e0ddf573c2f19ef7f17","signature":"73226465e2746e4e6e679adaf07df2c26247bc2020daab13f7fbb7562276eefe"},{"version":"c5a0ffa909f5277c3830ab0ea463558b135ea049b731b3852310e0b64ca3869f","signature":"40d5d8eeb71a7cfb02078d06351f4467bdf5d8dc4d47ba467480eb30b9b73e94"},{"version":"c0efe9f1a7841088fd3b0cdb9c2290a6f5c1fa7a3aebdf3aaf270d344ff56e21","signature":"7175ca141479166d4eb613d64104eeed84093343cca30afd909912737339e111"},{"version":"ff0d9317f266d554abc684692d66c0a4cc76c2ed22248400d7838032dea5d84c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"662ff9b16378365dce8ac7752370c8c8609f11c6f5d53480c5dad6bdc00a467d","signature":"b3f301ee5acbe3a478e4b2fc5c86b1eb021f624afbf2295dbc0c605cd052de16"},"fa452635b5ac56ff89f7c2bef9ae560435d24d8f50f4ba9e661dee9e50475d28","1a017381422d08696696ee73d0b477f65817ceb7db6a862c0ee1274ec8ebf19b",{"version":"ab3408c1be7894e73630304d2c8aebea85542ca23fb96a7011032812bb2f3f90","signature":"ba54342937eb116567785cc93f6ef7c8ce3b041a67bb2ecb579ab28359b06047"},{"version":"d99de358089926db30b5837d01580cce1c517a47112e34bd386e211d6802fe74","signature":"6b55b050c9d01c4cbb1de4a608f41277e50e804ef9be262387e618cd3f75ff53"},{"version":"1ac295e91285a5df3bf0b6b06dd3324451e9e0510c5e9243763f6739fb206f31","signature":"536701639205b9046c7be694d0f58df607a5cefe8bb81b239350d70ed574e2c6"},"532ebc18ccfd0dde7741c8b3063a4d38117516b46b83b92a79587713b8e64d67","1adc127175e94440e797af519f6778fcbbd7965dbff102f9c2bb8059b3650e28",{"version":"e7f7041381a9396b1e52568564b7f90ee87f156e53a2f563f12f8a4043b13325","signature":"9e0e1dc9f88c512d6ca9044e77913ba4a6df74736ee5ea089134a65aa6375cd9"},{"version":"11c4815cba4cd9b80431a6ac938bb837610f865c8211032589b22b11cff37d53","signature":"e0b22866a9bac6f71dd2f508ee1ddd445e17ed71601d1b94ec60013fa934b48e"},{"version":"938a200cab8ef63bd63ef811d7732a0d380cc5331c865d131395e442f73fb338","signature":"db24729f7c24caafaf30635fdc17359f1e9312bd97fdbed0da9d34f00a982190"},{"version":"af5f2422c3e48b5ec5ce234c717528d39c9ff3274cc603aaa79ba1c488ffe573","signature":"a40c98740a3c1d0b71f61f6c9a7337c455d837af954e117bd0f65f0e80598fa7"},{"version":"c722f93b3272613f2645e308b41c9bd2511ceb17a69860422d09f19361542334","signature":"31e2f5b6378057657d9b6ebf2ed6de79026baef70f5463ae397768a7ef76e21d"},{"version":"17cb6cb4f714d0efd0de6e7a5254a6e6b463ced0a8c266724660ff1352da2645","signature":"599c21046dfbaec96c624b6092e7073a3472d2688490bfc8527df9a3496408ec"},{"version":"4cb8ce370dded833aa921768d43d7b5c635363c8bf8e343139df3a5554329354","signature":"c10fe759e992c8f87f199e01729cc58a5c225ecc9a914ddcb86cfc0f912c3ef1"},{"version":"9b198294f6ac383029150d5d784b7ebb2e91eff67517a13462efaca48a27cf0b","signature":"c0f8673c25e3392d2841196ba47799fa401e4135a9f37f75fedb369b79270144"},"1414615c866abf5fa5557c370fd8744a06a31e6c4403c713e17272fa2db15a91","87efe6a08272fd6ad1f8c21c2c55b64cd713a48daf94a12e14ab4030944b7ba3","304fd3f360a445e635986b3264ee8017e6a3631a2a4f6faf0d0bf7d3f98e62eb",{"version":"00c9a5c5fda50a4811bf4db39355b1043ea43e82e5e13e1a02910c6f7fb1bcea","signature":"273f5f74d3126daf5eeb15df72604de561105d1d8a2f5825c88fb09ae65eee8b"},{"version":"3531e6a9e84fa1ba01a8971a55f5b3a74817d6e7122aebfadc764775a0484660","signature":"cfc174c783fc21759487bf9e1d56afab8a6f58526c444c24ec7172160de8b71a"},{"version":"704ba60c7bb6de74ea9ee213ab33b6895f951fb1d5133bb3444f2391927e2c4c","signature":"36699080ba48ae0564aa577db1d8279ff99bd8a98720e8e26e0a88231dffbb8d"},{"version":"eaa57d44de0c8b1ce0c6fb8cf9eec693b15563cfcd71d3501c27aa0615e98667","signature":"a533121eee3903d4cc40d812648fb68cf6f62d670913ef28f5f65a179e4ca6a8"},"187c0cd60326c19be31746d530c8082dcc0b395da934fddd17ad4a356c9a417b",{"version":"d74845f8b6f02aff4b3dd36b99003dd4fe58ac2376ae511f6d92bcb3a153d6aa","signature":"6781825fe0bacb6828c87c307a6126fe3a0ae631d079cb04b8b852804fdaa7bb"},{"version":"a63d1dceca466019e671006f8f071c0bf5596206b98557239ca64f9e58c191e0","signature":"3b31470d2936e80d198c801cc596e0569736a32b31f5544a6453f3934e24fba7"},{"version":"4763b947677a33bd554af8624d932aedea06bcf57a25b11ef6a7e057210358d6","signature":"6cf67db3bb1d66e8d7dbe762fed6bdf98d8236cc313c69a42dffae94476336ab"},{"version":"a63623ab42d5e0f1b159cfcf4d80b4ab2bc324db92a6d05a6077995df1638ba0","signature":"db8a0682e6c1d96fd0116f63cde511bd7b3de4387312aabd7da4bf53d72e0ad6"},"60193090d4e861a6cc875a6f00b0616a024ac586b866dfbdf09e4ca053aa8c65","efff1f1a0df5cb30394a6323bf9fad9532cf4284de8ca3820b8c49d8bd88b463","b9aeaade08803f4f2f5e1fcf82527451afc68b6c93896a749d27d92da239344a","9da27bb4af2cd79a55068f5fa60724e96145ecd54835420f3769c1aeca2a8ebd","56900c830691def7957f3b487b19d91136aa05af0752f83673b2204303c19152","ff25d2789578679f3f1b1f5dcbb5c87a0dad6488fa583cd1fded8ae83c58b1b5",{"version":"30cdf53273913e8ee8b04cabd23d6b53df2f92265a9c88470e5d7c957d95b7ef","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},"dc575c9e3b32d510c9a974135f13f83e2b836d8bb482310c40b2495b0a763dd2",{"version":"730539759405584ef46f895820a5723593e9da5fa3a66217debc9bce8aa57ed1","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"39422101b85c49c26174ec094efa2f65e5f4d3d26ae0d5cb1d9078b2ff157d5f","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},"abac25862a735319832d29b4daa0eac0af8d63e7a9f6dd0d95dcb687d8cab4a6","6e2133f8ad52b25f2ce9c7ccc30fdd2653214949e08552ee3fbd970716d07dc2","0d7c3b7a0922f5a70e863e5dc809850c9a42d9438ab0ee919657845b3db88c18","a9f7e7f88d04c20cb51988a6c5bceaf00920c6d7034f094106613a6a1f1e3160"],"root":[449,474,519,536,[539,608]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[601,1],[602,2],[603,3],[604,4],[605,5],[606,6],[607,7],[608,8],[600,9],[570,10],[574,11],[579,12],[582,13],[590,14],[595,15],[597,16],[599,17],[572,18],[578,19],[575,20],[576,21],[577,22],[571,23],[580,24],[581,25],[573,26],[596,27],[583,28],[584,29],[587,30],[585,31],[586,29],[588,32],[589,33],[598,34],[546,35],[592,36],[591,36],[593,36],[594,37],[536,38],[541,39],[540,36],[539,36],[543,40],[542,41],[545,42],[544,36],[547,43],[549,44],[548,28],[550,45],[552,46],[551,28],[553,47],[555,48],[554,36],[557,49],[556,28],[560,50],[559,51],[561,52],[558,36],[562,53],[563,54],[564,36],[566,55],[565,36],[567,56],[569,57],[568,28],[449,58],[395,36],[475,36],[121,59],[122,59],[123,60],[76,61],[124,62],[125,63],[126,64],[71,36],[74,65],[72,36],[73,36],[127,66],[128,67],[129,68],[130,69],[131,70],[132,71],[133,71],[134,72],[135,73],[136,74],[137,75],[77,36],[75,36],[138,76],[139,77],[140,78],[174,79],[141,80],[142,36],[143,81],[144,82],[145,83],[146,84],[147,85],[148,86],[149,87],[150,88],[151,89],[152,89],[153,90],[154,36],[155,91],[156,92],[158,93],[157,94],[159,95],[160,96],[161,97],[162,98],[163,99],[164,100],[165,101],[166,102],[167,103],[168,104],[169,105],[170,106],[171,107],[78,36],[79,36],[80,36],[118,108],[119,36],[120,36],[172,109],[173,110],[178,111],[179,112],[177,35],[175,113],[176,114],[60,36],[62,115],[291,35],[522,36],[523,116],[524,117],[527,118],[526,36],[487,36],[498,119],[493,120],[496,121],[511,122],[500,36],[503,123],[502,124],[514,124],[501,125],[525,36],[495,126],[497,126],[489,127],[492,128],[508,127],[494,129],[488,36],[61,36],[482,36],[532,130],[534,131],[533,132],[531,133],[530,36],[69,134],[398,135],[403,9],[405,136],[199,137],[347,138],[374,139],[274,36],[192,36],[197,36],[338,140],[266,141],[198,36],[376,142],[377,143],[319,144],[335,145],[239,146],[342,147],[343,148],[341,149],[340,36],[339,150],[375,151],[200,152],[273,36],[275,153],[195,36],[210,154],[201,155],[214,154],[243,154],[185,154],[346,156],[356,36],[191,36],[297,157],[298,158],[292,159],[426,36],[300,36],[301,159],[293,160],[430,161],[429,162],[425,36],[379,36],[334,163],[333,36],[424,164],[294,35],[217,165],[215,166],[427,36],[428,36],[216,167],[419,168],[422,169],[226,170],[225,171],[224,172],[433,35],[223,173],[261,36],[436,36],[439,36],[438,35],[440,174],[181,36],[344,175],[345,176],[368,36],[190,177],[180,36],[183,178],[313,35],[312,179],[311,180],[302,36],[303,36],[310,36],[305,36],[308,181],[304,36],[306,182],[309,183],[307,182],[196,36],[188,36],[189,154],[397,184],[406,185],[410,186],[350,187],[349,36],[258,36],[441,188],[359,189],[295,190],[296,191],[288,192],[280,36],[286,36],[287,193],[317,194],[281,195],[318,196],[315,197],[314,36],[316,36],[270,198],[351,199],[352,200],[282,201],[283,202],[278,203],[330,204],[358,205],[361,206],[259,207],[186,208],[357,209],[182,139],[380,210],[391,211],[378,36],[390,212],[70,36],[366,213],[246,36],[276,214],[362,36],[205,36],[389,215],[194,36],[249,216],[348,217],[388,36],[382,218],[187,36],[383,219],[385,220],[386,221],[369,36],[387,208],[213,222],[367,223],[392,224],[322,36],[325,36],[323,36],[327,36],[324,36],[326,36],[328,225],[321,36],[252,226],[251,36],[257,227],[253,228],[256,229],[255,229],[254,228],[209,230],[241,231],[355,232],[442,36],[414,233],[416,234],[285,36],[415,235],[353,199],[299,199],[193,36],[242,236],[206,237],[207,238],[208,239],[204,240],[329,240],[220,240],[244,241],[221,241],[203,242],[202,36],[250,243],[248,244],[247,245],[245,246],[354,247],[290,248],[320,249],[289,250],[337,251],[336,252],[332,253],[238,254],[240,255],[237,256],[211,257],[269,36],[402,36],[268,258],[331,36],[260,259],[279,260],[277,261],[262,262],[264,263],[437,36],[263,264],[265,264],[400,36],[399,36],[401,36],[435,36],[267,265],[235,35],[68,36],[218,266],[227,36],[272,267],[212,36],[408,35],[418,268],[234,35],[412,159],[233,269],[394,270],[232,268],[184,36],[420,271],[230,35],[231,35],[222,36],[271,36],[229,272],[228,273],[219,274],[284,88],[360,88],[384,36],[364,275],[363,36],[404,36],[236,35],[396,276],[63,35],[66,277],[67,278],[64,35],[65,36],[381,279],[373,280],[372,36],[371,281],[370,36],[393,282],[407,283],[409,284],[411,285],[413,286],[417,287],[448,288],[421,288],[447,289],[423,290],[431,291],[432,292],[434,293],[443,294],[446,177],[445,36],[444,295],[538,296],[537,36],[466,297],[464,298],[465,299],[453,300],[454,298],[461,301],[452,302],[457,303],[467,36],[458,304],[463,305],[469,306],[468,307],[451,308],[459,309],[460,310],[455,311],[462,297],[456,312],[477,313],[476,314],[365,315],[450,36],[472,316],[471,36],[470,36],[473,317],[512,36],[490,36],[491,318],[58,36],[59,36],[10,36],[11,36],[13,36],[12,36],[2,36],[14,36],[15,36],[16,36],[17,36],[18,36],[19,36],[20,36],[21,36],[3,36],[22,36],[23,36],[4,36],[24,36],[28,36],[25,36],[26,36],[27,36],[29,36],[30,36],[31,36],[5,36],[32,36],[33,36],[34,36],[35,36],[6,36],[39,36],[36,36],[37,36],[38,36],[40,36],[7,36],[41,36],[46,36],[47,36],[42,36],[43,36],[44,36],[45,36],[8,36],[51,36],[48,36],[49,36],[50,36],[52,36],[9,36],[53,36],[54,36],[55,36],[57,36],[56,36],[1,36],[96,319],[106,320],[95,319],[116,321],[87,322],[86,323],[115,295],[109,324],[114,325],[89,326],[103,327],[88,328],[112,329],[84,330],[83,295],[113,331],[85,332],[90,333],[91,36],[94,333],[81,36],[117,334],[107,335],[98,336],[99,337],[101,338],[97,339],[100,340],[110,295],[92,341],[93,342],[102,343],[82,344],[105,335],[104,333],[108,36],[111,345],[509,346],[506,347],[507,346],[510,348],[505,36],[486,349],[483,350],[481,351],[479,352],[478,36],[480,353],[484,36],[485,354],[518,355],[513,356],[504,357],[499,36],[528,358],[515,359],[529,360],[516,361],[521,362],[520,363],[517,364],[535,365],[474,366],[519,367]],"affectedFilesPendingEmit":[601,602,603,604,605,606,607,608,570,574,579,582,590,595,597,599,572,578,575,576,577,571,580,581,573,596,583,584,587,585,586,588,589,598,546,592,591,593,594,536,541,540,543,542,545,544,547,549,548,550,552,551,553,555,554,557,556,560,559,561,558,562,563,564,566,565,567,569,568,474,519],"version":"5.9.3"}
\ No newline at end of file
+{"fileNames":["./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/global.d.ts","./node_modules/.pnpm/csstype@3.2.3/node_modules/csstype/index.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/amp.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/get-page-files.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/compatibility/index.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/globals.typedarray.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/buffer.buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/globals.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/events.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/header.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/readable.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/file.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/fetch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/formdata.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/connector.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-dispatcher.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-origin.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool-stats.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/handlers.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/balanced-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-interceptor.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-client.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-pool.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-errors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-handler.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-agent.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/api.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/interceptors.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/util.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cookies.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/patch.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/websocket.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/eventsource.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/filereader.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/content-type.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cache.d.ts","./node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/index.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/web-globals/storage.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/assert.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/assert/strict.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/async_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/buffer.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/child_process.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/cluster.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/console.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/constants.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/crypto.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dgram.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dns.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/dns/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/domain.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/events.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/fs.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/fs/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/http.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/http2.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/https.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/inspector.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/inspector.generated.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/module.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/net.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/os.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/path.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/perf_hooks.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/process.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/punycode.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/querystring.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/readline.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/readline/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/repl.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/sea.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/sqlite.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/consumers.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/stream/web.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/string_decoder.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/test.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/timers.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/timers/promises.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/tls.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/trace_events.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/tty.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/url.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/util.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/v8.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/vm.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/wasi.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/worker_threads.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/zlib.d.ts","./node_modules/.pnpm/@types+node@22.19.21/node_modules/@types/node/index.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/canary.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/experimental.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/index.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/canary.d.ts","./node_modules/.pnpm/@types+react-dom@19.0.0/node_modules/@types/react-dom/experimental.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/fallback.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/body-streams.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/revalidate.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/constants.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/require-hook.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/page-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-kind.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/render-result.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/load-components.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/with-router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/route-loader.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/page-loader.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/pages.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/render.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/next-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/adapter.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/clean-async-snapshot-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/clean-async-snapshot.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/.pnpm/@types+react@19.0.0/node_modules/@types/react/jsx-runtime.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/client-page.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/search-params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/metadata/metadata-boundary.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/node-environment.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/http.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/utils.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/export/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/export/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/after.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/after-context.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/params.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request-meta.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/cli/next-test.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/config-shared.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-http/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/base-http/node.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/.pnpm/sharp@0.33.5/node_modules/sharp/lib/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/next-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/trace.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/shared.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/trace/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/webpack-config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/build/swc/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/react-dev-overlay/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/telemetry/storage.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/next.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/.pnpm/@next+env@15.1.3/node_modules/@next/env/dist/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_app.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/app.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/cache.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/runtime-config.external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/config.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_document.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/document.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dynamic.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/pages/_error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/error.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/head.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/head.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/cookies.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/headers.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/image-component.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/link.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/link.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/redirect.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/not-found.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/components/navigation.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/navigation.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/router.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/client/script.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/script.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/after/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/dist/server/request/connection.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/server.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types/global.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types/compiled.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/types.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/index.d.ts","./node_modules/.pnpm/next@15.1.3_react-dom@19.0.0_react@19.0.0__react@19.0.0/node_modules/next/image-types/global.d.ts","./next-env.d.ts","./node_modules/.pnpm/source-map-js@1.2.1/node_modules/source-map-js/source-map.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/previous-map.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/input.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/declaration.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/root.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/warning.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/lazy-result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/no-work-result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/processor.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/result.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/document.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/rule.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/node.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/comment.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/container.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/at-rule.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/list.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/postcss.d.ts","./node_modules/.pnpm/postcss@8.5.15/node_modules/postcss/lib/postcss.d.mts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/generated/corepluginlist.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/generated/colors.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/config.d.ts","./node_modules/.pnpm/tailwindcss@3.4.19/node_modules/tailwindcss/types/index.d.ts","./tailwind.config.ts","./node_modules/.pnpm/@types+estree@1.0.9/node_modules/@types/estree/index.d.ts","./node_modules/.pnpm/rollup@4.62.0/node_modules/rollup/dist/rollup.d.ts","./node_modules/.pnpm/rollup@4.62.0/node_modules/rollup/dist/parseast.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/hmrpayload.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/customevent.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/hot.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/types.d-agj9qkwt.d.ts","./node_modules/.pnpm/esbuild@0.21.5/node_modules/esbuild/lib/main.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/runtime.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/importglob.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/types/metadata.d.ts","./node_modules/.pnpm/vite@5.4.21_@types+node@22.19.21/node_modules/vite/dist/node/index.d.ts","./node_modules/.pnpm/@vitest+pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/types.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/helpers.d.ts","./node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/index-c1cfc5e9.d.ts","./node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/index.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/tasks-3znpj1lr.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/types-bxe-2udy.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/diff.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/types.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/error.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/environment.looobwuu.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/environment-ddx0edty.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/rawsnapshot-cpnkto81.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/environment.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/config.cy0c388z.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/trace-mapping.d-dlvdeqop.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/index-z0r8hvru.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/index.d.ts","./node_modules/.pnpm/@vitest+utils@2.1.9/node_modules/@vitest/utils/dist/source-map.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/client.d.ts","./node_modules/.pnpm/vite-node@2.1.9_@types+node@22.19.21/node_modules/vite-node/dist/server.d.ts","./node_modules/.pnpm/@vitest+runner@2.1.9/node_modules/@vitest/runner/dist/utils.d.ts","./node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/benchmark.geerunq4.d.ts","./node_modules/.pnpm/@vitest+snapshot@2.1.9/node_modules/@vitest/snapshot/dist/manager.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/reporters.nr4dxcka.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/vite.czkp4x9w.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/config.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/config.d.ts","./vitest.config.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/worker.tn5kgiih.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/worker.b9fxpcac.d.ts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/dist/chai.d.cts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/dist/index.d.ts","./node_modules/.pnpm/@vitest+expect@2.1.9/node_modules/@vitest/expect/index.d.ts","./node_modules/.pnpm/@vitest+spy@2.1.9/node_modules/@vitest/spy/dist/index.d.ts","./node_modules/.pnpm/@vitest+mocker@2.1.9_vite@5.4.21_@types+node@22.19.21_/node_modules/@vitest/mocker/dist/types-dzoqtgin.d.ts","./node_modules/.pnpm/@vitest+mocker@2.1.9_vite@5.4.21_@types+node@22.19.21_/node_modules/@vitest/mocker/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/mocker.crtm890j.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/chunks/suite.b2jumifp.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.d.ts","./node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.d.ts","./node_modules/.pnpm/vitest@2.1.9_@types+node@22.19.21/node_modules/vitest/dist/index.d.ts","./lib/api/client.test.ts","./node_modules/.pnpm/openapi-typescript-helpers@0.0.15/node_modules/openapi-typescript-helpers/index.d.ts","./node_modules/.pnpm/openapi-fetch@0.13.8/node_modules/openapi-fetch/dist/index.d.ts","./lib/api/schema.d.ts","./lib/api/config.ts","./lib/api/client.ts","./lib/api/types.ts","./lib/api/server.ts","./lib/api/server.test.ts","./lib/autosave/autosave.ts","./lib/autosave/autosave.test.ts","./components/toast.tsx","./lib/autosave/useautosave.ts","./lib/command/palette.ts","./lib/command/palette.test.ts","./lib/foreshadow/board.ts","./lib/foreshadow/board.test.ts","./lib/foreshadow/useforeshadow.ts","./lib/review/sse.ts","./lib/generation/cards.ts","./lib/generation/cards.test.ts","./lib/generation/usecharactergen.ts","./lib/generation/useworldgen.ts","./lib/jobs/job.ts","./lib/jobs/job.test.ts","./lib/jobs/usejobpoll.ts","./lib/outline/outline.ts","./lib/outline/outline.test.ts","./lib/outline/useoutline.ts","./lib/pace/beatmap.ts","./lib/pace/beatmap.test.ts","./lib/review/decisions.ts","./lib/review/decisions.test.ts","./lib/review/history.ts","./lib/review/history.test.ts","./lib/review/sse.test.ts","./lib/review/style-sse.test.ts","./lib/review/useaccept.ts","./lib/review/usereviewstream.ts","./lib/rules/rules.ts","./lib/rules/rules.test.ts","./lib/rules/userules.ts","./lib/settings/kimioauth.ts","./lib/settings/kimioauth.test.ts","./lib/settings/providers.ts","./lib/settings/providers.test.ts","./lib/settings/usekimioauth.ts","./lib/skills/skills.ts","./lib/skills/skills.test.ts","./lib/stream/sse.ts","./lib/stream/sse.test.ts","./lib/stream/usedraftstream.ts","./lib/style/style.ts","./lib/style/style.test.ts","./lib/style/userefine.ts","./lib/style/usestylelearn.ts","./lib/wizard/wizard.ts","./lib/wizard/wizard.test.ts","./lib/workbench/chapter.ts","./components/command/commandpalette.tsx","./components/command/commandpalettemount.tsx","./app/layout.tsx","./components/leftnav.tsx","./components/appshell.tsx","./components/projectcard.tsx","./app/page.tsx","./components/generation/charactercarditem.tsx","./components/generation/conflictadjudication.tsx","./components/generation/charactergenerator.tsx","./components/generation/worldgenerator.tsx","./components/codex/codexpage.tsx","./app/projects/[id]/codex/page.tsx","./components/foreshadow/foreshadowcard.tsx","./components/foreshadow/kanbancolumn.tsx","./components/foreshadow/registerform.tsx","./components/foreshadow/foreshadowboard.tsx","./app/projects/[id]/foreshadow/page.tsx","./components/outline/outlinechapterrow.tsx","./components/outline/outlineeditor.tsx","./app/projects/[id]/outline/page.tsx","./components/review/acceptpanel.tsx","./components/review/annotatedtext.tsx","./components/review/conflictcard.tsx","./components/review/foreshadowsuggestions.tsx","./components/review/beatmap.tsx","./components/review/pacepanel.tsx","./components/style/stylepanel.tsx","./components/style/refineview.tsx","./components/review/reviewreport.tsx","./app/projects/[id]/review/page.tsx","./components/rules/rulespage.tsx","./app/projects/[id]/rules/page.tsx","./components/skills/skillspage.tsx","./app/projects/[id]/skills/page.tsx","./components/style/fingerprintview.tsx","./components/style/styleupload.tsx","./components/style/stylepage.tsx","./app/projects/[id]/style/page.tsx","./components/workbench/chapterlist.tsx","./components/workbench/chapterassistant.tsx","./components/workbench/editor.tsx","./components/workbench/workbench.tsx","./app/projects/[id]/write/page.tsx","./components/projectwizard.tsx","./app/projects/new/page.tsx","./components/settings/kimicodeoauth.tsx","./components/settings/providerssettings.tsx","./app/settings/providers/page.tsx","./.next/types/cache-life.d.ts","./.next/types/app/layout.ts","./.next/types/app/page.ts","./.next/types/app/projects/[id]/codex/page.ts","./.next/types/app/projects/[id]/foreshadow/page.ts","./.next/types/app/projects/[id]/outline/page.ts","./.next/types/app/projects/[id]/review/page.ts","./.next/types/app/projects/[id]/rules/page.ts","./.next/types/app/projects/[id]/write/page.ts","./.next/types/app/settings/providers/page.ts"],"fileIdsList":[[76,124,141,142,308,597],[76,124,141,142,308,601],[76,124,141,142,308,607],[76,124,141,142,308,612],[76,124,141,142,308,615],[76,124,141,142,308,625],[76,124,141,142,308,627],[76,124,141,142,308,638],[76,124,141,142,308,643],[76,124,141,142,399,400,401,402],[76,124,141,142,447,547,596],[76,124,141,142,423,542,543,599,600],[76,124,141,142,431,543,606],[76,124,141,142,431,543,611],[76,124,141,142,431,543,614],[76,124,141,142,431,543,569,624],[76,124,141,142,431,543,626],[76,124,141,142,431,543,628],[76,124,141,142,431,543,588,632],[76,124,141,142,431,542,543,594,637],[76,124,141,142,599,639],[76,124,141,142,542,543,599,642],[62,76,124,141,142,423,598],[62,76,124,141,142,542,555,599,604,605],[62,76,124,141,142,431,549],[76,124,141,142,431,595],[62,76,124,141,142,542,551,553,599,609,610],[62,76,124,141,142,542,551],[76,124,141,142,542,551,608],[62,76,124,141,142,551],[76,124,141,142,542],[62,76,124,141,142,542,555,557,602,603],[76,124,141,142,555],[62,76,124,141,142,555,558],[76,124,141,142,423,431],[76,124,141,142,542,562],[62,76,124,141,142,542,562,564,599,613],[76,124,141,142,423,542],[62,76,124,141,142,431,541,547,592],[76,124,141,142,554],[76,124,141,142,565],[76,124,141,142,554,567],[76,124,141,142,554,620],[62,76,124,141,142,542,547,554,567,569,573,574,588,599,616,617,618,619,621,622,623],[62,76,124,141,142,542,575,577,599],[76,124,141,142,578,582],[62,76,124,141,142,541,542,547,580,641],[76,124,141,142,542,583,599],[76,124,141,142,588],[62,76,124,141,142,590],[76,124,141,142,542,588,591,599,630,631],[62,76,124,141,142,547,588],[62,76,124,141,142],[76,124,141,142],[62,76,124,141,142,423,542,548,587,594,599,634,635,636],[76,124,141,142,535],[76,124,141,142,538,539,540],[76,124,141,142,535,543],[76,124,141,142,540,542],[76,124,141,142,539],[76,124,141,142,535,545],[62,76,124,141,142,541,545,547],[76,124,141,142,535,549],[76,124,141,142,535,542,551],[62,76,124,141,142,541,542,547,551],[76,124,141,142,535,542,555],[76,124,141,142,542,554],[62,76,124,141,142,541,542,547,555],[76,124,141,142,535,559],[62,76,124,141,142,541,559],[76,124,141,142,535,542,562],[62,76,124,141,142,541,542,547],[76,124,141,142,535,565],[76,124,141,142,535,567],[76,124,141,142,535,542,569],[76,124,141,142,535,554],[62,76,124,141,142,541,542,547,567],[62,76,124,141,142,540,554],[76,124,141,142,535,542,575],[62,76,124,141,142,541,542,547,575],[76,124,141,142,535,559,578],[76,124,141,142,542,559],[76,124,141,142,535,580],[62,76,124,141,142,541,547,561,578],[76,124,141,142,535,542,583],[76,124,141,142,535,585],[62,76,124,141,142,540,585],[76,124,141,142,535,542,588],[62,76,124,141,142,541,542,547,588],[62,76,124,141,142,541,542,547,559,561,588],[76,124,141,142,535,592],[76,124,141,142,447,448],[76,121,122,124,141,142],[76,123,124,141,142],[124,141,142],[76,124,129,141,142,159],[76,124,125,130,135,141,142,144,156,167],[76,124,125,126,135,141,142,144],[71,72,73,76,124,141,142],[76,124,127,141,142,168],[76,124,128,129,136,141,142,145],[76,124,129,141,142,156,164],[76,124,130,132,135,141,142,144],[76,123,124,131,141,142],[76,124,132,133,141,142],[76,124,134,135,141,142],[76,123,124,135,141,142],[76,124,135,136,137,141,142,156,167],[76,124,135,136,137,141,142,151,156,159],[76,117,124,132,135,138,141,142,144,156,167],[76,124,135,136,138,139,141,142,144,156,164,167],[76,124,138,140,141,142,156,164,167],[74,75,76,77,78,79,80,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],[76,124,135,141,142],[76,124,141,142,143,167],[76,124,132,135,141,142,144,156],[76,124,141,142,145],[76,124,141,142,146],[76,123,124,141,142,147],[76,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],[76,124,141,142,149],[76,124,141,142,150],[76,124,135,141,142,151,152],[76,124,141,142,151,153,168,170],[76,124,136,141,142],[76,124,135,141,142,156,157,159],[76,124,141,142,158,159],[76,124,141,142,156,157],[76,124,141,142,159],[76,124,141,142,160],[76,121,124,141,142,156,161,167],[76,124,135,141,142,162,163],[76,124,141,142,162,163],[76,124,129,141,142,144,156,164],[76,124,141,142,165],[76,124,141,142,144,166],[76,124,138,141,142,150,167],[76,124,129,141,142,168],[76,124,141,142,156,169],[76,124,141,142,143,170],[76,124,141,142,171],[76,117,124,141,142],[76,117,124,135,137,141,142,147,156,159,167,169,170,172],[76,124,141,142,156,173],[62,76,124,141,142,177,179],[62,76,124,141,142,177,178],[62,66,76,124,141,142,176,393,440],[62,66,76,124,141,142,175,393,440],[60,61,76,124,141,142],[76,124,141,142,491,492,495],[76,124,141,142,523],[76,124,141,142,526],[76,124,141,142,492,493,495,496,497],[76,124,141,142,492],[76,124,141,142,492,493,495],[76,124,141,142,492,493],[76,124,141,142,500],[76,124,141,142,487,500,501],[76,124,141,142,487,500],[76,124,141,142,487,494],[76,124,141,142,488],[76,124,141,142,487,488,489,491],[76,124,141,142,487],[76,124,141,142,530,531],[76,124,141,142,530,531,532,533],[76,124,141,142,530,532],[76,124,141,142,530],[68,76,124,141,142],[76,124,141,142,397],[76,124,141,142,404],[76,124,141,142,183,196,197,198,200,357],[76,124,141,142,183,187,189,190,191,192,346,357,359],[76,124,141,142,357],[76,124,141,142,197,213,290,337,353],[76,124,141,142,183],[76,124,141,142,377],[76,124,141,142,357,359,376],[76,124,141,142,276,290,318,445],[76,124,141,142,283,300,337,352],[76,124,141,142,238],[76,124,141,142,341],[76,124,141,142,340,341,342],[76,124,141,142,340],[70,76,124,138,141,142,180,183,190,193,194,195,197,201,269,274,320,328,338,348,357,393],[76,124,141,142,183,199,227,272,357,373,374,445],[76,124,141,142,199,445],[76,124,141,142,272,273,274,357,445],[76,124,141,142,445],[76,124,141,142,183,199,200,445],[76,124,141,142,193,339,345],[76,124,141,142,150,291,353],[76,124,141,142,291,353],[62,76,124,141,142,291],[62,76,124,141,142,270,291,292],[76,124,141,142,218,236,353,429],[76,124,141,142,334,424,425,426,427,428],[76,124,141,142,333],[76,124,141,142,333,334],[76,124,141,142,191,215,216,270],[76,124,141,142,217,218,270],[76,124,141,142,270],[62,76,124,141,142,184,418],[62,76,124,141,142,167],[62,76,124,141,142,199,225],[62,76,124,141,142,199],[76,124,141,142,223,228],[62,76,124,141,142,224,396],[62,66,76,124,138,141,142,174,175,176,393,438,439],[76,124,136,138,141,142,187,213,241,259,270,343,357,358,445],[76,124,141,142,328,344],[76,124,141,142,393],[76,124,141,142,182],[76,124,141,142,150,276,288,309,311,352,353],[76,124,141,142,150,276,288,308,309,310,352,353],[76,124,141,142,302,303,304,305,306,307],[76,124,141,142,304],[76,124,141,142,308],[62,76,124,141,142,224,291,396],[62,76,124,141,142,291,394,396],[62,76,124,141,142,291,396],[76,124,141,142,259,349],[76,124,141,142,349],[76,124,138,141,142,358,396],[76,124,141,142,296],[76,123,124,141,142,295],[76,124,141,142,209,210,212,242,270,283,284,285,287,320,352,355,358],[76,124,141,142,286],[76,124,141,142,210,218,270],[76,124,141,142,283,352],[76,124,141,142,283,292,293,294,296,297,298,299,300,301,312,313,314,315,316,317,352,353,445],[76,124,141,142,281],[76,124,138,141,142,150,187,208,210,212,213,214,218,246,259,268,269,320,348,357,358,359,393,445],[76,124,141,142,352],[76,123,124,141,142,197,212,269,285,300,348,350,351,358],[76,124,141,142,283],[76,123,124,141,142,208,242,262,277,278,279,280,281,282],[76,124,138,141,142,262,263,277,358,359],[76,124,141,142,197,259,269,270,285,348,352,358],[76,124,138,141,142,357,359],[76,124,138,141,142,156,355,358,359],[76,124,138,141,142,150,167,180,187,199,209,210,212,213,214,219,241,242,243,245,246,249,250,252,255,256,257,258,270,347,348,353,355,357,358,359],[76,124,138,141,142,156],[76,124,141,142,183,184,185,187,194,355,356,393,396,445],[76,124,138,141,142,156,167,203,375,377,378,379,445],[76,124,141,142,150,167,180,203,213,242,243,250,259,267,270,348,353,355,360,361,367,373,389,390],[76,124,141,142,193,194,269,328,339,348,357],[76,124,138,141,142,167,184,242,355,357,365],[76,124,141,142,275],[76,124,138,141,142,386,387,388],[76,124,141,142,355,357],[76,124,141,142,187,212,242,347,396],[76,124,138,141,142,150,250,259,355,361,367,369,373,389,392],[76,124,138,141,142,193,328,373,382],[76,124,141,142,183,219,347,357,384],[76,124,138,141,142,199,219,357,368,369,380,381,383,385],[70,76,124,141,142,210,211,212,393,396],[76,124,138,141,142,150,167,187,193,201,209,213,214,242,243,245,246,258,259,267,270,328,347,348,353,354,355,360,361,362,364,366,396],[76,124,138,141,142,156,193,355,367,386,391],[76,124,141,142,323,324,325,326,327],[76,124,141,142,249,251],[76,124,141,142,253],[76,124,141,142,251],[76,124,141,142,253,254],[76,124,138,141,142,187,208,358],[62,76,124,138,141,142,150,182,184,187,209,210,212,213,214,240,355,359,393,396],[76,124,138,141,142,150,167,186,191,242,354,358],[76,124,141,142,277],[76,124,141,142,278],[76,124,141,142,279],[76,124,141,142,202,206],[76,124,138,141,142,187,202,209],[76,124,141,142,205,206],[76,124,141,142,207],[76,124,141,142,202,203],[76,124,141,142,202,220],[76,124,141,142,202],[76,124,141,142,248,249,354],[76,124,141,142,247],[76,124,141,142,203,353,354],[76,124,141,142,244,354],[76,124,141,142,203,353],[76,124,141,142,320],[76,124,141,142,204,209,211,242,270,276,285,288,289,319,355,358],[76,124,141,142,218,229,232,233,234,235,236],[76,124,141,142,336],[76,124,141,142,197,211,212,263,270,283,296,300,329,330,331,332,334,335,338,347,352,357],[76,124,141,142,218],[76,124,141,142,240],[76,124,138,141,142,209,211,221,237,239,241,355,393,396],[76,124,141,142,218,229,230,231,232,233,234,235,236,394],[76,124,141,142,203],[76,124,141,142,263,264,267,348],[76,124,138,141,142,249,357],[76,124,138,141,142],[76,124,141,142,262,283],[76,124,141,142,261],[76,124,141,142,258,263],[76,124,141,142,260,262,357],[76,124,138,141,142,186,263,264,265,266,357,358],[62,76,124,141,142,215,217,270],[76,124,141,142,271],[62,76,124,141,142,184],[62,76,124,141,142,353],[62,70,76,124,141,142,212,214,393,396],[76,124,141,142,184,418,419],[62,76,124,141,142,228],[62,76,124,141,142,150,167,182,222,224,226,227,396],[76,124,141,142,199,353,358],[76,124,141,142,353,363],[62,76,124,136,138,141,142,150,182,228,272,393,394,395],[62,76,124,141,142,175,176,393,440],[62,63,64,65,66,76,124,141,142],[76,124,129,141,142],[76,124,141,142,370,371,372],[76,124,141,142,370],[62,66,76,124,138,140,141,142,150,174,175,176,177,179,180,182,246,308,359,392,396,440],[76,124,141,142,406],[76,124,141,142,408],[76,124,141,142,410],[76,124,141,142,412],[76,124,141,142,414,415,416],[76,124,141,142,420],[67,69,76,124,141,142,398,403,405,407,409,411,413,417,421,423,431,432,434,443,444,445,446],[76,124,141,142,422],[76,124,141,142,430],[76,124,141,142,224],[76,124,141,142,433],[76,123,124,141,142,263,264,265,267,299,353,435,436,437,440,441,442],[76,124,141,142,174],[76,124,141,142,537],[76,124,141,142,465],[76,124,141,142,463,465],[76,124,141,142,454,462,463,464,466,468],[76,124,141,142,452],[76,124,141,142,455,460,465,468],[76,124,141,142,451,468],[76,124,141,142,455,456,459,460,461,468],[76,124,141,142,455,456,457,459,460,468],[76,124,141,142,452,453,454,455,456,460,461,462,464,465,466,468],[76,124,141,142,468],[76,124,141,142,450,452,453,454,455,456,457,459,460,461,462,463,464,465,466,467],[76,124,141,142,450,468],[76,124,141,142,455,457,458,460,461,468],[76,124,141,142,459,468],[76,124,141,142,460,461,465,468],[76,124,141,142,453,463],[76,124,141,142,476,485],[76,124,141,142,475,476],[76,124,141,142,156,174],[76,124,141,142,470,471],[76,124,141,142,469,472],[76,124,141,142,490],[76,89,93,124,141,142,167],[76,89,124,141,142,156,167],[76,84,124,141,142],[76,86,89,124,141,142,164,167],[76,124,141,142,144,164],[76,84,124,141,142,174],[76,86,89,124,141,142,144,167],[76,81,82,85,88,124,135,141,142,156,167],[76,89,96,124,141,142],[76,81,87,124,141,142],[76,89,110,111,124,141,142],[76,85,89,124,141,142,159,167,174],[76,110,124,141,142,174],[76,83,84,124,141,142,174],[76,89,124,141,142],[76,83,84,85,86,87,88,89,90,91,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,111,112,113,114,115,116,124,141,142],[76,89,104,124,141,142],[76,89,96,97,124,141,142],[76,87,89,97,98,124,141,142],[76,88,124,141,142],[76,81,84,89,124,141,142],[76,89,93,97,98,124,141,142],[76,93,124,141,142],[76,87,89,92,124,141,142,167],[76,81,86,89,96,124,141,142],[76,124,141,142,156],[76,84,89,110,124,141,142,172,174],[76,124,141,142,505,506],[76,124,141,142,505],[76,124,141,142,486,505,506,516],[76,124,135,136,138,139,140,141,142,144,156,164,167,173,174,469,476,477,478,479,480,481,482,483,484,485],[76,124,141,142,478,479,480,481],[76,124,141,142,478,479,480],[76,124,141,142,478],[76,124,141,142,479],[76,124,141,142,476],[76,124,141,142,517],[76,124,141,142,498,511,512,535],[76,124,141,142,487,498,502,503,535],[76,124,141,142,527],[76,124,136,141,142,156,486,487,492,498,499,502,504,507,508,509,510,513,514,516,522,535],[76,124,141,142,498,511,512,513,535],[76,124,141,142,486,515],[76,124,141,142,172,520],[76,124,141,142,498,499,502,504,507,535],[76,124,136,141,142,156,486,487,492,498,499,502,503,504,507,508,509,510,511,512,513,514,515,516,522,535],[76,124,136,141,142,156,172,486,487,492,495,498,499,502,503,504,507,508,509,510,511,512,513,514,515,516,520,521,522,524,525,527,528,529,534,535],[76,124,141,142,473],[76,124,141,142,146,518]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"36a2e4c9a67439aca5f91bb304611d5ae6e20d420503e96c230cf8fcdc948d94","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"a305ee2f90e34e9e70aba9a9e9a154ce20c4d5cd1499cd21b8dc3617e1e5c810","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"58647d85d0f722a1ce9de50955df60a7489f0593bf1a7015521efe901c06d770","impliedFormat":1},{"version":"9018e9493596e2d041fc531f5101a0d05f9d2e1311f4ae3c74345b1766158c08","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"a6f137d651076822d4fe884287e68fd61785a0d3d1fdb250a5059b691fa897db","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"bceb58df66ab8fb00170df20cd813978c5ab84be1d285710c4eb005d8e9d8efb","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"80523c00b8544a2000ae0143e4a90a00b47f99823eb7926c1e03c494216fc363","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"746911b62b329587939560deb5c036aca48aece03147b021fa680223255d5183","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c8d3e5a18ba35629954e48c4cc8f11dc88224650067a172685c736b27a34a4dc","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"2b55d426ff2b9087485e52ac4bc7cfafe1dc420fc76dad926cd46526567c501a","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"358765d5ea8afd285d4fd1532e78b88273f18cb3f87403a9b16fef61ac9fdcfe","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"865a2612f5ec073dd48d454307ccabb04c48f8b96fda9940c5ebfe6b4b451f51","impliedFormat":1},{"version":"89e7fd23f6e6ced38596054161f5fb88737018909c6529c946cb349b74b95275","impliedFormat":1},{"version":"115b2ad73fa7d175cd71a5873d984c21593b2a022f1a2036cc39d9f53629e5dc","impliedFormat":1},{"version":"1be330b3a0b00590633f04c3b35db7fa618c9ee079258e2b24c137eb4ffcd728","impliedFormat":1},{"version":"bb7a61dd55dc4b9422d13da3a6bb9cc5e89be888ef23bbcf6558aa9726b89a1c","impliedFormat":1},{"version":"3da0083607976261730c44908eab1b6262f727747ef3230a65ecd0153d9e8639","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"dd721e5707f241e4ef4ab36570d9e2a79f66aad63a339e3cbdbac7d9164d2431","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"3849a7f92d0e11b785f6ae7bedb25d9aad8d1234b3f1cf530a4e7404be26dd0a","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"57d6ac03382e30e9213641ff4f18cf9402bb246b77c13c8e848c0b1ca2b7ef92","impliedFormat":1},{"version":"f040772329d757ecd38479991101ef7bc9bf8d8f4dd8ee5d96fe00aa264f2a2b","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"57e47d02e88abef89d214cdf52b478104dc17997015746e288cbb580beaef266","impliedFormat":1},{"version":"04a2d0bd8166f057cc980608bd5898bfc91198636af3c1eb6cb4eb5e8652fbea","impliedFormat":1},{"version":"376c21ad92ca004531807ea4498f90a740fd04598b45a19335a865408180eddd","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"a9af0e608929aaf9ce96bd7a7b99c9360636c31d73670e4af09a09950df97841","impliedFormat":1},{"version":"48d37b90a04e753a925228f50304d02c4f95d57bf682f8bb688621c3cd9d32ec","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"b68c4ed987ef5693d3dccd85222d60769463aca404f2ffca1c4c42781dce388e","impliedFormat":1},{"version":"cfb5b5d514eb4ad0ee25f313b197f3baa493eee31f27613facd71efb68206720","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"9715fe982fccf375c88ac4d3cc8f6a126a7b7596be8d60190a0c7d22b45b4be4","impliedFormat":1},{"version":"1fe24e25a00c7dd689cb8c0fb4f1048b4a6d1c50f76aaca2ca5c6cdb44e01442","impliedFormat":1},{"version":"672f293c53a07b8c1c1940797cd5c7984482a0df3dd9c1f14aaee8d3474c2d83","impliedFormat":1},{"version":"0a66cb2511fa8e3e0e6ba9c09923f664a0a00896f486e6f09fc11ff806a12b0c","impliedFormat":1},{"version":"d703f98676a44f90d63b3ffc791faac42c2af0dd2b4a312f4afdb5db471df3de","impliedFormat":1},{"version":"0cfe1d0b90d24f5c105db5a2117192d082f7d048801d22a9ea5c62fae07b80a0","impliedFormat":1},{"version":"ef61792acbfa8c27c9bd113f02731e66229f7d3a169e3c1993b508134f1a58e0","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"414cc05e215b7fc5a4a6ece431985e05e03762c8eb5bf1e0972d477f97832956","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"5c2e5ca7d53236bbf483a81ae283e2695e291fe69490cd139b33fa9e71838a69","impliedFormat":1},{"version":"a73bee51e3820392023252c36348e62dd72e6bae30a345166e9c78360f1aba7e","impliedFormat":1},{"version":"6ea68b3b7d342d1716cc4293813410d3f09ff1d1ca4be14c42e6d51e810962e1","impliedFormat":1},{"version":"c319e82ac16a5a5da9e28dfdefdad72cebb5e1e67cbdcc63cce8ae86be1e454f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a020158a317c07774393974d26723af551e569f1ba4d6524e8e245f10e11b976","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"8bba776476c48b0e319d243f353190f24096057acede3c2f620fee17ff885dba","impliedFormat":1},{"version":"a3abe92070fbd33714bd837806030b39cfb1f8283a98c7c1f55fffeea388809e","impliedFormat":1},{"version":"ceb6696b98a72f2dae802260c5b0940ea338de65edd372ff9e13ab0a410c3a88","impliedFormat":1},{"version":"2cd914e04d403bdc7263074c63168335d44ce9367e8a74f6896c77d4d26a1038","impliedFormat":1},{"version":"ac60bbee0d4235643cc52b57768b22de8c257c12bd8c2039860540cab1fa1d82","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"3bc8605900fd1668f6d93ce8e14386478b6caa6fda41be633ee0fe4d0c716e62","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"9f31420a5040dbfb49ab94bcaaa5103a9a464e607cabe288958f53303f1da32e","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"f11d0dcaa4a1cba6d6513b04ceb31a262f223f56e18b289c0ba3133b4d3cd9a6","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"56013416784a6b754f3855f8f2bf6ce132320679b8a435389aca0361bce4df6b","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"9c066f3b46cf016e5d072b464821c5b21cc9adcc44743de0f6c75e2509a357ab","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c51641ab4bfa31b7a50a0ca37edff67f56fab3149881024345b13f2b48b7d2de","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"52abbd5035a97ebfb4240ec8ade2741229a7c26450c84eb73490dc5ea048b911","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"4360ad4de54de2d5c642c4375d5eab0e7fe94ebe8adca907e6c186bbef75a54d","impliedFormat":1},{"version":"c338dff3233675f87a3869417aaea8b8bf590505106d38907dc1d0144f6402ef","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"9c9cae45dc94c2192c7d25f80649414fa13c425d0399a2c7cb2b979e4e50af42","impliedFormat":1},{"version":"068f063c2420b20f8845afadb38a14c640aed6bb01063df224edb24af92b4550","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"b8719d4483ebef35e9cb67cd5677b7e0103cf2ed8973df6aba6fdd02896ddc6e","impliedFormat":1},{"version":"643672ce383e1c58ea665a92c5481f8441edbd3e91db36e535abccbc9035adeb","impliedFormat":1},{"version":"6dd9bcf10678b889842d467706836a0ab42e6c58711e33918ed127073807ee65","impliedFormat":1},{"version":"8fa022ea514ce0ea78ac9b7092a9f97f08ead20c839c779891019e110fce8307","impliedFormat":1},{"version":"c93235337600b786fd7d0ff9c71a00f37ca65c4d63e5d695fc75153be2690f09","impliedFormat":1},{"version":"10179c817a384983f6925f778a2dac2c9427817f7d79e27d3e9b1c8d0564f1f4","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"c0a666b005521f52e2db0b685d659d7ee9b0b60bc0d347dfc5e826c7957bdb83","impliedFormat":1},{"version":"807d38d00ce6ab9395380c0f64e52f2f158cc804ac22745d8f05f0efdec87c33","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"d05fb434f4ba073aed74b6c62eff1723c835de2a963dbb091e000a2decb5a691","impliedFormat":1},{"version":"10e6166be454ddb8c81000019ce1069b476b478c316e7c25965a91904ec5c1e3","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"703989a003790524b4e34a1758941d05c121d5d352bccca55a5cfb0c76bca592","impliedFormat":1},{"version":"a58abf1f5c8feb335475097abeddd32fd71c4dc2065a3d28cf15cacabad9654a","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"671aeae7130038566a8d00affeb1b3e3b131edf93cbcfff6f55ed68f1ca4c1b3","impliedFormat":1},{"version":"f0f05149debcf31b3a717ce8dd16e0323a789905cb9e27239167b604153b8885","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"955c69dde189d5f47a886ed454ff50c69d4d8aaec3a454c9ab9c3551db727861","impliedFormat":1},{"version":"cec8b16ff98600e4f6777d1e1d4ddf815a5556a9c59bc08cc16db4fd4ae2cf00","impliedFormat":1},{"version":"9e21f8e2c0cfea713a4a372f284b60089c0841eb90bf3610539d89dbcd12d65a","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"c226288bda11cee97850f0149cc4ff5a244d42ed3f5a9f6e9b02f1162bf1e3f4","impliedFormat":1},{"version":"210a4ec6fd58f6c0358e68f69501a74aef547c82deb920c1dec7fa04f737915a","impliedFormat":1},{"version":"8eea4cc42d04d26bcbcaf209366956e9f7abaf56b0601c101016bb773730c5fe","impliedFormat":1},{"version":"f5319e38724c54dff74ee734950926a745c203dcce00bb0343cb08fbb2f6b546","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"12b8dfed70961bea1861e5d39e433580e71323abb5d33da6605182ec569db584","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"7e560f533aaf88cf9d3b427dcf6c112dd3f2ee26d610e2587583b6c354c753db","impliedFormat":1},{"version":"71e0082342008e4dfb43202df85ea0986ef8e003c921a1e49999d0234a3019da","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"e71e103fb212e015394def7f1379706fce637fec9f91aa88410a73b7c5cbd4e3","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"794998dc1c5a19ce77a75086fe829fb9c92f2fd07b5631c7d5e0d04fd9bc540c","impliedFormat":1},{"version":"2b0b12d0ee52373b1e7b09226eae8fbf6a2043916b7c19e2c39b15243f32bde2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"bdc5fd605a6d315ded648abf2c691a22d0b0c774b78c15512c40ddf138e51950","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"6cd4b0986c638d92f7204d1407b1cb3e0a79d7a2d23b0f141c1a0829540ce7ef","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"d58265e159fc3cb30aa8878ba5e986a314b1759c824ff66d777b9fe42117231a","impliedFormat":1},{"version":"ff8fccaae640b0bb364340216dcc7423e55b6bb182ca2334837fee38636ad32e","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"59ee66cf96b093b18c90a8f6dbb3f0e3b65c758fba7b8b980af9f2726c32c1a2","impliedFormat":1},{"version":"c590195790d7fa35b4abed577a605d283b8336b9e01fa9bf4ae4be49855940f9","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"026a43d8239b8f12d2fc4fa5a7acbc2ad06dd989d8c71286d791d9f57ca22b78","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"14cf3683955f914b4695e92c93aae5f3fe1e60f3321d712605164bfe53b34334","impliedFormat":1},{"version":"12f0fb50e28b9d48fe5b7580580efe7cc0bd38e4b8c02d21c175aa9a4fd839b0","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"7cd657e359eac7829db5f02c856993e8945ffccc71999cdfb4ab3bf801a1bbc6","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"29c2aa0712786a4a504fce3acd50928f086027276f7490965cb467d2ce638bae","impliedFormat":1},{"version":"f14e63395b54caecc486f00a39953ab00b7e4d428a4e2c38325154b08eb5dcc2","impliedFormat":1},{"version":"e749bbd37dadf82c9833278780527c717226e1e2c9bc7b2576c8ec1c40ec5647","impliedFormat":1},{"version":"46e4e179b295f08d0bd0176fe44cf6c89558c9091d3cb3894f9eaaa42ea1add1","impliedFormat":1},{"version":"08f52a9edaabeda3b2ea19a54730174861ceed637c5ca1c1b0c39459fdc0853e","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"29164fb428c851bc35b632761daad3ae075993a0bf9c43e9e3bc6468b32d9aa5","impliedFormat":1},{"version":"3c01539405051bffccacffd617254c8d0f665cdce00ec568c6f66ccb712b734f","impliedFormat":1},{"version":"ef9021bdfe54f4df005d0b81170bd2da9bfd86ef552cde2a049ba85c9649658f","impliedFormat":1},{"version":"17a1a0d1c492d73017c6e9a8feb79e9c8a2d41ef08b0fe51debc093a0b2e9459","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"96e1caae9b78cde35c62fee46c1ec9fa5f12c16bc1e2ab08d48e5921e29a6958","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"9e0327857503a958348d9e8e9dd57ed155a1e6ec0071eb5eb946fe06ccdf7680","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"01aa917531e116485beca44a14970834687b857757159769c16b228eb1e49c5f","impliedFormat":1},{"version":"397f568f996f8ffcf12d9156342552b0da42f6571eadba6bce61c99e1651977d","impliedFormat":1},{"version":"e2fd426f3cbc5bbff7860378784037c8fa9c1644785eed83c47c902b99b6cda9","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"a52674bc98da7979607e0f44d4c015c59c1b1d264c83fc50ec79ff2cfea06723","impliedFormat":1},{"version":"bcca16e60015db8bbf6bd117e88c5f7269337aebb05fc2b0701ae658a458c9c3","impliedFormat":1},{"version":"5e1246644fab20200cdc7c66348f3c861772669e945f2888ef58b461b81e1cd8","impliedFormat":1},{"version":"eb39550e2485298d91099e8ab2a1f7b32777d9a5ba34e9028ea8df2e64891172","impliedFormat":1},{"version":"e108f38a04a607f9386d68a4c6f3fdae1b712960f11f6482c6f1769bab056c2e","impliedFormat":1},{"version":"a3128a84a9568762a2996df79717d92154d18dd894681fc0ab3a098fa7f8ee3b","affectsGlobalScope":true,"impliedFormat":1},{"version":"347791f3792f436950396dd6171d6450234358001ae7c94ca209f1406566ccbf","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"714d8ebb298c7acc9bd1f34bd479c57d12b73371078a0c5a1883a68b8f1b9389","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"51bf55bb6eb80f11b3aa59fb0a9571565a7ea304a19381f6da5630f4b2e206c4","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"02f8ef78d46c5b27f108dbb56709daa0aff625c20247abb0e6bb67cd73439f9f","impliedFormat":1},{"version":"b7fff2d004c5879cae335db8f954eb1d61242d9f2d28515e67902032723caeab","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"6812502cc640de74782ce9121592ae3765deb1c5c8e795b179736b308dd65e90","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"2b664c3cc544d0e35276e1fb2d4989f7d4b4027ffc64da34ec83a6ccf2e5c528","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"b10bc147143031b250dc36815fd835543f67278245bf2d0a46dca765f215124e","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"1e4c6ac595b6d734c056ac285b9ee50d27a2c7afe7d15bd14ed16210e71593b0","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"3c7b3aecd652169787b3c512d8f274a3511c475f84dcd6cead164e40cad64480","impliedFormat":1},{"version":"9a01f12466488eccd8d9eafc8fecb9926c175a4bf4a8f73a07c3bcf8b3363282","impliedFormat":1},{"version":"b80f624162276f24a4ec78b8e86fbee80ca255938e12f8b58e7a8f1a6937120b","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"b95a6f019095dd1d48fd04965b50dfd63e5743a6e75478343c46d2582a5132bf","impliedFormat":99},{"version":"c2008605e78208cfa9cd70bd29856b72dda7ad89df5dc895920f8e10bcb9cd0a","impliedFormat":99},{"version":"b97cb5616d2ab82a98ec9ada7b9e9cabb1f5da880ec50ea2b8dc5baa4cbf3c16","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"00b0f43b3770f66aa1e105327980c0ff17a868d0e5d9f5689f15f8d6bf4fb1f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"272a7e7dbe05e8aaba1662ef1a16bbd57975cc352648b24e7a61b7798f3a0ad7","affectsGlobalScope":true,"impliedFormat":1},{"version":"a1219ee18b9282b4c6a31f1f0bcc9255b425e99363268ba6752a932cf76662f0","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"f2b3bca04d1bfe583daae1e1f798c92ec24bb6693bd88d0a09ba6802dee362a8",{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"52dcc257df5119fb66d864625112ce5033ac51a4c2afe376a0b299d2f7f76e4a","impliedFormat":1},{"version":"e5bab5f871ef708d52d47b3e5d0aa72a08ee7a152f33931d9a60809711a2a9a3","impliedFormat":1},{"version":"e16dc2a81595736024a206c7d5c8a39bfe2e6039208ef29981d0d95434ba8fcf","impliedFormat":1},{"version":"cc4a4903fb698ca1d961d4c10dce658aa3a479faf40509d526f122b044eaf6a4","impliedFormat":1},{"version":"19ee8416e6473ed6c7adb868fa796b5653cf0fa2a337658e677eaa0d134388c3","impliedFormat":1},{"version":"1328ab4e442614b28cdb3d4b414cf68325c0da0dca07287a338d0654b7a00261","impliedFormat":1},{"version":"a039dc21f045919f3cbee2ec13812cc6cc3eebc99dae4be00973230f468d19a6","impliedFormat":1},{"version":"3fbe57af01460e49dcd29df55d6931e1672bc6f1be0fb073d11410bc16f9037d","impliedFormat":1},{"version":"f760be449e8562ec5c09bb5187e8e1eabf3c113c0c58cddda53ef8c69f3e2131","impliedFormat":1},{"version":"44325ed13294fce6ab825b82947bbeed2611db7dad9d9135260192f375e5a189","impliedFormat":1},{"version":"e392e8fb5b514eafc585601c1d781485aa6dd6a320e75daf1064a4c6918a1b45","impliedFormat":1},{"version":"46e4a36e8ddbdfb4e7330e11c81c970dc8b218611df9183d39c41c5f8c653b55","impliedFormat":1},{"version":"370bde134aa8c2abc926d0e99d3a4d5d5dba65c6ee65459137e4f02670cbf841","impliedFormat":1},{"version":"6332f565867cf4a740a70e30f31cefba37ef7cebcf74f22eab8d744fde6d193e","impliedFormat":1},{"version":"2977b7884aedc895a1d0c9c210c7cf3272c29d6959a08a6fa3ff71e0aff08175","impliedFormat":1},{"version":"17f2922d41ddd032830a91371c948cd9ce903b35c95adca72271a54584f19b0b","impliedFormat":1},{"version":"3eed76ede2a1a14d7c9bb0a642041282dcc264811139d3dd275c9fe14efc9840","impliedFormat":1},{"version":"e3cf0611709328b449ec13f8c436712d62003620ce480139fae46ce001c2ee9f","impliedFormat":1},{"version":"8d369483f0c2b9ee388129cfdb6a43bc8112b377e86a41884bd06e19ce04f4c1","impliedFormat":99},{"version":"b558c9a18ea4e6e4157124465c3ef1063e64640da139e67be5edb22f534f2f08","impliedFormat":1},{"version":"01374379f82be05d25c08d2f30779fa4a4c41895a18b93b33f14aeef51768692","impliedFormat":1},{"version":"b0dee183d4e65cf938242efaf3d833c6b645afb35039d058496965014f158141","impliedFormat":1},{"version":"c0bbbf84d3fbd85dd60d040c81e8964cc00e38124a52e9c5dcdedf45fea3f213","impliedFormat":1},"8ab2020159a945e311929abd23acd6b9e2ad9b665bf31a7247f2e12ccad7662d",{"version":"751764bb94219b4ce8f5475dc35d3de2e432fea01a0c9610cd7f69ad05e398c6","impliedFormat":1},{"version":"ee70b8037ecdf0de6c04f35277f253663a536d7e38f1539d270e4e916d225a3f","affectsGlobalScope":true,"impliedFormat":1},{"version":"a660aa95476042d3fdcc1343cf6bb8fdf24772d31712b1db321c5a4dcc325434","impliedFormat":1},{"version":"282f98006ed7fa9bb2cd9bdbe2524595cfc4bcd58a0bb3232e4519f2138df811","impliedFormat":1},{"version":"6222e987b58abfe92597e1273ad7233626285bc2d78409d4a7b113d81a83496b","impliedFormat":1},{"version":"cbe726263ae9a7bf32352380f7e8ab66ee25b3457137e316929269c19e18a2be","impliedFormat":1},{"version":"8b96046bf5fb0a815cba6b0880d9f97b7f3a93cf187e8dcfe8e2792e97f38f87","impliedFormat":99},{"version":"bacf2c84cf448b2cd02c717ad46c3d7fd530e0c91282888c923ad64810a4d511","affectsGlobalScope":true,"impliedFormat":1},{"version":"82e687ebd99518bc63ea04b0c3810fb6e50aa6942decd0ca6f7a56d9b9a212a6","impliedFormat":99},{"version":"7f698624bbbb060ece7c0e51b7236520ebada74b747d7523c7df376453ed6fea","impliedFormat":1},{"version":"8f07f2b6514744ac96e51d7cb8518c0f4de319471237ea10cf688b8d0e9d0225","impliedFormat":1},{"version":"257b83faa134d971c738a6b9e4c47e59bb7b23274719d92197580dd662bfafc3","impliedFormat":99},{"version":"d2e64a6f25013b099e83bfadb2c388d7bef3e8f3fdb25528225bbc841e7e7e3a","impliedFormat":99},{"version":"369ba5259e66ca8c7d35e3234f7a2a0863a770fdb8266505747c65cf346a0804","impliedFormat":99},{"version":"64d984f55025daf604f670b7dfd090ea765f2098aee871174ef2ee3e94479098","impliedFormat":99},{"version":"f147b6710441cf3ec3234adf63b0593ce5e8c9b692959d21d3babc8454bcf743","impliedFormat":99},{"version":"e96d5373a66c2cfbbc7e6642cf274055aa2c7ff6bd37be7480c66faf9804db6d","impliedFormat":99},{"version":"02bcdd7a76c5c1c485cbf05626d24c86ac8f9a1d8dc31f8924108bbaa4cf3ba9","impliedFormat":99},{"version":"c874ab6feac6e0fdf9142727c9a876065777a5392f14b0bbcf869b1e69eb46b5","impliedFormat":99},{"version":"7c553fc9e34773ddbaabe0fa1367d4b109101d0868a008f11042bee24b5a925d","impliedFormat":99},{"version":"9962ce696fbdce2421d883ca4b062a54f982496625437ae4d3633376c5ad4a80","impliedFormat":99},{"version":"e3ea467c4a7f743f3548c9ed61300591965b1d12c08c8bb9aaff8a002ba95fce","impliedFormat":99},{"version":"4c17183a07a63bea2653fbfc0a942b027160ddbee823024789a415f9589de327","impliedFormat":99},{"version":"3e2203c892297ea44b87470fde51b3d48cfe3eeb6901995de429539462894464","impliedFormat":99},{"version":"c84bf7a4abc5e7fdf45971a71b25b0e0d34ccd5e720a866dd78bb71d60d41a3f","impliedFormat":99},{"version":"e01ea380015ed698c3c0e2ccd0db72f3fc3ef1abc4519f122aa1c1a8d419a505","impliedFormat":99},{"version":"5ada1f8a9580c0f7478fe03ae3e07e958f0b79bdfb9dd50eeb98c1324f40011b","impliedFormat":99},{"version":"a8301dc90b4bd9fba333226ee0f1681aeeff1bd90233a8f647e687cb4b7d3521","impliedFormat":99},{"version":"e3225dc0bec183183509d290f641786245e6652bc3dce755f7ef404060693c35","impliedFormat":99},{"version":"09a03870ed8c55d7453bc9ad684df88965f2f770f987481ca71b8a09be5205bc","impliedFormat":99},{"version":"e6233e1c976265e85aa8ad76c3881febe6264cb06ae3136f0257e1eab4a6cc5a","impliedFormat":99},{"version":"2cdd50ddc49e2d608ee848fc4ab0db9a2716624fabb4209c7c683d87e54d79c5","impliedFormat":99},{"version":"e431d664338b8470abb1750d699c7dfcebb1a25434559ef85bb96f1e82de5972","impliedFormat":99},{"version":"2c4254139d037c3caca66ce291c1308c1b5092cfcb151eb25980db932dd3b01a","impliedFormat":99},{"version":"970ae00ed018cb96352dc3f37355ef9c2d9f8aa94d7174ccd6d0ed855e462097","impliedFormat":99},{"version":"d2f8dee457ef7660b604226d471d55d927c3051766bdd80353553837492635c3","impliedFormat":99},{"version":"110a503289a2ef76141ffff3ffceb9a1c3662c32748eb9f6777a2bd0866d6fb1","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"310e6b62c493ce991624169a1c1904015769d947be88dc67e00adc7ebebcfa87","impliedFormat":99},{"version":"62fefda288160bf6e435b21cc03d3fbac11193d8d3bd0e82d86623cca7691c29","impliedFormat":99},{"version":"fcc46a8bcbf9bef21023bba1995160a25f0bc590ca3563ec44c315b4f4c1b18a","impliedFormat":99},{"version":"669573548930fb7d0a0761b827e203dc623581e21febf0be80fb02414f217d74","impliedFormat":99},{"version":"f974db5be6d7428044e19c2848e72495a9b56a8d33b6fcab392e7fb5328eb8df","impliedFormat":99},{"version":"aa348c4fb2f8ac77df855f07fb66281c9f6e71746fdff3b13c7932aa7642b788","impliedFormat":99},{"version":"262d4c61b446d096f0615b5acb0a09007b2a425c6a0fd38b66e404e7b6021812","signature":"4b96dd19fd2949d28ce80e913412b0026dc421e5bf6c31d87c7b5eb11b5753b4"},{"version":"0309a01650023994ed96edbd675ea4fdc3779a823ce716ad876cc77afb792b62","impliedFormat":99},{"version":"f13d7beeea58e219daef3a40e0dc4f2bd7d9581ac04cedec236102a12dfd2090","impliedFormat":99},{"version":"48c411efce1848d1ed55de41d7deb93cbf7c04080912fd87aa517ed25ef42639","affectsGlobalScope":true,"impliedFormat":1},{"version":"a094636c05f3e75cb072684dd42cd25a4c1324bec4a866706c85c04cecd49613","affectsGlobalScope":true,"impliedFormat":99},{"version":"fe2d63fcfdde197391b6b70daf7be8c02a60afa90754a5f4a04bdc367f62793d","impliedFormat":99},{"version":"9a3e2c85ec1ab7a0874a19814cc73c691b716282cb727914093089c5a8475955","impliedFormat":99},{"version":"cbdc781d2429935c9c42acd680f2a53a9f633e8de03290ec6ea818e4f7bff19a","impliedFormat":99},{"version":"9f6d9f5dd710922f82f69abf9a324e28122b5f31ae6f6ce78427716db30a377e","impliedFormat":99},{"version":"ac2414a284bdecfd6ab7b87578744ab056cd04dd574b17853cd76830ef5b72f2","impliedFormat":99},{"version":"c3f921bbc9d2e65bd503a56fbc66da910e68467baedb0b9db0cc939e1876c0d7","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"0cc99fbb161d78729d71fad66c6c363e3095862d6277160f29fa960744b785c6","affectsGlobalScope":true,"impliedFormat":99},"129a86faf021cbd7dd75371cef3025e3eb2137128b7cd35ea06b566f3413315c",{"version":"61de421bab744b4e1e105a40e0409221ba5729868cde52fbdbe3e21a1fb2c69c","impliedFormat":99},{"version":"6c84d9410cb39c418e76c74200dc943c8909863efe798df05c552933801dc42f","impliedFormat":99},"5e401fc828b4921a6afcef15c2b6c9bc74b29040b71d57c2a76f4d1e7caec48f",{"version":"3aa67bb15606c55fdd705fc720d1c5550078cb83f21355806a72495e8f59cdfa","signature":"07415405713faaca7975ad1bfbf9b412ba297f47a6923fe9cebafd7f4dcb7395"},"6a9a61013d77df9d48372dd9f6da3b2b7395ca4675b4fb4cfbce80060beb8d86",{"version":"8a47212f6abe252070dd70813e9cbc0d5da90c9b38eea89d54adb11009d54367","signature":"db83c13502fee0c5dbd787ed5a1a04a38565773dc9682c4640a79445acbfd8c4"},{"version":"56385e5186ccf2b1324b83f7fbbbab4779f43560e447edd684324eac8392d7a9","signature":"7aab0d0c6a4ef4fb4182ef7b3c62d9b2793c59269032202bd13bcf627c424586"},{"version":"6bba4d19678505765461ca28cc01c6c51239bbe755fb8401adb58e8138e6a28b","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"0f407ed5c25d2fb2c97a216d175a3efc0fe21fd0faf4de3cca00931016375991","signature":"46f78a03a7c6287bd7eaa736b490ad45be142f90896a3d02a739501b3d5b2de0"},{"version":"ce2c97f3e5b6a222f19f005b6ef4886d89bf4c16bc1903658802d04a552fc9d8","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"cdec8f8e76fb1dfd3a6fdca604aae2bf3f51c1110f7a0012dc5604fdbb12f689","signature":"e79eaa521c0e7a44cd476d4c4ec7a2150ffd2800d3f4a92e0c4d32eee476849b"},"af7de680832f9a99aa122282a09b27ce3ca195b913239ed42775e10797136a9d",{"version":"109a57909ecfc571bd171917b30ddf31684c8f1725bf7d53e000b5d38c104d3b","signature":"2a7bab0a07cf0c896bf323315d1227ad4b827db34b9709aef32c8da66e27b264"},{"version":"23379963068ccab413c24e33c0f0679ffef1b88582a029ce9e957f0e6fdda250","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"d3b8f898932a894fcf3a2f0137add132d5345f85c97e58cb6974a3b5f80b6127","fe03821ef6e674b9bd68b3c957a27e0eec9f2c3da4afd8174eb2a1640b2a0a3b","306cc4071730e408e1e6df19d616bc3b4bf5dcec55343057834231cea7e2c9b8",{"version":"52a70f24d22923b7e17f60ed809fb9de4c5a4256f5569779625f48926b676916","signature":"1407a535ec14502779bad1ffe40e3a4b9c62fc4a41516f0707a2bc7fbc631dea"},"42907f4412fd066787e8b359f4128f20eb1135138802044bd442f35a29eb7cf5","f7f070618b5719d90fbfccf1bc459ef8a2a97edd90bf6487febf154ea3114f98","bf57f2e7ca5e569e5fd94ea50c2ce06ade65d259932c7a6d14031e9b364e550b","88f37b50ea286e8d8d32f3717c3e4ba7e579d148642e9e9328a00013c69ba1f3",{"version":"990c18ed3bda755e96cfa999e76bfbd7f6fcf94798fcfc0e829a51e84d145296","signature":"4eded94606b7000cf46dd5462b5b12f647134ec39c6e05fb6709cf272016dbc8"},{"version":"22f459701ac93eedb32de747e1ee5b222a3147d695081d12f0709c14dfebdca5","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"5871463359ee203e285aeefe48e86d85c8e7c0c8941fc2f7db2dbbd6411e703b","ce71392a988633a5400e5c42a22e14144c582f2c099ac74fa6e7327728343da5","1341b7ef069321245b63656c887094a74e28ad9a4f0579d55d50b9c8ea227262","f046a76e6c2b6bc3a9c5cc53b99da7340b63ebd7f84e13a8a53e60f99d7c85a5",{"version":"68448904d6c46e4a04f229602ea4fe77ebe567283b1e370aefe127c03c4ae630","signature":"146b47847e0270185276ad42ff155ebe162ecf46e15dbcf5148818abd6edbb89"},{"version":"a5d5c5023804437603b51acbf146f6d7bfa9585c6addb8e45488f0e14f8ddbcc","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"59dd6a66b9027174bc2de83cd568fc135fa912f806adc7a2fe2eee15bdb869b8","51d79ddaeeb46de8db2aa5928bf15a830b33ff0b91050c548a2ea82831010b60","1dd7fa4fda5354d9a835ff15de140db2664c9ad4bba57f4fa0c8ac7b25b2f0b6","030d523e29ac43dfe92303dc59be3348b02a6d3a1769e17fde3951bdfc56b464","3e0fc0ac00cc2be442d8ca11f3737d52118bf68c5b54e7857cffe8c663f52b12",{"version":"d98d37d22a4f21a4f30b39e97f89f7c0c835dd876833fef626da59bc4233f3a1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"4caa4cac2a1e939f9a5d8b39dd20a2f318f01881a3f3864a5c5af3a1529db9c1",{"version":"082384575b4e3102bd07382ebf1d5d092f8bc50a2a5adc60ec4a29edfb8cf0b0","signature":"afb8d4594629cbe256f2dfd5deda53112e0ebd357cdf0327c188bd54b345a8ac"},"b0d46e41c32b3782e20f25320f58232228e91c180a950db06e42609b91ef05b6","2da7275c608d241683551b29cbf9ea5c0fd3dc2b7ced764cc0f8c1165ae58e06","69c6d9d6b3010183174068ff14b4f9c1a848a353465b56e45a0c2924201fd9cc","86fda80ee4ecd4769ab8279ee610b1b8e0ecd47fecb58328d9c5166fae86f3bd","1295888b88f413131e5f4c37d9fd5c255ce9bfba635251e9814afcb2bcdfff5a",{"version":"a847e3f5afadb7ed21842c6cbf7fd60834ad1bcedff9fce23849dcd2b90378f7","signature":"c0a7be11d3925f4fefde7545d6b2ab556191569cb173b9e453c4a04c3fab8b70"},{"version":"de301f7af4b1661d04d5aac265f6ff2af7e441b7312d4019f652e3c08e1260d1","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},"5104a2f8fa881effd1b048038c25604132b01382e16daf61fbdc1e6f85b9f77c","94040c84ae4c3b99f8ea00427348c5c9ef9f47c807f1be0bb80e663495612695","fd4f4e6c74fa3649550c5eccc911d3f77ca234531145b8fdb010efdca03a1106",{"version":"c0efe9f1a7841088fd3b0cdb9c2290a6f5c1fa7a3aebdf3aaf270d344ff56e21","signature":"7175ca141479166d4eb613d64104eeed84093343cca30afd909912737339e111"},{"version":"ff0d9317f266d554abc684692d66c0a4cc76c2ed22248400d7838032dea5d84c","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"662ff9b16378365dce8ac7752370c8c8609f11c6f5d53480c5dad6bdc00a467d","signature":"b3f301ee5acbe3a478e4b2fc5c86b1eb021f624afbf2295dbc0c605cd052de16"},"47ebcf736abb92ce3ed3fb7343a6eb1d020eff483436727712fab16ed67ea278","cd508a03854158f7811ebe1b6e14d5c1ff156399f765beedaa4d497ba5043db6","022ee5aa38e927a6858cdee2229ab1140b2a2e2418aaee6d3e047dfadd23918f","1001ab28de9e55660a8c414bb2c341fcf254fdb5457e602f70565483e252de41","fa452635b5ac56ff89f7c2bef9ae560435d24d8f50f4ba9e661dee9e50475d28","1a017381422d08696696ee73d0b477f65817ceb7db6a862c0ee1274ec8ebf19b",{"version":"d56cbcaff5b30886b540c474659d8f5fc082047f3ae8976640d2aa3598f582e1","signature":"f8bd52b96ed7397f629111c5a304fb3f2ece87d337876744d26f0714522a7b9e"},{"version":"69d769ffc47cd8d0d806d6071eab28f4782f61ece0c7618a55012db5bd502b76","signature":"c33f75e17727a7df7e2881cd0b28d62dc70876d2d96222c348dc0598eb4fcaa4"},{"version":"486ed41bc98c68b73172b4622f0dc7acad350071d8a4fbeb653cb4569063990d","signature":"263fab41c7b64b6598d109105c670000624083b47863301fa31364d8536c5117"},{"version":"39206a279a23cf492b6f8c0eafba537730267bfd7664b67fad043d11f7e99e4b","signature":"ba54342937eb116567785cc93f6ef7c8ce3b041a67bb2ecb579ab28359b06047"},{"version":"dfcae003f00edd918fdd0dfcdb6870f1d4af24aa23801fad006c442a64c7ed21","signature":"17a9a39323eca460763521ba02db702489e22436e011d3f3939638cd661e128d"},"f43e26699703fd407e111d2558aafb92575cd3281858130b0d2f855374852417","532ebc18ccfd0dde7741c8b3063a4d38117516b46b83b92a79587713b8e64d67","1adc127175e94440e797af519f6778fcbbd7965dbff102f9c2bb8059b3650e28","dc4b10a73d3cee390f462c6048e5078bcf82daba93e72d9d0aecbd08955969d3","013d02117a4fc8d8fa1ec31bd3938cb8e90223caa927fe4f53d4ad4e7aff8925","99cf4a58ed1f267f2015406c09811c7022964aa3b5189653e2f718584f02f22b","a20c29441b8b2a250d239abb758985f9f1c65c819a416424e3c44ec2fa03d351","4d88e3cda8df2131fc7346db4b49fa4524fec4faf61bf38b6f787f49dc47e49d","3fe73618b3a62112b94b4278ee58588d7115540be222120fb53fc598917894ff","e7f7041381a9396b1e52568564b7f90ee87f156e53a2f563f12f8a4043b13325","11c4815cba4cd9b80431a6ac938bb837610f865c8211032589b22b11cff37d53","938a200cab8ef63bd63ef811d7732a0d380cc5331c865d131395e442f73fb338","af5f2422c3e48b5ec5ce234c717528d39c9ff3274cc603aaa79ba1c488ffe573","c722f93b3272613f2645e308b41c9bd2511ceb17a69860422d09f19361542334","17cb6cb4f714d0efd0de6e7a5254a6e6b463ced0a8c266724660ff1352da2645","4cb8ce370dded833aa921768d43d7b5c635363c8bf8e343139df3a5554329354",{"version":"e8d76e26b982ca730a86d8d37321f522b2cca011f01701e05a501eaf615798ea","signature":"c0f8673c25e3392d2841196ba47799fa401e4135a9f37f75fedb369b79270144"},"1414615c866abf5fa5557c370fd8744a06a31e6c4403c713e17272fa2db15a91","87efe6a08272fd6ad1f8c21c2c55b64cd713a48daf94a12e14ab4030944b7ba3","304fd3f360a445e635986b3264ee8017e6a3631a2a4f6faf0d0bf7d3f98e62eb","00c9a5c5fda50a4811bf4db39355b1043ea43e82e5e13e1a02910c6f7fb1bcea",{"version":"3531e6a9e84fa1ba01a8971a55f5b3a74817d6e7122aebfadc764775a0484660","signature":"cfc174c783fc21759487bf9e1d56afab8a6f58526c444c24ec7172160de8b71a"},"704ba60c7bb6de74ea9ee213ab33b6895f951fb1d5133bb3444f2391927e2c4c",{"version":"e48f6dcb71e4a88c72d93d3eeb11567059a9c075cee4e0e7c91fb6262365e105","signature":"57d980925947bc4bfd6e281f5e6bd3708ae959576cb9294fc21eda02d374d61e"},"bcd8ad29810bc2920e2ff325d33049563704dd3fc2c9740028746179cd31bf47","1ecf29c8dc2e9586a6c9535eadf19b5b17ea2d62745a867d21fba3e2829a683e",{"version":"4bc5e39495809a3cfa9547d01d1d5e521287fc443983326e22b4743fbf25b960","signature":"272b05835aacc3bab10b150e1cbbe13b0878f49f64f027cae57c19906d07469c"},"8c9b519d0d682e2d0dd5b8356b7e62d0a01b1b81d3705aee1c1a09ae5c56378b","6094bf0d11a37da8a60b167df89428c3d988500712dd8ed3c02917e0d623bebc","c72b2f435d11d58cc2a2020381cf36ac97a0bd7d3a668057dd5c8ed1d5874826","795e1c578622c4c8fd9a246e144e4540ef2f3a01b03b6a411ede901928fe2fc8","28c98194218617b9c9e803128051216d77c2166117d1693b10bae45b583a029e","ab48429c066dfe36aa14e12c8522072e6e135533676cf086d672de50641e7c72","cdff09ada67354d8826dc502e9a740f81bb1a3f34c2c45af61378aef68567fe9","e2addadd8a38f776ffd643ae6c08e3d91fe0b819e1a0fc4a1a13c298de43444d",{"version":"d74845f8b6f02aff4b3dd36b99003dd4fe58ac2376ae511f6d92bcb3a153d6aa","signature":"6781825fe0bacb6828c87c307a6126fe3a0ae631d079cb04b8b852804fdaa7bb"},{"version":"a63d1dceca466019e671006f8f071c0bf5596206b98557239ca64f9e58c191e0","signature":"3b31470d2936e80d198c801cc596e0569736a32b31f5544a6453f3934e24fba7"},{"version":"4763b947677a33bd554af8624d932aedea06bcf57a25b11ef6a7e057210358d6","signature":"6cf67db3bb1d66e8d7dbe762fed6bdf98d8236cc313c69a42dffae94476336ab"},{"version":"01e63a083e1d722533721f35cd10ed2011a5d17df5fa68b969abefadb74b8065","signature":"be09afc1ae89010a88add65672a54d0466689ebdf88ceb8c79581890a4dcb775"},{"version":"c6105d7065e1f76af49a833e0c362778e946099ffe3a6b37024c1d9878ab711e","signature":"5576567f6ca28958685673a4624caf179772e77116ae31d2fd92099fd66e2b1e"},"efff1f1a0df5cb30394a6323bf9fad9532cf4284de8ca3820b8c49d8bd88b463","b9aeaade08803f4f2f5e1fcf82527451afc68b6c93896a749d27d92da239344a","4a9d89488db5fc1b8f348f0a33e84232dae02df3f85edda019821bbd9508c8c1","1c6373a37b29674ecc5a712e2f349c66ee5cb9ef924f5863fb348d4857cb6104","4acddc3962efcacf5acb59b20ae27783e26bbe15b4367c9c162198ec9027739f","ff25d2789578679f3f1b1f5dcbb5c87a0dad6488fa583cd1fded8ae83c58b1b5","30cdf53273913e8ee8b04cabd23d6b53df2f92265a9c88470e5d7c957d95b7ef","dc575c9e3b32d510c9a974135f13f83e2b836d8bb482310c40b2495b0a763dd2",{"version":"e7b45666ab7e581dd6ac0ca1036a98fb75bbf75b388f981dd8c30f0b7dc78ab4","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"730539759405584ef46f895820a5723593e9da5fa3a66217debc9bce8aa57ed1","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"39422101b85c49c26174ec094efa2f65e5f4d3d26ae0d5cb1d9078b2ff157d5f","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},"abac25862a735319832d29b4daa0eac0af8d63e7a9f6dd0d95dcb687d8cab4a6",{"version":"e512d2d1e2e9b9c2b407b08d722041ebdfb76d4d66852bf0f6e4d8f8514a6d2d","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},"6e2133f8ad52b25f2ce9c7ccc30fdd2653214949e08552ee3fbd970716d07dc2",{"version":"a9f7e7f88d04c20cb51988a6c5bceaf00920c6d7034f094106613a6a1f1e3160","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"}],"root":[449,474,519,536,[539,653]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":9},"referencedMap":[[645,1],[646,2],[647,3],[648,4],[649,5],[650,6],[651,7],[652,8],[653,9],[644,10],[597,11],[601,12],[607,13],[612,14],[615,15],[625,16],[627,17],[629,18],[633,19],[638,20],[640,21],[643,22],[599,23],[606,24],[595,25],[596,26],[611,27],[608,28],[609,29],[610,30],[602,31],[604,32],[603,33],[605,34],[598,35],[613,36],[614,37],[600,38],[639,39],[616,31],[617,40],[620,41],[618,42],[619,40],[621,43],[624,44],[626,45],[641,46],[642,47],[628,48],[630,49],[623,50],[632,51],[622,40],[631,52],[547,53],[635,54],[634,54],[636,54],[637,55],[536,56],[541,57],[540,54],[539,54],[544,58],[543,59],[542,60],[546,61],[545,54],[548,62],[550,63],[549,54],[552,64],[551,31],[553,65],[556,66],[555,67],[557,68],[558,68],[560,69],[559,54],[561,70],[563,71],[562,31],[564,72],[566,73],[565,54],[568,74],[567,31],[570,75],[569,67],[571,76],[554,54],[572,76],[573,77],[574,78],[576,79],[575,31],[577,80],[579,81],[578,82],[581,83],[580,54],[582,84],[584,85],[583,31],[586,86],[585,54],[587,87],[589,88],[588,67],[590,89],[591,90],[593,91],[592,31],[594,54],[449,92],[395,54],[475,54],[121,93],[122,93],[123,94],[76,95],[124,96],[125,97],[126,98],[71,54],[74,99],[72,54],[73,54],[127,100],[128,101],[129,102],[130,103],[131,104],[132,105],[133,105],[134,106],[135,107],[136,108],[137,109],[77,54],[75,54],[138,110],[139,111],[140,112],[174,113],[141,114],[142,54],[143,115],[144,116],[145,117],[146,118],[147,119],[148,120],[149,121],[150,122],[151,123],[152,123],[153,124],[154,54],[155,125],[156,126],[158,127],[157,128],[159,129],[160,130],[161,131],[162,132],[163,133],[164,134],[165,135],[166,136],[167,137],[168,138],[169,139],[170,140],[171,141],[78,54],[79,54],[80,54],[118,142],[119,54],[120,54],[172,143],[173,144],[178,145],[179,146],[177,53],[175,147],[176,148],[60,54],[62,149],[291,53],[522,54],[523,150],[524,151],[527,152],[526,54],[487,54],[498,153],[493,154],[496,155],[511,156],[500,54],[503,157],[502,158],[514,158],[501,159],[525,54],[495,160],[497,160],[489,161],[492,162],[508,161],[494,163],[488,54],[61,54],[482,54],[532,164],[534,165],[533,166],[531,167],[530,54],[69,168],[398,169],[403,10],[405,170],[199,171],[347,172],[374,173],[274,54],[192,54],[197,54],[338,174],[266,175],[198,54],[376,176],[377,177],[319,178],[335,179],[239,180],[342,181],[343,182],[341,183],[340,54],[339,184],[375,185],[200,186],[273,54],[275,187],[195,54],[210,188],[201,189],[214,188],[243,188],[185,188],[346,190],[356,54],[191,54],[297,191],[298,192],[292,193],[426,54],[300,54],[301,193],[293,194],[430,195],[429,196],[425,54],[379,54],[334,197],[333,54],[424,198],[294,53],[217,199],[215,200],[427,54],[428,54],[216,201],[419,202],[422,203],[226,204],[225,205],[224,206],[433,53],[223,207],[261,54],[436,54],[439,54],[438,53],[440,208],[181,54],[344,209],[345,210],[368,54],[190,211],[180,54],[183,212],[313,53],[312,213],[311,214],[302,54],[303,54],[310,54],[305,54],[308,215],[304,54],[306,216],[309,217],[307,216],[196,54],[188,54],[189,188],[397,218],[406,219],[410,220],[350,221],[349,54],[258,54],[441,222],[359,223],[295,224],[296,225],[288,226],[280,54],[286,54],[287,227],[317,228],[281,229],[318,230],[315,231],[314,54],[316,54],[270,232],[351,233],[352,234],[282,235],[283,236],[278,237],[330,238],[358,239],[361,240],[259,241],[186,242],[357,243],[182,173],[380,244],[391,245],[378,54],[390,246],[70,54],[366,247],[246,54],[276,248],[362,54],[205,54],[389,249],[194,54],[249,250],[348,251],[388,54],[382,252],[187,54],[383,253],[385,254],[386,255],[369,54],[387,242],[213,256],[367,257],[392,258],[322,54],[325,54],[323,54],[327,54],[324,54],[326,54],[328,259],[321,54],[252,260],[251,54],[257,261],[253,262],[256,263],[255,263],[254,262],[209,264],[241,265],[355,266],[442,54],[414,267],[416,268],[285,54],[415,269],[353,233],[299,233],[193,54],[242,270],[206,271],[207,272],[208,273],[204,274],[329,274],[220,274],[244,275],[221,275],[203,276],[202,54],[250,277],[248,278],[247,279],[245,280],[354,281],[290,282],[320,283],[289,284],[337,285],[336,286],[332,287],[238,288],[240,289],[237,290],[211,291],[269,54],[402,54],[268,292],[331,54],[260,293],[279,294],[277,295],[262,296],[264,297],[437,54],[263,298],[265,298],[400,54],[399,54],[401,54],[435,54],[267,299],[235,53],[68,54],[218,300],[227,54],[272,301],[212,54],[408,53],[418,302],[234,53],[412,193],[233,303],[394,304],[232,302],[184,54],[420,305],[230,53],[231,53],[222,54],[271,54],[229,306],[228,307],[219,308],[284,122],[360,122],[384,54],[364,309],[363,54],[404,54],[236,53],[396,310],[63,53],[66,311],[67,312],[64,53],[65,54],[381,313],[373,314],[372,54],[371,315],[370,54],[393,316],[407,317],[409,318],[411,319],[413,320],[417,321],[448,322],[421,322],[447,323],[423,324],[431,325],[432,326],[434,327],[443,328],[446,211],[445,54],[444,329],[538,330],[537,54],[466,331],[464,332],[465,333],[453,334],[454,332],[461,335],[452,336],[457,337],[467,54],[458,338],[463,339],[469,340],[468,341],[451,342],[459,343],[460,344],[455,345],[462,331],[456,346],[477,347],[476,348],[365,349],[450,54],[472,350],[471,54],[470,54],[473,351],[512,54],[490,54],[491,352],[58,54],[59,54],[10,54],[11,54],[13,54],[12,54],[2,54],[14,54],[15,54],[16,54],[17,54],[18,54],[19,54],[20,54],[21,54],[3,54],[22,54],[23,54],[4,54],[24,54],[28,54],[25,54],[26,54],[27,54],[29,54],[30,54],[31,54],[5,54],[32,54],[33,54],[34,54],[35,54],[6,54],[39,54],[36,54],[37,54],[38,54],[40,54],[7,54],[41,54],[46,54],[47,54],[42,54],[43,54],[44,54],[45,54],[8,54],[51,54],[48,54],[49,54],[50,54],[52,54],[9,54],[53,54],[54,54],[55,54],[57,54],[56,54],[1,54],[96,353],[106,354],[95,353],[116,355],[87,356],[86,357],[115,329],[109,358],[114,359],[89,360],[103,361],[88,362],[112,363],[84,364],[83,329],[113,365],[85,366],[90,367],[91,54],[94,367],[81,54],[117,368],[107,369],[98,370],[99,371],[101,372],[97,373],[100,374],[110,329],[92,375],[93,376],[102,377],[82,378],[105,369],[104,367],[108,54],[111,379],[509,380],[506,381],[507,380],[510,382],[505,54],[486,383],[483,384],[481,385],[479,386],[478,54],[480,387],[484,54],[485,388],[518,389],[513,390],[504,391],[499,54],[528,392],[515,393],[529,394],[516,395],[521,396],[520,397],[517,398],[535,399],[474,400],[519,401]],"affectedFilesPendingEmit":[645,646,647,648,649,650,651,652,653,597,601,607,612,615,625,627,629,633,638,640,643,599,606,595,596,611,608,609,610,602,604,603,605,598,613,614,600,639,616,617,620,618,619,621,624,626,641,642,628,630,623,632,622,631,547,635,634,636,637,536,541,540,544,543,542,546,545,548,550,549,552,551,553,556,555,557,558,560,559,561,563,562,564,566,565,568,567,570,569,571,554,572,573,574,576,575,577,579,578,581,580,582,584,583,586,585,587,589,588,590,591,593,592,594,474,519],"version":"5.9.3"}
\ No newline at end of file
diff --git a/memory/contracts.md b/memory/contracts.md
index b98f0ce..f022645 100644
--- a/memory/contracts.md
+++ b/memory/contracts.md
@@ -16,10 +16,66 @@
- 档位路由 `resolve_route(tier)->Route(provider,model)` 读 `config.tier_defaults`(M1 仅全局默认;回退/熔断属 M5/T5.4,**未实现**)。
- 记账 `SqlAlchemyLedgerSink(session)` 写 `UsageLedger`(owner_id=scope.user_id 单用户 stub;列名 `cache_read`)。成本表 `pricing.py`(未知 provider/model→0)。
+### C1 扩展(T5.4, 2026-06-19)· 多 provider 韧性(回退链 + 熔断 + 能力协商/降级) owner @llm 状态: 稳定
+- 来源:`ARCHITECTURE.md §4.4(能力协商/降级)/§4.5(回退/重试/熔断)`。**仅加字段/形参,不破既有调用**(旧 `Gateway(adapters, ledger, resolver=resolve_route)` 仍可用)。
+- **`ServedBy` 加字段**(C1 形变,**仅加可选 bool,默认 False,向后兼容**):`ServedBy(provider, model, fell_back=False, degraded=False)`。`degraded` 标能力降级(所选 provider 不支持原生结构化输出,改走 instructor JSON-提示路径)。→ **OpenAPI 表面**:`ServedBy` 不直接出 API(review/draft SSE 不回 served_by,settings/providers 也不含它)→ 经核 **本次无 OpenAPI 形变,前端无需 re-gen**;若后续把 `served_by` 暴露到响应体,再触发 gen:api。
+- **`Gateway` 构造扩**(关键字,全可选):`Gateway(adapters, ledger, *, chain_resolver: Callable[[Tier], list[Route]] | None=None, resolver: Callable[[Tier], Route] | None=None, max_retries=2, breaker: CircuitBreaker | None=None)`。`chain_resolver`(回退链)优先;`resolver`(单路由,M1 兼容)被自动包成单元素链;皆缺省回退到默认 `resolve_chain`。`run`/`stream` 行为:沿链逐 provider→瞬时失败退避重试 R 次→仍失败/熔断打开/无适配器则切下一个;首个成功者服务,非链首即 `served_by.fell_back=True`;链耗尽抛 `AppError(LLM_UNAVAILABLE)`。记账记**实际服务方** provider/model(回退后不记主模型)。流式仅在首块产出前可切(产出后中途失败上抛,不静默重连,§4.5)。
+- **回退链解析(routing.py)**:`resolve_chain(tier)->list[Route]`(默认仅全局 `tier_defaults`,单元素);`chain_from_routing(tier, primary:str, fallback:list[str])->list[Route]`(据 DB `tier_routing` 行去重保序构链——供 apps/api 包成 `ChainResolver` 注入网关,**apps/api 待接线**:`build_gateway_for_tier` 目前仍只注主 provider 适配器 + `resolver=resolve_route`,要启用回退须按 fallback 预备多个适配器 + 传 `chain_resolver`)。`ChainResolver = Callable[[Tier], list[Route]]` 类型别名导出。
+- **熔断器**:`CircuitBreaker(*, threshold=5, reset_seconds=30.0, clock=time.monotonic)`:`is_open(provider)`/`record_failure`/`record_success`;连续失败超阈值→短时熔断(网关直接跳过该 provider 走回退);成功清零;冷却窗口过半开放行。**默认每个 Gateway 实例自带一个 breaker**(进程内、非跨实例共享)——跨请求持久熔断需调用方注入共享 breaker。
+- **瞬时错误契约**:适配器把厂商 429/超时/5xx/连接错误翻译为 `ww_llm_gateway.errors.TransientProviderError`(OpenAI 兼容/Anthropic/Gemini 适配器均已包装,按异常类名 + status_code 判定);网关另把 `AppError(RATE_LIMITED)` 也视作可重试/可回退。非瞬时错误(内容策略拒绝等)原样上抛、不重试。
+- **新适配器**(均经注入的客户端 Protocol,**测试不联网、不硬 import 厂商 SDK**):
+ - `AnthropicAdapter(provider, client: AnthropicClient, *, structured_client?)`:`capabilities()=(structured_output=True, prefix_cache=True, thinking=True)`;缓存断点经 system 块 `cache_control:{type:ephemeral}`(§4.6);结构化经 `instructor.from_anthropic`(懒构建)。
+ - `GeminiAdapter(provider, client: GeminiClient)`:`capabilities()=(structured_output=True, prefix_cache=False, thinking=True)`;结构化经 `config.response_mime_type=application/json + response_schema`,文本回 `model_validate_json`。
+ - `OpenAICompatAdapter` 不变(新增瞬时错误包装,参与回退链)。
+- **依赖**:`packages/llm_gateway/pyproject.toml` 加 `tenacity>=8.2`(已 `uv sync`;原为 instructor 传递依赖)。**`anthropic`/`google-genai` SDK 已由 @devops 加(已 `uv sync`)**——适配器仍懒导入/注入客户端,单测零依赖。
+
+#### C1 扩 follow-up #2(2026-06-19)· `build_adapter` provider→适配器工厂 owner @llm 状态: 稳定
+- 位置:`packages/llm_gateway/ww_llm_gateway/factory.py`,经包 `__init__` 导出 `build_adapter`。
+- **签名(稳定,apps/api `build_gateway_for_tier` 据此逐 provider 建适配器进 `adapters` dict)**:
+ `def build_adapter(provider: str, *, api_key: str, base_url: str | None = None) -> ProviderAdapter`。
+- **分派**:`provider=="anthropic"` → `AnthropicAdapter`(懒 import `AsyncAnthropic(api_key=, base_url?)`);`provider in {"gemini","google"}` → `GeminiAdapter`(懒 import `genai.Client(api_key=)`,**忽略 base_url**——SDK 无该形参);其余(deepseek/kimi/qwen/glm/openai…)→ `OpenAICompatAdapter(provider, AsyncOpenAI(api_key=, base_url=))`。
+- 工厂从 api_key 构**真实**客户端(适配器内部仍是注入客户端 Protocol,测试零联网);故单测只断**按 provider 名选对适配器类 + provider 字段透传**(`tests/test_build_adapter_factory.py`,6 测),不联网。
+- **@backend 行动**:`build_gateway_for_tier` 现可调 `build_adapter(provider, api_key=…, base_url=…)` 替换「一律 `OpenAICompatAdapter`」,使回退链中的 anthropic/gemini 走对应专用适配器。**无 OpenAPI 形变**(served_by 不出 API),前端无需 re-gen。
+
+#### C1 扩(K1.2, 2026-06-19)· Kimi Code 订阅 plan 适配器(伪造头 + OAuth bearer) owner @llm 状态: 稳定
+- 位置:`packages/llm_gateway/ww_llm_gateway/adapters/kimi_code.py`,经包 `__init__` 导出 `KimiCodeAdapter`/`build_kimi_code_client`/`kimi_code_headers`/`kimi_device_id`/`kimi_device_model`/`KIMI_CODE_BASE_URL`/`KIMI_CODE_USER_AGENT`/`KIMI_CLI_VERSION`/`KIMI_CODE_PLATFORM`/`KIMI_CODE_PROVIDER`。
+- **provider 名**:`kimi-code`(与 API-key provider `kimi` 分离,便于档位切换)。`build_adapter("kimi-code", api_key=, base_url=None)` 是 **K1.3 接线的缝**——`api_key` 即 OAuth **access token**(OpenAI SDK 自动发 `Authorization: Bearer `),`base_url` 缺省 → `KIMI_CODE_BASE_URL`。
+- **base URL**:`https://api.kimi.com/coding/v1`(OpenAI 兼容;`KimiCodeAdapter` 子类化 `OpenAICompatAdapter`,capabilities 继承:structured_output/prefix_cache=True)。
+- **model**:`kimi-for-coding` 是**路由/档位关注点**,经 `.complete(req, model)` 传入,**不**在适配器硬编码。
+- **必需伪造头 = 完整 7 头(opencode 规范)**:`User-Agent` + 6 个 `X-Msh-*`。`build_kimi_code_client` 把 `kimi_code_headers()` 作为 `AsyncOpenAI(default_headers=…)`,每次请求随客户端发出。
+- **头集真源校正(2026-06-19)= `github.com/ooojustin/opencode-kimi`(`src/headers.ts` `kimiHeaders()` + `src/constants.ts`,1:1 镜像 kimi-cli v1.37.0)**。早先曾误信 `picassio/pi-kimi-coder`(仅 UA、不带 X-Msh-*)把头集裁成 UA-only——那是分歧/错误参考,已**回退到 opencode 完整 7 头**。coding API 校验全部 7 头,偏差→Moonshot 后端 `access_terminated_error: only available for Coding Agents`(403)。7 头精确值:
+ - `User-Agent` = `KimiCLI/1.37.0`(`f"KimiCLI/{KIMI_CLI_VERSION}"`;UA 前缀 = `KimiCLI/`,version 必须 == `X-Msh-Version`)。
+ - `X-Msh-Platform` = `kimi_cli`(字面常量字符串,**非** OS 名)。
+ - `X-Msh-Version` = `1.37.0`(= UA 里的 CLI 版本)。
+ - `X-Msh-Device-Name` = 主机名 ASCII 化(`socket.gethostname()`/`platform.node()`,裁非 ASCII)。
+ - `X-Msh-Device-Model` = `kimi_device_model()`:macOS `f"macOS {platform.mac_ver()[0]} {platform.machine()}"`(如 `"macOS 14.5 arm64"`);Windows `f"Windows {release} {machine}"`;其它 `f"{system} {release} {machine}"`(`platform.machine()` 原样,返回 `arm64`/`x86_64`,不归一化)。
+ - `X-Msh-Os-Version` = `platform.version()`(OS 内核版本串,≈ Node `os.version()`)。
+ - `X-Msh-Device-Id` = **稳定** UUID4 hex 无连字符(32 位小写)。`kimi_device_id()`:env `KIMI_DEVICE_ID` 优先 → 否则读/建 `~/.kimi/device_id`(首次写一次 `uuid.uuid4().hex`、之后复用;与 kimi-cli/opencode 共享路径)。**跨调用/进程稳定,绝不每次随机**。
+- 单测(不联网,断构造出的客户端 base_url + default_headers(全 7 头键 + 固定字面值 + device-id 32 位小写 hex 稳定 + host 派生值非空 ASCII) + bearer + 工厂分派;env `KIMI_DEVICE_ID` 注入保证 CI 确定):`tests/test_kimi_code_adapter.py` + `tests/test_kimi_code_factory.py`。
+- **@backend 行动(K1.3)**:OAuth device 服务刷新 token 后,`_build_provider_adapter`(`build_gateway_for_tier`)对 provider `kimi-code` 调 `build_adapter("kimi-code", api_key=<当前 access_token>, base_url=…)` 即得带伪造头的适配器;token 刷新/获取归 K1.3,本适配器只收当前 access token。**无 OpenAPI 形变**,前端无需 re-gen。
+
+#### C1 扩(kimi-code-key, 2026-06-20)· Kimi Code 订阅 plan **静态 Console Key**(ToS 合规变体) owner @llm 状态: 稳定
+- 位置:`packages/llm_gateway/ww_llm_gateway/adapters/kimi_code_key.py`,经包 `__init__` 导出 `KimiCodeKeyAdapter`/`build_kimi_code_key_client`/`KIMI_CODE_KEY_PROVIDER`。
+- **provider 名**:`kimi-code-key`(与 OAuth 的 `kimi-code`、moonshot 的 `kimi` 均分离)。`build_adapter("kimi-code-key", api_key=, base_url=None)` 走此分支。
+- **与 OAuth `kimi-code` 的关键区别**:`build_kimi_code_key_client` 构造**纯** `AsyncOpenAI(api_key=, base_url=KIMI_CODE_BASE_URL)`——**不设 `default_headers`**,即**不伪造 UA、不带 `X-Msh-*` 头**。Kimi Console(`kimi.com/code/console`)签发的 Key 走订阅额度、命中同一 coding 端点(`https://api.kimi.com/coding/v1`)+ 同 model `kimi-for-coding`,但 plain `Authorization: Bearer ` 即可(openclaw + models.dev 确认,ToS 允许第三方 Key,仅 UA 篡改违规)→ 故这是**干净/合规的默认路径**(无封号风险,对照 `KimiCodeAdapter` 的伪造头变体)。
+- **结构化输出**:coding 端点 `kimi-for-coding` thinking 开启,与强制 `tool_choice` 互斥(live 400)。`KimiCodeKeyAdapter` **复用** `kimi_code.build_kimi_code_structured_client`(instructor `Mode.JSON`)——同 OAuth 变体的 JSON-mode 修复。
+- 单测(不联网):`tests/test_kimi_code_key_factory.py`(5 测:工厂分派 + coding base + bearer + **无 X-Msh-*/无 KimiCLI UA** + JSON-mode 结构化 + 显式 base_url 覆盖)。
+- **@backend 接线**:`kimi-code-key` 是**普通 api_key provider**(auth_type="api_key",存 `api_key_enc`,Fernet)——经既有 `build_gateway_for_tier`/`_build_provider_adapter`/probe 路径**无改动**即可工作(只需 `provider_deps._PROVIDER_BASE_URLS` 注册 base_url)。**不**触发 `_build_provider_adapter` 的 OAuth 刷新分支(其判据 `auth_type==oauth or provider=="kimi-code"` 不命中本 provider)。**无 OpenAPI 形变**(用既有 `PUT /settings/providers`),前端无需 re-gen。
+
## C2 · DB schema(SQLAlchemy 模型) owner @db 状态: 待定义
- 来源:`ARCHITECTURE.md §3.1` DDL(11 创作表 + chapter_reviews + 运营表;无向量列;users stub)。
- 消费方:`@backend`(Repository/记忆服务/验收)、`@llm`(Agent reads/writes)。
+### C2 扩展(K1.1, 2026-06-19)· `provider_credentials` 加 OAuth 列 owner @db 状态: 稳定
+- 背景:K1 Kimi Code OAuth(订阅 plan device-flow)。`provider_credentials` 表新增两列 + 改 `api_key_enc` 可空,使一行可表「api_key 凭据」或「oauth 凭据」二选一。
+- **模型变更(`packages/db/ww_db/models.py` `ProviderCredential`)**:
+ - 新增 `auth_type: Mapped[str]` = `Text, nullable=False, server_default="'api_key'"`(值域 `"api_key"` | `"oauth"`)。既有行迁移后默认 `'api_key'`,向后兼容。
+ - 新增 `oauth_enc: Mapped[bytes | None]` = `LargeBinary, nullable=True`。**持 Fernet 加密的 JSON 包** `{access_token, refresh_token, expires_at}`(@backend K1.3 写/读:`encrypt`/`decrypt`,key 取 `settings.credential_enc_key`,同 `api_key_enc` 的 Fernet)。
+ - `api_key_enc` 由 `nullable=False` 改 **`nullable=True`**(`Mapped[bytes | None]`)——OAuth-only 行无 api_key;既有 api_key 行迁移后仍保留其值。
+ - `UniqueConstraint("owner_id","project_id","provider")` **不变**(OAuth provider 用独立 provider 名 `kimi-code`,与 api_key 的 `kimi` 分离,不撞约束)。
+- **迁移**:`packages/db/migrations/versions/1f011c42bd4d_provider_credentials_oauth_columns.py`(down_revision=`220ca2e3d53f`)。up: add_column auth_type/oauth_enc + alter_column api_key_enc→nullable;down 逆向。`alembic check` 无漂移。
+- **⚠️ K1.3 @backend 必读**:`api_key_enc` 现为 `bytes | None`,`apps/api/ww_api/services/credentials.py` 的 `StoredCredential.api_key_enc: bytes`(dataclass 字段 line 28)与 store 的 `r.api_key_enc` 赋值(line 75/97)mypy 报 `arg-type`(`bytes | None` ↳ `bytes`)。**K1.1 不动 @backend 文件(目录所有权)**;K1.3 接 OAuth store 时须把 `StoredCredential.api_key_enc` 改成 `bytes | None`(并加 `auth_type`/`oauth_enc` 字段/读写路径),届时 mypy 即绿。当前全仓 mypy 唯余此 2 处错(都在 credentials.py),其余门禁绿(ruff/format/alembic check/pytest 400 passed)。
+
## C3 · API / OpenAPI 端点 owner @backend 状态: 稳定(M1 端点全落:T1.7 settings + T1.4 projects/chapters, 2026-06-18)
- 来源:`ARCHITECTURE.md §7.2` 端点清单(章节端点统一 `/projects/:id/chapters/:no/...`;含 `PUT .../draft`(自动保存)、`/refine`、`/jobs/:id`、`/reviews`)。
- 消费方:`@frontend`(经 OpenAPI→TS 客户端)。**改端点/字段 → 前端必须 `pnpm gen:api` 重生成客户端。**
@@ -54,6 +110,93 @@
- **beats 存储形**:DB `outline.beats` 是 JSONB dict→写侧包成 `{"beats":[...]}` 落库,API 出参 `OutlineChapterView.beats` 解包成裸 `list[str]`。
- **@frontend 行动**:T3.6 `pnpm gen:api` 纳入看板/大纲端点。
+### C3 扩展(T4.3, 2026-06-19)· 文风端点:学文风(jobs 异步) + 回炉 + 最新指纹(独立 `routers/style.py`,已注册)
+- snake_case;改字段 → **@frontend 必须 `cd apps/web && pnpm gen:api`** 重生成 TS 客户端(T4.4 前置)。
+- `POST /projects/{id}/style` ← `StyleLearnRequest{samples:list[str](min_length=1), mode:Literal["create","update"]="create"}` → **202** `StyleLearnResponse{job_id:uuid}`。项目不存在→404 `NOT_FOUND`;无凭据→**503** `LLM_UNAVAILABLE`(在 `get_style_extract_gateway` dep 解析阶段拦下,**调度 job 之前**,不凭空写注定失败的 job)。流程:`job_repo.create(project_id,"style_learn")` → `session.commit()`(job 行在 202 前持久化供轮询)→ `background_tasks.add_task(run_job, session_factory, job.id, work)`。`work` 在 `run_job` **自建独立 session** 上自造 analyst 网关(`build_gateway_for_tier(session, SqlCredentialStore(session), "analyst")`)+ 写侧 repo → `run_style_extraction(style_extract_spec, samples_text="\n\n".join(samples), ...)` → 拆 `dimensions_json={name:value}`/`evidence_json={name:[evidence]}` → `SqlStyleFingerprintWriteRepo.append`(version+1)→ 返回 `{version, dims_count}`(落 `jobs.result`)。`run_job` 拥有 session 生命周期/commit(业务写 + job done 同一事务)。`mode` 不改落库逻辑(写侧始终 append 新版本),仅供前端区分首学/更新语义。前端经 `GET /jobs/{job_id}` 轮询。
+- `GET /projects/{id}/style` → 200 `StyleFingerprintResponse{dimensions:dict, evidence:dict, version:int}`(最新版本完整指纹,UX §6.9)。项目不存在→404;无指纹→404 `NOT_FOUND`。**新增端点**(ARCH §7.2 原缺读指纹端点,见 decisions 2026-06-19)。读侧用写侧 repo 的 `latest`(含 evidence/version),**不动 C5 读侧** `SqlStyleRepo.latest`/`StyleView`(只 dimensions,assemble 用),故 assemble/C5 行为不变。
+- `POST /projects/{id}/chapters/{no}/refine` ← `RefineRequest{segment:str(min_length=1), instruction:str|None=None}` → 200 `RefineResponse{original:str, refined:str}`。同步回炉:writer 网关(`get_refine_gateway`)`run(refiner_spec)`,`output_schema=None` → 取 `resp.text` 为 refined;输入 = `【待重写段落】{segment}` + 可选 `【改写指令】{instruction}`。**不写库**(不变量 #3,作者采纳经既有 draft 自动保存合入);**末尾 `session.commit()`**(网关 ledger add-only,否则 usage_ledger 静默丢失,同 draft/review 纪律)。项目不存在→404;无凭据→503。
+- 注入缝(`services/project_deps.py`):`get_style_write_repo`(`SqlStyleFingerprintWriteRepo`,服务 `GET /style` 读侧)、`get_style_extract_gateway`(analyst,学文风的凭据探测缝)、`get_refine_gateway`(writer)。测试经 `app.dependency_overrides` override;学文风后台路径 monkeypatch `style.build_gateway_for_tier`/`style.SqlStyleFingerprintWriteRepo` + `job_runner.SqlJobRepo`(`run_job` 自建 repo,不走 dep)。
+- 写侧 repo(`packages/core/ww_core/domain/style_repo.py`,经 `ww_core.domain` 导出):`StyleFingerprintWriteRepo`/`SqlStyleFingerprintWriteRepo`(加 `Write` 避撞 C5 读侧 `SqlStyleRepo`,仿 `DigestAppendRepo` 先例):`append(project_id, *, dimensions_json, evidence_json)->int`(version=当前 max+1,首次 1;**只 flush 不 commit**)+ `latest(project_id)->StyleFingerprintView|None`(含 dimensions/evidence/version,供 `GET /style`)。
+- **@frontend 行动**:T4.4 前 `pnpm gen:api` 纳入 style/refine/GET style + jobs 轮询类型。
+
+### C3 扩展(T5.5, 2026-06-19)· 规则端点 `POST /projects/:id/rules`(独立 `routers/rules.py`,已注册)
+- snake_case;改字段 → **@frontend 必须 `cd apps/web && pnpm gen:api`**(T5.6 前置)。
+- `POST /projects/{project_id}/rules` ← `RuleCreateRequest{level:Literal["global","genre","style","project"], content:str(min_length=1)}` → **201** `RuleView{level:str, content:str}`。非法 `level` / 空 `content` → **FastAPI 422**(Pydantic Literal/min_length 校验,非 AppError 信封)。
+- 加规则是**作者显式动作**(不变量 #3:不经 AI 静默写库)。写一行 `rules`(绑 `project_id`),喂给 assemble 的四级合并 `merge_rules`(global→genre→style→project)。
+- 提交边界:`RuleWriteRepo.create` 只 `flush()`,端点写后 `await session.commit()`(仿 foreshadow/outline 写侧)。
+- 写侧 repo(`packages/core/ww_core/domain/rule_repo.py`,经 `ww_core.domain` 导出):`RuleWriteRepo`(Protocol)/`SqlRuleWriteRepo`/`RuleWriteView{project_id?,level,content}`(加 `Write` 避撞 C5 读侧 `domain.repositories.RuleView{level,content}`,仿 `OutlineWriteRepo` 先例)。
+- 注入缝(`services/project_deps.py`):`get_rule_write_repo`(`SqlRuleWriteRepo`)。测试经 `app.dependency_overrides` override 注 fake repo + fake session(断 commit 计数)。
+- **@frontend 行动**:T5.6 前 `pnpm gen:api` 纳入 `POST /rules`(规则页用)。
+
+### C3 扩展(T5.2, 2026-06-19)· 生成/入库 + 读端点(独立 `routers/generation.py`,已注册) owner @backend 状态: 稳定
+- snake_case;改字段 → **@frontend 必须 `cd apps/web && pnpm gen:api`**(T5.6 前置)。生成走**即时返回**(预览→作者确认→入库,M5-d,非 jobs)。
+- `POST /projects/{project_id}/world/generate` ← `WorldGenerateRequest{brief:str(min1)}` → **200** `WorldGenPreviewResponse{entities:[WorldEntityCardView{type:str,name:str,rules:list[str]}]}`。**预览不入库**(worldbuilder writer 网关 `run_worldbuilder`);末尾 `commit()` 仅落网关 ledger。项目不存在→404;无凭据→**503** `LLM_UNAVAILABLE`(`get_worldbuilder_gateway` dep 解析阶段拦下)。
+- `POST /projects/{project_id}/characters/generate` ← `CharacterGenerateRequest{brief:str(min1), count:int=1(ge1,le12), role:str|None}` → **200** `CharacterGenPreviewResponse{cards:[CharacterCardView{name,role,traits:list[str],backstory,arc:str,speech_tics:list[str],tags:list[str],relations:[CharacterRelationView{name,kind,note?}]}]}`。**预览不入库**(character-gen writer 网关,注入已有角色防雷同,generated_so_far=[]);末尾 commit 落 ledger。404/503 同上。
+- `POST /projects/{project_id}/characters`(入库)← `CharacterIngestRequest{cards:[CharacterCardView](min1), acknowledge_conflicts:bool=false}` → **201** `CharacterIngestResponse{created:list[str], rejected_tables:list[str]}`。**入库 gate**:① `precheck_generated_cards`(continuity 预检, analyst 网关) 比对卡 vs 世界观/已有角色真相源——有冲突且 `acknowledge_conflicts=false` → **409 `CONFLICT_UNRESOLVED`** + `details{conflicts:[{type,where,refs,suggestion}], conflict_count}`(仿 accept gate,不静默入库,不变量#3;`acknowledge_conflicts=true` 即作者裁决放行)。② `partition_writes(character_gen_spec, {"characters":cards})` 白名单过滤(越权表→`rejected_tables` 审计 log+丢弃,character-gen 只声明 writes=characters 故正常为空)。③ 写 `characters` 行(schema list/str → DB JSONB **dict** 形变,见写侧 repo)。404/503 同上。提交边界:precheck 网关 ledger + 角色写侧均只 flush → 端点末尾一次 `commit()`(含冲突 409 路径也 commit 落 precheck usage)。
+- `GET /projects/{project_id}/rules` → **200** `RuleListResponse{rules:[RuleView{level,content}]}`(规则页;复用 C5 读侧 `SqlRulesRepo.all_for_project`,不动 assemble)。
+- `GET /skills`(独立 `skills_router`,prefix `/skills`)→ **200** `SkillListResponse{skills:[SkillView{name,scope,tier,reads:list[str],writes:list[str],genre?}]}`(技能库 UI;经 `get_skill_registry` 读 registry,按 name 升序)。
+- 写侧 repo(`packages/core/ww_core/domain/`,经 `ww_core.domain` 导出):`CharacterWriteRepo`/`SqlCharacterWriteRepo`/`CharacterWriteView{id,name,role}`(`character_repo.py`)+ `WorldEntityWriteRepo`/`SqlWorldEntityWriteRepo`/`WorldEntityWriteView{id,type,name}`(`world_entity_repo.py`,预留对称入库)。**形变**:`traits`/`speech_tics`(list)→`{"items":[...]}`、`arc`(str)→`{"text":...}`、`rules`(list)→`{"rules":[...]}`;`tags`/`relations`→直落 JSONB list(仅 character 入库端点已落地;world ingest 端点本期未建,仅 worldbuilder 走预览)。
+- 注入缝(`services/project_deps.py`):`get_worldbuilder_gateway`(writer)/`get_character_gen_gateway`(writer)/`get_precheck_gateway`(analyst)/`get_character_write_repo`/`get_world_entity_write_repo`/`get_rules_read_repo`。测试经 `app.dependency_overrides` 注 fake repo + schema-routing fake 网关(按 `req.output_schema` 返 WorldGenResult/CharacterGenResult/ContinuityReview)+ fake session(断 commit 计数)。
+- **`build_gateway_for_tier` 多 provider 接线(T5.4 follow-up,已完成)**:据 DB `tier_routing` 取该 tier 的 `provider:model` + `fallback`,为每个可建适配器的 provider 预备适配器,注入 `chain_resolver=chain_from_routing(...)` 启用回退链。无 DB 路由行 → 退回全局 `resolve_route`(单 provider,M1 兼容);无任何可用凭据 → 503。单 provider 配置 = 单元素链(行为不变)。
+- **`build_adapter` per-provider 接线(M5 R1 follow-up #2,2026-06-19,已完成)**:`_build_provider_adapter`(原 `_build_openai_compat_adapter`)现调 `ww_llm_gateway.build_adapter(provider, api_key=…, base_url=…)`(C1 扩 follow-up #2 工厂)替代「一律 `OpenAICompatAdapter`」——OpenAI 兼容 provider(deepseek/kimi/qwen/glm/openai)经 `_PROVIDER_BASE_URLS` 传 base_url;Anthropic/Gemini 传 `base_url=None` 走原生适配器。这样 `tier_routing` 链里配置的 Anthropic/Gemini provider 也能拿到**真实适配器**参与回退(不再被 base_url 缺失跳过)。未配凭据的 provider 返 None(回退链跳过)。**无 OpenAPI 形变**(served_by 不出 API)。真实 Anthropic/Gemini 跑通仍需 @devops 加 `anthropic`/`google-genai` SDK 依赖(适配器懒导入/注入客户端)。
+- **@frontend 行动**:T5.6 前 `pnpm gen:api` 纳入 world/characters generate + characters ingest + GET rules + GET skills。
+
+### C3 扩展(M5 R1 follow-up, 2026-06-19)· 设定库 Codex 读端点(GET characters / world_entities) owner @backend 状态: 稳定
+- 补 PROGRESS「Codex 读端点缺口」余项:设定库 Codex 现可展示跨会话**全量**已入库角色/世界观(前端先前只能「生成+本次入库会话内回显」)。snake_case;新增端点 → **@frontend 必须 `cd apps/web && pnpm gen:api`** 重生成 TS 客户端 + CodexPage 初始列表去掉「会话内」局限(见 gotcha 2026-06-19 @frontend Codex 缺口条)。
+- `GET /projects/{project_id}/characters` → **200** `CharacterListResponse{characters:[CharacterCardView]}`(已入库角色全量;复用 C5 读侧 `SqlCharacterRepo`,经 `get_memory_repos` 的 `memory.character.list_for_project`,**不动 assemble**)。无行 → 空列表(非 404)。
+- `GET /projects/{project_id}/world_entities` → **200** `WorldEntityListResponse{world_entities:[WorldEntityCardView]}`(已入库世界观实体全量;复用 C5 读侧 `SqlWorldEntityRepo`)。
+- **形变(DB JSONB→API,T5.2 ingest 形变的逆向)**:`characters.traits`/`speech_tics` dict `{"items":[...]}`→裸 list、`arc` dict `{"text":...}`→str、`tags`/`relations`→直落 list(复用既有 `_existing_characters` helper);`world_entities.rules` dict `{"rules":[...]}`→裸 list。
+- **路由**:挂既有 `generation.router`(prefix `/projects`);GET `/{project_id}/characters` 与 POST 同路径,FastAPI 按 method 区分,无冲突。注入缝复用 `get_memory_repos`(无新 dep)。测试经 `app.dependency_overrides[get_memory_repos]` 注 fake `MemoryRepos`。
+- **@frontend 行动**:`pnpm gen:api` 纳入 `GET /projects/:id/characters` + `GET /projects/:id/world_entities`;`lib/api/server` 加 `fetchCharacters`/`fetchWorldEntities` + CodexPage 初始列表拉全量。
+
+### C3 扩展(大纲读端点 follow-up, 2026-06-20)· `GET /projects/{id}/outline`(挂既有 `routers/outline.py`,已注册) owner @backend 状态: 稳定
+- 补缺口:大纲页先前**只有** `POST .../outline`(生成+持久化),无读端点 → 重访页面已落库的大纲不回显,看似「未保存」。仿 Codex 读端点(GET characters/world_entities)补对称读侧。
+- `GET /projects/{project_id}/outline`(tag `outline`)→ **200** `OutlineResponse{chapters:list[OutlineChapterView{no,volume,beats:list[str],foreshadow_windows:list[ForeshadowWindowView]}]}`(与 `POST .../outline` 响应**同形**,前端类型对齐),按 `chapter_no` 升序。项目存在但无大纲 → **200 空列表**(非 404);项目不存在 → **404 `NOT_FOUND`**(仿 POST 的项目存在性检查)。**只读不写库**。
+- **复用 C5 读侧**:扩 `OutlineRepo`(domain protocol) + `SqlOutlineRepo`(C5 assemble 读侧 repo) 加 `list_for_project(project_id)->list[OutlineView]`(仿 `CharacterRepo`/`WorldEntityRepo` 命名,order_by chapter_no);既有 `get(project_id, chapter_no)` 不动,assemble 行为不变。新注入缝 `get_outline_read_repo`(`services/project_deps.py`,仿 `get_rules_read_repo`)。
+- **beats 形变**:DB `outline.beats` JSONB dict `{"beats":[...]}` → 出参 `OutlineChapterView.beats` 解包成裸 `list[str]`(同 POST 路径 / `OutlineWriteView._to_view`)。
+- **@frontend 行动**:`pnpm gen:api` 纳入 `GET /projects/:id/outline`;大纲页初次加载拉已持久化大纲(去掉「生成后才显示」局限)。
+
+### C3 扩展(草稿读端点 follow-up, 2026-06-20)· `GET /projects/{id}/chapters/{no}/draft`(挂既有 `routers/projects.py`,已注册) owner @backend 状态: 稳定
+- 补缺口:写作工作台先前**只有** `POST .../draft`(SSE 流写) + `PUT .../draft`(自动保存) 无读端点 → 重访工作台时编辑器空白、看似「未保存」(其实 `chapters` 行在库)。仿 `GET .../outline` 补对称读侧。
+- `GET /projects/{project_id}/chapters/{chapter_no}/draft` → **200** `DraftView{project_id, chapter_no, volume, status, version, content, length}`(**比 PUT 的 `DraftResponse` 多 `content`**——读侧需正文重建编辑器;`length`=content 字符数派生)。无草稿行**或正文空白** → **404 `NOT_FOUND`**(工作台据此呈现空编辑器,与其它「缺资源」端点一致)。**只读不写库**。
+- **复用读 seam**:直接调既有 `chapter_repo.get_draft(project_id, chapter_no)->ChapterDraftView|None`(同续审 `_resolve_review_draft` 的读缝);`ChapterDraftView` 已含全部字段,无需新 repo 方法/新 view。多版本时固定返草稿版次(`DRAFT_VERSION=1`)那条可编辑工作副本,与 PUT 保存的同一行。
+- **@frontend 行动**:`pnpm gen:api` 纳入 `GET /projects/:id/chapters/:no/draft`(新 schema `DraftView`);工作台初次加载拉已保存草稿回灌编辑器(去掉「重访看似未保存」局限)。
+
+### C3 扩展(K1.3, 2026-06-19)· Kimi Code OAuth device-flow 端点(独立 `routers/kimi_oauth.py`,已注册) owner @backend 状态: 稳定
+- snake_case;新增 3 端点 + 3 schema → **@frontend 必须 `cd apps/web && pnpm gen:api`**(K1.4 前置)。**响应/job 结果/日志绝不含 access/refresh token**(只 user_code/connected/expires_at 等非密信息);token 仅以 Fernet 密文存 `provider_credentials.oauth_enc`。
+- `POST /settings/providers/kimi-code/oauth/start` → **202** `OAuthStartResponse{job_id:uuid, user_code:str, verification_uri:str, verification_uri_complete:str|None, expires_in:int, interval:int}`。流程:`start_device_authorization(http)` → `job_repo.create(None,"kimi_oauth")` + `session.commit()`(202 前持久化供轮询)→ `background_tasks.add_task(run_job, session_factory, job.id, _make_poll_work(device))`。后台 `work` 在 `run_job` 自建独立 session 上**循环 `poll_token`**(`authorization_pending`→续轮询、`slow_down`→增大 interval、`expired`/`denied`→抛 AppError 置 job failed);成功 → `upsert_oauth_credential`(加密包)+ job done(结果只 `{connected:true, provider}`)。前端展示 user_code + 打开 verification_uri + 轮询 `GET /jobs/{id}`。
+- `POST /settings/providers/kimi-code/oauth/disconnect` → **200** `OAuthDisconnectResponse{disconnected:bool}`(`delete_credential` 清 OAuth 凭据行)。
+- `GET /settings/providers/kimi-code/oauth/status` → **200** `OAuthStatusResponse{connected:bool, expires_at:str|None}`(解密 `oauth_enc` 取过期时刻;**无 token 本体**;解密失败/无凭据→`connected:false`)。
+- **store OAuth 读写**(`services/credentials.py`,C2 扩 K1.1 收口):`StoredCredential` 加 `auth_type:str`/`oauth_enc:bytes|None`,`api_key_enc` 改 `bytes|None`(K1.1 的 2 处 mypy red 已消)。新方法:`upsert_oauth_credential(owner,provider,oauth_enc)`(auth_type="oauth"+清 api_key_enc,read-modify-write 守可空 project_id 约束)、`delete_credential(owner,provider)->bool`。`upsert_credential`(api_key 路径)现显式 set auth_type="api_key"+清 oauth_enc。
+- **`_build_provider_adapter` Kimi Code OAuth 接线**(`services/project_deps.py`):provider `kimi-code` 或 `auth_type="oauth"` → `_resolve_kimi_code_token`:解密 `oauth_enc` → `needs_refresh`(剩余寿命 < 300s)则经临时 `httpx.AsyncClient` 调 `kimi_oauth.refresh` + `upsert_oauth_credential` 持久化新包 → 用(刷新后的)access token 调 `build_adapter("kimi-code", api_key=, base_url=…)`(K1.2 工厂构带伪造头 + coding base 的 `KimiCodeAdapter`)。无 `oauth_enc`→`LLM_UNAVAILABLE`。`_PROVIDER_BASE_URLS` 加 `"kimi-code": "https://api.kimi.com/coding/v1"`。
+- **服务缝**(`services/kimi_oauth.py`,注入 `AsyncHttpClient` Protocol = `httpx.AsyncClient.post` 子集,测试零联网):`start_device_authorization(http)->DeviceAuth`、`poll_token(http, device_code)->TokenSet`(一次尝试,`AuthorizationPending`/`SlowDown` 异常供调用方循环)、`refresh(http, refresh_token)->TokenSet`;`encrypt_oauth_bundle`/`decrypt_oauth_bundle`(Fernet 复用 `security/credentials` 的 `encrypt_api_key`/`decrypt_api_key`,工作在 JSON 串上);`needs_refresh(token)`。client_id env `KIMI_CLIENT_ID` 可覆盖默认 `17e5f671-...`;device authorization **带 `scope=kimi-code`**(coding entitlement;实测省略 scope→`api.kimi.com/coding/v1` 回 401,故重新带上。token 交换/刷新不带 scope)。
+- **@frontend 行动(K1.4)**:`pnpm gen:api` 纳入 OAuth start/disconnect/status + 3 schema;连接流 = start 拿 user_code + 开 verification_uri → 轮询 `GET /jobs/{job_id}` 到 done(已连接)/failed(过期/拒绝);status 端点查连接态;档位路由到 `kimi-code:kimi-for-coding`。
+
+## C8 · Skill registry + 表权限沙箱 owner @backend 状态: 稳定(T5.5, 2026-06-19;M5 R3 迁入 ww_skills 包, 2026-06-19)
+- 来源:`ARCHITECTURE.md §5.6` / `PRODUCT_SPEC §5.5`(声明式 Skill = AgentSpec;「声明式权限 + apply 层白名单」,**非进程沙箱**)。消费方:M5 编排/生成入库层、技能库 UI(T5.6,经 C3)。
+- **实现位置(M5 R3 已迁移)**:实现现落 `packages/skills/ww_skills/{skill_registry,skill_permissions}.py`(`packages/skills` 已是 uv workspace member)。`ww_skills/__init__.py` **直接导出**(不再是再导出 ww_core 的门面)。消费方 `from ww_skills import SkillRegistry/SqlSkillRepo/SkillRecord/SkillRepo/partition_writes/filter_reads/validate_declaration/KNOWN_TABLES`。**`ww_core.domain` 已删除这两个模块 + 其 `__init__` 导出**(不再经 ww_core.domain 暴露 skill 符号)。历史:T5.5 曾因 skills 非 workspace member 暂落 ww_core.domain(见 decisions),现已迁回。
+- **registry**:`SkillRecord{name,scope,tier:Tier,system_prompt,reads:list[str],writes:list[str],genre?}`(frozen,`skills` 行的声明式快照;input/output JSON Schema 暂不携带 Python 类型——纯声明执行)。`SkillRepo`(Protocol)`.list_all()->list[SkillRecord]`;`SqlSkillRepo(session)` 读 `skills` 表(裸 `tier` 非法 → VALIDATION)。`SkillRegistry.load(repo)`(classmethod,async)→ 每条跑 `validate_declaration`(越权声明 → **AppError VALIDATION**,加载中断)→ frozen `dict[name,AgentSpec]`;`get(name)->AgentSpec`(缺 → NOT_FOUND)、`names()->list[str]`、`list_scope(scope)->list[AgentSpec]`。不变量 #2:只带 `tier`,不解析 model(model 解析在网关)。
+- **表权限沙箱**(`skill_permissions.py`,纯函数,不可变):`KNOWN_TABLES`(10 创作表白名单:projects/characters/world_entities/outline/chapter_digests/foreshadow/style_fingerprint/rules/chapters/chapter_reviews;系统/运营表不在列)。`filter_reads(spec, available)->dict`(注入只喂声明 `reads` 的表);`partition_writes(spec, produced)->(allowed:dict, rejected:list[str])`(写库只应用声明 `writes`,越权表名进 rejected 供审计 log + 丢弃);`validate_declaration(spec)`(reads/writes 越 `KNOWN_TABLES` → VALIDATION)。不变量 #3:`allowed` 仍须过验收 gate 才真入库,本层只裁剪白名单、不开写后门。
+- 注入缝:`get_skill_registry`(`SkillRegistry.load(SqlSkillRepo(session))`,async dep)。T5.2 生成入库层应用 `partition_writes` 落自定义 skill 产出(仅声明 writes 表,越权审计),仍经验收。
+- **@llm 注意**:自定义 skill 经网关执行时复用 §5.1 机制(registry 给 spec,网关按 tier 路由);apply 层(编排/入库)须调 `filter_reads`/`partition_writes` 守白名单。
+
+## C3.5 · jobs 长任务基建(写/进度/回收 + run_job) owner @backend 状态: 稳定(T4.1, 2026-06-18)
+- 来源:ARCH §7.4(`jobs` 表 + `GET /jobs/:id` 轮询;BackgroundTasks 无专用队列)。`jobs` 表 + `Job` 模型 + `GET /jobs/{id}` 读端点已 T0.3 建齐;T4.1 补**写/进度/回收**层,供 **T4.3「学文风走 jobs」** 复用。
+- 位置:`packages/core/ww_core/domain/job_repo.py`(经 `ww_core.domain` 导出 `JobRepo`/`JobView`/`SqlJobRepo`)+ `apps/api/ww_api/services/job_runner.py`(`run_job`)+ `services/project_deps.get_job_repo` + lifespan reaper。
+- **`JobView`**(frozen Pydantic, snake_case):`id:uuid`、`kind:str`、`status:str`、`progress:int=0`、`result:dict|None=None`、`error:str|None=None`(镜像 `GET /jobs/{id}` 出参)。
+- **`JobRepo`** Protocol(写方法**只 flush** 不 commit,提交归 `run_job`/端点;唯 `reap_zombies` 自 commit):
+ - `async create(project_id: uuid|None, kind: str) -> JobView`(status=`queued`, progress=0)
+ - `async set_running(job_id) -> JobView`(status=`running`)
+ - `async set_progress(job_id, pct: int) -> JobView`(pct 夹取到 0..100)
+ - `async complete(job_id, result: dict) -> JobView`(status=`done`, progress=100, result=)
+ - `async fail(job_id, error: str) -> JobView`(status=`failed`, error=)
+ - `async get(job_id) -> JobView | None`
+ - `async reap_zombies() -> int`(bulk UPDATE `running`→`failed`(+error 文案),返回被改条数,**自 commit**)
+ - 状态写方法对不存在的 job 抛 `LookupError`。状态常量导出:`STATUS_QUEUED/RUNNING/DONE/FAILED`、`PROGRESS_COMPLETE=100`(`job_repo` 模块级)。
+- **`run_job` 缝**(`services/job_runner.py`):`async def run_job(session_factory: SessionFactory, job_id: uuid, work: JobWork, *, request_id: str|None=None, repo_factory=_default_repo_factory) -> None`,其中 `JobWork = Callable[[AsyncSession], Awaitable[dict]]`。语义:**自建独立 session**(`session_factory`,仿 `run_overdue_scan`,请求 session 已关闭)→ `set_running` → `await work(session)`(业务写与 job 状态写同一 session)→ `complete(job_id, work 返回值)` → `commit`;异常路在**全新** session 里 `fail(job_id, str(exc))` + `commit`,**异常被吞不冒泡**(后台任务边界)。`SessionFactory` 复用 `services/foreshadow_scan.SessionFactory`(= `get_session_factory`/`get_sessionmaker()`)。`repo_factory: Callable[[AsyncSession], JobLifecycleRepo]`(最小 Protocol:仅 `set_running`/`complete`/`fail`)是可注入缝,单测注 fake。
+- **lifespan reaper**(M4-d):`main.py` `_lifespan` 在 `seed_stub_user` 之后 `SqlJobRepo(session).reap_zombies()`(幂等、自 commit)——进程重启丢 BackgroundTask 的残留 `running` 行标 `failed`,用户可见可重试。
+- **T4.3 行动**:`POST /style` → `job_repo.create(project_id,"style_learn")` + commit 返 202 `{job_id}`;`background_tasks.add_task(run_job, session_factory, job.id, work=<部分应用 run_style_extraction→StyleFingerprintWriteRepo.append 并返回 {version,dims_count} 摘要>)`。`work` 内自建 gateway(analyst)+ repo(用 `run_job` 传入的独立 session)。注入缝 `get_job_repo`/`get_session_factory` 测试经 `app.dependency_overrides` override。
+
## C4 · 编排器接口(LangGraph 写章图) owner @llm 状态: 稳定(M1/T1.3, 2026-06-18;M2 扩四审/验收)
- 来源:`ARCHITECTURE.md §5.2`(图)/ §7.3(SSE)。**M1 仅单 `write` 节点**;并行四审/collect/interrupt(accept) 属 M2。
- 位置:`packages/core/ww_core/orchestrator/`。
@@ -82,16 +225,48 @@
- `normalize_review` SSE(向后兼容,`section`/`conflict` 不变)新增:`foreshadow{kind,code?,title,where?,note?}`(每条建议一条)、`pace{water:[{where,reason}],hook:bool,beat_map:[int]}`(一条)。键按字典序确定性遍历(continuitydict|None`(取 style 审 ok 结果整 dict);`collect_reviews` 末尾 `style=extract_style(reviews)` 传入 `review_repo.record(...)`(`chapter_reviews.style` JSONB 列已存在)。无指纹降级(score=100/空段)仍是 `ok` 结果、照常落库(区别于 incomplete→None)。
+- `normalize_review` SSE(向后兼容)新增:`style{score:int, segments:[{idx:int,score:int,label:str|None}]}`(一条)。键按字典序遍历 → continuityLlmRequest`(`output_schema=StyleFingerprintResult`,`stream=False`,system 缓存断点前块) + `async run_style_extraction(spec, *, samples_text, gateway, user_id, project_id)->StyleFingerprintResult`(`gateway.run().parsed`;**只读不写库**;网关失败**直接上抛**不隔离;parsed 违约抛 `ValueError`,仿 run_outline)。
+- 消费方:T4.3 端点 `POST /style` 经 BackgroundTask 调 `run_style_extraction` 拿 `StyleFingerprintResult` → 拆 `dimensions_json`/`evidence_json` 写 `style_fingerprint` 表;`POST .../refine` 调 `refiner_spec`(writer 网关) `run()` 重写段返回 `{original,refined}`。**@frontend 行动**:T4.4 前 `pnpm gen:api` 纳入 style/refine/jobs 端点。
+
+### C6 扩展(T5.1, 2026-06-19)· worldbuilder + character-gen spec/schema + 入库前 continuity 校验缝
+- 状态:**稳定**。位置:`packages/agents/ww_agents/{schemas.py,specs.py}`(经 `ww_agents` 导出);生成节点在 `packages/core/ww_core/orchestrator/generation_node.py`(经 orchestrator 导出)。**T5.2 入库端点据此构建**(field 名已对齐 `world_entities`/`characters` DB 列,T5.2 ingest 直接映射)。
+- **worldbuilder schema**:`WorldEntityCard{type:str, name:str, rules:list[str]=[]}`(rules = **显式硬规则清单**,供 continuity 引用)、`WorldGenResult{entities:list[WorldEntityCard]=[]}`。映射 `world_entities` 表:type/name 列 + rules 入 JSONB(DB 列 `rules` 是 JSONB;入库可包成 `{"rules":[...]}` 或裸 list,由 T5.2 定夺)。
+- **character-gen schema**:`CharacterRelation{name:str, kind:str, note:str|None=None}`、`CharacterCard{name:str, role:str, traits:list[str]=[], backstory:str, arc:str, speech_tics:list[str]=[], tags:list[str]=[], relations:list[CharacterRelation]=[]}`(**必填**:name/role/backstory/arc;其余集合默认空)、`CharacterGenResult{cards:list[CharacterCard]=[]}`。映射 `characters` 表:name/role/backstory 列 + traits/arc/speech_tics/tags/relations 入各自 JSONB(DB `traits`/`arc`/`speech_tics` 是 JSONB dict 列、`tags`/`relations` 是 JSONB list——T5.2 ingest 须把 `list[str]` 的 traits/speech_tics 包进 dict 或调整,按 DB 列形落库;`role` = 角色定位 主角/CP/对手/导师/工具人)。
+- **`worldbuilder_spec`**(frozen `AgentSpec`):`name="worldbuilder"`, `tier="writer"`, `output_schema=WorldGenResult`, `reads=["projects"]`, `writes=["world_entities"]`(声明式,真写库经 T5.2)。独立生成(仿 outliner),**不进 review 图**。system_prompt 强调硬规则显式可校验。
+- **`character_gen_spec`**:`name="character-gen"`, `tier="writer"`, `output_schema=CharacterGenResult`, `reads=["world_entities","characters"]`, `writes=["characters"]`。system_prompt **注入「已生成卡 + 已有角色」差异化要求(M5-a 防雷同)**——含「已有角色」「已生成卡」「防雷同」字样。
+- 生成节点缝(**模块内自有 `GatewayRun` Protocol**,不跨模块复用、不在 `__init__` 重复导出,见 gotcha;导出公共函数 + 三个 context builder):
+ - `async run_worldbuilder(spec, *, brief, project_context, gateway, user_id, project_id)->WorldGenResult`(+ `build_worldbuilder_context(*, brief, project_context)->str`)。
+ - `async run_character_gen(spec, *, brief, count:int, role:str|None, world_context:str, existing_chars:Sequence[CharacterCard], generated_so_far:Sequence[CharacterCard], gateway, user_id, project_id)->CharacterGenResult`(+ `build_character_gen_context(*, brief, count, role, world_context, existing_chars, generated_so_far)->str`——**防雷同上下文**:注入已有+已生成卡的 `name(role):traits` 简表,要求差异化;空时给「(暂无…)」占位不崩)。
+ - **入库前 continuity 校验缝(ARCH §6.5,T5.2 调)**:`async precheck_generated_cards(spec, *, cards:Sequence[CharacterCard], world_context:str, characters_context:str, gateway, user_id, project_id)->list[Conflict]`(+ `build_precheck_context(*, cards, world_context, characters_context)->str`)。**复用 `continuity_spec`**(analyst 档,只读)跑生成卡 vs 世界观/已有角色真相源比对、返回 `list[Conflict]`(C6 `Conflict` 五类)。**编排器追加的检查,非 character-gen 直接互调**(守 §5.4 数据流/不变量 #1)。T5.2 入库端点用返回的 conflicts 做 gate(有冲突→提示作者裁决/调整,不静默入库)。
+ - 三函数均:生成用 `run()` 非 `stream()`;`output_schema=spec.output_schema`;system 缓存断点前块(cache=True,#9);网关失败**直接上抛**(端点处理,独立生成不做失败隔离);parsed 违约抛 `ValueError`(仿 run_outline/run_style_extraction);**只读不写库**(#3)。
+- 测试替身:复用 `packages/core/tests/fakes_orchestrator.py` 的 `FakeRunGateway`/`SchemaRoutingRunGateway`/`FailingRunGateway`(按 `req.output_schema` 路由 parsed)。
+- 消费方:**T5.2 端点** `POST /world/generate` 调 `run_worldbuilder`、`POST /characters/generate` 调 `run_character_gen`(批量循环时把已产卡传 `generated_so_far` 防雷同)、`POST /characters`(入库) 前调 `precheck_generated_cards` gate;analyst 网关复用 `build_gateway_for_tier(session,store,"analyst")`,writer 用 `"writer"`。记账闭环同既有(端点末尾 commit)。
+
## C5 · 记忆服务 `assemble` / `select_relevant_entities` owner @backend 状态: 稳定(T1.2, 2026-06-18)
- 来源:`ARCHITECTURE.md §3.4 / §5.3`(确定性选择:显式+主角+近况;渲染卡片;缓存断点)。
- 位置:`packages/core/ww_core/memory/`(+ `domain/repositories.py`)。
- 输出(决策:中性文本,非 `LlmRequest`,见 decisions 2026-06-18):
- `AssembledContext{ stable_core:str, volatile:str, selection:SelectionTrace }`
- - `stable_core`:断点前(世界硬规则+定型主角(无 latest_state)+文风指纹+合并规则),已排序/无时间戳/无 UUID。
- - `volatile`:断点后(注入卡片(含 latest_state)+伏笔窗口+近况摘要+本章 beats)。
+ - `stable_core`:断点前(**作品蓝本(title/logline/premise/theme)**+世界硬规则+定型主角(无 latest_state)+文风指纹+合并规则),已排序/无时间戳/无 UUID。「作品蓝本」是书级 spec、定型 → 入缓存前缀首段。
+ - `volatile`:断点后(**写作指令「请创作第 {chapter_no} 章的正文。」始终在场**+注入卡片(含 latest_state)+伏笔窗口+近况摘要+本章 beats)。
+ - **空-prompt 保证(2026-06-19 修,bugfix)**:哪怕世界观/角色/大纲全空,只要项目有 premise/title,`stable_core`(含作品蓝本) 与 `volatile`(含写作指令) **均非空** → writer 的 user message 永不为空(修 LLM `400 "message at position 0 ... must not be empty"`)。
- `SelectionTrace{ selected:list[SelectedEntity] }`;`SelectedEntity{ kind:"character"|"world_entity", name:str, reasons:list[SelectionReason] }`;`SelectionReason="explicit_beat"|"main_character"|"recent_digest"|"foreshadow_window"`。
- 入口:`async assemble(repos:MemoryRepos, project_id, chapter_no, recent_k=5)->AssembledContext`;纯函数 `select_relevant_entities(*, outline, characters, world_entities, recent_digests)->SelectionTrace`;`render_cards(selection, characters, world_entities)->str`;`merge_rules(rules)->list[RuleView]`(global→genre→style→project)。
-- 依赖注入:`MemoryRepos` 捆绑 7 个 Protocol(Outline/Character/WorldEntity/Digest/Foreshadow/Style/Rules);单测注入内存 fake,运行时 `sql_memory_repos(AsyncSession)`(**T1.4 注入点**)。
+- 依赖注入:`MemoryRepos` 捆绑 **8** 个 Protocol(Outline/Character/WorldEntity/Digest/Foreshadow/Style/Rules/**Project**);单测注入内存 fake,运行时 `sql_memory_repos(AsyncSession)`(**T1.4 注入点**)。`ProjectSpecRepo.spec(project_id)->ProjectSpecView|None`(`ProjectSpecView{title, logline?, premise?, theme?}`,按 project_id 读 `projects` 行;2026-06-19 新增,**消费方须在 `MemoryRepos(...)` 补 `project=` 字段**)。
- 不变量:确定性选择(无向量 #6);统一 project_id 过滤(§3.5);latest_state 严格归 volatile(#9)。
- 消费方:**T1.3 write 节点** → 构造 `LlmRequest(system=[Block(stable_core, cache=True)], input=volatile)`。
diff --git a/memory/decisions.md b/memory/decisions.md
index a508d68..6fab380 100644
--- a/memory/decisions.md
+++ b/memory/decisions.md
@@ -14,6 +14,64 @@
---
+## [2026-06-19] Kimi Code OAuth:token 存储包 + 刷新启发式 + 省略 scope + 后台轮询走 jobs — @backend (K1.3)
+- 背景:Kimi 订阅 plan 走 device-flow OAuth;需定 token 持久化形、何时刷新、device authorization 是否带 scope、后台轮询如何挂。
+- 选择:
+ 1. **存储包**:`TokenSet{access_token, refresh_token, expires_at}` 序列化为 JSON 串经 Fernet(复用 `encrypt_api_key`,工作在 str 上)加密入 `provider_credentials.oauth_enc`(C2 扩 K1.1 列)。`expires_at` 存**服务端驱动的绝对 UTC 时刻**(从 `expires_in` 计算入库时刻)。明文 token 绝不进日志/响应/job 结果。
+ 2. **刷新启发式**:建网关时若 access token 剩余寿命 < `MIN_REFRESH_BUFFER_SECONDS=300`(300s 缓冲,对 ~15min access 足够),调 `refresh` 换新包并 `upsert_oauth_credential` 持久化(下次复用)。任务文档原提 `max(300, 0.5*expires_in)`——本实现采固定 300s 缓冲(不存原始 expires_in,KISS;对 15min access 等价于 0.5*expires_in=450s 略激进但安全)。
+ 3. **省略 scope**:device authorization 不带 `scope`(匹配研究确认的 kimi-cli v1.41.0+;server 拒绝时再加 `scope=kimi-code` 兜底——本期先不带)。client_id env `KIMI_CLIENT_ID` 可覆盖默认。
+ 4. **后台轮询走 jobs**:`POST .../start` 创建 `jobs(kind="kimi_oauth")` 返 202 + user_code,复用 K1.1/M4 `run_job` 在独立 session 上**循环** `poll_token`(每 interval 秒,`authorization_pending` 续轮询、`slow_down` 增大 interval、过期/拒绝抛 AppError 置 job failed),成功落加密 token + job done(结果只 `{connected, provider}`)。前端轮询 `GET /jobs/{id}`。
+- 理由 / 取舍:复用既有 jobs/run_job 基建(无新长任务机制);固定 300s 缓冲省去存 expires_in(YAGNI)。代价:refresh-on-build 每次建网关多一次解密 + 可能一次刷新网络调用(仅临近过期时)。
+- 影响:C3 扩(OAuth 3 端点);C2 扩 K1.1 的 `StoredCredential` 收口(api_key_enc→nullable + auth_type/oauth_enc);`_build_provider_adapter` 加 OAuth 分支。K1.4 前端连接流、K1.5 E2E(真账号联网验证含封号风险,用户自担)。
+
+## [2026-06-19] Kimi Code OAuth:device authorization **重新带 `scope=kimi-code`**(实测 401 后修正,超越上条第 3 点)— @backend (K1.3 fix)
+- 背景:K1.5 真账号联网实测——device-flow 登录成功拿到真 JWT,但调 `api.kimi.com/coding/v1` 被拒 `401 Invalid Authentication`。根因:device_authorization 省略了 `scope`,拿到的 token 缺 coding entitlement。
+- 选择:`start_device_authorization` 的请求体在 `client_id` 之外加 `scope=kimi-code`(模块常量 `KIMI_CODE_SCOPE`)。**修正上条决策第 3 点的「省略 scope」**——`scope=kimi-code` 是 coding-agent 文档化 OAuth scope(`ooojustin/opencode-kimi` `constants.ts` 发送之;kimi-cli v1.41.0 已不发但服务端仍接受)。
+- 范围:scope **只**放 device_authorization 请求;token 交换(`poll_token`)/刷新(`refresh`)**不带** scope(OAuth device flow 惯例,参考实现亦然)。
+- 影响:device-auth 请求体现为 `{"client_id": , "scope": "kimi-code"}`;K1.3 单测 `test_start_device_authorization_parses_response_and_sends_scope` 断 `data["scope"]=="kimi-code"`。无 OpenAPI 形变,前端无需 re-gen。
+
+## [2026-06-19] Skill registry + 表权限沙箱迁回 `ww_skills` 包(@devops 已纳入 workspace)— @backend (M5 R3)
+- 背景:T5.5 曾因 `packages/skills` 非 uv workspace member 把 `skill_registry`/`skill_permissions` 暂落 `ww_core.domain`、`ww_skills/__init__` 做再导出门面(见上 T5.5 决策)。@devops 现已把 `packages/skills` 做成真 workspace member(`ww_skills` 可 import、有自己的 pyproject、`uv sync` 完成)——前提消除。
+- 选择:把两个模块**物理迁入** `packages/skills/ww_skills/{skill_registry,skill_permissions}.py`(内部相对 import 从 `ww_core.domain.skill_permissions` 改为 `ww_skills.skill_permissions`),`ww_skills/__init__` 改为**直接导出**(不再 re-export ww_core);删除 `ww_core.domain` 的两个模块文件 + `__init__` 里的 skill 导出。所有 importer(`apps/api` `generation.py`/`project_deps.py` + `test_generation.py`)改 `from ww_skills import …`;skill 单测从 `packages/core/tests/` 移到 `packages/skills/tests/`(import 也改 `ww_skills`)。
+- 理由 / 取舍:恢复任务文档原定位(skill 逻辑归 skills 包),物理位置与 §5.6 措辞一致;ww_core 不再承载 skill 符号(关注点分离)。代价:`packages/skills/pyproject.toml` deps 需校准——`ww_skills` 直接 import `ww_db`/`ww_agents`/`ww_shared`/`ww_llm_gateway`(`Tier`),故去掉不再用的 `ww-core`、补 `ww-llm-gateway` + `sqlalchemy`(`SqlSkillRepo` 用 `select`/`AsyncSession`);改后 `uv sync` 重建 ww-skills。
+- 影响:C8 实现位置更新(contracts.md C8 状态加「M5 R3 迁入 ww_skills 包」);消费方 import 路径从 `ww_core.domain`/`ww_core.domain.skill_registry` → `ww_skills`。`ww_core.domain.skill_*` 导入路径**已失效**(凡 grep 到旧路径须改)。`packages/skills/pyproject.toml` 改了 deps(在 @backend 可编辑的 skills 目录内,但属打包元数据)——已 PROGRESS 通知 @devops 复核根锁。
+
+## [2026-06-19] `build_gateway_for_tier` 改用 `build_adapter` 工厂(per-provider 适配器)— @backend (M5 R1 #2)
+- 背景:T5.4 接线只为回退链里的每个 provider 一律建 `OpenAICompatAdapter`——Anthropic/Gemini 即便配进 `tier_routing` 链也因 `_PROVIDER_BASE_URLS` 无 base_url 被跳过、拿不到真实适配器。@llm 加了 `build_adapter(provider, *, api_key, base_url=None)` 工厂(C1 扩 follow-up #2)按 provider 选适配器类。
+- 选择:`_build_openai_compat_adapter` 改名 `_build_provider_adapter`,去掉「base_url 缺失即返 None」分支,改调 `build_adapter(provider, api_key=…, base_url=_PROVIDER_BASE_URLS.get(provider))`——OpenAI 兼容 provider 传 base_url,Anthropic/Gemini 传 None 走原生适配器。仅在凭据缺失时返 None(回退链跳过)。保留既有 503/凭据/单 provider 兼容行为。
+- 理由 / 取舍:最小改动让回退链支持非 OpenAI-兼容 provider;adapter 选择逻辑收敛到网关包的工厂(单一真源),apps/api 不再硬编码适配器类。代价:真实 Anthropic/Gemini 仍需 @devops 加 SDK 依赖(适配器懒导入)。
+- 影响:C3 扩(build_gateway_for_tier 接线段更新);`apps/api` 去掉 `AsyncOpenAI`/`OpenAICompatAdapter` 直接 import。T5.7 切 provider E2E 可扩 Anthropic/Gemini 路径。
+
+## [2026-06-19] 设定库 Codex 读端点复用 C5 读侧 + 反向 JSONB 解包 — @backend (M5 R1)
+- 背景:PROGRESS 余项①——Codex 设定库本应展示 characters/world_entities **读/管理**视图,但后端只有生成/入库写端点,前端只能「生成+本次入库会话内回显」。
+- 选择:加 `GET /projects/:id/characters` + `GET /projects/:id/world_entities`,**复用 C5 读侧** `SqlCharacterRepo`/`SqlWorldEntityRepo`(经既有 `get_memory_repos` dep,不新增 repo、不动 assemble);响应做 T5.2 ingest 形变的**逆向**解包(`{"items":[...]}`/`{"text":...}`/`{"rules":[...]}`→裸 list/str),复用既有 `_existing_characters` helper。无行返空列表(非 404,列表语义)。
+- 理由 / 取舍:KISS——读端点是写入形变的镜像,复用已有读侧 + helper 零新依赖;GET 与 POST 同路径靠 method 区分无冲突。代价:读侧 CharacterView 仍是裸 dict,端点层做解包(与写侧 repo 的包裹对称)。
+- 影响:C3 扩(两读端点);@frontend `pnpm gen:api` + CodexPage 初始列表拉全量(去掉「会话内」局限,解 gotcha 2026-06-19 @frontend Codex 缺口条)。
+
+## [2026-06-19] 角色入库 continuity gate:有冲突→409 CONFLICT_UNRESOLVED,作者 acknowledge 后放行 — @backend (T5.2)
+- 背景:ARCH §6.5 要求生成角色入库前过 continuity 校验(`precheck_generated_cards`),但任务文档留了两条路线(block / 返回冲突供作者裁决)。要选最简单正确、且守不变量 #3「无 AI 静默写库」。
+- 选择:入库端点跑 precheck;检出冲突且请求未带 `acknowledge_conflicts=true` → **409 `CONFLICT_UNRESOLVED`** + `details{conflicts:[...], conflict_count}`(不写库)。作者在前端查看冲突、裁决后重发带 `acknowledge_conflicts=true` → 放行写库。零冲突 → 直接写库 201。
+- 理由 / 取舍:复用既有 `CONFLICT_UNRESOLVED`(409) 码(语义=未决冲突禁写入,与 accept gate 一致),无需新增错误码;显式 `acknowledge` 标志=作者裁决的 HITL gate(不静默入库,守不变量 #3),比「自动返回冲突 + 另开裁决端点」更省(无新端点、无新状态表,原型 KISS)。代价:作者确认 = 重发一次请求(带 flag),可接受。
+- 影响:C3 扩(POST /characters);`partition_writes` 在 gate 后再过写白名单(character-gen 只声明 writes=characters)。T5.6 前端入库流:预览→点入库→若 409 展示冲突→裁决勾选→带 acknowledge 重发。T5.7 E2E 覆盖两条路径。
+
+## [2026-06-19] 角色卡 schema↔DB 形变落写侧 repo(list→{"items"}、arc str→{"text"}、rules→{"rules"})— @backend (T5.2)
+- 背景:T5.1 gotcha 指出 `CharacterCard.traits`/`speech_tics`(list)/`arc`(str) 与 DB `characters` JSONB **dict** 列不同形;`WorldEntityCard.rules`(list) 与 `world_entities.rules` JSONB dict 列不同形。需定一个固定包裹形。
+- 选择:写侧 repo 做转换层——`traits`/`speech_tics` 包成 `{"items":[...]}`、`arc` 包成 `{"text":...}`、`rules` 包成 `{"rules":[...]}`;`tags`/`relations` 是 JSONB list 直落。读回(喂防雷同/precheck)反向解包同形(`_existing_characters`/`_world_context`)。
+- 理由 / 取舍:固定 wrapper key 让 round-trip 确定(写=读互逆),不改 @db 列类型(@db owned)。代价:assemble/C5 读侧 `CharacterView.traits` 仍是裸 dict(本期 assemble 不消费 items 内层,无影响);若后续 assemble 要渲染 traits 文本,按同 wrapper 解包即可。
+- 影响:C3 扩(写侧 repo 形变契约);ww_core.domain 新增 `character_repo`/`world_entity_repo`。@db 若日后规整列类型为 list,可去 wrapper(迁移由届时 owner 执行)。
+
+## [2026-06-19] 顺带补 `GET /projects/:id/style` 读指纹 + 写侧 repo 独立读视图 — @backend (T4.3)
+- 背景:UX §6.9 要展示完整 16 维指纹 + 证据,但 ARCH §7.2 端点清单**无**读指纹端点(只 `POST /style`)。C5 读侧 `SqlStyleRepo.latest` 已存在,但其 `StyleView` **只含 `dimensions`**(assemble 只需维度文本,不需证据/版本),扩它会牵动 C5 assemble 契约。
+- 选择:(1) **加 `GET /projects/:id/style`** 返最新指纹 `{dimensions, evidence, version}`(无指纹→404 `NOT_FOUND`)。(2) 不动 C5 读侧——在写侧 `SqlStyleFingerprintWriteRepo` 上加一个 `latest(project_id)->StyleFingerprintView`(含 evidence_json + version 的更全视图),`GET /style` 用它;C5 `SqlStyleRepo.latest`/`StyleView`(只 dimensions,供 assemble stable_core)保持不变。
+- 理由 / 取舍:读写分离 + 各自最小视图(同 `DigestAppendRepo` vs 读侧 `DigestRepo` 先例),不破坏 C5 assemble;前端无须靠 job `result` 拼凑摘要、可直接拉完整指纹。
+- 影响:C3 扩(新增 `GET /style`);**待回写规格**:ARCH §7.2 端点清单 + PRODUCT_SPEC §6 应补 `GET /projects/:id/style`(spec 一致性纪律,@docs 回写)。T4.4 前端 FingerprintView 消费此端点。
+
+## [2026-06-19] 学文风 `work` 自建网关+repo(不走 FastAPI dep),凭据探测前移到请求阶段 — @backend (T4.3)
+- 背景:学文风走 jobs(M4-c):`POST /style` 立即返 202,提取经 BackgroundTask `run_job` 在**独立 session** 上跑(请求 session 已关闭)。但「无凭据→503」需在请求阶段就让用户知道(不能 202 后再静默 fail job)。
+- 选择:(1) `work(session)` 闭包内**自建** analyst 网关(`build_gateway_for_tier(session, SqlCredentialStore(session), "analyst")`)+ 写侧 repo(用 `run_job` 传入的独立 session),不依赖 FastAPI dep(dep 绑的是请求 session)。(2) 端点仍声明 `get_style_extract_gateway`(analyst) 依赖**仅作凭据探测**——无凭据时 dep 解析阶段抛 503,在 `job_repo.create` 之前拦下;其返回的网关本身不被复用(请求 session 即将关闭)。
+- 理由 / 取舍:兼顾「BackgroundTask 必须自建 session」(gotcha)与「无凭据请求阶段即 503」(友好、不写注定失败的 job)。代价:凭据被探测两次(请求阶段 + 后台 work),原型可接受。
+- 影响:C3 扩(POST /style);测试后台路径 monkeypatch `style.build_gateway_for_tier`/`style.SqlStyleFingerprintWriteRepo` + `job_runner.SqlJobRepo`(见 gotcha)。
+
## [2026-06-18] 网关结构化输出经可注入 `StructuredClient` 缝(instructor)— @llm
- 背景:`OpenAICompatAdapter` 需接 instructor 产结构化输出(C1 类型预留 `output_schema`/`parsed` 但 M1 未接线),但测试绝不可联网/碰真实 LLM。
- 选择:定义 `StructuredClient` Protocol(`create_with_completion(*, messages, response_model, **kw) -> (parsed, raw)`,即 `instructor.AsyncInstructor` 的形);adapter 构造可选注入 `structured_client`,未注入时懒构建 `instructor.from_openai(self._client)`。`run()` 透传 `result.parsed → LlmResponse.parsed`。
@@ -78,3 +136,9 @@
- 选择:CLAUDE.md 只保留跨文档、易错的**规则**(含 9 条架构不变量);所有枚举型/具体值改为指向 ARCHITECTURE/PRODUCT_SPEC 对应 §(唯一真源)。新增"冲突裁决"段:ARCHITECTURE > UX_SPEC/DEV_PLAN > PRODUCT_SPEC,矛盾时回写上游。
- 理由 / 取舍:消除文档间漂移面、降低每 session 上下文成本;代价是查具体值需多跳一层到 ARCHITECTURE(可接受)。
- 影响:编辑 CLAUDE.md 的任何 agent 遵循此纪律——契约/枚举值落在 spec 与 `memory/contracts.md`,不在 CLAUDE.md。
+
+## [2026-06-19] T5.5 Skill registry + 表权限沙箱落 `ww_core.domain`,非 `packages/skills/ww_skills` — @backend
+- 背景:T5.5 任务文档把 registry/沙箱定位在 `packages/skills/ww_skills/`,但该包当前**不是 uv workspace member**(根 `pyproject.toml` 的 `[tool.uv.workspace].members` 不含它、mypy_path/ruff src 也不含),无 pyproject → 仅靠 sys.path hack 才能 import,app/测试/mypy 都无法干净消费它。根 pyproject 由 @devops 持有,自己接线会越权 + 阻塞。
+- 选择:把实现落在已是 workspace member、且承载「编排/写库 apply 层」的 `ww_core.domain`(`skill_registry.py` + `skill_permissions.py`,经 `ww_core.domain` 导出)。`ARCHITECTURE §5.6` 明确「强制点在编排/写库层」=ww_core,语义上也吻合。`packages/skills/ww_skills/__init__.py` 留**再导出门面**(import 自 ww_core),保留稳定「skills」导入名。
+- 理由 / 取舍:非阻塞、门禁可干净覆盖(ruff/mypy/pytest 全跑)、不越权改根配置;代价是物理位置与任务文档措辞略偏,但功能/契约(C8)完整。待 @devops 把 `packages/skills` 纳入 workspace + mypy_path 后,可平滑迁移实现到 `ww_skills` 而不动调用方(只改门面方向)。
+- 影响:消费方 import `ww_core.domain` 的 `SkillRegistry`/`filter_reads`/`partition_writes`/`validate_declaration`(或经 `ww_skills` 门面)。若 @devops 后续纳入 workspace,迁移由届时的 @backend/@llm 执行。
diff --git a/memory/gotchas.md b/memory/gotchas.md
index c9c83ec..1154a9b 100644
--- a/memory/gotchas.md
+++ b/memory/gotchas.md
@@ -7,6 +7,33 @@
---
+- [2026-06-20] @frontend **页面重访回显已存内容用 RSC 读 helper(`lib/api/server.ts`)作初值种入 client 组件,404/错误降级、绝不阻塞进页**(大纲/写作重载):先前大纲页 `initialChapters=[]`、工作台 `useState("")` → 重访空白虽然库里有数据。范式:① **大纲**:`fetchOutline(projectId)` 包 `getJson` 取 `chapters??[]`,**try/catch 整体降级为 `[]`**(项目存在无大纲后端返 200 空列表,但任何错误也不该让大纲页 500);`useOutline(initial)` 已据 `initial.length>0` 置 `status="ready"`,传非空即回显,生成流照常覆盖。② **草稿**:`fetchDraft` 直接复用既有 `getJsonOrNull`(**404→null**,后端无草稿行正是 404),页面 `draft?.content ?? ""` 作 `Workbench` 的 `initialText`。③ **不要把初值塞进 stream 的 reducer**——`Workbench` 用 `useState(initialText)` 起步即可:流式那条 `useEffect` 起始 `stream.state.text` 为空、仅当 `phase∈{streaming,done,aborted}` 且文本变化才 `setText`,所以初值不会被空流反扑,SSE+AbortController+PUT 自动保存全不用动。④ 测纯加载/降级逻辑:`lib/api/server.test.ts` 用 `vi.stubGlobal("fetch", …new Response(JSON, {status}))` + `afterEach(vi.unstubAllGlobals)`(node 环境够用,`server.ts` 只依赖 `process.env`/`fetch`),断 200 解包、空/错误→`[]`、404→null。
+
+- [2026-06-19] @qa **Kimi OAuth E2E 要 token 真落 pg:override `get_session_factory`→`e2e_sm`、但**不能** monkeypatch `kimi_oauth.SqlCredentialStore`(K1.3 单测那样会让落库走内存 fake,验不到真 pg)**(K1.5):与 K1.3 单测(FakeSession/FakeStore/FakeJobRepo,验逻辑)不同,E2E 要验真持久化——让后台 `work` 自建的 `SqlCredentialStore(session)` + `run_job` 默认 `SqlJobRepo` **保持真**,只 override `get_session_factory`→`e2e_sm`(后台 work 用真 session 写真 pg)。仍须:monkeypatch **模块级** `routers.kimi_oauth._default_http_client`→同一 scripted fake(后台 work 自建 http 非 dep)+ `app.dependency_overrides[kimi_oauth._default_http_client]`→同一 fake(端点 device-auth 走 dep;call[0]=device-auth、call[1..]=token 轮询顺序共享)+ monkeypatch `routers.kimi_oauth.asyncio.sleep`→no-op。断「加密非明文」:`_ACCESS_TOKEN.encode() not in row.oauth_enc`(Fernet 密文里找不到明文字节)+ `decrypt_oauth_bundle` 回环。`Gateway` 无公开 adapters accessor → 用 `gateway._adapters[provider]`;`KimiCodeAdapter._client` 读 `.base_url`/`.default_headers`/`.api_key`(同 K1.2 单测)。refresh-on-build 直测 `_build_provider_adapter`(非经 HTTP,更清晰):字符串 target monkeypatch `"ww_api.services.project_deps.httpx.AsyncClient"` + `setattr(project_deps,"kimi_refresh",fake)`。清理:删 `provider_credentials`(kimi-code) + `jobs`(kind=kimi_oauth) + `tier_routing`(project_id 为 NULL 的全局行)。ruff E501 按**显示宽度**算(中文字符占 2 列)——含中文注释/docstring 的行别贴边 100。
+
+- [2026-06-19] @frontend **Kimi Code OAuth 三个端点无 params/body → openapi-fetch 用 `api.POST(PATH, {})` / `api.GET(PATH)`;路径用 `as const` 字面量常量复用**(K1.4):`POST .../oauth/start`、`.../oauth/disconnect`、`GET .../oauth/status` 的生成 schema 全是 `path?: never; ...; requestBody?: never`——`api.POST(START, {})`(传空 init obj,typecheck 绿)、`api.GET("/settings/providers/kimi-code/oauth/status")`(无第二参)。把 `start`/`disconnect` 路径抽成模块级 `const START = "/settings/...start"` 给 `api.POST` 时,**字符串字面量类型仍被 openapi-fetch 推断为合法 path key**(无需 `as const`,但抽常量去重 DRY)。② 连接流复用 `useJobPoll`(M4)零改:`connect` 拿 `data.job_id` 调 `poll.poll(job_id)`;轮询终态用 `useEffect([poll.status])` + `startedRef` 守门(同 `useStyleLearn` 先例,`initialPollState.status` 默认 `"polling"` 不能进页即据此显进度)。③ kimi_oauth job 完成态 result 只 `{connected, provider}`(**无 token**)——`jobConnected(job)=job.result?.["connected"]===true`,**别**期待/解析任何 token 字段。④ user_code a11y:用 `