Compare commits

...
34 Commits
Author SHA1 Message Date
gozeros da6521b6cb sync: complete remaining files (source dhk-1) 2026-10-21 15:14:06 +08:00
gozeros 42f2a26fa8 sync update from LAN repo (source dhk-1 @ 3e87be55) 2026-10-21 15:06:08 +08:00
董宏凯 ab6068a7c1 Merge branch 'dhk-1' of http://192.168.0.200/knowledge-base-bj/deerflow-code into dhk-1 2026-09-09 22:29:54 +08:00
董宏凯 5ab3d7f254 代码修改 2026-09-09 22:29:47 +08:00
董宏凯 30e1c2dd90 代码合并 2026-09-09 19:49:38 +08:00
董宏凯 673adef7df 代码提交 2026-09-09 12:48:06 +08:00
董宏凯 6f7919ded2 Merge branch 'dhk-1' of http://192.168.0.200/knowledge-base-bj/deerflow-code into dhk-1 2026-09-08 14:41:55 +08:00
董宏凯 014d754707 代码提交 2026-09-08 14:33:03 +08:00
董宏凯 8859c13cfb 代码合并 2026-09-08 09:40:31 +08:00
董宏凯 b0970de677 代码提交 2026-09-07 18:38:51 +08:00
董宏凯andCursor 53cac5f81a Merge branch 'master' into dhk-1
Resolve conflicts by accepting master version for:
- workspace-nav-chat-list.tsx
- ResearchSidebar.tsx

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 17:55:32 +08:00
董宏凯 4f7f07504a Merge branch 'master-nj' into 'master'
fix(buttons): 行动分析宿主路由支持切换任务id/行动id

See merge request knowledge-base-bj/deerflow-code!39
2026-09-07 09:53:09 +00:00
董宏凯 1244fb1f2f 代码提交 2026-09-07 17:30:53 +08:00
吉鑫升 3ca5528640 feat(knowledge): support standalone file ingestion and configurable wiki entry
Use neutral knowledge-service labels and sanitize user-facing task errors.
2026-09-07 17:07:27 +08:00
董宏凯 250db4de04 fix(buttons): 行动分析宿主路由支持切换任务id/行动id
- TaskButtonsPage: 宿主路由追加任务id时,行动分析(xdfx) tab 下增加 idKind 下拉框可选任务id/行动id,与外部链接类型一致
- runTaskButton: host 分支按 idKind 取 ctx.actionId 或 ctx.taskId(行动id缺失回退任务id),与 url 分支同规则
- AGENTS.md: 同步更新 idKind 适用范围说明(url/host 均适用)
2026-09-07 15:56:50 +08:00
董宏凯 f9b6d7e322 Merge branch 'master-nj' into 'master'
feat: 新增报告协作页,并强化 Deep Research 运行诊断与报告兜底

See merge request knowledge-base-bj/deerflow-code!38
2026-09-07 06:58:42 +00:00
董宏凯 6edb6fa7aa 本地模式线上区分-v1 2026-09-07 14:57:17 +08:00
董宏凯 cf9fe58905 Merge branch 'master' of http://192.168.0.200/knowledge-base-bj/deerflow-code into master-nj
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	frontend-web/src/components/workspace/workspace-nav-chat-list.tsx
#	frontend-web/src/pages/WorkspaceRoutes.tsx
#	offline-backend-20260512/backend/app/gateway/app.py
#	offline-backend-20260512/backend/app/gateway/deps.py
#	offline-backend-20260512/backend/pyproject.toml
2026-09-07 14:46:36 +08:00
董宏凯 56bb4f141d 代码提交 2026-09-06 22:17:53 +08:00
董宏凯 75f288b241 代码提交 2026-09-06 20:49:35 +08:00
董宏凯 bf42a48fcc 代码提交 2026-09-06 20:25:14 +08:00
董宏凯andCursor 5e953c9fcb feat(report-collaboration): complete RC-BE-015–018 backend, eval suite, and MySQL sync
Ship report rewrite/version APIs, security budget and audit persistence, AgentScope worker wiring, and the 20-task quality eval harness so the frontend can integrate against a stable contract once enabled in config.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-06 17:32:15 +08:00
董宏凯 814a44f0b9 代码修改 2026-09-05 11:28:25 +08:00
董宏凯 b80433f091 chore: 同步仓库执行说明与约束文档
功能说明:\n- 更新仓库根目录 AGENTS.md 为当前约定和功能说明口径\n- 主要是开发约束与架构说明的同步,供后续提交与协作流程对齐
2026-09-04 23:18:59 +08:00
董宏凯 5c75e1af16 feat(deep-research): 强化任务链路可靠性并接入 agentfx 报告助手种子
功能说明:\n- 重构 Deep Research 调度与执行链,补齐 dispatcher、executor、live hub 与依赖注入入口,提升任务下发、执行、回调的稳定性\n- 增强运行时持久化与恢复能力,接入业务映射、任务来源与任务事件日志模型,覆盖持久化与执行可靠性关键路径测试\n- 新增 agentfx 报告助手 seed 与任务报告导入脚本配置,支持离线环境快速初始化与任务流模板回填\n- 完善 deep-research 与 thread_runs 路由参数编排,兼容不同业务映射与运行入口的前后端联动\n- 补充前端 agentfx deep-link 解析,保证 deep-research 场景下的登录跳转与任务上下文一致
2026-09-04 23:18:52 +08:00
董宏凯 438772f0b7 feat(frontend): 上线报告协作与多轮 RAG 消息聚合体验
功能说明:\n- 新增报告协作页(Report Collaboration)核心状态层、消息适配与会话面板,支持按节点/阶段选择和协作上下文展示\n- 补齐 report-collaboration API 与类型定义,并接入页面路由/构建路径,形成可运行的协作链路\n- 重构多轮 RAG 与 write/artefact 消息分组逻辑,优化 AgentChat 与 DeepResearch 场景下的消息展示顺序与可读性\n- 新增/更新 DeepResearch 前端页面(Workbench、报告配置、沙箱联动)以承接报告生成与复用能力\n- 增加报表协作相关单测,覆盖事件归并、消息适配与关键 reducer 行为
2026-09-04 23:18:27 +08:00
吉鑫升 dea4697ea0 feat: improve knowledge sync and entity alignment 2026-09-04 18:43:12 +08:00
g zy 7dbac9cc56 Merge branch 'gzy_browser' into 'master'
fix

See merge request knowledge-base-bj/deerflow-code!37
2026-09-04 03:21:48 +00:00
董宏凯 e58da3fbdf 代码提交 2026-09-03 20:06:54 +08:00
吉鑫升 60e2c72514 feat: preserve Wiki vectors across knowledge transfers and add local BGE-M3
Unify Wiki reading, preserve metadata and graph links, add asynchronous file-backed transfers, and reuse compatible vectors. Includes regression coverage and live acceptance notes.
2026-09-03 19:21:10 +08:00
董宏凯 1eae02f782 代码提交 2026-09-03 17:45:06 +08:00
董宏凯andCursor db333a4c33 feat: 重构多轮RAG展示并完善报告协作、业务链审核与 agentfx 种子
抽出专用多轮RAG消息列表并升级会话模型;强化报告协作画布与检查器;业务链普通用户改为提交审核;扩展 agentfx 报告技能种子,并将含 admin 的用户名自动提权为管理员。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 17:34:16 +08:00
董宏凯andCursor f109a95535 feat: 新增报告协作页,并强化 Deep Research 运行诊断与报告兜底
支持浏览器侧素材快照撰写、拒绝报告读时修复、Mermaid PNG 导出与 Word 导出加固。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 11:16:50 +08:00
董宏凯andCursor 33dea724a1 Hide workspace 岗位会商 nav entry and enable AI writing notebook space entries.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-10 19:01:24 +08:00
1758 changed files with 290861 additions and 14381 deletions

No files matched your search

+75 -13
View File
@@ -71,6 +71,7 @@ offline-backend-20260512/backend/.deer-flow/data/*.db-*
offline-backend-20260512/backend/.deer-flow/*
offline-backend-20260512/backend/.env
offline-backend-20260512/backend/.env.local
offline-backend-20260512/backend/.extensions_config.json.lock
offline-backend-20260512/start_backend.sh
offline-backend-20260512/stop_backend.sh
offline-backend-20260512/check_backend.sh
@@ -88,19 +89,52 @@ skills/
hermes-agent-main/
.gitignore
offline-backend-20260512/backend/tests/*
# Keep the openGauss URL/dialect/schema compatibility regression coverage versioned.
!offline-backend-20260512/backend/tests/test_gauss_database.py
!offline-backend-20260512/backend/tests/test_large_file_safety.py
# Seeder-owned Agent Skills are product source; keep them out of the broad
# runtime `skills/` ignore above.
!offline-backend-20260512/backend/app/gateway/routers/_*_seed_assets/skills/
!offline-backend-20260512/backend/app/gateway/routers/_*_seed_assets/skills/**
# Keep startup seed completeness and task-report conversion regressions versioned.
!offline-backend-20260512/backend/tests/test_seed_assets.py
!offline-backend-20260512/backend/tests/test_latency_diagnosis_seed.py
!offline-backend-20260512/backend/tests/test_task_report_convert.py
!offline-backend-20260512/backend/tests/test_task_deeplink_details.py
!offline-backend-20260512/backend/tests/test_offline_docs.py
!offline-backend-20260512/backend/tests/test_migration_compatibility.py
# Keep the dedicated taskId + goPath history Gateway contract versioned.
!offline-backend-20260512/backend/tests/test_task_deeplink_threads_api.py
# Keep project-scoped cross-conversation memory regressions versioned.
!offline-backend-20260512/backend/tests/test_analysis_project_memory.py
!offline-backend-20260512/backend/tests/test_analysis_project_memory_integration.py
!offline-backend-20260512/backend/tests/test_analysis_project_memory_runtime.py
!offline-backend-20260512/backend/tests/test_analysis_project_memory_store.py
# Keep answer-level feedback persistence, scoring, resolver, and migration regressions versioned.
!offline-backend-20260512/backend/tests/test_answer_feedback.py
# Keep wx common-agent category configuration regressions versioned.
!offline-backend-20260512/backend/tests/test_wx_common_agents.py
# Keep the built-in public-document seed regressions versioned.
!offline-backend-20260512/backend/tests/test_gongwen_agent_seed.py
!offline-backend-20260512/backend/tests/test_gongwen_documents.py
# Keep the openGauss URL/dialect/schema compatibility regression coverage versioned.
!offline-backend-20260512/backend/tests/test_gauss_database.py
!offline-backend-20260512/backend/tests/test_llmwiki_weknora.py
!offline-backend-20260512/backend/tests/test_llmwiki_knowledge_management.py
!offline-backend-20260512/backend/tests/test_llmwiki_advanced_retrieval.py
!offline-backend-20260512/backend/tests/test_knowledge_live_embedding.py
!offline-backend-20260512/backend/tests/test_health_auth_bypass.py
# Keep formal Word OOXML and embedded-font regression coverage versioned.
!offline-backend-20260512/backend/tests/test_word_export.py
# Keep generated-file library aggregation and filtering regression coverage versioned.
!offline-backend-20260512/backend/tests/test_artifact_library.py
# Keep generated-file library aggregation and filtering regression coverage versioned.
!offline-backend-20260512/backend/tests/test_artifact_library.py
# Keep Web App site artifact (multi-page site) regression coverage versioned.
!offline-backend-20260512/backend/tests/test_web_app_sites.py
# Keep the model configuration regression coverage versioned even though
# this deployment repository otherwise ignores newly added backend tests.
!offline-backend-20260512/backend/tests/test_model_management.py
# Keep the forced-research agent runtime gate and sandbox-policy regressions versioned.
!offline-backend-20260512/backend/tests/test_forced_research_agent.py
# Keep strategy-selection built-in agent seed and safety-contract regressions versioned.
!offline-backend-20260512/backend/tests/test_strategy_selection_seed.py
# Keep fixed-question exact-match and streaming regression coverage versioned.
!offline-backend-20260512/backend/tests/test_fixed_questions.py
# Keep configurable es_query system-prompt routing regression coverage versioned.
@@ -109,38 +143,55 @@ offline-backend-20260512/backend/tests/*
!offline-backend-20260512/backend/tests/test_llmwiki_local_index_repository.py
!offline-backend-20260512/backend/tests/test_llmwiki_vector_search.py
!offline-backend-20260512/backend/tests/test_llmwiki_wiki_sync.py
!offline-backend-20260512/backend/tests/test_llmwiki_incremental_sync_contract.py
!offline-backend-20260512/backend/tests/test_llmwiki_encoder_runtime.py
!offline-backend-20260512/backend/tests/test_external_llmwiki_search.py
!offline-backend-20260512/backend/tests/test_llmwiki_wiki_retrieval.py
!offline-backend-20260512/backend/tests/test_public_llmwiki_embed.py
# Keep assistant-source snapshot publication atomic across re-import jobs.
!offline-backend-20260512/backend/tests/test_assistant_snapshot_pointer_contract.py
# Deep-research reliability regression coverage.
!offline-backend-20260512/backend/tests/test_deep_research_persistence.py
!offline-backend-20260512/backend/tests/test_deep_research_event_sink.py
!offline-backend-20260512/backend/tests/test_deep_research_executor_reliability.py
!offline-backend-20260512/backend/tests/test_deep_research_start_job.py
!offline-backend-20260512/backend/tests/test_summarization_human_message.py
!offline-backend-20260512/backend/tests/test_llmwiki_title_ranking.py
!offline-backend-20260512/backend/tests/test_menu_overrides.py
!offline-backend-20260512/backend/tests/test_compact_menu_overrides.py
# Keep database-backed upload-policy, enforcement and bounded-preview regressions versioned.
!offline-backend-20260512/backend/tests/test_upload_policy.py
!offline-backend-20260512/backend/tests/test_uploads_policy_enforcement.py
!offline-backend-20260512/backend/tests/test_artifact_preview.py
# Independent enterprise-research persistence, HTTP and race regressions.
!offline-backend-20260512/backend/tests/test_enterprise_research_tasks.py
# Position-roundtable per-turn write/present cap (weak-model duplicate artifacts).
!offline-backend-20260512/backend/tests/test_position_artifact_cap.py
# Roundtable coordinator/seat capability isolation regressions.
!offline-backend-20260512/backend/tests/test_roundtable_role_boundary.py
# Workflow Studio: schemas / definitions / engine / run-lifecycle regressions
!offline-backend-20260512/backend/tests/test_position_artifact_cap.py
# Roundtable coordinator/seat capability isolation regressions.
!offline-backend-20260512/backend/tests/test_roundtable_role_boundary.py
# Workflow Studio: schemas / definitions / engine / run-lifecycle regressions
# plus the concurrency remediation suite (atomic resume, draft CAS, publish
# counter, event seq) — required in CI per docs/WORKFLOW_STUDIO_BACKEND_REMEDIATION_ZH.md P1-06.
!offline-backend-20260512/backend/tests/test_workflow_schemas.py
!offline-backend-20260512/backend/tests/test_workflow_definitions.py
!offline-backend-20260512/backend/tests/test_workflow_engine.py
!offline-backend-20260512/backend/tests/test_workflow_runs.py
!offline-backend-20260512/backend/tests/test_workflow_concurrency.py
!offline-backend-20260512/backend/tests/test_workflow_schema_rebuild.py
# Writing-mode first-report pipeline enforcement regressions.
!offline-backend-20260512/backend/tests/test_workflow_engine.py
!offline-backend-20260512/backend/tests/test_workflow_runs.py
!offline-backend-20260512/backend/tests/test_workflow_concurrency.py
!offline-backend-20260512/backend/tests/test_workflow_schema_rebuild.py
# Writing-mode first-report pipeline enforcement regressions.
!offline-backend-20260512/backend/tests/test_writing_mode_report_middleware.py
!offline-backend-20260512/backend/tests/test_writing_rewrite_prompt.py
# LLM call timeout retry classification regression coverage.
!offline-backend-20260512/backend/tests/test_llm_error_handling_middleware.py
# Adaptive context recovery, large-payload and real-adapter streaming regressions.
!offline-backend-20260512/backend/tests/test_context_recovery.py
!offline-backend-20260512/backend/tests/test_context_recovery_transport.py
!offline-backend-20260512/backend/tests/test_summarization_multiturn.py
# Writing-mode pipeline conversation-material regressions.
!offline-backend-20260512/backend/tests/test_deep_research_report_tool.py
# Report collaboration (RC-BE-000–018) regression coverage.
!offline-backend-20260512/backend/tests/test_report_collaboration_*.py
!offline-backend-20260512/backend/tests/test_ensure_orm_columns_sync.py
deploy
.history
@@ -172,9 +223,20 @@ docs/backups
# Frontend skills components must be tracked (excluded from the broad skills/ ignore above)
!frontend-web/src/components/workspace/skills/
# Built-in analysis mind-map skill is product source, not a per-user runtime skill.
!offline-backend-20260512/backend/skills/
offline-backend-20260512/backend/skills/*
!offline-backend-20260512/backend/skills/public/
offline-backend-20260512/backend/skills/public/*
!offline-backend-20260512/backend/skills/public/analysis-mindmap/
!offline-backend-20260512/backend/skills/public/analysis-mindmap/SKILL.md
# uv lockfile (not tracked per project preference)
offline-backend-20260512/backend/uv.lock
!offline-backend-20260512/backend/tests/test_wiki_package_roundtrip.py
!offline-backend-20260512/backend/tests/test_llmwiki_incremental_sync_scheduler.py
# Local-only scratch directory (not committed)
0622/
@@ -0,0 +1,113 @@
# Web App 产物模式:多页面站点 / 流程图 / 地图(可降级 + 独立页面风格)
## 总体架构
让智能体在沙箱里产出**完整多页面站点**(site.json 清单 + src/pages + public 数据),平台统一构建(预置 node_modules,永不 npm install)与受控静态托管,前端新注册一个**风格独立**的「站点预览」路由页。
**两条铁律(本次新增的核心原则)**:
1. **零侵入**:新页面不复用现有工作台风格/布局组件,走独立路由 + 独立样式表;对已有代码的修改仅限「一个带开关的入口按钮」。
2. **可降级**:沙箱/模板/node/构建链任何一环部署失败,Web App 功能整体退回"不可用"状态并优雅提示,智能体回退到现有单页 HTML 模式,**现有功能零影响**。
```
智能体(技能 web-app)
0. 检查可用性:/mnt/web-app-template 缺失 → 回退现有单页 HTML 模式(行为与今天完全一致)
1. scaffold → outputs/my-site/(排除 node_modules/dist)
2. 写 src/pages/*.tsx、public/data.geojson、编辑 site.json
3. build_site.py build → 软链/junction 预置 node_modules → vite build → dist/(失败自修复 ≤3 次)
4. present_files site.json
平台(Gateway,全部能力探测 + 开关保护)
GET /api/web-app/capability # 可用性探测(配置/模板/node_modules/node 四项检查)
GET /api/threads/{tid}/sites # 站点列表 + 构建状态
POST /api/threads/{tid}/sites/{name}/build # 触发构建
GET /api/threads/{tid}/sites/{name}/build-status
POST /api/threads/{tid}/sites/{name}/preview-url → 签发短时效签名 URL
GET /api/public/site-preview/{token}/{path} # 静态托管 dist/
前端(独立路由 + 独立风格)
#/sites/:threadId/:siteName # SiteStudioPage:全新视觉风格,无现有 chrome
产物面板 site.json →「打开站点预览」按钮(仅 capability 可用时渲染)
```
已确认决策:混合构建(技能自修复 + 平台按钮兜底);预览工作台先行;GeoJSON 优先 + 可配置底图。
---
## 一、页面风格:独立设计系统(不用现有风格)
新页面 `SiteStudioPage` 做成**深色"预览工作室"**风格,视觉上与现有工作台完全区隔:
- **独立路由**:直接注册在 `App.tsx` 顶层(`/share/:shareCode`、`/login` 同款先例),**不进** `PageLayout`/`WorkspaceLayout`/`Header`/`PageSidebar`——零现有 chrome 继承,零布局耦合。
- **独立样式**:新建 `src/pages/site-studio/site-studio.css`(自建 design tokens:深空灰底 `#0f1117` 系、靛蓝/青渐变强调色、玻璃拟态面板、细腻边框与阴影、系统字体栈),所有类名 `site-studio-` 前缀,**只被该页面 import**,不碰 tailwind 配置、全局 CSS、主题 provider。
- **布局**:
- 顶部悬浮工具栏:站点标题 + 构建状态胶囊(成功/构建中/失败三态,带呼吸灯动画)+ 设备切换分段控件(桌面/平板/手机)+ 刷新/重新构建/新窗口打开;
- 左侧窄导航栏:site.json 的页面列表(图标 + 标题,点击设 iframe hash 切页),可折叠成纯图标模式;
- 中央:**浏览器壳**造型(圆角 + 顶部仿 URL 地址条 + 红黄绿窗控点缀)内嵌 iframe,设备切换时宽度平滑过渡动画;
- 底部可折叠**构建控制台**(等宽字体日志、错误红色高亮、构建时长);
- 加载/失败/未启用态都有设计过的插画式空状态(纯 CSS/SVG,不引组件库新依赖)。
- 登录守卫:轻量检查 stored auth,缺失跳 `#/login`(复用既有跳转工具,不动鉴权体系)。
## 二、降级与故障隔离矩阵(部署失败不影响已有功能)
| 故障点 | 行为 | 对现有功能影响 |
|---|---|---|
| config `web_app.enabled: false`(默认关) | capability 返回不可用;入口按钮不渲染;技能第 0 步探测失败即回退单页 HTML | 无 |
| 模板目录/node_modules 未预置(发版遗漏) | 同上:capability 四项检查(配置/模板/依赖/node 二进制)逐项报告原因 | 无 |
| sandbox.mounts 加了模板挂载但目录缺失 | 已核实 `local_sandbox_provider.py:81-90` 对不存在宿主路径**静默跳过仅告警**;Aio provider 同样按"存在才挂"实现 | 无(沿用既有容错语义) |
| vite build 失败/超时 | 子进程隔离(`asyncio.to_thread` + 超时强杀 + 每站点并发锁),错误收进 JSON;预览页显示日志;智能体读报错自修复 | 无(不进 gateway 请求主链路) |
| 签名 URL 过期/伪造 | 403 JSON,页面自动重签 | 无 |
| 前端新页面任何异常 | 独立路由独立样式,最坏就是该页白屏,error boundary 兜底 | 无 |
| 产物面板入口按钮 | 唯一改动点:capability 请求失败/不可用 → 按钮不渲染,**原 UI 一字不变** | 无 |
| 新 router 注册 | 纯增量 include_router,异常全部在路由内转结构化响应 | 无 |
技能回退语义(关键):SKILL.md 第 0 步强制探测 `ls /mnt/web-app-template/scripts/build_site.py`——探测失败时明确指示"改用现有方式产出单个 xxx.html",保证部署不完整时智能体产物质量与今天一致。
## P1 站点模板(`offline-backend-20260512/backend/templates/web-app-template/`,源码入库,node_modules gitignore 由发版仓库预置)
- Vite + React + react-router-dom(**HashRouter**:静态托管路径下无需服务端 fallback)+ **纯 CSS design tokens**(不用 Tailwind v4,规避 oklch 在 chrome100 老内核失效)+ build target es2018。
- 依赖:react 系、@xyflow/react、maplibre-gl、echarts、mermaid、pmtiles、vite/@vitejs/plugin-react(lockfile 锁死版本)。
- 内置组件:`AppShell`(读 site.json 导航)、`FlowCanvas`(xyflow 流程图封装,自动分层布局)、`GeoMap`(**默认无底图模式**:背景 + GeoJSON 点线面离线开箱即用;site.json 可配 tilesUrl/PMTiles)、`ChartPanel`(echarts)、`MermaidBlock`(静态图)。
- `router.tsx` 用 `import.meta.glob('../pages/*.tsx')` 自动注册路由——智能体丢页面文件即生效,零手动接线。
- `site.json`:`{ title, description, nav[], map:{tilesUrl?, tilesType?, pmtiles?}, theme? }`。
- Makefile 加 `make webapp-template`(开发机一次性 pnpm install);发版仓库构建脚本负责镜像内预置 node_modules(文档写明)。
- config.yaml `sandbox.mounts` 增模板只读挂载(`templates/web-app-template → /mnt/web-app-template`,缺失自动跳过已验证);AioSandboxProvider `_get_thread_mounts` 小扩展:同样"存在才挂"地补读 config 自定义 mounts。
## P2 构建脚本 + 技能包(`skills/public/web-app/`)
- `SKILL.md`:触发词(多页面网站/站点/web app/带流程图或地图的网页);工作流 = ①可用性探测(失败即回退单页 HTML)②scaffold ③配置 site.json ④写页面 ⑤数据放 public/ ⑥build(失败读 JSON 报错修复重试 ≤3)⑦present_files site.json ⑧引导点「站点预览」;硬约束:不手写 dist、一律 `/mnt/user-data/...` 虚拟路径。
- `scripts/build_site.py`(纯标准库):`check`(能力探测 JSON)/ `scaffold`(copytree 忽略 node_modules、dist)/ `build`(定位 node → 软链 node_modules 进站点:Linux symlink、Windows `mklink /J` junction 免管理员 → `node vite.js build` → 结构化 JSON 输出,180s 超时)/ `status`。
- 模板定位顺序:env `DEERFLOW_WEBAPP_TEMPLATE` → `/mnt/web-app-template`(沙箱内)→ 后端相对路径(宿主侧)。
## P3 后端:纯引擎 + 路由(全部能力开关保护)
- `packages/harness/deerflow/webapp/`(纯引擎,不 import app.*,对齐 `address_scan.py` 先例):站点发现(outputs/* 下 site.json + dist 状态)、构建执行(子进程 + 锁 + 超时)、HMAC token(`tid|site|exp`,密钥首用生成持久化 `{base_dir}/.web-app-secret` 0600)。
- `app/gateway/routers/sites.py`(app.py 注册):
- `GET /api/web-app/capability`:enabled + 模板 + node_modules + node 四项检查,逐项给原因(前端与技能共用);
- 列表/build/build-status/preview-url:`@require_permission("threads", "read", owner_check=True)`,能力不可用时返回结构化 `{"available": false, "reason"}` 而非 5xx;
- `GET /api/public/site-preview/{token}/{path}`:验签 + 过期 → `aresolve_thread_virtual_path` 且**限定站点 dist/ 根**(复用遍历防护)→ 正确 MIME(.js/.mjs→text/javascript、.geojson→application/json、.wasm)→ **HTML inline** + `CSP: default-src 'self' data: blob:; worker-src 'self' blob:`(maplibre worker 需要)+ `X-Content-Type-Options: nosniff` + `Cache-Control: no-cache` + ETag(mtime)。
- config.yaml + `deerflow/config/configuration.py` 加 `web_app:`(enabled **默认 false**、build_timeout、token_ttl、template_dir、node 覆盖)——与 `workflows` 同款总开关模式。
- 测试 `tests/test_web_app_sites.py`:capability 降级路径、token 签发/过期/伪造、路径穿越拒绝、MIME、站点发现(构建子进程 mock)。
## P4 前端
- `src/core/sites/api.ts`:capability/sites/build/status/mintPreviewUrl + TanStack Query hooks(照 `light-apps.ts` 模式)。
- `src/pages/site-studio/SiteStudioPage.tsx` + `site-studio.css` + `App.tsx` 注册顶层路由 `/sites/:threadId/:siteName`(独立风格见第一节;iframe `sandbox="allow-scripts allow-forms allow-popups"` 无 allow-same-origin,延续既有安全契约;token 过期自动重签;设备切换;重新构建 → 轮询 build-status → cache-bust 刷新)。
- `artifact-file-detail.tsx` **唯一既有文件改动**:产物路径以 site.json 结尾且 capability 可用时渲染「打开站点预览」按钮(navigate 到新路由);capability 请求失败/超时一律不渲染,原 UI 不变。
- token URL 构造等纯函数补单测;`pnpm typecheck` 通过。
## P5 测试与文档
- 后端 `make test` + ruff;前端 typecheck + 单测;`build_site.py` 本机真实跑通 scaffold→build→静态服务器自测 dist。
- 文档:AGENTS.md 增「Web App 产物」小节(含降级矩阵);发版文档补 node_modules 预置说明。
## 安全边界
短时效签名 URL(默认 2h)只覆盖该站点 dist/ 静态读;dist/ 根限定 + 复用 `aresolve_thread_virtual_path` 遍历防护;HTML inline 仅在 site-preview 路径开放(不影响 artifacts 接口既有强制 attachment);CSP + nosniff + iframe sandbox 三重纵深。
## 二期(本期不做)
源码编辑 + 保存即重建;跨对话站点 gallery;PMTiles 底图资产打包;`visualizations/` 内联图表(进行中,互补不冲突)的底图配置统一(其 map-renderer.tsx 硬编码 Google 瓦片属离线隐患,另行处理)。
## 风险
- Windows 开发机 junction 链接免管理员,node 前端机必有;生产镜像已含 nodejs+npm。
- Aio provider 自定义 mounts 扩展是小改动,按"存在才挂"实现即可,不影响现有挂载。
- dist/ 出现在产物目录扫描(文件噪音)——一期保持现状,二期优化。
+7 -3
View File
@@ -90,21 +90,25 @@ The frontend (`frontend-web/`) is a React 19 + Vite SPA:
- **Pages**: `ChatPage` (thread chat), `AgentChatPage` (custom agent chat), `AgentsPage`/`NewAgentPage` (agent management), `ScheduledTasksPage`/`ScheduledTaskRunDetailPage` (scheduled tasks), `LightAppManagementPage`/`LightAppListPage`/`LightAppIframeView` (轻应用中心 — register/list/embed mini-apps)
- **AI 写作·对话版(对话驱动写作台)**: a **parallel new entry** beside the classic AI-writing page (classic page/routes untouched) — route `/page/canvas/ai-writing-chat`, sidebar 「问答管理 → 课题研究(对话版)」. Page `open-canvas/pages/AIWritingChatPage.tsx` reuses the engine (`AIWritingContext` + `useStream` + `AIWritingDraftPanel`); **idle keeps the classic setup layout** (`WritingSetupChat` + collapsible form) and switches to the chat workbench once writing starts; left panel `components/ai-writing-chat/WritingChatPanel.tsx` = **unified chronological feed** (`ChatFlowTimeline.tsx`, chat bubbles anchor-interleaved into the progress timeline so triggered events render below the user's message) + button-only intervention cards (`ChatInterventionCards.tsx`, inline textareas removed) + bottom unified smart composer. Intent resolution: frontend fast path + `POST /api/ai-writing/sessions/{id}/intent` → `deerflow/agents/ai_writing/intent_router.py` (per-pause action whitelist, high-cost confidence gates, payload cleaning); supports QA bypass, clarify follow-ups, control intents, and material-stage auto re-search + pending-intent resume. Tests `tests/test_ai_writing_intent.py`; plan doc `frontend-web/docs/ai-writing-对话驱动写作台-实现方案.md`.
- **轻应用中心 (Light App Center)**: a sibling of 任务管理 in the sidebar with two pages — 应用管理 (admin registers `window_open` or `iframe` mini-apps with a route-param builder: login params resolved from `localStorage.userInfo`, theme params literal) and 应用列表 (card grid of `window_open` apps, `window.open` launch). `iframe` apps are mounted under a chosen parent menu (XK/PG/TY/综合管理) and **dynamically injected into the sidebar** by `core/page-layout/use-sidebar-menu.ts` (`useSidebarMenuItems`), opening in the `LightAppIframeView` host. Client + React Query hooks in `strategy-components/api/light-apps.ts`; URL builder/constants in `strategy-components/lib/light-app.ts`. Backend: `/api/light-apps` (global/shared, admin-gated writes).
- **菜单管理 (Menu Management, admin-only)**: an **overlay** over the static sidebar menu — `MenuManagementPage` (`pages/MenuManagementPage.tsx`, route `/page/strategy/admin/menu-management`, opened from the admin-only 「菜单管理」 item in the **bottom-left settings dropdown** of `components/page-sidebar.tsx`). Admins rename / 停用·启用 / reorder (↑↓ or @dnd-kit drag) / re-parent (horizontal drag **or** the edit dialog's 「移动到」parent selector) any node across 一级/二级/三级, **including injected iframe light-app nodes** (`light-app-<appId>`). The static tree (`core/page-layout/sidebar-menu.ts`) stays the source of icons/routes; only per-node deltas (`label`/`disabled`/`parentId`/`sortOrder`) persist to the backend. Merge logic in `core/page-layout/menu-overrides.ts` (`applyMenuOverrides`, applied in `use-sidebar-menu.ts` after light-app injection, before role filter; disabling a node hides its whole subtree) — strict **3-level cap** (`wouldExceedDepth`). Client `strategy-components/api/menu-overrides.ts`. Backend: `/api/menu-overrides` (global/shared; open read, admin atomic replace-all). **简介模式** uses a separate overlay: every compact workspace menu item can be placed in `sidebar`(左侧菜单)、`settings`(设置和更多)or `toolbox`(右侧工具箱); `CompactToolbox` (`components/workspace/compact-toolbox.tsx`, mounted by `PageRoutes.tsx`) opens only when the square right-edge trigger is clicked, then slides a compact, content-height right-side drawer over a backdrop above global dropdowns; configured items render top-to-bottom as centered square cards with a large icon above the label while the effective layout is compact. `/api/compact-menu-overrides` stores two independent sets (type `A` / `B`); each website picks one via `runtime-config.js` `VITE_COMPACT_MENU_TYPE`.
- **菜单管理 (Menu Management, admin defaults + personal layouts)**: `MenuManagementPage` (`pages/MenuManagementPage.tsx`) is available from「设置和更多」and the global Header account dropdown. Administrators edit the shared default menu; ordinary users edit only their own full/compact menu snapshot. For users with a saved snapshot, known node ids keep the personal `label`/`disabled`/`parentId`/`sortOrder` (or compact placement/order), while administrator defaults for node ids absent from that snapshot flow through—so newly-added code menus and iframe light apps follow the current default without overwriting older personal choices. Temporarily absent dynamic nodes are preserved when an unrelated personal edit is saved;「恢复默认」deletes only that user's snapshot. The static tree (`core/page-layout/sidebar-menu.ts`) remains the source of icons/routes and administrator defaults remain sparse per-node deltas. Merge/snapshot helpers live in `core/page-layout/menu-overrides.ts` and `compact-menu-overrides.ts`; full-menu writes carry the complete effective parent hierarchy for cycle/strict three-level validation while persisting only the sparse admin delta or full personal snapshot. Backend `/api/menu-overrides` uses `menu_overrides` (default) + `user_menu_override_configs` (per-user snapshot); `/api/compact-menu-overrides` uses `compact_menu_overrides` (type `A` / `B` defaults) + `user_compact_menu_override_configs` (per-user/type snapshots). Each website selects its compact type via `runtime-config.js` `VITE_COMPACT_MENU_TYPE`; `CompactToolbox` renders `toolbox` items.
- **舆情分析智能体(前端虚拟智能体)**: 智能体管理页新增「舆情分析」类型 tab,固定一张虚拟智能体卡片(名称/简介可编辑,存 localStorage)。对话页 `pages/SentimentAgentChatPage.tsx`(路由 `agents/sentiment-analysis/chats/:thread_id`)复用普通问答布局 + `MessageList`,**输入框只有发送按钮**;问答经 Gateway `POST /api/sentiment-agent/stream` 转发外部 AG-UI 网关(不经过 LangGraph),会话仅存 sessionStorage。接口地址 + Basic Auth 在 `runtime-config.js` 配置(`VITE_SENTIMENT_AGENT_API_URL` / `_AUTH_USERNAME` / `_AUTH_PASSWORD`)并随请求传给后台;后台按传入地址转发、关闭 TLS 校验,失败时把完整错误回传。核心模块 `src/core/sentiment-agent/`。
- **简洁模式用户引导 (Compact-mode onboarding tour)**: 简洁模式(外观设置的「全量/简洁」= compact,无全局导航栏 + 页面侧边栏)新增 TDesign React `Guide` 组件驱动的分步引导。组件 `components/workspace/compact-mode-guide.tsx`(挂在 `ChatPage`):9 步 —— 欢迎弹窗(dialog 模式)→ 左侧功能导航 → 聊天记录 → 设置和更多 → 换肤 → 界面模式(切回全量)→ 常用提示词栏 → 提示词管理弹窗 → 输入框;高亮目标由各组件上的 `data-compact-guide` 标记定位(`workspace-nav-chat-list` / `recent-chat-list` / `workspace-nav-menu` / `appearance-controls` / `custom-prompt-menu.tsx` 的 `CustomPromptBar`(提示词栏,仅新对话落地态渲染)与其 `ManagePromptsDialog`(`prompt-dialog` 锚点)/ ChatPage 输入框容器),**启动时按目标是否存在过滤步骤**(如新用户无聊天记录、侧栏收起时自动剔除该步 —— Guide 对缺失元素会抛错,不能盲传)。**提示词管理弹窗步骤是动态目标**:弹窗平时不渲染,引导切到该步前先广播 `PROMPT_DIALOG_GUIDE_EVENT`(`custom-prompt-menu.tsx` 导出)让 `CustomPromptBar` 打开 `ManagePromptsDialog` 并**接管**(`owner: true` 期间 ESC/点遮罩不关闭,防止高亮目标消失导致 Guide 报错),轮询锚点真正挂载后才切步;离开该步 / 跳过 / 完成 / 中途卸载时以 `owner: false` 收回弹窗,4 秒打不开则跳过该步防卡死。**TDesign Guide × Radix 模态互操作**:Radix 模态弹窗打开时会把 `body` 置 `pointer-events:none`(滚动锁定),Guide 直接挂在 body 下的遮罩/高亮/弹层会一并继承失活(引导按钮点不动),`styles/tdesign-theme.css` 末尾对 `.t-guide__overlay/__highlight/__popup/__wrapper` 显式 `pointer-events:auto` 兜底。触发:① 自动 —— 简洁模式新对话落地页自动演示,直到用户显式点「完成」或「跳过」才写 localStorage `deerflow.compact-guide-finished` 记忆(浏览器级);**中途刷新/离开不记忆,下次进入还会再演示**。② 手动 —— 「设置和更多」下拉新增「界面使用引导」(该下拉仅存在于简洁模式工作区侧栏 footer):已在问答页时广播 `compact-guide:start` 窗口事件直接启动,否则写 sessionStorage 请求并导航 `/chats/new` 由挂载的引导消费(消费在定时器真正启动时才执行,规避 StrictMode effect 双跑吞请求)。`eligible` 排除 embed=1 / hideChrome=1 / chats_iframe / FORCE_COLLAPSED_SIDEBAR(无界)等同样无全局 chrome 的特殊环境;启动延迟 800ms 等侧栏宽度过渡稳定后再测量高亮框。
- **智能体使用引导 (Agent onboarding tour, 三段式)**: 智能体「浏览管理 → 创建与配置 → 发起问答」全流程分步引导,组件 `components/workspace/agents/agent-guide.tsx`(同一 TDesign `Guide` 封装模式,与简洁模式引导共享记忆语义:**仅显式点「完成/跳过」才写 localStorage** `deerflow.agent-guide.<stage>.finished`,中途刷新/离开不记忆)。**自动演示仅 create / chat 两段**(进智能体列表页不弹引导,点「创建智能体」进入新建页时才自动演示;list 段的 `AgentGuide` 传 `autoStart={false}`,只能由头部「使用引导」按钮手动触发)。三段各自挂载、各自记忆:① `list`(`agent-gallery.tsx`,/page/workspace/agents)——分区 Tabs、搜索排序、智能体卡片操作、右上「创建智能体」按钮;**完成/跳过就地结束(不跳转创建页,与 create 段无联动)**,最后一步仅提示进入创建页后会有配置引导;② `create`(`NewAgentPage.tsx`,/page/workspace/agents/new)——左侧对话式创建输入区、基本信息、运行模型+入库模板名、知识空间、SOUL.md、技能穿梭框、保存按钮(9 步);③ `chat`(`AgentChatPage.tsx`,agents/:id/chats)——左栏切换与历史、常用问题 chips、输入框(落地态/会话态两个挂点同一 `chat-input` 标记)、右侧详情面板;`eligible` 限定普通智能体对话页(`showAgentList && !isEmbedded && !taskCtxOn`,即排除通用问答原地嵌入与任务深链工作区),三段共用 `isAgentGuideEnvironment()` 排除 embed/hideChrome/无界环境。高亮锚点 `data-agent-guide`:列表页 `tabs`/`search`/`agent-card`(卡片+列表两种布局的根元素)/`new-agent-btn`,创建页 `create-chat-input`/`create-basic`/`create-model`/`create-knowledge`/`create-soul`/`create-skills`/`create-save-btn`,对话页 `chat-sidebar`/`chat-questions`/`chat-input`/`chat-detail`;同样**启动时按目标存在性过滤步骤**(新用户无卡片/无预设问题时自动剔除)。手动重放:三个页面头部各有「使用引导」按钮(列表页创建按钮左侧、创建页 header 右侧、对话页 header),广播 `agent-guide:start:<stage>` 窗口事件强制重放(忽略已看过记忆)。
- **敏感词管理 (Sensitive Word Management)**: configures the **展示层脱敏映射** (原始词 → 替换词). **Gated on the username `lqq`** (not the admin role) — `getAccountDisplayName().toLowerCase() === "lqq"` (username = email `@` prefix, else `user_id`, same as the profile page); the dropdown entry, the route guard, and the page-level guard all use this check. `src/lib/desensitize.ts` ships a frozen seed map `SEED_DESENSITIZE_MAP` (the deployment seed) plus a live mutable `DESENSITIZE_MAP` (initialized from the seed so `desensitize()` works before any fetch). `DesensitizeWordsLoader` (mounted in `App.tsx` `AppProviders`) fetches `/api/sensitive-words` on startup and, if non-empty, calls `replaceDesensitizeMap(...)` to make the backend config the source of truth (empty/failed → keep seed). Page `SensitiveWordsPage.tsx` (route `/page/strategy/admin/sensitive-words`, opened from the 「敏感词管理」 item in the **bottom-left settings dropdown** of `components/page-sidebar.tsx`, present in **both** full + collapsed sidebar modes) lists/adds/edits/启停/deletes words and saves the **whole set atomically** (replace-all); a 「导入种子词」 button merges any missing `SEED_DESENSITIZE_MAP` entries into the editor. Client + hooks in `strategy-components/api/sensitive-words.ts` (`wordsToMap` folds enabled rows into the runtime map). Backend: `/api/sensitive-words` (global/shared; open read, write allowed for admin **or** the `lqq` account), mirroring 菜单管理's store shape. **侧边栏 + 导航栏 menu labels are desensitized** at the data source: `useSidebarMenuItems`/`useActiveThirdLevel` (`core/page-layout/use-sidebar-menu.ts`) map every node's `label` through a desensitizer built **directly from the `/api/sensitive-words` query** (`makeDesensitizer(wordsToMap(words))`, falling back to the global seed `desensitize` while empty) — this sidesteps the `replaceDesensitizeMap` effect-timing race. Only `label` is rewritten; `path`/`id`/`icon` are untouched so routing/highlight are unaffected. All nav surfaces (`page-sidebar.tsx`, `page-sidebar-dark.tsx`, `page-sidebar-v1.tsx`, `strategy-components/Header.tsx`, `PageRoutes.tsx`) consume this hook and are covered; `MenuManagementPage` uses the raw tree so admins still edit real labels.
- **技能上下线 (Skill Availability, owner/admin)**: the global `Skill.enabled` flag is the online/offline switch. Taking a skill offline persists `skills.<name>.enabled=false` in `extensions_config.json` but does **not** delete its directory or remove it from existing agent/workflow configurations; the row remains visible and bringing it online restores those references. Administrators may toggle every skill from 「技能管理」; ordinary users may toggle only their own non-built-in `custom` skills from 「技能 → 技能列表 → 我的」. Offline skills must be absent from every model-facing skill catalogue (`lead` prompt, `search_skills`, `skill_list`, `skill_view`, subagents, AI writing/deep research/roundtable and Workflow run-start validation), from agent skill pickers, and from direct sandbox access: `read_file`/`ls`/`glob`/`grep`/`bash` reject offline, unknown, root-wide and category-wide `/mnt/skills` paths. The backend mutation is `PUT /api/skills/{name}`, authorizes either an administrator or the persisted owner of the target custom skill, patches the raw document so MCP placeholders and unrelated extension fields survive, uses atomic replacement when supported plus a Docker single-file-bind fallback, and refreshes the skill prompt cache. Launchers keep the runtime extensions file in a writable persisted directory. Existing agent/strategy-agent references stay visible as 「已下线」 and may be removed but not newly selected.
- **技能地址管理 (Skill Address Management, admin-only)**: 扫描运行中的技能(`skills/{public,custom}` 下所有 `.md`/`.py`)里的 **http(s) URL 与 IP/IP:端口**,按唯一地址聚合全部出现位置(跨 .md/.py),支持**批量替换**(如把 UAT 地址一次性改成生产)。主体抽成可复用组件 `SkillAddressesPanel`(`pages/SkillAddressesPage.tsx` 导出,默认导出 `SkillAddressesPage` 仍保留旧路由 `/page/strategy/admin/skill-addresses` 作直链回退),**作为「技能地址管理」标签页嵌入管理员「技能管理」页 `pages/AdminSkillsPage.tsx`**(该页加 Tabs:「技能列表」+「技能地址管理」;入口在「设置和更多」下拉的「技能管理」`workspace-nav-menu.tsx` → `/page/workspace/admin/skills`,不再单列于 `components/page-sidebar.tsx` 左下角设置下拉):打开自动扫描并带**进度条**(扫描逐技能 / 应用逐文件 X/N,应对 150+ 技能),URL/IP 类型过滤 + 搜索 + 「片段批量套用」(如所有 `uat.4.cn` → `prod.4.cn`)、每地址「替换为」输入框(可展开看 `file:line` 片段)、内联**预览**、**应用**、**多关键词检索**(空格分隔全部命中),以及**修改记录**面板——每次应用生成一条记录(显示时间/改了哪些地址 from→to/文件数/处数),可**针对单次记录回退**(二次确认)。client + NDJSON 流式解析在 `strategy-components/api/skill-addresses.ts`。后端 `/api/skill-addresses`(**仅管理员**):所有文件 IO 走 `asyncio.to_thread` 不阻塞事件循环;`GET /scan`(+`/scan/stream`)、`POST /preview`、`POST /apply`(+`/apply/stream`)、`GET /backups`、`POST /rollback/{id}`;**子串安全替换**(基于 token span,不会误伤子串)、`expected_total` 乐观锁(409)、写前自动磁盘备份到 `{skills_root}/.address-edits/`(同时写一份 `{id}.manifest.json` 记录本次替换明细,供修改记录展示)、应用后刷新技能系统提示缓存。纯引擎 `deerflow/skills/address_scan.py`,router `app/gateway/routers/skill_addresses.py`,tests `tests/test_skill_addresses.py`。范围仅运行中技能,不动 `deploy/minimal/skills/`。
- **按钮管理 (Task Button Management, admin-only)**: configures the **可配置跳转按钮** rendered in the left task panel of the task deep-link workspaces (rwfx / 3qfx / xdfx, see `TaskFxWorkspace`). Each button = `{business, label, linkType, target, appendTaskId, idParam, idKind, enabled, sortOrder}`; `linkType` is `url` (外链 `window.open`, optional append of `?{idParam}={id}` — **`idParam` is the editable query-param name**, e.g. `id`/`task_id`), `business` (内部切换到另一业务, reuses `resolveTaskDeeplinkTarget`, always task id), or `purpose` (弹出当前任务的「任务目的」表格 via the shared `TaskPurposeDialog`, no target — configurable for any page incl. xdfx). **`host`(宿主路由)** asks the embedding parent to navigate: `runTaskButton` sends `window.parent.postMessage({ type:'HOST_NAVIGATE', path, queryParams:{ [idParam]: Number(taskId) } }, hostOrigin)` — `target` is the host route, `appendTaskId`/`idParam` become numeric `queryParams` (task id only), origin from `runtime-config.js` `VITE_HOST_NAVIGATE_ORIGIN` (else ancestorOrigins/referrer; never `*`). **`idKind` (`task`/`action`) only matters for xdfx**: its deep-link `?taskId=` is an 行动id and the 任务id is resolved at login (`fetchTaskIdFromAction`) — `TaskContext` now also stashes the raw `actionId`, so a url button can append either (default `task`). Page `TaskButtonsPage.tsx` (route `/page/strategy/admin/task-buttons`, **admin-gated**, entry「按钮管理」in the bottom-left settings dropdown of `components/page-sidebar.tsx`, both full + collapsed modes) has per-business tabs, edits name/type/target/启停/排序/增删, saves the **whole set atomically** (replace-all), plus 「导入默认」 to merge code-seed defaults. **Seed/fallback** (like 敏感词管理): only when the whole table is empty (fresh deploy, never saved) do the workspace + page fall back to `defaultTaskButtons(cfg)` — rwfx's url buttons (编辑/下一步/3q详情) built from the **live** `/api/public/config/task-deeplink` plus cross-business switch buttons for **all three** businesses (each switches to the other two — rwfx→三情/行动, 3qfx→任务/行动, xdfx→任务/三情). Once saved, the store is the sole source of truth. The old hardcoded jump buttons were migrated out: `TaskFxWorkspace` no longer hardcodes 切换到三情分析, `TaskActionsPanel` keeps only the non-jump 清除记录, and `TaskPurposePanel` dropped its 3q详情 button (now configurable) **and defaults the 目的树 table to collapsed** (chevron toggle). Helpers `core/auth/task-buttons.ts` (`TaskButton`, `defaultTaskButtons`, `resolveTaskButtons`, `buttonsForBusiness`, `runTaskButton`), client+hooks `strategy-components/api/task-buttons.ts`. Backend: `/api/task-buttons` (global/shared; open read, admin replace-all), store `deerflow.persistence.task_buttons` (table `task_buttons`, auto-created by `create_all`), wired `app.state.task_button_store`. Tests: `tests/test_task_buttons.py`.
- **按钮管理 (Task Button Management, admin-only)**: configures the **可配置跳转按钮** rendered in the left task panel of the task deep-link workspaces (rwfx / 3qfx / xdfx, see `TaskFxWorkspace`). Each button = `{business, label, linkType, target, appendTaskId, idParam, idKind, enabled, sortOrder}`; `linkType` is `url` (外链 `window.open`, optional append of `?{idParam}={id}` — **`idParam` is the editable query-param name**, e.g. `id`/`task_id`), `business` (内部切换到另一业务, reuses `resolveTaskDeeplinkTarget`, always task id), or `purpose` (弹出当前任务的「任务目的」表格 via the shared `TaskPurposeDialog`, no target — configurable for any page incl. xdfx). **`host`(宿主路由)** asks the embedding parent to navigate: `runTaskButton` sends `window.parent.postMessage({ type:'HOST_NAVIGATE', path, queryParams:{ [idParam]: Number(taskId) } }, hostOrigin)` — `target` is the host route, `appendTaskId`/`idParam` become numeric `queryParams` (task id, or action id when `idKind:"action"` — same selector as url buttons, xdfx only), origin from `runtime-config.js` `VITE_HOST_NAVIGATE_ORIGIN` (else ancestorOrigins/referrer; never `*`). **`idKind` (`task`/`action`) only matters for xdfx**: its deep-link `?taskId=` is an 行动id and the 任务id is resolved at login (`fetchTaskIdFromAction`) — `TaskContext` now also stashes the raw `actionId`, so a url **or host** button can append either (default `task`). Page `TaskButtonsPage.tsx` (route `/page/strategy/admin/task-buttons`, **admin-gated**, entry「按钮管理」in the bottom-left settings dropdown of `components/page-sidebar.tsx`, both full + collapsed modes) has per-business tabs, edits name/type/target/启停/排序/增删, saves the **whole set atomically** (replace-all), plus 「导入默认」 to merge code-seed defaults. **Seed/fallback** (like 敏感词管理): only when the whole table is empty (fresh deploy, never saved) do the workspace + page fall back to `defaultTaskButtons(cfg)` — rwfx's url buttons (编辑/下一步/3q详情) built from the **live** `/api/public/config/task-deeplink` plus cross-business switch buttons for **all three** businesses (each switches to the other two — rwfx→三情/行动, 3qfx→任务/行动, xdfx→任务/三情). Once saved, the store is the sole source of truth. The old hardcoded jump buttons were migrated out: `TaskFxWorkspace` no longer hardcodes 切换到三情分析, `TaskActionsPanel` keeps only the non-jump 清除记录, and `TaskPurposePanel` dropped its 3q详情 button (now configurable) **and defaults the 目的树 table to collapsed** (chevron toggle). Helpers `core/auth/task-buttons.ts` (`TaskButton`, `defaultTaskButtons`, `resolveTaskButtons`, `buttonsForBusiness`, `runTaskButton`), client+hooks `strategy-components/api/task-buttons.ts`. Backend: `/api/task-buttons` (global/shared; open read, admin replace-all), store `deerflow.persistence.task_buttons` (table `task_buttons`, auto-created by `create_all`), wired `app.state.task_button_store`. Tests: `tests/test_task_buttons.py`.
- **单智能体任务深链聊天记录(2026-10-19)**: `3qfx` / `rwfx` / `xdfx` 的会话历史使用独立 `task_deeplink_threads` 索引表及 `GET /api/task-deeplink-threads?task_id=&go_path=`,前端由 `useTaskDeeplinkThreads` 读取,不再筛选通用聊天列表。LangGraph thread/checkpoint 仍是消息权威数据;专用表仅按 `(task_id, go_path)` 定位 thread。`3qfx/rwfx` 按任务 id,`xdfx` 按 URL 原始行动 id;新 thread 写 `metadata.{taskRecordId,taskGoPath,thread_type:"task_deeplink"}` 并自动登记,通用最近对话默认隐藏。迁移 `20261019_01` 只建表、**不迁移/回填旧数据**。
- **State**: TanStack Query for server state, context providers (`SubtasksProvider`, `ArtifactsProvider`, `PromptInputProvider`) scoped to `WorkspaceRoutes`
- **LLM streaming**: `@langchain/langgraph-sdk` streams messages from the backend via SSE
- **UI**: Radix UI primitives + TailwindCSS v4 + shadcn-style components in `src/components/ui/`
- **Shims**: `src/shims/` provides `next/link` and `next/navigation` polyfills (original code was Next.js)
- **Login URL params** (`LoginPage.tsx`, frontend-only — the backend never sees these): besides the auth params (`ticket`/`userId`/`env`/`theme`/`yUserId`/`token`/`access_token`/`authToken`/`password`), the page supports a **task deep-link** via `?taskId=&goPath=`. After login it calls the consumer API `GET {VITE_AIPI_CONSUMER_URL}/taskAnalyseSearch/cop-task-detail?taskId=` (header `Authorization: Bearer admin`, auto-injected by `utils/request.ts`), stashes the full `data` (`id/overview/taskDirection/taskName/taskContent`) into `sessionStorage["login.copTaskDetail"]`, then routes by `goPath`: base codes `3qfx`/`rwfx`/`xdfx` → that business's configured **single agent** Q&A inside a **任务工作区** (resolved via `/api/business-mapping`; `resolveTaskDeeplinkTarget` calls `persistTaskContext({taskId, agentId, openingText})` and navigates to the `…/chats` **index**, which opens the latest task conversation or 新建 — a 1:N task↔对话 workspace, NOT `pendingAgentMessage`); the `-A` suffix (`3qfx-A`/…) → **multi-agent roundtable** with `roundtablePrefill.businessCode` so the page auto-selects the configured 业务链条. **xdfx's deep-link `?taskId=` is actually an 行动id, not a 任务id** — `resolveDeeplinkTaskId(goPath, rawId)` (called in `LoginPage` before everything) detects `xdfx`/`xdfx-A` and resolves it via `GET {action_detail_url}/{actionId}` → `data.taskId` (`fetchTaskIdFromAction`; `action_test`+`mock.action_detail` for offline testing); the resolved 任务id then drives `cop-task-detail`, the purpose tree, and the 1:N binding (other businesses pass the id through unchanged). **xdfx additionally partitions its chat records by 行动id (action_id), NOT by task_id**: `resolveTaskDeeplinkTarget` sets `partitionByAction:true` + keeps the raw action_id as `TaskContext.actionId`, so new threads are tagged `metadata.{taskId,actionId}` (taskId kept only to preserve the backend「任务深链对话全局共享」semantics, which keys off `metadata.taskId`) and the conversation list filters by `actionId` (+`agent_id`) — different actions of the same task get separate lists. xdfx's first-question opening text also appends `行动id(action_id)为…` (see `buildOpeningText` / `taskThreadMetadata`). goPath→businessCode: `3qfx`→`3Q`, `rwfx`→`6BF`, `xdfx`→`7BF`. The roundtable target also threads the URL `taskId` into `roundtablePrefill.taskId` (`resolveTaskDeeplinkTarget(goPath, detail, taskId)`), which turns on two taskId-scoped behaviors: (1) **separate, unpartitioned per-task storage** — taskId 会商聊天记录 live in their **own** store/table (`roundtable_task_drafts`), keyed **only by `task_id`** (one taskId = many records, shared across users, **no user 分权** at all; `created_by` is audit-only), kept entirely out of the per-user personal store (`roundtable_drafts`, now strictly per-user) so the two never mix. Backend router `/api/roundtable-task-drafts` (`GET /by-task/{id}` + `/by-task/{id}/latest`, `POST`, `GET/PUT/DELETE /{id}` — all user-agnostic); frontend client `roundtable-planning/api/task-drafts.ts`, and `useDraftPersistence`/`useRoundtableDrafts` route **all** CRUD to it when a `taskId` is present (else the personal `drafts.ts`). The matching **background jobs** are shared by task too: `roundtable_jobs.task_id` marks a task job; reads/dedup/stream/resume/cancel pass `?task_id=` so any user opening the task sees the same running 研讨 (progress/flowchart), and the executor writes the final report back to the task-draft store. Existing rows are moved over by `scripts/migrate_roundtable_task_drafts.py` (migration `20260618_01`). The history dropdown filters to the task and 新建 keeps the binding; (2) **Step-3 auto structured output** — after the `roundtable-summary` agent writes the Markdown report it automatically runs a second round emitting a `flow-json` flowchart (`useStep3Summary` `autoFlow`+`chainLevels`), whose hierarchy follows the business chain's stage goals (任务 → 目的 → 行为体 → …; coordinatorPrompt-specified hierarchy wins) and whose nodes never carry 阶段编号/阶段名 (the prior bug). **Both targets render embedded under the 任务管理 layout** — nested routes under `task-management/<business-path>/{roundtable,agent/:agent_id/chats}` in `StrategyRoutes.tsx` (`<business-path>` = `plan-task-sentiment` / `plan-task/plan-task-fx` / `plan-task/plan-task-action`, matching the COH中任务 menu paths in `sidebar-menu.ts`) so the global sidebar keeps 任务管理 → COH中任务 → the matching business highlighted. **All three single-agent businesses share `TaskFxWorkspace`** (`task-components/TaskFxWorkspace.tsx`): left = a「当前任务」card (`readCopTaskDetail()` 任务名称 + 简介, desensitized, 留存 above 新建对话) + per-task 1:N conversation list (`useTaskThreads(taskId, agentId, actionId?)` — filtered by **both** `metadata.taskId` AND `metadata.agent_id` so the three businesses' conversations staLine truncated
- **agentfx 任务深链(新系统 iframe 嵌入的智能体问答,3qfx 单智能体 lite 模式的独立复制)**: goPath=`agentfx`(`${loginBase}?password=…&taskId=…&goPath=agentfx&embed=1&hideJump=1&theme=dark-blue`)。与 3qfx 的差异:①任务详情不走外部 consumer,改调**本系统 Gateway** `GET {backend}/taskAnalyseSearch/cop-task-detail?taskId=`(`apiFetch` 带刚登录的 DeerFlow 态;任务数据来自 TaskCOP 任务表,与任务列表/报告导入同一份);②**固定使用内置智能体** `agentfx-analyst`(不走业务映射);③路由 `task-management/plan-task-agentfx/agent/:agent_id/chats`,页面为独立复制的 `AgentFxWorkspace.tsx`(`AgentFxWorkspace`/`AgentFxIndexResolver`/`AgentFxChat` —— 左「当前任务」卡 + 1:N 对话列表 + 新建对话,`AgentChatPage taskBind` 自动发开场白;无 TaskJumpBar/清除记录/闸门/xdfx 行动卡)。模块 `core/auth/agentfx-deeplink.ts`(`isAgentfxGoPath`/`fetchAgentfxTaskDetail`/`resolveAgentfxDeeplinkTarget`);任务详情整包仍存共享键 `login.copTaskDetail`(按标签页隔离),`AgentChatPage` 的 taskBind 自恢复对 agentfx 生效。`LoginPage` 在既有深链分支前用 `isAgentfxGoPath` 分流。**内置「任务研判报告助手」**:后端 seeder `app/gateway/routers/_agentfx_seed.py` + `_agentfx_seed_assets/`(config.yaml + SOUL.md,目录存在不覆盖;app.py lifespan 在 `_sync_legacy_agents` 之前调用,自动进 agents 表 user_id IS NULL)——SOUL 规定工作流「搜集信息(`web_search`,**首步不可跳过**)→ `write_file` 生成 `/mnt/user-data/outputs/report.md` + `task-reports.json`(14 个 categoryType 的 `{contentJson, categoryType}` 数组,contentJson 为 JSON 字符串)→ 聊天总结 → 引导入库」,硬性约束:categoryType 逐字用 14 个全称(禁缩写自造如「DQ关键事件」)、JSON 直接写汉字禁 `\uXXXX` 转义、每类必须有实质内容不许空数组/纯占位、文件一律用 `/mnt/user-data/...` 虚拟路径(防宿主路径泄漏进 tool args)。**检索依赖 `config.yaml tools.web_search`(DDG,enabled 已开;内网需换 configurable_search 时改回并填 endpoint)**。任务工作区输入框在 `taskCtxOn` 时隐藏「写作/记忆/参考文献」(InputBox `hideContextTools` prop)与「知识空间」(不传 knowledgeSelector);消息流里 write_file 文件卡片只显示文件名(`artifactDisplayName` 收敛宿主/虚拟路径)。**入库两条路径**:(a) **前端协助卡** —— `AgentChatPage`(taskBind 且非 taskGate)检测线程 artifacts 出现 `task-reports.json`(按文件名后缀匹配,兼容本地沙箱记录的宿主路径)且流式结束时,向 displayThread 尾部注入 `additional_kwargs.task_report_import_approval` 虚拟 ai 消息(`core/messages/utils.ts` 分组 `assistant:task-report-import` + `MessageList` 的 `taskReportImportSlot` 插槽渲染 `TaskReportImportCard`),点「开始入库」前端校验(`strategy-components/lib/agentfx-report-schema.ts`,14 类 + contentJson 合法 JSON + **\u 转义归一化还原为汉字**,与技能脚本同规则)后 `importTaskReport`(`strategy-components/api/task-reports.ts`)POST 导入接口整体替换;任务已有详情**不抑制**卡片(入库即整体替换,卡片文案说明覆盖语义),导入成功按 thread 记 sessionStorage(刷新不重复询问)、「暂不入库」为会话内记忆;(b) **入库技能** `task-report-import`(seeder 一并复制到 `skills/public/`,public 类技能 extensions_config 缺省即启用)—— 用户聊天里说「入库」时智能体按 SKILL.md 用 bash 跑 `scripts/task_report_import.py`(纯标准库:同样的校验规则 + 转义归一化;**默认关闭 SSL 证书校验** `ssl._create_unverified_context` + 忽略代理,适配内网自签证书;鉴权 `--token`/env `AGENTFX_IMPORT_TOKEN`→`DEERFLOW_AUTH_TOKEN`→`TOOLS_TOKEN` 优先,缺省经公共端点 `POST /api/parallel-agents/auth/token` 以 `agentfx-import` 用户现签 token——内网既有鉴权旁路;gateway 地址 env `AGENTFX_GATEWAY_URL` 或 `http://127.0.0.1:{DEER_FLOW_GATEWAY_PORT}`);`--strict` 可要求 14 类齐全,默认允许部分导入并在 stdout JSON 的 `missing_categories` 里列缺。入库成功后任务 TaskCOP 状态自动置 25(已完成分析)。**入库成功通知宿主**(iframe 嵌入场景):两条路径成功后都会 `window.top.postMessage({source: "magent-web", type: "task-report:saved", taskId: String(taskId)}, "*")`(`notifyTaskReportSaved`,`agentfx-report-schema.ts`)——卡片路径在 `TaskReportImportCard` 导入成功回调里直发;技能路径由 `AgentChatPage` 从消息流检测 `task_report_import.py` 的成功 stdout(`"success": true` + `task_id`)代发——**仅在回合结束后检测**(`!thread.isLoading` 才扫描;流式过程中 messages Line truncated
- **agentfx 任务深链(新系统 iframe 嵌入的智能体问答,3qfx 单智能体 lite 模式的独立复制)**: goPath=`agentfx`(`${loginBase}?password=…&taskId=…&goPath=agentfx&embed=1&hideJump=1&theme=dark-blue`)。与 3qfx 的差异:①任务详情不走外部 consumer,改调**本系统 Gateway** `GET {backend}/taskAnalyseSearch/cop-task-detail?taskId=`(`apiFetch` 带刚登录的 DeerFlow 态;任务数据来自 TaskCOP 任务表,与任务列表/报告导入同一份);②**固定使用内置智能体** `agentfx-analyst`(不走业务映射);③路由 `task-management/plan-task-agentfx/agent/:agent_id/chats`,页面为独立复制的 `AgentFxWorkspace.tsx`(`AgentFxWorkspace`/`AgentFxIndexResolver`/`AgentFxChat` —— 左「当前任务」卡 + 1:N 对话列表 + 新建对话,`AgentChatPage taskBind` 自动发开场白;无 TaskJumpBar/清除记录/闸门/xdfx 行动卡)。模块 `core/auth/agentfx-deeplink.ts`(`isAgentfxGoPath`/`fetchAgentfxTaskDetail`/`resolveAgentfxDeeplinkTarget`);任务详情整包仍存共享键 `login.copTaskDetail`(按标签页隔离),`AgentChatPage` 的 taskBind 自恢复对 agentfx 生效。`LoginPage` 在既有深链分支前用 `isAgentfxGoPath` 分流。**内置「任务研判报告助手」**:后端 seeder `app/gateway/routers/_agentfx_seed.py` + `_agentfx_seed_assets/`(config.yaml + SOUL.md,目录存在不覆盖;app.py lifespan 在 `_sync_legacy_agents` 之前调用,自动进 agents 表 user_id IS NULL)——SOUL 规定工作流「搜集信息(`web_search`,**首步不可跳过**)→ 每次检索 JSON 原样落盘 `/mnt/user-data/workspace/hits/qN.json` → **一条命令** `task-report-build`(group `all`)技能函数转换成 14 类 `task-reports.json`(模型禁止手写该 JSON;分页技能 `task-report-{enemy,our,env,judge}` 仅用于事后补跑单页;五者都是共享引擎 `task-report-lib/convert_lib.py` 的瘦 CLI,`resolve_hit_files` 对虚拟绝对路径读空的情况自动回退到相对写法并在 `warnings` 里列出尝试过的路径,免得智能体自己写脚本排查)→ `write_file` 生成 `/mnt/user-data/outputs/report.md` → 聊天总结 → 引导入库」,硬性约束:categoryType 逐字用 14 个全称(禁缩写自造如「DQ关键事件」)、JSON 直接写汉字禁 `\uXXXX` 转义、每类必须有实质内容不许空数组/纯占位、文件一律用 `/mnt/user-data/...` 虚拟路径(防宿主路径泄漏进 tool args)。**检索依赖 `config.yaml tools.web_search`(DDG,enabled 已开;内网需换 configurable_search 时改回并填 endpoint)**。任务工作区输入框在 `taskCtxOn` 时隐藏「写作/记忆/参考文献」(InputBox `hideContextTools` prop)与「知识空间」(不传 knowledgeSelector);消息流里 write_file 文件卡片只显示文件名(`artifactDisplayName` 收敛宿主/虚拟路径)。**入库两条路径**:(a) **前端协助卡** —— `AgentChatPage`(taskBind 且非 taskGate)检测线程 artifacts 出现 `task-reports.json`(按文件名后缀匹配,兼容本地沙箱记录的宿主路径)且流式结束时,向 displayThread 尾部注入 `additional_kwargs.task_report_import_approval` 虚拟 ai 消息(`core/messages/utils.ts` 分组 `assistant:task-report-import` + `MessageList` 的 `taskReportImportSlot` 插槽渲染 `TaskReportImportCard`),点「开始入库」前端校验(`strategy-components/lib/agentfx-report-schema.ts`,14 类 + contentJson 合法 JSON + **\u 转义归一化还原为汉字**,与技能脚本同规则)后 `importTaskReport`(`strategy-components/api/task-reports.ts`)POST 导入接口整体替换;任务已有详情**不抑制**卡片(入库即整体替换,卡片文案说明覆盖语义),导入成功按 thread 记 sessionStorage(刷新不重复询问)、「暂不入库」为会话内记忆;(b) **入库技能** `task-report-import`(seeder 一并复制到 `skills/public/`,public 类技能 extensions_config 缺省即启用)—— 用户聊天里说「入库」时智能体按 SKILL.md 用 bash 跑 `scripts/task_report_import.py`(纯标准库:同样的校验规则 + 转义归一化;**默认关闭 SSL 证书校验** `ssl._create_unverified_context` + 忽略代理,适配内网自签证书;鉴权 `--token`/env `AGENTFX_IMPORT_TOKEN`→`DEERFLOW_AUTH_TOKEN`→`TOOLS_TOKEN` 优先,缺省经公共端点 `POST /api/parallel-agents/auth/token` 以 `agentfx-import` 用户现签 token——内网既有鉴权旁路;gateway 地址 env `AGENTFX_GATEWAY_URL` 或 `http://127.0.0.1:{DEER_FLOW_GATEWAY_PORT}`);`--strict` 可要求 14 类齐全,默认允许部分导入并在 stdout JSON 的 `missing_categories` 里列缺。入库成功后任务 TaskCOP 状态自动置 25(已完成分析)。**入库成功通知宿主**(iframe 嵌入场景):两条路径成功后都会 `window.top.postMessage({source: "magent-web", type: "Line truncated
- **iframe 嵌入模式 (外部系统用 iframe 套用深链问答/会商)** — 三个**按标签页**开关,全部由深链 URL 参数开启、存进同一份 `sessionStorage` 会话(`embed.session`,键名常量在 `core/embed/embed-session.ts`),跨登录跳转/刷新/页内导航存活,**绝不跨标签污染**同浏览器另开的正常系统页。核心隔离手法:用 `sessionStorage`(按标签页/浏览上下文隔离,iframe 即便同源也有独立一份)而非 `localStorage`(跨标签共享)。开关:(1) **`?embed=1`(可带 `&theme=`)** — 去全局 chrome(`PageLayout` 的 `pageSidebarAllowed && !isEmbedActive()` → 隐藏 Header + PageSidebar)+ 按外部主题色**强制**明/暗:走 next-themes 的 `forcedTheme`(`App.tsx` `AppProviders` 调 `resolveForcedEmbedTheme`,**不写** localStorage `strategy-theme`),离散映射 `dark-blue`/`dark`→深、其余→浅;嵌入侧**绝不**调 `setTheme`/`persistSidebarEnabled`(那俩落 localStorage 跨标签污染,`LoginPage` 据 `readEmbedParam` 跳过)。(2) **`?hideJump=1`** — 隐藏深链底部「快捷跳转」栏 `TaskJumpBar` 的两部分(底部「下一步」等按钮 + 右侧垂直居中悬浮菜单),单智能体问答 + 会商共用该栏,`isJumpBarHidden()` 为真时整条 `return null`。(3) **登录用户隔离** — 嵌入态下鉴权/身份键(`deerflow.auth` token、`userInfo`、`login.*`、`tools_token`、maxkey/userDetail token)全部经 `core/auth/scoped-storage.ts` 的 `authStorage()`(嵌入→sessionStorage / 正常→localStorage)读写,使 iframe 里登录的用户与正常登录用户**完全隔离**、互不覆盖;所有 token 读取都过 `getStoredAuth()`/`getAuthorizationHeaderValue()`(API 客户端 `core/api/*` 全走这俩),改中心函数即让全体调用方自动隔离;跨标签账号变更监听 `AuthSyncWatcher`(`core/auth/sync.tsx`)在嵌入态禁用。嵌入会话在 `main.tsx` 渲染前 `syncEmbedSessionFromCurrentUrl()` 同步启用(早于任何鉴权写入,避免首帧 effect 顺序漏写 localStorage);`AppProviders` 的 effect 再按 SPA 导航同步。`embed=0`/`hideJump=0` 可显式关闭。正常(非嵌入)标签页所有 `isXxx()` 恒 false,行为**零变化**。第三个开关 `?isolate=1`(**独立登录会话**)把同一套**登录用户隔离**单独提供给**普通(带 chrome)标签页**——只切换鉴权存储、不去 chrome / 不强制主题 / 不动跳转栏:`authStorage()` 的条件从「仅嵌入」放宽为 `isEmbedActive() || isIsolatedSession()`,`AuthSyncWatcher` 的跨标签监听对嵌入态**或**独立态都即时忽略(handler 内实时判,运行时开关也即刻生效)。除 URL 参数外,**设置 → 外观 →「登录会话」** 有一个 Switch「独立登录会话(本标签页)」运行时切换(`setIsolatedSession`,`appearance-settings-page.tsx`):**开启时先 `migrateAuthToSession()`**(`scoped-storage.ts`,把 localStorage 的受保护键 `deerflow.auth`/`userInfo`/`userId`/`tools_token` + `login.*` 前缀快照搬进 sessionStorage)再翻标志,故无感不登出;此后本标签页登录态独立——别处登录/换号影响不到它,它换号也影响不到别人,但关掉该标签即结束(需重登)。开启给 toast 提示。
- **Web App 产物模式(多页面站点 / 流程图 / 地图,默认关闭、全链路可降级)**: 让智能体产出**完整站点**而非单个 xxx.html。沙箱技能 `skills/public/web-app` 用模板(`backend/templates/web-app-template`,经 `config.yaml sandbox.mounts` 只读挂载到 `/mnt/web-app-template`,目录缺失自动跳过)脚手架到 `outputs/<site>/`(site.json 清单 + `src/pages/*.tsx` 文件名即路由 + `public/` 数据),再跑 `scripts/build_site.py build` 用**预置 node_modules**(symlink/junction 链接,离线永不 npm install)执行 vite build 出 `dist/`;内置组件 `FlowCanvas`(@xyflow/react)/`GeoMap`(MapLibre,默认无底图 GeoJSON 模式,site.json 可配 tilesUrl/PMTiles)/`ChartPanel`(ECharts)/`MermaidBlock`。平台侧 `app/gateway/routers/web_app_sites.py` + 纯引擎 `deerflow/webapp/`(发现/子进程构建/HMAC 签名):`GET /api/web-app/capability`(enabled/模板/依赖/node 四项探测)、`GET|POST /api/threads/{tid}/sites[...]{build,build-status,preview-url}`,静态托管走 `GET /api/public/site-preview/{token}/{path}`(`/api/public/` 免鉴权命名空间,**唯一允许 HTML inline 的出口**——签名令牌 + dist/ 根限定 + 按请求真实 origin 显式声明的 CSP(iframe sandbox 不透明 origin 下 'self' 失效)+ nosniff;artifacts 接口既有的强制 attachment 不变)。前端:**独立风格**预览工作台 `pages/site-studio/SiteStudioPage.tsx`(顶层路由 `#/sites/:threadId/:siteName`,`App.tsx` 注册,不进 PageLayout/WorkspaceLayout;自带 site-studio.css 深色设计体系,零全局样式污染;iframe `sandbox="allow-scripts allow-forms allow-popups allow-modals"` 无 allow-same-origin)+ 产物面板 `artifact-file-detail.tsx` 里 site.json 的「站点预览」按钮(capability 可用才渲染,唯一既有文件改动)。**降级铁律**:默认**完全关闭**(`sandbox.mounts` 的模板挂载条目与文件末尾的 `web_app:` 段在 config.yaml 里**均为注释状态**);开启需**同时放开两处**(挂载让沙箱技能可探测 `/mnt/web-app-template`,`web_app.enabled: true` 开平台 API——只开一处会得到"生成了看不了"或"看不了也不生成"的中间态)。`build_site.py check` 在宿主侧会读 config.yaml 的 `web_app.enabled`(env `DEERFLOW_WEBAPP_ENABLED`/`DEERFLOW_WEBAPP_CONFIG` 可覆盖;沙箱内读不到配置时"挂载即视为开")。任何一环(配置/模板/node_modules/node)缺失 → capability 不可用 + 技能第 0 步探测失败即**回退单页 HTML 模式** + 入口按钮不渲染 + 构建失败只收进任务状态,现有功能零影响。配置 `web_app:`(enabled/template_dir/build_timeout_seconds/preview_token_ttl_seconds/node_path);测试 `tests/test_web_app_sites.py`;开发机 `make webapp-template` 预置依赖,离线发版由 deerflow-offline-deployment 仓库在 CI 预置 node_modules 进镜像。
### Configuration
Backend config lives in `offline-backend-20260512/backend/config.yaml`. Values starting with `$` resolve as environment variables (e.g., `$OPENAI_API_KEY`). MCP servers and skills are configured in `extensions_config.json` in the same directory.
+7 -1
View File
@@ -81,8 +81,11 @@ The frontend (`frontend-web/`) is a React 19 + Vite SPA:
- **Routing**: React Router v7; top-level routes in `src/pages/PageRoutes.tsx`, workspace sub-routes in `src/pages/WorkspaceRoutes.tsx`
- **Pages**: `ChatPage` (thread chat), `AgentChatPage` (custom agent chat), `AgentsPage`/`NewAgentPage` (agent management), `ScheduledTasksPage`/`ScheduledTaskRunDetailPage` (scheduled tasks), `LightAppManagementPage`/`LightAppListPage`/`LightAppIframeView` (轻应用中心 — register/list/embed mini-apps)
- **普通问答地图可视化**: shared `MarkdownContent` recognizes only dedicated `map-json` fences and otherwise preserves the legacy Markdown/Streamdown path byte-for-byte. `components/workspace/visualizations/` validates the bounded map contract with Zod, lazy-loads MapLibre, and provides loading/error/text fallbacks so malformed or unreachable maps never blank the message. The lead-agent prompt emits `map-json` only when the user explicitly asks for a map; flowcharts and other graphics continue through the existing Markdown/Mermaid path. The raster tile template comes from the single post-build setting `runtime-config.js` → `VITE_MAP_TILE_URL` (Google by default); replace it with the intranet `{z}/{x}/{y}` endpoint for disconnected deployments.
- **HTML 成品离线绘图运行时**: 新生成的 `.html` 源码直接引用 Gateway `/api/public/html-runtime/` 下的浏览器版 `g6.min.js`、`echarts.min.js`、`leaflet.js/css` 和 `deerflow-runtime.js`;文件随后台源码存放于 `vendor/html-runtime/`,不再依赖前端 Vite/node_modules 产物或公网 CDN。`config.yaml -> html_runtime.base_url` 是写入新 HTML 的浏览器可访问绝对地址,`html_runtime.map_tile_url` 是初始化脚本读取的瓦片模板。用户上传的栅格图若要作为页面背景,使用同一 Gateway 源下的签名地址 `/api/public/html-images/{token}` 写入 `background-image`。流程图使用 `data-df-flow` + G6(不用 Mermaid),图表使用 `data-df-chart`,地图使用 Leaflet `data-df-map`。普通问答保持独立:消息列表 `map-json` 继续由前端原逻辑渲染,普通流程图/图表仍走 Markdown/Mermaid。
- **定时任务「JSON → HTML 模板填充」(模板数据转换器)**: a third scheduled-task execution mode. In the create/edit dialog (`ScheduledTasksPage.tsx`), selecting the built-in agent **「模板数据转换器」** (`execution_agent_name === "template-json-builder"`, `TEMPLATE_BUILDER_AGENT_ID`) reveals a 「HTML模板」 select (`useHtmlTemplates` → `GET /api/scheduled-tasks/html-templates`) + a **.json 上传** button (reads the file's text into the prompt box) and **hides** the 必须生成 HTML/Markdown switches (this mode always outputs HTML). The chosen template persists as `execution_context.template_config.template_id` (`TemplateFillConfig` in `core/scheduled-tasks/types.ts`). At run time the backend converts the source JSON (the task prompt) into that template's `DATA` structure, validates (retry-on-fail), fills the template, and stores an HTML page previewable via the existing run-detail HTML preview. The source can be **pure JSON, plain text, or text-with-embedded-JSON** — the backend pre-extracts any embedded JSON and the agent's `SOUL.md` maps it (or the plain text's semantics) to the template's schema skeleton. **Chat-page debug entry (only this agent's `AgentChatPage`)**: when `agent_id === "template-json-builder"`, the chat header shows a 「页面模板」 select + a 「渲染预览」 button — after a normal Q&A run, the button takes the **latest AI reply**, POSTs it to `POST /api/scheduled-tasks/template-fill/render` (`renderTemplateFill`), and pops a dialog rendering the filled HTML in a `sandbox="allow-scripts"` iframe plus the structure-validation status/errors/gaps — for debugging the agent's conversion effect without creating a scheduled task. Gated strictly on the agent id, so other agents' chat pages are untouched. See the backend CLAUDE.md「Template-fill mode」for the engine (`deerflow/runtime/scheduler/template_fill.py`), agent seed, dispatch, and the render API.
- **AI 写作·对话版(对话驱动写作台)**: a **parallel new entry** beside the classic AI-writing page (classic page/routes untouched) — route `/page/canvas/ai-writing-chat` (`OpenCanvasRoutes.tsx`), sidebar 「问答管理 → 课题研究(对话版)」. Page `open-canvas/pages/AIWritingChatPage.tsx` fully **reuses the engine** (`AIWritingContext` state machine + LangGraph `useStream`, `AIWritingHistoryProvider`, right-side `AIWritingDraftPanel`) but replaces the presentation: left = `components/ai-writing-chat/WritingChatPanel.tsx` (timeline + **button-only intervention cards** `ChatInterventionCards.tsx` — the five pause cards copied from `InterventionCard.tsx` with all inline textareas removed, confirm/action buttons kept — plus a bottom **unified smart composer**). Free text goes through two-level intent resolution (`open-canvas/api/ai-writing-intent.ts`): deterministic short-phrase fast path (确认/继续/重新搜索/重写大纲…; high-cost actions never fast-pathed) then `POST /api/ai-writing/sessions/{id}/intent` — the backend reads the graph checkpoint (authoritative pause point from the pending interrupt) and routes via `deerflow/agents/ai_writing/intent_router.py` (LLM + strict per-pause action whitelist aligned to the graph's real resume actions, confidence gates: finalize/force_finalize/覆盖式 re_search need ≥0.85 else clarify, payload key-whitelist cleaning). Results map to `submitIntervention`/control (暂停/继续/后台挂起)/QA bypass (`answer` rendered as a chat bubble, never touches graph state)/clarify (amber follow-up bubble with multi-turn context). **Two-state layout**: idle (未开始写作) renders the **classic setup experience unchanged** — `WritingSetupChat` + collapsible `AIWritingPanel` form under `WritingFormProvider` (same as `AIWritingPage`'s idle branch); once writing starts (or history view opens) it switches to the chat workbench. **Unified chronological feed** (`ChatFlowTimeline.tsx`): intent-layer chat bubbles are interleaved INTO the progress-event timeline by anchor (= `progressEvents.length` at message creation), so events triggered by a user message render **below** it (Google-AI-Studio-style single stream, not timeline-above/chat-below); composer-driven interventions suppress the duplicate 「我的要求」card (`suppressedRequirementIndices`). **素材不足自动续跑** only at `material_confirm` (the only pause the graph supports re-search from): needResearch caches the payload as a pending intent, submits `re_search + userQuery`, and auto-submits the pending intent when the new material-confirm pause arrives (cancellable pill). Stage chips + per-stage placeholder + 「当前交互对象」status badge. Backend tests `tests/test_ai_writing_intent.py`. Plan doc: `frontend-web/docs/ai-writing-对话驱动写作台-实现方案.md`.
- **策略优选**: route `/page/strategy/qa/strategy-selection`, sidebar 「问答管理 → 策略优选」. The standalone `src/strategy-selection/` workbench has two horizontal split stages: Step 1 keeps intent-understanding conversation cards on the left and an editable task draft plus angle selection on the right; Step 2 keeps multiplexed parallel strategy cards and automatic judging on the left while the right stays dedicated to the selected-strategy basket and final Markdown synthesis. It consumes the existing `POST /api/parallel-agents/run/stream` protocol, isolates output by stable agent id, supports expanded per-agent dialogs, user selection, Markdown editing/copy/download, and shared server-side DOCX export. A clearly marked neutral Taiwan-Strait risk-management demo is loaded by default for offline UI acceptance. Administrators can edit the SOUL and skills of the nine built-in functional agents through the page dialog; the backend seeds `strategy-selection-{intent,angle-*,judge,solution}` only when their directories are missing, preserving later admin changes. Pure parsing/SSE tests live in `src/strategy-selection/lib.test.ts`; backend seed tests live in `tests/test_strategy_selection_seed.py`.
- **轻应用中心 (Light App Center)**: a sibling of 任务管理 in the sidebar with two pages — 应用管理 (admin registers `window_open` or `iframe` mini-apps with a route-param builder: login params resolved from `localStorage.userInfo`, theme params literal) and 应用列表 (card grid of `window_open` apps, `window.open` launch). `iframe` apps are mounted under a chosen parent menu (XK/PG/TY/综合管理) and **dynamically injected into the sidebar** by `core/page-layout/use-sidebar-menu.ts` (`useSidebarMenuItems`), opening in the `LightAppIframeView` host. Client + React Query hooks in `strategy-components/api/light-apps.ts`; URL builder/constants in `strategy-components/lib/light-app.ts`. Backend: `/api/light-apps` (global/shared, admin-gated writes).
- **菜单管理 (Menu Management, admin-only)**: an **overlay** over the static sidebar menu — `MenuManagementPage` (`pages/MenuManagementPage.tsx`, route `/page/strategy/admin/menu-management`, opened from the admin-only 「菜单管理」 item in the **bottom-left settings dropdown** of `components/page-sidebar.tsx`). Admins rename / 停用·启用 / reorder (↑↓ or @dnd-kit drag) / re-parent (horizontal drag **or** the edit dialog's 「移动到」parent selector) any node across 一级/二级/三级, **including injected iframe light-app nodes** (`light-app-<appId>`). The static tree (`core/page-layout/sidebar-menu.ts`) stays the source of icons/routes; only per-node deltas (`label`/`disabled`/`parentId`/`sortOrder`) persist to the backend. Merge logic in `core/page-layout/menu-overrides.ts` (`applyMenuOverrides`, applied in `use-sidebar-menu.ts` after light-app injection, before role filter; disabling a node hides its whole subtree) — strict **3-level cap** (`wouldExceedDepth`). Client `strategy-components/api/menu-overrides.ts`. Backend: `/api/menu-overrides` (global/shared; open read, admin atomic replace-all). **简介模式** left-nav is a separate overlay: `/api/compact-menu-overrides` stores two independent sets (type `A` / `B`); each website picks one via `runtime-config.js` `VITE_COMPACT_MENU_TYPE`.
- **白名单管理 (Login Whitelist, admin-only)**: gates **who may log in**, run as a **blocklist (黑名单模式)**. Two pieces: a global master switch (`system_settings.json → login_whitelist.enabled`, surfaced by **admin-only** `GET|PUT /api/system-settings/login-whitelist`) + a per-user **放行** flag (`users.approved` column, migration `20260625_02`). Users **self-register** as usual and **every new account defaults to `approved=True`** — so a user who has never logged into this system before is allowed straight through; the switch only blocks users an admin has explicitly **取消放行** (`approved=False`). (`create_user`/`User`/`UserRow` all default `approved=True`; admins are always approved; existing rows are backfilled `True`.) Login interception is the single helper `enforce_user_approved(user)` in `app/gateway/routers/auth.py`, called in all three login endpoints (`/login/{local,username,token}`) **after** the account is resolved/created and **before** the JWT is issued: switch off → everyone passes; `system_role=="admin"` → always passes (anti-lockout); else `approved=False` (admin-revoked) → 403 `{code:"PENDING_APPROVAL"}` (the frontend's `extractErrorMessage` surfaces the message and login throws before writing any auth state, so no half-login). Admin approves on `pages/LoginWhitelistPage.tsx` (route `/page/strategy/admin/login-whitelist`, entry「白名单管理」in the bottom-left settings dropdown of `components/page-sidebar.tsx`, both sidebar modes): a top master-switch + a list of all registered users (`GET /api/v1/auth/users`, now carrying `approved`) with a per-row 放行 Switch (`PUT /api/v1/auth/users/{id}/approval`, admin-only; revoking only blocks the **next** login — `token_version` is left untouched, existing sessions are not force-killed). Client/hooks: switch in `strategy-components/api/login-whitelist.ts`, user list + approval in `core/auth/{api,hooks}.ts` (`useSetUserApproval`). Backend tests: `tests/test_login_approval.py`. **账号级「登录口令」(per-account password — 临时开通)**: the same page also lets an admin assign one specific account an individual **plaintext** password (`users.login_password` column, migration `20260625_04`, nullable; auto-added on startup by `_ensure_orm_columns_sync`). Once set, **that account's** username login (`POST /api/v1/auth/login/username`) **requires** `?password=` to exactly match it and that personal password **takes precedence over** the global shared-password gate (`username_login.require_password`); accounts without one keep today's shared-gate / passwordless behavior. The user then logs in via the **existing** frontend URL `#/login/<用户名>?password=xxxx` (LoginPage already forwards `?password=`; `<用户名>` = the account's email `@`-prefix). **Plaintext is intentional** — the password rides in the URL query anyway, so storing it readably lets the admin re-copy the full login link from the page; it is exposed only on the **admin-only** `GET /api/v1/auth/users` (`UserListItem.login_password`) and the new admin-only `PUT /api/v1/auth/users/{id}/login-password` (set non-empty / clear with null·empty; doesn't touch `token_version`). The whitelist page renders a per-user 「登录口令」 column with a set/修改 editor + a live 「复制登录链接」 button (`useSetUserLoginPassword` in `core/auth/hooks.ts`, `setUserLoginPassword` in `core/auth/api.ts`). Backend gate helper `_lookup_user_by_username` (lookup-only, no auto-register) in `routers/auth.py`; tests `tests/test_login_personal_password.py`.
@@ -90,7 +93,10 @@ The frontend (`frontend-web/`) is a React 19 + Vite SPA:
- **技能地址管理 (Skill Address Management, admin-only)**: 扫描运行中的技能(`skills/{public,custom}` 下所有 `.md`/`.py`)里的 **http(s) URL 与 IP/IP:端口**,按唯一地址聚合全部出现位置(跨 .md/.py),支持**批量替换**(如把 UAT 地址一次性改成生产)。主体抽成可复用组件 `SkillAddressesPanel`(`pages/SkillAddressesPage.tsx` 导出,默认导出 `SkillAddressesPage` 仍保留旧路由 `/page/strategy/admin/skill-addresses` 作直链回退),**作为「技能地址管理」标签页嵌入管理员「技能管理」页 `pages/AdminSkillsPage.tsx`**(该页加 Tabs:「技能列表」+「技能地址管理」;入口在「设置和更多」下拉的「技能管理」`workspace-nav-menu.tsx` → `/page/workspace/admin/skills`,不再单列于 `components/page-sidebar.tsx` 左下角设置下拉):打开自动扫描并带**进度条**(扫描逐技能 / 应用逐文件 X/N,应对 150+ 技能),URL/IP 类型过滤 + 搜索 + 「片段批量套用」(如所有 `uat.4.cn` → `prod.4.cn`)、每地址「替换为」输入框(可展开看 `file:line` 片段)、内联**预览**、**应用**、**多关键词检索**(空格分隔全部命中),以及**修改记录**面板——每次应用生成一条记录(显示时间/改了哪些地址 from→to/文件数/处数),可**针对单次记录回退**(二次确认)。client + NDJSON 流式解析在 `strategy-components/api/skill-addresses.ts`。后端 `/api/skill-addresses`(**仅管理员**):所有文件 IO 走 `asyncio.to_thread` 不阻塞事件循环;`GET /scan`(+`/scan/stream`)、`POST /preview`、`POST /apply`(+`/apply/stream`)、`GET /backups`、`POST /rollback/{id}`;**子串安全替换**(基于 token span,不会误伤子串)、`expected_total` 乐观锁(409)、写前自动磁盘备份到 `{skills_root}/.address-edits/`(同时写一份 `{id}.manifest.json` 记录本次替换明细,供修改记录展示)、应用后刷新技能系统提示缓存。纯引擎 `deerflow/skills/address_scan.py`,router `app/gateway/routers/skill_addresses.py`,tests `tests/test_skill_addresses.py`。范围仅运行中技能,不动 `deploy/minimal/skills/`。
- **按钮管理 (Task Button Management, admin-only)**: configures the **可配置跳转按钮** rendered in the left task panel of the task deep-link workspaces (rwfx / 3qfx / xdfx, see `TaskFxWorkspace`). Each button = `{business, label, linkType, target, appendTaskId, idParam, idKind, openMode, loginParams, appendAuth, authTokenParam, authNameParam, enabled, sortOrder}`; `linkType` is `url` (外链 `window.open`, optional append of `?{idParam}={id}` — **`idParam` is the editable query-param name**, e.g. `id`/`task_id`), `business` (内部切换到另一业务, reuses `resolveTaskDeeplinkTarget`, always task id), or `purpose` (弹出当前任务的「任务目的」表格 via the shared `TaskPurposeDialog`, no target — configurable for any page incl. xdfx). **`host`(宿主路由)** asks the embedding parent to navigate: `runTaskButton` sends `window.parent.postMessage({ type:'HOST_NAVIGATE', path, queryParams:{ [idParam]: Number(taskId) } }, hostOrigin)` — `target` is the host route (e.g. `/task/item-aiceh/step-analysis-audience`), `appendTaskId`/`idParam` become numeric `queryParams` (task id only), origin from `runtime-config.js` `VITE_HOST_NAVIGATE_ORIGIN` (else ancestorOrigins/referrer; never `*`). Independent-tab clicks toast and do nothing. **`url` buttons support two independent, coexisting login-append mechanisms**: (1) **`loginParams` (追加登录参数, like 应用管理/Light App Center)** — a list of `{key, source, customValue}` resolved at click time: `runTaskButton` reads `userInfo` (via `readUserInfo` from `strategy-components/lib/light-app`, embed-isolated through `authStorage`) and appends `?{key}={value}` for each (`source` = a userInfo field such as `accessToken`/`yUserId`, or `other` → literal `customValue`); empty list = nothing appended. Reuses `LOGIN_SOURCE_OPTIONS` from light-app; stored as JSON column `task_buttons.login_params_json` (`PortableJSON`, migration `20260625_01`). (2) **`appendAuth` (追加登录态)** — appends `?{authTokenParam}={token1}&{authNameParam}={username}` (default param names `token`/`username`) so the跳转目标外部系统 can identify the current user — e.g. `…/action/sentiment?token=<JWT>&username=lizhaoxing`. **Gated on token login**: `token1` = the `?authToken=` used by token-exchange login; `username` = the raw login name the backend resolved from `getTokenInfo`. Both are stashed at login by `persistLoginCredentials` into `login.token1`/`login.username` (`core/auth/login-credentials.ts`, scoped via [[scoped-storage]] so嵌入/独立会话隔离 + 随独立会话迁移). Only token login writes them → a non-token-login session has no `token1`, so `runTaskButton` silently skips the auth append (the jump still works, just without those two params). The frontend reads `username` straight from the new `UsernameLoginResponse.username` field (the stored email local-part is lossy — lowercased/normalized — so not used), **not** by decoding the JWT. Stored as columns `task_buttons.{append_auth, auth_token_param, auth_name_param}` (migration `20260625_02`). The admin page renders both editors for `url` buttons (loginParams 虚线框 + 追加登录态 switch). **`idKind` (`task`/`action`) only matters for xdfx**: its deep-link `?taskId=` is an 行动id and the 任务id is resolved at login (`fetchTaskIdFromAction`) — `TaskContext` now also stashes the raw `actionId`, so a url button can append either (default `task`). Page `TaskButtonsPage.tsx` (route `/page/strategy/admin/task-buttons`, **admin-gated**, entry「按钮管理」in the bottom-left settings dropdown of `components/page-sidebar.tsx`, both full + collapsed modes) has per-business tabs, edits name/type/target/启停/排序/增删, saves the **whole set atomically** (replace-all), plus 「导入默认」 to merge code-seed defaults. **Seed/fallback** (like 敏感词管理): only when the whole table is empty (fresh deploy, never saved) do the workspace + page fall back to `defaultTaskButtons(cfg)` — rwfx's url buttons (编辑/上一步/下一步/3q详情) built from the **live** `/api/public/config/task-deeplink` plus cross-business switch buttons for **all three** businesses (each switches to the other two — rwfx→三情/行动, 3qfx→任务/行动, xdfx→任务/三情). Once saved, the store is the sole source of truth. **Bottom 快捷跳转 bar groups by prev/next**: `TaskJumpBar` renders only「上一步/下一步」-type buttons in the bottom bar — a `prev` group (id 含 `prev` 或文案「上一步」, 描边次要样式 + ←图标) rendered **左侧**, then a `next` group (id 含 `next` 或文案「下一步」, 实心主色) on the **右侧** (container `justify-end`); everything else goes to the right-edge floating menu. The default 上一步 button (`rwfx-prev`, `url`, target = `DASHBOARD_AGENT_ROUTE` `/page/strategy/dashboard-agent`, `appendTaskId` + `idParam=taskId` → `?taskId=任务id`, `openMode=self`) 默认指向**应用内**「大屏绘制」Line truncated
- **实时并发监控 (Concurrency / System-Pressure Monitor, admin-only)**: a live panel mounted at the **top of the admin 排行榜 page** (`components/workspace/admin/concurrency-monitor.tsx` `ConcurrencyMonitorPanel`, rendered in `pages/AdminUserLeaderboardPage.tsx`) showing **当前并发的大模型调用数 + 列表(用户/智能体/类型/已运行时长)**, each row with a 「停止」button (and a 「全部停止」) that **truly aborts** that run's in-flight model stream via `RunManager.cancel` (`POST /api/admin/active-runs/{run_id}/cancel`) — to relieve pressure when 使用人数过多. Two line charts (minute/hour toggle): **并发量折线图** (peak/avg, `GET /api/admin/active-runs/concurrency`, fed by a lightweight background sampler into `concurrency_samples`) and **大模型出字速度折线图** (tokens/sec, `GET /api/admin/active-runs/token-speed`, aggregated from `llm_call_metrics`) so a dip pinpoints where the bottleneck is. A **「后台采样」Switch** (`system_settings.json → concurrency_monitor.enabled`, **default off / opt-in**, `GET|PUT /api/admin/active-runs/settings`) turns the background sampling on/off **at runtime, no restart** (the live concurrency count + 「停止」 work even while sampling is off — only the 并发量 history chart needs it on); everything is designed to **never impact production** (one tiny INSERT/15s, retention-purged, best-effort). Client/hooks: `core/admin/active-runs.ts` + `core/admin/hooks.ts` (`useActiveRuns`/`useConcurrencySeries`/`useTokenSpeedSeries`/`useCancel*`/`useMonitorSettings`). See the backend CLAUDE.md「Admin Concurrency Monitor」.
- **会话冲突 409 中文化**: when a user submits a new question while the previous turn is still streaming, the backend's `RunManager.create_or_reject` rejects with HTTP 409 (`services.py` now returns a **Chinese** detail「当前对话还在生成回答…」). `core/threads/hooks.ts` `onError` detects the conflict (`isActiveRunConflictError`: 409 / "already has an active run" / the Chinese text). A premature SSE close (SDK treats it as success and restores the send button while the backend run is still occupying the thread) is recovered by `confirmOrRejoin`: hold the stop button, `joinStream` the live run, and on 409 auto-reconnect instead of a fake AI bubble. `sse_consumer` no longer ends a resumable (`on_disconnect=continue`) stream on a false-positive `is_disconnected()`.
- **会话冲突与模型工具流恢复**: when a user submits a new question while the previous turn is still streaming, the backend's `RunManager.create_or_reject` rejects with HTTP 409 (`services.py` returns the Chinese detail「当前对话还在生成回答…」). `core/threads/hooks.ts` detects the conflict and routes it through `confirmOrRejoin`. Run-creation SSE responses expose `Content-Location` plus the SDK `Location` reconnect endpoint; `core/api/api-client.ts` records the last consumed event id, so an in-page rejoin resumes after that cursor instead of replaying tool arguments from `-1`. `write_file` argument updates carried by `messages-tuple` count as business activity. A known active run is probed only after 90 seconds without a business frame; the watchdog hides `lg:stream`, stops only the old browser reader, and rejoins without cancelling the backend run. Clean EOF, transport errors, 409 and watchdog recovery share one single-flight Promise to prevent duplicate joins/finalization. The backend `PatchedChatOpenAI` separately normalizes confirmed cumulative argument snapshots and bounded compatible response shapes while preserving standard deltas; its content-free diagnostics never log prompts, arguments or file bodies.
- **React #185 与流式刷新保护**: every direct LangGraph `useStream` surface (main/agent chat, AI writing, Canvas and Studio notebook) uses shared `STREAM_RENDER_BATCHING = true`. The installed SDK implements numeric `throttle: 16` as a trailing debounce, which can indefinitely postpone UI updates during a continuous fast stream; `true` retains asynchronous next-task batching without requiring 16ms silence. `stream-error-classification.ts` detects `Maximum update depth exceeded` / production `#185` through bounded, cycle-safe wrappers. Main chat filters the SDK's stored error and duplicate submit rejection; `react-ui-error.ts` gives each send at most one automatic UI recovery, then pauses rejoin without cancelling the server run. Token activity must not reset that budget. The top-level `ReactUiErrorBoundary` catches only this render/commit error, shows a static friendly reload panel, and never automatically remounts the failing tree. Other errors are not swallowed. Shared toasts are deduplicated for 30 seconds; diagnostics retain error type, recovery phase, thread/run identifiers and component stack but never message/tool content. Real SDK SSE replay covers partial reasoning and write_file Markdown before generation ends; the browser boundary check triggers a real production React layout-effect loop and requires manual reload to recover. These safeguards do not prove the unknown production component loop is fixed.
- **流式游标作用域**: resumable cursors belong to the current SDK message accumulator, not a module-global map. `createStreamAPIClient` isolates same-reader recovery; newly mounted chats and real thread switches must replay the retained prefix instead of borrowing another reader's consumed position. Clearing replaces the cursor map so late old-reader events cannot pollute the new scope. The shared `getAPIClient` does not rewrite replay-from-start cursors. `stream-cursor-replay.test.ts` uses the real SDK to protect against both duplicate tool arguments on reconnect and missing prefixes on reopening.
- **单智能体任务深链聊天记录(2026-10-19)**: `3qfx` / `rwfx` / `xdfx` 已不再通过 `useTaskThreads` 扫描、过滤通用 `threads_meta` 列表;`TaskFxWorkspace` / `TaskFxIndexResolver` 统一调用 `useTaskDeeplinkThreads` → `GET /api/task-deeplink-threads?task_id=&go_path=`,读取独立表 `task_deeplink_threads` 的 `(task_id, go_path)` 索引,再按 `thread_id` 关联 canonical `threads_meta` 标题/状态(消息与 checkpoint 仍由 LangGraph 管理)。新会话 metadata 写 `taskRecordId`、`taskGoPath`、`thread_type=task_deeplink` 并由 thread-meta 仓储同事务登记;`3qfx/rwfx` 的 `taskRecordId` 是任务 id,`xdfx` 是 URL 原始行动 id(真实任务 id 仍保留在 `metadata.taskId` 维持共享访问语义)。普通聊天记录默认隐藏该 thread type;显式 taskId 查询仍可读取。迁移 `20261019_01` **只建表、不回填旧数据**,因此上线前旧深链会话不会出现在新列表。`/api/open/3qfx/ask` 同样写专用索引。Tests: `packages/harness/tests/test_task_deeplink_threads.py`, `src/core/auth/task-purpose-metadata.test.ts`, `src/core/threads/hidden-chats.test.ts`。
- **State**: TanStack Query for server state, context providers (`SubtasksProvider`, `ArtifactsProvider`, `PromptInputProvider`) scoped to `WorkspaceRoutes`
- **LLM streaming**: `@langchain/langgraph-sdk` streams messages from the backend via SSE
- **UI**: Radix UI primitives + TailwindCSS v4 + shadcn-style components in `src/components/ui/`
Binary file not shown.
+117
View File
@@ -0,0 +1,117 @@
# Wiki 数据流转与本地编码验收(2026-09-03)
## 结论
本地 BGE-M3 小批量编码已安装、配置并实际运行。已有 Wiki 的向量化、自动沉淀、
普通库文件导出、助手库异步导入、助手库文件导出、空普通库回流及两端向量检索已实测通过。
**技能自动生成 Wiki 尚未完成验收**:WeKnora 的两个生成模型都返回 HTTP 402
`Insufficient Balance`。不能把后半段通过等同于全链路通过。
本次修改运行在本地 DeerFlow 开发服务(前端 5175、后端 8001),连接用户提供的远端
WeKnora;没有把业务代码发布到服务器,没有提交 Git,也没有直接写 WeKnora 图数据库。
## 本地编码配置
- 实现:FastEmbed / ONNX Runtime CPU,官方 `BAAI/bge-m3` 权重。
- 配置名:`bge-embedding-m3`,1024 维,每批 2 条,2 个 CPU 推理线程。
- 运行依赖:FastEmbed 随标准后端依赖安装;模型必须提前放在离线目录中,仍按需加载,不影响主服务启动。
- 权重:约 2.3 GB,持久缓存 `C:\Users\ji\.cache\deerflow\embeddings`,不进入业务仓库。
- 配置:`llmwiki.local_wiki_index.embedding.provider: local`;可用
`local_model_path` 指定离线权重目录,详见后端 README。
- 查询向量在本地实时编码。已有 Wiki 在内容哈希、模型指纹匹配且向量非空时直接复用。
不同模型或维度不能混用;导入不静默重新编码。
- 原始文档分段向量与模型生成后的 Wiki 文本不同,不能相互冒用。本次新建的是 Wiki
正文的 BGE-M3 索引,不是把既有 512 维文档向量改名为 1024 维。
## 实际数据验收
来源是已有普通知识库“图片测试111”,没有改写其 Wiki 内容。
| 阶段 | Wiki | Wiki 向量 | 实体 | 引用关系 |
| --- | ---: | ---: | ---: | ---: |
| 普通知识库导出 | 19 | 101 | 19 | 108 |
| 助手知识库导入后导出 | 19 | 101 | 19 | 108 |
| 新建普通知识库回流后导出 | 19 | 101 | 19 | 108 |
| 知识梳理总库导出(7 个来源记录) | 19 | 101 | 19 | 108 |
逐项断言通过:
1. 三份包的 Wiki slug、标题、正文、摘要、页面类型、别名、分类路径、出链一致。
2. 101 条向量的原始 float32 字节、分段编号、维度和模型指纹完全一致。
3. 将导入前后实体 ID 映射回原 Wiki slug 后,108 条关系的端点及谓词集合完全一致。
4. 总库导出没有因多个来源重复同步而重复导出同一当前页面的分段向量。
5. 元数据回归测试另覆盖 tags、目录恢复、原始文档片段不计入 Wiki 数量、原生技能无
ownership 记录时可被选中、短 Wiki 不漏向量、模型指纹不兼容时不假装向量检索。
普通包约 1.01 MB,助手包约 0.79 MB,回流包约 0.83 MB。普通包还带 2 个原始文档及
108 个原始片段;助手导出提供 Wiki、Wiki 向量和关系,不重新上传原始文档触发二次生成。
### 检索
实际查询:“军方用什么工具评估极端天气带来的风险?”
- 回流普通库 `/api/llmwiki/search` 返回 `mode=vector`,首条“国防气候评估工具”。
- 助手库 `/api/assistant-knowledge/search` 返回 `retrieval_mode=vector`,首条同上;
首个命中分段余弦相似度约 0.6343。
- 浏览器实际提交总库检索,显示“向量检索 · 余弦相似度”和相同命中;不是只检查返回字段。
- 普通库采用已有页面级聚合评分,因此页面最终分数与单分段余弦值不必相等。
- 回流的向量写入 **DeerFlow 管理的本地 Wiki 索引**。未写入 WeKnora 原生向量数据库,
本次结论适用于本系统问答检索,不能声称原生 WeKnora 自身的检索也已完成该向量导入。
### 页面
普通与助手库复用同一 Wiki 阅读组件:目录树、类型/标签筛选、分类面包屑、别名标签、
正文内链、出链、反向链接及邻接关系视图。窄窗口可收起目录。
浏览器验证了“索引 → 国防气候评估工具”、展开关系视图、助手 Wiki 跳回普通库来源,
以及普通 Wiki 的“在知识梳理中查看”反向跳转。
### 验收数据位置
- 原有普通库:`48f669dc-433e-4aab-b5ea-897b6f7a3d2f`
- 知识梳理总库:`c3fd9841-0718-44dc-860e-aae3020fd485`
- 最终助手测试库:`489be905-08c8-40fe-8d60-42ac21dd945a`
- 最终普通回流测试库:`428162aa-03f6-4c61-8994-360f2f39e0c9`
新建测试库名称均以“Wiki链路验收-0903-1826”开头,保留用于复核;未删除用户已有库。
本地 `.runtime/wiki-live-result.json` 保存验收结果,`.runtime/wiki_live_acceptance.py`
保存本次验收流程,均为忽略的运行产物,不包含认证口令。
## 这次实际修复的问题
- 内置技能只有磁盘目录、没有归属记录时,被错误判定为不存在。
- 技能应上传去代码后的知识正文,让 WeKnora 生成 Wiki,而不是伪造一个 `skill` 类型页面。
- Wiki 生成等待使用实际 stats 接口;零页结果明确失败,不无期限等待。
- 向量化完成后自动导出沉淀到总库,并阻断导出回调重复触发全量同步。
- 短摘要和短正文同时存在时,旧分段阈值会将整页过滤为零向量。
- 分类路径必须先创建 WeKnora folder 并绑定 folder_id;只传 category_path 会被服务端清空。
- Wiki 的标签/分类/别名/原始引用保留;图节点绑定真实 Wiki,避免生成重复的空实体页面。
- 保留归一化 float32 原始字节;导入中不能用 `section_index or index`,因为合法编号 0 会被改写。
- 总库多个来源保留审计,但检索与导出只使用当前 Wiki 版本对应的向量,避免旧贡献污染检索。
- 包解析改为同盘临时 SQLite,上传先落盘再异步导入,导出完成后才提供文件下载。
## 阻塞与未通过的范围
1. 技能 `knowledge-base-ingest` 已送入专门创建的普通测试库,但远端
`deepseek-v4-flash` 和 `qwen3.6-flash` 均因余额不足失败。WeKnora 即使失败也可能把
文档 parse_status 标为 completed。需恢复可用生成模型后重新运行技能全链路验收。
2. 已采用磁盘暂存和有界批处理,但本次只有约 1 MB 实测,**未做数 GB 压测、断点续传、
进程中断恢复验收**。大包部署仍需磁盘容量、代理 body size 和超时配置。
3. 当前实体去重主要按类型及规范化名称;这次没有完成跨别名、歧义实体的模型对齐及
多来源事实冲突合并验收,不能视为这一历史需求已全面完成。
4. 重复上传仍会保留独立来源记录。向量导出已消除旧版本重复,但上传身份幂等、部分远端
写入失败后的无损重试、跨实例图片资源打包仍需独立验证。
5. 当前版本已补充自动增量链路:服务启动立即补扫,之后默认每 30 秒扫描全部普通
知识库;Wiki 抽取完成后无需手工点击向量化,并自动以来源快照覆盖同步到知识梳理总库。
6. 回流当前只允许新建空 Wiki 普通库,避免覆盖已有页面。普通库现有页面列表的超大规模
分页与助手问答的最终生成回答不属于这轮通过结论。
## 自动化检查
- 后端相关测试:118 passed(assistant repository、Wiki sync、package roundtrip、
WeKnora 接口、向量检索与知识库管理)。
- 修改的 Python 功能文件 Ruff 检查通过;app.py 既有启动顺序导致的 E402 不在此次改动中处理。
- `git diff --check` 通过(只有仓库换行提示)。
- 前端完整 `pnpm typecheck` 未通过:57 个既有/本轮范围外错误,涉及 nextra 缺失、Markdown
插件类型及其他模块;本轮新建/修改的 Wiki 组件、助手 API、技能面板没有出现在错误清单中。
浏览器实际页面与跳转已验证,但不能声称全项目构建通过。
@@ -0,0 +1,188 @@
# 报告协作工作台 — 交互重设计方案(v2)
> 日期:2026-09-07
> 状态:**待确认**(确认后按 §7 分期实施)
> 取代:现行「规则澄清 + 三张模板方案卡 + 画布为主」的交互
> 原则:回到最初设计 —— **协调者智能体全程与用户对话的主线**
---
## 1. 设计目标与总体形态
用户视角只有一条主线:**和一个规划师智能体对话 → 它摸清意图并规划工作流 → 确认后执行、过程在流程图上可见 → 随时插话干预、指定智能体重跑 → 最终成稿、继续对话式修改**。
```
┌─────────────────────────────────────────────────────────┐
│ 阶段1 计划模式 阶段2/3 执行+干预 阶段4 成稿 │
│ ┌───────────┐ ┌───────────┐ ┌──────────┐ │
│ │ 规划师对话 │ 确认 │ 聊天时间线 │ 审稿过 │ 对话改稿 │ │
│ │ 需求摘要卡 │ ─────→ │ +成员Tab │ ─────→ │ 版本/diff│ │
│ │ 工作流卡 │ 冻结 │ +流程图动画│ 报告卡 │ 上游重跑 │ │
│ └───────────┘ └───────────┘ └──────────┘ │
│ ↑ 回到计划模式(replan)随时可触发 ↑ │
└─────────────────────────────────────────────────────────┘
```
与现行的核心差异:
| 维度 | 现行(做偏的) | v2 |
|------|----------------|-----|
| 需求澄清 | 规则模板问句秒回 | 规划师智能体(LLM)领域化提问 |
| 工作流来源 | 3 张确定性模板 | LLM 按意图生成自定义 DAG,对话迭代修改 |
| 执行展示 | 画布为主、聊天为辅 | **聊天时间线为主**(成员 Tab 切换保留),**执行状态在流程图上动画呈现** |
| 干预 | 命令确认卡体系 | 输入框常开 → 协调者智能体分类;支持**指定智能体重跑** |
| 报告修改 | 改写候选(纯文字层) | 意图分流:润色走改写;**事实性问题触发上游节点重跑** |
---
## 2. 阶段 1:计划模式 — 需求梳理与工作流规划
### 2.1 计划模式状态
- 用户发出第一条需求消息起进入**计划模式**,Header 显示徽章「📋 计划模式」,输入框提示语切换为「梳理需求中,你可以随时补充或纠正…」。
- 计划模式在**工作流确认冻结**时结束;执行期大改可通过 replan 回到计划模式(见 §5.4)。
### 2.2 规划师对话(LLM,替换现行规则路由)
- 必须调用独立内置智能体 `report-collaboration-intent` 的真实 LangGraph thread,并原样消费 `messages-tuple` SSE;禁止用 `ainvoke()` 等待完整 JSON 后再包装成一话。
- 流式正文、思考内容和工具步骤直接进入工作台现有 `MessageList`,与普通智能体问答采用同一套步骤条 / Reasoning 展示。
- 模型:`report_collaboration.planner_model`(回退 `coordinator_model` → 默认模型,复用现有 model_resolver 冻结机制)。
- 提问是领域化的:写市场分析就问「关注整车还是供应链 / 要不要竞品对比 / 给谁看 / 篇幅」,而非固定模板问句。
- 澄清轮次上限沿用 `max_clarification_rounds`;用户随时可以说「直接开始」按默认假设推进。
### 2.3 需求摘要卡
信息足够后,规划师在对话中输出一张**结构化需求摘要卡**(业务卡插槽渲染):主题 / 目的 / 读者 / 角度清单 / 篇幅与重点 / 默认假设。用户逐项看,可在对话里继续改(「读者改成投资人」→ 卡片更新出新版)。
### 2.4 工作流生成与迭代
- 规划师判断意图信息足够后,立即按已确认意图自动生成**一份**工作流方案(不是三选一),不再要求用户额外点击“确认意图/生成方案”;方案以工作流卡(成员构成、各节点使命、预计时长/成本)+ 右侧流程图预览呈现。
- 生成约束(保证可执行):**节点类型受词表约束** — `research / verify / analyze / write / review`;自由的是结构(几条线、并行还是串行、核验深度)与每个节点的使命/角度/工具。
- 对话式修改:「把价格策略单独拆一条线」「加一个政策风险分析」→ 规划师修订后出新版工作流卡(带变更说明),再确认。
- 「开始执行」→ 冻结需求 + 工作流(含模型/预算快照,沿用现有 freeze 机制)。
---
## 3. 阶段 2:执行 — 过程在流程图上可见
### 3.1 流程图动画规格(执行状态的可视化主载体)
节点状态 → 样式(`ReportFlowCanvas` / `ReportFlowNode`,状态源 `selectNodeStatusMap`,事件源 `node.status.changed` SSE):
| 状态 | 节点样式 | 连入边 |
|------|----------|--------|
| planned / queued | 灰色常态 | 静态 |
| **running** | 主色描边 + **呼吸脉冲动画**(ring)+ 角标 spinner | **流动动画**(@xyflow 内置 `animated: true` 的 dash 流动) |
| **completed** | 绿色 + ✓ 角标,常驻 | 恢复静态实线 |
| failed | 红色 + 错误角标 | 红色虚线 |
| awaiting_input | 橙色 + ?角标 | 橙色静态 |
| interrupted / superseded | 半透明降级 | 灰 |
- 语义:**边流动 = 数据正在流入该节点**(该节点执行中);节点完成即停动画换完成样式。
- 实现成本:@xyflow 原生支持 animated 边与自定义节点 className,纯 CSS 补呼吸动画即可,无需新依赖。
### 3.2 聊天区(保留现有 Tab 切换)
- 并行成员沿用 **AgentStageSelector Tab 切换**(维持现状,不改交错时间线)。
- 每个 Tab 内是该成员的完整流式输出:思考、工具调用卡、检索过程、正文 —— 与普通智能体问答同观感(message-adapter 已具备映射能力)。
- 协调者播报(「研究线 A 完成,核心发现 X,开始交叉核验」)进入总时间线 Tab。
---
## 4. 阶段 3:干预 — 对话即干预 + 指定重跑
### 4.1 输入框常开,协调者智能体分流
执行期输入框永远可用,所有输入先到协调者(LLM 分类,规则高精度直判兜底——复用 RC-BE-014 IntentRouter 骨架,把「规则优先」反转为「LLM 优先 + 规则快路径」):
| 用户意图 | 协调者行为 | 生效时机 |
|----------|-----------|----------|
| 提问(「查到什么了?」) | 直接回答 | 立即,不打断 |
| 修正方向(「重点看下沉市场」) | 小改:在安全点注入 overlay,并回复说明 | 下个安全点 |
| **指定重跑**(见 §4.2) | 出确认卡 → 重开节点 attempt | 确认后 |
| 大改(砍线/加线) | 提议 replan → 回计划模式 | 用户确认 |
| 停止 | 直接取消 | 立即 |
### 4.2 指定智能体重跑(新增核心交互)
**场景**:用户发现某节点传给下游的信息不对(如研究线 B 的结论有误)。
两个入口,同一后端动作:
1. **对话**:「研究线 B 的结果不对,重新查,重点看价格战」→ 协调者解析出 `node_id` + 新指令;
2. **画布**:点节点 → 抽屉(NodeInspectorDrawer)加「↻ 重新执行」按钮。
**级联规则**(复用并补全 ledger 的 targeted rework):
```
重跑节点 N(attempt+1,带用户新指令作为 mission overlay)
→ N 完成 → 所有直接/间接依赖 N 产物的下游节点标记 superseded
→ 按 DAG 依赖序逐波重跑(边动画重新流动,用户全程可见)
→ 受影响章节重新生成
```
- 确认卡必须展示**影响面**:「将级联重跑 核验B / 分析 / 写作 共 3 个节点,预计 +8 分钟」。
- 成本保护:级联重跑计入 run 预算(现有 budget 机制),预算不足时明确报因。
---
## 5. 阶段 4:报告成稿与对话式修改
### 5.1 成稿
审稿通过 → 报告卡出现在时间线(点开 ReportPreviewDialog 全文),协调者播报总结(各线核心发现 → 结论如何形成)。
### 5.2 修改意图分流(协调者 LLM 判定)
| 用户说 | 判定 | 动作 |
|--------|------|------|
| 「把第二章结论写得更笃定」 | 文字润色 | 改写候选(现有 rewrite API)→ 对比预览 → apply → 新版本 |
| 「这个数据不对 / 重新收集一下」 | **事实性问题** | 定位来源节点 → **触发该节点重跑(可带新角度)→ 级联 §4.2 → 受影响章节重生成 → 新版本** |
| 「再从供应链角度补一节」 | 结构性新增 | replan:回计划模式改工作流(只增不删已完成部分) |
| 「恢复到上一版」 | 版本回退 | 现有 restore API |
- 版本链:每次 apply / restore / 级联重跑产出均产生新 head,历史完整保留(现有机制)。
- 修改对话里协调者必须先**复述理解**(「你是想让研究线 B 换个角度重查,然后重写第三章,对吗?」)再执行,避免误触发昂贵的级联。
---
## 6. 工程改造清单
### 6.1 保留(本次联调已验证)
- 持久化 13 张表 + durable SSE + `after_seq` 断线续传;幂等键 / 乐观锁 / 预算框架。
- MessageList 复用链路(message-adapter → LangGraph Message → 业务卡插槽)。
- AgentStageSelector Tab、commands 安全点骨架(RC-BE-014)、报告版本三件套(RC-BE-015)、前端 hooks/reducer/selectors 主体。
- 「清空历史」「生成候选方案」等本次新增入口。
### 6.2 后端重做 / 新建
| 项 | 说明 |
|----|------|
| 规划师智能体 | LLM 多轮澄清(替换 `requirement/intent_router.py` 规则路由)+ 需求摘要结构化 + **工作流 LLM 生成**(受 §2.4 词表约束,替换 `planning/controller.py` 三模板)+ 对话式工作流修订 |
| 协调者智能体升级 | 干预分类 LLM 优先;修改意图分流(§5.2);执行前复述确认 |
| 指定重跑 + 级联 | ledger targeted rework 补全级联 superseded/重跑波次;确认卡带影响面计算 |
| AgentScope 适配器修复 | `count_tokens` 协议缺失(上下文压缩必崩);attempt 耗尽终态化(防僵尸 run) |
| 事件补齐 | 边流动所需的节点状态事件已有;补「级联影响预告」事件 |
### 6.3 前端重做 / 新建
| 项 | 说明 |
|----|------|
| 计划模式 UI | Header 徽章、composer 提示切换、需求摘要卡、工作流卡(替换现三张模板卡) |
| 流程图动画 | running 节点呼吸动画 + 入边 `animated`、completed ✓ 样式、级联重跑时边动画重流(§3.1) |
| 节点抽屉 | 加「重新执行」按钮 → 确认卡 |
| 干预交互 | 执行期输入框常开;意图确认卡(含影响面);修改对话复述确认 |
| 布局 | 聊天为主、画布为辅的默认比例微调(不动整体两栏结构) |
---
## 7. 实施分期
| 期 | 内容 | 验收 |
|----|------|------|
| **P0 地基** | AgentScope `count_tokens` 修复;attempt 耗尽终态化;级联重跑的 ledger 补全 | 执行阶段不崩、无僵尸 run、单节点重跑级联正确(后端测试) |
| **P1 计划模式 + 执行可视化** | 规划师 LLM 对话、需求摘要卡、工作流 LLM 生成与迭代、计划模式 UI、流程图动画 | 从新会话到确认工作流全程对话完成;执行时动画正确随状态流转 |
| **P2 干预 + 成稿修改** | 协调者 LLM 分类、指定重跑(两入口 + 影响面确认卡)、报告修改意图分流、上游级联重跑 | 四类干预各自走通;「数据不对→重跑→新章节→新版本」全链路 |
每期结束跑 RC 全量测试(当前基线 233 通过)+ 前端 26 单测,不引入对其他模块的影响。
@@ -1,13 +1,25 @@
# AgentScope 2.0 多智能体报告协作工作台——前端实施计划
> 文档状态:待评审、尚未开始编码
> 编写日期:2026-09-02
> 文档状态:前端代码已落地、契约冻结;联调等待后端运行中干预与报告改写
> 编写日期:2026-09-02;进度更新日期:2026-09-06
> 目标项目:`F:\react01\deeflow-code\deerflow-server\frontend-web`
> 视觉与交互参考:`F:\react01\coze-frontend\frontend\apps\coze-studio`
> 参考页面:`http://localhost:3000/work_flow?space_id=1&workflow_id=wf_88cb9f4c6642`
> 配套后端计划:`docs/agentscope-多智能体报告协作工作台-后端实施计划.md`
> 本文只规划新增页面,不修改现有多智能体会商页面,不复用旧 Workflow Studio 执行状态。
## 0. 当前进度
`frontend-web/src/report-collaboration/` 已实现两栏工作台、会话/方案/run API 客户端、mock driver、SSE reducer、`MessageList` 适配、方案卡、画布、节点抽屉、Composer、报告预览与历史。`RC-FE-001`~`013` 的页面骨架与契约层视为完成。
后端已通规划前路径(澄清 → 出方案 → 选定 → `queued` run),见后端计划 §0。前端真实执行体验仍受阻:
- `GET /runs/{id}/stream` 已挂载(`RC-BE-011`);dispatcher/租约已落地(`RC-BE-012`),默认 `worker_enabled=false` 仍不自动领取 queued run。
- 报告改写 / 恢复接口已由后端 `RC-BE-015` 挂载。
- 质量角色已由 `QualityRoleKernel` 跑通(`RC-BE-013`);真实 AgentScope 模型/`web_search` 仍待后续票。
联调顺序:先打开 `report_collaboration.enabled` 验证规划前 REST 与 `GET /runs/{id}/stream` 重放;画布自动执行需同时打开 `worker_enabled`(默认关)。
## 1. 前端目标
新增独立的「报告协作」工作台。用户可以通过对话提出任意报告需求,查看系统生成的多个协作方案,点击方案卡片在流程图中预览协作结构,确认后观察多智能体执行,并在全过程通过统一对话框提出修改意见。
@@ -662,6 +674,8 @@ POST /api/report-collaboration/sessions/{session_id}/report-rewrites/{id}/appl
## 9. 前端开发顺序
页面与契约层已完成(代码在 `frontend-web/src/report-collaboration/`):
1. `RC-FE-001`:视觉截图与行为基线。
2. `RC-FE-002`:路由、菜单和左对话/右画布两栏空壳。
3. `RC-FE-003`:页面 token、弹窗、抽屉和响应式。
@@ -678,15 +692,17 @@ POST /api/report-collaboration/sessions/{session_id}/report-rewrites/{id}/appl
依赖规则:
- `RC-FE-004` 必须等待前后端契约冻结。
- 页面骨架和视觉可与后端 BE-0~BE-3 并行。
- 真实 SSE 联调必须等待后端 durable event store。
- 报告版本操作必须等待后端候选应用语义完成。
- 契约已冻结为 `src/report-collaboration/api/types.ts`;后端 `RC-BE-001` 已对齐。
- 真实 SSE 联调:后端 `RC-BE-011` 已挂载 `GET /runs/{id}/stream`;默认无后台循环时流只会重放已入库事件。
- 报告版本操作已接后端 `RC-BE-015`(改写候选 / apply / restore)。
- 执行画布实时状态依赖打开 `worker_enabled`;质量角色内核已在 `RC-BE-013` 落地,真实成员见 `RC-BE-017`。
---
## 10. 前端完成定义
页面与 mock 联调路径已落地。真实后端可联调 `GET /runs/{id}/stream`、领取/恢复(`RC-BE-012`,默认不挂 worker)、质量角色(`RC-BE-013`)以及报告改写/恢复(`RC-BE-015`)。此前以 mock driver 覆盖交互,不以「前端未编码」计未完成。
前端只有同时满足以下条件才算完成:
1. 页面结构与 Coze 参考页面逐状态验收通过。
@@ -0,0 +1,485 @@
# 报告协作工作台 — 前端对接指南
> **适用版本:** RC-BE-000~018 已落地(2026-09-06)
> **配套文档:**
> - [前端实施计划](./agentscope-多智能体报告协作工作台-前端实施计划.md)
> - [后端实施计划](./agentscope-多智能体报告协作工作台-后端实施计划.md)
> - 模块 README:`frontend-web/src/report-collaboration/README.md`
本文说明**前端如何对接真实后端**:环境配置、API 契约、用户流程、状态机、错误处理与联调验收。前端 API 客户端与页面已实现,通常**无需再写一套 HTTP 层**。
---
## 1. 结论速览
| 问题 | 答案 |
|------|------|
| 前端还要写接口吗? | **不用**。`src/report-collaboration/api/` 已封装 REST + SSE,页面通过 `getReportCollaborationDriver()` 调用。 |
| 为什么调不通? | 最常见:`config.yaml` 里 `report_collaboration.enabled: false` → 全部 **503**;或 `worker_enabled: false` → run 不入队执行。 |
| Mock 和真后端怎么切? | **Mock 已移除**。前端永远请求真实后端(`deerflow.rc-mock` / `VITE_RC_MOCK` 开关已删除);联调需 `enabled: true`。 |
| 字段会和 MySQL 对不上吗? | 前端只认 JSON 契约(`api/types.ts`),不直连表。缺列表现为后端 500/SQL 错误,部署后需**重启 Gateway** 跑 `create_all` + 列同步。 |
| 从哪进页面? | `/page/workspace/report-collaboration`(新会话)或 `/page/workspace/report-collaboration/:sessionId`。侧边栏入口默认注释,可直接 URL 访问。 |
---
## 2. 架构:前端已有什么
```
ReportCollaborationPage
└─ useReportCollaborationSession(唯一数据入口)
├─ driver.getSnapshot() ← REST 冷启动
├─ driver.openRunStream() ← SSE 实时事件
├─ driver.sendMessage / createCommand / startRun …
└─ event-reducer ← 事件 → UI 状态
ReportConversationPanel
└─ MessageList(message-adapter 映射 CollaborationMessage → LangGraph Message)
```
### 2.1 目录结构
```text
frontend-web/src/report-collaboration/
api/
types.ts # 冻结契约(snake_case,与后端 Pydantic 一致)
client.ts # rcFetch:鉴权、幂等键、乐观锁、错误映射
sessions.ts # 会话 CRUD、消息
plans.ts # 方案请求 / 列表 / 选定
runs.ts # Run、SSE、命令、节点合同
reports.ts # 报告版本、改写、恢复
driver.ts # 真实后端统一门面(Mock 驱动已移除)
state/
event-normalizer.ts
event-reducer.ts
selectors.ts
message-adapter.ts
hooks/
useReportCollaborationSession.ts # 快照 → SSE → reducer → 动作
useReportCollaborationStream.ts # 断线重连、after_seq 续接
useReportCollaborationMessages.ts
components/ …
pages/ReportCollaborationPage.tsx
```
### 2.2 事实源原则
- **冷启动:** `GET /sessions/{id}` 返回 `SessionSnapshot`(session、messages、plans、run、nodes、agent_runs、commands、report_versions、last_seq)。
- **运行中:** `GET /runs/{id}/stream?after_seq=N` 订阅 durable SSE。
- **断线 / 序号缺口:** `GET /runs/{id}/events?after_seq=N` REST 补拉。
- **澄清 / 规划阶段(尚无 run):** 每 2.5s 轮询快照(后端事件主要在 run 流上)。
纯展示状态(预览方案、聚焦成员、面板宽度)**不**写入服务器,仅存前端。
---
## 3. 后端启用与部署
### 3.1 必改配置
文件:`offline-backend-20260512/backend/config.yaml`
```yaml
report_collaboration:
enabled: false # 联调必须改为 true
worker_enabled: false # 要看成员执行必须 true
planner_model: null # 建议填可用模型名
coordinator_model: null
research_model: null
writer_model: null
reviewer_model: null
# … token_budget、max_concurrent_runs_per_user 等见同文件
```
| 配置 | 现象 |
|------|------|
| `enabled: false` | 所有路由 **503**,`code: REPORT_COLLABORATION_DISABLED` |
| `enabled: true`,`worker_enabled: false` | REST/SSE 可用,run 入队但不自动执行(测 API 可用,无成员流式输出) |
| 两者 `true` + 模型配置完整 | 完整联调 |
### 3.2 MySQL / 表结构
生产环境 Alembic 自动升级默认关闭,启动时走 `create_all` + `_ensure_orm_columns_sync`。
- 迁移脚本(可选手动跑):`20260905_01`、`20260906_01`、`20260907_01`
- ORM:`packages/harness/deerflow/persistence/report_collaboration/model.py`(13 张表)
- **部署后务必重启 Gateway**,观察日志是否有 `Failed to auto-add column report_collaboration_*`
已有 RC-003 时代旧表会通过列同步补上 `template_snapshot_json`、`budget_snapshot_json`、`usage_json`、`budget_exhausted_reason` 等可空列,并创建 `rewrite_proposals`、`audits`。
### 3.3 前端环境变量
| 变量 | 说明 |
|------|------|
| `VITE_BACKEND_BASE_URL` | Gateway 地址,默认 `http://127.0.0.1:8001` |
请求经 `apiFetch`,自动携带登录 JWT(`credentials: "include"`)。**必须先登录 DeerFlow**。
---
## 4. API 基址与请求约定
### 4.1 Base URL
```text
{VITE_BACKEND_BASE_URL}/api/report-collaboration
```
代码常量:`REPORT_COLLABORATION_BASE()`(`api/client.ts`)。
### 4.2 请求头
| Header | 何时携带 | 说明 |
|--------|----------|------|
| `Authorization` | 全部 | 由 `apiFetch` 注入 |
| `X-Idempotency-Key` | 写操作 | 格式 `rc-{uuid}`;重复提交返回首次结果 |
| `X-Expected-Revision` | 选方案、开 run、改会话等 | 会话 `requirement_revision` 或方案 `revision` |
| `Accept: text/event-stream` | SSE | `openRunStream` |
### 4.3 错误响应格式
```json
{
"code": "REPORT_COLLABORATION_DISABLED",
"message": "报告协作工作台未启用",
"detail": { }
}
```
前端映射(`api/client.ts`):
| HTTP | 异常类 | 典型场景 |
|------|--------|----------|
| 409 | `ConflictError` | revision 过期、active run 冲突 |
| 422 | `ValidationError` | 参数/语义错误、缺幂等键 |
| 429 | `RateLimitError` | 预算耗尽、每用户并发 run 上限 |
| 5xx | `ServerError` | 可带同一幂等键重试 |
| 0 | `NetworkError` | fetch 失败 |
### 4.4 完整路由清单(与前端 client 对齐)
| 方法 | 路径 | 前端模块 |
|------|------|----------|
| POST | `/sessions` | `sessions.createSession` |
| GET | `/sessions` | `sessions.listSessions` |
| GET | `/sessions/{id}` | `sessions.getSessionSnapshot` |
| PATCH | `/sessions/{id}` | `sessions.updateSession` |
| DELETE | `/sessions/{id}` | `sessions.deleteSession` |
| GET | `/sessions/{id}/messages` | `sessions.listMessages` |
| POST | `/sessions/{id}/messages` | `sessions.sendMessage` |
| POST | `/sessions/{id}/plan-requests` | `plans.requestPlans` |
| GET | `/sessions/{id}/plans` | `plans.listPlans` |
| POST | `/sessions/{id}/plans/{planId}/select` | `plans.selectPlan` |
| POST | `/sessions/{id}/runs` | `runs.startRun` |
| GET | `/sessions/{id}/reports` | `reports.listReportVersions` |
| POST | `/sessions/{id}/report-rewrites` | `reports.createRewrite` |
| POST | `/sessions/{id}/report-rewrites/{id}/apply` | `reports.applyRewrite` |
| POST | `/sessions/{id}/report-versions/{id}/restore` | `reports.restoreReportVersion` |
| GET | `/runs/{id}` | `runs.getRun` |
| GET | `/runs/{id}/events` | `runs.listRunEvents` |
| GET | `/runs/{id}/stream` | `runs.openRunStream` |
| POST | `/runs/{id}/cancel` | `runs.cancelRun` |
| POST | `/runs/{id}/commands` | `runs.createCommand` |
| POST | `/runs/{id}/commands/{id}/confirm` | `runs.confirmCommand` |
| POST | `/runs/{id}/commands/{id}/cancel` | `runs.cancelCommand` |
| GET | `/runs/{id}/nodes/{nodeId}/contract` | `runs.getNodeContract` |
| GET | `/runs/{id}/artifacts` | (后端已暴露,前端按需接) |
| GET | `/runs/{id}/sources` | (后端已暴露,前端按需接) |
---
## 5. 用户流程与 API 时序
```mermaid
sequenceDiagram
participant U as 用户
participant FE as 前端 RC
participant API as Gateway
participant W as Worker
U->>FE: 输入需求
FE->>API: POST /sessions
FE->>API: POST /sessions/{id}/messages
API-->>FE: 澄清(快照 messages)
U->>FE: 回答澄清
FE->>API: POST /messages
FE->>API: POST /plan-requests
API-->>FE: 候选方案(快照 plans)
U->>FE: 采用方案
FE->>API: POST /plans/{id}/select
U->>FE: 开始协作
FE->>API: POST /runs
FE->>API: GET /runs/{id}/stream
W-->>API: 持久化事件
API-->>FE: SSE
U->>FE: 运行中干预
FE->>API: POST /commands → confirm
```
### 5.1 阶段 0:进入页面
| 路由 | 行为 |
|------|------|
| `/page/workspace/report-collaboration` | 空态;`POST /sessions` 后跳转带 `sessionId` |
| `/page/workspace/report-collaboration/:sessionId` | 加载快照;有 `location.state.rcInitialMessage` 时自动发首条消息 |
路由注册:`frontend-web/src/pages/WorkspaceRoutes.tsx`。
侧边栏入口已在 `workspace-nav-chat-list.tsx` 中放开(「报告协作」项),也可直接访问 URL。
### 5.2 阶段 1:需求澄清(无 run)
```http
POST /api/report-collaboration/sessions
POST /api/intent/init
POST /api/intent/stream
POST /api/report-collaboration/sessions/{sessionId}/intent-turns
```
- 报告页面固定调用内置 `report-collaboration-intent` 智能体;`/api/intent/stream` 原样透传 LangGraph `messages-tuple`,不是等待模型完成后包装响应。
- 正文 token、provider reasoning 和工具调用步骤实时写入当前 `MessageList` 气泡;终态后 `/intent-turns` 只负责持久化镜像与状态交接,不再调用模型识别意图。
- 旧 `/sessions/{sessionId}/messages` 在 `empty/clarifying` 阶段返回 `409 USE_STREAMING_INTENT`,防止退回阻塞式 `ainvoke` 路径。
- 会话状态:`empty` → `clarifying` → `planning` → `proposal_ready`。
- 澄清问题以 `CollaborationMessage` 进入时间线;业务卡通过 `MessageList` 的 `businessCardSlot` 渲染(`additional_kwargs.report_collaboration_card`)。
- 意图智能体输出 `INTENT_READY` 后,`POST /intent-turns` 自动创建并完成 `plan_request`;前端无需再展示“确认意图”或“生成候选方案”按钮。
### 5.3 阶段 2:候选方案
```http
# 正常路径:由最后一轮 POST /messages 自动触发规划
POST /api/report-collaboration/sessions/{sessionId}/plans/{planId}/select
```
- `POST /plan-requests` 仅保留给“重新生成”与兼容调用,不是首次规划的必经步骤。
- 规划师尚未判定 `ready` 时直接调用该兼容接口会返回 `422 REQUIREMENT_NOT_READY`,防止跳过意图确认。
- **预览方案**(`previewPlanId`):纯前端,不调 API。
- **采用方案**:`select` 写 `selected_plan_id`,**不**创建 run。
- 409:方案 revision 过期 → 前端 `refreshSnapshot`,提示用户重选。
- 右栏 `ReportFlowCanvas` 使用 `PlanNode` / `PlanEdge` 渲染 DAG。
### 5.4 阶段 3:开始执行与 SSE
```http
POST /api/report-collaboration/sessions/{sessionId}/runs
{ "plan_id": "plan-xxx" }
GET /api/report-collaboration/runs/{runId}/stream?after_seq=0
Accept: text/event-stream
```
SSE 每条 `data:` 为 JSON:
```typescript
interface CollaborationEvent {
eventId: string;
sessionId: string;
runId?: string | null;
seq: number;
type: CollaborationEventType;
timestamp: string;
data: unknown;
}
```
常用 `type`(完整列表见 `api/types.ts`):
| 类型 | 含义 |
|------|------|
| `message.created` / `message.delta` / `message.completed` | 对话镜像流式 |
| `tool_call.*` / `tool_result.*` | 工具调用与返回 |
| `team_message.*` | 成员 / 协调者广播 |
| `plan.proposed` / `plan.selected` | 方案生命周期 |
| `run.status.changed` | Run 状态 |
| `node.status.changed` / `node.progress` | 节点执行 |
| `command.confirmation.required` | 需用户确认的干预 |
| `report.version.created` | 报告版本落盘 |
| `heartbeat` | 保活 |
### 5.5 阶段 4:运行中干预
```http
POST /api/report-collaboration/runs/{runId}/commands
POST /api/report-collaboration/runs/{runId}/commands/{commandId}/confirm
POST /api/report-collaboration/runs/{runId}/commands/{commandId}/cancel
POST /api/report-collaboration/runs/{runId}/cancel
```
- Run 活跃时 `send()` 自动走 `createCommand`(而非 `sendMessage`)。
- 可从节点抽屉 / `AgentStageSelector` 传入 `target_node_id`、`agent_run_id`。
- `InterventionCard` 展示 `confirmation_required` 命令;确认后调 `confirmCommand`。
`CommandIntent` 枚举(后端分类,前端展示 reason/impact):`answer_question`、`update_requirements`、`research_again`、`rewrite_section`、`rerun_node`、`replan`、`cancel` 等。
### 5.6 阶段 5:报告版本与改写
```http
GET /api/report-collaboration/sessions/{sessionId}/reports
POST /api/report-collaboration/sessions/{sessionId}/report-rewrites
POST /api/report-collaboration/sessions/{sessionId}/report-rewrites/{rewriteId}/apply
POST /api/report-collaboration/sessions/{sessionId}/report-versions/{versionId}/restore
```
- `createRewrite` 只生成候选,不直接覆盖正式版本。
- `applyRewrite` / `restoreReportVersion` 产生新的 head 版本,历史保留。
- `ReportPreviewDialog` 展示 `ReportVersion.markdown`。
---
## 6. 前端状态机(接入方须知)
**不要**在页面里用零散 REST 字段拼 UI 状态。统一路径:
```text
SessionSnapshot → applySessionSnapshot()
CollaborationEvent[] → normalizeCollaborationEvent() → applyCollaborationEvents()
→ selectors(derivePagePhase、selectPlanForCanvas、…)
```
### 6.1 核心 Selector
| Selector | 用途 |
|----------|------|
| `derivePagePhase` | Composer 占位、方案卡、空态 |
| `selectPlanForCanvas` | 画布 DAG(含预览方案) |
| `selectAgentTabs` | 并行成员 Tab |
| `selectNodeStatusMap` | 节点颜色 / 状态 |
| `selectPendingConfirmationCommands` | 干预确认卡 |
| `selectHeadReportVersion` | 报告预览入口 |
| `selectStreamingAgentRunIds` | 流式成员指示 |
### 6.2 SSE 连接策略(`useReportCollaborationStream`)
- Run 非终态时连接;`completed` / `failed` / `cancelled` 后关流并 REST 最终补拉。
- 断线:指数退避重连(1s~30s),`after_seq` 取 reducer 内 `lastSeq`。
- `hasSeqGap`:暂停 live 应用,先 `listRunEvents` 回填。
### 6.3 乐观消息
用户发送时插入 `metadata.optimistic: true` 的 pending 消息;服务端镜像带相同 `idempotency_key` 时 reducer 原地替换。
---
## 7. 消息与 MessageList 复用
`useReportCollaborationMessages` 将 `CollaborationMessage[]` 转为 `@langchain/langgraph-sdk` 的 `Message[]`,并构造合成 `BaseStream` 供 `MessageList` 消费。
| 来源 | 渲染 |
|------|------|
| `human` / `ai` 文本 | 普通气泡 |
| `tool_calls` + `tool` | 现有工具卡 |
| `team_message` 事件 | 带 `name` 的成员发言 |
| 业务卡 | `businessCardSlot`(方案组、命令确认、报告可用) |
分组键:`core/messages/utils.ts` 中 `assistant:task-report-import` 同级扩展 `assistant:business-card`,判据 `additional_kwargs.report_collaboration_card`。
### 7.1 在其他页面嵌入 RC(最小集)
1. `useReportCollaborationSession(sessionId)`
2. `ThreadContext.Provider` + `MessageList` + `businessCardSlot`
3. 引入 `report-collaboration/styles/report-collaboration.css`
---
## 8. 单测
Mock 驱动(原 `api/mock.ts`)与 `deerflow.rc-mock` / `VITE_RC_MOCK` 开关已移除,前端只请求真实后端;本地演示请直接联调后端。
单测:
```bash
cd frontend-web
pnpm test:report-collaboration
```
---
## 9. 联调验收清单
### 9.1 准备
- [ ] `report_collaboration.enabled: true`
- [ ] `report_collaboration.worker_enabled: true`
- [ ] 五个 `*_model` 已配置且可用
- [ ] Gateway 已重启(MySQL 表/列就绪)
- [ ] 前端已登录(JWT 有效)
### 9.2 探活
```bash
curl -s -H "Authorization: Bearer <token>" \
http://127.0.0.1:8001/api/report-collaboration/sessions
```
期望:**200** + `{ "sessions": [...] }`,而非 503。
### 9.3 页面流程(浏览器 Network)
1. 打开 `/#/page/workspace/report-collaboration`
2. 输入需求 → `POST .../sessions` **200**
3. 跳转 → `GET .../sessions/{id}` **200**,响应含 `SessionSnapshot`
4. 首条消息 → `POST .../messages` **200**
5. 请求方案 → `POST .../plan-requests` **200**
6. 采用方案 → `POST .../plans/{id}/select` **200**
7. 开始协作 → `POST .../runs` **200**
8. `GET .../runs/{id}/stream` 持续收到 SSE(`message.delta`、`node.status.changed` 等)
9. 刷新页面 → 消息与 `last_seq` 续接正确
10. 运行中输入「重新检索」→ `POST .../commands` → 确认卡 → `confirm` **200**
### 9.4 常见失败
| 现象 | 原因 | 处理 |
|------|------|------|
| 全部 503 | `enabled: false` | 改配置并重启 |
| run 一直 queued | `worker_enabled: false` | 改为 true |
| 409 选方案 | revision 过期 | 刷新后重选(前端已自动 refresh) |
| 429 | 预算/并发上限 | 调大配置或取消其他 run |
| 500 + SQL | 表缺列 | 重启 Gateway,查列同步日志 |
---
## 10. 契约与类型维护
- **单一事实源:** `frontend-web/src/report-collaboration/api/types.ts`
- **后端镜像:** `offline-backend-20260512/backend/app/report_collaboration/contracts/`
- 修改字段须同步两份实施计划与本指南,并跑前后端相关测试。
`SessionSnapshot` 形状(摘要):
```typescript
interface SessionSnapshot {
session: ReportSession;
messages: CollaborationMessage[];
plans: ReportPlanCandidate[];
run: ReportRun | null;
nodes: CollaborationNodeState[];
agent_runs: AgentRun[];
commands: CollaborationCommand[];
report_versions: ReportVersion[];
last_seq: number;
}
```
`ReportRun` 可选字段(RC-BE-016):`usage`、`budget_exhausted_reason`。
---
## 11. 后续工作(非对接阻塞)
| 项 | 说明 |
|----|------|
| RC-BE-019 | 灰度发布与离线部署文档 |
| 侧边栏入口 | 已放开(`workspace-nav-chat-list.tsx` 的「报告协作」项) |
| iframe 嵌入 | RC 页尚未专门适配 `embed=1`,可参考现有深链模式扩展 |
| 真人盲评 | RC-BE-018 为确定性评测集,上线前仍需计划 §11.4 人工评审 |
---
## 12. 相关代码索引
| 用途 | 路径 |
|------|------|
| API 客户端 | `frontend-web/src/report-collaboration/api/` |
| 会话 Hook | `frontend-web/src/report-collaboration/hooks/useReportCollaborationSession.ts` |
| 页面 | `frontend-web/src/report-collaboration/pages/ReportCollaborationPage.tsx` |
| 路由 | `frontend-web/src/pages/WorkspaceRoutes.tsx` |
| 后端路由 | `offline-backend-20260512/backend/app/gateway/routers/report_collaboration.py` |
| 配置 | `offline-backend-20260512/backend/config.yaml` → `report_collaboration` |
| 持久化 | `offline-backend-20260512/backend/packages/harness/deerflow/persistence/report_collaboration/` |
@@ -1,11 +1,44 @@
# AgentScope 2.0 多智能体报告协作工作台——后端实施计划
> 文档状态:待评审、尚未开始编码
> 编写日期:2026-09-02
> 文档状态:实施中(`RC-BE-000`~`018` 已完成;下一票 `RC-BE-019`)
> 编写日期:2026-09-05;进度更新日期:2026-09-06
> 目标项目:`F:\react01\deeflow-code\deerflow-server\offline-backend-20260512\backend`
> 协作框架:[AgentScope 2.0](https://github.com/agentscope-ai/agentscope)
> 配套前端计划:`docs/agentscope-多智能体报告协作工作台-前端实施计划.md`
> 前端契约来源:`frontend-web/src/report-collaboration/api/types.ts`、`api/mock.ts`、`state/event-reducer.ts`(前端已完成,契约冻结)
> 尖峰基线:`offline-backend-20260512/backend/docs/AGENTSCOPE_BASELINE.md`
> 本文只规划新增的报告协作后端,不修改现有多智能体会商,不使用旧 Workflow Studio DAG 调度器。
> 下文 §8 的 `BE-n` 与 §10 的 `RC-BE-00n` 是同一批工单的两种编号;进度以 `RC-BE-*` 为准。
## 0. 当前进度
已打通 **建会话 → 澄清需求 → 生成 2~3 套候选方案 → 选定 → 创建 `queued` run(冻结模型 + 模板 + 预算)**,以及 **TaskLedger 波次调度 + 受控成员 / 真实 AgentScope 内核 + 主张级 QualityGate + 正式报告版本/改写 + 产品 durable SSE + 租约 dispatcher/恢复 + 运行期自然语言干预 + owner/预算/审计**。主开关默认关闭:`config.yaml → report_collaboration.enabled` / `worker_enabled` 均为 `false`。
运行期 IntentRouter 的歧义兜底已可调用冻结的 coordinator 模型,但明确命令走高精度规则;该智能体无工具且不能调度。`POST /runs` 仍只入队。`worker_enabled` 为 false 时 lifespan **不**挂载后台循环;测试用 `dispatch_once()` / `WaveExecutor` 驱动。`GET /runs/{id}/stream` 可重放/尾随已持久化事件。生产 worker 在双开时注入 `AgentScopeMemberKernel`。20 项质量评测已在 `RC-BE-018` 落地(确定性打分,不跑真实模型)。
| 工单 | 状态 | 代码落点 / 缺口 |
|---|---|---|
| `RC-BE-000` | 条件完成 | `agentscope_runtime/event_adapter.py`。`agentscope==2.0.5` 仅为 optional extra,**未写入 `uv.lock`**(等纵向 POC)。 |
| `RC-BE-001` | 完成 | `app/report_collaboration/contracts/` + `execution/completion.py`。 |
| `RC-BE-002` | 完成 | `deerflow.config.report_collaboration_config`;默认双关。 |
| `RC-BE-003` | 完成 | `deerflow.persistence.report_collaboration/`,迁移 `20260905_01`。 |
| `RC-BE-004` | 完成 | `/api/report-collaboration`。改写 / apply / restore 见 `015`。SSE 见 `011`。 |
| `RC-BE-005` | 完成 | `requirement/`:规划前澄清;「直接开始」记默认假设;澄清完成 ≠ 出方案。 |
| `RC-BE-006` | 完成 | `planning/`:目录投影(无 SOUL/密钥)+ 三策略方案;select 与 start-run 独立。 |
| `RC-BE-007` | 完成 | `DeerFlowChatModelAdapter` + `freeze_plan_models`;缺模型/能力不匹配在 `POST /runs` 前 422。 |
| `RC-BE-008` | 完成 | `permission_adapter.py` + `tool_adapter.py`:角色 × 工具矩阵、注入扫描。 |
| `RC-BE-009` | 完成 | `execution/ledger.py` + `member_runner.py`:TaskLedger 按依赖波次分派;AgentScope/协调者建议不能选下一节点;空输出/非法 JSON 进入新 attempt;超限失败不完成整个 run。内核目前为 `ScriptedMemberKernel`(单测),真实模型/web_search 仍待接。 |
| `RC-BE-010` | 完成 | `execution/quality_gate.py` + `ArtifactBoard.supersede_previous`:冲突数字/缺口径拒绝;未核验或争议主张不能进分析/章节;审稿必须映射到节点或章节;覆盖矩阵缺口拒绝。Ledger 先 schema 再质检,通过才 `validated`。 |
| `RC-BE-011` | 完成 | `execution/live_hub.py`:run-scoped Live Hub、`PublishingReportCollaborationStore`(先入库再发布)、`after_seq`/`Last-Event-ID` 重放、心跳、token delta 合帧、SSE 断开不取消 run。`GET /runs/{id}/stream` 已挂载。报告正式版本见 `015`。 |
| `RC-BE-012` | 完成 | store `list_claimable` / `renew_lease` / `finalize_cancel`;`execution/{dispatcher,executor,recovery}.py`:原子 claim、心跳续租、两阶段取消、过期租约接管。恢复保留已完成节点,遗留 in-flight 标 `interrupted`/`superseded` 并开新 `planned` attempt。晚到成果 `validation_status=superseded`,不改终态。`start()` 仅在 `worker_enabled` 时挂循环;默认双关。 |
| `RC-BE-013` | 完成 | `execution/quality_roles.py`:Verifier/Analyst/Writer/Reviewer/Reviser 作为受控成员(确定性内核,非 AgentScope Team)。核验标记过期/跨包冲突;缺失角度只重开检索链;审稿 `revise` 只重开目标写作节点 + 审稿自身。`TaskResult.additional_drafts` 允许多条 VerificationResult。脚本内核仍供单测;生产 worker 见 `017`。 |
| `RC-BE-014` | 完成 | `interventions/`:运行期 IntentRouterAgent(规则优先、模型兜底且无工具)+ 确定性 ImpactAnalyzer + command CAS/safe-point。支持节点/agent 归属校验、角度定向、确认闸门、等待边界、overlay、新 attempt、成果失效及晚到结果隔离。 |
| `RC-BE-015` | 完成 | `reporting/`:run 开始冻结模板;`ReportFinalizer` 在审稿通过且 lint 后落正式版本并发 `report.version.created`;改写候选 / apply / restore 已挂路由,不再 503。 |
| `RC-BE-016` | 完成 | owner 隔离、跨会话幂等拒绝、`sanitize_for_user` + 审计落库、每用户 active run 上限、每 run 预算 fail-closed。 |
| `RC-BE-017` | 完成 | `AgentScopeMemberKernel` 真实 `reply_stream` + 工具 + 结构化成果;生产 worker 注入该内核;冒烟可跳过未安装的 `agentscope`。并发交错/租约恢复仍由 `012` 测试覆盖。 |
| `RC-BE-018` | 完成 | `app/report_collaboration/eval/`:20 项标注任务(政策/市场/企业/事件/对比各 4)、确定性指标、金标/失败实例、会商盲评对照、20 次固定任务可靠性计数。`cite_required` 与当前默认 knobs 为通过评测的推荐画像。真实模型盲评仍属上线决策,不阻塞本票。 |
| `RC-BE-019` | 未开始 | 灰度与离线发版。 |
当前可联调路径:打开 `report_collaboration.enabled` 后,规划前轮询 `GET /sessions/{id}` 即可看到澄清消息和方案卡;`POST /runs` 只入队;`GET /runs/{id}/stream` 重放已入库事件。默认 **不**自动领取 queued run(`worker_enabled=false`)。测试可调 `dispatch_once()`。
## 1. 后端目标
@@ -14,7 +47,7 @@
1. 理解任意报告需求并在必要时澄清。
2. 从当前用户可见的智能体、技能、模型和资料源中筛选能力。
3. 生成 2~3 个真实有差异的候选协作计划。
4. 将选定计划实例化为 AgentScope leader-worker 团队。
4. 将选定计划实例化为受 `TaskLedger` 约束的 AgentScope 任务成员,而非让自由会商自行决定流程。
5. 通过结构化成果物完成检索、核验、分析、综合、写作、审稿和返工。
6. 接受全过程自然语言干预,并只重开受影响的任务和下游成果。
7. 使用数据库租约、durable events、幂等命令和版本化报告保证恢复与一致性。
@@ -23,11 +56,27 @@
框架能提供协作机制,不能单独保证报告质量。质量由证据合同、来源核验、独立审稿、定向返工、模板约束和真实评测集共同保证。
### 1.1 前端冻结契约:后端必须适配,不要求前端回退
已完成的双栏前端以 `SessionSnapshot` 和 run-scoped durable SSE 为唯一事实来源;后端实现不得另造一套摘要卡或节点结果接口来补足过程。
| 前端已实现行为 | 后端适配要求 |
|---|---|
| run 创建前轮询 `GET /sessions/{id}` | 澄清、需求修订、候选方案与选择结果必须同步写入完整快照;不能只存在于内存任务。 |
| run 后以 `seq` 重放/订阅 SSE | 每个 run 的事件先持久化、再发布,`seq` 严格单调且可用 `after_seq` 补拉。 |
| 并行成员横向阶段卡 | 同一并行波次使用相同 `phase_id`;`node_run_id` 固定为 `${node_id}-attempt{attempt}`,不可使用前端无法反查节点的随机值。 |
| 复用 `MessageList` 展示过程 | assistant、TeamSay、工具调用、工具结果与流式增量均写入 `report_collaboration_messages` 并按原消息协议推送。 |
| 成员状态来自局部快照合并 | 前端没有 `agent_run.*` 事件;成员开始、等待输入、完成、失败、取消时必须发 `session.snapshot: {agent_runs:[...], run?:...}`。 |
| 报告在对话流中渐显 | 写作阶段发 `report.delta`;质量门和版本落库完成后才发 `report.version.created`。 |
| 节点抽屉只读 | `GET /runs/{id}/nodes/{node_id}/contract` 只返回职责合同,永不返回执行结果正文。 |
每个写操作读取前端发送的 `X-Idempotency-Key` 与 `X-Expected-Revision`;用户消息的服务端镜像必须将同一幂等键放入 `metadata.idempotency_key`,以替换前端乐观消息而不重复显示。
---
## 2. 关键架构决策
### 2.1 使用 AgentScope Core,不采用演示服务作为事实源
### 2.1 AgentScope 用作任务执行内核,不把 Agent Team 自主会商当作调度器
AgentScope 官方提供 Event System、Agent Team、任务规划和 Agent Service 示例,但团队可靠性与多进程/分布式服务仍在持续完善:
@@ -36,13 +85,16 @@ AgentScope 官方提供 Event System、Agent Team、任务规划和 Agent Servic
- [Agent Team 可靠性跟踪](https://github.com/agentscope-ai/agentscope/issues/1802)
- [多进程与分布式能力跟踪](https://github.com/agentscope-ai/agentscope/issues/1722)
因此采用:
因此采用下列职责分界:
- AgentScope Core:Agent、Team、Tool、Event、Permission。
- AgentScope Core:单个受控任务成员的模型调用、流式事件、工具调用、权限封装与结构化输出修复。
- DeerFlow `TaskLedger`:计划实例化、依赖拓扑、并发波次、输入成果选择、状态迁移、重试、租约、取消、命令安全点与业务终态。
- DeerFlow `ArtifactBoard` / `QualityGate`:成果物事实源、验收、失效传播、引用约束和报告版本准入。
- `Coordinator`:受限的规划/协调智能体,只能提出 `PlanProposal`、`TaskAssignment`、`RepairRequest` 和 `ClarificationRequest`;它不能自行创建任意图、改变数据库或宣告节点完成。
- AgentScope TeamSay:允许作为可见的协作交接文本,但不是任务分派、完成判定或数据依赖的事实来源。
- DeerFlow:身份、权限、配置、数据库、消息、命令、作业、租约、SSE、成果、文件、报告版本和审计。
- AgentScope Event 既驱动运行状态,也通过无损 adapter 转为 DeerFlow 领域事件和 LangChain 兼容消息;其用户可见内容不能在 adapter 中被压缩为进度摘要。
- 不运行 AgentScope `examples/web_ui`。
- 不把 AgentScope Session、MessageBus 或内存对象作为恢复依据。
- AgentScope Event 通过无损 adapter 转为 DeerFlow 领域事件和 LangChain 兼容消息;用户可见内容不能在 adapter 中压缩为进度摘要。
- 不运行 AgentScope `examples/web_ui`,不使用 AgentScope Session、MessageBus 或内存对象作为恢复/重放依据,也不使用其 session SSE 作为产品 SSE。
- 节点公开接口只返回职责合同、图关系和状态,不返回节点实际输出;实际阶段消息、工具过程和成果仍从会话消息流以原生消息、工具步骤、引用和报告文件卡的形式呈现。
### 2.2 新功能独立,不进入旧运行内核
@@ -63,7 +115,7 @@ AgentScope 官方提供 Event System、Agent Team、任务规划和 Agent Servic
- 展示当前状态和返工范围。
- 供用户理解和选择方案。
真正调度由 `Coordinator + TaskLedger + ArtifactBoard + QualityGate` 完成。协调者只能从服务端允许的动作中选择,不能返回任意 Python、任意图或数据库操作。
真正调度由 `TaskLedger + ArtifactBoard + QualityGate` 完成。协调者只能提出服务端可验证的建议;`TaskLedger` 才能执行分派、依赖解锁、重试和终态迁移,不能返回任意 Python、任意图或数据库操作。
### 2.4 数据库是唯一事实源
@@ -89,12 +141,12 @@ flowchart LR
API --> DISP[Lease Dispatcher]
DISP --> EXEC[Report Collaboration Executor]
EXEC --> AS[AgentScope Core]
AS --> LEAD[Coordinator]
AS --> TEAM[Research/Verify/Analyze/Write/Review Team]
EXEC --> LEDGER[Task Ledger]
EXEC --> BOARD[Typed Artifact Board]
LEDGER --> COORD[Constrained Coordinator]
LEDGER --> AS[AgentScope Core Task Workers]
COORD --> LEDGER
AS --> TEAM[Research / Verify / Analyze / Write / Review]
EXEC --> MODEL[DeerFlow Model Adapter]
EXEC --> TOOL[Allowlisted Tool Adapter]
@@ -290,6 +342,59 @@ completed -> reopened -> queued
- 期望成果物落库、Schema 通过、验收器通过后才能完成。
- 返工创建新 attempt,保留旧 attempt 和旧成果。
### 5.6 `TaskLedger`:智能体协作的确定性边界
每个计划节点被实例化为一条可审计的 ledger task;并行只是多个 ready task 位于同一波次,不是让多个智能体在群聊中自行决定谁做什么。
```text
TaskAssignment
node_id / node_run_id / phase_id / attempt
requirement_revision
allowed_input_artifact_ids[]
expected_artifact_type + acceptance_criteria
agent_profile_snapshot + allowed_tools + budgets
command_overlay_revision
TaskResult
artifact_draft | clarification_request | repair_request | failure
cited_artifact_ids[]
source_ids[]
```
调度规则:
1. `PlanAssembler` 将角色建议转为固定节点、边、成果合同和质量门;只允许无环主图。
2. `TaskLedger` 仅在依赖的成果物均为 `validated`、未 supersede 且预算可用时使节点 ready。
3. 每个成员只读 requirement revision、自己的 `TaskAssignment` 与允许的已验证上游成果;不能把其他成员的原始上下文、工具返回或内部 prompt 当作输入。
4. 并行节点共享 `phase_id`,但各自有独立 `agent_run_id`;串行节点用独立 `phase_id`。成员的 `node_run_id` 一律为 `${node_id}-attempt{attempt}`。
5. 只有 `TaskResult` 转为成果物并通过 `QualityGate` 后,ledger 才解锁下游;TeamSay 与普通文本没有解锁能力。
6. 修复/返工创建新 attempt;旧 attempt 及其消息留存但被标为 `superseded`,不得重新进入下游。
### 5.7 用户命令的约束化协议
每句话先持久化为用户消息和 `CollaborationCommand`,再由独立的**意图理解智能体(`IntentRouterAgent`)**进行两段式判定:规则/上下文高精度路由优先,必要时调用模型并输出受 Pydantic 约束的 `IntentDecision`。它负责理解,不负责调度或执行。
同一能力有两种受控入口:
| 时机 | `IntentRouterAgent` 的工作 | 后续处理 |
|---|---|---|
| 尚未开始 run | 提取主题、目标、受众、范围、时间、角度、模板和来源约束;判断真正缺失的信息 | `RequirementResolver` 写 requirement revision;`PlanController` 生成候选计划。 |
| run 正在执行或已有报告 | 结合当前选中的 node/agent、等待澄清、成果血缘和报告章节,识别用户是在回答、补角度、重检索、重分析、重跑、改章节、润色、查询还是改规划 | `ImpactAnalyzer` 计算实际影响;`TaskLedger` 在确认和安全点后执行。 |
输入包含用户文本、session/run 状态、最新 requirement、当前 `target_node_id` / `agent_run_id`、awaiting clarification、当前报告版本和可见成果摘要;不得输入其他成员的内部 prompt、隐藏推理或无权访问的资料。
```text
IntentDecision
intent / confidence / normalized_instruction
scope: conversation | node | artifact_lineage | section | plan
requested_target_ids[] / requirement_patch?
requires_confirmation / cost_level / reason
```
服务端必须验证 `target_node_id`、`agent_run_id` 属于当前 session/run,再解析到 node 和成果物血缘;不可相信客户端传入的任意 ID。`ImpactAnalyzer` 产出实际 `impact_nodes`、失效成果和新 attempt 计划,用户所见的命令状态由它和 `TaskLedger` 写入,而非由协调者文本决定。
`IntentRouterAgent` 的置信度不足、目标不唯一、涉及 requirement/plan revision、预计成本为 high 或影响多个并行分支时,必须返回 `requires_confirmation=true`;只有用户确认后 `TaskLedger` 才可执行。对于处于 `awaiting_input` 的节点,明确回答优先分类为 `answer_question`,只解锁该节点的依赖链。
---
## 6. 数据库设计
@@ -300,9 +405,9 @@ completed -> reopened -> queued
| `report_collaboration_messages` | 对话原文镜像 | message_id、role、content、tool_calls_json、tool_call_id、agent_run_id、node_run_id、phase_id、visibility、metadata、command_id、created_at、completed_at |
| `report_collaboration_plans` | 候选及修订 | proposal_group_id、revision、status、graph_json、validation_json |
| `report_collaboration_runs` | 正式执行 | plan_id、status、lease_owner、lease_until、cancel_requested、next_event_seq |
| `report_collaboration_node_runs` | 节点尝试 | node_id、attempt、status、contract_json、agent_snapshot、error、started_at、ended_at |
| `report_collaboration_node_runs` | 节点尝试/ledger task | node_id、attempt、`node_run_id=${node_id}-attempt{attempt}`、status、contract_json、input_artifact_ids、command_overlay_revision、agent_snapshot、error、started_at、ended_at |
| `report_collaboration_agent_runs` | 实际团队成员实例 | agent_run_id、node_run_id、phase_id、role、display_name、avatar_key、status、agent_snapshot、started_at、ended_at |
| `report_collaboration_artifacts` | 成果物 | artifact_type、producer_node_run_id、schema_version、content_json、validation_status |
| `report_collaboration_artifacts` | 成果物及血缘 | artifact_type、producer_node_run_id、requirement_revision、schema_version、content_json、validation_status、superseded_by |
| `report_collaboration_events` | durable SSE | run_id、seq、event_type、payload、created_at |
| `report_collaboration_commands` | 用户命令 | command_id、idempotency_key、intent、scope、status、expected_revision |
| `report_collaboration_report_versions` | 报告版本 | version、parent_version_id、markdown、source_index、change_summary |
@@ -316,6 +421,7 @@ completed -> reopened -> queued
- 事件 seq 在 run 行内原子分配。
- 用户消息、协调者消息、成员 TeamSay、流式回复、工具调用和工具返回使用稳定 `message_id` 与 timeline/event seq;同一 `agentRunId` 内严格保持因果顺序,保证 `MessageList` 能以原生消息重建全过程。
- 并行成员的消息分别归属 `agent_run_id`,同一事件流仍全量持久化;前端选择标签只能过滤/聚焦,不能影响事件写入或消息顺序。
- 成员状态的 event 投影使用 `session.snapshot` 中的增量 `agent_runs[]` 合并;不可自定义未被前端处理的 `agent_run.*` 事件。
- 删除 session 显式删除子资源,不依赖 SQLite cascade pragma。
- 写事务在 commit 前完成必要序列化。
- 不使用跨连接 post-commit refresh。
@@ -346,6 +452,7 @@ GET /api/report-collaboration/runs/{run_id}/events?after_seq={seq}
GET /api/report-collaboration/runs/{run_id}/stream?after_seq={seq}
POST /api/report-collaboration/runs/{run_id}/commands
POST /api/report-collaboration/runs/{run_id}/commands/{command_id}/confirm
POST /api/report-collaboration/runs/{run_id}/commands/{command_id}/cancel
POST /api/report-collaboration/runs/{run_id}/cancel
GET /api/report-collaboration/runs/{run_id}/nodes/{node_id}/contract
@@ -357,10 +464,17 @@ POST /api/report-collaboration/sessions/{session_id}/report-rewrites/{id}/appl
POST /api/report-collaboration/sessions/{session_id}/report-versions/{id}/restore
```
写请求必须支持:
`GET /sessions/{session_id}` 必须返回完整 `SessionSnapshot`:
- `idempotencyKey`
- `expectedRevision`
```text
session / messages / plans / run / nodes / agent_runs / commands /
report_versions / last_seq
```
写请求必须读取并回显:
- `X-Idempotency-Key`(服务端唯一去重键;用户消息镜像写入 `metadata.idempotency_key`)
- `X-Expected-Revision`(陈旧修改返回 409,不静默覆盖)
计划请求和 run 请求只创建 durable command/job,不在请求生命周期中等待完整模型输出。
@@ -437,10 +551,11 @@ heartbeat
- 历史与实时使用相同 envelope。
- 支持 `Last-Event-ID` 和 `after_seq`。
- token delta 可 live-only 合帧,但需周期性保存可恢复 checkpoint。
- 不能每 token 写数据库。
- 高频 token/tool 参数增量按 50~250ms 或大小阈值合帧为**已持久化** `message.delta` / `tool_call.delta` / `tool_result.delta`;不能每 token 写数据库,也不能仅 live-only 后丢失重放。
- 每一批增量与消息最新正文同事务写入,再向 Live Hub 发布;断线时能通过 `after_seq` 重放,快照也能读到最新 checkpoint。
- 状态、成果、命令和报告版本必须 durable。
- 读取事件暂时失败时保持连接并发送可恢复 warning。
- `session.snapshot` 可以携带完整快照,也可以携带 `run`、`nodes`、`agent_runs` 的增量集合;成员状态变化必须用此事件投影。
`message.*` / `team_message.*` 的 `data` 必须携带 LangChain 兼容的消息字段和归属,而非仅有摘要:
@@ -466,26 +581,33 @@ heartbeat
工具调用先在同一条 `ai` 消息的 `tool_calls` 上创建/追加参数 delta;返回值随后以 `type: "tool"`、相同 `tool_call_id` 的独立消息传输。允许用户查看的正文、参数和返回必须保持原文以及原始增量边界;只有统一脱敏器标记的字段可替换,替换理由写入 metadata。每个成员在开始生成时先发送 `message.created`,因此即使首 token 尚未到达,前端也能立即显示该成员正在回复,而非静默等待。
`reasoning_delta` 仅可承载经策略批准的、面向用户的工作说明(如“正在比对统计口径”),不允许桥接模型隐藏推理链、系统 prompt 或内部审稿思路;如模型未提供安全的公开工作说明,省略字段,过程可见性由 TeamSay、工具步骤、来源与普通 assistant 文本承担。
---
## 8. 分步实施计划
工单状态见 §0。本节正文仍是验收标准,不因部分完成而删减。
### BE-0:AgentScope 兼容性尖峰
**状态:条件完成(`RC-BE-000`)。** 事件适配与 dict/真实事件单测已有;真实双模型流式与 `uv.lock` 仍属后续 POC。20 项标注评测与 20 次固定任务统计已在 `RC-BE-018` 落地。
先写实验和测试,不接前端,不建正式业务表。
验证:
1. 当前 Python 3.12 与稳定 AgentScope 2.x 兼容。
2. 固定精确版本和 commit,不使用浮动 `main`。
1. 当前 Python 3.12 与候选 `agentscope==2.0.5` 兼容;POC 通过后才将精确版本写入 lock,不能使用浮动 `main`。
2. 验证单任务 worker 所需的 Agent、Tool、Event、Permission API;Agent Team 的自主调度与 Session SSE 不属于本功能的正确性依赖。
3. `uv lock` 后检查 Pydantic、SQLAlchemy、FastAPI、OpenAI SDK 冲突。
4. 使用当前至少两个模型完成流式回复。
5. leader + 2 workers 完成任务分配、TeamSay、并发和结构化输出。
5. `TaskLedger` 分派 2 个并行 worker,完成可见 TeamSay、并发和结构化输出。
6. 主动取消、超时、工具异常和成员异常行为可控。
7. AgentScope Event 可稳定关联 agent、task、message、tool call,并能一对一写出 LangChain 兼容的 assistant/tool 消息。
8. 内存状态丢失后可由 DeerFlow task ledger/artifact 重建。
9. 连续运行 20 次固定任务,统计早停、空输出、非法 JSON、事件缺失。
10. 从首个 `REPLY_START`、TeamSay、tool-call start、tool result 到 token delta 的首个可见事件均在指定时限内到达 adapter;并行两个成员交错输出时不串 message ID 或 tool call ID。
10. 从首个 `REPLY_START`、TeamSay、tool-call start、tool result 到 token delta 的首个可见事件均在指定时限内到达 adapter;并行两个成员交错输出时不串 message ID 或 tool call ID,并能按前端既定 `phase_id` / `node_run_id` 聚焦。
11. 人为注入流式中断、未闭合 tool call、等待用户输入与恢复场景;adapter 必须补发终态 tool result,不能留下永久 loading 的工具步骤。
交付:
@@ -502,6 +624,8 @@ Go 条件:
### BE-1:配置、依赖和总开关
**状态:完成(`RC-BE-002`)。** `enabled` / `worker_enabled` 默认 `false`。
新增 `config.yaml -> report_collaboration`:
```text
@@ -538,6 +662,8 @@ cost_budget
### BE-2:领域合同和状态机
**状态:完成(`RC-BE-001`)。**
完成 Pydantic 合同:
- requirement。
@@ -560,6 +686,8 @@ cost_budget
### BE-3:迁移和持久化 Store
**状态:完成(`RC-BE-003`)。** 表前缀 `report_collaboration_*`,迁移 `20260905_01`。
任务:
1. 创建数据库迁移。
@@ -579,15 +707,19 @@ cost_budget
### BE-4:会话、消息和计划 API 骨架
**状态:骨架完成(`RC-BE-004`)+ SSE 已挂载(`RC-BE-011`)+ 改写/恢复已挂载(`RC-BE-015`)。** REST 会话/消息/方案/run 已挂载;`GET /runs/{id}/stream` 为 durable SSE。
任务:
- session CRUD 和用户隔离。
- `GET /sessions/{id}` 一次返回前端 `SessionSnapshot` 的全部字段;run 前的澄清/规划阶段由该快照在 2.5 秒轮询内可见。
- message 持久化:完整保存用户可见的 assistant、TeamSay、`tool_calls` 与 `tool` 返回,并保留 `agent_run_id`/`node_run_id`/`phase_id`;不只保存阶段摘要。
- plan request 入队。
- plan list/select。
- run create/query。
- OpenAPI 响应模型。
- 路由注册顺序和统一错误模型。
- 按 `frontend-web/src/report-collaboration/api/types.ts` 建立契约测试:字段名保持 snake_case,枚举、事件类型、命令确认/取消端点和 revision 头不可漂移。
安全:
@@ -597,14 +729,17 @@ cost_budget
### BE-5:需求解析和澄清
`RequirementResolver`:
**状态:完成(`RC-BE-005`)。** 规则路径;尚未接规划用 LLM。澄清完成不会自动 `plan-requests`。
`IntentRouterAgent`(规划前模式)与 `RequirementResolver`:
1. 读取会话消息和最新 requirement snapshot。
2. 提取主题、目标、受众、范围、角度、模板和来源限制。
3. 判断是否缺少影响方案选择的必要信息。
4. 必要时产生一个或多个独立 clarification。
5. 用户回答后创建新 requirement revision。
6. 达到澄清上限后使用明确假设继续,不能无限追问。
2. `IntentRouterAgent` 输出结构化需求提取、澄清判定或“可直接规划”判定;不得自行建图或启动任务。
3. `RequirementResolver` 校验并写入主题、目标、受众、范围、角度、模板和来源限制。
4. 判断是否缺少真正影响方案选择的必要信息。
5. 必要时产生一个或多个独立 clarification。
6. 用户回答后创建新 requirement revision。
7. 达到澄清上限后使用明确假设继续,不能无限追问。
验收:
@@ -614,6 +749,8 @@ cost_budget
### BE-6:智能体目录、筛选和候选计划
**状态:完成(`RC-BE-006`)。** 选择器为规则重排,未接规划模型。`POST /plan-requests` 在 `worker_enabled=false` 时于请求内同步生成方案(非 LLM)。
五阶段:
1. `AgentCatalogProjection`:读取用户可见且启用的 agent、技能和工具元数据。
@@ -638,6 +775,8 @@ cost_budget
### BE-7:AgentScope 模型适配
**状态:完成(`RC-BE-007`)。** 适配器鸭子类型对齐 `ChatModelBase.__call__`;运行前冻结快照。真实 Provider 冒烟仍属 `RC-BE-009` / `017`。
目标:让 AgentScope 使用 DeerFlow 配置的模型,不再建立第二套 Provider 配置。
任务:
@@ -657,13 +796,15 @@ cost_budget
### BE-8:工具与权限适配
**状态:完成(`RC-BE-008`)。**
第一版允许:
- Web 检索和正文获取。
- 用户有权限的知识库/Wiki 检索。
- 明确批准的只读技能/MCP。
- 成果物板读写工具。
- AgentScope 团队通信与任务工具。
- 用户可见交接(TeamSay)与受控成果物/澄清工具。
第一版默认禁止:
@@ -678,7 +819,7 @@ cost_budget
- Researcher 不能发布报告。
- Writer 不能把未核验证据改成已核验。
- Reviewer 只能输出 ReviewDecision,不能覆盖报告。
- Coordinator 只能通过受控工具创建任务、分派、等待、返工和建议完成。
- Coordinator 只能提出受控的计划、澄清与修复建议;实际创建任务、分派、等待、返工和完成由 `TaskLedger` 服务执行。
- 所有成员获得本次 run 的最小权限快照。
验收:
@@ -688,7 +829,9 @@ cost_budget
- 网页 prompt injection 不能提升权限。
- AgentScope 工具异常被转换为 node/tool event,不泄露内部堆栈给用户。
### BE-9:AgentScope Team 运行器
### BE-9:受控任务成员运行器(不采用自治 Team 调度)
**状态:完成(`RC-BE-009` 脚本内核 + `RC-BE-013` 质量角色内核)。** `TaskLedger` + `WaveExecutor` + `QualityRoleKernel`。真实 AgentScope `reply_stream` / web_search 仍待接。
基础角色:
@@ -706,18 +849,21 @@ cost_budget
- 计划决定 Researcher/Analyst 数量和角度。
- 服务器限制团队人数、并发、attempt、返工和预算。
- 每个成员上下文只包含当前任务、需求快照和必要上游成果。
- Coordinator 不直接操作数据库,只调用 TaskLedger/ArtifactBoard 工具。
- `Coordinator` 只接收结构化成果摘要、质量失败与用户命令影响,输出候选计划/修复建议;它不拥有自由消息总线或数据库权限。
- `TaskLedger` 依据已验证成果、预算与并发限额逐波创建 AgentScope worker;AgentScope 不选择下一节点,也不决定任务完成。
- 每个 worker 只能调用 `submit_artifact`、`request_clarification`、`request_repair`、允许的资料工具和可见 TeamSay;所有改变运行状态的调用都由服务端重新校验。
- 任务完成由 QualityGate 判定,不接受文本声明。
事件映射:
- AgentScope `REPLY_*` → 以原始文本 chunk 创建/更新协调者或成员的稳定 assistant 消息,保留 `agentRunId`;不得等待 reply 完成后再合成为进度摘要。
- AgentScope TeamSay → 以成员身份创建/更新普通 assistant 消息,保留完整用户可见交接内容与因果顺序。
- AgentScope TeamSay → 以成员身份创建/更新普通 assistant 消息,保留完整用户可见交接内容与因果顺序;它不会改变 ledger 状态。
- `MODEL_CALL_*` → 内部遥测,默认不下发原始 prompt。
- `TOOL_CALL_*` → 原生 `ai.tool_calls` 参数事件;对应工具返回 → 原生 `tool` 消息和相同 `tool_call_id`。两者在 SSE 和持久化中一一对应,供现有 `MessageGroup` 直接渲染。
- team task/update → node/task ledger 状态。
- `TaskLedger` 的分派/校验/重试/取消结果 → node/task ledger 状态;AgentScope 仅上报可验证的 TaskResult。
- 成果工具 → artifact created/validated/rejected。
- 每个用户可见阶段开始、进展、完成、等待、返工和失败都必须同步产生稳定的消息或状态事件,供左侧 DeerFlow `MessageList` 按原始消息顺序展示;不能要求前端读取节点结果后自行拼装过程,也不能用聚合卡替换 AgentScope 的实时可见消息。
- agent run 的开始、等待输入、完成、失败和取消,紧邻节点状态事件发 `session.snapshot` 增量,至少包括相应的 `agent_runs[]` 和当前 `run.current_phase`。
验收:
@@ -727,6 +873,8 @@ cost_budget
### BE-10:成果物板与质量门
**状态:完成(`RC-BE-010`)。** `QualityGate` 为确定性规则(无二次模型调用)。模板冻结快照与正式 `ReportVersion` 落库已在 `RC-BE-015` 落地。
`ArtifactBoard`:
- 按 run/node/attempt/type 保存成果。
@@ -736,21 +884,26 @@ cost_budget
`QualityGate`:
- EvidenceBundle:来源数量、日期、URL/文档 ID、摘录和去重。
- VerificationResult:支持与反对证据、冲突、时效。
- AngleAnalysis:结论必须引用验证过的 evidence ID。
- ReportSectionDraft:关键事实和数字必须有引用。
- ReviewDecision:问题必须有目标、严重度和修复要求。
- ReportVersion:模板覆盖、引用完整、章节一致、正式版本已持久化。
- `CoverageMatrix`:把需求中的每个必选角度/关键问题映射到证据目标、分析节点和模板章节;计划和最终版本均不可漏项。
- `EvidenceBundle`:按**主张级**保存 `claim_id`、来源 URL/文档 ID、发布/获取时间、原文摘录、事实或推断标签、适用范围、数值单位/口径、可信度与去重结果。
- `VerificationResult`:对关键主张给出支持与反对证据、冲突、时效、来源独立性和可用结论;数字及重要事实必须经此门。
- `AngleAnalysis`:结论只能引用验证过的 evidence ID,必须分别标示事实、推断、假设、局限和资料缺口。
- `ReportSectionDraft`:关键事实和数字必须有 evidence ID;章节的主张与引用一一可追溯。
- `CitationTemplateLinter`:校验模板章节、必答问题、来源格式、数字/日期/实体一致性、不可用或过期来源和未引用的关键事实。
- `ReviewDecision`:独立于 Writer 上下文运行,问题必须有目标、严重度、证据和修复要求;不能笼统地要求“整体再优化”。
- `ReportVersion`:覆盖矩阵、引用、模板、章节衔接、结论一致性和正式版本持久化全部通过才可创建。
验收:
- 无成果、空成果和不合 Schema 的成果不能完成节点。
- 未验证来源不能直接进入最终事实陈述。
- Reviewer 的返工目标可映射到具体节点和章节。
- 同一事实出现冲突或同一数字缺少口径时,QualityGate 必须拒绝或要求定向补证,不能由 Writer 擅自选择一个答案。
### BE-11:后台 Dispatcher、租约和恢复
**状态:完成(`RC-BE-012`)。** `worker_enabled` 默认仍为 false:Gateway lifespan 只有 `enabled` **且** `worker_enabled` 同时为真才 `dispatcher.start()`。测试走 `dispatch_once()` / `executor.execute()`,不要依赖后台循环。
复用现有成熟模式:
- active-dedupe key。
@@ -780,6 +933,8 @@ cost_budget
### BE-12:SSE 和 Live Hub
**状态:完成(`RC-BE-011`)。** `ConversationReplica` 已实现协议镜像;产品 SSE 经 `execution/live_hub.py` 挂载。报告正式版本写入已在 `RC-BE-015` 落地。
任务:
- 实现 event repository。
@@ -790,6 +945,7 @@ cost_budget
- 将高频 token delta 合帧。
- 建立 `ConversationReplica`:将 AgentScope 用户可见消息、TeamSay、assistant delta、`tool_calls`、`tool` 返回、协助请求、报告文件和领域状态写成可重放的统一消息事件。它是协议镜像而不是摘要生成器。
- 对话镜像完整保留用户有权查看的 TeamSay、原始工具参数和返回;不包含 chain-of-thought、系统/内部 prompt、密钥、权限不可见内容和异常堆栈。脱敏在入库/下发前一次执行,不能由前端自行推断。
- 对前端已支持的 `reasoning_delta`,仅推送由本系统生成或模型明确允许公开的短工作说明;它不等同于原始思维链,也不得用作质量门、调度或审计的唯一依据。
- `agentRunId` 是并行消息流的稳定归属。Live Hub 始终发布所有成员事件;前端标签切换只在本地选择消息,不启动第二条运行流,也不取消未聚焦成员的 delta。
验收:
@@ -799,10 +955,15 @@ cost_budget
- 终态前报告正式版本已写入并可读。
- SSE 断开不取消后台 run。
- 仅依靠会话快照和事件重放即可重建每个成员的完整原生消息、工具过程和左侧聚焦视图,不需要调用节点结果接口。
- 在模型/工具中断、取消和接管时,所有已开始的 tool call 都有对应的 completed tool result(成功、失败或 interrupted),不会让复用的 `MessageList` 永久显示执行中。
### BE-13:全过程命令与影响分析
`IntentRouter` 输入:
**状态:完成(`RC-BE-014`)。** `app/report_collaboration/interventions/` 已实现运行期 IntentRouterAgent、ImpactAnalyzer 和 RuntimeCommandService;SQL/memory store 通过 `patch_command(..., expected_statuses=...)` 对命令执行权做 CAS。置信度阈值由 `report_collaboration.intent_confirmation_threshold` 配置(默认 `0.8`)。
实现边界:本票负责识别、授权、影响计算、确认、定向失效、overlay 和安全重排队。`rewrite_section` / `polish_report` 已能严格保留研究链并只返工写作/审校链;replacement candidate、正式版本 apply/restore 已在 `RC-BE-015` 落地。`add_angle` 优先使用用户定向的研究分支,否则按冻结计划顺序确定性选择一个研究分支,只接入其必要下游;无研究支路立即 `rejected`。`rerun_node` 没有合法目标时不得扩成整图。需求补丁只在返工成功后写入。卡住的 `executing` 可在接管或租约超时后收回。`command.cancelled` 进入 SSE。运行中 `POST /sessions/{id}/messages` 返回 409。
`IntentRouterAgent`(运行中干预模式)输入:
- 用户文本。
- session/run 状态。
@@ -810,7 +971,7 @@ cost_budget
- 当前选中节点或章节上下文。
- 后端动作白名单。
输出:
先由规则层处理明确的回答、取消和节点/章节显式指令;其余由 `IntentRouterAgent` 输出:
```text
intent
@@ -823,6 +984,8 @@ cost_level
reason
```
模型输出必须通过 Pydantic 校验,并经服务端 target 授权与置信度/成本闸门后才创建可执行 command;`IntentRouterAgent` 不可调用分派、取消、成果写入或报告版本工具。
命令类型:
- `answer_question`
@@ -839,11 +1002,15 @@ reason
`ImpactAnalyzer`:
- 重新检索:失效指定 EvidenceBundle 和引用它的下游。
- 重新分析:保留 evidence,失效分析和对应章节。
- 章节重写:保留研究/分析,只生成 replacement candidate。
- 语气润色:创建新报告版本,不失效事实成果。
- 全局改向:新建 plan revision,确认后创建 run branch。
- `answer_question`:只解析相应的 clarification;仅解锁依赖该答案的节点,其他并行节点继续。
- `research_again`:为指定节点/角度追加或替换 EvidenceBundle,失效其验证、分析、综合和受影响章节,不误伤无关角度。
- `reanalyze`:保留 validated evidence,只失效指定分析、综合、写作和审校分支。
- `add_angle`:在当前冻结计划中选择一个明确目标或确定性的研究支路承接新角度,再只失效其必要下游;无可用支路时拒绝执行。运行中不暗改前端已选择的 DAG,真正新增画布节点只发生在后续重新规划产生的新 plan revision 中。
- `rerun_node`:以新 attempt 重跑指定节点及必要下游,旧结果保留为 superseded。
- `rewrite_section`:保留研究/分析,只生成 replacement candidate 和该章节审校;用户 apply 后才产生新版本。
- `polish_report`:不重检索;在当前正式版本上创建新版本,并再次执行模板/引用 lint。
- `update_requirements` / `replan`:均先展示整图影响与高成本确认;前者写入新的 requirement revision 后重开冻结计划,后者重开当前计划全部节点。生成新候选 plan/run branch 的交互留在后续规划版本能力,不能在旧 run 的 SSE 内偷偷切换 run。
- `ask_report`:只读取当前已验证成果或报告版本,不改变任务图。
Safe-point:
@@ -851,15 +1018,19 @@ Safe-point:
- 不可立即取消的 attempt 继续到边界,command 状态为 `accepted_waiting_boundary`。
- 晚到输出可保存但标记 superseded,不能进入下游。
- 每条 command 有分类、影响范围、状态和结果。
- command overlay 仅在模型调用之间、工具返回后或节点提交成果前生效;在不可中断工具期间不能伪称已经完成修改。
验收:
- 低置信度“改一下”必须追问。
- 相同幂等键不重复创建返工任务。
- 运行中命令不能造成 completed/cancelled 状态竞争。
- 用户针对画布/成员卡的定向修改只影响解析出的节点和成果血缘,不能被协调者扩大为整场重跑。
### BE-14:报告模板、写作、审稿和版本
**状态:完成(`RC-BE-015`)。** 改写 / apply / restore 已挂载;正式版本在质量门通过后落库。
复用 `/api/report-structures`,run 开始时冻结模板快照:
- 章节层级。
@@ -871,25 +1042,25 @@ Safe-point:
写作规则:
- Writer 只能使用 validated artifacts。
- 关键判断绑定 evidence ID。
- 无法证实的内容明确标为分析、假设或局限。
- 每章获得确定 evidence set。
- 完整报告执行引用、模板、衔接和结论一致性检查。
- Reviewer 输出结构化 ReviewDecision。
- Reviser 只修改指定问题。
- Writer 只能使用冻结模板、`CoverageMatrix` 和 validated artifacts,禁止携带 Web/知识库工具或原始检索上下文进入写作提示词。
- 关键判断绑定 evidence ID;无法证实的内容明确标为分析、假设或局限。
- 每章获得确定 evidence set、字数目标和必答问题,输出章节草稿及主张—引用映射。
- 完整报告先执行 CitationTemplateLinter,再由独立 Reviewer 进行事实、逻辑、受众适配和结论审校。
- Reviewer 输出结构化 ReviewDecision;Reviser 只修改指定问题,并由同一质量门复验。
- 达到返工上限后报告未解决问题,不无限循环。
版本规则:
- 流式 Markdown 是临时草稿,不覆盖当前正式版本。
- 正式版本落库并确认后 run 才能 completed。
- 正式版本须在所有质量门通过后落库,随后才发 `report.version.created` 并令 run completed。
- 章节改写生成 replacement candidate。
- 用户 apply 后创建新版本。
- restore 也创建新头版本,不破坏历史。
### BE-15:安全、审计和预算
**状态:完成(`RC-BE-016`)。** 快照与 SSE 走统一脱敏;资源 owner、跨会话幂等、预算上限与审计已落地。
- 所有资源校验 owner 或明确授权。
- agent snapshot 移除密钥和不可见 prompt。
- 工具参数/结果完整镜像到用户消息流;只有密钥、令牌、隐私字段、越权内容和明确的异常堆栈由统一脱敏器替换并记录原因。不能以大小截断或摘要替代用户有权查看的返回;超大返回以可重放 chunk 传输和按需折叠展示,正文仍可完整读取。
@@ -897,10 +1068,12 @@ Safe-point:
- 每用户 active run 上限。
- 每 run 团队、并发、检索、来源正文、模型调用、token、费用和总时长上限。
- 审计 agent 选择、工具调用、来源、用户命令、人工确认和报告版本。
- 不持久化、不下发原始 chain-of-thought、系统/内部 prompt 或模型推理 token;这不影响用户可见 assistant 文本、TeamSay、工具调用与工具返回的完整镜像。
- 不持久化、不下发原始 chain-of-thought、系统/内部 prompt 或模型隐藏推理 token;前端可显示的 `reasoning_delta` 必须是经过独立策略筛选的公开工作说明。这不影响用户可见 assistant 文本、TeamSay、工具调用与工具返回的完整镜像。
### BE-16:测试与质量评测
**状态:完成(`RC-BE-018`)。** 合同 / 适配器 / 规划 / 需求 / SSE / lease / 20 项标注评测集已有。打分器不调用模型;金标必须过门,会商风失败实例必须可复现。
单元测试:
- Pydantic 合同。
@@ -912,6 +1085,8 @@ Safe-point:
- AgentScope event adapter。
- 同一 `message_id` 的 token delta、同一 `tool_call_id` 的参数/结果 delta 以及脱敏字段替换;断线重放后消息正文不得被摘要化或重复。
- 两个 `agentRunId` 并行交错输出、其中一员等待用户协助时,消息归属、工具配对、状态和命令目标均正确。
- `session.snapshot` 增量 `agent_runs[]` 能驱动前端阶段卡从 pending → running → waiting_input → terminal;`node_run_id` 与画布节点前缀匹配。
- 流式中断后每个已创建 tool call 都有终态 tool result,`MessageList` 不留下旋转步骤。
集成测试:
@@ -925,7 +1100,7 @@ Safe-point:
真实 AgentScope 冒烟:
- 至少两个已配置模型。
- leader + 多 worker。
- ledger 驱动的多 worker(不依赖 AgentScope Team 自动分派)。
- 并发工具和 TeamSay。
- 取消、超时、异常成员和结果修复。
@@ -934,19 +1109,24 @@ Safe-point:
- 不少于 20 个真实任务。
- 覆盖政策影响、市场趋势、企业研究、事件研判、对比分析。
- 每个任务记录必选角度、关键事实、禁止编造项、模板和评审规则。
- 与现有多智能体会商进行盲评。
- 指标:事实准确、来源质量、角度覆盖、逻辑一致、格式遵循、可修改性、耗时和成本。
- 与现有多智能体会商进行盲评,并保留每项失败的可复现实例。
- 指标:事实准确、来源质量、关键主张引用覆盖、角度覆盖、逻辑一致、模板遵循、定向修改正确率、耗时和成本。
- 上线前为每个任务标注关键主张/数字、可接受来源、必选角度和模板约束;不能只以主观“文章更像报告”判定成功。
---
## 9. 首个纵向 POC
POC-1 的规则路径已通(三策略差异方案、select ≠ start)。**POC-2 脚本闭环已通**(研究员 → 撰写员,协调者建议不调度)。**POC-3 质量角色已通**(核验 / 分析 / 审稿定向返工,确定性内核)。真实检索与 AgentScope 成员仍待接。**POC-4 未开始**。
固定任务:
> 撰写一份新能源汽车主要品牌市场趋势分析报告,覆盖 2024 年至今的市场份额、价格策略、技术路线、海外进展、政策环境和未来 12~24 个月风险与机会,面向企业战略决策人员,要求所有关键数字给出来源。
### POC-1:候选计划
**状态:规则路径已通(`RC-BE-006`)。** 三策略差异方案、select ≠ start;尚未接规划 LLM。
- 生成 3 种真实有差异的方案。
- 每个方案只引用有效 agent/tool/template。
- 每个节点有职责、输入、输出和验收条件。
@@ -954,6 +1134,8 @@ Safe-point:
### POC-2:三角色闭环
**状态:脚本路径已通(`RC-BE-009`),SSE 重放已挂载(`RC-BE-011`)。** 真实 Web 检索仍待工具实装接入运行器。
角色:Coordinator、Researcher、Writer。
- 完成真实 Web 检索。
@@ -964,6 +1146,8 @@ Safe-point:
### POC-3:质量角色
**状态:完成(`RC-BE-010` 质检 + `RC-BE-013` 受控成员 + `RC-BE-017` 真实内核)。** `QualityRoleKernel` 跑通 Verifier / Analyst / Writer / Reviewer / Reviser;过期与跨包冲突写入 `VerificationResult`;缺失角度只重开检索链;审稿失败只重开目标写作节点。生产 worker 使用 `AgentScopeMemberKernel`。
加入 Verifier、Analyst、Reviewer、Reviser。
- 过期资料被识别。
@@ -973,6 +1157,8 @@ Safe-point:
### POC-4:全过程干预
**状态:命令与 safe-point 路径完成(`RC-BE-014`)。** 增加角度、指定角度重检索、章节重写/润色的定向影响已覆盖;运行中旧 attempt 在边界后 supersede,晚到成果不能进入下游。正式报告 replacement/version 展示已在 `RC-BE-015` 落地。
必须演示:
1. “增加供应链与原材料风险角度”。
@@ -986,28 +1172,36 @@ Safe-point:
## 10. 后端开发顺序
1. `RC-BE-000`:AgentScope 版本与兼容性尖峰。
已完成:
1. `RC-BE-000`:AgentScope 版本与兼容性尖峰(条件 Go;未写入 `uv.lock`)。
2. `RC-BE-001`:领域合同和状态机。
3. `RC-BE-002`:配置、依赖和 feature flag。
4. `RC-BE-003`:数据库迁移和 stores。
5. `RC-BE-004`:session/message/plan API。
5. `RC-BE-004`:session/message/plan API(改写 / 恢复见 `015`)。
6. `RC-BE-005`:需求解析与澄清。
7. `RC-BE-006`:agent catalog、selector、plan assembler/validator。
8. `RC-BE-007`:AgentScope model adapter。
9. `RC-BE-008`:tool/permission adapter。
10. `RC-BE-009`:三角色 team POC。
11. `RC-BE-010`:task ledger、artifact board、quality gate。
10. `RC-BE-009`:三角色 team POC(脚本内核 + TaskLedger 波次)。
11. `RC-BE-010`:artifact board 主张级质量门(Coverage / 核验 / 审稿)。
12. `RC-BE-011`:durable event、原生消息镜像、SSE 和 live hub。
13. `RC-BE-012`:dispatcher、executor、lease、cancel、recovery。
14. `RC-BE-013`:Verifier/Analyst/Reviewer/Reviser。
15. `RC-BE-014`:intent router、impact analyzer、safe-point commands。
16. `RC-BE-015`:report structures、report versions、rewrite candidates。
17. `RC-BE-016`:安全、审计、预算、监控。
18. `RC-BE-017`:并发、故障恢复和真实 AgentScope 测试。
19. `RC-BE-018`:20 项质量评测和参数调优。
15. `RC-BE-014`:intent router、impact analyzer、safe-point commands(已完成)。
16. `RC-BE-015`:report structures、report versions、rewrite candidates(已完成)。
17. `RC-BE-016`:安全、审计、预算、监控(已完成)。
18. `RC-BE-017`:并发、故障恢复和真实 AgentScope 测试(已完成)。
19. `RC-BE-018`:20 项质量评测和参数调优(已完成)。
下一票起:
20. `RC-BE-019`:灰度与离线发布接入。
前后端共同依赖:在 `RC-BE-001` 后冻结 OpenAPI、event envelope 和共享枚举,前端再开始真实 API 层开发。
编号说明:§8 的 `BE-n` 与上表 `RC-BE-00n` 在 **008 之后不完全一一对应**(例如 §8 `BE-11` dispatcher ≈ `RC-BE-012`,`BE-12` SSE ≈ `RC-BE-011`)。排期与代码以 `RC-BE-*` 为准。
前后端共同依赖:OpenAPI / event envelope / 共享枚举已按 `frontend-web/src/report-collaboration/api/types.ts` 冻结;前端页面与 mock 已完成。真实 SSE 可联调 `GET /runs/{id}/stream`(`RC-BE-011`);领取/恢复见 `RC-BE-012`;质量角色见 `RC-BE-013`;运行中自然语言命令见 `RC-BE-014`。正式报告改写候选与版本 apply/restore 已在 `RC-BE-015` 挂载。生产默认仍不挂后台循环(`worker_enabled=false`)。
---
@@ -1082,7 +1276,7 @@ TeamSay 不作为事实来源;报告事实只能引用 validated EvidenceBundl
后端只有同时满足以下条件才算完成:
1. 使用 AgentScope 2.0 Core 组织真实 leader-worker 团队。
1. 使用 AgentScope 2.0 Core 执行真实的受控任务成员,且由 DeerFlow `TaskLedger` 而非自治 Team 调度协作。
2. 不调用旧 Workflow Studio DAG scheduler。
3. 不改变现有多智能体会商行为。
4. DeerFlow 数据库是状态唯一事实源。
+31
View File
@@ -0,0 +1,31 @@
# 日常使用复查(2026-09-14)
范围:ji 分支上继续检查知识库管理、助手知识库、我的项目、项目问答和官方入口。沿用本地隔离后端数据目录 `.runtime/ji-acceptance`,浏览器检查 464×642 与 1440×900 两种尺寸;未向生产库写入验收项目。
## 发现与修复
**首屏白屏的直接业务触发点也已修复:** `LlmWikiKnowledgeSelector` 在懒加载/未加载知识目录时,每次渲染创建新的空数组,推荐问题 effect 随之回写父页状态,形成无限更新。将目录空结果和默认助手库选择稳定化后,重新从登录进入新对话成功,欢迎引导、输入框及知识空间下拉均可操作(截图 06)。仅升级依赖并不能消除这个业务循环;上述修复与依赖兼容更新共同保留。
1. **首次进入:高影响,已修复依赖。** 本轮首次登录后出现整页空白,控制台为 ScrollArea `Maximum update depth exceeded`。安装版本 1.2.10 使用不稳定引用回调;升级并锁定 1.2.18,同步 package-lock,核对源码使用稳定 setState 引用。冷启动仍复现组合引用循环,进一步发现各基础组件带入 Slot 1.2.3/1.2.4;统一锁定 Slot 1.3.3,npm 与 pnpm 均配置覆盖间接依赖。依据 [Radix 发布记录](https://www.radix-ui.com/primitives/docs/overview/releases) 的 React 19 无限重渲染及 Slot 引用修复。
2. **新建项目:已修复。** 手机表单出现两个菜单按钮(截图 01)。由外层提供菜单时不再显示内部按钮;官方入口也去掉无效的内部侧栏按钮。
3. **项目历史:已修复。** 我的项目侧栏链接跳到旧项目空间(截图 02),导致返回目的地变化。现在保留我的项目路径,普通聊天与旧项目空间保持原路由。手机点历史后菜单自动收起。官方侧栏原本把项目聊天打开为普通聊天,现按项目归属进入项目工作区;`/official/workspace/projects/groups` 根路径回到普通问答项目列表。
4. **项目设置与失败恢复:已修复。** 默认智能体更新后,同步新对话选择器;项目对话权限/归属加载失败、普通问答项目列表失败均有重试入口。
5. **助手知识检索:已修复。** 连续检索或切换库时,以递增请求序号隔离过期成功/失败/结束回调,防止旧结果覆盖新库;新检索清除旧结果;空输入禁止提交。无结果原本只有小标签(截图 03),现在说明可调整关键词并提供浏览目录按钮。输入框补充可访问名称。
6. **窄屏输入栏:已修复。** 464px 截图中发送箭头被工具栏挤出右边缘(截图 07);工具区允许换行、发送区不收缩,截图 08 确认按钮完整可见。项目问答网络错误也增加“重新加载对话”,只重建读取状态,不自动重发消息。
## 复测记录
- 具名项目创建成功;新建项目对话,通过真实模型收到“项目问答回归通过”;刷新保留消息与项目归属。
- 修复工具栏后,在窄屏实际点击发送并收到“窄屏发送通过”;首屏知识空间菜单完成加载、选择助手库后显示已选择 1 个,未再出现循环白屏。
- 手机项目历史链接确认留在 `qa/projects/groups`;桌面官方 groups 根路径跳回正确列表,官方项目历史进入相同项目。
- 官方和常规项目切换时偶发网络 `Failed to fetch`,刷新/重新挂载恢复历史消息;没有将其描述为已排除的网络问题,新增恢复按钮未进行独立网络故障注入测试。
- 助手知识库列表、阅读、检索无结果、返回入口实际操作;空查询按钮禁用。检索竞态由代码检查修复,未使用浏览器网络故障注入。
- 后端 22 个相关文件重新运行:**273 passed**,涵盖知识库管理/导入/检索、Wiki、编码与向量、项目归属隔离、策略持久化及调用边界。
- 前端项目与策略测试:**33 passed**,新增项目历史保持入口、旧项目和普通聊天/创建向导回归。
- 生产构建通过。全仓类型检查仍有 **59 个既有错误**,与上一轮错误列表一致;不宣称全仓类型检查通过。
## 证据与边界
本轮截图与日志存放在 `frontend-web/.runtime/daily-usage-audit/`:`01-create-before.png`、`02-history-before.png`、`03-knowledge-search.png`、`04-official-desktop.png`、`05-official-project-after.png`(网络失败状态)、后续 after 截图,以及 tests/build/typecheck 日志。后端日志为 `.runtime/ji-acceptance/daily-regression.txt`。
上一轮真实文件入库、向量命中、方案生成与保存的验证详见 `ji-knowledge-projects-acceptance.md`。本轮不重复付费生成完整方案;没有进行生产全量语料质量评估、负载测试、断网故障注入或完整无障碍合规审计。已检查可见导航、表单名称、窄屏显示和主要失败恢复,不能据此承诺所有模块没有缺陷。代码未提交、推送或部署。
+37
View File
@@ -0,0 +1,37 @@
# ji 分支知识库与我的项目验收
日期:2026-09-14。基线:`ji` / `f62a512a`,已从 origin fast-forward 更新。
## 修复范围
1. 简洁模式进入我的项目保留功能导航,覆盖列表、策略工作台和问答项目;移动端有菜单展开入口。
2. 策略结果卡片的模型思考前置;检查意图、评判、方案、展开视图保持过程在正文前。
3. 普通问答改为真实项目分组:先填写项目名称创建项目,再在项目中创建多条问答。服务端持久归属与权限校验沿用 analysis-projects;不自动迁移或标记历史普通聊天。官方入口支持对应 groups 子路由。
4. 修复方案列表要求预先选中策略导致无法进入的问题;任务变化后已有方案仍可查看;请求序号避免旧请求失败覆盖成功结果。
5. 修复知识库管理目录缓存导致上游删除后仍显示的问题;保留检索端缓存。
6. 项目编辑保留知识库绑定,助手知识库补充普通用户空态。
## 自动化
- 后端 22 个相关测试文件:273 passed(知识库、Wiki、向量、编码器、项目归属、策略配置与持久化、边界等)。
- 前端策略与项目测试:31 passed。新增用例覆盖未选择策略、已有方案变更任务、未完成评判及全部策略失败。
- `tests/test_knowledge_live_embedding.py`:真实接口 1 passed;默认跳过,设置 `KNOWLEDGE_TEST_EMBEDDING_KEY` 后运行。默认服务 SiliconFlow、模型 BAAI/bge-m3;可用 `KNOWLEDGE_TEST_EMBEDDING_URL` / `KNOWLEDGE_TEST_EMBEDDING_MODEL` 更换。
- `pnpm build`:通过。
- `pnpm typecheck`:未完全通过,59 个错误位于此次未改动文件(nextra 缺失、Markdown 插件类型、旧 JSX 类型及旧问答页等);修改文件无类型错误。不能将生产构建通过等同于全仓类型检查通过。
## 真实接口与浏览器
测试使用 `.runtime/ji-acceptance` 独立数据目录,保留测试数据便于复查,未操作业务数据库已有项目。
- BAAI/bge-m3:中英文 3 条输入,均返回 1024 维有效向量。
- 实际检索服务:相关正文排名、指定库范围、草稿排除、概览查询通过。
- 两个项目 A/B:A 创建两条对话,B 列表为零;跨项目读取 A 的对话返回 404。
- 浏览器:简洁模式完整菜单可见;新建具名普通问答项目;项目内真实发送并收到回答;第二条对话独立;回列表仍是一张项目卡,刷新后保留。
- 方案整理:真实模型返回 1539 字符正文,含 agent_done / all_done,流正常结束。
- 方案入口:完成评判但尚未勾选策略的测试项目出现在方案列表。浏览器进入后勾选策略、生成 2755 字符正文、保存成功;服务端校验 synthesisStatus=done 且 solutionSavedAt 非空,刷新后正文与导出按钮恢复。
- 官方入口:`/official/workspace/projects/groups/new` 正常展示具名问答项目表单,官方侧栏保留。
- 助手知识库:创建、重命名、列表、拒绝 .py 上传通过;上传测试文本任务 completed;生成 Wiki 页面,向量检索命中 1 条,详情 HTTP 200。
## 限制
真实模型测试使用最小合成读书活动/设备资料,未对生产全量知识语料进行质量评估。API 密钥仅用于测试进程,不提交配置。仓库原有未跟踪 docx 与 packages1.zip 保持不变。未提交、推送代码,也未执行离线发布。
@@ -0,0 +1,105 @@
# 本地问答、资料库与技能并发验收
测试日期:2026-09-28 至 2026-09-29 凌晨。环境:Windows、约 8GB 物理内存、Python 3.12、Node 22、Gateway 单进程、SQLite、默认 MiniMax-M2.5。前端 Vite 5174,Gateway 8001。模型并发限流与 Redis 分布式门控未启用。
本次保留并验收了工作区原有的资料库索引、问答收尾、沙箱超时和流式恢复修改,另外修复了压测复现的技能刷新重复校验,以及真实地图问答暴露的内部待办提醒显示问题。测试结果不能等同于 Linux 多进程生产环境容量。
## 版本范围
开始测试时本地为 `3a07c476`,没有 `cognitive-situation-map`。随后拉取远程,发现 `origin/master` 已前进至 `3107f2cc`,包含该技能的初始化及 v2.6.0 精简运行时。保留本地修改后合入远程,合并提交为 `c9effe83`;最终绘图与多用户测试以合入后的代码为准。
旧本地版本里 `page-report-generation` 的地图仅支持中、美、日、台,因此其“不支持菲律宾”的反馈不能用于评价最新的 `cognitive-situation-map`。
## 真实问答
使用独立普通测试账户,阶梯间顺序运行,逐条检查预期测试关键词与结果、SSE `end`、持久化 `success`,结束后删除本轮生成的测试线程。1/3/5/10 路检查正文包含“测试”和数字 4;20 路增强为检查“测试正常”及明确的“2加2等于4”。这属于简单问答验收,不是复杂内容质量评测。
| 同时问答用户数 | 成功 | 首段答案中位耗时 | 流结束中位耗时 | 流结束最慢 |
|---|---:|---:|---:|---:|
| 1 | 1/1 | 5.792s | 6.439s | 6.439s |
| 3 | 3/3 | 2.214s | 3.245s | 3.287s |
| 5 | 5/5 | 2.885s | 3.702s | 4.373s |
| 10 | 10/10 | 2.920s | 4.796s | 5.416s |
| 20 | 20/20 | 4.364s | 10.622s | 11.135s |
累计 39/39 通过。20 用户整批耗时 11.829s,同期 health 12/12 成功、中位 13ms、最大 371ms。这里是一次阶梯短时突发,并未证明百用户持续长任务容量。
取消接口返回 204,58ms 后状态为 `interrupted`,测试线程删除返回 200。同期健康探测 20 次均成功,中位 9ms,最大 3.044s;说明仍存在数秒调度尖峰,不能仅凭平均值宣布无阻塞。
早期多个磁盘基准同时运行时,单路首段答案为 33.853s,日志显示同步 agent 构建 28.344s,health 也有 5s 超时。这批明确有本机资源争用,不纳入稳定容量结论。开发脚本启动的服务进程还曾消失,没有拿到退出或 reload 日志,原因未确认;期间的连接失败剔除。后续使用无热重载的固定进程复测。
## 资料库和知识库
- 资料库/知识库相关回归 186 项通过;资料库新存储 Memory/SQLite 专项另有 8 项通过,覆盖用户隔离、分页、原子换代、并发租约和旧任务围栏。
- 真实测试账户看到 2 个共享知识库;选中的库包含 668 份文档、168 个 Wiki 页面。首次 hybrid 查询 3.58s,返回 5 条。
- 排除合成磁盘负载后,空资料库、知识库目录和 hybrid 查询分别测试 1/4/8 并发,每档 8 次,总计 72/72 HTTP 200,零异常。
| 接口 | 8 并发中位耗时 | 8 并发最大耗时 |
|---|---:|---:|
| 资料库列表(空测试账户) | 165ms | 224ms |
| 知识库目录 | 517ms | 557ms |
| hybrid 检索(相同问题、5条结果) | 426ms | 441ms |
这组重复使用同账号和查询,包含热缓存效果;空账户列表不代表有大量文件的资料库。独立测试 SQLite 另生成了 101 个合成用户、10 万条资料元数据,480 次查询全部成功;其中单用户 5 万条资料关键词查询 P95 为 1.08s、首页 P95 为 130ms,但此轮同时有其他 IO 测试,只作为规模观察值。
资料库索引按 5 分钟过期刷新;进程异常退出时正在同步的租约最多等待 30 分钟恢复。大库关键词查询仍使用多字段包含匹配,不能据当前测试证明生产规模检索始终快速。
## 大技能与并发刷新
文件体积大和小文件数量多分别测试,使用隔离临时目录,不改真实技能内容。
| 场景 | 结果 |
|---|---|
| 真实技能副本,269 文件 / 2.75MiB,稳定缓存 64 并发 | 全成功,约 1.05s;早期混合负载观察值 |
| 同副本改 1 个资产,32 并发,修复前混合负载 | 27 成功、5 个 30s 锁等待超时,复现问题 |
| 独占配对:同副本改 1 个资产,32 并发,旧逻辑 | 32/32 成功,P95 13.700s,最大 14.842s |
| 独占配对:同场景,新逻辑 | 32/32 成功,P95 / 最大 3.963s,P95 降低约 71% |
| 128MiB 资产、34 文件,新逻辑 | 冷初始化 5.54s;变更后 32 并发 P95 0.584s,全成功 |
| 约 160 技能、5120 个小文件(修复前规模观察) | 冷加载执行者约 107s,31/32 等待者超时;热校验约 18s |
修复让同一进程内、相同权限配置的一批重叠请求共享一次刷新及最终校验。等待者仍检查当前缓存身份、权限哈希和能力标记;失败或技能下线时不返回旧视图。新增 8 项回归连同原有 62 项,共 70 项通过。
**剩余风险:** 数千个小文件的单次 Windows 元数据扫描仍昂贵,本次修复没有消除这项成本,也未证明跨进程刷新能合并。不能把 32/64 个技能调用线程等同于 32/64 个真实模型用户。
实际调用 `gongwen-assistant` 读取技能并输出短通知,10.672s 内正常结束,无工具卡住。但答案把要求的 10月8日改成10月15日:通信/技能读取通过,内容日期遵循失败,不能记为整体语义验收通过。
## 绘图与界面
普通问答已实际生成菲律宾地图底图及马尼拉、吕宋北部、巴拉望、棉兰老四个点位,点位弹窗可打开。使用明确标注的虚构数据,未进行时事真实性评估。初次模型额外要求确认消息内地图,回复确认后完成,属于可见的额外交互成本。
这次真实对话发现 `todo_completion_reminder` 被当作用户问题展示。修复后,后端待办提醒带 `hide_from_ui`,前端也按两个历史内部名字过滤聊天和问题导航;真实用户引用 `<system_reminder>` 的文本仍保留。后端 3 项、前端新及关联分组 12 项通过。
最新 `cognitive-situation-map v2.6.0` 已经通过真实问答完整调用:读取技能和菲律宾参考 JSON、写入场景、调用技能自带 `node render.mjs`、检查文件、`present_files` 交付,42.481s 完成,SSE 正常结束并保存 `success`。产物标题为“菲律宾冲突认知态势图(功能验收示意)”,包含 4 个事件、4 个标注、3 个行为体和 2 条连线,明确标注所有事件与等级均为测试数据。输出 `status=ok`、无恢复和警告。模型曾发起两次过宽的目录 glob,被技能访问隔离拒绝后自行改用指定技能目录完成,没有循环卡住。
同一最新技能的直接渲染为 624ms;8 个并发独立渲染进程全部成功,范围 650–949ms。围栏/截断/缺失字段/约 600KiB 场景输入的恢复、非空目录拒绝覆盖、脚本结束标签转义均完成专项检查。真实 Windows LocalSandbox 虚拟路径经 PowerShell 调用 Node 成功(含冷 shell 7.85s)。CSS、JS、菲律宾底图和旗帜资源均返回 200;2.93MB 底图一次请求耗时 208ms。8 个渲染进程不等于 8 个同时生成长内容的模型用户。
**最新 HTML 的视觉验收未通过:** 测试账户能在会话中看到生成文件卡片,文件接口返回 200,源码模式能显示 HTML、正确资源地址和页面结构,但 Codex 内嵌浏览器的预览面板空白;DOM 中 iframe 的 `src` 为 `blob:http://127.0.0.1:5174/...`,可访问树仍为 `about:blank`。普通地图组件的可见性不能替代这个 HTML 的验收,渲染器返回成功也不能证明最终视觉正确。已保留空白预览截图,未将此项计为通过。
进一步在内存中执行真实 `prepareGeneratedHtml`:11,269 字节输入变为 14,597 字节,原有 3 个脚本逐字保留、DOM id 与外链 URL 均不变,新增兼容脚本语法有效。暂未发现生成器或预处理破坏 HTML 的证据;故障停在预览加载/Blob 导航阶段,具体原因未确认,不将其归为已知浏览器限制,也不宣称已修复。
界面还复现了 TDesign 新手引导闲置时的 `reading 'element'` 异常:`current=-1` 时其 resize 监听仍读取不存在的步骤。简洁模式与智能体两处包装组件已改为只有有效步骤才挂载 Guide。浏览器复测了文件面板开关、侧栏切换、手动启动、下一步、跳过,未再出现新的同类异常;完成/跳过的持久化语义保持不变。
## 合并后回归
- 完整 harness:274/274 通过,53.07s;包含前述技能及 Todo 子集,不重复累加。
- 模型并发配置专项:10 项通过;种子技能资源:4 项通过。
- 前端流式恢复专项:9 组共 44 项通过;待办可见性及相关消息分组:12 项通过。
- 全量前端构建和类型检查最初并行运行时,系统空闲内存降到约 17MB,因此主动停止该轮类型检查,改为串行执行;该轮中断不作为代码失败。
- `pnpm build` 通过(10m25s,包含前期资源争用,有大 chunk 警告)。其后新增的两个 Guide 挂载条件已通过单独语法检查和真实浏览器回归;另补齐问题导航抽取后的类型导入,不改变生成代码行为。
- 最终 `pnpm typecheck` 仍未通过,共 73 项原有诊断;与测试前本地 `hang-typecheck.log` 的 75 项逐条比较,减少了消息分组测试的 2 项,没有新增诊断。本轮发现的 `QuestionItem` 类型导入遗漏已经修正,复跑后消失。剩余问题包括 `nextra` 依赖、旧 JSX 类型和既有组件/测试类型,未扩大范围处理,不能标记全量类型检查通过。
## 本地证据和复测入口
详细证据仅保留在 `.runtime/test-results/20260928-local-qa/`,不提交模型凭证、问答日志、测试数据库、构建 ZIP 或技能临时大目录:
- `qa-concurrency-results.json`:逐轮时间、结束事件、答案检查和清理结果。
- `qa-concurrency-20-results.json`、`harness-full.xml`:20 个独立用户问答和完整 harness 回归。
- `knowledge-stable-recheck.json`、`knowledge-library-report.md`:知识库与资料库稳定复测及限制。
- `skills-benchmark-paired.json`、`skills-findings.md`:同机旧/新刷新配对;“旧逻辑”在隔离进程禁用新合并函数回放,不改服务源码。
- `philippines-html.json`、`large-skill.json`:旧页面技能能力缺口与公文技能真实调用。
- `philippines-map-ui.png`:普通地图真实浏览器渲染。
- `cognitive-philippines.json`、`cognitive-skill-probe.json`:最新技能真实完整问答及渲染器专项。
- `cognitive-preview-ui.png`:最新 HTML 在本轮内嵌浏览器出现空白的实际截图。
- `frontend-build.log`、`frontend-typecheck-final.log`:全量构建与最终类型检查。
提交的回归入口包括 `packages/harness/tests/test_skill_view_refresh_coalescing.py`、`test_skill_view_waits.py`、`test_sandbox_command_liveness.py`、`test_run_completion_timeout.py`、`test_todo_reminder_visibility.py`,以及前端 `control-timeout`、`stream-terminal-recovery` 和 `todo-reminder-visibility` 测试。
@@ -0,0 +1,358 @@
# 技能并发与流式修复:配置、前端包与验收说明
日期:2026-09-27(2026-09-28 补充高并发运行配置)。
本说明对应本次提交的修改,包含模型流式兼容与恢复、React #185 错误处理和已完成的最小技能示例,
以及可选的技能视图复用、版本缓存、模型计时日志队列和 Gateway 线程池扩容。
部署前通过 `git log -1` 核对代码提交;仅修改配置或替换旧包不能确认已部署本次修复。
## 1. 先确认需要更新什么
- **后台代码也要更新**:四个新开关需要新版后台读取。只换前端 dist 或只把字段写进旧版 config.yaml 不会实现后台优化。
- **前端**:使用本说明末尾的 ZIP,里面是完整 `dist/`,不是源码或 node_modules。
- **仓库运行配置已开启优化**:两份 config.yaml 的四个开关均已显式设为 `true`。但已有服务器的持久化/外挂配置不会被仓库自动覆盖,仍需手工同步。
- 开启线程池开关后可将上限调整为 100,但不提高模型服务商配额、不启用多 worker。
## 2. .env 需要修改哪些项
后台启动所使用的 `.env` 保持或修改为:
```dotenv
# Gateway 进程数,不是线程池大小;当前内存流/运行管理器必须单进程。
GATEWAY_WORKERS=1
# 默认就是 0;显式写 0 可防止误用不安全的多进程覆盖开关。
DEERFLOW_ALLOW_UNSAFE_MULTIWORKER=0
# Gateway asyncio 线程池上限;还必须打开下一节的 asyncio_thread_pool_enabled。
DEER_FLOW_ASYNCIO_THREAD_POOL_SIZE=100
```
适用于已更新的 `scripts/start_backend-online.sh` 和 `offline-backend-20260512/start_backend.sh`。
若通过自定义服务直接运行 Uvicorn,也应确认实际命令是单 worker;仅修改 .env 不会改变硬编码的启动命令。
启动环境中已导出的同名变量可能覆盖 .env,需一并核对。
前面三个技能/日志优化没有独立环境变量,它们配置在下一节的 `config.yaml`。
线程池上限变量现已真正接入;修改 .env 后必须重新启动后台,Docker 应通过现有启动脚本重新创建容器,
单纯 `docker restart` 不会更新容器创建时注入的环境变量。
其余模型地址、密钥、数据库连接等保留服务器原值,不用本机 .env 覆盖生产文件。
### 100 个线程与环境变量优先级
`GATEWAY_WORKERS=100` **不是**把工具线程池调成 100,而是启动 100 个进程,当前部署不要这样设置。
`DEER_FLOW_ASYNCIO_THREAD_POOL_SIZE=100` **已经实现**,兼容别名 `DEERFLOW_IO_THREADS=100`,
两者任选其一即可。开启线程池开关后,按以下顺序取第一个非空值:
1. `DEER_FLOW_ASYNCIO_THREAD_POOL_SIZE`(正式变量)
2. `DEERFLOW_IO_THREADS`(兼容别名)
3. `config.yaml` 的 `runtime_performance.asyncio_thread_pool_size`(默认 100)
有效范围为整数 **1..1024**。选中环境变量的值非法时启动失败并提示,不会静默退回默认小线程池。
开关关闭时不读取环境变量来改变线程池,保留 Python 原默认。已有正式变量时,修改别名不会覆盖它。
线程按需创建,100 是上限,不会预先常驻启动 100 个线程。
## 3. config.yaml 新增配置
在实际加载的 `config.yaml` **顶层**配置这一段,不能缩进到 `models`、`sandbox` 或 `skills` 内,
也不要重复定义两个 `runtime_performance`。下面是程序在字段缺失时的兼容性默认值;
当前仓库的两份可运行配置已使用后文的高并发开启值:
```yaml
runtime_performance:
# 一次工具调用内只获取一次已验证技能视图,默认 false。
skill_view_reuse_enabled: false
# 版本未变时复用技能视图,默认 false。
skill_view_cache_enabled: false
# 模型计时日志使用独立队列,不争抢耗时工具线程,默认 false。
model_timing_nonblocking_enabled: false
# 是否覆盖 Gateway asyncio 默认线程池,默认 false。
asyncio_thread_pool_enabled: false
asyncio_thread_pool_size: 100
```
如决定启用本次四项优化并将线程池上限设为 100,改为:
```yaml
runtime_performance:
skill_view_reuse_enabled: true
skill_view_cache_enabled: true
model_timing_nonblocking_enabled: true
asyncio_thread_pool_enabled: true
asyncio_thread_pool_size: 100
```
四个开关独立生效,可以单独开关。关闭/省略对应项时,对应的性能优化走原实现。
这四项不控制模型协议兼容修复或前端错误处理;全部关闭不等于回退所有代码。
为了让已有模型实例和所有后台进程配置一致,修改后重启后台;回退也是改回 `false` 后重启。
### 确认没有改错配置文件
- 已设置 `DEER_FLOW_CONFIG_PATH` 时,以它指向的文件为准;Docker 内该值必须是容器内可访问路径。
- 本仓库包含 `offline-backend-20260512/config.yaml` 和 `offline-backend-20260512/backend/config.yaml`;
两份已同步为高并发开启值,但服务器要修改的仍是启动时实际加载/挂载的那一份。
- 启动日志 `Loaded AppConfig from ...` 可确认路径,`RUNTIME_PERFORMANCE` 可确认实际开关值。
## 4. 开启后做了什么,没做什么
### 单次调用复用
同一次 `read_file`、`ls`、`glob`、`grep`、`bash` 等工具执行期间,技能检查、沙箱初始化、
路径替换和输出脱敏共用一份已验证视图;退出调用后清理作用域,下一次调用重新验证。
不会把一个问答最初的技能启停状态一直沿用到结束。
### 技能版本缓存
每次核对启停 JSON 内容、已知源/目标文件和目录的元数据及标记。
版本一致时不重新发现/解析全部技能、不复制文件、不持有全局刷新锁。
新增、编辑、删除、启停及支持文件变化触发重建,并保留跨进程锁合并并发刷新。
没有采用会延迟下线的 TTL 缓存;下一次工具调用能发现下线或无效配置并拒绝访问。
已经开始执行的工具不因此被强行中断。
缓存仅用于标准 `LocalSkillStorage`,自定义存储实现/子类保守回到原刷新路径。
同一 Gateway 进程内,仅在多个热命中调用真正重叠时合并为一次元数据校验;
该批调用结束后结果立即丢弃,新调用仍重新校验。启停文件哈希不同的调用不会加入旧校验,
校验异常会对该批调用统一 fail-closed,且不会阻止后续调用重试。
热路径仍有 O(文件数) 元数据校验,慢磁盘/网络挂载仍可能耗时;这不是“所有文件操作零开销”,
也没有把受锁保护的重建变成无锁写入。
### 模型计时日志
仅轻量计时/入队在回调中执行,日志 I/O 由独立线程消费,不占工具共享线程池或事件循环。
队列上限 4096 条;日志端长期阻塞时会丢弃诊断并在恢复后汇总告警,不无限积压或阻塞模型输出。
退出时最多等待 0.5 秒尽力刷新,异常退出可能丢失尾部日志。
这是 `ModelTimingCallback` 的优化,不会改变其他回调(如 RunJournal、可选追踪插件)、
子智能体调度池、数据库池或模型上游的连接/速率限制。不能据此保证 100 个会话同时生成。
### Gateway 工具线程池
开启 `asyncio_thread_pool_enabled` 后,在后台启动时、LangGraph 初始化之前设置当前
Gateway 事件循环的默认执行器。同步工具、该循环的 `asyncio.to_thread` 等可使用最多 100 个槽位。
关闭时完全不替换默认执行器;退出时由拥有该事件循环的服务运行器清理。
这不是 AnyIO 的同步 HTTP 路由线程池,也不改变子智能体专用池或独立事件循环的默认池。
线程池扩大只能减少可并行 I/O 的排队,不能让同一把技能锁同时被多人持有,也不会增加模型端的生成配额。
## 5. 日志检查
确认加载日志类似:
```text
RUNTIME_PERFORMANCE skill_view_reuse_enabled=True skill_view_cache_enabled=True model_timing_nonblocking_enabled=True asyncio_thread_pool_enabled=True asyncio_thread_pool_size=100
```
线程池最终是否生效,以另一条启动日志为准(进程 id 随实际启动变化):
```text
GATEWAY_ASYNCIO_EXECUTOR enabled=true max_workers=100 source=DEER_FLOW_ASYNCIO_THREAD_POOL_SIZE pid=12345
```
如果仍显示 `enabled=false max_workers=python_default`,说明线程池开关没打开或改错了配置文件,
不是已经调整到 100。改环境变量后若日志仍取旧值,检查是否重建了 Docker 容器及实际 env-file。
查看以下日志:
| 日志 | 用途 |
| --- | --- |
| `GATEWAY_ASYNCIO_EXECUTOR` | 实际线程池开关、最终上限、配置来源和进程 id;关闭时显示使用 Python 默认,不虚报 100 |
| `SKILL_VIEW_CACHE status=refreshed` | 发生重建;包含 `lock_wait_ms`、`refresh_ms`、`total_ms` 和条目数 |
| `SKILL_VIEW_CACHE status=joined` | 等待期间其他线程/进程已完成重建,本次直接复用 |
| `SKILL_VIEW_CACHE status=hit` | 缓存命中;正常命中为 DEBUG,超过 500ms 的慢命中为 INFO |
| `SKILL_VIEW_CACHE status=failed` | 刷新失败,旧能力不可继续使用,需要排查配置/磁盘 |
| `LLM_CONNECT_START/OK`、`LLM_REQUEST_DONE/FAILED` | 模型调用生命周期、首块和总耗时 |
| `LLM_CALL_RETRY/FAILED`、`OPENAI_TOOL_STREAM_SUMMARY` | 模型重试/兼容处理诊断 |
| `LLM_TIMING_LOG_DROPPED` | 日志队列曾满;检查日志磁盘/采集端速度 |
如需查看正常缓存命中,可短时设置顶层 `log_level: debug`;排查后恢复 `info`。
首块耗时不等于纯网络连接等待,仍包括上游排队、模型处理等阶段。单次 curl 成功也不能证明并发容量足够。
本次日志不记录技能正文、模型正文或 API 密钥。
## 6. 前端替换与回退
1. 先备份服务器当前前端目录和 `runtime-config.js`,并备份实际后台 .env/config.yaml。
2. 校验下载 ZIP 的 SHA-256 与本说明记录一致,再解压到一个新的发布目录。
3. ZIP 包含 `dist/`;Web 根目录应指向其内部,确保根下直接能找到 `index.html`,不要误放成 `dist/dist/`。
4. **保留/恢复服务器自己的 `runtime-config.js`**。核对生产后台地址、认证和嵌入参数;不要把构建机默认值当生产配置。
也不要覆盖生产 .env 的密钥与数据库配置。恢复 runtime-config 后其单文件哈希自然与原包不同。
5. 更新后台代码并按需设置四个开关,使用现有部署方式重启后台,再切换前端发布目录。
6. 刷新浏览器缓存后检查普通问答、智能体问答、技能示例、读取技能、生成文件以及离开后重新打开正在生成的会话。
7. 如需回退性能优化,后台关闭相应开关并重启,无需删除技能文件或技能缓存目录。
如需回退模型协议或前端修复,须恢复对应的已备份代码/前端发布目录,单独关闭性能开关不够。
正式 Linux AMD64 离线镜像/发版仍通过独立的 `knowledge-base-bj/deerflow-offline-deployment`
仓库流程执行;本次前端 ZIP 不是完整后台离线镜像。
## 7. 本地验证结果与限制
### 后台
- 提交前最终专项:技能/线程池/日志/启动配置及技能老回归 **166 项通过**;模型适配、错误分类、
诊断失败保护、SSE 响应头、会话计时与真实 transport **123 项通过**(两组不重叠,共 289 项)。
对应修改模块与测试 Ruff 通过,harness 未增加对 app 的反向依赖。后续没有执行生产模型并发压测。
- 本次技能复用/缓存/配置、日志回调与相关技能目录、大文件安全专项:**79 项通过**;
又补充 1 项开关关闭/重新开启的回退测试,通过缓存文件全部 13 项回归,合计 **80 项**。
- 模型工具参数流/推理流、错误恢复、SSE 传输和启动 worker 校验另 **76 项通过**;相关专项共 **156 项通过**。
- 后续新增线程池接入与配置专项 **45 项通过**(与前述配置测试有重叠,不相加充当独立总数):
覆盖默认关闭、两个环境变量优先级/别名、非法值拒启、启动顺序,以及实际 **24 / 100 个本地阻塞任务同时执行**、
退出后线程全部清理;新模块和相关测试 Ruff 检查通过。另一次联合技能/日志/启动配置回归 **88 项通过**。
该测试只验证本地线程池,不是 100 路模型压测。
- 线程池接入后再次尝试后台全量测试(首次失败即停止):**604 通过、2 跳过、1 失败**,
仍停在修改前已复现的 `test_llm_metrics.py::test_middleware_records_success_with_speed`,不是全量通过。
- 冷启动 8 线程并发只构建一次;真实第二进程复用持久化清单、不再发现技能。
- 下线/无效配置、源文件变更、同长保留 mtime 编辑、目标篡改、Windows junction 保护均有回归覆盖。
- 真实 LangChain 异步模型测试证明:默认工具线程池占满且日志处理器阻塞时,启用计时日志优化仍可开始、输出 token 并结束;未调用真实模型服务。
- 修改后尝试全量测试,设置失败 5 项停止:**871 通过、2 跳过、5 失败**,并非全量通过。
失败包括 `test_llm_metrics.py` 的 3 项记录时序断言、既有 Wiki `auto_sync` 配置与测试预期不一致、
`test_model_management.py` 的 thinking 参数预期不一致。首项在本轮修改前即已复现;
本轮没有修改指标中间件、模型工厂或 Wiki auto_sync 设置,不为过测试擅改这些业务配置。
- 本次针对 #185/MD 的追加回归:模型真实 SDK 回放、工具参数、思考字段、日志、配置、线程池、
错误中间件和传输联合 **145 项通过**。其中新增 SDK 回放 19 项,覆盖 8 种写文件形态,
确认 JSON 尚未闭合时部分 MD 已传出、最终工具名称和参数正确;没有连接线上模型。
### 合成性能对比
Windows 11 / Python 3.12.13,本机临时目录:150 个技能、450 个文件,各模式预热一次后串行 10 次。
旧刷新平均约 **3133ms**,缓存命中平均约 **806ms**(本次约 3.89 倍);
`load_skills` 从 10 次降为 0 次。临时数据已清理。
这不是生产 Linux 性能结论,也不是多人访问模型的承载压测。
### 前端与交付包
- 提交前最终前端专项 **60 项通过**(原 57 项,加同 reader 续接/新页面重播与迟到事件隔离 2 项、
最小技能示例 1 项);真实生产 React 浏览器错误边界测试再次通过。
- 最终全量类型检查仍有既有 **75 个诊断**,本次变更文件无新增诊断;记录在
`.runtime/frontend-final-typecheck-20260927.log`。不要把构建成功等同全量类型检查通过。
- 前端本次流式恢复、React #185 分类、技能示例、流式预览及相关权限等 **32 项专项通过**;
修正新增测试文件一处 `.ts` 导入导致的 TS5097 后,相关 16 项再次通过。
- 最新追加前端专项 **57 项通过**,含真实 SDK SSE → 文件参数 → MD 预览;旧 16ms 设置在连续流中
没有通知刷新,修复后在 JSON 尚未闭合时持续更新。真实 Chrome + 生产 React 中用循环 layoutEffect
触发 #185,验证静态友好兜底、无自动重挂、点击重新载入后恢复。
- 最新 `pnpm build` **通过**:Vite 13,853 个模块,8 分 49 秒;存在大于 500KB 的 chunk 提醒,不是构建失败。
构建日志 `.runtime/frontend-final-build-20260927.log`;本次修复重新打开会话的游标边界后已重新打包。
- 前一版临时 localhost 静态 HTTP 烟测 **通过**:入口 HTML 及引用的主 JS/CSS/runtime 等 5 个资源均 200,
响应字节 SHA-256 与磁盘一致,没有缺失资源;烟测服务已关闭。
- 最新 ZIP 中 **886 个文件**均逐文件比对大小和 SHA-256,与本次 dist 一致;入口/CSS 的 99 个本地资源引用全部存在。包内无 `.env`,
入口为 `dist/index.html`,包含 `dist/runtime-config.js`。
- 全量 `pnpm typecheck` **未通过**:复测还有 **75 个诊断、涉及 29 个本次未修改文件**,
包括现有 nextra / `@codemirror/state` 依赖、旧测试及 legacy QA 类型问题;
本次新增测试文件的 TS5097 已修正并复测,本次前端变更文件无类型诊断。
完整本地日志为 `.runtime/qa/frontend-delivery-typecheck-20260927.log`;
不能把生产构建通过表述成整个项目类型检查通过。
追加修复后本次产品变更文件无全量类型诊断,新增测试及依赖专项 TypeScript 检查为 0 诊断。
- 附加 legacy Chrome CSS 检查 **未通过**,报三个本轮未修改文件
`legacy-html-preview.test.ts`、`legacy-html-preview.ts`、`SolutionProjectLanding.tsx` 中的现代颜色函数字样;
没有报本次 dist CSS 缺失回退,也不能据此宣称完成旧浏览器端到端兼容验收。
交付 ZIP(仓库根目录):
```text
deerflow-frontend-dist-20260927-master-final.zip
```
- ZIP 大小:35,869,210 字节(约 34.2 MiB)。
- 此包替代 `deerflow-frontend-dist-20260927-191207-185-stream-fix.zip` 及更早版本;旧包保留,但不包含最终的游标作用域修复。
- 解压后文件总大小:66,347,693 字节。
- ZIP SHA-256:
```text
7E4C1525BDEB686161FFF5AED12D36087407BBF4F472B87E78CA04DADD1DF371
```
Linux 可用 `sha256sum deerflow-frontend-dist-20260927-master-final.zip`;
Windows 可用 `Get-FileHash -Algorithm SHA256 .\deerflow-frontend-dist-20260927-master-final.zip`。
未连接生产环境进行真实多人模型问答端到端验收,不能宣称所有线上卡顿已被实测消除。
## 8. 关于 20 人同时使用
当前不能承诺 20 人同时问答一定流畅。本次验证了本地代码路径和构建产物,并没有验证你的两台服务器、
上游模型地址/API key 配额、数据库/磁盘及实际技能耗时在 20 路负载下的容量。
部署新版后台、明确开启所需开关后,先在测试环境按 5 → 10 → 20 个独立会话逐级测试:
1. 纯聊天和包含技能读取/写文件的问答分开测试,保持提示长度和输出规模接近真实业务。
2. 每级记录成功/失败/超时数、首字和总耗时的分位数,以及技能 `lock_wait_ms/total_ms`。
3. 同步查看模型端 429/503、排队数量/首 token 延迟,以及应用 CPU、内存、磁盘和数据库等待。
4. 若上游只能并行处理少量请求,应用扩线程无法增加上游容量;应控制并发或扩容模型服务。
20 个在线用户、20 条运行中的问答和 20 个正在模型端生成的请求不是同一指标。
## 9. #185 与 MD 流式追加修复
用户已确认思考模式正常;本节针对仍出现的 #185 和 MD 预览卡住。
- 四个直接流入口统一使用 `STREAM_RENDER_BATCHING=true`,避免安装版本 SDK 的数字 16ms
尾部防抖不断推迟 UI 刷新;保留异步批处理,不改成每个片段同步强制渲染。
- #185 流错误不会再按模型异常直接显示原始错误串;同一轮最多一次自动恢复,重复失败暂停重连,
不主动取消后台任务,不循环重新发送问题。友好提示 30 秒去重。
- 真正渲染/commit 阶段的 #185 由页面错误边界接住,进入静态提示,只能手动重新载入;
不吞其他错误。控制台 `react_ui_render_error` 保留组件堆栈,帮助定位未知的线上循环组件。
- 恢复游标与当前页面的消息累积器绑定:同一页面断线续接不重复拼接工具参数,离开后重新打开或
切换会话则完整重播,不能复用全局旧游标跳过正文前缀;旧 reader 的迟到事件也不污染新页面。
- 后端兼容重复工具名称,避免 `write_filewrite_file` 使工具路由和预览识别失效;
`OPENAI_TOOL_STREAM_SUMMARY repeated_tool_names` 可确认是否命中。也修复了空 `error:{}` 被误判失败。
- 诊断日志写入失败不会覆盖真实模型异常、取消或成功结果。错误脱敏后仍在内存中识别上下文超限,
并识别 `invalid_api_key` 等结构化认证错误,防止本应修剪上下文或停止重试的请求反复排队。
- **实时预览和物理落盘不同**:模型还在生成参数时展示虚拟 MD 内容;参数完整、工具校验通过后,
`write_file` 才正式写沙箱文件。此次不把未完成的 JSON 直接写进最终文件。
本地已经复现并修复上述具体缺陷,但没有线上原始流/组件堆栈,不能保证它们解释全部生产故障。
## 10. `threads` 创建慢:数据库路由与阶段耗时
`POST /api/threads` 会先查/写应用数据库的 `threads_meta`,再调用检查点存储写入空 checkpoint;
它不需要请求模型,也不会在创建时遍历所有旧问答。
当前仓库部署模板是 `database.backend: mysql`,同时 `checkpointer.type: postgres`、
`checkpointer.use_postgres: false`。如果服务器保持这些值且没有 `DEER_FLOW_USE_PG_CHECKPOINTER`
覆盖,实际是 **MySQL 元数据 + SQLite 检查点**,清理 PostgreSQL 不会加速这条链路。
本地开发配置则是 SQLite。线上以实际加载的配置和环境覆盖为准,不凭数据库连接串是否存在判断。
新建/搜索接口新增 `GATEWAY_OPERATION_TIMING` 日志和 `Server-Timing` 响应头,
浏览器 Network 的响应头也能看到,不必先拿服务器日志:
- `metadata_lookup` / `metadata_create`:应用元数据查询/写入。
- `checkpoint_create`:检查点创建,包含 saver 锁/连接池等待和数据库写入,不能等同为纯 SQL 时间。
- `metadata_search`:会话列表的元数据查询。
- `handler_total`:进入处理函数后的耗时;**不含进入函数前的事件循环/鉴权/代理排队**。
模型图构建仍保持原来的任务上下文。新增 `AGENT_BUILD_TIMING`:超过 500ms 以 WARNING 记录,
正常为 DEBUG。已在本地复现同步慢工厂阻塞同事件循环的其他请求,但不能直接裸搬到线程:
延迟工具注册上下文需回传,MCP 初始化存在跨事件循环约束。此处目前是定位日志,不是已经消除阻塞。
本机安装的 SQLite saver 和 PostgreSQL saver 都存在实例级串行锁;后者使用连接池也不代表
同一个 saver 自动并行处理所有检查点。扩大 asyncio 线程池不解除此锁。
尚未取得线上分阶段耗时,因此不能承诺新建 10 秒已经根治,也不能承诺归档即可解决。
新增计时与真实 SQLite 锁测试、已有分析项目/浏览器内部创建入口联合 **43 项通过**,
其中计时专项 14 项;这是本地回归,不是线上延迟验收。
## 11. 30 天已结束会话的 PostgreSQL 归档 SQL
归档脚本单独交付,不进入本源码仓库,也不在启动时执行。使用 `pg_checkpoint_archive.sql` 搬移,
`pg_checkpoint_restore.sql` 按提交批次恢复;两者需与 `pg_checkpoint_common.sql` 放在同一目录。
应用数据库在 MySQL / PostgreSQL 的候选只读导出分别为
`mysql_checkpoint_candidates.sql` / `pg_checkpoint_candidates.sql`。
按用户确认,候选仅包括 30 天未更新、所有运行已结束的会话,排除正在运行、待处理和等待用户输入的会话。
PG 检查点没有应用会话终态字段,因此不能只按 `checkpoint.ts` 猜测,必须先从应用元数据导出候选 ID。
脚本仅在确认实际启用 PG、全部写入服务已停、备份可恢复及双读回退关闭后允许提交。
默认是预览/回滚;提交时三张原始表的全部 namespace 一起搬入新归档 schema,逐行校验后才删源行,
恢复时不覆盖同 ID 的新数据,归档副本保留。不删除沙箱文件。
**范围与风险**:这是 PG 检查点冷归档,不是完整应用归档。它不搬 MySQL `threads_meta/runs`,
因此旧会话列表入口可能仍在,但归档正文需要恢复后才能读取/续聊。归档到同一库的新表也不保证减少总磁盘占用。
SQL 的验证记录与具体执行命令见单独交付包的 README;部署前仍须在生产备份恢复的隔离副本演练。
不要跳过预检直接在生产提交。如果线上仍为 SQLite,这组 PG SQL 不适用。
独立包 `deerflow-db-archive-20260927.zip` 含 SQL、README 和复验测试,不含数据库数据、连接凭据或数据库二进制。
已在 PostgreSQL 16.15 + 安装的 LangGraph checkpoint 3.1.2 原始建表结构上通过 13 项真实执行和 14 项静态检查;
未执行真实 MySQL 候选导出,详见独立包 README。ZIP 共 9 个文件、20,983 字节,SHA-256:
```text
E90A189DA7D7032527EED7CEC9B51DE1A30FB6D48B4382CE06AF1C287B670F59
```
@@ -0,0 +1,73 @@
# 问答停在执行命令或读取技能:排查与修复
日期:2026-09-28。
本次检查针对普通问答和智能体问答:页面停在工具步骤,上游模型没有收到下一轮请求。
以下是代码审查和受控回归确认的触发路径;尚未收到具体生产对话 ID 和现场日志,不能据此认定某次现场故障只有一个根因。
## 已修复的等待路径
| 位置 | 原来的触发条件 | 当前行为 |
| --- | --- | --- |
| LocalSandbox 命令执行 | `subprocess.run` 超时仅终止外层 shell,后代仍持有输出管道;Windows 超时收尾继续无期限 `communicate` | 独立进程组、关闭交互输入、临时文件采集输出;超时清理进程树并限制回收等待 |
| 技能视图刷新 | 进程锁、跨进程文件锁和等待并发校验结果均没有统一等待上限 | 等待最多 30 秒后抛出明确工具错误;不删除正在执行的刷新,不使用未经校验的旧视图 |
| AIO 沙箱 | 上次命令未返回时,后续命令或文件操作无限等待同一把锁 | 锁等待 30 秒后失败;不抢占旧请求的锁,不自动重复执行命令 |
| 文件分段读取 | 超长单行先读完整行才裁剪;FIFO/设备文件可能一直等数据 | 达到当前行输出预算即返回;本地读取用非阻塞打开并检查普通文件,远端分段读取同步限制 |
| 前端结束恢复 | 后端已 success/error,但旧 SSE 仍开着,SDK `isLoading` 一直为真;整段 SSE 期间的 `sendInFlight` 又把同一轮结束快照丢掉 | 核对轮次及运行 ID 后清理重连标记、关闭本地 reader、应用结束快照;不因此取消后端或覆盖新一轮 |
| 前端恢复查询 | `/runs`、检查点或历史请求悬挂,占着 single-flight 恢复槽 | 普通 SDK 请求设置 30 秒超时;恢复失败仍保留后续检查机会,SSE 不受此总时长限制 |
| 后端结束收尾 | 图已返回,但检查点检查、状态写入、日志刷新或标题同步永远不返回,导致 `end` 发不出去 | 终态检查、终态状态写入分别有 10 秒预算;后续元数据收尾共享 10 秒预算,独立 finally 发出 `end` |
技能锁超时走原有工具错误处理链,模型可收到失败结果并决定后续操作;不会把“超时”伪装成成功。
元数据收尾超时只发生在图已经结束后,不给整个研究/子智能体执行增加统一短时限。
终态检查点超时会报告运行失败,不能直接当成模型成功。
## 配置和上线
本次需同时更新后端代码和前端构建产物,重启后端后生效。只刷新页面或调线程数不能替代修复。
本次未操作生产服务、未发布前端包;正式离线发版仍使用独立的 `deerflow-offline-deployment` 仓库。
本地命令保留原来的 600 秒默认执行时限,新版允许在实际加载的配置文件中调整:
```yaml
sandbox:
# 合并到已有 sandbox 段中,保留 use、mounts 等既有字段。
command_timeout_seconds: 600
```
该字段只控制 LocalSandbox 命令执行,必须为有限正数。未配置仍为 600 秒;不要把它误认为模型请求超时。
命令输出每个流最多读入 1 MiB 头尾内容,再经过既有工具输出裁剪。
运行中的命令约每 100ms 检查累计捕获输出,超过 16 MiB 请求终止;这是采样护栏,不是磁盘硬配额。
正常退出后的显式后台进程需要自行重定向输出和管理生命周期。
前端已有的无业务事件检查窗口仍为 90 秒;这是启动后台状态核对的时机,不是宣判任务失败的时限。
30 秒控制请求超时不影响合法的长时间 SSE 和工具执行。
## 仍需现场证据区分的情况
- 磁盘/NFS 本身阻塞的刷新执行者仍可能卡在操作系统 I/O;本次限制的是其他调用的等待,不宣称能终止任意内核 I/O。
- 自定义技能脚本自身请求外部服务不返回,仍会等到本地命令时限;建议技能内各 HTTP 请求也设置连接/读取超时。
- 整个图中的第三方异步工具、模型前中间件和检查点操作没有统一的“无事件即取消”规则,避免误杀合法研究任务。
- 检查点回滚是实际数据修改,保持原有完成/取消语义,不把仍在执行的回滚丢到后台后开放下一轮。
- 已提交给数据库的单条写入未必能被驱动撤销;取消和新轮次检查可阻止旧清理继续发起后续写入,不能保证撤回已经执行的 SQL。
再次出现时,记录对话 ID、运行 ID、最后一步和时间,并对照上游请求日志、运行状态及
`RUN_COMPLETION_TIMEOUT run_id=... phase=terminal_checkpoint|terminal_status|metadata_cleanup`。
技能锁超时会包含 `Timed out waiting for skill view ...`;沙箱命令超时会包含 `Command timed out`。
不要仅凭页面最后显示“读取技能”就断定仍在读文件,它也可能是结束消息没有传到浏览器。
## 回归入口
- 后端 `packages/harness/tests/test_skill_view_waits.py`:锁竞争和并发校验超时、失败后恢复。
- 后端 `packages/harness/tests/test_skill_view_cache.py`、`test_skill_view_tool_reuse.py`:既有启停与缓存安全性。
- 后端 `packages/harness/tests/test_sandbox_command_liveness.py`:真实父子进程、输出、非交互输入及特殊文件/长行。
- 后端 `packages/harness/tests/test_run_completion_timeout.py`:挂起的结束收尾、取消、新一轮隔离及正常长图执行。
- 前端 `src/core/api/control-timeout.test.ts`、`src/core/threads/stream-terminal-recovery.test.ts`:控制请求超时及真实 SDK reader 恢复。
- 前端既有 `stream-recovery`、`stream-display`、`stream-reasoning-replay`、`stream-cursor` 测试继续验证断流重连和消息显示。
## 本机验证结果
- 后端 `python -m pytest packages/harness/tests -q`:**263 passed**。旧同步日志断言补了显式测试配置,避免当前启用异步日志时与后台队列竞速;异步日志仍由独立测试覆盖,未改实际运行配置。
- 前端八个相关测试文件打包后用 Node test runner 执行:**31 passed**;新增测试类型修正后其中三个新增用例再次通过。
- 修改的后端代码和测试通过 Ruff;`git diff --check` 通过。
- 完整前端类型检查未通过:**75 个 TypeScript 错误**,均在本次未修改的文件,涉及 `nextra` 缺失、旧 JSX 类型、既有测试类型等。本次修改文件及新增测试无诊断。详细输出保留在 `frontend-web/.runtime/hang-typecheck.log`(本机文件,不作为发版资产)。
- 命令进程树测试在 Windows 使用真实子进程;Linux 进程组分支已审查,但本机未做 Linux 实跑,AIO 未连接实际容器。未用生产模型调用或真实业务技能执行验证。
+3
View File
@@ -17,6 +17,9 @@ VITE_MOCK_COP_TASK_DETAIL=true
VITE_BACKEND_BASE_URL=http://127.0.0.1:8001
VITE_LANGGRAPH_BASE_URL=http://127.0.0.1:8001/api
# 开发环境普通登录后进入 Vite 内隔离的官方风格页面;/page/* 仍访问当前定制版。
VITE_USE_OFFICIAL_DEERFLOW_ENTRY=false
# 轻应用「新建笔记」系列接口
VITE_NOTE_API_BASE_URL=http://47.88.25.99:8000
# CMZS后端接口
+9
View File
@@ -59,4 +59,13 @@
# 简介模式左侧菜单类型:与菜单管理页「简介模式」的类型 A / B 对应。两套网页分别填 A 或 B。
# 不填或留空时按类型 A,继续使用原来的简介菜单数据。
# VITE_COMPACT_MENU_TYPE=A
# 问答页「极速」模式:true=在模式菜单显示并直连大模型;false 或未设=隐藏。
# 部署后优先改 dist/runtime-config.js 的 VITE_TURBO_MODE_VISIBLE,刷新即可。
# VITE_TURBO_MODE_VISIBLE=false
# VITE_GITHUB_OAUTH_TOKEN=
# --- 官方 DeerFlow 风格页面入口(仅 Vite,不引入 Next.js)---
# true 时,普通登录后进入同一 Vite 应用中隔离的 /official/*。
# 已指定 redirect 或 taskId/goPath 的登录仍保持原来的优先级。
# 仅 .env.development 默认 true;其余环境保持 false。
# VITE_USE_OFFICIAL_DEERFLOW_ENTRY=false
+3
View File
@@ -17,6 +17,9 @@ VITE_MOCK_COP_TASK_DETAIL=false
VITE_BACKEND_BASE_URL=http://10.19.200.5:2026
VITE_LANGGRAPH_BASE_URL=http://10.19.200.5:2026/api
# 内网环境保持当前定制版默认入口。
VITE_USE_OFFICIAL_DEERFLOW_ENTRY=false
# 轻应用「新建笔记」系列接口
VITE_NOTE_API_BASE_URL=http://47.88.25.99:8000
# CMZS后端接口
+3
View File
@@ -14,6 +14,9 @@ VITE_MOCK_SQ_REPORT=false
VITE_BACKEND_BASE_URL=https://ch1.b.uat.4.cn/deerflow
VITE_LANGGRAPH_BASE_URL=https://ch1.b.uat.4.cn/deerflow/api
# 线上保持当前定制版默认入口。
VITE_USE_OFFICIAL_DEERFLOW_ENTRY=false
# 轻应用「新建笔记」系列接口
VITE_NOTE_API_BASE_URL=https://your-intranet-host/note-api
# CMZS后端接口
+3
View File
@@ -17,6 +17,9 @@ VITE_MOCK_COP_TASK_DETAIL=false
VITE_BACKEND_BASE_URL=https://ch1.b.sit.4.cn/deerflow
VITE_LANGGRAPH_BASE_URL=https://ch1.b.sit.4.cn/deerflow/api
# SIT 环境保持当前定制版默认入口。
VITE_USE_OFFICIAL_DEERFLOW_ENTRY=false
# 轻应用「新建笔记」系列接口
VITE_NOTE_API_BASE_URL=https://your-intranet-host/note-api
# CMZS后端接口
+70
View File
@@ -0,0 +1,70 @@
# 我的项目
入口:全量与简洁导航中的「我的项目」,路由 `/page/strategy/qa/projects`。旧地址 `/page/workspace/projects` 仅保留兼容跳转,不再展示独立入口或旧页面。
## 使用流程
1. 新建项目,选择普通问答、策略优选或方案生成。三类项目独立保存和展示,方案生成不会复用或标记策略优选项目。
2. 选择项目默认智能体,填写目标、约束等项目说明。新建项目对话时仍可换用另一个可访问的智能体。
3. 创建后进入项目工作区。普通问答项目可包含多条独立问答;方案项目可包含多条方案记录。最近对话根据 `analysis_project_mode` 回到普通问答分组或方案项目。
4. 方案生成可从「工作流配置 · 报告结构」选择、启停、编辑、删除结构,选择后自动填充项目报告结构配置;也可直接编辑大纲及固定/弹性约束。
5. 策略智能项目保存默认 Strategy Lens;每轮依次进行问题重构、各 Lens 独立分析、策略辩论和方案生成,并分为「意图理解」「多视角策略分析」「完整策略方案」三栏。右栏以文档形式给出执行摘要、目标原则、分阶段行动、资源配置、风险应对、评估复盘、形成依据与备选策略切换条件;原始对话和引用仍可切换查看。
6. 策略智能对话底部提供改变约束、增删视角、重新推演和挑战当前策略快捷动作;项目主页面可直接添加或删除 Lens。
模式在创建后不可修改,前后端都不提供修改入口。项目、对话归属和智能体访问权限在服务端校验;方案结构及每条对话所选智能体也以服务端配置为准。
## 普通问答跨会话记忆
- 仅普通问答项目显示「跨会话项目记忆」。它只受项目自身开关控制,与系统「问答记忆」总开关相互独立;成功完成的会话会在后台整理,状态包括等待沉淀、整理中、已就绪和异常。
- 新会话提问时会召回同一账号、同一项目中其他会话的事实、决策、约束、偏好、术语与未决事项。不会读取其他项目或其他账号,也不会混用普通聊天/智能体的通用记忆。
- 项目设置和问答页头部均可打开「项目记忆」面板,查看精简快照、条目可信度/重要度和来源对话;支持手工新增、编辑、固定、删除、清空以及从当前项目历史对话重建。项目暂停后仍可清空已有数据,但不能重建。
- 编辑、删除或清空原对话时,服务端先让对应记忆失效,再修改消息;重建期间不注入旧快照。固定或手工条目在重建时保留,自动条目按来源 checkpoint 时间判断新旧,较早会话不会因后台任务晚完成而覆盖新的纠正。
- 交互问答在首包前使用有界关键词召回,避免等待远程向量编码;后台整理仍可写入向量供显式或离线检索使用,编码不可用不影响跨会话记忆。输入提示块有条数与 token 上限,历史内容只作为非指令资料,当前消息优先。
## 代码与运行
- `src/pages/AnalysisProjectsPage.tsx`:普通问答与方案项目的新建、设置、历史和工作台布局。
- `src/strategy-selection/StrategyProjectsPage.tsx`:「我的项目」三类项目列表及创建入口。
- `src/pages/analysis-projects/ProjectConversation.tsx`:现有聊天、研究进度、方案画布的项目级组合。
- `src/core/analysis-projects/api.ts`:Gateway 项目 API 与按账号隔离的缓存。
- `src/pages/analysis-projects/ProjectMemoryPanel.tsx`:项目记忆状态、快照、来源和管理操作。
- `src/core/analysis-projects/memory-status.ts`:总开关/项目开关、状态和按钮可用性映射。
- 后端:`/api/analysis-projects`,对话仍通过现有 `/api/threads/{thread_id}/runs/*` 流式接口发送;服务端从不可变归属读取项目配置。
启动新版 Gateway 后自动创建项目表。生产持久化依赖已配置的数据库;memory 模式中的项目随服务重启丢失。前端沿用仓库的 Vite 开发与构建命令。
## 验证范围
后端回归命令:在 `offline-backend-20260512/backend` 执行
```shell
python -m pytest tests/test_analysis_projects.py tests/test_deep_research_report_tool.py tests/test_writing_mode_report_middleware.py -q
```
涵盖内存与 SQLite 存储、跨用户隔离、禁止修改模式、运行时覆盖伪造模式与知识库、更新配置、对话归属与失败清理、固定方案模板及知识库资料进入研究管线。
跨会话记忆专项:
```shell
python -m pytest tests/test_analysis_project_memory.py tests/test_analysis_project_memory_store.py tests/test_analysis_project_memory_runtime.py tests/test_analysis_project_memory_integration.py tests/test_analysis_projects.py -q
```
前端专项:`node --experimental-strip-types --test src/core/analysis-projects/memory-status.test.ts`,并执行 `pnpm build`。
部署前还需使用实际知识库和模型验证三类问答、流式断线续接及方案后续修改。接口模拟与单元测试不能替代真实模型联调。
## 2026-09-14 我的项目与知识库验收修复
日常使用复查:我的项目侧栏历史保持 `qa/projects/groups` 上下文,官方侧栏同样识别项目归属;手机选择历史后收起菜单;项目表单不重复显示侧栏按钮。设置保存后默认智能体同步到新对话选择器。读取项目对话失败提供重试,项目列表失败可原地重试。助手知识检索隔离过期响应,清空输入时禁用检索,无结果提供目录入口。ScrollArea 固定为包含 React 19 引用回调修复的 1.2.18。详细记录见 `docs/daily-usage-audit-20260914.md`。
- 简洁模式的「我的项目」列表、策略工作台和普通问答项目恢复工作区功能导航;窄屏提供展开菜单按钮。
- 普通问答先创建具名项目,再在项目内创建多个对话。列表读取 `/api/analysis-projects` 的 `qa` 项目,不再读取、标记或自动归入全局普通聊天。
- 项目设置更新保留既有知识库绑定。旧「项目空间」入口已移除并跳转「我的项目」;普通问答路由为 `qa/projects/groups/*`,方案生成路由为 `qa/projects/solutions/*`。
- 策略生成卡片统一先显示思考/执行过程,再显示正文;评判、方案及展开视图沿用相同顺序。
- 策略优选工作台拆为三步:意图与任务定义、策略生成与智能评判、综合方案生成。第二步不重复展示问答记录,突出当前任务并扩大策略回答、评判正文;第三步将生成过程默认收起并为方案正文保留固定可视空间,集中完成方案生成、Markdown 编辑、项目保存以及 Markdown/Word 直接下载。保存后的项目卡也提供 Markdown/Word 下载;`?module=solution` 直接进入第三步,历史方案即使任务已修改仍可查看下载。
- 资料库把已完成的策略优选综合方案作为独立「策略生成」来源展示,并标注具体策略项目名称;条目可回到策略项目第三步,也可直接下载 Markdown/Word。普通策略项目线程生成的文件沿用原文件下载链路并显示同类来源标识。
- 方案生成使用独立项目和固定报告结构,通过 `deep_research_report` 直接生成并打开方案画布;策略优选项目不再出现在方案生成分类中。
- 知识库管理页读取上游最新目录,避免远端删除后命中旧缓存;检索调用仍共享短期目录缓存。
- 普通用户访问尚未初始化的助手知识库时显示明确空态及刷新按钮。
验证:后端相关回归 273 项、前端策略/项目回归 31 项通过;真实 BAAI/bge-m3 编码、指定库向量检索与草稿隔离通过;本地隔离服务的问答项目创建/分组、真实问答、方案整理、助手库创建/编辑/文件入库/向量检索/详情均通过。完整验收记录见仓库 `docs/ji-knowledge-projects-acceptance.md`。不在源码或文档中保存接口密钥。
@@ -0,0 +1,977 @@
# 多智能体会商功能与接口梳理
## 1. 文档范围与入口
本文梳理的是常规「多智能体会商 / 圆桌规划」页面 `RoundtablePlanningPage` 的完整三步流程:
1. 任务意图理解
2. 多智能体研讨
3. 方案结果展示
主入口为 `/page/workspace/qa/multi-agent-roundtable`。它也可由任务深链 `3qfx-A`、`rwfx-A`、`xdfx-A` 打开,分别挂载在任务管理下的三情、任务、行动业务路由中;另有 `#/embed/roundtable` 嵌入入口。
任务深链会预填任务名称、任务目标、业务编码和任务 ID,并自动选择对应业务链:
| 深链 | 业务编码 | 嵌入路由 |
| --- | --- | --- |
| `3qfx-A` | `3Q` | `/page/strategy/task-management/plan-task-sentiment/roundtable` |
| `rwfx-A` | `6BF` | `/page/strategy/task-management/plan-task/plan-task-fx/roundtable` |
| `xdfx-A` | `7BF` | `/page/strategy/task-management/plan-task/plan-task-action/roundtable` |
> 本文不展开独立的阵位会商,以及仅展示报告的 8BF 变体。8BF 复用了部分组件,但其第三步为 `report-only`,不属于常规三步会商主路径。
## 2. 公共机制
### 2.1 会商记录与保存
功能:
- 保存任务意图、研讨过程、会商结论、第三步报告及当前已完成步骤。
- 支持新建会商、历史记录切换、重命名、删除和从已有记录继续。
- 普通入口按当前用户隔离;任务深链按任务共享,进入同一任务的用户看到同一批会商和后台任务。
- 更新使用乐观锁;版本不一致时后端返回 `409`,前端应以服务端最新记录为准后再处理。
接口:
| 场景 | 接口 | 调用方式与参数 |
| --- | --- | --- |
| 普通会商列表 | `GET /api/roundtable-drafts` | 无请求体,按当前用户返回。 |
| 新建普通会商 | `POST /api/roundtable-drafts` | JSON:`{ id?, title, furthest_step, step1, step2, step3 }`。 |
| 读取/更新/删除普通会商 | `GET/PUT/DELETE /api/roundtable-drafts/{draftId}` | `PUT` 传需更新字段及 `expected_version`。 |
| 任务会商列表 | `GET /api/roundtable-task-drafts/by-task/{taskId}` | `taskId` 为任务 ID。 |
| 读取最新任务会商 | `GET /api/roundtable-task-drafts/by-task/{taskId}/latest` | 用于任务进入后恢复最近一次会商。 |
| 新建任务会商 | `POST /api/roundtable-task-drafts` | JSON:`{ id?, task_id, title, furthest_step, step1, step2, step3 }`。 |
| 读取/更新/删除任务会商 | `GET/PUT/DELETE /api/roundtable-task-drafts/{draftId}` | `PUT` 为局部更新并携带 `expected_version`。 |
参数说明:
- `step1`、`step2`、`step3` 保存的是各步骤可恢复的完整页面状态,而不是单一文本。
- `furthest_step` 记录用户历史上到达的最远步骤,用于恢复导航可用性。
- `xdfx-A` 的页面记录和后台任务分区使用 URL 原始行动 ID;任务正文及任务详情仍使用解析后的实际任务 ID。
### 2.2 流式调用与附件
功能:
- 大模型回复、意图澄清、推荐、研讨、报告和流程图均通过 SSE 增量返回。
- 用户可停止当前流;前端按事件逐段追加正文、思考、工具调用、澄清卡片和错误信息。
- 有文件时先上传到对应线程,再将后端返回的虚拟路径传给后续流式接口。
附件接口:
```http
POST /api/threads/{threadId}/uploads
Content-Type: multipart/form-data
files: <file> # 可重复,支持多文件
```
响应中的 `{ filename, size, virtual_path }` 应转换后传入流式请求:
```json
{
"files": [
{
"filename": "任务材料.docx",
"size": 10240,
"path": "/mnt/user-data/uploads/任务材料.docx",
"status": "uploaded"
}
]
}
```
所有 JSON 接口经前端 `apiFetch` 调用,自动携带当前登录态;流式接口使用 `ReadableStream` 解析 SSE。
---
# 第一步:任务意图理解
## 1. 功能
第一步将用户的自然语言任务转成可供多个智能体研讨的结构化意图,并确定后续参会智能体来源。
- 创建内置「任务意图理解」智能体会话,接收任务描述、补充材料和多轮追问。
- 流式展示智能体回复、思考过程和工具型澄清问题。
- 澄清卡支持单选、多选、自定义填写、跳过和继续追问;同一轮可返回多张澄清卡。
- 意图完成后生成可编辑的结构化摘要:目标(`objective`)、约束(`constraints`)、可选假设(`assumptions`)。
- 支持手工修改意图摘要后再进入下一步。
- 智能体选择有两条路径:
- **智能体推荐**:依据意图,在可用智能体候选池中推荐参会人及理由。
- **业务链条**:选择已配置的业务链条,带入席位、阶段、阶段目标、协调人提示词和席位模型策略。
- 任务深链模式自动加载任务卡片、预填任务上下文并自动选择业务链;普通模式在“选择参会智能体”弹窗中手工选择“我的”或“公共”业务链。
步骤流转:
```text
输入任务 / 上传材料
↓
意图智能体澄清(可多轮)
↓
结构化意图完成并可编辑
↓
智能体推荐 或 选择业务链
↓
进入第二步
```
### 1.1 业务链功能与两个弹窗
业务链是可命名、可复用、可保存的“智能体编排模板”,不是单纯的智能体名单。一条链定义了谁参会、谁先后执行、哪些席位并行、每阶段应产出什么,以及协调人如何派发和汇总。
第一步中有两个职责不同的业务链入口:
| 入口 | 弹窗/页面 | 作用 | 是否直接开始研讨 |
| --- | --- | --- | --- |
| 页面顶栏“业务链条” | BusinessChainListDialog | 浏览、筛选、启停、发布、进入配置页编辑业务链。 | 否,它是管理入口。 |
| 意图完成后点击“选择参会智能体 / 进入研讨” | RecommendAgentsDialog 的“业务链条”页签,内部为 BusinessChainPicker | 从可用链中选择一条,校验席位后把编排结果带入第二步。 | 是,确认“进入研讨”后开始第二步。 |
#### A. 选择参会智能体弹窗中的“业务链条”页签
功能:
- 弹窗有“智能体推荐”和“业务链条”两个页签;切换到业务链条后不会调用推荐模型,而是读取已保存的链条。
- 可在“我的 / 公共”之间筛选:
- **我的**:当前用户创建的业务链。
- **公共**:已发布、所有账号都可查看和选用的业务链。
- 每张链条卡展示链条名称、描述、席位数和席位编排流:
- 线性链用“智能体 A → 智能体 B → 智能体 C”表示顺序。
- 同一阶段有多个席位时显示“并行 N”分组;阶段之间仍用箭头表示串行依赖。
- 实时智能体名称优先于链条保存时的名称快照;已删除智能体会置灰并提示。
- 仅启用的链条可进入选择列表;选中链条后,至少保留 **2 个仍存在的可用席位** 才能点击“进入研讨”。
- 如果链条配置了默认执行模式,弹窗顶部的执行模式会自动带入该值,用户仍可临时改为:
- flash:快速,不开启思考。
- thinking:思考,低推理强度。
- pro:专业问答,思考 + 中等推理强度。
- ultra:多智能体问答,思考 + 高推理强度,并允许席位调用子代理。
- 如果链条含有有效分层、阶段目标或协调人提示词,选择后按 dag 编排;否则按 chain 线性编排。
- DAG 链在弹窗底部额外选择执行方式:
- **快速 · 双列禁写**:同阶段席位并行,主区双列展示,并行席位不写文件。
- **沙箱 · 单列可写**:同阶段仍并行,但主区单列展示,允许文件产出,右侧沙箱随当前席位切换。
- “配置链条”按钮跳转到业务链编辑页;没有链条时也会显示“去配置业务链条”引导。
确认后传给第二步的运行结果可概括为:
~~~json
{
"agents": [
{
"agent_id": "agent-001",
"name": "态势专家",
"model": "该席位模型覆盖,可为空",
"seat_skill_directive": true,
"human_validation": false,
"seatMode": "该席位推理深度覆盖,可为空"
}
],
"mode": "chain | dag",
"chain": {
"id": "chain-id",
"title": "任务综合研判链"
},
"plan": {
"stages": "仅 dag 时携带的分层执行计划"
},
"dagExecMode": "fast | sandbox",
"seatMode": "本次会商的链条级执行模式",
"gatherFirst": true
}
~~~
其中,席位数组的顺序就是线性链执行顺序;DAG 时按阶段展平。单席位的 seatMode、模型覆盖、技能识别强化和人工校验会一并继承到第二步。
#### B. 顶部“业务链条”管理弹窗
功能:
- 顶栏点击“业务链条”打开,定位为“浏览和管理”,不直接选择参会人。
- 支持按 **我的 / 公共 / 全部(仅管理员)** 筛选,再按 **全部状态 / 启用 / 停用** 过滤。
- 每张卡片展示:
- 名称、描述、席位数量;
- 公共、待审核、已停用等状态;
- 配置人和最后修改时间;
- 智能体编排流:同 Stage 的席位用“并行 N”分组,Stage 之间以箭头连接。
- 本人链条或管理员可在卡片上:
- 启用 / 停用;
- 发布为公共、取消发布;
- 草稿链提交审核,审核中可撤回;
- 点击“设置”跳转到该链条的编辑页。
- 新建和编辑页面包含:
- **基本信息**:标题、描述、默认执行模式、研讨前并行取数开关、总控提示词。
- **链条编排**:从候选智能体池拖拽到 Stage;可新建、删除、上下移动 Stage,移动/拆分/合并席位。
- **阶段目标**:每个 Stage 可填写目标,并可使用 AI 优化;阶段目标会指导协调人该阶段产出什么、如何承接前一阶段结果。
- **总控提示词**:说明协调人如何派发、汇总、收口,支持 Markdown 和 AI 优化。
- **席位级配置**:模型覆盖、推理深度覆盖、会商技能识别强化、人工校验;岗位协同专用入口还可配置岗位归属和产物位置。
- **编排预览**:以流程图检查“同层并行、层间串行”的最终结构。
两个业务链弹窗的页面结构:
~~~text
顶部“业务链条”管理弹窗
┌ 标题和说明 ───────────────────────────────────────────────────────────┐
│ 筛选:我的 / 公共 / 全部(管理员) | 全部状态 / 启用 / 停用 │
├ 业务链卡片列表 ───────────────────────────────────────────────────────┤
│ 名称、席位数、公共/待审核/停用状态、配置人、更新时间、说明 │
│ 智能体 A → [并行:智能体 B、智能体 C] → 智能体 D │
│ 操作:启停 / 发布或撤回 / 设置 │
├───────────────────────────────────────────────────────────────────────┤
│ 底部:配置(新建或跳转到业务链编辑页) │
└───────────────────────────────────────────────────────────────────────┘
“选择参会智能体”弹窗的业务链页签
┌ 顶部:智能体推荐 | 业务链条 | 本次执行模式(快速/思考/专业/多智能体)┐
│ 筛选:我的 / 公共 │
├ 链条选择卡片 ─────────────────────────────────────────────────────────┤
│ 名称、描述、席位数、线性/分层编排、已删除席位告警 │
├───────────────────────────────────────────────────────────────────────┤
│ DAG 链:快速·双列禁写 | 沙箱·单列可写 │
│ 底部:配置链条 | 取消 | 进入研讨 │
└───────────────────────────────────────────────────────────────────────┘
~~~
## 2. 接口、调用时机与传参
### 2.1 初始化意图会话
```http
POST /api/intent/init
Content-Type: application/json
```
```json
{
"model": "模型标识,可选,使用页面当前模型",
"task_context": {
"task_id": "任务深链时的任务 ID",
"task_name": "任务名称",
"task_goal": "任务目标",
"business_code": "3Q | 6BF | 7BF"
},
"intent_agent_id": "roundtable-intent"
}
```
说明:
- 普通会商一般省略 `task_context`,`intent_agent_id` 也可省略并由后端使用内置 `roundtable-intent`。
- 返回 `{ status, agent_id, thread_id }`;后续上传与流式对话都使用此 `thread_id`。
### 2.2 发送任务或回答澄清问题
```http
POST /api/intent/stream
Accept: text/event-stream
```
```json
{
"thread_id": "意图会话线程 ID",
"message": "用户任务描述,或对澄清问题的答案",
"model": "页面当前模型,可选",
"intent_agent_id": "roundtable-intent",
"task_context": {
"task_id": "任务 ID",
"task_name": "任务名称",
"task_goal": "任务目标",
"business_code": "业务编码"
},
"client_turn_id": "前端生成的幂等 ID",
"files": [
{
"filename": "材料.pdf",
"size": 10240,
"path": "/mnt/user-data/uploads/材料.pdf",
"status": "uploaded"
}
]
}
```
主要 SSE 完成帧字段:
```json
{
"status": "asking | clarification | done | error",
"content": "本轮可见回答",
"summary": {
"objective": "任务目标",
"constraints": ["约束 1"],
"assumptions": ["可选假设"]
},
"question": "单个澄清问题",
"clarifications": ["多个澄清问题"],
"clarification_type": "single | multiple | text",
"clarification_context": {},
"options": ["选项 A", "选项 B"],
"allow_custom": true,
"allow_multiple": false,
"error_detail": "失败原因"
}
```
调用要点:
- `client_turn_id` 用于避免重试或网络抖动造成同一用户轮次重复执行。
- 用户选项、填写的自定义答案、点击“继续/跳过”后,仍通过本接口的 `message` 继续同一 `thread_id`。
- 上传接口必须先于本接口调用;流式接口只传上传成功后的虚拟路径。
### 2.3 获取候选智能体和执行推荐
```http
GET /api/agents
```
前端从返回列表中排除内置工具型智能体,将可参会的候选人整理成:
```json
{
"agent_id": "agent-001",
"name": "态势研判专家",
"description": "负责态势信息研判"
}
```
```http
POST /api/recommend/stream
Accept: text/event-stream
```
```json
{
"intent": {
"objective": "形成处置建议",
"constraints": ["时限 24 小时"],
"assumptions": ["数据可访问"]
},
"candidates": [
{
"agent_id": "agent-001",
"name": "态势研判专家",
"description": "负责态势信息研判"
}
],
"model": "页面当前模型"
}
```
推荐完成帧可得到:
```json
{
"status": "done",
"content": "推荐过程说明",
"picks": [
{
"agent_id": "agent-001",
"reason": "与任务目标的匹配理由"
}
]
}
```
普通会商会额外保存推荐记录,便于恢复和审计:
| 接口 | 参数 |
| --- | --- |
| `GET /api/roundtable-drafts/{draftId}/recommendations` | 无请求体,读取该草稿的推荐历史。 |
| `POST /api/roundtable-drafts/{draftId}/recommendations` | `{ objective, status, model?, rationale?, picks, candidates }`。 |
### 2.4 读取、选择和管理业务链
| 接口 | 调用方式与参数 |
| --- | --- |
| `GET /api/roundtable-chains?scope=public\|mine\|pending\|all` | 获取业务链列表;入口页通常读取公开链。 |
| `GET /api/roundtable-chains/{id}` | 获取某条业务链完整定义。 |
| `POST /api/roundtable-chains` | 新建业务链。 |
| `PUT /api/roundtable-chains/{id}` | 更新业务链。 |
| `DELETE /api/roundtable-chains/{id}` | 删除业务链。 |
| `POST /api/roundtable-chains/optimize-prompt` | 使用模型优化总控提示词或阶段目标。 |
读取与弹窗行为:
- “选择参会智能体”弹窗调用 GET /api/roundtable-chains(我的)或 GET /api/roundtable-chains?scope=public(公共),并同时调用 GET /api/agents 校验每个席位是否仍存在。
- 顶部管理弹窗在管理员身份下还可调用 scope=all;scope=pending 用于待审核链的管理场景。非管理员请求 all/pending 时后端会退化为当前用户范围。
- 启用状态为 false 的链条仍可在管理弹窗查看,但不会出现在“选择参会智能体”弹窗中。
创建或更新业务链的核心参数示例:
```json
{
"title": "任务综合研判链",
"description": "先收集信息,再由专家研讨",
"seats": [
{
"agent_id": "agent-001",
"name": "态势专家",
"description": "分析外部态势",
"model": "模型标识",
"seat_skill_directive": true,
"human_validation": true,
"seat_mode": "flash"
}
],
"stages": [["agent-001", "agent-002"], ["agent-003"]],
"stage_goals": ["平行收集信息", "汇总形成判断"],
"coordinator_prompt": "协调人需要按风险、机会、建议汇总",
"seat_mode": "flash",
"gather_first": true
}
```
其中:
- 链条创建时前端使用驼峰字段 stageGoals、coordinatorPrompt、seatMode、gatherFirst;请求发送前映射为接口字段 stage_goals、coordinator_prompt、seat_mode、gather_first。更新时传 null 可清除分层、阶段目标、总控提示词或默认执行模式。
- `stages` 表示 DAG 分层:同层可并行,不同层按顺序执行。
- `seat_mode` / 席位 `seat_mode` 可取 `flash`、`thinking`、`pro`、`ultra`。
- model 为空时,该席位跟随第二步页面的全局模型;有值时仅覆盖该席位。
- seat_skill_directive: true 会在该席位运行时强化其技能识别和使用指令;它与智能体自身同名开关任一开启即可生效。
- `human_validation: true` 表示该席位输出后需要人工确认才可继续。
- `gather_first: true` 表示先让全部席位平行收集信息,再进入正式研讨。
发布、审核和启停均通过更新接口发送局部字段:
| 操作 | PUT /api/roundtable-chains/{id} 请求体 |
| --- | --- |
| 启用 / 停用 | { "enabled": true } / { "enabled": false } |
| 直接发布 / 取消发布 | { "is_public": true } / { "is_public": false } |
| 提交审核 / 撤回审核 | { "pending_review": true } / { "pending_review": false } |
| 管理员审核发布 | { "is_public": true, "pending_review": false } |
提示词优化接口的请求示例:
~~~json
{
"prompt_text": "当前总控提示词或阶段目标原文",
"kind": "coordinator | stage_goal",
"context": "链条标题、各阶段席位及已有阶段目标组成的上下文",
"model_name": "可选模型标识"
}
~~~
返回:
~~~json
{
"optimized": "可直接回填到总控提示词或阶段目标编辑框的文本"
}
~~~
## 3. 页面结构
```text
┌──────────────────────────────── 页面顶部 ────────────────────────────────┐
│ 三步导航:①任务意图理解 ②多智能体研讨 ③方案结果展示 │
│ 业务链 / 新建或重新分析 / 保存草稿 / 历史记录 / 模型选择 / 设置 │
└───────────────────────────────────────────────────────────────────────┘
┌──────────── 左侧入口栏 ───────────┬──────────── 中间工作区 ────────────┬──── 右侧摘要栏 ────┐
│ 任务卡(仅任务深链) │ Step1 对话面板 │ 当前模型选择 │
│ 业务链列表与展开关系 │ - 意图智能体消息 │ 结构化意图摘要 │
│ 会商/分析历史记录 │ - 思考、工具、澄清卡 │ - 目标 │
│ │ - 输入框、附件、发送/停止/继续 │ - 约束 │
│ │ │ - 假设,均可编辑 │
└───────────────────────────────────┴────────────────────────────────────┴────────────────────┘
```
任务深链还会显示任务目的入口和底部 `TaskJumpBar` 快捷跳转栏;任务模式隐藏手工选择业务链的入口,以深链业务编码自动选择链条。
---
# 第二步:多智能体研讨
## 1. 功能
第二步以第一步意图和选定智能体/业务链为输入,创建协调人与多个席位智能体,进行可观察、可干预、可恢复的会商。
- 为每位参会智能体和协调人创建独立线程。
- 支持三种编排模式:
- **推荐模式(recommend)**:协调人自由决定调度哪些推荐智能体。
- **链条模式(chain)**:按业务链指定的席位顺序执行。
- **DAG 模式(dag)**:同阶段并行、跨阶段串行;可传入阶段目标和协调人提示词。
- DAG 支持 `fast` 与 `sandbox` 执行视图。快速模式会限制并行席位写文件;沙箱模式可产出和预览文件。
- 可选“先收集后研讨”:全部席位先并行收集资料,再将有效交付共享给正式研讨。
- 协调人可继续派发、发起人工澄清,或在不再派发时形成共识。
- 用户可暂停/继续本地编排,回答协调人的澄清问题,向协调人追加文字或附件。
- 席位可配置人工验证点;达到验证点后需人工确认。
- 支持打开角色画像抽屉,在下一次运行前调整单席位模型和运行策略。
- 实时展示角色状态、对话、思考、工具调用、阶段流图、并行席位、文件产物和人工参与记录。
- 每次重新推荐或选择链条都会创建独立“研讨运行快照”;历史运行互不覆盖,可在左侧切换查看。
- 支持将长时间会商转入后台任务。后台任务可被同一任务的其他用户看到、跟踪、继续回答或接管。
步骤门槛:
- 只有第一步的 `intentReady` 为真时,第二步可进入。
- 协调人形成共识(`hasConsensus`)后,第三步才可进入。
## 2. 接口、调用时机与传参
### 2.1 初始化所有参会线程
```http
POST /api/multi-agent/init
Accept: text/event-stream
```
```json
{
"agent_names": ["agent-001", "agent-002"],
"main_agent_name": "coordinator",
"model": "页面当前模型,可选"
}
```
说明:
- `agent_names` 为选中的席位智能体标识。
- `main_agent_name` 为协调人标识。
- SSE 依次返回席位就绪、协调人就绪和初始化完成事件,最终得到线程映射:
```json
{
"thread_ids": {
"agent-001": "席位线程 ID",
"agent-002": "席位线程 ID",
"coordinator": "协调人线程 ID"
}
}
```
### 2.2 驱动协调人和席位研讨
```http
POST /api/multi-agent/run/stream
Accept: text/event-stream
```
基础请求:
```json
{
"agent_type": "leader | special",
"agent_name": "coordinator 或席位 agent ID",
"display_name": "页面展示名称",
"d_agent_thread_id": {
"coordinator": "协调人线程 ID",
"agent-001": "席位线程 ID"
},
"new_message": "给协调人或席位的本轮提示词",
"model": "模型标识",
"business_code": "3Q | 6BF | 7BF",
"files": []
}
```
关键参数:
| 参数 | 适用对象 | 用途 |
| --- | --- | --- |
| `agent_type: "leader"` | 协调人 | 由协调人分析现状、派发席位或形成共识。 |
| `agent_type: "special"` | 席位、总结/报告等专用智能体 | 执行专业分析、总结或文件生成。 |
| `d_agent_thread_id` | 全部 | 是“智能体 ID → 线程 ID”的映射,不是单一线程 ID。 |
| `thinking_enabled?` | 席位 | 是否启用思考。 |
| `reasoning_effort?` | 席位 | 推理强度。 |
| `subagent_enabled?` | 席位 | 是否允许席位再调用子智能体。 |
| `seat_skill_directive?` | 席位 | 席位特定的技能使用指令。 |
| `parallel_no_file?` | 并行席位 | 快速并行时限制文件写入。 |
| `peer_deliveries?` | 后续席位/协调人 | 传递前序席位可见交付物。 |
| `synthesis_mode?` | 协调人 | 指示汇总类执行。 |
| `suppress_pre_broadcast?` | 协调人 | 控制汇总前广播。 |
| `skill_stop_names?` | 全部 | 指定需要拦截/停止的技能名称。 |
流式完成后前端重点读取:
```json
{
"dispatched": ["agent-001"],
"content": "可见回答或结论",
"agentName": "本次完成者",
"modelUsed": "实际使用模型",
"clarification": {
"question": "需要用户回答的问题",
"options": ["选项 A"]
},
"invalidDelivery": false
}
```
编排规则:
- 协调人先运行并决定需调度的席位。
- 每个席位完成后,将其“可见回答”作为交付物回传,思考内容不作为席位交付。
- 链条模式按链定义的顺序运行;DAG 模式同层并行、阶段间串行。
- 协调人返回不再派发且无待答澄清时,前端将结果标记为共识。
### 2.3 产物查看与下载
| 接口 | 调用方式 |
| --- | --- |
| `GET /api/threads/{threadId}/artifacts` | 查询指定协调人或席位线程的文件产物。 |
| `GET /api/threads/{threadId}/artifacts/{path}` | 预览或下载文件;下载时前端附加 `?download=true`。 |
### 2.4 后台会商任务
开始后台任务:
```http
POST /api/roundtable-jobs
Content-Type: application/json
```
```json
{
"requestId": "前端请求 ID",
"draftId": "会商草稿 ID",
"taskId": "任务深链时的任务 ID",
"intent": {
"objective": "任务目标",
"constraints": ["约束"],
"assumptions": ["假设"]
},
"agents": [
{
"agentId": "agent-001",
"name": "态势专家"
}
],
"model": "当前模型",
"orchestrationMode": "recommend | chain | dag",
"chain": {
"id": "chain-id",
"title": "业务链名称"
},
"orchestrationPlan": {},
"seedMessage": "首次给协调人的提示",
"threadIds": {
"coordinator": "线程 ID",
"agent-001": "线程 ID"
},
"coordinatorName": "协调人名称",
"seat_skill_directives": {
"agent-001": true
},
"seat_modes": {
"agent-001": "thinking"
},
"gather_first": true,
"business_code": "3Q",
"thinking_enabled": true,
"reasoning_effort": "medium",
"subagent_enabled": false
}
```
后台任务接口:
| 接口 | 调用方式与参数 |
| --- | --- |
| `GET /api/roundtable-jobs?draft_id={draftId}&task_id={taskId}` | 获取草稿/任务关联的后台任务列表。任务模式需带 `task_id`。 |
| `GET /api/roundtable-jobs/{jobId}?task_id={taskId}` | 获取任务快照。 |
| `GET /api/roundtable-jobs/{jobId}/stream?task_id={taskId}` | SSE 跟踪后台任务完整进度。 |
| `POST /api/roundtable-jobs/{jobId}/resume?task_id={taskId}` | JSON:`{ answer }`,用于回答后台任务等待的人工问题。 |
| `POST /api/roundtable-jobs/{jobId}/cancel?task_id={taskId}` | 取消后台任务;前台接管时也会先结束后台执行。 |
状态和阶段:
| 分类 | 枚举 |
| --- | --- |
| 任务状态 | `queued`、`running`、`awaiting_input`、`done`、`error`、`cancelled` |
| 执行阶段 | `initializing`、`leader_thinking`、`dispatching`、`consensus`、`summary_writing`、`report_drawing`、`report_done` |
## 3. 页面结构
```text
┌──────────────────────────────── 页面顶部 ────────────────────────────────┐
│ 三步导航 / 会商记录 / 模型选择 │
└───────────────────────────────────────────────────────────────────────┘
┌──────────── 可收起左栏 ───────────┬──────────── 中间研讨区 ─────────────┬──── 右侧沙箱区 ────┐
│ 意图摘要 │ 顶部:模型、暂停/继续、转后台 │ Step2SandboxLayout │
│ 角色列表与席位筛选 │ DAG 流程缩略图、并行席位切换 │ 文件/网页预览 │
│ 人工参与/验证状态 │ 对话时间线:协调人、席位、工具、思考 │ 产物查看和下载 │
│ 多次研讨运行快照切换 │ 澄清问题卡、人工确认卡 │ 打开时左栏自动收起 │
│ 任务概览(任务深链) │ 底部:追加消息、附件、发送 │ │
└───────────────────────────────────┴─────────────────────────────────────┴────────────────────┘
```
页面展示规则:
- 角色卡显示席位状态、当前轮次、模型和是否等待人工确认。
- 对话区可按角色筛选,思考内容默认折叠,工具调用和交付物以步骤卡展示。
- DAG 研讨可打开流程面板观察阶段、依赖和并行关系。
- 打开沙箱/产物预览后,优先保证产物可读区域,左侧栏自动收起。
---
# 第三步:方案结果展示
## 1. 功能
第三步基于第一步意图、第二步席位交付和协调人共识,生成可阅读、可修改、可下载、可继续加工的最终方案。
- 首次进入常规会商第三步时,启动三个相互独立的产出流:
- **方案总结报告**:生成 Markdown 方案,是默认主界面。
- **经典可视化报告**:生成 HTML 风格报告/仪表盘。
- **结构化仪表盘**:生成 `report-json`,由前端 `DashboardApp` 渲染。
- 三条流以约 0 / 300 / 600 ms 错峰启动,减少瞬间请求峰值,但互不等待。
- 已保存草稿再次打开时优先恢复既有结果,不重复生成;切换到新会商运行时会终止旧运行的活跃流。
- Markdown 报告完成后,常规模式自动调用流程图提取,生成 `flow-json` 结构化流程图。
- 用户可对报告提问、要求重写、修改流程图、重新生成流程图或报告。
- 任务深链可根据业务链层次和业务编码生成任务 → 目的 → 行为体等结构;行动业务会使用行动名称作为根节点。
- 若任务和技能条件满足,可选择“现在导入 / 稍后导入 / 不再导入”,把报告和流程结构交给 `roundtable-structure` 进行任务结构化入库。
- 左侧汇集协调人共识、各席位交付和文件;右侧可预览 Markdown、流程图及沙箱产物。
- 支持下载会商文档和智能体产物。任务模式还可将选定产物提交到任务交付接口。
## 2. 接口、调用时机与传参
### 2.1 初始化第三步专用智能体
| 目标 | 接口 | 返回 |
| --- | --- | --- |
| Markdown 方案总结 | `POST /api/multi-agent/summary/init` | `{ agent_name, thread_id }` |
| 经典可视化报告 | `POST /api/multi-agent/report/init` | `{ agent_name, thread_id }` |
| 结构化仪表盘 | `POST /api/multi-agent/dashboard/init` | `{ agent_name, thread_id }` |
| 任务结构化入库 | `POST /api/multi-agent/structure/init` | `{ agent_name, thread_id }` |
初始化请求体均为:
```json
{}
```
各专用智能体初始化后,均继续通过统一接口 `POST /api/multi-agent/run/stream` 运行。
### 2.2 生成或修改 Markdown 方案总结
```json
{
"agent_type": "special",
"agent_name": "roundtable-summary",
"display_name": "方案总结报告",
"d_agent_thread_id": {
"roundtable-summary": "总结线程 ID"
},
"new_message": "包含结构化意图、席位交付、协调人共识、链条提示词的生成或修改指令",
"model": "当前模型",
"business_code": "3Q",
"files": []
}
```
功能覆盖:
- 首次生成 Markdown 报告。
- 在报告下方继续问答或下达修改指令。
- 支持暂停、继续、重新生成和生成后下载 Docx。
- 上述问答与修改沿用同一总结线程,保证上下文连续。
### 2.3 生成经典报告和仪表盘
经典报告请求使用 `roundtable-report`:
```json
{
"agent_type": "special",
"agent_name": "roundtable-report",
"d_agent_thread_id": {
"roundtable-report": "报告线程 ID"
},
"new_message": "包含意图、席位交付、共识、主题模式、修订要求和已有 HTML 的提示词",
"model": "当前模型"
}
```
结构化仪表盘请求使用 `roundtable-dashboard`:
```json
{
"agent_type": "special",
"agent_name": "roundtable-dashboard",
"d_agent_thread_id": {
"roundtable-dashboard": "仪表盘线程 ID"
},
"new_message": "包含意图、席位交付、共识,或对已有仪表盘的修订要求",
"model": "当前模型"
}
```
前端依据返回的 HTML 或 `report-json` 分别渲染经典报告和 `DashboardApp`,并提供全屏、在新窗口打开、继续优化以及切回经典报告的操作。
### 2.4 自动提取和修改流程图
```http
POST /api/flow-extract/stream
Accept: text/event-stream
```
```json
{
"report_md": "Markdown 完整报告",
"seat_deliveries": [
{
"sender": "态势专家",
"content": "席位可见交付"
}
],
"chain_levels": [["信息收集"], ["研判决策"]],
"coordinator_prompt": "协调人对结构层次的要求",
"business_code": "3Q",
"root_label": "任务根节点名称",
"model": "当前模型",
"current_graph": {
"nodes": [],
"edges": []
},
"instruction": "用户对当前流程图的修改要求"
}
```
参数使用规则:
- 首次自动提取可不传 `current_graph` 和 `instruction`。
- 修改现有图时传当前图和用户要求。
- 任务模式的 `chain_levels` 由业务链阶段目标推导;若 `coordinator_prompt` 明确规定层次,则优先使用该要求。
流中包含提取增量、校验过程、模型切换和最终图数据,最终形成前端 `FlowGraphPanel` 使用的 `flow-json`。
### 2.5 判断用户在第三步的意图
| 接口 | 请求体 | 返回用途 |
| --- | --- | --- |
| `POST /api/flow-extract/intent` | `{ message, has_report, has_flow, can_ingest, model? }` | 分类为 `regenerate_report`、`modify_flow`、`regenerate_flow`、`ingest`、`qa`、`unclear`,决定继续调用哪一条流。 |
| `POST /api/flow-extract/ingest-intent` | `{ message, model? }` | 在已提示导入或稍后导入时,判断 `start_ingest`、`add_data`、`other`、`unclear`。 |
### 2.6 任务结构化入库
先检查是否可入库:
```http
POST /api/flow-extract/ingest-eligibility
```
```json
{
"task_id": "任务 ID",
"business_code": "3Q",
"chain_name": "业务链名称",
"coordinator_prompt": "协调人提示词",
"report_title": "报告标题",
"model": "当前模型"
}
```
返回:
```json
{
"can_ingest": true,
"skill": "命中的入库技能",
"reason": "是否可入库的原因"
}
```
符合条件且用户确认后,初始化 `roundtable-structure` 线程,并调用统一研讨流:
```json
{
"agent_type": "special",
"agent_name": "roundtable-structure",
"d_agent_thread_id": {
"roundtable-structure": "结构化入库线程 ID"
},
"new_message": "包含 taskId、业务编码/链条/技能提示、最新 Markdown 报告、flow-json 和用户补充说明的指令",
"model": "当前模型",
"business_code": "3Q"
}
```
注意:前端不会直接调用某个入库技能,而是交给 `roundtable-structure` 智能体在具备完整报告和流程图上下文后执行。
### 2.7 任务交付物提交
任务深链启用交付配置时,第三步可以查询和更新任务产物提交状态。
| 接口 | 调用方式与参数 |
| --- | --- |
| `GET /api/roundtable/tasks/{taskId}/artifacts?submitted=true\|false` | 获取任务已提交或待提交产物。 |
| `PUT /api/roundtable/tasks/{taskId}/artifacts/submission` | 更新某个产物的提交状态。 |
```json
{
"taskId": "任务 ID",
"submitted": true,
"registerOnly": false,
"submissionId": "已有提交记录 ID,可选",
"taskDeliveryId": "外部交付 ID,可选",
"expectedVersion": 3,
"artifact": {
"sessionId": "会商会话 ID",
"threadId": "产物所属线程 ID",
"nodeKey": "流程节点标识,可选",
"sourceKey": "来源标识",
"path": "/mnt/user-data/outputs/report.md",
"name": "会商报告.md",
"mimeType": "text/markdown",
"revision": 1
}
}
```
## 3. 页面结构
默认的“方案总结”视图:
```text
┌──────────────────────────────── 页面顶部 ────────────────────────────────┐
│ 步骤导航 / 报告标题 / 模型 / 总结报告、流程图、最终方案等视图切换 │
└───────────────────────────────────────────────────────────────────────┘
┌──────────── 可收起左栏 ───────────┬──────────── 中间主内容 ─────────────┬──── 右侧结果区 ────┐
│ 协调人共识 │ Markdown 总结报告对话流 │ Markdown 预览 │
│ 各席位交付 │ 报告问答、修改、重生成入口 │ 或 FlowGraphPanel │
│ 各线程文件/产物 │ 输入框与附件 │ 文件/沙箱预览 │
│ │ │ │
└───────────────────────────────────┴─────────────────────────────────────┴────────────────────┘
```
其他结果视图:
| 视图 | 中央区域 | 右侧/扩展区域 |
| --- | --- | --- |
| 最终方案 | 汇总后的静态方案和文件清单 | 可查看来源与下载产物。 |
| 经典可视化报告 | 报告生成对话、修订指令 | HTML/沙箱预览,支持全屏和下载。 |
| 结构化仪表盘 | 仪表盘优化对话 | `DashboardApp` 或 HTML 视图,可新窗口打开。 |
| 流程图 | 报告问答和流程图修改指令 | `FlowGraphPanel` 显示节点、关系和层级。 |
常规第三步会隐藏外层研讨左导航,以扩大结果阅读区域;任务深链保留任务环境相关的交付与跳转能力。
## 4. 三步数据衔接
```text
第一步
结构化意图 + 已选智能体/业务链 + 意图线程
│
▼
第二步
席位线程 + 协调人线程 + 对话/交付 + 共识 + 研讨运行快照
│
▼
第三步
Markdown 报告 + HTML 报告 + report-json 仪表盘 + flow-json 流程图 + 任务入库/交付
```
关键约束:
- 第一步未形成有效意图,不能开始第二步。
- 第二步未形成协调人共识,不能进入第三步。
- 第三步的所有产出都应绑定对应的第二步研讨运行快照,避免新一轮会商覆盖历史结果。
- 后端发生模型降级/切换时,前端应保留已收到的流内容并显示实际使用模型或切换提示。
## 5. 主要前端模块索引
| 范围 | 主要文件/目录 |
| --- | --- |
| 页面编排 | `src/roundtable-planning/pages/RoundtablePlanningPage.tsx` |
| 第一步 | `components/Step1Panel.tsx`、`hooks/useStep1Intent.ts`、`api/intent.ts` |
| 推荐与业务链 | `api/recommend.ts`、`components/RecommendAgentsDialog.tsx`、`api/chains.ts` |
| 第二步 | `components/Step2Panel.tsx`、`components/Step2SandboxLayout.tsx`、`hooks/useStep2Orchestration.ts` |
| 多智能体与后台任务 API | `api/multi-agent.ts`、`api/roundtable-jobs.ts` |
| 第三步 | `components/Step3Panel.tsx`、`components/HighFidelityReport.tsx`、`hooks/useStep3Summary.ts`、`hooks/useStep3Report.ts`、`hooks/useStep3Dashboard.ts` |
| 流程图与任务入库 | `api/flow-extract.ts`、`api/ingest.ts` |
| 草稿持久化 | `api/drafts.ts`、`api/task-drafts.ts`、相关草稿 hooks |
+33 -5
View File
@@ -13,11 +13,39 @@
} catch (e) {}
})();
</script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>CM 助手</title>
<script src="/runtime-config.js" data-runtime-config></script>
</head>
<script>
// React 19's development profiler uses the User Timing Level 3
// `performance.measure(name, options)` overload. Firefox 101 only
// implements the legacy string-marker form and otherwise throws a
// DOMException during every scheduled update. Detect that overload
// before React loads and disable its optional profiler when unavailable.
(function () {
try {
var perf = window.performance;
if (!perf || typeof perf.measure !== "function" || typeof perf.now !== "function") return;
var probe = "__deerflow_user_timing_options_probe__";
var now = perf.now();
perf.measure(probe, { start: now, end: now });
if (typeof perf.clearMeasures === "function") perf.clearMeasures(probe);
} catch (e) {
try {
Object.defineProperty(window.performance, "measure", {
value: undefined,
configurable: true,
});
} catch (defineError) {
try {
window.performance.measure = undefined;
} catch (assignmentError) {}
}
}
})();
</script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>CM 助手</title>
<script src="/runtime-config.js" data-runtime-config></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
+355 -350
View File
@@ -28,10 +28,10 @@
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-scroll-area": "1.2.18",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-slot": "1.3.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
@@ -69,7 +69,9 @@
"gsap": "^3.13.0",
"hast": "^1.0.0",
"katex": "^0.16.28",
"leaflet": "^1.9.4",
"lucide-react": "^0.562.0",
"maplibre-gl": "^6.9.0",
"mermaid": "^11.15.0",
"motion": "^12.26.2",
"nanoid": "^5.1.6",
@@ -1702,6 +1704,92 @@
"@lezer/lr": "^1.4.0"
}
},
"node_modules/@mapbox/jsonlint-lines-primitives": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz",
"integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==",
"license": "MIT",
"engines": {
"node": ">= 22"
}
},
"node_modules/@mapbox/point-geometry": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz",
"integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
"license": "ISC"
},
"node_modules/@mapbox/tiny-sdf": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz",
"integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==",
"license": "BSD-2-Clause"
},
"node_modules/@mapbox/unitbezier": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz",
"integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==",
"license": "BSD-2-Clause"
},
"node_modules/@mapbox/vector-tile": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/@mapbox/vector-tile/-/vector-tile-3.0.0.tgz",
"integrity": "sha512-Qf10S1uIHMk20ri/IVBnpS+esUEkVaR5Hftmz88jTInrpmWgPGJfPe3LVjjlE77trLx8tH6qjTG7uWH9hIq/0Q==",
"license": "BSD-3-Clause",
"dependencies": {
"@mapbox/point-geometry": "~1.1.0",
"@types/geojson": "^7946.0.16",
"pbf": "^5.0.0"
}
},
"node_modules/@maplibre/geojson-vt": {
"version": "6.1.1",
"resolved": "https://registry.npmmirror.com/@maplibre/geojson-vt/-/geojson-vt-6.1.1.tgz",
"integrity": "sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==",
"license": "ISC",
"dependencies": {
"kdbush": "^4.1.0"
}
},
"node_modules/@maplibre/maplibre-gl-style-spec": {
"version": "26.4.2",
"resolved": "https://registry.npmmirror.com/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-26.4.2.tgz",
"integrity": "sha512-6J0vZqMZvRAJKtdWJdDGHEh1YJ2ZHG08/GOur8gCArYhO8ZkM//OXqtI2AAEz4jc2G+Wq4MfcePg8qNK5TZ4Kg==",
"license": "ISC",
"dependencies": {
"@mapbox/jsonlint-lines-primitives": "^2.0.3",
"@mapbox/unitbezier": "^1.0.0",
"json-stringify-pretty-compact": "^4.0.0",
"minimist": "^1.2.8",
"quickselect": "^3.0.0",
"tinyqueue": "^3.0.0"
},
"bin": {
"gl-style-format": "dist/gl-style-format.mjs",
"gl-style-migrate": "dist/gl-style-migrate.mjs",
"gl-style-validate": "dist/gl-style-validate.mjs"
}
},
"node_modules/@maplibre/mlt": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/@maplibre/mlt/-/mlt-1.2.1.tgz",
"integrity": "sha512-5n5dgolE2EYxwCKgx8vlwURCB8A+kyfJJyThlYimjlGgOcOe2Bhw9VxxnnHC91OC9PgHg+nVdgVPTYPkIo62vg==",
"license": "(MIT OR Apache-2.0)",
"dependencies": {
"@mapbox/point-geometry": "^1.1.0"
}
},
"node_modules/@maplibre/vt-pbf": {
"version": "4.3.2",
"resolved": "https://registry.npmmirror.com/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz",
"integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==",
"license": "MIT",
"dependencies": {
"@mapbox/point-geometry": "^1.1.0",
"@types/geojson": "^7946.0.16",
"pbf": "^5.1.0"
}
},
"node_modules/@marijn/find-cluster-break": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
@@ -1787,23 +1875,6 @@
}
}
},
"node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-avatar": {
"version": "1.1.11",
"license": "MIT",
@@ -1893,23 +1964,6 @@
}
}
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
@@ -1971,23 +2025,6 @@
}
}
},
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
@@ -2086,23 +2123,6 @@
}
}
},
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-direction": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
@@ -2165,23 +2185,6 @@
}
}
},
"node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-dropdown-menu": {
"version": "2.1.16",
"license": "MIT",
@@ -2245,23 +2248,6 @@
}
}
},
"node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-focus-guards": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
@@ -2322,23 +2308,6 @@
}
}
},
"node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-hover-card": {
"version": "1.1.15",
"license": "MIT",
@@ -2404,23 +2373,6 @@
}
}
},
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-icons": {
"version": "1.3.2",
"license": "MIT",
@@ -2520,23 +2472,6 @@
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
@@ -2604,23 +2539,6 @@
}
}
},
"node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-portal": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
@@ -2666,23 +2584,6 @@
}
}
},
"node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-presence": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
@@ -2816,36 +2717,21 @@
}
}
},
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-scroll-area": {
"version": "1.2.10",
"version": "1.2.18",
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz",
"integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==",
"license": "MIT",
"dependencies": {
"@radix-ui/number": "1.1.1",
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.1"
"@radix-ui/number": "1.1.3",
"@radix-ui/primitive": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.5",
"@radix-ui/react-context": "1.2.2",
"@radix-ui/react-direction": "1.1.4",
"@radix-ui/react-presence": "1.1.10",
"@radix-ui/react-primitive": "2.1.10",
"@radix-ui/react-use-callback-ref": "1.1.4",
"@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -2862,10 +2748,38 @@
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/number": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz",
"integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/primitive": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz",
"integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==",
"license": "MIT"
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz",
"integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-context": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz",
"integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@@ -2876,12 +2790,28 @@
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-direction": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz",
"integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-presence": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz",
"integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.3"
"@radix-ui/react-use-layout-effect": "1.1.4"
},
"peerDependencies": {
"@types/react": "*",
@@ -2898,13 +2828,49 @@
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-primitive": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz",
"integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
"@radix-ui/react-slot": "1.3.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz",
"integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz",
"integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@@ -2992,23 +2958,6 @@
}
}
},
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-separator": {
"version": "1.1.8",
"license": "MIT",
@@ -3031,10 +2980,12 @@
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.2.4",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz",
"integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
"@radix-ui/react-compose-refs": "1.1.5"
},
"peerDependencies": {
"@types/react": "*",
@@ -3046,6 +2997,21 @@
}
}
},
"node_modules/@radix-ui/react-slot/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz",
"integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-switch": {
"version": "1.2.6",
"license": "MIT",
@@ -3109,23 +3075,6 @@
}
}
},
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tabs": {
"version": "1.1.13",
"license": "MIT",
@@ -3190,23 +3139,6 @@
}
}
},
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-toggle": {
"version": "1.1.10",
"license": "MIT",
@@ -3293,23 +3225,6 @@
}
}
},
"node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
@@ -3332,23 +3247,6 @@
}
}
},
"node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip": {
"version": "1.2.8",
"license": "MIT",
@@ -3417,23 +3315,6 @@
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
@@ -3622,23 +3503,6 @@
}
}
},
"node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
@@ -5557,6 +5421,15 @@
"version": "1.2.1",
"license": "BSD-2-Clause"
},
"node_modules/bidi-js": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.1.0.tgz",
"integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==",
"license": "MIT",
"dependencies": {
"require-from-string": "^2.0.2"
}
},
"node_modules/browserslist": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
@@ -6459,6 +6332,12 @@
"node": ">= 0.4"
}
},
"node_modules/earcut": {
"version": "3.2.3",
"resolved": "https://registry.npmmirror.com/earcut/-/earcut-3.2.3.tgz",
"integrity": "sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==",
"license": "ISC"
},
"node_modules/electron-to-chromium": {
"version": "1.5.349",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.349.tgz",
@@ -6853,6 +6732,12 @@
"node": ">= 0.4"
}
},
"node_modules/gl-matrix": {
"version": "3.4.4",
"resolved": "https://registry.npmmirror.com/gl-matrix/-/gl-matrix-3.4.4.tgz",
"integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==",
"license": "MIT"
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -7351,6 +7236,12 @@
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
},
"node_modules/json-stringify-pretty-compact": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==",
"license": "MIT"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -7390,6 +7281,12 @@
"katex": "cli.js"
}
},
"node_modules/kdbush": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/kdbush/-/kdbush-4.1.0.tgz",
"integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==",
"license": "ISC"
},
"node_modules/khroma": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
@@ -7432,6 +7329,12 @@
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
"integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="
},
"node_modules/leaflet": {
"version": "1.9.4",
"resolved": "https://registry.npmmirror.com/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
"license": "BSD-2-Clause"
},
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
@@ -7766,6 +7669,39 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/maplibre-gl": {
"version": "6.9.0",
"resolved": "https://registry.npmmirror.com/maplibre-gl/-/maplibre-gl-6.9.0.tgz",
"integrity": "sha512-vFMwMK0Zs+NM/rOMSdtu8bO30DIexhBEVi5KC6f70/XtI+L/K2wC3LsDCAXFZ4s8ik5gAuDugfCNbpllpdJ9bA==",
"license": "BSD-3-Clause",
"dependencies": {
"@mapbox/point-geometry": "^1.1.0",
"@mapbox/tiny-sdf": "^2.2.0",
"@mapbox/unitbezier": "^1.0.0",
"@mapbox/vector-tile": "^3.0.0",
"@maplibre/geojson-vt": "^6.1.1",
"@maplibre/maplibre-gl-style-spec": "^26.4.2",
"@maplibre/mlt": "^1.2.1",
"@maplibre/vt-pbf": "^4.3.2",
"@types/geojson": "^7946.0.16",
"bidi-js": "^1.1.0",
"earcut": "^3.2.3",
"gl-matrix": "^3.4.4",
"kdbush": "^4.1.0",
"murmurhash-js": "^1.0.0",
"pbf": "^5.1.2",
"potpack": "^2.1.0",
"quickselect": "^3.0.0",
"tinyqueue": "^3.0.0"
},
"engines": {
"node": ">=16.14.0",
"npm": ">=8.1.0"
},
"funding": {
"url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1"
}
},
"node_modules/markdown-it": {
"version": "14.1.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
@@ -8713,6 +8649,15 @@
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
@@ -8771,6 +8716,12 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/murmurhash-js": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==",
"license": "MIT"
},
"node_modules/mustache": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
@@ -8943,6 +8894,18 @@
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="
},
"node_modules/pbf": {
"version": "5.1.2",
"resolved": "https://registry.npmmirror.com/pbf/-/pbf-5.1.2.tgz",
"integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==",
"license": "BSD-3-Clause",
"dependencies": {
"resolve-protobuf-schema": "^2.1.0"
},
"bin": {
"pbf": "bin/pbf"
}
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -9035,6 +8998,12 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/potpack": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/potpack/-/potpack-2.1.0.tgz",
"integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==",
"license": "ISC"
},
"node_modules/prismjs": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
@@ -9218,6 +9187,12 @@
"prosemirror-transform": "^1.1.0"
}
},
"node_modules/protocol-buffers-schema": {
"version": "3.6.1",
"resolved": "https://registry.npmmirror.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz",
"integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==",
"license": "MIT"
},
"node_modules/proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
@@ -9235,6 +9210,12 @@
"node": ">=6"
}
},
"node_modules/quickselect": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
"license": "ISC"
},
"node_modules/raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
@@ -9618,6 +9599,24 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"license": "MIT",
"dependencies": {
"protocol-buffers-schema": "^3.3.1"
}
},
"node_modules/robust-predicates": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",
@@ -9971,6 +9970,12 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyqueue": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/tinyqueue/-/tinyqueue-3.0.0.tgz",
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
"license": "ISC"
},
"node_modules/tiptap-markdown": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/tiptap-markdown/-/tiptap-markdown-0.9.0.tgz",
+14 -2
View File
@@ -11,6 +11,12 @@
"build:network": "node --max-old-space-size=8192 ./node_modules/vite/bin/vite.js build --mode network",
"preview": "vite preview",
"typecheck": "tsc --noEmit",
"test:input-mode": "node --experimental-strip-types --test src/components/workspace/input-mode.test.ts",
"test:answer-feedback": "node --experimental-strip-types --test src/components/workspace/agents/agent-feedback-visibility.test.ts src/components/workspace/messages/answer-feedback-eligibility.test.ts src/strategy-components/api/answer-feedback-batching.test.ts",
"test:retrieval-debug": "node --experimental-strip-types --test src/core/llmwiki/retrieval-debug.test.ts",
"test:html-preview": "pnpm exec esbuild src/core/utils/legacy-html-preview.test.ts --bundle --platform=node --format=esm --outfile=.runtime/unit-tests/legacy-html-preview.test.mjs && node --test .runtime/unit-tests/legacy-html-preview.test.mjs",
"test:visualizations": "node --experimental-strip-types --test src/components/workspace/visualizations/visualization-spec.test.ts",
"test:report-collaboration": "npx esbuild src/report-collaboration/tests/event-reducer.test.ts --bundle --platform=node --format=esm --outfile=.runtime/unit-tests/event-reducer.test.mjs && npx esbuild src/report-collaboration/tests/message-adapter.test.ts --bundle --platform=node --format=esm --outfile=.runtime/unit-tests/message-adapter.test.mjs && node --test .runtime/unit-tests/event-reducer.test.mjs .runtime/unit-tests/message-adapter.test.mjs",
"check:chrome109-css": "node scripts/check-chrome109-css.mjs",
"check:legacy-chrome-css": "node scripts/check-chrome109-css.mjs"
},
@@ -35,10 +41,10 @@
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-scroll-area": "1.2.18",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-slot": "1.3.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
@@ -76,7 +82,10 @@
"gsap": "^3.13.0",
"hast": "^1.0.0",
"katex": "^0.16.28",
"leaflet": "^1.9.4",
"lucide-react": "^0.562.0",
"maplibre-gl": "^6.9.0",
"marked": "16.4.2",
"mermaid": "^11.15.0",
"motion": "^12.26.2",
"nanoid": "^5.1.6",
@@ -120,5 +129,8 @@
"vite": "^6.0.0",
"vite-plugin-source-identifier": "^1.1.2"
},
"overrides": {
"@radix-ui/react-slot": "1.3.3"
},
"packageManager": "pnpm@10.26.2"
}
+3
View File
@@ -0,0 +1,3 @@
# Keep indirect Radix primitives on the React 19 ref-stability fix too.
overrides:
'@radix-ui/react-slot': 1.3.3
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 52 52">
<defs>
<linearGradient id="ap-add" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#f5222d"/>
<stop offset="1" stop-color="#a8071a"/>
</linearGradient>
</defs>
<rect width="52" height="52" rx="8" fill="#ffcfd1"/>
<path fill="url(#ap-add)" d="M26 14a2 2 0 0 1 2 2v8h8a2 2 0 1 1 0 4h-8v8a2 2 0 1 1-4 0v-8h-8a2 2 0 1 1 0-4h8v-8a2 2 0 0 1 2-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 503 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52" height="52" viewBox="0 0 52 52"><defs><style>.a{fill:#ffcfd1;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f5222d"/><stop offset="1" stop-color="#a8071a"/></linearGradient></defs><g transform="translate(0 -116.002)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(2.236 118.236)"><path class="b" d="M9204.336-22218.762a6.4,6.4,0,0,1-2.489-.691,6.242,6.242,0,0,1-2-1.615,6.363,6.363,0,0,1-1.206-2.283,6.357,6.357,0,0,1-.246-1.863c-.047-.033-.094-.066-.139-.1a6.4,6.4,0,0,1-2.272-3.4,6.418,6.418,0,0,1,.268-4.076,6.336,6.336,0,0,1,1.233-1.912,6.383,6.383,0,0,1-.4-1.973,6.41,6.41,0,0,1,.53-2.809,6.441,6.441,0,0,1,1.7-2.289,6.424,6.424,0,0,1,1.751-1.057,4.969,4.969,0,0,1,.343-1.289,4.965,4.965,0,0,1,1.334-1.859,5.038,5.038,0,0,1,2.016-1.082,5.037,5.037,0,0,1,2.289-.074,5.024,5.024,0,0,1,2.089.943,4.947,4.947,0,0,1,.871.861,5.147,5.147,0,0,1,.876-.861,5,5,0,0,1,2.083-.943,5.035,5.035,0,0,1,2.289.074,5.06,5.06,0,0,1,2.021,1.082,5.031,5.031,0,0,1,1.334,1.859,5.008,5.008,0,0,1,.343,1.289,6.424,6.424,0,0,1,1.751,1.057,6.441,6.441,0,0,1,1.7,2.289,6.405,6.405,0,0,1,.524,2.809,6.434,6.434,0,0,1-.4,1.973,6.315,6.315,0,0,1,1.234,1.912,6.418,6.418,0,0,1,.268,4.076,6.4,6.4,0,0,1-2.272,3.4l-.143.105a6.214,6.214,0,0,1-.248,1.859,6.26,6.26,0,0,1-1.2,2.283,6.271,6.271,0,0,1-2.01,1.615,6.354,6.354,0,0,1-2.485.691c-.14.006-.278.012-.418.012a6.213,6.213,0,0,1-2.139-.375,6.394,6.394,0,0,1-2.2-1.34,6.4,6.4,0,0,1-.915-1.066,6.415,6.415,0,0,1-.91,1.066,6.427,6.427,0,0,1-2.206,1.34,6.283,6.283,0,0,1-2.143.377C9204.609-22218.748,9204.473-22218.752,9204.336-22218.762Zm6.768-6.342a4.232,4.232,0,0,0,.346,1.652,4.123,4.123,0,0,0,.972,1.379,4.116,4.116,0,0,0,1.44.877,4.062,4.062,0,0,0,1.67.234,4.159,4.159,0,0,0,1.63-.447,4.159,4.159,0,0,0,1.313-1.061,4.227,4.227,0,0,0,.787-1.49c.02-.07.037-.139.054-.209a6.32,6.32,0,0,1-1.422.164,1.1,1.1,0,0,1-1.1-1.1,1.1,1.1,0,0,1,1.1-1.1,4.178,4.178,0,0,0,2.529-.859,4.138,4.138,0,0,0,1.485-2.221,4.216,4.216,0,0,0-.173-2.67,4.2,4.2,0,0,0-1.765-2.01,1.1,1.1,0,0,1-.4-1.5,1.1,1.1,0,0,1,1.063-.543,4.16,4.16,0,0,0,.1-.758,4.249,4.249,0,0,0-.346-1.838,4.156,4.156,0,0,0-1.117-1.5,4.078,4.078,0,0,0-.569-.408,4.928,4.928,0,0,1-.385.838,1.1,1.1,0,0,1-.955.553,1.08,1.08,0,0,1-.547-.15,1.093,1.093,0,0,1-.4-1.5,2.906,2.906,0,0,0,.374-1.229,1.022,1.022,0,0,1,.006-.145,2.841,2.841,0,0,0-.218-1.133,2.836,2.836,0,0,0-.754-1.051,2.794,2.794,0,0,0-1.139-.607,2.8,2.8,0,0,0-1.29-.045,2.782,2.782,0,0,0-1.173.535,2.84,2.84,0,0,0-.82.994,2.912,2.912,0,0,0-.3,1.262v5.254a4.426,4.426,0,0,0,.877,2.625,4.4,4.4,0,0,0,2.277,1.58,1.1,1.1,0,0,1,.748,1.361,1.1,1.1,0,0,1-1.367.748,6.6,6.6,0,0,1-2.535-1.41v6.928Zm-10.352,1.145a4.119,4.119,0,0,0,.787,1.49,4.148,4.148,0,0,0,1.313,1.061,4.105,4.105,0,0,0,1.624.447,4.076,4.076,0,0,0,1.676-.234,4.125,4.125,0,0,0,1.44-.877,4.208,4.208,0,0,0,.972-1.379,4.226,4.226,0,0,0,.34-1.652v-6.924a6.628,6.628,0,0,1-2.534,1.406,1.117,1.117,0,0,1-.308.045,1.1,1.1,0,0,1-1.055-.793,1.093,1.093,0,0,1,.748-1.361,4.393,4.393,0,0,0,2.272-1.58,4.354,4.354,0,0,0,.876-2.625v-5.254a2.906,2.906,0,0,0-.29-1.262,2.84,2.84,0,0,0-.82-.994,2.778,2.778,0,0,0-1.179-.535,2.8,2.8,0,0,0-1.29.045,2.832,2.832,0,0,0-1.139.607,2.821,2.821,0,0,0-.748,1.051,2.819,2.819,0,0,0-.212,1.271,2.821,2.821,0,0,0,.374,1.234,1.1,1.1,0,0,1-.4,1.5,1.1,1.1,0,0,1-1.5-.4,5.009,5.009,0,0,1-.386-.838,4.127,4.127,0,0,0-.568.408,4.156,4.156,0,0,0-1.117,1.5,4.249,4.249,0,0,0-.346,1.838,4.246,4.246,0,0,0,.1.758,1.106,1.106,0,0,1,1.064.543,1.1,1.1,0,0,1-.4,1.5,4.2,4.2,0,0,0-1.765,2.01,4.216,4.216,0,0,0-.173,2.67,4.138,4.138,0,0,0,1.485,2.221,4.178,4.178,0,0,0,2.529.859,1.1,1.1,0,0,1,1.1,1.1,1.1,1.1,0,0,1-1.1,1.1,6.32,6.32,0,0,1-1.422-.164C9200.714-22224.1,9200.732-22224.029,9200.752-22223.959Z" transform="translate(-9186.242 22256.754)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52" height="52" viewBox="0 0 52 52"><defs><style>.a{fill:#ffcfd1;}.b{opacity:0;}.b,.c{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f5222d"/><stop offset="1" stop-color="#a8071a"/></linearGradient></defs><g transform="translate(0 -116.002)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(18559 4825)"><rect class="b" width="42" height="42" transform="translate(-18554 -4704)"/><path class="c" d="M-9736.837-26950.139a1.008,1.008,0,0,1-.542-.889v-3.371h-.752a4.506,4.506,0,0,1-4.5-4.5v-2.629c-1.89-.08-3.366-2-3.366-4.367s1.477-4.287,3.366-4.369v-2.633a4.5,4.5,0,0,1,4.5-4.5h15.658v.129a3.676,3.676,0,0,0,.494,1.869h-16.152a2.5,2.5,0,0,0-2.5,2.5v14a2.5,2.5,0,0,0,2.5,2.5h1.741a1.006,1.006,0,0,1,1.009,1v2.432l4.544-3.244a.97.97,0,0,1,.576-.189h13.124a2.5,2.5,0,0,0,2.506-2.5v-9.441a3.524,3.524,0,0,0,1.118.189h.033a3.634,3.634,0,0,0,2.764-1.293,4.793,4.793,0,0,1,1.456,3.549,5.014,5.014,0,0,1-.982,3.041,3.23,3.23,0,0,1-2.391,1.334v2.621a4.507,4.507,0,0,1-4.5,4.5h-12.806l-5.858,4.184a1.021,1.021,0,0,1-.582.191A1.007,1.007,0,0,1-9736.837-26950.139Zm1.334-10.389a1.876,1.876,0,0,1-1.876-1.877v-7.869a1.876,1.876,0,0,1,1.876-1.877h15.739a1.876,1.876,0,0,1,1.876,1.877v7.869a1.876,1.876,0,0,1-1.876,1.877Zm10.931-5.811a1.752,1.752,0,0,0,1.754,1.746,1.751,1.751,0,0,0,1.747-1.746,1.754,1.754,0,0,0-1.747-1.756A1.755,1.755,0,0,0-9724.572-26966.338Zm-9.617,0a1.752,1.752,0,0,0,1.748,1.746,1.751,1.751,0,0,0,1.747-1.746,1.755,1.755,0,0,0-1.747-1.756A1.755,1.755,0,0,0-9734.189-26966.338Zm20.1-5.277-.453-1.551a4.433,4.433,0,0,0-2.946-3.006l-1.537-.467a.649.649,0,0,1-.447-.611.524.524,0,0,1,.021-.148.616.616,0,0,1,.217-.346.624.624,0,0,1,.217-.115l.284-.088,1.232-.365a4.417,4.417,0,0,0,2.96-3.02l.46-1.551a.625.625,0,0,1,.42-.434v.006a.538.538,0,0,1,.183-.033.574.574,0,0,1,.292.08.581.581,0,0,1,.3.381l.46,1.557a4.422,4.422,0,0,0,2.96,3.014l1.171.346.346.107a.647.647,0,0,1,.454.609.641.641,0,0,1-.447.611l-1.537.467a4.434,4.434,0,0,0-2.946,3.006l-.453,1.551a.631.631,0,0,1-.6.461A.636.636,0,0,1-9714.089-26971.615Zm-4.571-7.34-.183-.623a2.7,2.7,0,0,0-1.829-1.83l-.623-.189a.29.29,0,0,1-.2-.277.284.284,0,0,1,.2-.277l.61-.184a2.7,2.7,0,0,0,1.842-1.842l.183-.615a.32.32,0,0,1,.2-.2.327.327,0,0,1,.224.027.275.275,0,0,1,.143.17l.183.615a2.713,2.713,0,0,0,1.842,1.842l.609.184a.289.289,0,0,1,.21.277.3.3,0,0,1-.21.277l-.257.076-.366.113a2.725,2.725,0,0,0-1.828,1.83l-.183.623a.293.293,0,0,1-.278.2A.3.3,0,0,1-9718.66-26978.955Z" transform="translate(-8805.37 22282.029)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="1080" viewBox="0 0 1920 1080"><defs><style>.a{fill:url(#a);}</style><pattern id="a" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" viewBox="0 0 3840 2160"><image width="3840" height="2160" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 1.7 MiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="582" height="667" viewBox="0 0 582 667"><image width="582" height="667" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 664 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="139" height="765" viewBox="0 0 139 765"><defs><style>.a{fill:url(#a);}</style><pattern id="a" width="1" height="1" patternTransform="matrix(-1, 0, 0, 1, 278, 0)" viewBox="0 0 139 765"><image preserveAspectRatio="xMidYMid slice" width="139" height="765" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 51 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="139" height="765" viewBox="0 0 139 765"><defs><style>.a{fill:url(#a);}</style><pattern id="a" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" viewBox="0 0 278 1530"><image width="278" height="1530" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 51 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="273" height="323" viewBox="0 0 273 323"><image width="273" height="323" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAiIAAAKGCAYAAAB3OfDSAAAgAElEQVR4nOy9abMlO3YdtpDDme5YVW/ufv16YLNp0gzJCsuOsBz+YDkUIf1ef/QnR9AWJYeowSRlNalmz/3mV1V3OFMmHBsJZO7cB8jhDLdu3cKuypsnkUgkcgIW1h6g/ve/1IgSJcrTk6IElAKuFsDH19qsabtKV0hUtc3FbmcAJgCeAfgMwI8B/ATADwB8YtOvAJwBmNFhANYA7gG8BvBSa3wB4HcAfgHg7zXwdxr4Qmvcao01tNbU8tCfUgO6BEpU66qOGjdL4OsbhZulMvnSJL6kUaI8RcniU40SJQoTAhXXAD6woINAyGca+CGA7yngQ9qvgUsAc1Xlhx3OrLXGjQJea21ACi1TAitaGcDyGwB/AAxIuYs3PUqUKIhAJEqUKEI+BfCPAPwPAP5Ua8OCXGrgwgALhQVvNxyfSoyFBiZa47lhTLQBMj/WwJ+piiX5yjAjGv8OwP8D4G8A3MSbHyVKlAhEokSJkgL4yKph/lxr/BMLRH5qWY1KlF2aFVqKXV0tmnZrzIkxIWCimwN+ZlmWj1S1/lvLkty+808gSpR3WCIQiRIlCgGQ/wXA/6Q1/rwkkKDxoSK1ikJlS4IKTKgAELEYBGUCKF3Ze7idpQMoGh8rpUlV82Ol8N8D+L+0xv8B4D9C4Q7RXC1KlHdSIhCJEuXdlbm1AflnWuNfEAuiNT7VyoKIBLVBaw1AxFpb8GCwhqpAiLbAoyxR79S6Vt88B6lvFH6oYNQ4U6vu+U9WfRPhSJQo75hEIBIlyjsmzjNGA38CjX+lgX+uNf5Mk4GqtvsZAJELOCPCAIkFGoYNUQHQorUynjFaYaqAP1dKX5IBrNbG+PXfQOH3EYxEifJuSQQiUaK8e0IeLH+sNf65Bv4l2YToih0xQrghsWyIWZIqMelhRErLiMAxIUnljkvr0rrlOrBigcsCCv9NooxnTQJlTvEXAD6P72SUKO+ORCASJco7JlrjR4D+lwD+VanVP9KVGy5U0jAZnYtgRAwW0UBiQUYrjxJl2jS4+CFaEYD5LFH6Xygg1Rp3SuEvofBtfC+jRHk3JAKRKFGeuOjGvZa+9w808E+h8b9qjf+u1DgHAwkcMCi004kZ2QEirHxjE2JVO0rXeGNHeLplRlIofKoU/mcAXxvD1crFN8YaiRLlHZAIRKJEeeJCnX1BkVY1XiSVZwyBEHLTvdC6HV21lw0RKhqtG01MbVsiAIzSAuCwtTNkLao8nyml/7dEGw+arwH11/HdjBLl6UsEIlGivAOiNTKt8X0N/LOyNN4xLyQI6ZLELs5+pGUjUjYMRzHyVipu6KqQK4U/KRW+1MBfa43flxqvtMY2vqNRojxdiUAkSpTHKQMhQqe4WGKEId7XGn9awqhjfqS1mlQGHeJETtci2A0nNSti533hRqhgDEhQLxMQ400Dc+y0UPgjKP0/augvAPUfFPCNK/5Y9yRKlCiPRyIQiRLliUrtTqtxpjV+Co1/rDV+WJaYMayxl5ykN7cGrFB4rhT+aaLw+2mGXxc5vtls6yxHQSNRokR5PBLns4wS5TSiDlz2lea8DdvxPoB/Umr8k0KrD0pdzWbbe5a2q209U25pDVPNUnu/tGOJuPVgUU2ckaLAeVGq/zZN8I8WE/3xYqKRp7oOlHYgFnlTzyVKlCgBiYxIlChPTBzIyBJgkkHNJ/ggTfBnZYmfamBKACIZOASpQ7fbDWUtU1vGqmVjtKoFWyK3e89XgRkKX3KdJvhsmuPTosT1coPXKBxhEiVKlKckEYhEiTJMTt3/HbN8TR36LAcu5np+McdHWaJ/VGgTwTTRlhGR7IU1GG3tA2M23LosRLRUwZoctFh7EdootSYm54+Uwo+UUj9ns/Uek50Yq2Uae95okxIlSo9EIBIlyhMTraFJMZOlyOYTPJ9NzGy3z7clMl22r1XLpbErMaJsZFQFFiNEhm1nbrycIdkBO06tM4ApsfPUnGuFH1IANg0T+v2m45AoUaK8pRKBSJR3XU7FdOxb7qHHOU8Z6voXSmly2f0UWl1UAccUs8ewBxBwSBhogGBCWBh3SAChmzxdzIjXloSdT+aFAUPqHNCfao0fmBl629cqJwAeK5qVNUZOxaBE5iTKOysRiESJ8rSk7vg0cFZqfFIW+Ijml3EdfMJAQ81sWEBSqsbfl6b0T5iLbsvVV3SbWgAN87u0C9CmQXSbeSnRLLwuiubESfChBj4EzAy9TqKZSJQoT0giEIny1OXYndbY8k6VXzICSu7RGnOtTfyQ98lkpDVBnf2hArYgBBCS0s6uq+zEdcxSVKn2ca4cXQowss9SOjsRTFDi/bK6hnpSvsA9kmnHZhjGMijHZk4iYxLlyUoEIlGiPA45hSpnojWuyhKXGpgYbxm0IYxkSWBn0SUQUibNCVQpApsJ4KI8qhjDsDDgUzI1TE1/lP4u1oIcpRUuAFzRtexx/V1yqGonSpQoR5IIRKK87XIsxuPUzMWh+2WHqXr2k+S6CmZGS+5UL6rp6GsAUhunwoIQ1YANM1dM0swZ45OSMyIem49eZsQdwxgVVOXNzcR8Foioppp99+dY+8fKUObkWIxJBFJR3nqJQCRKlLdD9gE0Galkykotk2o3I66z9dCNXQiRH9qFbRcsiFnK3XDvXLzGqiIOiQMkNUsijGN3yqxWVKspgLTnWofcjyhRojxCiUAkytsiD818HIvJCKXLkfNQ5gMiXyg0mTLgoySVjM7h4ocILxhtC3DMhWIFun3uR1cwsZYHjnDn9bIi0sXXw6S4gsuq7gmbv8Z3zcIxeW+mZGz6WDk2YxJtS6K89RKBSJQob0b6gEpo20lfbFRltSptI1T7g6Mb4ynDGQ/VrMHYEfQwIm6t4QcfrQisuqlPzZyUgTgjpfe+hO5HCFj0SWRQokR5QxKBSJTHJod2CKdmPMYyHaF8fczI0PJDI/XE9P0am7LE2mhCrM7F2H8wQ1RuE5Jw1Yy2IIXyMG8Zn7SAiLI2J7q93oktwgALOEPCbFbssQUzYZHEzLGYjbGMx6mYk4diTCJTEuXRSAQiUaIcV4YyGnL/UOATYkJ4utOwUH++pAUltpp7njivF9W2CXFsSL2w/BgCRDi4YGvOkJQin1PLlCzwGVMHFQBWFh/RNRXiWvuAWV96SEKqsMicRIlyZIlAJMqbklMzH6diPPrKkx2eBA4yva+DC+ULdqzW9pRsRDZa40aXuAExI1YlU+tsnCcMRy5g3jGqYUI05Fn8V+UAScm2ue1HGVDdlDs2Ihqq1FSfm1LjNdWfARFfLQ5lKFy+UmwjkD60vGMxJkNVTpEpifLWSQQiUaIcJmOBiwQmcjvEqPSl8/2ZLZeAyMsSeAlgXXunWOWGi+ehVTtyag1UhJ1I51WFgAgkwGDMiFXHgIETLoRDtDJ1/47qb68pCTAiUsaquEIAse/Kj2XEGiXKOysRiEQ5tbwtzMe+5ciOq297KNAYW0+enkKRpwlWpVZflhpfKo17XSORKqsu2126ZtYX2lpjaDHJXUgaL5emrJaHDrP98MYRKRtD2sZWRK+01l+WpfpKAysFpNbxp6s2Q5mIQ5mS0PZQOTaA6WPWIlMS5dFKBCJRonTLPkAABzAfx9jOrLHqfVHic1oSpZdAFTq9Vr0kLGKqaoKdmYKYTUgLiPTYiOwAEbbf58pbckbEedWY6O70D/cAPrfLPYsl0sWIhDrksaqUPqak73mNBTxRoryzEoFIlGPJ2858HJvxGKtiObS+iv1KybxDa7XcFvoPidK/S1P9WlkQ4gKbgduMtDUxrd/ae5K2+IBIiba6ZQeIlIwxacUV0RbE6BsN/Fprs9xZIKIOfNf6AMdY4HAoY3Lo+YcCr2MzJRFYRTmaRCAS5anK0M7q2IzHqQDImG3TWZcltluFV0mBL5XCV4nS99BqLoFI4jxUWAh3x4LUhQ70mvExIk4jtOOeK71qWBFlxYi8Lkv8ugR+o4Fb216VAxmRnSqK9VAA0JfvUMYkSpR3XiIQibKv7DsqfWzMx6kYj32BxMGApO4pNdS2VJsE6qskwX9VCX6stf4BNIVMV+3p/Us2o64DIp65ZYa477ofWqYLRgRs3XjTaANCSmBbQn1Ravyy1OoPxO4AmDmPoAMYjbG2IceyKZHH9dmUHMqUhKSPKYm2JFEeXCIQifK2y7GYj1MxHmMBxtGYEQ2Y0O6FVt+WJf6mhP5UQX8AqCnAVDQMeLgExe1FeOGBbobbgrTSJSDhbrpl27vGrTV0UZb4vAT+odTqcw11twcTMnQ7lG8sU9LHkEg5FdCIEuWtkwhEogyVh2ZADmU+hjIdsgMIzt0SWIf2nyp9+LaqAphprb4rNP5aaf1JovTPgPJK6cRkgAMJci4Z3XjOtC5uJCNSpzEGxAGRmhmpjyN/XcOIfKc1/pOG+k9ak8eMOWsunuFYW4+hgOLQ9LFMx1jvm1MzJdGWJMqDSwQiUd4WORXz4STk1XIo43FsgDJmO7dYYlmU+K0C/gtIRQP9EaAvdKlaqhkocXLOkLj0PkaEJWiW4J0UT7ePr9JMJPovSqj/oKH+GsArVBFhtZufT9yLUzEhofR9bUVC5fflj0xJlCcvEYhEkfK2MR99KhXZscsGfkjIdF85ofSHBizB7fqCNdZFqW6h1S8B/PtE6WcK+k+h9bmyRUnVjHYARNytvt4wyIzwOCJobEOcGLsQXRIj8nUJ9XOt1f+roX5h1TK55xr7gEMoXx8w6Ov4hwKDsQDiTTElISbkVLYkEVBLine truncated

After

Width:  |  Height:  |  Size: 193 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="727" height="727" viewBox="0 0 727 727"><defs><style>.a{fill:url(#a);}.b{fill:url(#d);}.c,.d,.f{fill:none;}.c{stroke:rgba(32,194,255,0);stroke-width:8px;}.d{stroke:rgba(255,173,32,0.15);stroke-width:5px;stroke-dasharray:7 5;}.e{stroke:none;}.g{filter:url(#e);}.h{filter:url(#b);}</style><linearGradient id="a" x1="0.761" y1="0.205" x2="0.284" y2="0.581" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#ffd2ab"/><stop offset="1" stop-color="#ff7855"/></linearGradient><filter id="b" x="88.068" y="60.324" width="217.004" height="181.59" filterUnits="userSpaceOnUse"><feOffset input="SourceAlpha"/><feGaussianBlur stdDeviation="7.5" result="c"/><feFlood flood-color="#ff941a" flood-opacity="0.4"/><feComposite operator="in" in2="c"/><feComposite in="SourceGraphic"/></filter><linearGradient id="d" x1="0.237" y1="0.761" x2="0.718" y2="0.394" xlink:href="#a"/><filter id="e" x="424.041" y="484.359" width="215.309" height="181.689" filterUnits="userSpaceOnUse"><feOffset input="SourceAlpha"/><feGaussianBlur stdDeviation="7.5" result="f"/><feFlood flood-color="#ff941a" flood-opacity="0.4"/><feComposite operator="in" in2="f"/><feComposite in="SourceGraphic"/></filter></defs><g transform="translate(4 4)"><g transform="translate(-126.998 -127.002)"><g class="h" transform="matrix(1, 0, 0, 1, 123, 123)"><path class="a" d="M18115.566-21172.494q4.919-8.443,10.389-16.543a294.531,294.531,0,0,1,35.713-43.289,294.545,294.545,0,0,1,43.293-35.717,292.5,292.5,0,0,1,49.682-26.969,290.614,290.614,0,0,1,31.271-11.162l1.656,5.506a288.092,288.092,0,0,0-166.973,131.084Z" transform="translate(-18005 21389)"/></g><g class="g" transform="matrix(1, 0, 0, 1, 123, 123)"><path class="b" d="M18451.539-20750.967a288.209,288.209,0,0,0,165.279-131.172l5.029,2.92q-5.1,8.815-10.8,17.258a294.659,294.659,0,0,1-35.717,43.287,294.647,294.647,0,0,1-43.293,35.717,292.2,292.2,0,0,1-49.686,26.971,291.1,291.1,0,0,1-29.178,10.537Z" transform="translate(-18005 21389)"/></g><circle class="c" cx="359.5" cy="359.5" r="359.5" transform="translate(126.998 127.002)"/></g><g class="d" transform="translate(98 98)"><circle class="e" cx="261.5" cy="261.5" r="261.5"/><circle class="f" cx="261.5" cy="261.5" r="259"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="727" height="727" viewBox="0 0 727 727"><defs><style>.a{fill:url(#a);}.b{fill:url(#d);}.c,.d,.f{fill:none;}.c{stroke:rgba(32,194,255,0);stroke-width:8px;}.d{stroke:rgba(255,173,32,0.15);stroke-width:5px;stroke-dasharray:7 5;}.e{stroke:none;}.g{filter:url(#e);}.h{filter:url(#b);}</style><linearGradient id="a" x1="0.761" y1="0.205" x2="0.284" y2="0.581" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#ffd2ab"/><stop offset="1" stop-color="#ff7855"/></linearGradient><filter id="b" x="88.068" y="60.324" width="217.004" height="181.59" filterUnits="userSpaceOnUse"><feOffset input="SourceAlpha"/><feGaussianBlur stdDeviation="7.5" result="c"/><feFlood flood-color="#ff941a" flood-opacity="0.4"/><feComposite operator="in" in2="c"/><feComposite in="SourceGraphic"/></filter><linearGradient id="d" x1="0.237" y1="0.761" x2="0.718" y2="0.394" xlink:href="#a"/><filter id="e" x="424.041" y="484.359" width="215.309" height="181.689" filterUnits="userSpaceOnUse"><feOffset input="SourceAlpha"/><feGaussianBlur stdDeviation="7.5" result="f"/><feFlood flood-color="#ff941a" flood-opacity="0.4"/><feComposite operator="in" in2="f"/><feComposite in="SourceGraphic"/></filter></defs><g transform="translate(4 4)"><g transform="translate(-126.998 -127.002)"><g class="h" transform="matrix(1, 0, 0, 1, 123, 123)"><path class="a" d="M18115.566-21172.494q4.919-8.443,10.389-16.543a294.531,294.531,0,0,1,35.713-43.289,294.545,294.545,0,0,1,43.293-35.717,292.5,292.5,0,0,1,49.682-26.969,290.614,290.614,0,0,1,31.271-11.162l1.656,5.506a288.092,288.092,0,0,0-166.973,131.084Z" transform="translate(-18005 21389)"/></g><g class="g" transform="matrix(1, 0, 0, 1, 123, 123)"><path class="b" d="M18451.539-20750.967a288.209,288.209,0,0,0,165.279-131.172l5.029,2.92q-5.1,8.815-10.8,17.258a294.659,294.659,0,0,1-35.717,43.287,294.647,294.647,0,0,1-43.293,35.717,292.2,292.2,0,0,1-49.686,26.971,291.1,291.1,0,0,1-29.178,10.537Z" transform="translate(-18005 21389)"/></g><circle class="c" cx="359.5" cy="359.5" r="359.5" transform="translate(126.998 127.002)"/></g><g class="d" transform="translate(98 98)"><circle class="e" cx="261.5" cy="261.5" r="261.5"/><circle class="f" cx="261.5" cy="261.5" r="259"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="458" height="113.588" viewBox="0 0 458 113.588"><defs><style>.a{opacity:0.78;}.b{fill:url(#a);}.c{fill:#fff;}.d{filter:url(#d);}.e{filter:url(#b);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#fff"/><stop offset="1" stop-color="#fff9c1"/></linearGradient><filter id="b" x="104.221" y="19.443" width="226.559" height="61.289" filterUnits="userSpaceOnUse"><feOffset dy="1" input="SourceAlpha"/><feGaussianBlur stdDeviation="5.5" result="c"/><feFlood flood-color="#fe0" flood-opacity="0.71"/><feComposite operator="in" in2="c"/><feComposite in="SourceGraphic"/></filter><filter id="d" x="104.221" y="19.443" width="226.559" height="61.289" filterUnits="userSpaceOnUse"><feOffset input="SourceAlpha"/><feGaussianBlur stdDeviation="2" result="e"/><feFlood flood-color="#fff" result="f"/><feComposite operator="out" in="SourceGraphic" in2="e"/><feComposite operator="in" in="f"/><feComposite operator="in" in2="SourceGraphic"/></filter></defs><g transform="translate(-731 -564.412)"><image class="a" width="458" height="113.588" transform="translate(731 564.412)" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 625 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{opacity:0;fill:url(#a);}.b{fill:url(#b);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f3212c"/><stop offset="1" stop-color="#aa071a"/></linearGradient><linearGradient id="b" x1="0.405" x2="1" xlink:href="#a"/></defs><g transform="translate(14482.893 -23044.904)"><rect class="a" width="68" height="68" transform="translate(-14482.893 23044.904)"/><path class="b" d="M110.023,105.37H98.2a1.721,1.721,0,0,1-1.716-1.716v-1.918h10.189a1.721,1.721,0,0,0,1.716-1.716V72.8a1.721,1.721,0,0,0-1.716-1.716H98.2V67.437A3.444,3.444,0,0,0,94.76,64H87.83a3.444,3.444,0,0,0-3.437,3.437v3.652H75.625A1.721,1.721,0,0,0,73.91,72.8v27.21a1.721,1.721,0,0,0,1.716,1.716h10.49v1.918a1.721,1.721,0,0,1-1.716,1.716H72.3a1.721,1.721,0,0,0-1.716,1.716v10.1A1.721,1.721,0,0,0,72.3,118.9h37.724a1.721,1.721,0,0,0,1.716-1.716v-10.1A1.726,1.726,0,0,0,110.023,105.37Zm-8.406-19.1a3.437,3.437,0,1,1-3.437-3.437A3.436,3.436,0,0,1,101.616,86.266ZM87.83,67.437h6.924v3.646H87.83ZM81.121,86.266A3.437,3.437,0,1,1,84.558,89.7,3.436,3.436,0,0,1,81.121,86.266Zm32.48-7.089V93.355a1.716,1.716,0,0,1-3.431,0V79.177a1.716,1.716,0,0,1,3.431,0Zm5.122,3.548v6.881a1.716,1.716,0,0,1-3.431,0V82.724a1.716,1.716,0,0,1,3.431,0ZM70.644,95.07a1.717,1.717,0,0,1-1.716-1.716V79.177a1.716,1.716,0,0,1,3.431,0V93.355A1.713,1.713,0,0,1,70.644,95.07ZM67.231,82.724v6.881a1.716,1.716,0,1,1-3.431,0V82.724a1.716,1.716,0,0,1,3.431,0Zm0,0" transform="translate(-14540.154 22987.455)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="188" height="241" viewBox="0 0 188 241"><defs><style>.a{fill:url(#a);}.b{fill:#fff;opacity:0.47;}</style><pattern id="a" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" viewBox="0 0 376 482"><image width="376" height="482" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 68 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;stroke:#707070;stroke-width:3.75px;opacity:0;}.b{fill:url(#a);}.c{stroke:none;}.d{fill:none;}</style><linearGradient id="a" x1="0.431" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f3212c"/><stop offset="1" stop-color="#aa071a"/></linearGradient></defs><g transform="translate(-71.077 -67.746)"><g class="a" transform="translate(71.077 67.746)"><rect class="c" width="68" height="68"/><rect class="d" x="1.875" y="1.875" width="64.25" height="64.25"/></g><path class="b" d="M114.406,106.791a7.707,7.707,0,0,0-4.354,1.353L98.7,99.937a13.3,13.3,0,0,0,1.742-6.583c0-.421-.03-.842-.06-1.263L112,85.207a4.687,4.687,0,1,0-1.952-2.345L99.691,88.995a13.678,13.678,0,0,0-6.1-7.365l2.072-10.341c.15,0,.3.03.451.03a4.66,4.66,0,1,0-4.655-4.659,4.727,4.727,0,0,0,1.412,3.367l-2.1,10.462A13.479,13.479,0,0,0,80.05,81.87l-6.4-5.712A6.129,6.129,0,1,0,71.7,78.443L77.677,83.7a13.456,13.456,0,0,0,.691,19.991l-5.316,5.622a5.948,5.948,0,0,0-3.394-1.022,6.163,6.163,0,1,0,6.156,6.162,6.343,6.343,0,0,0-.721-2.916l5.826-6.193a13.464,13.464,0,0,0,16.1-2.946l11.022,7.966a7.77,7.77,0,0,0-1.2,4.118,7.658,7.658,0,1,0,15.316,0A7.817,7.817,0,0,0,114.406,106.791Zm0,0" transform="translate(13 9.671)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68">
<defs>
<linearGradient id="ap-idea" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#f3212c"/>
<stop offset="1" stop-color="#aa071a"/>
</linearGradient>
</defs>
<g fill="url(#ap-idea)" transform="translate(12 10)">
<path d="M22 2.2A12.8 12.8 0 0 0 9.2 15c0 5.1 2.9 9.5 7.1 11.6v3.8a2.4 2.4 0 0 0 2.4 2.4h6.6a2.4 2.4 0 0 0 2.4-2.4v-3.8A12.8 12.8 0 0 0 34.8 15 12.8 12.8 0 0 0 22 2.2Zm3.3 26.8h-6.6v-2.1h6.6Zm-3.3-26.8A10.4 10.4 0 0 1 32.4 15a10.4 10.4 0 0 1-6.3 9.5 2.4 2.4 0 0 0-1.3 2.1v.3h-5.6v-.3a2.4 2.4 0 0 0-1.3-2.1A10.4 10.4 0 0 1 11.6 15 10.4 10.4 0 0 1 22 2.2Z"/>
<path d="M17.6 39.2a2 2 0 0 0 2 2h4.8a2 2 0 0 0 2-2v-1.4h-8.8Zm-1.8-3.8h12.4a1.6 1.6 0 1 0 0-3.2H15.8a1.6 1.6 0 1 0 0 3.2Z"/>
<path d="M8.4 22.6a1.6 1.6 0 0 0 0-3.2H3.8a1.6 1.6 0 1 0 0 3.2Zm31.8 0a1.6 1.6 0 0 0 0-3.2h-4.6a1.6 1.6 0 1 0 0 3.2ZM7.2 10.8a1.6 1.6 0 0 0 2.3-2.3L6.2 5.3A1.6 1.6 0 0 0 4 7.6Zm29.3-2.3a1.6 1.6 0 0 0 2.3 2.3l3.2-3.2A1.6 1.6 0 1 0 39.7 5.3Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f3212c"/><stop offset="1" stop-color="#aa071a"/></linearGradient></defs><g transform="translate(14482.893 -23044.904)"><rect class="a" width="68" height="68" transform="translate(-14482.893 23044.904)"/><path class="b" d="M43.013,57.336h-.255l-.074,0a14.064,14.064,0,0,1-8.911-3.467,6.216,6.216,0,0,1,6.1-5.029h6.278a6.218,6.218,0,0,1,6.1,5.03A14.073,14.073,0,0,1,43.6,57.324h-.009l-.067,0H43.5l-.063,0h-.424ZM30.159,54.9H8.5a1.717,1.717,0,0,1-1.715-1.716v-10.1A1.717,1.717,0,0,1,8.5,41.363H20.6a1.717,1.717,0,0,0,1.716-1.715V37.73H11.824a1.717,1.717,0,0,1-1.716-1.715V8.8a1.718,1.718,0,0,1,1.716-1.716h8.768V3.437A3.44,3.44,0,0,1,24.029,0h6.93A3.441,3.441,0,0,1,34.4,3.437V7.089H42.87A1.718,1.718,0,0,1,44.586,8.8V26.022c-.515-.046-1.044-.07-1.574-.07A17.327,17.327,0,0,0,30.157,54.9Zm4.219-36.07a3.437,3.437,0,1,0,3.438,3.437A3.441,3.441,0,0,0,34.378,18.828Zm-13.619,0A3.437,3.437,0,1,0,24.2,22.265,3.441,3.441,0,0,0,20.758,18.828ZM24.029,3.437V7.083h6.924V3.437Zm7.256,47.59v0a14.059,14.059,0,1,1,23.456,0,9.508,9.508,0,0,0-8.589-5.455H44.686a7.193,7.193,0,1,0-3.347,0H39.873a9.5,9.5,0,0,0-8.587,5.457ZM43.013,42.5a3.923,3.923,0,1,1,3.923-3.924A3.928,3.928,0,0,1,43.013,42.5ZM6.844,31.07a1.717,1.717,0,0,1-1.716-1.715V15.176a1.715,1.715,0,1,1,3.431,0V29.355A1.717,1.717,0,0,1,6.844,31.07ZM49.8,27.327h0a17.187,17.187,0,0,0-3.429-1.051v-11.1a1.715,1.715,0,1,1,3.431,0v12.15Zm3.406-.007a1.717,1.717,0,0,1-1.715-1.715V18.724a1.715,1.715,0,0,1,3.431,0v6.881A1.717,1.717,0,0,1,53.206,27.32Zm-51.491,0A1.717,1.717,0,0,1,0,25.605V18.724a1.715,1.715,0,0,1,3.431,0v6.881A1.717,1.717,0,0,1,1.715,27.32Z" transform="translate(-14477.664 23050.15)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68">
<defs>
<linearGradient id="ap-task" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#f3212c"/>
<stop offset="1" stop-color="#aa071a"/>
</linearGradient>
</defs>
<g fill="url(#ap-task)" transform="translate(14 12)">
<path d="M28.4 2.2H15.6A3.4 3.4 0 0 0 12.2 5.6v2.8H8.6A3.4 3.4 0 0 0 5.2 11.8v28.8A3.4 3.4 0 0 0 8.6 44h22.8a3.4 3.4 0 0 0 3.4-3.4V11.8a3.4 3.4 0 0 0-3.4-3.4h-3.6V5.6A3.4 3.4 0 0 0 28.4 2.2Zm-12.8 3.4h12.8v2.8H15.6Zm19.2 35.8H8.6V11.8h3.6v2.2a1.6 1.6 0 0 0 1.6 1.6h12.8a1.6 1.6 0 0 0 1.6-1.6v-2.2h3.6Z"/>
<rect x="11.4" y="24.2" width="3.6" height="11.6" rx="1.2"/>
<rect x="18.2" y="20.4" width="3.6" height="15.4" rx="1.2"/>
<rect x="25" y="27.2" width="3.6" height="8.6" rx="1.2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 924 B

+11 -2
View File
@@ -40,6 +40,10 @@ window.__CM_RUNTIME_CONFIG__ = {
// Canvas/AI 写作独立 LangGraph 地址:为空时前端会回退使用 VITE_LANGGRAPH_BASE_URL。
VITE_CANVAS_LANGGRAPH_BASE_URL: "",
// 地图瓦片地址模板:仅供普通问答消息列表中的交互地图使用。
// 后续接入内网瓦片服务时,只需替换此项并保留 {z}/{x}/{y} 占位符。
VITE_MAP_TILE_URL: "http://119.254.86.251:8023/tiles/{z}/{x}/{y}.png?v=zh-z14-13",
// routeKey 短路由的用户名直登口令:例如 #/login/admin?routeKey=a。
// 填值时优先使用这里的 password;留空时使用 URL 中的 ?password=。
VITE_ROUTE_LOGIN_PASSWORD: "",
@@ -94,10 +98,10 @@ window.__CM_RUNTIME_CONFIG__ = {
VITE_AIPI_MOPECHAT_URL: "https://ch1.b.uat.4.cn/likun_b1_mope",
// 工作室/空间后端接口:空间列表、空间详情、素材上传等使用。
VITE_AIPI_BACKENP_URL: "http://47.88.25.99:8000",
VITE_AIPI_BACKENP_URL: "https://knowledgetoolserver.b.uat.4.cn",
// 工作室/空间后端别名:兼容旧配置命名,通常与 VITE_AIPI_BACKENP_URL 保持一致。
VITE_NOTEBOOK_API_BASE_URL: "http://47.88.25.99:8000",
VITE_NOTEBOOK_API_BASE_URL: "https://knowledgetoolserver.b.uat.4.cn",
// 工作室/空间前端地址:用于 auth/callback、新建对话等跳转。
VITE_AIPI_FRONTEND_URL: "http://47.88.25.99:3000",
@@ -151,6 +155,11 @@ window.__CM_RUNTIME_CONFIG__ = {
// GitHub OAuth Token;不使用 GitHub 相关能力时留空。
VITE_GITHUB_OAUTH_TOKEN: "",
// 是否在问答输入框「模式」菜单显示「极速」:"true" 显示,"false" 隐藏。
// 默认关闭。开启后可直连大模型流式问答,不走智能体工具与技能。
// 部署后改 dist/runtime-config.js 再刷新页面即可,无需重新打包。
VITE_TURBO_MODE_VISIBLE: "true",
// 是否在智能体管理页显示「舆情分析」类型:"true" 显示,"false" 隐藏。
// 关闭后,该智能体的专用对话地址也会自动跳回智能体管理页。
VITE_SENTIMENT_AGENT_VISIBLE: "true",
+39 -2
View File
@@ -28,8 +28,8 @@ const requiredLegacyFallbacks = [
},
{
file: "src/components/ai-elements/prompt-input.tsx",
pattern: /<InputGroup className="h-auto min-h-16">/,
description: "Prompt textarea must not rely on :has() for its parent height",
pattern: /<InputGroup className="h-auto min-h-16 flex-col items-stretch">/,
description: "Prompt textarea must not rely on :has() for its layout",
},
{
file: "src/core/utils/mermaid.ts",
@@ -41,6 +41,43 @@ const requiredLegacyFallbacks = [
pattern: /export function downlevelModernColors[\s\S]*function injectLegacyChartShim/,
description: "Generated HTML charts must downlevel modern CSS colors for Chrome 109",
},
{
file: "src/pages/ChatPage.tsx",
pattern: /chat-landing-composer-offset[\s\S]*chat-landing-content-lift/,
description: "Chat landing layout must not rely on Tailwind's individual translate property",
},
{
file: "src/styles/globals.css",
pattern: /\.chat-landing-composer-offset\s*\{\s*position:\s*relative;\s*top:\s*calc\(96px\s*-\s*50vh\);\s*\}/,
description: "Chat landing composer must offset with top, not Tailwind translate",
},
{
file: "src/styles/globals.css",
pattern:
/\.legacy-dialog-viewport\s*\{[\s\S]*?display:\s*flex;[\s\S]*?align-items:\s*center;[\s\S]*?justify-content:\s*center;/,
description: "Dialog content must be flex-centered without Tailwind translate",
},
{
file: "src/components/ui/dialog.tsx",
pattern: /legacy-dialog-viewport[\s\S]*relative grid/,
description: "Dialog panel must not use fixed + translate centering",
},
{
file: "src/styles/globals.css",
pattern:
/\.legacy-dialog-viewport\s*>\s*\[data-slot="dialog-content"\]\s*\{[\s\S]*?min-height:\s*0;[\s\S]*?max-height:\s*100%;/,
description: "Flex-centered dialogs must keep min-height:0 so inner scroll works",
},
{
file: "src/styles/globals.css",
pattern: /\.legacy-floating-layer\s*\{\s*z-index:\s*300;/,
description: "Portaled select/dropdown layers must sit above dialog overlay z-index 200",
},
{
file: "src/components/workspace/skills/skill-file-preview-dialog.tsx",
pattern: /minHeight:[\s\S]*100vh[\s\S]*maxHeight:[\s\S]*100vh/,
description: "Skill file preview must size with vh, not dvh, for Chrome 102",
},
];
for (const file of cssFiles) {
@@ -0,0 +1,39 @@
import { spawnSync } from "node:child_process";
import { mkdirSync, mkdtempSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const require = createRequire(import.meta.url);
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const { build } = await import(pathToFileURL(require.resolve("esbuild", {
paths: [dirname(require.resolve("vite"))],
})).href);
const output = join(root, ".runtime", "ui-boundary-check");
mkdirSync(output, { recursive: true });
await build({
absWorkingDir: root,
entryPoints: ["src/components/react-ui-error-boundary.browser-check.tsx"],
bundle: true,
platform: "browser",
format: "iife",
define: { "process.env.NODE_ENV": JSON.stringify("production") },
outfile: join(output, "check.js"),
});
const browser = process.env.CHROME_PATH || (process.platform === "win32"
? "C:/Program Files/Google/Chrome/Application/chrome.exe"
: "/usr/bin/chromium");
const response = spawnSync(browser, [
"--headless=new", "--disable-gpu", "--disable-background-networking", "--no-first-run",
`--user-data-dir=${mkdtempSync(join(output, "browser-profile-"))}`,
"--virtual-time-budget=3000", "--dump-dom",
pathToFileURL(join(root, "scripts", "react-ui-error-boundary-check.html")).href,
], { encoding: "utf8", windowsHide: true, timeout: 30_000 });
if (response.error) throw response.error;
const payload = response.stdout?.match(/<pre id="boundary-check-result">([^<]+)<\/pre>/)?.[1];
if (!payload || response.status !== 0) {
throw new Error(`Browser check did not finish: exit=${response.status}; ${response.stderr?.slice(-500)}`);
}
const result = JSON.parse(payload);
if (result.passed !== true) throw new Error(`Browser check failed: ${payload}`);
console.log("React production boundary browser check:", result);
@@ -0,0 +1,8 @@
<!doctype html>
<html lang="zh-CN">
<head><meta charset="utf-8"><title>Offline React UI boundary check</title></head>
<body>
<div id="root"></div>
<script src="../.runtime/ui-boundary-check/check.js"></script>
</body>
</html>
+100 -70
View File
@@ -1,10 +1,11 @@
import { useEffect, useMemo } from "react";
import { lazy, Suspense, useEffect, useMemo } from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { DesensitizeWordsLoader } from "@/components/desensitize-words-loader";
import { FontScaleApplier } from "@/components/font-scale";
import { QueryClientProvider } from "@/components/query-client-provider";
import { RouteLoadingFallback } from "@/components/route-loading";
import { AuthSyncWatcher } from "@/core/auth/sync";
import { I18nProvider } from "@/core/i18n/context";
import { fetchYLogConfig } from "@/core/log/y-log-config";
@@ -25,26 +26,46 @@ import {
import { HashRouter, useLocation } from "react-router-dom";
import LandingPage from "./pages/LandingPage";
import LoginPage from "./pages/LoginPage";
import PageRoutes from "./pages/PageRoutes";
import PublicScheduledTaskPage, {
PublicScheduledTaskFullscreenPage,
} from "./pages/PublicScheduledTaskPage";
import PublicSharePage from "./pages/PublicSharePage";
import PublicLeaderboardPage from "./pages/PublicLeaderboardPage";
import PublicRoundtableSharePage from "./pages/PublicRoundtableSharePage";
import EmbedChatPage from "./pages/EmbedChatPage";
import EmbedRoundtablePage from "./pages/EmbedRoundtablePage";
import DashboardStandalonePage from "@/roundtable-planning/pages/DashboardStandalonePage";
import PositionRoundtablePage from "@/position-roundtable/pages/PositionRoundtablePage";
import WeKnoraWikiPageView from "@/strategy-components/components/resource-management/llmwiki/WeKnoraWikiPageView";
import {
isPositionRoundtableStandaloneMode,
POSITION_ROUNDTABLE_ROUTE,
} from "@/position-roundtable/demo/position-roundtable-demo-config";
import { ChatRuntime } from "./pages/WorkspaceRoutes";
import { NotFoundPage } from "./pages/NotFoundPage";
const LandingPage = lazy(() => import("./pages/LandingPage"));
const LoginPage = lazy(() => import("./pages/LoginPage"));
const PageRoutes = lazy(() => import("./pages/PageRoutes"));
const PublicScheduledTaskPage = lazy(() => import("./pages/PublicScheduledTaskPage"));
const PublicScheduledTaskFullscreenPage = lazy(() =>
import("./pages/PublicScheduledTaskPage").then((module) => ({
default: module.PublicScheduledTaskFullscreenPage,
})),
);
const PublicSharePage = lazy(() => import("./pages/PublicSharePage"));
const PublicLeaderboardPage = lazy(() => import("./pages/PublicLeaderboardPage"));
const PublicRoundtableSharePage = lazy(() => import("./pages/PublicRoundtableSharePage"));
const EmbedChatPage = lazy(() => import("./pages/EmbedChatPage"));
const EmbedRoundtablePage = lazy(() => import("./pages/EmbedRoundtablePage"));
const SiteStudioPage = lazy(() => import("./pages/site-studio/SiteStudioPage"));
const DashboardStandalonePage = lazy(
() => import("@/roundtable-planning/pages/DashboardStandalonePage"),
);
const PositionRoundtablePage = lazy(
() => import("@/position-roundtable/pages/PositionRoundtablePage"),
);
const WeKnoraWikiPageView = lazy(
() => import("@/strategy-components/components/resource-management/llmwiki/WeKnoraWikiPageView"),
);
const ChatRuntime = lazy(() =>
import("@/components/workspace/chat-runtime").then((module) => ({
default: module.ChatRuntime,
})),
);
const NotFoundPage = lazy(() =>
import("./pages/NotFoundPage").then((module) => ({
default: module.NotFoundPage,
})),
);
const OfficialDeerFlowEntry = lazy(() => import("./pages/OfficialDeerFlowEntry"));
const PUBLIC_EMBED_PREFIXES = [
"/share/",
@@ -153,59 +174,68 @@ export function App() {
return (
<HashRouter>
<AppProviders>
<Routes>
<Route path="/" element={<RootRedirect />} />
<Route
path="/login"
element={positionStandaloneMode ? standaloneLoginRedirect : <LoginPage />}
/>
<Route
path="/login/:username"
element={positionStandaloneMode ? standaloneLoginRedirect : <LoginPage />}
/>
<Route path="/landing" element={<LandingPage />} />
<Route path="/share/:shareCode" element={<PublicSharePage />} />
<Route path="/public/leaderboard" element={<PublicLeaderboardPage />} />
<Route
path="/roundtable-share/:shareCode"
element={<PublicRoundtableSharePage view="landing" />}
/>
<Route
path="/roundtable-share/:shareCode/report"
element={<PublicRoundtableSharePage view="report" />}
/>
<Route
path="/roundtable-share/:shareCode/dashboard"
element={<PublicRoundtableSharePage view="dashboard" />}
/>
<Route
path="/public/scheduled-tasks/:taskId"
element={<PublicScheduledTaskPage />}
/>
<Route
path="/public/scheduled-tasks/:taskId/runs/:runId/fullscreen"
element={<PublicScheduledTaskFullscreenPage />}
/>
<Route
path="/embed/chats/:thread_id"
element={
<ChatRuntime>
<EmbedChatPage />
</ChatRuntime>
}
/>
<Route path="/embed/roundtable" element={<EmbedRoundtablePage />} />
<Route
path="/embed/knowledge/:noteId"
element={<WeKnoraWikiPageView publicEmbed />}
/>
<Route
path="/roundtable-dashboard"
element={<DashboardStandalonePage />}
/>
<Route path="/page/*" element={pageElement} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
<Suspense fallback={<RouteLoadingFallback />}>
<Routes>
<Route path="/" element={<RootRedirect />} />
<Route
path="/login"
element={
positionStandaloneMode ? standaloneLoginRedirect : <LoginPage />
}
/>
<Route
path="/login/:username"
element={
positionStandaloneMode ? standaloneLoginRedirect : <LoginPage />
}
/>
<Route path="/landing" element={<LandingPage />} />
<Route path="/share/:shareCode" element={<PublicSharePage />} />
<Route path="/public/leaderboard" element={<PublicLeaderboardPage />} />
<Route
path="/roundtable-share/:shareCode"
element={<PublicRoundtableSharePage view="landing" />}
/>
<Route
path="/roundtable-share/:shareCode/report"
element={<PublicRoundtableSharePage view="report" />}
/>
<Route
path="/roundtable-share/:shareCode/dashboard"
element={<PublicRoundtableSharePage view="dashboard" />}
/>
<Route
path="/public/scheduled-tasks/:taskId"
element={<PublicScheduledTaskPage />}
/>
<Route
path="/public/scheduled-tasks/:taskId/runs/:runId/fullscreen"
element={<PublicScheduledTaskFullscreenPage />}
/>
<Route
path="/embed/chats/:thread_id"
element={
<ChatRuntime>
<EmbedChatPage />
</ChatRuntime>
}
/>
<Route path="/embed/roundtable" element={<EmbedRoundtablePage />} />
<Route
path="/embed/knowledge/:noteId"
element={<WeKnoraWikiPageView publicEmbed />}
/>
<Route
path="/roundtable-dashboard"
element={<DashboardStandalonePage />}
/>
<Route path="/official/*" element={<OfficialDeerFlowEntry />} />
{/* Web App 站点预览工作室:独立路由 + 独立视觉风格(无主应用 chrome) */}
<Route path="/sites/:threadId/:siteName" element={<SiteStudioPage />} />
<Route path="/page/*" element={pageElement} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</Suspense>
</AppProviders>
</HashRouter>
);
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="53" height="53" viewBox="0 0 53 53"><defs><style>.a{fill:#fddadc;stroke:#f86c73;stroke-dasharray:4;}.b{fill:#fff;opacity:0;}.c{fill:url(#a);}</style><linearGradient id="a" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f5222d"/><stop offset="1" stop-color="#a8071a"/></linearGradient></defs><g transform="translate(0.5 -109.5)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 110)"/><g transform="translate(6.5 116.5)"><rect class="b" width="39" height="39"/><path class="c" d="M20.72,28.846a8.938,8.938,0,0,1-7.044-14.437c.01-.014.02-.025.029-.037s.026-.035.04-.051l.02-.025.017-.02.012-.014c.01-.012.021-.023.03-.037l.015-.018a.179.179,0,0,0,.021-.025l.011-.012a.139.139,0,0,1,.011-.012l.064-.078.026-.031A.221.221,0,0,0,14,14.021a8.936,8.936,0,0,1,12.315-1.082c.093.072.181.146.279.234,0,0,0,0,.008.006.023.021.048.041.07.063a.393.393,0,0,1,.037.033c.015.012.028.025.041.037l.016.016a.055.055,0,0,1,.013.01l.014.012.014.012a.043.043,0,0,0,.008.01l0,0a8.964,8.964,0,0,1,2.719,5.055l0,.014c0,.031.01.061.015.09l0,.016,0,.016c0,.021.007.045.011.066l0,.018v0a.04.04,0,0,1,0,.01l0,.016,0,.018,0,.016c0,.018,0,.035.006.053a.171.171,0,0,1,0,.031c.011.078.02.158.026.238l.008.07v.018c0,.016,0,.029,0,.045a.106.106,0,0,0,0,.018.108.108,0,0,1,0,.018.28.28,0,0,0,0,.035l0,.051v.006c0,.021,0,.045,0,.064s0,.02,0,.029a.064.064,0,0,1,0,.014v.014c0,.021,0,.041,0,.063v.014c0,.01,0,.02,0,.029v.018c0,.025,0,.049,0,.074s0,.033,0,.049a.131.131,0,0,0,0,.02V19.7c0,.016,0,.027,0,.043v.041c0,.041,0,.082,0,.123A8.947,8.947,0,0,1,20.72,28.846Zm-3.574-9.953a1.016,1.016,0,0,0,0,2.031H19.7v2.563a1.016,1.016,0,0,0,2.031,0V20.924h2.563a1.016,1.016,0,0,0,0-2.031H21.73V16.334a1.016,1.016,0,0,0-2.031,0v2.559Zm-6.872,9.547H2.726A2.731,2.731,0,0,1,0,25.709V18.166a2.728,2.728,0,0,1,2.726-2.725h7.548a2.736,2.736,0,0,1,1.336.35A10.015,10.015,0,0,0,12.954,26.2,2.727,2.727,0,0,1,10.273,28.439ZM2.726,17.877a.292.292,0,0,0-.289.289v7.543A.293.293,0,0,0,2.726,26h7.548a.292.292,0,0,0,.289-.295V18.166a.292.292,0,0,0-.289-.289ZM10.273,13H2.726A2.731,2.731,0,0,1,0,10.273V2.73A2.731,2.731,0,0,1,2.726,0h7.548A2.731,2.731,0,0,1,13,2.73v7.543A2.731,2.731,0,0,1,10.273,13Zm17.09-.559v0a9.929,9.929,0,0,0-3.091-1.881H25.71A.288.288,0,0,0,26,10.273V2.73a.288.288,0,0,0-.288-.289H18.166a.286.286,0,0,0-.289.289v7.543c0,.014,0,.027,0,.049a9.841,9.841,0,0,0-2.246.979,2.737,2.737,0,0,1-.2-1.027V2.73A2.734,2.734,0,0,1,18.166,0H25.71a2.733,2.733,0,0,1,2.729,2.73v7.543a2.709,2.709,0,0,1-1.075,2.17Z" transform="translate(5.281 5.278)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 52 52">
<defs>
<linearGradient id="ap-add" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#f5222d"/>
<stop offset="1" stop-color="#a8071a"/>
</linearGradient>
</defs>
<rect width="52" height="52" rx="8" fill="#ffcfd1"/>
<path fill="url(#ap-add)" d="M26 14a2 2 0 0 1 2 2v8h8a2 2 0 1 1 0 4h-8v8a2 2 0 1 1-4 0v-8h-8a2 2 0 1 1 0-4h8v-8a2 2 0 0 1 2-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 503 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52" height="52" viewBox="0 0 52 52"><defs><style>.a{fill:#ffcfd1;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f5222d"/><stop offset="1" stop-color="#a8071a"/></linearGradient></defs><g transform="translate(0 -116.002)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(2.236 118.236)"><path class="b" d="M9204.336-22218.762a6.4,6.4,0,0,1-2.489-.691,6.242,6.242,0,0,1-2-1.615,6.363,6.363,0,0,1-1.206-2.283,6.357,6.357,0,0,1-.246-1.863c-.047-.033-.094-.066-.139-.1a6.4,6.4,0,0,1-2.272-3.4,6.418,6.418,0,0,1,.268-4.076,6.336,6.336,0,0,1,1.233-1.912,6.383,6.383,0,0,1-.4-1.973,6.41,6.41,0,0,1,.53-2.809,6.441,6.441,0,0,1,1.7-2.289,6.424,6.424,0,0,1,1.751-1.057,4.969,4.969,0,0,1,.343-1.289,4.965,4.965,0,0,1,1.334-1.859,5.038,5.038,0,0,1,2.016-1.082,5.037,5.037,0,0,1,2.289-.074,5.024,5.024,0,0,1,2.089.943,4.947,4.947,0,0,1,.871.861,5.147,5.147,0,0,1,.876-.861,5,5,0,0,1,2.083-.943,5.035,5.035,0,0,1,2.289.074,5.06,5.06,0,0,1,2.021,1.082,5.031,5.031,0,0,1,1.334,1.859,5.008,5.008,0,0,1,.343,1.289,6.424,6.424,0,0,1,1.751,1.057,6.441,6.441,0,0,1,1.7,2.289,6.405,6.405,0,0,1,.524,2.809,6.434,6.434,0,0,1-.4,1.973,6.315,6.315,0,0,1,1.234,1.912,6.418,6.418,0,0,1,.268,4.076,6.4,6.4,0,0,1-2.272,3.4l-.143.105a6.214,6.214,0,0,1-.248,1.859,6.26,6.26,0,0,1-1.2,2.283,6.271,6.271,0,0,1-2.01,1.615,6.354,6.354,0,0,1-2.485.691c-.14.006-.278.012-.418.012a6.213,6.213,0,0,1-2.139-.375,6.394,6.394,0,0,1-2.2-1.34,6.4,6.4,0,0,1-.915-1.066,6.415,6.415,0,0,1-.91,1.066,6.427,6.427,0,0,1-2.206,1.34,6.283,6.283,0,0,1-2.143.377C9204.609-22218.748,9204.473-22218.752,9204.336-22218.762Zm6.768-6.342a4.232,4.232,0,0,0,.346,1.652,4.123,4.123,0,0,0,.972,1.379,4.116,4.116,0,0,0,1.44.877,4.062,4.062,0,0,0,1.67.234,4.159,4.159,0,0,0,1.63-.447,4.159,4.159,0,0,0,1.313-1.061,4.227,4.227,0,0,0,.787-1.49c.02-.07.037-.139.054-.209a6.32,6.32,0,0,1-1.422.164,1.1,1.1,0,0,1-1.1-1.1,1.1,1.1,0,0,1,1.1-1.1,4.178,4.178,0,0,0,2.529-.859,4.138,4.138,0,0,0,1.485-2.221,4.216,4.216,0,0,0-.173-2.67,4.2,4.2,0,0,0-1.765-2.01,1.1,1.1,0,0,1-.4-1.5,1.1,1.1,0,0,1,1.063-.543,4.16,4.16,0,0,0,.1-.758,4.249,4.249,0,0,0-.346-1.838,4.156,4.156,0,0,0-1.117-1.5,4.078,4.078,0,0,0-.569-.408,4.928,4.928,0,0,1-.385.838,1.1,1.1,0,0,1-.955.553,1.08,1.08,0,0,1-.547-.15,1.093,1.093,0,0,1-.4-1.5,2.906,2.906,0,0,0,.374-1.229,1.022,1.022,0,0,1,.006-.145,2.841,2.841,0,0,0-.218-1.133,2.836,2.836,0,0,0-.754-1.051,2.794,2.794,0,0,0-1.139-.607,2.8,2.8,0,0,0-1.29-.045,2.782,2.782,0,0,0-1.173.535,2.84,2.84,0,0,0-.82.994,2.912,2.912,0,0,0-.3,1.262v5.254a4.426,4.426,0,0,0,.877,2.625,4.4,4.4,0,0,0,2.277,1.58,1.1,1.1,0,0,1,.748,1.361,1.1,1.1,0,0,1-1.367.748,6.6,6.6,0,0,1-2.535-1.41v6.928Zm-10.352,1.145a4.119,4.119,0,0,0,.787,1.49,4.148,4.148,0,0,0,1.313,1.061,4.105,4.105,0,0,0,1.624.447,4.076,4.076,0,0,0,1.676-.234,4.125,4.125,0,0,0,1.44-.877,4.208,4.208,0,0,0,.972-1.379,4.226,4.226,0,0,0,.34-1.652v-6.924a6.628,6.628,0,0,1-2.534,1.406,1.117,1.117,0,0,1-.308.045,1.1,1.1,0,0,1-1.055-.793,1.093,1.093,0,0,1,.748-1.361,4.393,4.393,0,0,0,2.272-1.58,4.354,4.354,0,0,0,.876-2.625v-5.254a2.906,2.906,0,0,0-.29-1.262,2.84,2.84,0,0,0-.82-.994,2.778,2.778,0,0,0-1.179-.535,2.8,2.8,0,0,0-1.29.045,2.832,2.832,0,0,0-1.139.607,2.821,2.821,0,0,0-.748,1.051,2.819,2.819,0,0,0-.212,1.271,2.821,2.821,0,0,0,.374,1.234,1.1,1.1,0,0,1-.4,1.5,1.1,1.1,0,0,1-1.5-.4,5.009,5.009,0,0,1-.386-.838,4.127,4.127,0,0,0-.568.408,4.156,4.156,0,0,0-1.117,1.5,4.249,4.249,0,0,0-.346,1.838,4.246,4.246,0,0,0,.1.758,1.106,1.106,0,0,1,1.064.543,1.1,1.1,0,0,1-.4,1.5,4.2,4.2,0,0,0-1.765,2.01,4.216,4.216,0,0,0-.173,2.67,4.138,4.138,0,0,0,1.485,2.221,4.178,4.178,0,0,0,2.529.859,1.1,1.1,0,0,1,1.1,1.1,1.1,1.1,0,0,1-1.1,1.1,6.32,6.32,0,0,1-1.422-.164C9200.714-22224.1,9200.732-22224.029,9200.752-22223.959Z" transform="translate(-9186.242 22256.754)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52" height="52" viewBox="0 0 52 52"><defs><style>.a{fill:#ffcfd1;}.b{opacity:0;}.b,.c{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f5222d"/><stop offset="1" stop-color="#a8071a"/></linearGradient></defs><g transform="translate(0 -116.002)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(18559 4825)"><rect class="b" width="42" height="42" transform="translate(-18554 -4704)"/><path class="c" d="M-9736.837-26950.139a1.008,1.008,0,0,1-.542-.889v-3.371h-.752a4.506,4.506,0,0,1-4.5-4.5v-2.629c-1.89-.08-3.366-2-3.366-4.367s1.477-4.287,3.366-4.369v-2.633a4.5,4.5,0,0,1,4.5-4.5h15.658v.129a3.676,3.676,0,0,0,.494,1.869h-16.152a2.5,2.5,0,0,0-2.5,2.5v14a2.5,2.5,0,0,0,2.5,2.5h1.741a1.006,1.006,0,0,1,1.009,1v2.432l4.544-3.244a.97.97,0,0,1,.576-.189h13.124a2.5,2.5,0,0,0,2.506-2.5v-9.441a3.524,3.524,0,0,0,1.118.189h.033a3.634,3.634,0,0,0,2.764-1.293,4.793,4.793,0,0,1,1.456,3.549,5.014,5.014,0,0,1-.982,3.041,3.23,3.23,0,0,1-2.391,1.334v2.621a4.507,4.507,0,0,1-4.5,4.5h-12.806l-5.858,4.184a1.021,1.021,0,0,1-.582.191A1.007,1.007,0,0,1-9736.837-26950.139Zm1.334-10.389a1.876,1.876,0,0,1-1.876-1.877v-7.869a1.876,1.876,0,0,1,1.876-1.877h15.739a1.876,1.876,0,0,1,1.876,1.877v7.869a1.876,1.876,0,0,1-1.876,1.877Zm10.931-5.811a1.752,1.752,0,0,0,1.754,1.746,1.751,1.751,0,0,0,1.747-1.746,1.754,1.754,0,0,0-1.747-1.756A1.755,1.755,0,0,0-9724.572-26966.338Zm-9.617,0a1.752,1.752,0,0,0,1.748,1.746,1.751,1.751,0,0,0,1.747-1.746,1.755,1.755,0,0,0-1.747-1.756A1.755,1.755,0,0,0-9734.189-26966.338Zm20.1-5.277-.453-1.551a4.433,4.433,0,0,0-2.946-3.006l-1.537-.467a.649.649,0,0,1-.447-.611.524.524,0,0,1,.021-.148.616.616,0,0,1,.217-.346.624.624,0,0,1,.217-.115l.284-.088,1.232-.365a4.417,4.417,0,0,0,2.96-3.02l.46-1.551a.625.625,0,0,1,.42-.434v.006a.538.538,0,0,1,.183-.033.574.574,0,0,1,.292.08.581.581,0,0,1,.3.381l.46,1.557a4.422,4.422,0,0,0,2.96,3.014l1.171.346.346.107a.647.647,0,0,1,.454.609.641.641,0,0,1-.447.611l-1.537.467a4.434,4.434,0,0,0-2.946,3.006l-.453,1.551a.631.631,0,0,1-.6.461A.636.636,0,0,1-9714.089-26971.615Zm-4.571-7.34-.183-.623a2.7,2.7,0,0,0-1.829-1.83l-.623-.189a.29.29,0,0,1-.2-.277.284.284,0,0,1,.2-.277l.61-.184a2.7,2.7,0,0,0,1.842-1.842l.183-.615a.32.32,0,0,1,.2-.2.327.327,0,0,1,.224.027.275.275,0,0,1,.143.17l.183.615a2.713,2.713,0,0,0,1.842,1.842l.609.184a.289.289,0,0,1,.21.277.3.3,0,0,1-.21.277l-.257.076-.366.113a2.725,2.725,0,0,0-1.828,1.83l-.183.623a.293.293,0,0,1-.278.2A.3.3,0,0,1-9718.66-26978.955Z" transform="translate(-8805.37 22282.029)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52" height="52" viewBox="0 0 52 52"><defs><style>.a{fill:#ffcfd1;}.b{fill:#fff;opacity:0;}.c{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f5222d"/><stop offset="1" stop-color="#a8071a"/></linearGradient></defs><g transform="translate(0 -116.002)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(18558 4829)"><rect class="b" width="42" height="42" transform="translate(-18553 -4708)"/><path class="c" d="M-9520.252-26774.373h-17.5a8.261,8.261,0,0,1-8.248-8.254v-8.746a8.261,8.261,0,0,1,8.248-8.256h7.75v-2.582a2.5,2.5,0,0,1-1.5-2.287,2.506,2.506,0,0,1,2.5-2.5,2.5,2.5,0,0,1,2.5,2.5,2.515,2.515,0,0,1-1.5,2.293v2.576h2.752a1.254,1.254,0,0,1,1.251,1.252,1.254,1.254,0,0,1-1.251,1.252h-12.5a5.758,5.758,0,0,0-5.752,5.752v8.746a5.758,5.758,0,0,0,5.752,5.752h17.5a5.758,5.758,0,0,0,5.752-5.752v-5.746a1.252,1.252,0,0,1,1.251-1.25,1.252,1.252,0,0,1,1.252,1.25v5.746A8.264,8.264,0,0,1-9520.252-26774.373Zm-2.624-6.25h-12.251a5.506,5.506,0,0,1-5.5-5.5,5.5,5.5,0,0,1,5.5-5.5h12.251a5.507,5.507,0,0,1,5.5,5.5A5.509,5.509,0,0,1-9522.876-26780.623Zm-.989-7.812a.992.992,0,0,0-.706.291l-1.312,1.313a1,1,0,0,0-.289.707.985.985,0,0,0,.289.705l1.313,1.313a1,1,0,0,0,.706.3,1,1,0,0,0,.706-.3.988.988,0,0,0,.293-.707.987.987,0,0,0-.293-.705l-.605-.605.605-.605a.988.988,0,0,0,.293-.707.988.988,0,0,0-.293-.707A.988.988,0,0,0-9523.865-26788.436Zm-10.388.561a1.751,1.751,0,0,0-1.749,1.75,1.75,1.75,0,0,0,1.749,1.748,1.75,1.75,0,0,0,1.749-1.748A1.751,1.751,0,0,0-9534.253-26787.875Zm19.262-3.531a.587.587,0,0,1-.552-.4l-1.5-4.568-4.555-1.447a.593.593,0,0,1-.4-.551.577.577,0,0,1,.4-.553l4.5-1.514,1.419-4.5a.59.59,0,0,1,.557-.412.609.609,0,0,1,.17.027.569.569,0,0,1,.377.377l1.5,4.568,4.7,1.668a.579.579,0,0,1,.384.559.59.59,0,0,1-.417.545l-4.6,1.293-1.426,4.5A.572.572,0,0,1-9514.991-26791.406Z" transform="translate(-9002.999 22102.623)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="1080" viewBox="0 0 1920 1080"><defs><style>.a{fill:url(#a);}</style><pattern id="a" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" viewBox="0 0 3840 2160"><image width="3840" height="2160" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 1.7 MiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="53" height="53" viewBox="0 0 53 53"><defs><style>.a{fill:#d4e9ff;stroke:#55a6fe;stroke-dasharray:4;}.b{fill:#fff;opacity:0;}.c{fill:url(#a);}</style><linearGradient id="a" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#0077fb"/><stop offset="1" stop-color="#004bb5"/></linearGradient></defs><g transform="translate(0.5 -109.5)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 110)"/><g transform="translate(6.5 116.5)"><rect class="b" width="39" height="39"/><path class="c" d="M20.72,28.846a8.938,8.938,0,0,1-7.044-14.437c.01-.014.02-.025.029-.037s.026-.035.04-.051l.02-.025.017-.02.012-.014c.01-.012.021-.023.03-.037l.015-.018a.179.179,0,0,0,.021-.025l.011-.012a.139.139,0,0,1,.011-.012l.064-.078.026-.031A.221.221,0,0,0,14,14.021a8.936,8.936,0,0,1,12.315-1.082c.093.072.181.146.279.234,0,0,0,0,.008.006.023.021.048.041.07.063a.393.393,0,0,1,.037.033c.015.012.028.025.041.037l.016.016a.055.055,0,0,1,.013.01l.014.012.014.012a.043.043,0,0,0,.008.01l0,0a8.964,8.964,0,0,1,2.719,5.055l0,.014c0,.031.01.061.015.09l0,.016,0,.016c0,.021.007.045.011.066l0,.018v0a.04.04,0,0,1,0,.01l0,.016,0,.018,0,.016c0,.018,0,.035.006.053a.171.171,0,0,1,0,.031c.011.078.02.158.026.238l.008.07v.018c0,.016,0,.029,0,.045a.106.106,0,0,0,0,.018.108.108,0,0,1,0,.018.28.28,0,0,0,0,.035l0,.051v.006c0,.021,0,.045,0,.064s0,.02,0,.029a.064.064,0,0,1,0,.014v.014c0,.021,0,.041,0,.063v.014c0,.01,0,.02,0,.029v.018c0,.025,0,.049,0,.074s0,.033,0,.049a.131.131,0,0,0,0,.02V19.7c0,.016,0,.027,0,.043v.041c0,.041,0,.082,0,.123A8.947,8.947,0,0,1,20.72,28.846Zm-3.574-9.953a1.016,1.016,0,0,0,0,2.031H19.7v2.563a1.016,1.016,0,0,0,2.031,0V20.924h2.563a1.016,1.016,0,0,0,0-2.031H21.73V16.334a1.016,1.016,0,0,0-2.031,0v2.559Zm-6.872,9.547H2.726A2.731,2.731,0,0,1,0,25.709V18.166a2.728,2.728,0,0,1,2.726-2.725h7.548a2.736,2.736,0,0,1,1.336.35A10.015,10.015,0,0,0,12.954,26.2,2.727,2.727,0,0,1,10.273,28.439ZM2.726,17.877a.292.292,0,0,0-.289.289v7.543A.293.293,0,0,0,2.726,26h7.548a.292.292,0,0,0,.289-.295V18.166a.292.292,0,0,0-.289-.289ZM10.273,13H2.726A2.731,2.731,0,0,1,0,10.273V2.73A2.731,2.731,0,0,1,2.726,0h7.548A2.731,2.731,0,0,1,13,2.73v7.543A2.731,2.731,0,0,1,10.273,13Zm17.09-.559v0a9.929,9.929,0,0,0-3.091-1.881H25.71A.288.288,0,0,0,26,10.273V2.73a.288.288,0,0,0-.288-.289H18.166a.286.286,0,0,0-.289.289v7.543c0,.014,0,.027,0,.049a9.841,9.841,0,0,0-2.246.979,2.737,2.737,0,0,1-.2-1.027V2.73A2.734,2.734,0,0,1,18.166,0H25.71a2.733,2.733,0,0,1,2.729,2.73v7.543a2.709,2.709,0,0,1-1.075,2.17Z" transform="translate(5.281 5.278)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52" height="52" viewBox="0 0 52 52"><defs><style>.a{fill:#aad2ff;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#0077fb"/><stop offset="1" stop-color="#004bb5"/></linearGradient></defs><g transform="translate(0 -116.002)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(2.236 118.236)"><path class="b" d="M9204.336-22218.762a6.4,6.4,0,0,1-2.489-.691,6.242,6.242,0,0,1-2-1.615,6.363,6.363,0,0,1-1.206-2.283,6.357,6.357,0,0,1-.246-1.863c-.047-.033-.094-.066-.139-.1a6.4,6.4,0,0,1-2.272-3.4,6.418,6.418,0,0,1,.268-4.076,6.336,6.336,0,0,1,1.233-1.912,6.383,6.383,0,0,1-.4-1.973,6.41,6.41,0,0,1,.53-2.809,6.441,6.441,0,0,1,1.7-2.289,6.424,6.424,0,0,1,1.751-1.057,4.969,4.969,0,0,1,.343-1.289,4.965,4.965,0,0,1,1.334-1.859,5.038,5.038,0,0,1,2.016-1.082,5.037,5.037,0,0,1,2.289-.074,5.024,5.024,0,0,1,2.089.943,4.947,4.947,0,0,1,.871.861,5.147,5.147,0,0,1,.876-.861,5,5,0,0,1,2.083-.943,5.035,5.035,0,0,1,2.289.074,5.06,5.06,0,0,1,2.021,1.082,5.031,5.031,0,0,1,1.334,1.859,5.008,5.008,0,0,1,.343,1.289,6.424,6.424,0,0,1,1.751,1.057,6.441,6.441,0,0,1,1.7,2.289,6.405,6.405,0,0,1,.524,2.809,6.434,6.434,0,0,1-.4,1.973,6.315,6.315,0,0,1,1.234,1.912,6.418,6.418,0,0,1,.268,4.076,6.4,6.4,0,0,1-2.272,3.4l-.143.105a6.214,6.214,0,0,1-.248,1.859,6.26,6.26,0,0,1-1.2,2.283,6.271,6.271,0,0,1-2.01,1.615,6.354,6.354,0,0,1-2.485.691c-.14.006-.278.012-.418.012a6.213,6.213,0,0,1-2.139-.375,6.394,6.394,0,0,1-2.2-1.34,6.4,6.4,0,0,1-.915-1.066,6.415,6.415,0,0,1-.91,1.066,6.427,6.427,0,0,1-2.206,1.34,6.283,6.283,0,0,1-2.143.377C9204.609-22218.748,9204.473-22218.752,9204.336-22218.762Zm6.768-6.342a4.232,4.232,0,0,0,.346,1.652,4.123,4.123,0,0,0,.972,1.379,4.116,4.116,0,0,0,1.44.877,4.062,4.062,0,0,0,1.67.234,4.159,4.159,0,0,0,1.63-.447,4.159,4.159,0,0,0,1.313-1.061,4.227,4.227,0,0,0,.787-1.49c.02-.07.037-.139.054-.209a6.32,6.32,0,0,1-1.422.164,1.1,1.1,0,0,1-1.1-1.1,1.1,1.1,0,0,1,1.1-1.1,4.178,4.178,0,0,0,2.529-.859,4.138,4.138,0,0,0,1.485-2.221,4.216,4.216,0,0,0-.173-2.67,4.2,4.2,0,0,0-1.765-2.01,1.1,1.1,0,0,1-.4-1.5,1.1,1.1,0,0,1,1.063-.543,4.16,4.16,0,0,0,.1-.758,4.249,4.249,0,0,0-.346-1.838,4.156,4.156,0,0,0-1.117-1.5,4.078,4.078,0,0,0-.569-.408,4.928,4.928,0,0,1-.385.838,1.1,1.1,0,0,1-.955.553,1.08,1.08,0,0,1-.547-.15,1.093,1.093,0,0,1-.4-1.5,2.906,2.906,0,0,0,.374-1.229,1.022,1.022,0,0,1,.006-.145,2.841,2.841,0,0,0-.218-1.133,2.836,2.836,0,0,0-.754-1.051,2.794,2.794,0,0,0-1.139-.607,2.8,2.8,0,0,0-1.29-.045,2.782,2.782,0,0,0-1.173.535,2.84,2.84,0,0,0-.82.994,2.912,2.912,0,0,0-.3,1.262v5.254a4.426,4.426,0,0,0,.877,2.625,4.4,4.4,0,0,0,2.277,1.58,1.1,1.1,0,0,1,.748,1.361,1.1,1.1,0,0,1-1.367.748,6.6,6.6,0,0,1-2.535-1.41v6.928Zm-10.352,1.145a4.119,4.119,0,0,0,.787,1.49,4.148,4.148,0,0,0,1.313,1.061,4.105,4.105,0,0,0,1.624.447,4.076,4.076,0,0,0,1.676-.234,4.125,4.125,0,0,0,1.44-.877,4.208,4.208,0,0,0,.972-1.379,4.226,4.226,0,0,0,.34-1.652v-6.924a6.628,6.628,0,0,1-2.534,1.406,1.117,1.117,0,0,1-.308.045,1.1,1.1,0,0,1-1.055-.793,1.093,1.093,0,0,1,.748-1.361,4.393,4.393,0,0,0,2.272-1.58,4.354,4.354,0,0,0,.876-2.625v-5.254a2.906,2.906,0,0,0-.29-1.262,2.84,2.84,0,0,0-.82-.994,2.778,2.778,0,0,0-1.179-.535,2.8,2.8,0,0,0-1.29.045,2.832,2.832,0,0,0-1.139.607,2.821,2.821,0,0,0-.748,1.051,2.819,2.819,0,0,0-.212,1.271,2.821,2.821,0,0,0,.374,1.234,1.1,1.1,0,0,1-.4,1.5,1.1,1.1,0,0,1-1.5-.4,5.009,5.009,0,0,1-.386-.838,4.127,4.127,0,0,0-.568.408,4.156,4.156,0,0,0-1.117,1.5,4.249,4.249,0,0,0-.346,1.838,4.246,4.246,0,0,0,.1.758,1.106,1.106,0,0,1,1.064.543,1.1,1.1,0,0,1-.4,1.5,4.2,4.2,0,0,0-1.765,2.01,4.216,4.216,0,0,0-.173,2.67,4.138,4.138,0,0,0,1.485,2.221,4.178,4.178,0,0,0,2.529.859,1.1,1.1,0,0,1,1.1,1.1,1.1,1.1,0,0,1-1.1,1.1,6.32,6.32,0,0,1-1.422-.164C9200.714-22224.1,9200.732-22224.029,9200.752-22223.959Z" transform="translate(-9186.242 22256.754)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52" height="52" viewBox="0 0 52 52"><defs><style>.a{fill:#aad2ff;}.b{opacity:0;}.b,.c{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#0077fb"/><stop offset="1" stop-color="#004bb5"/></linearGradient></defs><g transform="translate(0 -116.002)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(18559 4825)"><rect class="b" width="42" height="42" transform="translate(-18554 -4704)"/><path class="c" d="M-9736.837-26950.139a1.008,1.008,0,0,1-.542-.889v-3.371h-.752a4.506,4.506,0,0,1-4.5-4.5v-2.629c-1.89-.08-3.366-2-3.366-4.367s1.477-4.287,3.366-4.369v-2.633a4.5,4.5,0,0,1,4.5-4.5h15.658v.129a3.676,3.676,0,0,0,.494,1.869h-16.152a2.5,2.5,0,0,0-2.5,2.5v14a2.5,2.5,0,0,0,2.5,2.5h1.741a1.006,1.006,0,0,1,1.009,1v2.432l4.544-3.244a.97.97,0,0,1,.576-.189h13.124a2.5,2.5,0,0,0,2.506-2.5v-9.441a3.524,3.524,0,0,0,1.118.189h.033a3.634,3.634,0,0,0,2.764-1.293,4.793,4.793,0,0,1,1.456,3.549,5.014,5.014,0,0,1-.982,3.041,3.23,3.23,0,0,1-2.391,1.334v2.621a4.507,4.507,0,0,1-4.5,4.5h-12.806l-5.858,4.184a1.021,1.021,0,0,1-.582.191A1.007,1.007,0,0,1-9736.837-26950.139Zm1.334-10.389a1.876,1.876,0,0,1-1.876-1.877v-7.869a1.876,1.876,0,0,1,1.876-1.877h15.739a1.876,1.876,0,0,1,1.876,1.877v7.869a1.876,1.876,0,0,1-1.876,1.877Zm10.931-5.811a1.752,1.752,0,0,0,1.754,1.746,1.751,1.751,0,0,0,1.747-1.746,1.754,1.754,0,0,0-1.747-1.756A1.755,1.755,0,0,0-9724.572-26966.338Zm-9.617,0a1.752,1.752,0,0,0,1.748,1.746,1.751,1.751,0,0,0,1.747-1.746,1.755,1.755,0,0,0-1.747-1.756A1.755,1.755,0,0,0-9734.189-26966.338Zm20.1-5.277-.453-1.551a4.433,4.433,0,0,0-2.946-3.006l-1.537-.467a.649.649,0,0,1-.447-.611.524.524,0,0,1,.021-.148.616.616,0,0,1,.217-.346.624.624,0,0,1,.217-.115l.284-.088,1.232-.365a4.417,4.417,0,0,0,2.96-3.02l.46-1.551a.625.625,0,0,1,.42-.434v.006a.538.538,0,0,1,.183-.033.574.574,0,0,1,.292.08.581.581,0,0,1,.3.381l.46,1.557a4.422,4.422,0,0,0,2.96,3.014l1.171.346.346.107a.647.647,0,0,1,.454.609.641.641,0,0,1-.447.611l-1.537.467a4.434,4.434,0,0,0-2.946,3.006l-.453,1.551a.631.631,0,0,1-.6.461A.636.636,0,0,1-9714.089-26971.615Zm-4.571-7.34-.183-.623a2.7,2.7,0,0,0-1.829-1.83l-.623-.189a.29.29,0,0,1-.2-.277.284.284,0,0,1,.2-.277l.61-.184a2.7,2.7,0,0,0,1.842-1.842l.183-.615a.32.32,0,0,1,.2-.2.327.327,0,0,1,.224.027.275.275,0,0,1,.143.17l.183.615a2.713,2.713,0,0,0,1.842,1.842l.609.184a.289.289,0,0,1,.21.277.3.3,0,0,1-.21.277l-.257.076-.366.113a2.725,2.725,0,0,0-1.828,1.83l-.183.623a.293.293,0,0,1-.278.2A.3.3,0,0,1-9718.66-26978.955Z" transform="translate(-8805.37 22282.029)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52" height="52" viewBox="0 0 52 52"><defs><style>.a{fill:#aad2ff;}.b{fill:#fff;opacity:0;}.c{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#0077fb"/><stop offset="1" stop-color="#004bb5"/></linearGradient></defs><g transform="translate(0 -116.002)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(18558 4829)"><rect class="b" width="42" height="42" transform="translate(-18553 -4708)"/><path class="c" d="M-9520.252-26774.373h-17.5a8.261,8.261,0,0,1-8.248-8.254v-8.746a8.261,8.261,0,0,1,8.248-8.256h7.75v-2.582a2.5,2.5,0,0,1-1.5-2.287,2.506,2.506,0,0,1,2.5-2.5,2.5,2.5,0,0,1,2.5,2.5,2.515,2.515,0,0,1-1.5,2.293v2.576h2.752a1.254,1.254,0,0,1,1.251,1.252,1.254,1.254,0,0,1-1.251,1.252h-12.5a5.758,5.758,0,0,0-5.752,5.752v8.746a5.758,5.758,0,0,0,5.752,5.752h17.5a5.758,5.758,0,0,0,5.752-5.752v-5.746a1.252,1.252,0,0,1,1.251-1.25,1.252,1.252,0,0,1,1.252,1.25v5.746A8.264,8.264,0,0,1-9520.252-26774.373Zm-2.624-6.25h-12.251a5.506,5.506,0,0,1-5.5-5.5,5.5,5.5,0,0,1,5.5-5.5h12.251a5.507,5.507,0,0,1,5.5,5.5A5.509,5.509,0,0,1-9522.876-26780.623Zm-.989-7.812a.992.992,0,0,0-.706.291l-1.312,1.313a1,1,0,0,0-.289.707.985.985,0,0,0,.289.705l1.313,1.313a1,1,0,0,0,.706.3,1,1,0,0,0,.706-.3.988.988,0,0,0,.293-.707.987.987,0,0,0-.293-.705l-.605-.605.605-.605a.988.988,0,0,0,.293-.707.988.988,0,0,0-.293-.707A.988.988,0,0,0-9523.865-26788.436Zm-10.388.561a1.751,1.751,0,0,0-1.749,1.75,1.75,1.75,0,0,0,1.749,1.748,1.75,1.75,0,0,0,1.749-1.748A1.751,1.751,0,0,0-9534.253-26787.875Zm19.262-3.531a.587.587,0,0,1-.552-.4l-1.5-4.568-4.555-1.447a.593.593,0,0,1-.4-.551.577.577,0,0,1,.4-.553l4.5-1.514,1.419-4.5a.59.59,0,0,1,.557-.412.609.609,0,0,1,.17.027.569.569,0,0,1,.377.377l1.5,4.568,4.7,1.668a.579.579,0,0,1,.384.559.59.59,0,0,1-.417.545l-4.6,1.293-1.426,4.5A.572.572,0,0,1-9514.991-26791.406Z" transform="translate(-9002.999 22102.623)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="1080" viewBox="0 0 1920 1080"><image width="1920" height="1080" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAADwAAAAhwCAYAAACQvstyAAAgAElEQVR4nOzdi3KjOrYA0JCZ///huR1uJTGOLSzrgcRzrapzOoltwCCEkMTew9fXOH4AcHnTxWC4/3D1PcKZaOwAcCZhM811bluazQDnoDsEAACgj7D/crrvSvVruj+DHWjVYRKrCNhG4XFt3W+21rhW7vUGAOgn1X4ovU5rRnIF9/Mich/lPAA4l5x+l/8bPz7+a2ITAI+iNw5wYIozAGfmOrexRh3szY9jbYePiVjASdRO8EtVg7HqdWk9rroFAAB6ad3dk3v/E1tPq34w/aKwY61P0FYdL2HHTuz32OdK1zfZa4WV6uhq3GE1palZe3e4XgDAfrlOw5xpOwCEvvtU/iu1AQAfj+MWQmBeisMMAMfj+r0z04SVvUTaDAtIagJT6fKWSk2kiq1fvxVQqHZeZuxzqeoVAACAvPum0vsp/aGwoaUBJie1HSi9Igakfl+6/r1XXGFHWGJ7WwWO8OAvAFzP3uLCbMV44jXkBppOGYIFKD8A5/aunh9u14/vfz9X3CYAAAAAAAAAAAAAAAAAAOCN7weA/yskBACPwshB94hDQmOeisMJANDGPWPJwlSQtQl6sz+w9wZgbP/ptwJWEkuY7v4ZAACgTqpbx/0WHFAqtXdorVTftam1cpd70QqrWfe8Ch8AuLiF00k4iNrjK+MvADGPc5dkAAYAAAAAAAAAAAAAAAAAgB35r5AiALxzD2gaiWxaGqizNNJzr0CtAADQUqodmyuaUCC3Ibz3jASp7crNFLFVBofYge19Y1S6vNr9o38Qik+fVtXR0m563fwAAECo9f3KWp8DOijN3JuS+7m1KoJe6zlrRZb6Xo06mMYxb3UAAFdx1nZRq2kXKWtPg6j9PsWfk/kXgAwyAAMAAAAAAAAAAAAAAAAAwI7818EAIEcYaegeILYwYmdtJh2RjQAAOIIxaMA2ywScGzL1qhkJYlpn2q3NzBt+rlXGjdj3K81U7IYLdkMmYAAA4KhaJRYFOihNycU1tcr8q7gBAJza0nkwobDd2HqcM3eaRG37VcZfANYgAzAAAAAAAAAAAAAAAAAAAOyIDMAAVLkHiJ0iERVmAi4lMigAAEfSKhMwlVpn/q3Vej17+V7A7kjkAwAApKzdP+U+BVYQ9EMnSdXNg1imtdrrxT3zr3IFAHBqvTPlHnU6R4zMvwC0IAMwAAAAAAAAAAAAAAAAAADsiAzAACwSRiaaiOgJAAAVGRgCMqVU2ssOix33Mfg3TK3ggAMAAAAbK+2eWNgNBtQI+xOdgBRonolMvzYAANwNMv4C0JAMwAAAAAAAAAAAAAAAAAAAsCP/HW7B1wRgW4cIHsDZzTICj0//wKWE5V47AACu594euP0wLM0ELEPsseQep9bvK3X28pTKxJx6H2xA4h4AAKDW1rf5uq3YtbMXUCfgpZUe/iH4t9ZoXhAAcGP4lSsL588r/0Brtffdi+/7HcnNDA/7/78X/P4AAJswgRsAAAAAAAAAOAsTgQEAAAD6+u+U/deDKOvQ4QVcTiRDmfqQK1P+AeB6puv/Z6Nvrj0BDaQyasf+riOVHXN9AAAAYhmXSu8X3F9wCLkpxhRoLiRV3MPMZEtPD6cXABCjncAVhO3rifIP7IX66LimYzfKAAwAsB4ZgAEA7QEAAAAAAODoTCAGAAAAWMd/W0VyI09qgm/pcTBhGDiKIbjeDOPz73BmYTnPjrgLABxOsn0bPAG89+v+1u117SKayC3IrQq8Bj8NCRwBAADkWuu2losrzbi7dkZeBfjUcjOch8Vu6Xy8WHHeS/958XYs7Gi6Z59xvgHA5cQu/8axuJIw46/yD/Sy9LZ7af3ktn97w8Nx+LzyjgAAAAAAAAAAAAAAAAAAgL35rxQC+7C3J+MVC6CXMPLRRGRQzqw08m5u4GwAYD2t7pPHhx+GBQscbp87Wjtau4hTKy3YCjQdhPWsYgYAANflfoBV5N6Amg9AR60TTcc+t9f5fbmm/bT4+uB8BoDLkfkXHtrTCj4QkbpdLq0+SufZhZbOG1m6ftqSARgAAABgA6MOMgAAAAAAAAAAAAAi/mvHHFvrCAGh3ssHCDMCywTMmYXXTQnCAOA4Wl+Hx2mZ4+3ni2QCXrqZuZ/XbqKrpQVZAaVC7X2h+0kAADiv1t1Bhml5kntDmUoFUluwpBi5tNJxdN5rlalsGoew3wHg/GT8hfn8duUfiHGfzBpkAAZgVwY3SQAAXIQMwAAAAAAAAAAAAADEzDIApwIoCrB4LKUReBxXOI+jZXi5X1+mSEkih8KM8+G9teq7sH51XCAu9zw56vm09nan7uNab08sc32r+rbV8s6QQGL8+NuQ6gj8hV8otv/Pfl3bKnFruF5Bjy4idl5KycoKwuKn2AEAwHnk9rfCIrmZf3sxAHcJDnNfYcayWmH/Uu18xNR4ROnnc8XWUzs/ttX4Uulyeo9rmf8A+xU7P9eaH7B0OaVS14dW14Xcz591vgsQN83b0b8CpJRe/3PbIeH7lt5Pa6ecgwzAAOyKDMAAAAAAAAAAAAAAAADA1c0yAKee7Pbk9zJ7ybTQ6jiKLAv7d9QML7PMZzICc0IiAPbVOiJkLHFbSiqyEpxZbjk/6vmw9nZvtZ961X+5kezC5a11P7uW0kj5Mff90ygT8NH1Or615ZeDWLuBLjU0AAAAO6AfgyelE4F6p8jk0HK7vxSXvpZmSBwz5+uUjs/njiO1yvyb+nts/aWfz1WbKaoX5x8cV+51NPW+3HHrpfVXaXMy9++l29FqnH7t+rP7PLTIhdAwJlcg8y+cT+95cbXtpNh2LZ2XybnNHgAGAAAAAAAAAAAAAAAAOCsP0gFwBMO/f+NvkLaTXbmWRhzai71HONTgAdaWG1kUjiC7HAvpA/XCUFlbpbpcuh17TdF51tShsKFh+q/yvBqDH7SbjyXVD6S6PZjSjr3Y+7dux7BLR29GAgAA+UozEe49YxUHk1vAFKRVHDVTHeu6Z85d2CH0PT9nfJinc6ejCQDOI7OB6fLPGd3Ltcy/wE2r+RW10/711xAabuXi39fHx6e9AwAAALAfOvMAAAAAAIAtGasAAOAKBg//AnAA/z3rQWrVAbX1E/V770hLbZ/GENBaGKF0FmkUDiQ7IVjuG6V2grnwfOh1fpRGvu6dsk0kbjiscYrcdjuPSyP035sNkfaDBB37VFtNq+4PojS1sxCfNJQqTuoNAADYv+zxJGhBQdu1Xhlo1DMn0yDz7+8PQZnQkQSszTwoaC93vPL9n+HQpnk4yjcQWivzb+37ubbTPgAMwDUYiAIA4Gw8nAUAAAAAAGzh4flfAAA4DXNvADgyDwAn6Mha5mz7L/agYe4DiLkJXwoDLGXvZw3XbW09if9s5STMbDbcvqB6my2sVu5KMwHXfj53O1J675i9V1ixSKSpC0LtfpMpbl9i+7/2uCw9nr3Lw1HKW209uDQDc2w5pQ3EVITj0gjIra4HvfdnKPyeezmerUzt2oeIo2PF6nIvCy4X+5B7HEqbgWv3I3h4PdDqwC59f2uXP7Abq8x4UFvMHG4AAFjfWrd9p+8Xyh0vq+0AadXPeDQ6FLuoHS6AV+Wmtj9nyvy7uHydpWOp9fdofd056kQwFRg1rlJutsqEkpoYvbatriNrzwtIaTUPIndie+XHWzMuRQ/3cnWwzL+508RCa32/2umxtcslb7/FlE43rL3dqC2XW01jhCPxADAAh6bBBgDAmdU8/AsAAAAAAJBrNO8GAICTM/cGgCPzADA8SD1IWBtRpFUCrlhElNKEkBqwx7Q0Ik7tg7JHKTdDsIGjkQl2qFmC1qUndC+tMtfmbu9aIcxaNQBKUy/WFhj1H8TVZo5tfV7V3nCk6qFWNx659XLp/mydEWOt+m5pRpBcsf0bbkepKYJpq8j9HEqvfozS6lM/BDR0O6FKz6/S/kMAAGA9ud1qrbvfqh31hr91qpO9ZQhjF0pPj9r3t6Y/4Nju88cq658x+HfxxLmz1IOtv8fSE33pOGIvKhBoZ6vx77Xmk5UOaNQuL3d9W2U8rr3/2LrB2JnbKGqkTuOpfXy08lU6zTT3fWs1b2vXk5qWtnR5pc5WL9XO82k9LTy2nqXclnAFn44yAAAAwH7ppAQAAAAAAHoxDgEAwFkMHioH4ISGf//G3ySJrnJcwNJIFJPcwEup9y19PVzObiL+ZpIJ6FduILFW+2tpOTnKcbsHqDNKcWi5hy+3Pj1scSi9gAAApzI8RiatjeAvAzA7FGvW5iYiv3p/wmqWhtR1oPq47d/S60Lp/bLDBwAA/eSO/7eeDzDL8Fja8N/7/V5pxq/cjgguxeHnCJZm/p18jx+MZ55fU3udi31eBQHwrHZiXu2AoHq4jdRA7dEmpDdiXGzfUv0oQ/CHs2Zwbd28Xbpe8zjW1at6ru0my5334/INdYbb+fPv6+Pjv/YhAGd2+Ac/AQC4PM/QAQAAAAAAPZhPAwDAmZhbA8AZeQCYS2nVYdl68nVsu3K3t/R7Lc2M2ep7HyWD7NkyJ6eOd2lA5HC5e3H/nlMkJxnPDmVpvVb6+mpqT5yFkcc9NATAru09YstWIQmD/fITgX/4+Pgcf18rva4P4fKabSjUK70v1Z49GAesr9v+rb3fc3gAAGC/dt9vc/QbitYDiyJS70qrcdFYJu6P4O+wleGjbqzgqRxPBXncuEyvfYKVVhQmXAD0UVvfa5i1kZtC0v5lx+6Zfw+W8bc0k+pSSzPChr+rHvpq/TxPbnlLvb90+coHtLine truncated

After

Width:  |  Height:  |  Size: 1.9 MiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="582" height="667" viewBox="0 0 582 667"><image width="582" height="667" xlink:href="data:image/png;base64,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VkILine truncated

After

Width:  |  Height:  |  Size: 608 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="139" height="765" viewBox="0 0 139 765"><defs><style>.a{fill:url(#a);}</style><pattern id="a" width="1" height="1" patternTransform="matrix(-1, 0, 0, 1, 278, 0)" viewBox="0 0 139 765"><image preserveAspectRatio="none" width="139" height="765" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 50 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="139" height="765" viewBox="0 0 139 765"><image width="139" height="765" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARYAAAX6CAYAAAA2hEelAAAgAElEQVR4nOy96Zcj2Xmf+UYggMyq3tVNNilqsTZTsiQvsjRjnxl7jv1x/to583XmzLF2L9pF0ZZIbSZFNltk71WVmUDEnRPAvZkXF3eLwBbL8/RBV+WCACoT8eIX7/J7i//7W0r6UKTvY77F+wD9HrU/nR/vyCd46X8fwJAo+W0AwKmp+h7PvCN3UC6hQwDAxECxAMDJ6a1YDBmyI0PUdD0kAAyZowPLEaQCDgEGYKScO7AUPRRLCAINwEi4pmIJ4QYiN09MgAEYOHZgOfWlif39fVRL4fwZenwCDcDAGKJiCZETaAgyAAOg6qAmOqkOpU9y606nyrW4hAINQQbgSlxSsZw6R+ILVAUBBuD6VObEU6dXFEXg7+eC8jXAQLiUYvGd1Odq9fflYlAxABfEDixK//+k6kI9ncS7P4uD3pZLq5n2eTTOxwBwQq6pWHzkNNR1DQTu8agqAZwZX2BxT67TKgrlVTDiCSrnVDL28KX9fFAyACfgWn0sMbUQUhj2x6fKw/g+h5IBOJKcwHIpBSOOYjAKpkwEm1MGGVfJqEDyFwAiDK3zNlS9iV0m+QJd30DgHtcElULcJDTBBiBIn8CinA/OlQsxj7NVMcV+GdlWMb6hxVM+J/fSy1YxTeR+ALNlDLNCMRUj1uVLKAl8zPCi+xhiBRTlqJrcYwJMnlMElvPmYJ4exJeLKYqnE7wIWG2eo6PYPI6bg2nIyQCMa7rZxlYIhXVT+qRXnkulU1STDL5jhi6X3K8BTJ5zBJbQSXRuJeP+6VaVcpVV3wY8+xLMrSg1BBeYE2NVLC72SVvrPwtHvRQBFWM4xYnvHjeWiyHQwGS5ZGC5SC7GebzHXIxzS/XGnAo7FyMeBUNVCSbJVBRLiFAuRjI+PsVEtFtVMoGl1EGl4DIJpsg1A8up+2Fycibm77UJIsVhRSnUH3MKfIFNCC4wNaauWCTi+m8neUtLScTyMKd8TqWTeyGwwGQYUmBRzkddT+he1Si1n+uoi2LvpA/lY06FHdQILjAZ5qBYclCOgrFzMspzCSMRJZT7eOIJLHXifgCjYMiB5VT9MCE7Bv83u34xOslaFAcqxp1J6qNq7OM1ViADGDUolidSuRjlVHdCVSbfMVKPW1rBCtUCo2eMgaVvP0wn5eLcr1Bqr3LTFMXjsRaZvjEhzPcsZKeYqBDB6EGxhIn5vKg26Vsczic1Ttk6N4i5lSlUC4yaKQQW77t7pC+mr3LZO0ahdEWpkKZ4uoRplU3pKJmcAONr2kO1wGhBsfSj0IpFPGXixlEfiw5BjFwLTIIpB5ZULiZXuYRa/Au1++z2j+LpKOaSqNFBxe3oTT3OQv9JrgVGi88YCXrgyBblBBg7QMQCTOncAEbJnC6Fjs2thJSLLhsFH7OxelRSXbw+qwdUC4wO3hVPRKQfv+loj3CJUQKAs0JgOX5GZ6/q47kkanQiNucxzPeYpG+XxC/AYCCwnJ8+uRa7G5fAAqNjjuXmrtWg0P26Dh3Wst+nknoOC32/TYfHARgEk1QsA812uqolpVhQLTBaJqVYlDydticILsdVkfbvZSuWLj9z08Hru5QCGCwolssRyrXEsFULwGiYc0t/KGcSyn+Evs/7sdKfLQ6Vi70tMXU5JFauJbeyBHB1eCe8LMrpa0kNKNKJC6NkEorF5FZO/HYeyrH0qQrZx6wtFZJTmSqtvhZUC4yCUb8LjtR9uk+ehVwLjIpRKha7+iPXDy5dcy1GtdgzRCF8nbhUh2DwoFiuQ9NxhqiwgguqBQYPRk9PAiikPEJk51qKw4SKncTNvcRZZCgcgEEwmsCiAn8fMbVl6pQKUubrdicul0MwWEYRWEJVn4GcWcmZIuPX4qSFaqvK08UTd2HlaAguMEhGpVgmdha5nbg5lzh2EneCPxKYCiQCnwidqCc5gQMHUZnqw1ZBC/2GQK4FBguB5bo0jhFUKljYlSF+dzBYBn0pZOdWrqD5u043e3MtSu1/g9lyZn1PbSVycztx7Z1FXA7B4BhcYHk8Ea8XUC6N289SJvpbCmd+iP1DMDgGFViUCSZq/3MjZ686ZOugYt8T1w4mIeVit8Tg0wKDZZCKZQLBpAvK6cTNyZ2U+ndXczkEQ4TO20OO3T9k6DIFbedaYvubD6pDSmR95PMEODlXrywoS6XMTKnYNNo0e9PDToHqEAyOqyqWNpA0w8+puM8o5DCXcvHf78Q9HNDeWHmT1O+lcJrl3BwNwFW5eGBxg4ibrJ0xff1wTV8LnbgwGC4aWA4Uyuld365JJ2e5gCeum8TNqQ5VWu1QHYLBcPHrczufwllwQNNRuZS0+MMQoSoUxvVpcUlVj0I5mL1v8Pi0mOqQnZhNVYfY8QyD4uyBRU330ucc2EncKqNHxc2z0NMCg+CsgcXNqZzJTf9SKL/IOCDXtCk0SV07VZ7cVawL674AV+UsgcUEkMZTTgbPD8ufwFUZQcxgd+KSxIWrcx7FogNKM71Ln1zVYuPPtZifjXXxsufkrx4DTCx/4s4OLawmO4CrcZaq0GM3Lb/XNP4fUuNYV6YwiqWiExeGAFWhNEZhdK26ZOda2iDcqhXrktHOtVRUh2BsHPXuVuydHc7MD3LlGHyzQ6kkrj07RHCBq3KUYlHWZU9tBRNlfwP0/dG6l0Kx4FI4qqUkiQvXpFdgsXtTzN8bVMpReLLBXbtwxWrxXzOUCNekU2B5LGCYUrKVoJ1RSTmVM8n9/N5xCqtKZN3BdpdLXbYqp8U/N/ELcHI65Vhs96HGDi68fI8m8CO0cy0561ULqkMwBDopFvdqp0sH1wTIdZZLzggFvj/VieuuY/Udh8ACgyD64nOrPo1zCZQqVcDRKEuxuL+S0K/MnR0CuDhJxWJXfeyA8tgtyu8sF/tHlXvxaAKLWx0K4c4OUR2Cq1D5XqV256zJp9SWhwrB5DzYZ7/HSqHrjufKqhARWOCiVJajwSOuQhFa9H0E4sCBsvDlUA6+31WC6rA6tHEMnUIWCbaz3MK6jAK4GFXtaRJvrMBinwkolfMSijxOdWiRkZh1FQtWCnBRqo1+ybnxxQQVFXlrhEciMSGbPWXj/LxDnbg+Ckex8H4AF+dQsTjwqhwEqufeoQpnObgGle/VRjCJktvP4pL6/tiJb1eHujjL2bNDXA7BxSjtfZ7kUQbEYZhRPWeH6GmBi1MhkI/mFPmVPSLOco2lXFLVIXGSuMwOwcWg7XuohJ3lNh3sJ2nxh6uAg9xpyU2s2rgB4qk6dFiSc138U4vK2sugpQ5E91f9ycCs4F1sXDTO7FAqd1I6CVyAi4Bi6Y/pN8lVICGi9y8Ov+abHYr1tCw8znIAZ4V3sYHjMebr4yzHxDNcFG8fC1yE5LSz0s5ycjg71PY15pg/mePa1SGmneHsoFgGTsBK2HWWS8HeIbgovMhOhwrHgSS+PMne59T+zdfiH+t1LHV1aMmlEFwCAss4cftZysTvEsUCF4Wq0PH4Zodi/Swxj9tcl7mu1SETTOyJZ/IscDZ49xoBnmssZamW3PJxYa0GWWR8P0BvqAqdj1wbyaRKCTjL2Yvj64T69FWHmB2Cs4FiGQGRrHDjsVOIwewQXARyLKcnd1NizAs39TmxPrex/HBDwaKwkrzb6pDazQ7h0QJngXetceP2s1AdgkGAH8t56bItJb3judDrbP3+LKnqkPn8kgQunBvetcaN6tiB67OsBDg5vLBOR6jrNtWN6yqM4PcqtX/TuAncVP+LOJdEdOLCySGwjB/X/CkHE1iWvAbgHFAVGh4h9RJzlnMXmqW6ftvvWellZg9Uh+DU8G41DZQOEraVQqo6tKQ6BOeCztvz4aqNlLOcz0kumHvxrGBd69tNxjYXLoXgrPCiGime2aGu1SHbspIELpwUAsvp8VWBTuXR8nQw9WTMYiVwuwwliqVcCC5wUggsI8UTvZQzlJjTnEfZGc4CVaHLcczO59zZoUY9XRKViTxLYe0dWvZQOwBBUCzTwp0dSq3jxrISzgKK5fzYyiInkHeqDnlWg6ydFay+ipTdgbukOgSnhhfTtHADS06OZUE/C5wa+lguSyzPEsqdxHIqoU2JaytfkpNnsa0UyLPA0fAuNXKUOog4bmXIkMq1UHaGk0GO5XzsTmrlde/POeFDrv9q75PFUz9L8XSP2pPAjT1OqWeH8MKFk4BiGTkexSKevUMpaPGHk4KD3OVJ9bPEfG+bxPeJ9X1mdqhKKFPbC3elRO6G+EODccG70zRpAkncEEw7w0khx3I5uvazuPTpZ1k794t54ZpLIbxw4Wh4d7oeJ7sIDWxKdKedY4qFaWc4KSiW82OGkX1qI3Wyux/7e1mcBK6uDu0uh5Q3L+N7LGXNDlUdEr8AB6BYrsMxNgoHwSFwMBVYDxLDVIdWqBY4Bjpvr0dIgaRO6PC0s98L1/ja3lj5k9js0Eqrlge6cKEvKJZx0VVF2EncnCDxWHbmtQHHQI7levT1Z4lWhxwarTzs6lARCTILS7EQWKA3vHgmhDrsxHU7cHP8WdjtDEeDYrk8x/qzeHMzSpedlPXVojhYCxI6pv15O7gs2DkEfeBd6fp0zZ9Hq0Oe2SG3MpR6vIJpZzgWFMvl2PWzHE47S08fXAlWhw6p1VOL/9L6/lCQsZ3lalQLdAXFcl0uVe1vApdEPpRTduY1Ap2pFI0s1ybUlxJTMU2P2SFTHVpZlzu+IGO797ff+2pcP04YArwbzQPXvT/1e0exwFGQY7k+faaeoz64HrpMO4tj/MS0M3SLine truncated

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="727" height="727" viewBox="0 0 727 727"><defs><style>.a{fill:url(#a);}.b{fill:url(#d);}.c,.d,.f{fill:none;}.c{stroke:rgba(32,194,255,0);stroke-width:8px;}.d{stroke:rgba(32,194,255,0.15);stroke-width:5px;stroke-dasharray:7 5;}.e{stroke:none;}.g{filter:url(#e);}.h{filter:url(#b);}</style><linearGradient id="a" x1="0.351" y1="0.016" x2="0.065" y2="0.243" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#1ec6fe"/><stop offset="1" stop-color="#3279ff"/></linearGradient><filter id="b" x="88.068" y="60.324" width="217.004" height="181.59" filterUnits="userSpaceOnUse"><feOffset input="SourceAlpha"/><feGaussianBlur stdDeviation="7.5" result="c"/><feFlood flood-color="#1a9cff" flood-opacity="0.8"/><feComposite operator="in" in2="c"/><feComposite in="SourceGraphic"/></filter><linearGradient id="d" x1="0.025" y1="0.971" x2="0.971" y2="0.034" xlink:href="#a"/><filter id="e" x="424.041" y="484.359" width="215.309" height="181.689" filterUnits="userSpaceOnUse"><feOffset input="SourceAlpha"/><feGaussianBlur stdDeviation="7.5" result="f"/><feFlood flood-color="#1a9cff" flood-opacity="0.8"/><feComposite operator="in" in2="f"/><feComposite in="SourceGraphic"/></filter></defs><g transform="translate(4 4)"><g transform="translate(-126.998 -127.002)"><g class="h" transform="matrix(1, 0, 0, 1, 123, 123)"><path class="a" d="M18115.566-21172.494q4.919-8.443,10.389-16.543a294.531,294.531,0,0,1,35.713-43.289,294.545,294.545,0,0,1,43.293-35.717,292.5,292.5,0,0,1,49.682-26.969,290.614,290.614,0,0,1,31.271-11.162l1.656,5.506a288.092,288.092,0,0,0-166.973,131.084Z" transform="translate(-18005 21389)"/></g><g class="g" transform="matrix(1, 0, 0, 1, 123, 123)"><path class="b" d="M18451.539-20750.967a288.209,288.209,0,0,0,165.279-131.172l5.029,2.92q-5.1,8.815-10.8,17.258a294.659,294.659,0,0,1-35.717,43.287,294.647,294.647,0,0,1-43.293,35.717,292.2,292.2,0,0,1-49.686,26.971,291.1,291.1,0,0,1-29.178,10.537Z" transform="translate(-18005 21389)"/></g><circle class="c" cx="359.5" cy="359.5" r="359.5" transform="translate(126.998 127.002)"/></g><g class="d" transform="translate(98 98)"><circle class="e" cx="261.5" cy="261.5" r="261.5"/><circle class="f" cx="261.5" cy="261.5" r="259"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="458" height="113.588" viewBox="0 0 458 113.588"><defs><style>.a{fill:url(#a);}.b{fill:#fff;}.c{filter:url(#d);}.d{filter:url(#b);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f0feff"/><stop offset="1" stop-color="#c1e6ff"/></linearGradient><filter id="b" x="111.721" y="26.943" width="211.559" height="46.289" filterUnits="userSpaceOnUse"><feOffset dy="1" input="SourceAlpha"/><feGaussianBlur stdDeviation="3" result="c"/><feFlood flood-color="#00ffa2" flood-opacity="0.322"/><feComposite operator="in" in2="c"/><feComposite in="SourceGraphic"/></filter><filter id="d" x="111.721" y="26.943" width="211.559" height="46.289" filterUnits="userSpaceOnUse"><feOffset input="SourceAlpha"/><feGaussianBlur stdDeviation="2" result="e"/><feFlood flood-color="#fff" result="f"/><feComposite operator="out" in="SourceGraphic" in2="e"/><feComposite operator="in" in="f"/><feComposite operator="in" in2="SourceGraphic"/></filter></defs><g transform="translate(-731 -564.412)"><image width="458" height="113.588" transform="translate(731 564.412)" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 813 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#2bbaff"/><stop offset="0.498" stop-color="#1a8cfd"/><stop offset="1" stop-color="#006aef"/></linearGradient></defs><g transform="translate(14482.893 -23044.904)"><rect class="a" width="68" height="68" transform="translate(-14482.893 23044.904)"/><path class="b" d="M110.023,105.37H98.2a1.721,1.721,0,0,1-1.716-1.716v-1.918h10.189a1.721,1.721,0,0,0,1.716-1.716V72.8a1.721,1.721,0,0,0-1.716-1.716H98.2V67.437A3.444,3.444,0,0,0,94.76,64H87.83a3.444,3.444,0,0,0-3.437,3.437v3.652H75.625A1.721,1.721,0,0,0,73.91,72.8v27.21a1.721,1.721,0,0,0,1.716,1.716h10.49v1.918a1.721,1.721,0,0,1-1.716,1.716H72.3a1.721,1.721,0,0,0-1.716,1.716v10.1A1.721,1.721,0,0,0,72.3,118.9h37.724a1.721,1.721,0,0,0,1.716-1.716v-10.1A1.726,1.726,0,0,0,110.023,105.37Zm-8.406-19.1a3.437,3.437,0,1,1-3.437-3.437A3.436,3.436,0,0,1,101.616,86.266ZM87.83,67.437h6.924v3.646H87.83ZM81.121,86.266A3.437,3.437,0,1,1,84.558,89.7,3.436,3.436,0,0,1,81.121,86.266Zm32.48-7.089V93.355a1.716,1.716,0,0,1-3.431,0V79.177a1.716,1.716,0,0,1,3.431,0Zm5.122,3.548v6.881a1.716,1.716,0,0,1-3.431,0V82.724a1.716,1.716,0,0,1,3.431,0ZM70.644,95.07a1.717,1.717,0,0,1-1.716-1.716V79.177a1.716,1.716,0,0,1,3.431,0V93.355A1.713,1.713,0,0,1,70.644,95.07ZM67.231,82.724v6.881a1.716,1.716,0,1,1-3.431,0V82.724a1.716,1.716,0,0,1,3.431,0Zm0,0" transform="translate(-14540.154 22987.455)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,48 @@
<svg xmlns="http://www.w3.org/2000/svg" width="188" height="241" viewBox="0 0 188 241" fill="none">
<defs>
<radialGradient id="surface" cx="0" cy="0" r="1" gradientTransform="translate(63 27) rotate(61) scale(148 156)" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFFFFF"/>
<stop offset=".2" stop-color="#FAFDFF"/>
<stop offset=".48" stop-color="#E4F2FF"/>
<stop offset=".72" stop-color="#A7D4FF"/>
<stop offset="1" stop-color="#3C9AF4"/>
</radialGradient>
<radialGradient id="inner-blue" cx="0" cy="0" r="1" gradientTransform="translate(94 101) rotate(90) scale(68 84)" gradientUnits="userSpaceOnUse">
<stop stop-color="#68B6FF" stop-opacity=".58"/>
<stop offset="1" stop-color="#1688F7" stop-opacity="0"/>
</radialGradient>
<linearGradient id="lower-blue" x1="94" y1="103" x2="94" y2="165" gradientUnits="userSpaceOnUse">
<stop stop-color="#4EA7FA" stop-opacity="0"/>
<stop offset="1" stop-color="#188AF4" stop-opacity=".72"/>
</linearGradient>
<linearGradient id="edge" x1="38" y1="10" x2="151" y2="160" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFFFFF" stop-opacity=".98"/>
<stop offset=".52" stop-color="#FFFFFF" stop-opacity=".86"/>
<stop offset="1" stop-color="#C6E8FF" stop-opacity=".9"/>
</linearGradient>
<filter id="glow" x="-13" y="-13" width="214" height="214" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="4.8"/>
</filter>
<filter id="shadow" x="18" y="162" width="152" height="49" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="7"/>
</filter>
<clipPath id="clip">
<circle cx="94" cy="82" r="81"/>
</clipPath>
</defs>
<ellipse cx="94" cy="188" rx="65" ry="9" fill="#147BD2" fill-opacity=".23" filter="url(#shadow)"/>
<circle cx="94" cy="82" r="82" fill="#5EBAFF" fill-opacity=".2" filter="url(#glow)"/>
<circle cx="94" cy="82" r="81" fill="url(#surface)"/>
<g clip-path="url(#clip)">
<ellipse cx="94" cy="106" rx="80" ry="61" fill="url(#inner-blue)"/>
<ellipse cx="94" cy="143" rx="91" ry="38" fill="url(#lower-blue)"/>
<ellipse cx="94" cy="151" rx="76" ry="14" fill="#FFFFFF" fill-opacity=".11"/>
<path d="M20 145C47 157 141 157 168 145" stroke="#FFFFFF" stroke-opacity=".28" stroke-width="1.2"/>
<path d="M31 160C55 168 133 168 157 160" stroke="#7CC4FF" stroke-opacity=".35" stroke-width="1.1"/>
<path d="M22 64C27 37 49 16 77 9" stroke="#FFFFFF" stroke-opacity=".42" stroke-width="8" stroke-linecap="round" filter="url(#glow)"/>
</g>
<circle cx="94" cy="82" r="81" stroke="url(#edge)" stroke-width="2"/>
<circle cx="94" cy="82" r="76" stroke="#FFFFFF" stroke-opacity=".24"/>
<path d="M19 70C24 39 49 13 81 5" stroke="#FFFFFF" stroke-opacity=".88" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;stroke:#707070;stroke-width:3.75px;opacity:0;}.b{fill:url(#a);}.c{stroke:none;}.d{fill:none;}</style><linearGradient id="a" x1="0.431" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#2bbaff"/><stop offset="0.498" stop-color="#1a8cfd"/><stop offset="1" stop-color="#006aef"/></linearGradient></defs><g transform="translate(-71.077 -67.746)"><g class="a" transform="translate(71.077 67.746)"><rect class="c" width="68" height="68"/><rect class="d" x="1.875" y="1.875" width="64.25" height="64.25"/></g><path class="b" d="M114.406,106.791a7.707,7.707,0,0,0-4.354,1.353L98.7,99.937a13.3,13.3,0,0,0,1.742-6.583c0-.421-.03-.842-.06-1.263L112,85.207a4.687,4.687,0,1,0-1.952-2.345L99.691,88.995a13.678,13.678,0,0,0-6.1-7.365l2.072-10.341c.15,0,.3.03.451.03a4.66,4.66,0,1,0-4.655-4.659,4.727,4.727,0,0,0,1.412,3.367l-2.1,10.462A13.479,13.479,0,0,0,80.05,81.87l-6.4-5.712A6.129,6.129,0,1,0,71.7,78.443L77.677,83.7a13.456,13.456,0,0,0,.691,19.991l-5.316,5.622a5.948,5.948,0,0,0-3.394-1.022,6.163,6.163,0,1,0,6.156,6.162,6.343,6.343,0,0,0-.721-2.916l5.826-6.193a13.464,13.464,0,0,0,16.1-2.946l11.022,7.966a7.77,7.77,0,0,0-1.2,4.118,7.658,7.658,0,1,0,15.316,0A7.817,7.817,0,0,0,114.406,106.791Zm0,0" transform="translate(13 9.671)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.337" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#2bbaff"/><stop offset="0.498" stop-color="#1a8cfd"/><stop offset="1" stop-color="#006aef"/></linearGradient></defs><g transform="translate(-204.426 -230.797)"><rect class="a" width="68" height="68" transform="translate(204.426 230.797)"/><g transform="translate(209.658 232.191)"><path class="b" d="M25.52,64.045c-1.725-.852-8.6-4.486-10.846-5.656-2.309-1.207-3.3-1.01-5.09.146L6.346,60.67,0,46.383s6.887-3.254,10.033-4.508C15,39.885,17.273,41.6,20.115,43.4c1.209.762,5.131,3.664,5.131,3.664a17.886,17.886,0,0,0,7.783,2.316c5.928,0,6.975.344,6.975,2.266,0,2.113-3.859,2.266-3.883,2.266H26.605c-3.236,0-7.766-1.893-7.766-1.893s3.846,4.156,7.766,4.156h9.547c2.105-.047,6.1-1.047,6.1-4.537,0-4.529-4.066-4.529-9.219-4.529a5.2,5.2,0,0,1-1.127-.145c.2-.1.4-.207.639-.35.9-.523,2.145-1.27,3.535-2.158,3.67-2.3,5.555-2.592,7.387-.152a5.718,5.718,0,0,1,4.248-1.678c1.551,0,2.865,1.039,2.494,2.719,3.975-2.377,5.936-1.42,6.809-.266,1.156,1.527.373,3.109-2.143,4.926C53.244,51.17,37.809,61.543,35.635,62.82c-1.732,1-3.326,2.395-5.8,2.395A9.825,9.825,0,0,1,25.52,64.045ZM22.51,39.5l-.229-.865a.706.706,0,0,1,.129-.607.688.688,0,0,1,.557-.275H35.734a.689.689,0,0,1,.547.275.7.7,0,0,1,.121.607l-.219.865a7.065,7.065,0,0,1-13.674,0Zm6.84,2.486a4.265,4.265,0,0,0,3.178-1.424H26.174A4.239,4.239,0,0,0,29.35,41.982Zm-6.285-5.07a.885.885,0,1,1,0-1.77H35.658a.885.885,0,0,1-.008,1.77Zm-.189-2.637a.7.7,0,0,1-.707-.7V31.021a.565.565,0,0,0-.244-.461,13.168,13.168,0,1,1,14.844,0,.563.563,0,0,0-.242.461v2.547a.7.7,0,0,1-.7.707Zm8.672-8.785a6.207,6.207,0,0,0,2.455-.3c.547-.258.721-.676.721-1.572V14.719H33.059v8.887c0,.219-.1.281-.334.3-.234,0-1.033,0-1.732-.039A6.217,6.217,0,0,1,31.547,25.49Zm-6.18-2.166c0,1.535.463,1.967,1.969,1.967h1.7c1.285,0,1.717-.516,1.877-2.174a4.544,4.544,0,0,1-1.443-.607c-.062,1.141-.145,1.361-.586,1.361H27.557c-.494,0-.578-.055-.578-.562V20.117h1.475a3.131,3.131,0,0,1-.213,1.223.374.374,0,0,1-.334.131,6.154,6.154,0,0,1-.836-.047,3.419,3.419,0,0,1,.41,1.4,8.144,8.144,0,0,0,1.3-.045,1.284,1.284,0,0,0,.844-.455c.281-.35.395-1.223.465-3.107l.016-.2.744-.707v4.154h1.619V15.793H30.848V18.17a28.18,28.18,0,0,0-2.326-2.758c.084-.158.152-.328.221-.486l-1.535-.479a9.09,9.09,0,0,1-3.215,3.9,7.919,7.919,0,0,1,1.2,1.27l.174-.129Zm.906-4.643a11.211,11.211,0,0,0,1.465-1.855c.518.609,1.049,1.27,1.469,1.855Zm17.676-2.123a1.606,1.606,0,0,1,1.186-1.943l2.363-.578a1.608,1.608,0,0,1,.76,3.125l-2.369.576a1.629,1.629,0,0,1-.383.047A1.611,1.611,0,0,1,43.949,16.559ZM12.631,17.631,10.389,16.7a1.608,1.608,0,0,1,1.23-2.971l2.242.941a1.6,1.6,0,1,1-1.23,2.963Zm4.088-9.514L15.328,6.125a1.605,1.605,0,0,1,2.637-1.832l1.391,1.99a1.6,1.6,0,0,1-.4,2.234,1.58,1.58,0,0,1-.916.289A1.6,1.6,0,0,1,16.719,8.117Zm22.709.32a1.614,1.614,0,0,1-.273-2.252l1.488-1.914A1.6,1.6,0,1,1,43.174,6.24L41.686,8.16a1.626,1.626,0,0,1-2.258.277ZM27.746,4.043V1.6a1.607,1.607,0,0,1,3.215,0V4.043a1.607,1.607,0,0,1-3.215,0Z" transform="translate(0 0)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#2bbaff"/><stop offset="0.498" stop-color="#1a8cfd"/><stop offset="1" stop-color="#006aef"/></linearGradient></defs><g transform="translate(14482.893 -23044.904)"><rect class="a" width="68" height="68" transform="translate(-14482.893 23044.904)"/><path class="b" d="M43.013,57.336h-.255l-.074,0a14.064,14.064,0,0,1-8.911-3.467,6.216,6.216,0,0,1,6.1-5.029h6.278a6.218,6.218,0,0,1,6.1,5.03A14.073,14.073,0,0,1,43.6,57.324h-.009l-.067,0H43.5l-.063,0h-.424ZM30.159,54.9H8.5a1.717,1.717,0,0,1-1.715-1.716v-10.1A1.717,1.717,0,0,1,8.5,41.363H20.6a1.717,1.717,0,0,0,1.716-1.715V37.73H11.824a1.717,1.717,0,0,1-1.716-1.715V8.8a1.718,1.718,0,0,1,1.716-1.716h8.768V3.437A3.44,3.44,0,0,1,24.029,0h6.93A3.441,3.441,0,0,1,34.4,3.437V7.089H42.87A1.718,1.718,0,0,1,44.586,8.8V26.022c-.515-.046-1.044-.07-1.574-.07A17.327,17.327,0,0,0,30.157,54.9Zm4.219-36.07a3.437,3.437,0,1,0,3.438,3.437A3.441,3.441,0,0,0,34.378,18.828Zm-13.619,0A3.437,3.437,0,1,0,24.2,22.265,3.441,3.441,0,0,0,20.758,18.828ZM24.029,3.437V7.083h6.924V3.437Zm7.256,47.59v0a14.059,14.059,0,1,1,23.456,0,9.508,9.508,0,0,0-8.589-5.455H44.686a7.193,7.193,0,1,0-3.347,0H39.873a9.5,9.5,0,0,0-8.587,5.457ZM43.013,42.5a3.923,3.923,0,1,1,3.923-3.924A3.928,3.928,0,0,1,43.013,42.5ZM6.844,31.07a1.717,1.717,0,0,1-1.716-1.715V15.176a1.715,1.715,0,1,1,3.431,0V29.355A1.717,1.717,0,0,1,6.844,31.07ZM49.8,27.327h0a17.187,17.187,0,0,0-3.429-1.051v-11.1a1.715,1.715,0,1,1,3.431,0v12.15Zm3.406-.007a1.717,1.717,0,0,1-1.715-1.715V18.724a1.715,1.715,0,0,1,3.431,0v6.881A1.717,1.717,0,0,1,53.206,27.32Zm-51.491,0A1.717,1.717,0,0,1,0,25.605V18.724a1.715,1.715,0,0,1,3.431,0v6.881A1.717,1.717,0,0,1,1.715,27.32Z" transform="translate(-14477.664 23050.15)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;stroke:#707070;stroke-width:3.75px;opacity:0;}.b{fill:url(#a);}.c{stroke:none;}.d{fill:none;}</style><linearGradient id="a" x1="0.414" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#2bbaff"/><stop offset="0.498" stop-color="#1a8cfd"/><stop offset="1" stop-color="#006aef"/></linearGradient></defs><g transform="translate(-2.29 3.525)"><g class="a" transform="translate(2.29 -3.525)"><rect class="c" width="68" height="68"/><rect class="d" x="1.875" y="1.875" width="64.25" height="64.25"/></g><path class="b" d="M36.066,58.469a15.431,15.431,0,0,1,0-21.866,15.011,15.011,0,0,1,3.577-2.662,2.018,2.018,0,0,1,1.878,3.571,11.53,11.53,0,0,0-2.6,1.947A11.446,11.446,0,0,0,55.132,55.622a11.938,11.938,0,0,0,1.878-2.462A2.02,2.02,0,1,1,60.5,55.2,15.711,15.711,0,0,1,58,58.469a15.533,15.533,0,0,1-21.929,0Zm-5.87-.994H5.225A5.225,5.225,0,0,1,0,52.263V13.925A5.226,5.226,0,0,1,5.225,8.712H53.769A5.226,5.226,0,0,1,59,13.925V32.117A19.526,19.526,0,0,0,30.2,57.474v0ZM21.753,18.77l-8.5,7.4a2.531,2.531,0,0,0-.239,3.565,2.536,2.536,0,0,0,3.568.239l6.835-5.958L28.446,28.4a2.52,2.52,0,0,0,3.351-.015L42.282,19a2.508,2.508,0,0,0,.191-3.557,2.529,2.529,0,0,0-3.567-.192L30.085,23.14l-5-4.369a2.544,2.544,0,0,0-3.335,0ZM47.03,49.554a2.017,2.017,0,0,1-2.022-2.017V34.091a2.021,2.021,0,0,1,2.022-2.017A15.485,15.485,0,0,1,62.537,47.537a2.018,2.018,0,0,1-2.024,2.017Zm2.025-4.034h9.258a11.471,11.471,0,0,0-9.258-9.229ZM7.53,5.051a2.521,2.521,0,1,1,0-5.043H50.681a2.521,2.521,0,1,1,0,5.043Z" transform="translate(6.296 0.013)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="582" height="667" viewBox="0 0 582 667"><image width="582" height="667" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 664 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="53" height="53" viewBox="0 0 53 53"><defs><style>.a{fill:rgba(0,121,254,0.3);stroke:rgba(0,121,254,0.5);}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#92eeff"/><stop offset="1" stop-color="#00c7ff"/></linearGradient></defs><g transform="translate(0.5 -115.502)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(2.236 118.236)"><path class="b" d="M9204.336-22218.762a6.4,6.4,0,0,1-2.489-.691,6.242,6.242,0,0,1-2-1.615,6.363,6.363,0,0,1-1.206-2.283,6.357,6.357,0,0,1-.246-1.863c-.047-.033-.094-.066-.139-.1a6.4,6.4,0,0,1-2.272-3.4,6.418,6.418,0,0,1,.268-4.076,6.336,6.336,0,0,1,1.233-1.912,6.383,6.383,0,0,1-.4-1.973,6.41,6.41,0,0,1,.53-2.809,6.441,6.441,0,0,1,1.7-2.289,6.424,6.424,0,0,1,1.751-1.057,4.969,4.969,0,0,1,.343-1.289,4.965,4.965,0,0,1,1.334-1.859,5.038,5.038,0,0,1,2.016-1.082,5.037,5.037,0,0,1,2.289-.074,5.024,5.024,0,0,1,2.089.943,4.947,4.947,0,0,1,.871.861,5.147,5.147,0,0,1,.876-.861,5,5,0,0,1,2.083-.943,5.035,5.035,0,0,1,2.289.074,5.06,5.06,0,0,1,2.021,1.082,5.031,5.031,0,0,1,1.334,1.859,5.008,5.008,0,0,1,.343,1.289,6.424,6.424,0,0,1,1.751,1.057,6.441,6.441,0,0,1,1.7,2.289,6.405,6.405,0,0,1,.524,2.809,6.434,6.434,0,0,1-.4,1.973,6.315,6.315,0,0,1,1.234,1.912,6.418,6.418,0,0,1,.268,4.076,6.4,6.4,0,0,1-2.272,3.4l-.143.105a6.214,6.214,0,0,1-.248,1.859,6.26,6.26,0,0,1-1.2,2.283,6.271,6.271,0,0,1-2.01,1.615,6.354,6.354,0,0,1-2.485.691c-.14.006-.278.012-.418.012a6.213,6.213,0,0,1-2.139-.375,6.394,6.394,0,0,1-2.2-1.34,6.4,6.4,0,0,1-.915-1.066,6.415,6.415,0,0,1-.91,1.066,6.427,6.427,0,0,1-2.206,1.34,6.283,6.283,0,0,1-2.143.377C9204.609-22218.748,9204.473-22218.752,9204.336-22218.762Zm6.768-6.342a4.232,4.232,0,0,0,.346,1.652,4.123,4.123,0,0,0,.972,1.379,4.116,4.116,0,0,0,1.44.877,4.062,4.062,0,0,0,1.67.234,4.159,4.159,0,0,0,1.63-.447,4.159,4.159,0,0,0,1.313-1.061,4.227,4.227,0,0,0,.787-1.49c.02-.07.037-.139.054-.209a6.32,6.32,0,0,1-1.422.164,1.1,1.1,0,0,1-1.1-1.1,1.1,1.1,0,0,1,1.1-1.1,4.178,4.178,0,0,0,2.529-.859,4.138,4.138,0,0,0,1.485-2.221,4.216,4.216,0,0,0-.173-2.67,4.2,4.2,0,0,0-1.765-2.01,1.1,1.1,0,0,1-.4-1.5,1.1,1.1,0,0,1,1.063-.543,4.16,4.16,0,0,0,.1-.758,4.249,4.249,0,0,0-.346-1.838,4.156,4.156,0,0,0-1.117-1.5,4.078,4.078,0,0,0-.569-.408,4.928,4.928,0,0,1-.385.838,1.1,1.1,0,0,1-.955.553,1.08,1.08,0,0,1-.547-.15,1.093,1.093,0,0,1-.4-1.5,2.906,2.906,0,0,0,.374-1.229,1.022,1.022,0,0,1,.006-.145,2.841,2.841,0,0,0-.218-1.133,2.836,2.836,0,0,0-.754-1.051,2.794,2.794,0,0,0-1.139-.607,2.8,2.8,0,0,0-1.29-.045,2.782,2.782,0,0,0-1.173.535,2.84,2.84,0,0,0-.82.994,2.912,2.912,0,0,0-.3,1.262v5.254a4.426,4.426,0,0,0,.877,2.625,4.4,4.4,0,0,0,2.277,1.58,1.1,1.1,0,0,1,.748,1.361,1.1,1.1,0,0,1-1.367.748,6.6,6.6,0,0,1-2.535-1.41v6.928Zm-10.352,1.145a4.119,4.119,0,0,0,.787,1.49,4.148,4.148,0,0,0,1.313,1.061,4.105,4.105,0,0,0,1.624.447,4.076,4.076,0,0,0,1.676-.234,4.125,4.125,0,0,0,1.44-.877,4.208,4.208,0,0,0,.972-1.379,4.226,4.226,0,0,0,.34-1.652v-6.924a6.628,6.628,0,0,1-2.534,1.406,1.117,1.117,0,0,1-.308.045,1.1,1.1,0,0,1-1.055-.793,1.093,1.093,0,0,1,.748-1.361,4.393,4.393,0,0,0,2.272-1.58,4.354,4.354,0,0,0,.876-2.625v-5.254a2.906,2.906,0,0,0-.29-1.262,2.84,2.84,0,0,0-.82-.994,2.778,2.778,0,0,0-1.179-.535,2.8,2.8,0,0,0-1.29.045,2.832,2.832,0,0,0-1.139.607,2.821,2.821,0,0,0-.748,1.051,2.819,2.819,0,0,0-.212,1.271,2.821,2.821,0,0,0,.374,1.234,1.1,1.1,0,0,1-.4,1.5,1.1,1.1,0,0,1-1.5-.4,5.009,5.009,0,0,1-.386-.838,4.127,4.127,0,0,0-.568.408,4.156,4.156,0,0,0-1.117,1.5,4.249,4.249,0,0,0-.346,1.838,4.246,4.246,0,0,0,.1.758,1.106,1.106,0,0,1,1.064.543,1.1,1.1,0,0,1-.4,1.5,4.2,4.2,0,0,0-1.765,2.01,4.216,4.216,0,0,0-.173,2.67,4.138,4.138,0,0,0,1.485,2.221,4.178,4.178,0,0,0,2.529.859,1.1,1.1,0,0,1,1.1,1.1,1.1,1.1,0,0,1-1.1,1.1,6.32,6.32,0,0,1-1.422-.164C9200.714-22224.1,9200.732-22224.029,9200.752-22223.959Z" transform="translate(-9186.242 22256.754)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="53" height="53" viewBox="0 0 53 53"><defs><style>.a{fill:rgba(0,121,254,0.3);stroke:rgba(0,121,254,0.5);}.b{opacity:0;}.b,.c{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#92eeff"/><stop offset="1" stop-color="#00c7ff"/></linearGradient></defs><g transform="translate(0.5 -115.502)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(18559 4825)"><rect class="b" width="42" height="42" transform="translate(-18554 -4704)"/><path class="c" d="M-9736.837-26950.139a1.008,1.008,0,0,1-.542-.889v-3.371h-.752a4.506,4.506,0,0,1-4.5-4.5v-2.629c-1.89-.08-3.366-2-3.366-4.367s1.477-4.287,3.366-4.369v-2.633a4.5,4.5,0,0,1,4.5-4.5h15.658v.129a3.676,3.676,0,0,0,.494,1.869h-16.152a2.5,2.5,0,0,0-2.5,2.5v14a2.5,2.5,0,0,0,2.5,2.5h1.741a1.006,1.006,0,0,1,1.009,1v2.432l4.544-3.244a.97.97,0,0,1,.576-.189h13.124a2.5,2.5,0,0,0,2.506-2.5v-9.441a3.524,3.524,0,0,0,1.118.189h.033a3.634,3.634,0,0,0,2.764-1.293,4.793,4.793,0,0,1,1.456,3.549,5.014,5.014,0,0,1-.982,3.041,3.23,3.23,0,0,1-2.391,1.334v2.621a4.507,4.507,0,0,1-4.5,4.5h-12.806l-5.858,4.184a1.021,1.021,0,0,1-.582.191A1.007,1.007,0,0,1-9736.837-26950.139Zm1.334-10.389a1.876,1.876,0,0,1-1.876-1.877v-7.869a1.876,1.876,0,0,1,1.876-1.877h15.739a1.876,1.876,0,0,1,1.876,1.877v7.869a1.876,1.876,0,0,1-1.876,1.877Zm10.931-5.811a1.752,1.752,0,0,0,1.754,1.746,1.751,1.751,0,0,0,1.747-1.746,1.754,1.754,0,0,0-1.747-1.756A1.755,1.755,0,0,0-9724.572-26966.338Zm-9.617,0a1.752,1.752,0,0,0,1.748,1.746,1.751,1.751,0,0,0,1.747-1.746,1.755,1.755,0,0,0-1.747-1.756A1.755,1.755,0,0,0-9734.189-26966.338Zm20.1-5.277-.453-1.551a4.433,4.433,0,0,0-2.946-3.006l-1.537-.467a.649.649,0,0,1-.447-.611.524.524,0,0,1,.021-.148.616.616,0,0,1,.217-.346.624.624,0,0,1,.217-.115l.284-.088,1.232-.365a4.417,4.417,0,0,0,2.96-3.02l.46-1.551a.625.625,0,0,1,.42-.434v.006a.538.538,0,0,1,.183-.033.574.574,0,0,1,.292.08.581.581,0,0,1,.3.381l.46,1.557a4.422,4.422,0,0,0,2.96,3.014l1.171.346.346.107a.647.647,0,0,1,.454.609.641.641,0,0,1-.447.611l-1.537.467a4.434,4.434,0,0,0-2.946,3.006l-.453,1.551a.631.631,0,0,1-.6.461A.636.636,0,0,1-9714.089-26971.615Zm-4.571-7.34-.183-.623a2.7,2.7,0,0,0-1.829-1.83l-.623-.189a.29.29,0,0,1-.2-.277.284.284,0,0,1,.2-.277l.61-.184a2.7,2.7,0,0,0,1.842-1.842l.183-.615a.32.32,0,0,1,.2-.2.327.327,0,0,1,.224.027.275.275,0,0,1,.143.17l.183.615a2.713,2.713,0,0,0,1.842,1.842l.609.184a.289.289,0,0,1,.21.277.3.3,0,0,1-.21.277l-.257.076-.366.113a2.725,2.725,0,0,0-1.828,1.83l-.183.623a.293.293,0,0,1-.278.2A.3.3,0,0,1-9718.66-26978.955Z" transform="translate(-8805.37 22282.029)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="53" height="53" viewBox="0 0 53 53"><defs><style>.a{fill:rgba(0,121,254,0.3);stroke:rgba(0,121,254,0.5);}.b{fill:#fff;opacity:0;}.c{fill:url(#a);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#92eeff"/><stop offset="1" stop-color="#00c7ff"/></linearGradient></defs><g transform="translate(0.5 -115.502)"><rect class="a" width="52" height="52" rx="8" transform="translate(0 116.002)"/><g transform="translate(18558 4829)"><rect class="b" width="42" height="42" transform="translate(-18553 -4708)"/><path class="c" d="M-9520.252-26774.373h-17.5a8.261,8.261,0,0,1-8.248-8.254v-8.746a8.261,8.261,0,0,1,8.248-8.256h7.75v-2.582a2.5,2.5,0,0,1-1.5-2.287,2.506,2.506,0,0,1,2.5-2.5,2.5,2.5,0,0,1,2.5,2.5,2.515,2.515,0,0,1-1.5,2.293v2.576h2.752a1.254,1.254,0,0,1,1.251,1.252,1.254,1.254,0,0,1-1.251,1.252h-12.5a5.758,5.758,0,0,0-5.752,5.752v8.746a5.758,5.758,0,0,0,5.752,5.752h17.5a5.758,5.758,0,0,0,5.752-5.752v-5.746a1.252,1.252,0,0,1,1.251-1.25,1.252,1.252,0,0,1,1.252,1.25v5.746A8.264,8.264,0,0,1-9520.252-26774.373Zm-2.624-6.25h-12.251a5.506,5.506,0,0,1-5.5-5.5,5.5,5.5,0,0,1,5.5-5.5h12.251a5.507,5.507,0,0,1,5.5,5.5A5.509,5.509,0,0,1-9522.876-26780.623Zm-.989-7.812a.992.992,0,0,0-.706.291l-1.312,1.313a1,1,0,0,0-.289.707.985.985,0,0,0,.289.705l1.313,1.313a1,1,0,0,0,.706.3,1,1,0,0,0,.706-.3.988.988,0,0,0,.293-.707.987.987,0,0,0-.293-.705l-.605-.605.605-.605a.988.988,0,0,0,.293-.707.988.988,0,0,0-.293-.707A.988.988,0,0,0-9523.865-26788.436Zm-10.388.561a1.751,1.751,0,0,0-1.749,1.75,1.75,1.75,0,0,0,1.749,1.748,1.75,1.75,0,0,0,1.749-1.748A1.751,1.751,0,0,0-9534.253-26787.875Zm19.262-3.531a.587.587,0,0,1-.552-.4l-1.5-4.568-4.555-1.447a.593.593,0,0,1-.4-.551.577.577,0,0,1,.4-.553l4.5-1.514,1.419-4.5a.59.59,0,0,1,.557-.412.609.609,0,0,1,.17.027.569.569,0,0,1,.377.377l1.5,4.568,4.7,1.668a.579.579,0,0,1,.384.559.59.59,0,0,1-.417.545l-4.6,1.293-1.426,4.5A.572.572,0,0,1-9514.991-26791.406Z" transform="translate(-9002.999 22102.623)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="1080" viewBox="0 0 1920 1080"><defs><style>.a{fill:url(#a);}</style><pattern id="a" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" viewBox="0 0 3840 2160"><image width="3840" height="2160" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 4.3 MiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="582" height="667" viewBox="0 0 582 667"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><pattern id="a" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" viewBox="0 0 1164 1312"><image width="1164" height="1312" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 968 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="139" height="765" viewBox="0 0 139 765"><defs><style>.a{fill:url(#a);}</style><pattern id="a" width="1" height="1" patternTransform="matrix(-1, 0, 0, 1, 278, 0)" viewBox="0 0 139 765"><image preserveAspectRatio="xMidYMid slice" width="139" height="765" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARYAAAX6CAYAAAA2hEelAAAgAElEQVR4nOy9aZfsSnaet4FEDWe6t+/U3ZfiJImDJEvysklZ1rIsLemjf4F/pv1ZXl4iKZHiZFEkm4NEdjd5SXXfoe9wphoygfACEJEZGRkRCCAnIPE83XlPnazKrDpViV1v7OHd2f/5fytJJc9ElBJ5KEVeLUXuVyKVEskykWz3Odq7lKR/ggEc9cltAp/oZJ8fYELkfb5UcxHVgaTI21sdPepgo7jCAEBT9PpGqDa4XOUiz4pWwbxVIo9le79HtUj4bn7ZA1wqvQKLiQR1QLle6PuK9h3LSh+LtKIBgPnST7FYAaaOHVcLkac6kLx9FHmsvMqlb5hByQBMnGGBRR+J1spFJ3VlJbIsk5VLV8AhwABMlEGBxbBWLrnI0yutXNSWcskGKJbYpwOACbBfYDmMcnFxP3pdjCLAAEwDO7AMPpoElItylEtfzGO6AgqBBmBk7KVYDDvKRTbKZVVZPS4DI8zm0Vt/+gIKQQZgBBQ9ciCdH+dUi1SjXJbrPpcsP86VHwo0BBmAM3EQxWJwlUslIg+r9mikVBsA9IccIqPre4qMAANwfgpPcvRQNAqlbvu/UpJVOvDUfx7pUqd8DTASDqpYDM0VrM9FtXLJc1F1hais2lJ01cqWdSDIth+7bwDw5WJQMQAnxA4sB1MuJlm7yERuFqKu2wNKUyXKlvX/JVObj83EOsNUcpSBRid2tcLJ+jsAHJCjKBb7am4moLPWPCHT0aOoNgGk3CgYE2S8SsZEngGKxg2UVJUAjowvsLgX12FyL5lIXYm+KURdt59B1aXoZSlZPcBYVpI1/9sMG+18XqXvOYCise0ilPUnSgbgABxVsYislciaItMKRh+VGpOoUqTM2w+rVUv9xkp5cjHt85jws09CJlRREpQMwP6kBJbDK5hs88dVLmphRgEyqRoLBtWUqbNazSiRXG0rmfrOrBkf0F/fngrG/ve4Ssa+AUAiR1csLq6CqXteFiZg6FxMri/nRSaqPptUSrL6qFSZSCI60LTBxjeKpHbeSMN+qswKKplzZBJUDECYIYHloApmXRKy7mhyMXU1KW8+WVW2LnXrXIzKJK+DjfbazZyvYK1t1FrS7PMV7jTd2SqmijwOYLacXLG4rHtenKvX+OnWd5e6opSbXEzrsdsomFI1QabNzbSqJ7Pjla/T1/PpQpiHuUekylEyqBgAi0MEluNUkaxnykO5mEqyh5Wo+s9Sxx67GUcZReN5WvdI1vOrMoHGzcFUzt8BZsnZFUuIpFxM3qqGvM6/1MFFNeomr9qEbqbVkCleZybfq5TnCNYv0NjdvT614gYcAg3MimMEltBFdJxcjO6LUarti3msGgUjOtnb5GPcPImOMCH/qb5VJjvIuGMEyjk6AcyC0SoWl2AuxuqLafIylZSLNhdTB5a8Djh1gFkpHWh0L4wxoLIC1tBKko07pxTLxRBo4GI5ZWA5SS6mriQV2TppWzXNdpVk96umqlRPWTe9Mesj0uYrWysYM3pwgCvfzsWIR8FQVYKLZDKKJYSbi1m4uRhpkryNalhUbT+MyLqq1CqZqkn0mk2x67hygKlrt6pkAkuug0rGMQkukXMGlkMrmMzzVvN5minrovGFUbqqVNaKpe7svVtJ9qi0irHdM63uGKXPTge4+rNATobgAhfF5BWLiycX01y8dXApdC5FSxJVV5rqo1GerStJudXhu1Y2cvir3pSs7dwLgQUuhjEFlq0LS6neCiZYjXIrSrZfzG0h6mahczFKyoelZI9Vk/TNVKtb8qaCdBjFsvO16QBDcIGL4uIUiw+jYnxX7VXWeGjWfTFq2V7adV9MU0lqTkBtb0xWJ4BXusM323T42vQJCnYfnx1YykP/2wHOwZgDy6H6YeyLOIzVF3Ol+2K0CXi1bKtKuSqbJrxGxdgzSQO/LrHyLHYyF9UCk2cWiiWGVVVqAkOxSamuczGLtqZT52TysnW9a8YIqla9ZNnGmGqIijGBJUe1wKUwxcAytJqUplzcj8hELUSy20KqeuK6Tuw+lFK9afcl1X0xi3w7Gdv3azMfo1e9USGC6TN7xeJiVZUy++qulUyuXe7yrMm7NCNHy7IJNnWBqVY4ldWz4lMwIexcS45qgalzCYGlby4mXbkEHni1EPW8rSqp10qqej6pPiFl28plYQWXrs/l9raQa4FJg2JJxO7wzTPJrhftUECtXKS2bihbj966NSbLttTHokcQI9cCF8ElB5auXEyqctkx2TYDjFe5yPOrtrP3tWqXselRgErnShbO0Sjl8yz0JyHXApMFxTIAd0e1Vi4iq2adSa1clO51MQEmd447oYBh97SQa4HJMqfAMji34jzOVS6ilYtsKZf2WGQmmCvZzr/EPofdI0OuBSYJimUPdpSLtMol08pF2qhQZZtjUQpu0xyBBSYHgeUASkbLC3W1EHmuI8gbEXlcacWSNUeaPjucTNKXGSKYJDk/tv1Ral0RapTLzaJVMbIZUao86iPlSNR1dAIYJXNULH2rQaHH7agIsxnAsxrAlI/dvpbY12AUy6rj6wQYHSiWA6Is9aJ3HdnRx1UtXYoF1QKThcASZnBuY+3/slEuRrGUPZ8zs3phCC4wGQgsB2Sdec3aPIuda5FwriWGrVoAJsOcq0KhnEko/xH6uM3f26NPUx16pv1d3qya6lD7AVl7UrLUUNdxSKxcS1+1A3A2+E14QLYUS9FWiBobfrXe82GrFkkYUMydG8AkoI8lTCjJGqwKuR8g2fa6RbVRHosExSLOUWiBaoGpwG/BI1BplZKbvpZis+tIDcuzkGuBSYFi2Z9grqXQuZZ8O9eidCeuPUMUwu3EXdDmD1OA34BHwFz1C6s6ZCkWsYJK6orVzAou/Mxg9KBYuqtDIZJyLZFOXNtSISVYLBIUDsAo4LffEenoxC17VIfoxIVJgWLZn86ZooRO3D6euAsrKJFrgVFCYDkibq6lqkRW1dZqabtClKJC7CQudgowWggsG0K5leF+LfroE6gONZ24lvqIHUvtr6EOKoVS2FbCeCHHckRMNCisTtxcNjkXS7GkHonsyhA/OxgtKJYwfZVKdL2qNtfe/gC1lWtJWRNil53xxIXRwm+9E7DTibvwduIaun4mGfNDMHZGp1jU5f3+3erEfZ5pN/9l64mr2gVnpRNcQsols45MiwE2DAAngd94JyDWietUh1KDRK5/KdDTAqOEwLLLocq4O01vJs+ShT1xy47czk51iMACY4TAckKUzrWU+lZtOnErbZq9GmCnwM8QRsfZcywTyKmEdkD3dfHf6sTNtr1ajBu/yZt0/Vwyp1nOzdEAnBXKzWfA14lrbUzs64dr+lroxIXRcPLAcoFVH0PntLOyOnGd6lBLtpXEjSVm7epQodUO1SEYDZzPT0hPn5bU6hBJXBgdHIXCNBe2Cl+wXZ250U5cZ9rZ/hhTHbITs26QcatDKV27ACfjJIqFzebb2NUhx6dFDawOsdQMRsVRFYuS3Wgy4eCinNxGiK5cS6pPS+V8fOi57OBiHgtwVk5yFEKpbGMCbsCnxc6x9PFpKTCAgrFwlMBiLpwLfHWnqhab3VyLqQ7lIs8Kv0+LNZwYy5+4s0ML6xgFcDaoCp2BLcVSBKtDfZbIG8VS8DOFMUBVqJv2wla9E6OduZbMCDsrqKjtjYmlEyyoDsEkOOhvN6UrHAjxNMy3qkqbHepK4tqzQwQXOCsHUSxuN62vGgRhArNDvmnnWK7FVi05SVw4J3sFlp2Awsu4FzEXf2t2qO/8UP0zXTKUCOdkUGCZeUDp6k9JvX9THTKzQyLyuq4Obay1K1FJLv7m67Jb/FMTvwAHZ68cyzqnAsO+f/rPndkhf64lZb1qRnUIxgBVoXT67nQ2dIXeTHfgqi1nObX+r+3iH/v8GYEFxkKvFx8K5ThsOctVba7FMzskPapDzA7BWUlSLCRpD4J9kXu/g9lu5DCBpcsL132KnOoQnJNoYCGgnIatXEvRqhdnx7M9A9RndshUiPjJwUnxBhYCShL2d8WnRlxv3PDHdzjLZVkbaxxDp9AWRNtZbmEdowBORhELGgSU02ArloXuZ7nT4UBHCbs6tEjIjbmKBSsFOClJigU6CamRPmxVhyQ8O5TiXGcrFhK4cHKiigVOi9JB3d47pMPCUGc5e1siP2k4GfSx9Ce1n8Wl6+PXF35k71BfZzl7dojjEJwMmqhGRIeLvxqwBD6jpwXOAYFlfw7nFa4bEOvq0Ivr9la/bVlRVD37WuwkLoEFTgaBZUR0KBbx+LR0QYs/nAVebIcl9WL3NNlu3t+kV3SOJbJ3qPI8l/uc9THoSt/4WcPJIHk7QiLVocqZHQpZVor1/kVi7wvAwSCwDCeU43Av8sHT0B3VoVRnuYXHWQ7gqBBYRkjMWc6z3zl1dsgEFlyJ4eggj89HOM8SqQ7phsbSMn+KBQnzPqpDcFIILCOkZ3Uo5WjD3iE4KRyFDsc+80LeCeid2SG1NTtkAktKsMit6tDDqb4hMF8ILCNmqzpUBatDygosIfWCYoGTQmDZH191KJZUjW1I9LrM9XCWC31OE0zsiWcSuHA0CCwjxsLine truncated

After

Width:  |  Height:  |  Size: 53 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="139" height="765" viewBox="0 0 139 765"><defs><style>.a{fill:url(#a);}</style><pattern id="a" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" viewBox="0 0 278 1530"><image width="278" height="1530" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 53 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#fff"/><stop offset="1" stop-color="#a6cfff"/></linearGradient></defs><g transform="translate(14482.893 -23044.904)"><rect class="a" width="68" height="68" transform="translate(-14482.893 23044.904)"/><path class="b" d="M110.023,105.37H98.2a1.721,1.721,0,0,1-1.716-1.716v-1.918h10.189a1.721,1.721,0,0,0,1.716-1.716V72.8a1.721,1.721,0,0,0-1.716-1.716H98.2V67.437A3.444,3.444,0,0,0,94.76,64H87.83a3.444,3.444,0,0,0-3.437,3.437v3.652H75.625A1.721,1.721,0,0,0,73.91,72.8v27.21a1.721,1.721,0,0,0,1.716,1.716h10.49v1.918a1.721,1.721,0,0,1-1.716,1.716H72.3a1.721,1.721,0,0,0-1.716,1.716v10.1A1.721,1.721,0,0,0,72.3,118.9h37.724a1.721,1.721,0,0,0,1.716-1.716v-10.1A1.726,1.726,0,0,0,110.023,105.37Zm-8.406-19.1a3.437,3.437,0,1,1-3.437-3.437A3.436,3.436,0,0,1,101.616,86.266ZM87.83,67.437h6.924v3.646H87.83ZM81.121,86.266A3.437,3.437,0,1,1,84.558,89.7,3.436,3.436,0,0,1,81.121,86.266Zm32.48-7.089V93.355a1.716,1.716,0,0,1-3.431,0V79.177a1.716,1.716,0,0,1,3.431,0Zm5.122,3.548v6.881a1.716,1.716,0,0,1-3.431,0V82.724a1.716,1.716,0,0,1,3.431,0ZM70.644,95.07a1.717,1.717,0,0,1-1.716-1.716V79.177a1.716,1.716,0,0,1,3.431,0V93.355A1.713,1.713,0,0,1,70.644,95.07ZM67.231,82.724v6.881a1.716,1.716,0,1,1-3.431,0V82.724a1.716,1.716,0,0,1,3.431,0Zm0,0" transform="translate(-14540.154 22987.455)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,47 @@
<svg xmlns="http://www.w3.org/2000/svg" width="188" height="241" viewBox="0 0 188 241" fill="none">
<defs>
<radialGradient id="surface" cx="0" cy="0" r="1" gradientTransform="translate(60 30) rotate(60) scale(150 158)" gradientUnits="userSpaceOnUse">
<stop stop-color="#46B6FF"/>
<stop offset=".24" stop-color="#1A83DE"/>
<stop offset=".6" stop-color="#075FBC"/>
<stop offset="1" stop-color="#003675"/>
</radialGradient>
<radialGradient id="inner-light" cx="0" cy="0" r="1" gradientTransform="translate(94 82) rotate(90) scale(79 84)" gradientUnits="userSpaceOnUse">
<stop stop-color="#0876DC" stop-opacity=".42"/>
<stop offset="1" stop-color="#001F55" stop-opacity="0"/>
</radialGradient>
<linearGradient id="lower-depth" x1="94" y1="118" x2="94" y2="165" gradientUnits="userSpaceOnUse">
<stop stop-color="#005DBB" stop-opacity="0"/>
<stop offset="1" stop-color="#002A68" stop-opacity=".58"/>
</linearGradient>
<linearGradient id="edge" x1="36" y1="12" x2="152" y2="160" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFFFFF" stop-opacity=".98"/>
<stop offset=".48" stop-color="#B6EAFF" stop-opacity=".78"/>
<stop offset="1" stop-color="#48BBFF" stop-opacity=".72"/>
</linearGradient>
<filter id="glow" x="-12" y="-12" width="212" height="212" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="4.5"/>
</filter>
<filter id="shadow" x="22" y="163" width="144" height="48" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="6.5"/>
</filter>
<clipPath id="clip">
<circle cx="94" cy="82" r="81"/>
</clipPath>
</defs>
<ellipse cx="94" cy="188" rx="61" ry="9" fill="#00112F" fill-opacity=".58" filter="url(#shadow)"/>
<circle cx="94" cy="82" r="82" fill="#139DFF" fill-opacity=".22" filter="url(#glow)"/>
<circle cx="94" cy="82" r="81" fill="url(#surface)"/>
<g clip-path="url(#clip)">
<circle cx="94" cy="82" r="79" fill="url(#inner-light)"/>
<ellipse cx="94" cy="142" rx="89" ry="38" fill="url(#lower-depth)"/>
<ellipse cx="94" cy="150" rx="78" ry="15" fill="#55B9FF" fill-opacity=".13"/>
<path d="M19 145C46 157 142 157 169 145" stroke="#BCEAFF" stroke-opacity=".24" stroke-width="1.2"/>
<path d="M30 160C54 169 134 169 158 160" stroke="#1599F7" stroke-opacity=".28" stroke-width="1.2"/>
<path d="M24 62C29 36 49 17 76 11" stroke="#D8F4FF" stroke-opacity=".3" stroke-width="9" stroke-linecap="round" filter="url(#glow)"/>
</g>
<circle cx="94" cy="82" r="81" stroke="url(#edge)" stroke-width="2"/>
<circle cx="94" cy="82" r="76" stroke="#27B2FF" stroke-opacity=".28"/>
<path d="M19 69C24 38 49 12 82 4" stroke="#FFFFFF" stroke-opacity=".88" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;stroke:#707070;stroke-width:3.75px;opacity:0;}.b{fill:url(#a);}.c{stroke:none;}.d{fill:none;}</style><linearGradient id="a" x1="0.431" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#fff"/><stop offset="1" stop-color="#a6cfff"/></linearGradient></defs><g transform="translate(-71.077 -67.746)"><g class="a" transform="translate(71.077 67.746)"><rect class="c" width="68" height="68"/><rect class="d" x="1.875" y="1.875" width="64.25" height="64.25"/></g><path class="b" d="M114.406,106.791a7.707,7.707,0,0,0-4.354,1.353L98.7,99.937a13.3,13.3,0,0,0,1.742-6.583c0-.421-.03-.842-.06-1.263L112,85.207a4.687,4.687,0,1,0-1.952-2.345L99.691,88.995a13.678,13.678,0,0,0-6.1-7.365l2.072-10.341c.15,0,.3.03.451.03a4.66,4.66,0,1,0-4.655-4.659,4.727,4.727,0,0,0,1.412,3.367l-2.1,10.462A13.479,13.479,0,0,0,80.05,81.87l-6.4-5.712A6.129,6.129,0,1,0,71.7,78.443L77.677,83.7a13.456,13.456,0,0,0,.691,19.991l-5.316,5.622a5.948,5.948,0,0,0-3.394-1.022,6.163,6.163,0,1,0,6.156,6.162,6.343,6.343,0,0,0-.721-2.916l5.826-6.193a13.464,13.464,0,0,0,16.1-2.946l11.022,7.966a7.77,7.77,0,0,0-1.2,4.118,7.658,7.658,0,1,0,15.316,0A7.817,7.817,0,0,0,114.406,106.791Zm0,0" transform="translate(13 9.671)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.337" x2="1" y2="1.007" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#fff"/><stop offset="1" stop-color="#a6cfff"/></linearGradient></defs><g transform="translate(-204.426 -230.797)"><rect class="a" width="68" height="68" transform="translate(204.426 230.797)"/><g transform="translate(209.658 232.191)"><path class="b" d="M25.52,64.045c-1.725-.852-8.6-4.486-10.846-5.656-2.309-1.207-3.3-1.01-5.09.146L6.346,60.67,0,46.383s6.887-3.254,10.033-4.508C15,39.885,17.273,41.6,20.115,43.4c1.209.762,5.131,3.664,5.131,3.664a17.886,17.886,0,0,0,7.783,2.316c5.928,0,6.975.344,6.975,2.266,0,2.113-3.859,2.266-3.883,2.266H26.605c-3.236,0-7.766-1.893-7.766-1.893s3.846,4.156,7.766,4.156h9.547c2.105-.047,6.1-1.047,6.1-4.537,0-4.529-4.066-4.529-9.219-4.529a5.2,5.2,0,0,1-1.127-.145c.2-.1.4-.207.639-.35.9-.523,2.145-1.27,3.535-2.158,3.67-2.3,5.555-2.592,7.387-.152a5.718,5.718,0,0,1,4.248-1.678c1.551,0,2.865,1.039,2.494,2.719,3.975-2.377,5.936-1.42,6.809-.266,1.156,1.527.373,3.109-2.143,4.926C53.244,51.17,37.809,61.543,35.635,62.82c-1.732,1-3.326,2.395-5.8,2.395A9.825,9.825,0,0,1,25.52,64.045ZM22.51,39.5l-.229-.865a.706.706,0,0,1,.129-.607.688.688,0,0,1,.557-.275H35.734a.689.689,0,0,1,.547.275.7.7,0,0,1,.121.607l-.219.865a7.065,7.065,0,0,1-13.674,0Zm6.84,2.486a4.265,4.265,0,0,0,3.178-1.424H26.174A4.239,4.239,0,0,0,29.35,41.982Zm-6.285-5.07a.885.885,0,1,1,0-1.77H35.658a.885.885,0,0,1-.008,1.77Zm-.189-2.637a.7.7,0,0,1-.707-.7V31.021a.565.565,0,0,0-.244-.461,13.168,13.168,0,1,1,14.844,0,.563.563,0,0,0-.242.461v2.547a.7.7,0,0,1-.7.707Zm8.672-8.785a6.207,6.207,0,0,0,2.455-.3c.547-.258.721-.676.721-1.572V14.719H33.059v8.887c0,.219-.1.281-.334.3-.234,0-1.033,0-1.732-.039A6.217,6.217,0,0,1,31.547,25.49Zm-6.18-2.166c0,1.535.463,1.967,1.969,1.967h1.7c1.285,0,1.717-.516,1.877-2.174a4.544,4.544,0,0,1-1.443-.607c-.062,1.141-.145,1.361-.586,1.361H27.557c-.494,0-.578-.055-.578-.562V20.117h1.475a3.131,3.131,0,0,1-.213,1.223.374.374,0,0,1-.334.131,6.154,6.154,0,0,1-.836-.047,3.419,3.419,0,0,1,.41,1.4,8.144,8.144,0,0,0,1.3-.045,1.284,1.284,0,0,0,.844-.455c.281-.35.395-1.223.465-3.107l.016-.2.744-.707v4.154h1.619V15.793H30.848V18.17a28.18,28.18,0,0,0-2.326-2.758c.084-.158.152-.328.221-.486l-1.535-.479a9.09,9.09,0,0,1-3.215,3.9,7.919,7.919,0,0,1,1.2,1.27l.174-.129Zm.906-4.643a11.211,11.211,0,0,0,1.465-1.855c.518.609,1.049,1.27,1.469,1.855Zm17.676-2.123a1.606,1.606,0,0,1,1.186-1.943l2.363-.578a1.608,1.608,0,0,1,.76,3.125l-2.369.576a1.629,1.629,0,0,1-.383.047A1.611,1.611,0,0,1,43.949,16.559ZM12.631,17.631,10.389,16.7a1.608,1.608,0,0,1,1.23-2.971l2.242.941a1.6,1.6,0,1,1-1.23,2.963Zm4.088-9.514L15.328,6.125a1.605,1.605,0,0,1,2.637-1.832l1.391,1.99a1.6,1.6,0,0,1-.4,2.234,1.58,1.58,0,0,1-.916.289A1.6,1.6,0,0,1,16.719,8.117Zm22.709.32a1.614,1.614,0,0,1-.273-2.252l1.488-1.914A1.6,1.6,0,1,1,43.174,6.24L41.686,8.16a1.626,1.626,0,0,1-2.258.277ZM27.746,4.043V1.6a1.607,1.607,0,0,1,3.215,0V4.043a1.607,1.607,0,0,1-3.215,0Z" transform="translate(0 0)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#fff"/><stop offset="1" stop-color="#a6cfff"/></linearGradient></defs><g transform="translate(14482.893 -23044.904)"><rect class="a" width="68" height="68" transform="translate(-14482.893 23044.904)"/><path class="b" d="M43.013,57.336h-.255l-.074,0a14.064,14.064,0,0,1-8.911-3.467,6.216,6.216,0,0,1,6.1-5.029h6.278a6.218,6.218,0,0,1,6.1,5.03A14.073,14.073,0,0,1,43.6,57.324h-.009l-.067,0H43.5l-.063,0h-.424ZM30.159,54.9H8.5a1.717,1.717,0,0,1-1.715-1.716v-10.1A1.717,1.717,0,0,1,8.5,41.363H20.6a1.717,1.717,0,0,0,1.716-1.715V37.73H11.824a1.717,1.717,0,0,1-1.716-1.715V8.8a1.718,1.718,0,0,1,1.716-1.716h8.768V3.437A3.44,3.44,0,0,1,24.029,0h6.93A3.441,3.441,0,0,1,34.4,3.437V7.089H42.87A1.718,1.718,0,0,1,44.586,8.8V26.022c-.515-.046-1.044-.07-1.574-.07A17.327,17.327,0,0,0,30.157,54.9Zm4.219-36.07a3.437,3.437,0,1,0,3.438,3.437A3.441,3.441,0,0,0,34.378,18.828Zm-13.619,0A3.437,3.437,0,1,0,24.2,22.265,3.441,3.441,0,0,0,20.758,18.828ZM24.029,3.437V7.083h6.924V3.437Zm7.256,47.59v0a14.059,14.059,0,1,1,23.456,0,9.508,9.508,0,0,0-8.589-5.455H44.686a7.193,7.193,0,1,0-3.347,0H39.873a9.5,9.5,0,0,0-8.587,5.457ZM43.013,42.5a3.923,3.923,0,1,1,3.923-3.924A3.928,3.928,0,0,1,43.013,42.5ZM6.844,31.07a1.717,1.717,0,0,1-1.716-1.715V15.176a1.715,1.715,0,1,1,3.431,0V29.355A1.717,1.717,0,0,1,6.844,31.07ZM49.8,27.327h0a17.187,17.187,0,0,0-3.429-1.051v-11.1a1.715,1.715,0,1,1,3.431,0v12.15Zm3.406-.007a1.717,1.717,0,0,1-1.715-1.715V18.724a1.715,1.715,0,0,1,3.431,0v6.881A1.717,1.717,0,0,1,53.206,27.32Zm-51.491,0A1.717,1.717,0,0,1,0,25.605V18.724a1.715,1.715,0,0,1,3.431,0v6.881A1.717,1.717,0,0,1,1.715,27.32Z" transform="translate(-14477.664 23050.15)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;stroke:#707070;stroke-width:3.75px;opacity:0;}.b{fill:url(#a);}.c{stroke:none;}.d{fill:none;}</style><linearGradient id="a" x1="0.207" x2="0.793" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#fff"/><stop offset="1" stop-color="#a6cfff"/></linearGradient></defs><g transform="translate(-2.29 3.525)"><g class="a" transform="translate(2.29 -3.525)"><rect class="c" width="68" height="68"/><rect class="d" x="1.875" y="1.875" width="64.25" height="64.25"/></g><path class="b" d="M36.066,58.469a15.431,15.431,0,0,1,0-21.866,15.011,15.011,0,0,1,3.577-2.662,2.018,2.018,0,0,1,1.878,3.571,11.53,11.53,0,0,0-2.6,1.947A11.446,11.446,0,0,0,55.132,55.622a11.938,11.938,0,0,0,1.878-2.462A2.02,2.02,0,1,1,60.5,55.2,15.711,15.711,0,0,1,58,58.469a15.533,15.533,0,0,1-21.929,0Zm-5.87-.994H5.225A5.225,5.225,0,0,1,0,52.263V13.925A5.226,5.226,0,0,1,5.225,8.712H53.769A5.226,5.226,0,0,1,59,13.925V32.117A19.526,19.526,0,0,0,30.2,57.474v0ZM21.753,18.77l-8.5,7.4a2.531,2.531,0,0,0-.239,3.565,2.536,2.536,0,0,0,3.568.239l6.835-5.958L28.446,28.4a2.52,2.52,0,0,0,3.351-.015L42.282,19a2.508,2.508,0,0,0,.191-3.557,2.529,2.529,0,0,0-3.567-.192L30.085,23.14l-5-4.369a2.544,2.544,0,0,0-3.335,0ZM47.03,49.554a2.017,2.017,0,0,1-2.022-2.017V34.091a2.021,2.021,0,0,1,2.022-2.017A15.485,15.485,0,0,1,62.537,47.537a2.018,2.018,0,0,1-2.024,2.017Zm2.025-4.034h9.258a11.471,11.471,0,0,0-9.258-9.229ZM7.53,5.051a2.521,2.521,0,1,1,0-5.043H50.681a2.521,2.521,0,1,1,0,5.043Z" transform="translate(6.296 0.013)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="139" height="765" viewBox="0 0 139 765"><defs><style>.a{fill:url(#a);}</style><pattern id="a" width="1" height="1" patternTransform="matrix(-1, 0, 0, 1, 278, 0)" viewBox="0 0 139 765"><image preserveAspectRatio="xMidYMid slice" width="139" height="765" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 51 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="139" height="765" viewBox="0 0 139 765"><defs><style>.a{fill:url(#a);}</style><pattern id="a" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" viewBox="0 0 278 1530"><image width="278" height="1530" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 51 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="54.923" height="54.898" viewBox="0 0 54.923 54.898"><defs><style>.a{fill:url(#a);}</style><linearGradient id="a" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f3212c"/><stop offset="1" stop-color="#aa071a"/></linearGradient></defs><path class="a" d="M110.023,105.37H98.2a1.721,1.721,0,0,1-1.716-1.716v-1.918h10.189a1.721,1.721,0,0,0,1.716-1.716V72.8a1.721,1.721,0,0,0-1.716-1.716H98.2V67.437A3.444,3.444,0,0,0,94.76,64H87.83a3.444,3.444,0,0,0-3.437,3.437v3.652H75.625A1.721,1.721,0,0,0,73.91,72.8v27.21a1.721,1.721,0,0,0,1.716,1.716h10.49v1.918a1.721,1.721,0,0,1-1.716,1.716H72.3a1.721,1.721,0,0,0-1.716,1.716v10.1A1.721,1.721,0,0,0,72.3,118.9h37.724a1.721,1.721,0,0,0,1.716-1.716v-10.1A1.726,1.726,0,0,0,110.023,105.37Zm-8.406-19.1a3.437,3.437,0,1,1-3.437-3.437A3.436,3.436,0,0,1,101.616,86.266ZM87.83,67.437h6.924v3.646H87.83ZM81.121,86.266A3.437,3.437,0,1,1,84.558,89.7,3.436,3.436,0,0,1,81.121,86.266Zm32.48-7.089V93.355a1.716,1.716,0,0,1-3.431,0V79.177a1.716,1.716,0,0,1,3.431,0Zm5.122,3.548v6.881a1.716,1.716,0,0,1-3.431,0V82.724a1.716,1.716,0,0,1,3.431,0ZM70.644,95.07a1.717,1.717,0,0,1-1.716-1.716V79.177a1.716,1.716,0,0,1,3.431,0V93.355A1.713,1.713,0,0,1,70.644,95.07ZM67.231,82.724v6.881a1.716,1.716,0,1,1-3.431,0V82.724a1.716,1.716,0,0,1,3.431,0Zm0,0" transform="translate(-63.8 -64)"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;stroke:#707070;stroke-width:3.75px;opacity:0;}.b{fill:url(#a);}.c{stroke:none;}.d{fill:none;}</style><linearGradient id="a" x1="0.431" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f3212c"/><stop offset="1" stop-color="#aa071a"/></linearGradient></defs><g transform="translate(-71.077 -67.746)"><g class="a" transform="translate(71.077 67.746)"><rect class="c" width="68" height="68"/><rect class="d" x="1.875" y="1.875" width="64.25" height="64.25"/></g><path class="b" d="M114.406,106.791a7.707,7.707,0,0,0-4.354,1.353L98.7,99.937a13.3,13.3,0,0,0,1.742-6.583c0-.421-.03-.842-.06-1.263L112,85.207a4.687,4.687,0,1,0-1.952-2.345L99.691,88.995a13.678,13.678,0,0,0-6.1-7.365l2.072-10.341c.15,0,.3.03.451.03a4.66,4.66,0,1,0-4.655-4.659,4.727,4.727,0,0,0,1.412,3.367l-2.1,10.462A13.479,13.479,0,0,0,80.05,81.87l-6.4-5.712A6.129,6.129,0,1,0,71.7,78.443L77.677,83.7a13.456,13.456,0,0,0,.691,19.991l-5.316,5.622a5.948,5.948,0,0,0-3.394-1.022,6.163,6.163,0,1,0,6.156,6.162,6.343,6.343,0,0,0-.721-2.916l5.826-6.193a13.464,13.464,0,0,0,16.1-2.946l11.022,7.966a7.77,7.77,0,0,0-1.2,4.118,7.658,7.658,0,1,0,15.316,0A7.817,7.817,0,0,0,114.406,106.791Zm0,0" transform="translate(13 9.671)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{opacity:0;fill:url(#a);}.b{fill:url(#b);}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f3212c"/><stop offset="1" stop-color="#aa071a"/></linearGradient><linearGradient id="b" x1="0.337" x2="1" xlink:href="#a"/></defs><g transform="translate(-204.426 -230.797)"><rect class="a" width="68" height="68" transform="translate(204.426 230.797)"/><g transform="translate(209.658 232.191)"><path class="b" d="M25.52,64.045c-1.725-.852-8.6-4.486-10.846-5.656-2.309-1.207-3.3-1.01-5.09.146L6.346,60.67,0,46.383s6.887-3.254,10.033-4.508C15,39.885,17.273,41.6,20.115,43.4c1.209.762,5.131,3.664,5.131,3.664a17.886,17.886,0,0,0,7.783,2.316c5.928,0,6.975.344,6.975,2.266,0,2.113-3.859,2.266-3.883,2.266H26.605c-3.236,0-7.766-1.893-7.766-1.893s3.846,4.156,7.766,4.156h9.547c2.105-.047,6.1-1.047,6.1-4.537,0-4.529-4.066-4.529-9.219-4.529a5.2,5.2,0,0,1-1.127-.145c.2-.1.4-.207.639-.35.9-.523,2.145-1.27,3.535-2.158,3.67-2.3,5.555-2.592,7.387-.152a5.718,5.718,0,0,1,4.248-1.678c1.551,0,2.865,1.039,2.494,2.719,3.975-2.377,5.936-1.42,6.809-.266,1.156,1.527.373,3.109-2.143,4.926C53.244,51.17,37.809,61.543,35.635,62.82c-1.732,1-3.326,2.395-5.8,2.395A9.825,9.825,0,0,1,25.52,64.045ZM22.51,39.5l-.229-.865a.706.706,0,0,1,.129-.607.688.688,0,0,1,.557-.275H35.734a.689.689,0,0,1,.547.275.7.7,0,0,1,.121.607l-.219.865a7.065,7.065,0,0,1-13.674,0Zm6.84,2.486a4.265,4.265,0,0,0,3.178-1.424H26.174A4.239,4.239,0,0,0,29.35,41.982Zm-6.285-5.07a.885.885,0,1,1,0-1.77H35.658a.885.885,0,0,1-.008,1.77Zm-.189-2.637a.7.7,0,0,1-.707-.7V31.021a.565.565,0,0,0-.244-.461,13.168,13.168,0,1,1,14.844,0,.563.563,0,0,0-.242.461v2.547a.7.7,0,0,1-.7.707Zm8.672-8.785a6.207,6.207,0,0,0,2.455-.3c.547-.258.721-.676.721-1.572V14.719H33.059v8.887c0,.219-.1.281-.334.3-.234,0-1.033,0-1.732-.039A6.217,6.217,0,0,1,31.547,25.49Zm-6.18-2.166c0,1.535.463,1.967,1.969,1.967h1.7c1.285,0,1.717-.516,1.877-2.174a4.544,4.544,0,0,1-1.443-.607c-.062,1.141-.145,1.361-.586,1.361H27.557c-.494,0-.578-.055-.578-.562V20.117h1.475a3.131,3.131,0,0,1-.213,1.223.374.374,0,0,1-.334.131,6.154,6.154,0,0,1-.836-.047,3.419,3.419,0,0,1,.41,1.4,8.144,8.144,0,0,0,1.3-.045,1.284,1.284,0,0,0,.844-.455c.281-.35.395-1.223.465-3.107l.016-.2.744-.707v4.154h1.619V15.793H30.848V18.17a28.18,28.18,0,0,0-2.326-2.758c.084-.158.152-.328.221-.486l-1.535-.479a9.09,9.09,0,0,1-3.215,3.9,7.919,7.919,0,0,1,1.2,1.27l.174-.129Zm.906-4.643a11.211,11.211,0,0,0,1.465-1.855c.518.609,1.049,1.27,1.469,1.855Zm17.676-2.123a1.606,1.606,0,0,1,1.186-1.943l2.363-.578a1.608,1.608,0,0,1,.76,3.125l-2.369.576a1.629,1.629,0,0,1-.383.047A1.611,1.611,0,0,1,43.949,16.559ZM12.631,17.631,10.389,16.7a1.608,1.608,0,0,1,1.23-2.971l2.242.941a1.6,1.6,0,1,1-1.23,2.963Zm4.088-9.514L15.328,6.125a1.605,1.605,0,0,1,2.637-1.832l1.391,1.99a1.6,1.6,0,0,1-.4,2.234,1.58,1.58,0,0,1-.916.289A1.6,1.6,0,0,1,16.719,8.117Zm22.709.32a1.614,1.614,0,0,1-.273-2.252l1.488-1.914A1.6,1.6,0,1,1,43.174,6.24L41.686,8.16a1.626,1.626,0,0,1-2.258.277ZM27.746,4.043V1.6a1.607,1.607,0,0,1,3.215,0V4.043a1.607,1.607,0,0,1-3.215,0Z" transform="translate(0 0)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{fill:#fff;opacity:0;}.b{fill:url(#a);}</style><linearGradient id="a" x1="0.405" x2="1" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f3212c"/><stop offset="1" stop-color="#aa071a"/></linearGradient></defs><g transform="translate(14482.893 -23044.904)"><rect class="a" width="68" height="68" transform="translate(-14482.893 23044.904)"/><path class="b" d="M43.013,57.336h-.255l-.074,0a14.064,14.064,0,0,1-8.911-3.467,6.216,6.216,0,0,1,6.1-5.029h6.278a6.218,6.218,0,0,1,6.1,5.03A14.073,14.073,0,0,1,43.6,57.324h-.009l-.067,0H43.5l-.063,0h-.424ZM30.159,54.9H8.5a1.717,1.717,0,0,1-1.715-1.716v-10.1A1.717,1.717,0,0,1,8.5,41.363H20.6a1.717,1.717,0,0,0,1.716-1.715V37.73H11.824a1.717,1.717,0,0,1-1.716-1.715V8.8a1.718,1.718,0,0,1,1.716-1.716h8.768V3.437A3.44,3.44,0,0,1,24.029,0h6.93A3.441,3.441,0,0,1,34.4,3.437V7.089H42.87A1.718,1.718,0,0,1,44.586,8.8V26.022c-.515-.046-1.044-.07-1.574-.07A17.327,17.327,0,0,0,30.157,54.9Zm4.219-36.07a3.437,3.437,0,1,0,3.438,3.437A3.441,3.441,0,0,0,34.378,18.828Zm-13.619,0A3.437,3.437,0,1,0,24.2,22.265,3.441,3.441,0,0,0,20.758,18.828ZM24.029,3.437V7.083h6.924V3.437Zm7.256,47.59v0a14.059,14.059,0,1,1,23.456,0,9.508,9.508,0,0,0-8.589-5.455H44.686a7.193,7.193,0,1,0-3.347,0H39.873a9.5,9.5,0,0,0-8.587,5.457ZM43.013,42.5a3.923,3.923,0,1,1,3.923-3.924A3.928,3.928,0,0,1,43.013,42.5ZM6.844,31.07a1.717,1.717,0,0,1-1.716-1.715V15.176a1.715,1.715,0,1,1,3.431,0V29.355A1.717,1.717,0,0,1,6.844,31.07ZM49.8,27.327h0a17.187,17.187,0,0,0-3.429-1.051v-11.1a1.715,1.715,0,1,1,3.431,0v12.15Zm3.406-.007a1.717,1.717,0,0,1-1.715-1.715V18.724a1.715,1.715,0,0,1,3.431,0v6.881A1.717,1.717,0,0,1,53.206,27.32Zm-51.491,0A1.717,1.717,0,0,1,0,25.605V18.724a1.715,1.715,0,0,1,3.431,0v6.881A1.717,1.717,0,0,1,1.715,27.32Z" transform="translate(-14477.664 23050.15)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="68" viewBox="0 0 68 68"><defs><style>.a{stroke:#707070;stroke-width:3.75px;opacity:0;fill:url(#a);}.b{fill:url(#b);}.c{stroke:none;}.d{fill:none;}</style><linearGradient id="a" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#f3212c"/><stop offset="1" stop-color="#aa071a"/></linearGradient><linearGradient id="b" x1="0.414" x2="1" xlink:href="#a"/></defs><g transform="translate(-2.29 3.525)"><g class="a" transform="translate(2.29 -3.525)"><rect class="c" width="68" height="68"/><rect class="d" x="1.875" y="1.875" width="64.25" height="64.25"/></g><path class="b" d="M36.066,58.469a15.431,15.431,0,0,1,0-21.866,15.011,15.011,0,0,1,3.577-2.662,2.018,2.018,0,0,1,1.878,3.571,11.53,11.53,0,0,0-2.6,1.947A11.446,11.446,0,0,0,55.132,55.622a11.938,11.938,0,0,0,1.878-2.462A2.02,2.02,0,1,1,60.5,55.2,15.711,15.711,0,0,1,58,58.469a15.533,15.533,0,0,1-21.929,0Zm-5.87-.994H5.225A5.225,5.225,0,0,1,0,52.263V13.925A5.226,5.226,0,0,1,5.225,8.712H53.769A5.226,5.226,0,0,1,59,13.925V32.117A19.526,19.526,0,0,0,30.2,57.474v0ZM21.753,18.77l-8.5,7.4a2.531,2.531,0,0,0-.239,3.565,2.536,2.536,0,0,0,3.568.239l6.835-5.958L28.446,28.4a2.52,2.52,0,0,0,3.351-.015L42.282,19a2.508,2.508,0,0,0,.191-3.557,2.529,2.529,0,0,0-3.567-.192L30.085,23.14l-5-4.369a2.544,2.544,0,0,0-3.335,0ZM47.03,49.554a2.017,2.017,0,0,1-2.022-2.017V34.091a2.021,2.021,0,0,1,2.022-2.017A15.485,15.485,0,0,1,62.537,47.537a2.018,2.018,0,0,1-2.024,2.017Zm2.025-4.034h9.258a11.471,11.471,0,0,0-9.258-9.229ZM7.53,5.051a2.521,2.521,0,1,1,0-5.043H50.681a2.521,2.521,0,1,1,0,5.043Z" transform="translate(6.296 0.013)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="273" height="323" viewBox="0 0 273 323"><image width="273" height="323" xlink:href="data:image/png;base64,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 truncated

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Loaded 100 of 1758 files, more files were not shown because too many files have changed in this diff. Show more