Server 项目管理
项目详情页围绕已上传版本工作。当前 server web 不显示 draft 目标;目标选项来自 latest 和在线版本列表,已下线版本仍保留在版本详情里用于审计和重新上线。 默认管理端入口已切到server/web。项目详情、版本、KV、环境变量、Embed 和 Webhook 使用 shadcn/ui source components、Radix primitives 和 Tailwind CSS v4 tokens 组成的极简管理台:左侧主导航可折叠,项目详情使用固定左栏二级导航;右侧页面顶部展示项目状态、标题和刷新入口,避免出现没有实际行为的装饰按钮。当前基础控件以本地 shadcn Button、Input、Select、Switch、Badge、Sheet、Table、Skeleton 和 PaginationFooter 为主,搜索框、状态摘要、运行队列列表、版本摘要、公开入口设置、状态下拉、空状态、骨架屏和日志抽屉都使用同一套 shadcn/Radix 控件语法,不再由页面各自手写同类结构。列表、运行记录、版本和 KV 表格共享 Table 的标准密度、表头、hover、行高、20 条分页和中文页脚配置。项目切换、tab 切换、筛选、刷新或分页加载时,表格会进入骨架行过渡,并清空或隔离上一项目/上一筛选的旧行数据;旧请求晚到时不会覆盖当前项目页面,避免切换项目时短暂闪现上一张表格。
项目详情
“项目详情”展示项目标识、名称、当前版本、最近发布、运行入口和最近执行统计。基础信息卡会明确说明这是服务器已发布包的最小运行画像。详情页先通过分页接口读取最近一页已发布版本执行记录并立即展示;如果仍有更多记录,会在后台继续补全最近 250 条用于成功率、P95 延迟、平均执行时间和最近 7 天执行趋势,避免大项目因为历史 run 过多而阻塞项目详情。最近执行记录在前端按 20 条一页展示,默认显示第一页;超过当前补全上限的更早记录可到项目菜单下的“执行日志”继续筛选查看。最近执行表格提供“执行回放”和“查看日志”两个 icon-only 入口:执行回放会跳转到执行日志 tab 并打开同一个 shared 只读回放抽屉,不在概览页复制回放 UI。最近执行、执行日志、Webhook 投递日志和 KV Last writer 表格列都会先渲染完整 Run ID,只有列宽不足时才用 CSS 省略并保留title。最近 7 天趋势从今天开始往前 7 天统计已加载的真实 run 数,并用折线图展示。
从 issue #95 起,server 项目身份与权限边界正式切到顶层 package.json.id UUID。项目需要先通过 POST /api/workflows 在 server 中创建,服务端返回 UUID,并把创建者自动记为 owner;上传、发布、远程运行和权限判断都以这个 UUID 为主键。当前阶段仍保留 name 作为人类可读句柄和路由参数,因此 server web 会继续用项目名称导航,但所有 package 校验、成员授权和 run actor 记录都围绕 UUID 展开。管理端项目列表与详情会显示 visibility、ownerUserId、createdByUserId,并在项目列表直接展示上传用户头像与显示名称;公开项目第一版仍要求登录后才能运行,不支持匿名执行。(issue #95)
旧版非 UUID 项目不再兼容展示或运行。需要把 workspace/workflow/* 全量重传到 server 并清理历史目录、KV 和版本 artifact 时,可在 server 环境中执行 pnpm -C server migrate:workspace-projects。该脚本会删除遗留的 non-UUID 项目数据,再按每个 workflow 顶层 package.json.id 重新创建、上传和发布 workspace 项目,避免项目列表、权限边界和运行数据继续混入旧主键。(issue #90)
项目详情里的“设置”和“成员”工作区承载了成员权限与运行控制区域:
- 成员列表来自
GET /api/workflows/{name}/members,支持owner/manager/runner/viewer四类角色。 - owner 和 manager 可以用
PUT /api/workflows/{name}/members/{userId}授权或调整成员;当前阶段不支持 owner 转移,manager 也不能把任何人提升为 owner。 - 邀请同事加入项目用
POST /api/workflows/{name}/invitations,请求体{ "role": "manager" | "runner" | "viewer" };服务端会签发一次性 7 天有效 token 并返回{ invitation, token }。owner 角色不通过邀请授予。前端管理端把这个接口暴露在项目成员 tab 的“邀请成员”区块,复制${origin}/invite/${token}链接发给已注册同事即可。被邀请人在浏览器打开链接会进入/invite/:token接受页;后端先调用公开的GET /api/invitations/:token校验 token 并返回项目名 + 角色(接口本身不需要登录,token 就是 bearer 凭据),未登录用户在页面会被引导先登录,登录后调用POST /api/invitations/:token/accept把该用户写入项目成员表。DELETE /api/workflows/{name}/invitations/:invitationId可撤销尚未接受的邀请;过期、撤销或重复接受的 token 会返回410或409。 PATCH /api/workflows/{name}/visibility在private/public之间切换项目可见性。public 只放开“已登录用户可读/可运行”,匿名访问仍不开放。GET /api/workflows/{name}/run-users展示按 actor 聚合的运行用户统计,包括 run count、success/failed、最近运行时间和 token usage 汇总。GET /api/workflows/{name}/run-blocks、PUT /api/workflows/{name}/run-blocks/{userId}与DELETE /api/workflows/{name}/run-blocks/{userId}管理运行禁止名单;owner 和 manager 不能被加入 run block。
runner 角色只能运行,viewer 只读,owner/manager 既能运行也能上传/发布/配置;全局管理员权限仍可跨项目兜底。server web 的“运行用户统计”“禁止运行”以及最近执行表格中的“执行用户”列都直接读取这些权限和 actor 字段,不再把所有运行都视为匿名的 API key 调用。(issue #95)
执行中的任务从“执行情况统计”中移出,单独展示在“执行中的任务”卡片里。该区域可打开对应日志,也可点击“停止”调用 POST /api/workflows/{name}/runs/{runId}/cancel 停止当前 server 进程仍在管理的 runner。停止请求期间按钮会禁用并显示加载态,失败会展示错误反馈。最近执行记录仍通过 includeDraft=false 在服务端分页前排除 draft run,避免 legacy draft run 占用分页名额。服务端会把运行中占位记录与当前 runner 进程表做一次校正;超过启动缓冲且没有对应 runner 的旧占位会按 timed_out 返回,避免日志筛选把服务重启前遗留的 running 记录误认为还在执行。(issue #45、issue #74)
如果 workflow 执行到 workflow.createUserInputNode(...),server 会把 run 持久化为 waiting_for_input,并在 run record/report 中保留 pendingUserInput。这个状态是可恢复的非终态,不会被 stale running 逻辑改成 timed_out;服务重启后仍可读取等待请求并提交答案。管理端或外部调用可以通过 GET /api/workflows/{name}/runs/{runId} 查询等待表单,再调用 POST /api/workflows/{name}/runs/{runId}/user-input 提交 { requestId, values }。server 会校验 workflow、runId、requestId 和当前状态,拒绝重复提交、过期请求或错误 run,然后用同一个 runId 和原始 args/target/conversation 继续执行。(issue #87)
如果 workflow 的 executor 声明了 tools,server 运行记录的 external.toolPermissionPolicies 会保存本次工具策略快照。管理 API、server embed 和 user-input resume 请求都可以携带 toolPermissionPolicies;server 只把其中已注册工具的 enabled/autoApprove 覆盖值传给 runner。未携带时 runner 使用 executor 默认值。工具启用但未自动批准时,runtime 会通过普通 user-input 协议生成 tool-approval 等待请求;提交审批答案时,管理端或 embed 会继续带同一份策略,避免恢复执行后丢失用户选择。(issue #92)
项目详情页还有独立的“运行调度”卡片。server 会为每个 workflow 持久化 acceptsNewRuns 和 concurrency,默认接收新任务且并发数为 3;超过并发上限的 run 会先写入 waiting 记录,等可用槽位后再切换成 running。卡片可暂停接收新任务、调整并发数,并通过“停止全部”同时中止当前 server 进程可控制的 waiting 与 running 任务;响应里的 skippedRunIds 表示已列出但不在当前进程中、未被本次请求标记为已停止的任务。运行调度区会把“执行中”和“等待中”分成两个列表,并定时静默刷新 run-queue 与 in-flight 数据,用户不需要切换日志筛选或手动刷新才能看到当前任务;如果并发数输入框有未保存草稿,轮询不会覆盖正在编辑的值。接收新任务 Switch 和并发步进按钮都是即时保存控件:切换后先在本地可见更新,保存失败再回滚到上一次服务端值并显示错误,不再出现单独的“保存变更”按钮。每个任务卡片用第一行展示 runId、目标版本和时间,第二行用内容宽度的状态标签与日志/停止 icon 按钮表达当前状态,避免整条黄色横幅撑高任务卡片;按钮在保存、停止和取消期间都会禁用并显示加载态。执行情况统计面板只统计已完成记录,不再放置执行中横幅。当前队列占位和取消控制是单 server 实例语义;如果用 PostgreSQL 共享存储部署多个 server 实例,concurrency 是每实例上限,停止全部也只作用于接收该请求的实例。管理端使用 GET /api/workflows/{name}/run-queue、PUT /api/workflows/{name}/run-queue、GET /api/workflows/{name}/runs/in-flight 和 POST /api/workflows/{name}/runs/stop 驱动该区域。(issue #78、issue #79)
版本详情
版本页读取/api/workflows/{name}/versions,展示版本号、发布日志、发布时间、生命周期状态、上线方式和 latest 标记。版本号由 server 自动生成,发布日志来自 App 或 CLI 的 publish 输入。
发布时新版本会先进入 preparing,依赖安装、runtime 构建和版本记录准备完成后才更新 latest,避免 latest 指向不可运行版本。同一 workflow 的发布请求按顺序分配版本号;数据库模式使用独立连接池持有发布锁,避免 advisory lock 占用业务查询连接。latest 切换后视为发布已提交,后续自动保留策略失败不会删除已经激活的版本。默认最近 3 个自动发布的在线版本会保留为预准备可运行状态;更早的自动版本会被标为 offline,数据库与对象存储记录仍保留。手动“重新上线”的版本标记为 manual,不会被自动保留策略下线,需要在版本页点击“下线”才会关闭访问。
管理端会把 online 版本排在前面,再按发布时间倒序展示,缺少发布时间时按版本号倒序兜底,并按 20 条一页分页。发布日志只在版本行内展示,不再保留单独的“发布日志详情”空占位区域。版本行可调用 POST /api/workflows/{name}/versions/{version}/online 重新上线,或调用 POST /api/workflows/{name}/versions/{version}/offline 手动下线非 latest 版本。(issue #76)
KV 数据库
项目详情页的 “KV 数据库” tab 只读取服务器 PostgreSQL 中当前项目相关的 workflow / conversation scope KV,不混入 global scope。列表通过 GET /api/workflows/{name}/kv 分页展示,底部使用中文分页页脚切换当前页,不再把下一页追加到已有列表;筛选或刷新会回到第一页。接口返回筛选后的 total,页面 Keys 指标统计全部匹配 key,而不是当前页可见行数;payload 体积、日志关联覆盖率、scope 分布、类型分布和热点 key 仍基于当前页展示。筛选支持按 key、conversation id、run id、scope、value type 和日志关联状态筛选。日志关联状态以最后写入 run 是否仍有可打开的执行记录为准,避免旧 KV 残留的 last_run_id 显示成可跳转日志。每行展示 value 类型、大小、摘要、更新时间、conversation id、最后写入 run id 和关联状态;长 key、runId、conversationId 和 value 摘要会被限制在当前列内省略或换行,避免覆盖更新时间和操作列。issue #79 后,KV 列表接口只返回 valuePreview、类型和字节数,不再把完整 value 随 20 行列表一起传给前端;点击某一行或“查看 KV 详情”图标时会打开 shadcn/Radix Dialog 详情弹窗,并通过 GET /api/workflows/{name}/kv/entry 按 scope/key/conversationId 读取单条完整值。详情弹窗独立滚动,不挤压表格高度;复制 key、复制摘要/JSON、加载状态和错误反馈都在弹窗内完成。这样即使某些 conversation.state 达到 MB 级,也不会在打开 KV tab、筛选或翻页时阻塞浏览器主线程;前端对超大详情也只展示摘要,避免格式化大 JSON 再次卡顿。当前 server/web 使用 shadcn Input / Radix Select 作为筛选控件,表格和分页页脚使用统一 Table 与 PaginationFooter,每页固定 20 条。(issue #75、issue #79)
server 运行时会在 KV 写入时记录最后写入来源:last_workflow_name、last_run_id、last_conversation_id。新数据可从 KV 表直接跳转到执行日志;旧数据如果没有这些来源字段,会显示为“未记录”,但仍可按 scope/key/value 查看。
“全局 KV 数据”页面现在使用独立的页面内左侧导航,且左栏只保留一个 全局 KV 数据 入口,不再混入项目工作台、设置或管理中心。它读取 GET /api/kv/global,只展示 global scope。这个入口适合检查跨项目共享缓存、全局开关或平台级运行状态,也支持按 value type、run id 和可打开日志关联状态筛选。Global KV 使用同样的分页控件和全量 total 统计,避免 Keys 指标只反映当前页,并使用更紧凑的标题和统计卡片间距,避免首屏出现过大的空白。Global KV 列表同样只返回摘要,点击行会打开同一套 KV 详情弹窗,并通过 GET /api/kv/global/entry 获取单条完整值。日志入口仍保留在项目列表行、项目详情运行记录和 KV 关联位置,不放入一级导航栏。
为了配合开源工具做内部排查,server 初始化数据库时会创建只读 view:
workflow_project_kv_view:包含workflow/conversationscope,可接 NocoDB 做表格筛选视图。workflow_global_kv_view:只包含globalscope,可接 Metabase / Grafana 做统计面板。
知识库
项目详情页的 “知识库” tab 是 issue #86 对context.persistentValue 的第一批管理端应用。它使用轻量文档工作区布局:左侧是当前 workflow 项目的分页文档列表、唯一的全文搜索输入和搜索结果,主区域展示选中文档的 markdown 预览或编辑器。文档区域标题栏承载知识库信息、新建、预览/编辑切换、保存和删除等操作;更详细的存储、文档 id、创建/更新时间、摘要和 markdown 统计默认隐藏在 “知识库信息” 弹窗里。文档正文固定包含 markdown 字段,标题、摘要、创建时间和更新时间用于列表展示。
知识库只在 server PostgreSQL storage 下可用。管理端不直接读写本地 KV,也不新增数据库表;server 通过 core helper 把索引写入 context.persistentValue.workflow 的 knowledge.documents key,把单篇正文写入 knowledge.document.<id> key,底层仍复用 workflow_kv_entries。如果 server 没有启用 PostgreSQL,知识库接口返回 501,普通项目运行的本地 KV fallback 不会被用来保存这些文档。
主要操作:
- 新建文档:打开对话框输入标题和 markdown,调用
POST /api/workflows/{name}/knowledge-documents?response=summary,成功后刷新列表并选中新文档。response=summary只回传摘要,避免大 markdown 写入后再把完整正文传回管理端。 - 列出文档:左侧列表调用
GET /api/workflows/{name}/knowledge-documents?pageSize=20,只加载标题、摘要和时间等摘要数据;继续浏览时用返回的nextCursor加载下一页。列表项保持紧凑,只显示标题和更新时间,正文按需读取。 - 编辑文档:在主区域编辑模式修改标题或 markdown,调用
PUT /api/workflows/{name}/knowledge-documents/{documentId}?response=summary,成功后刷新摘要和预览。预览/编辑使用同一个模式切换按钮,保存、删除与新建入口都位于文档区域标题栏,避免文档操作挤占正文阅读高度。 - 删除文档:确认后调用
DELETE /api/workflows/{name}/knowledge-documents/{documentId}。由于 KV/PersistentValue 暂无 delete primitive,server 会从索引移除文档,并把单篇文档 key 写成 tombstone 标记用于审计。 - 查找内容:左侧搜索框按 Enter 调用
GET /api/workflows/{name}/knowledge-documents/search并查找全部文档。q是搜索词,beforeLines/afterLines控制每条匹配向上、向下返回多少行上下文,pageSize/cursor控制服务端分页;旧maxMatches仍兼容为本页大小。管理端默认每页展示 200 条,通过totalMatches/hasMore/nextCursor提示和加载更多;结果显示在左侧列表下方,不再展示独立右侧查找面板。需要指定文件 n~m 行时调用GET /api/workflows/{name}/knowledge-documents/{documentId}/lines?startLine=n&endLine=m。
run-workspace-input 的 Markdown 渲染能力,因此标题、列表、代码块、表格和链接样式与日志回放、公开 embed 和 App 输出保持一致。若 markdown 图片引用 /api/workflows/files/<fileId>,管理端会先通过带 Bearer 鉴权的 server 文件 API 拉取图片并转成临时 blob URL,再交给 Markdown 图片预览层展示;文档原文保持干净的相对 API 链接,不把 token 写入持久内容。若单篇 markdown 超过管理端预览阈值,预览区会显示大文档提示并要求切换到编辑模式查看原文,避免一次性 Markdown 渲染阻塞页面。若单篇文档包含大量截图(当前阈值为 48 张以上),知识库预览会自动切换到“稳定模式”:先渲染文字与结构,图片改为按张点击后再加载,避免数百张截图同时解析时导致滚动条抖动和页面持续重排。
本阶段评估过 Fumadocs、Nextra、Docusaurus、React Arborist 等开源方案。前三者主要面向文档站或 Next.js/MDX 内容源,React Arborist 只提供树视图能力;当前管理端知识库是 Vite 管理台内的 PostgreSQL/PersistentValue CRUD 工作区,且后端暂未提供目录树模型,因此本阶段不引入额外文档组织依赖,继续复用 shadcn/Radix 基础组件组合。
环境变量
环境变量按目标版本读取和保存。页面的变量表会把 system 变量只读、自定义变量可编辑的边界写在卡片说明中,并展示两类变量:
- systemVars:由 workflow 结构或平台推导出的系统变量。
- customVars:用户添加的自定义变量。
/api/workflows/{name}/env,传入当前 target、systemVars 和 customVars。敏感值保存在 server 侧,workflow 运行时通过 workflow.getEnv(name) 读取。
执行日志
执行日志是项目详情菜单中的 tab,放在“项目详情”下面,不再作为独立页面或独立左栏展示。日志 tab 用表格展示已发布版本执行记录,表格顶部提供关键词搜索和状态筛选,底部用 20 条分页页脚展示当前页范围和总数;表格列包含执行 ID、状态、目标版本、创建时间、耗时和操作。 从 issue #95 起,日志表格与详情会额外展示执行用户信息。server 在所有运行入口都会把 actor 快照写入 run metadata:server web 使用当前登录用户,Workspace CLI 和 App 使用当前 API key owner,external API 继续保留 body 里的user 作为外部最终用户字段,但 actor 仍记录实际 API key owner。日志列表会新增“执行用户”列,详情和回放区会显示 actorType、actorDisplayName、actorUserId 以及可用的 API key 信息,便于区分 web 用户、device-flow API key、admin key 或 external 集成流量。(issue #95)
表格最右侧提供两个主要图标按钮:第一个打开右侧“执行回放”抽屉,第二个打开右侧“详细日志”抽屉。带 runId 的日志深链会先进入轻量详细日志;执行回放改为用户点击后按需加载,默认请求 /api/workflows/{name}/runs/{runId}/replay?messageLimit=20,服务端把 conversation replay window 限制在 1 到 100 条消息之间,并把超大的 args、report 与 output item 内容裁剪为可展开预览。普通 workflow 回放会显示 target、raw arguments、args chips、状态和 Markdown 输出;conversation workflow 会按同一个 conversation_id 汇总当前 run 之前及当前 run 的输入输出,形成只读 transcript,并平铺展示 assistant output items 与附件入口。默认管理端 server/web 复用 shared run-workspace-input 样式和附件打开回调,因此日志回放中的对话输出与公开 embed / App 的展示保持一致。workflow 的最终输出如果包含 displayInput 或 display_input,conversation 回放会优先把它作为用户输入展示,适合 webhook 等原始参数很大的场景;原始 args、provider payload、完整 output item 内容和节点输入仍保留在详细日志中用于排查。(issue #47、issue #79)
运行列表接口只传输表格摘要字段,不携带 stdout、stderr 或完整 report;概览页执行统计也只补全最近有限数量的摘要记录。回放只读取执行记录,不提供 run、send、upload 或新建会话入口。conversation 回放以当前 run 的 createdAt 为边界,之后新增的同会话执行不会被加载进这一次回放。需要排查单条 run 的 stdout、stderr、runner diagnostics 或 report.json 时,打开复用 shared Logs workspace 的“详细日志”抽屉即可。对应接口是 GET /api/workflows/{name}/runs/{runId}/replay,只服务管理端展示,不改变现有执行 API。
审计日志
issue #96 起,项目管理之外新增全局“审计日志”工作区,用于补齐项目级日志无法覆盖的账号、权限和跨项目敏感操作。和项目详情里的执行日志不同,审计页是全局视角:它会混合展示登录、登出、API key、device flow、角色变更、项目上传/发布、版本上下线、环境变量保存、Embed/Webhook 配置、external / embed 运行、取消执行以及权限拒绝等事件。 审计事件模型至少包含:- 事件类型、结果、principal 类型、actor userId / displayName。
- 资源类型、资源 ID。
- request IP、User-Agent。
- 结构化 context,以及可选的 workflow / project / run 关联字段。
- 事件创建时间和分页 cursor。
audit.read;项目 owner、manager 或普通 runner/viewer 都不能因为能看本项目执行日志而自动读取全局审计。管理端查询会记录 audit.events.list 审计事件本身,便于追踪谁在什么时候检索过全局审计。
审计详情中的 context 会经过统一脱敏,不保存或回显以下明文:
- token、access token、refresh token、API key、admin key。
- secret、Webhook secret、Authorization、cookie、session 标识。
- 环境变量原值和其它凭据类字段。
Embed 工作流入口
Embed 配置用于生成公开访问页面。开启后 server 返回 publicUrl,可嵌入 iframe 或直接打开。管理端工作流入口页只保留入口设置,不再显示左侧 Embed preview。
主要字段:
enabled:是否启用公开入口。title/description:入口配置元信息;公开嵌入页不再把它们作为首屏大标题展示。target:latest 或在线的具体版本号。token:公开入口 token,可重置。重置后旧链接失效。
server/src/routes/embed.ts 注入 bootstrap,实际界面使用和 /app Run workspace、默认 server/web preview 相同的共享 React 输入模块,不依赖 Electron 或管理端登录态。页面会先读取 /embed/api/workflows/{workflowName}/site,然后展示适合 iframe 的全屏紧凑 Run workspace 视图,不再显示大标题、brand、overview 或 meta 区块。
普通 workflow 在声明 executor params 时默认进入 Form 输入,并提供与 /app Run workspace 一致的 Form / Raw 切换;没有 params 或切到 Raw 时使用 CLI args 输入。Form 会按 param type 和 options/control 渲染文本、长文本 textarea、数字、布尔开关、单选、下拉、多选和 file 字段,不再额外展示 args chips;字段 description 会作为输入框 placeholder 使用,输入框下方不再重复展示说明或 format: json 这类实现细节。图片 file param 会作为缩略图附件展示,并同时提供上传图片与添加剪贴板图片两个入口;公开 embed 会把剪贴板图片通过 server 文件接口上传成 managed file reference。剪贴板里没有图片、浏览器拒绝读取或上传失败时必须在 Run workspace 内显示错误反馈,成功时只添加附件缩略图,不额外弹成功提示。图片点击后在当前页面弹出大图预览层,不在输入区暴露 managed file JSON,也不会打开系统应用、新窗口或 about:blank;切到 Raw 模式时仍可查看和编辑 CLI 风格 args。panel: "auxiliary" 的参数默认收进二级输入面板。多选字段会生成一个 flag 加 JSON 数组字符串,file param 支持 accept/multiple 和上传后的文件引用;运行时向 /embed/api/workflows/{workflowName}/run/stream 发送 args 并消费 NDJSON runtime events,output 节点 chunk 会在 run 仍处于 running 时增量渲染到当前 Output 区,不等最终 blocking JSON 才一次性显示。普通 workflow 运行中时,shared Run workspace 右上区域也会显示 Cancel,它会同时中止当前流式请求并调用 server 取消接口,把该 run 收敛成 aborted 状态;不再只有 conversation 模式能中止运行。(issue #62、issue #68、issue #80、issue #89、issue #90)
公开 embed 读取 workflow 结构后,如果发现 executor tools,会在 shared Run workspace 顶部标题栏右侧显示“工具权限”统计区域,不放在 Input/composer/footer 内。触发区展示工具主图标,以及全部、启用、自动批准三组图标和数量;弹层从标题栏向下展开,并用表格展示可用工具、“启用工具”和“自动批准”两列开关。用户调整后,覆盖值只保存到公开页面 localStorage,刷新后恢复;运行、流式运行和 user-input 恢复请求都会携带同一份 toolPermissionPolicies。弹层只管理全局策略,不直接提交某一次运行中的审批答案;具体工具审批仍由等待表单完成。(issue #92)
conversation embed 现在把身份信息直接挂到每条消息两侧,而不是额外渲染顶部 identity header:assistant / 项目消息左侧显示当前公开版本对应 package.json > workflowCode.projectCard 的图标;user 消息右侧显示当前登录用户头像与默认 fallback。由于公开 embed 支持 latest 或指定在线版本,这里的项目图标以当前 resolvedTarget 对应版本为准,而不是 server 上当前 draft 或其它版本的图标配置。
同一份版本级 projectCard 现在也会驱动浏览器选项卡 favicon。embed HTML 先以内置 Workflow Code 品牌图标作为默认 favicon,/embed/api/workflows/{workflowName}/site 返回后再根据当前 resolvedTarget 的图标覆盖:显式 iconUrl 会先在前端本地化为 blob: URL 后写入 tab,内置 workflow / conversation / bot 等图标则生成同色系的 SVG favicon。这样在 latest 与不同在线版本之间切换时,页面内项目身份区和浏览器 tab 图标保持一致;其中默认 workflow 项目会和页面一样使用内置 Workflow 图标,而不是品牌 mark。(issue #90)
开启 conversation 的 workflow 会切换成聊天界面:共享组件按容器宽度自适应,宽容器下左侧显示会话列表与新建入口,右侧显示当前 transcript 和底部 input/composer;左侧会话列表可拖拽调整宽度,也可隐藏后从主区域重新展示;会话按创建时间新到旧展示,新建会话必须位于第一条,长列表默认显示最新一页并向下分页加载更旧会话,公开 embed 的本地会话缓存会保留足够历史供分页使用,但不会把 activeRequestId、activeRunId 或 running / queued 临时消息状态作为可恢复状态保存;刷新、断线或上次执行中断后,这些临时消息会被标记为失败/中断,页面仍可新建会话、切换会话和继续输入。重复点击新建会话时,如果已有未发送、未运行的空白会话,会复用并选中该会话,不会生成多个空白会话;窄容器下仍使用顶部选择/新建会话、中间 transcript、底部 composer 的单列结构。
assistant 回复保存为 items: WorkflowOutputItem[],conversation transcript 中用户输入使用右侧轻量气泡展示,assistant 文本和 output item 按顺序平铺展示;默认 Output 的 markdown/text item 直接作为文字展示,工具调用、JSON、带自定义 title/icon/metadata 或默认折叠的 item 才渲染为独立折叠块,消息与 item 间距约 5px。普通 embed 输出和日志 replay 使用同一套 item 展示,contentType 决定 Markdown、纯文本、格式化 JSON 或音频播放器;音频 content 使用 { src, mimeType, name, size, durationMs?, format? },播放器只接受安全 data:audio/...;base64,... 和 http(s) URL。createDetailsOutputNode 会输出默认折叠的独立 item,预置 icon token 会在 UI summary 中映射为 Lucide 图标,非 React 的日志、CLI 和 stdout 仍保留 [list] 这类文本 token。composer 同样默认使用 Form、支持 Raw 切换、file/image 上传和 pending attachments,发送时继续携带签名后的 conversation_id,图片等文件输入会作为 server managed file JSON refs 写入对应 file param;图片在 composer 中只显示缩略图,发送后显示在用户消息上方,不把 refs JSON 显示成消息文本,点击缩略图在当前页面弹层预览大图。conversation 发送使用 /embed/api/workflows/{workflowName}/run/stream 读取 NDJSON runtime events,output 节点的流式 chunk 会在 workflow 仍处于 running 时增量追加到当前 assistant 平铺消息内容,字符串 chunk 写入默认 Markdown item,结构化 output_item_* chunk 按 item 边界合并;timer_started / timer_finished 会把业务计时卡片写进当前 assistant 消息和普通输出区,最终 run_result.report.timers 再做收尾校准;token_usage_updated 运行事件不会提前写入正在生成的消息,最终 run_result / report 回来后才在完成的 assistant 消息上展示 token badge。发送期间会在对应会话列表项上显示 running 标记,只禁用该会话自身的 composer/Send,不阻止切换到其它会话或新建空白会话;切走后原会话仍继续执行并把流式输出写回原会话。如果 workflow 开启 conversation.inputQueue,运行中新增输入只进入当前会话队列;当前 run 完成、失败或取消后会清理 active 标记,已排队项按顺序继续执行,之后新增输入恢复为普通发送,不再误入等待队列。Advanced options 在 composer 左下角以小按钮呈现,点击后在 composer 内从下向上展开局部弹窗;对话运行中仍可打开该弹窗,并可点击 Cancel 中止当前 embed 请求,server 会把客户端断开映射到 workflow runner 的 abort signal。(issue #62、issue #68、issue #80、issue #83、issue #88)
公开 embed 也支持用户输入节点。流式运行时如果收到 user_input_requested,页面会先保持运行中状态;最终 run_result 返回 status: "waiting_for_input" 与 pendingUserInput 后,才在当前 Run workspace / conversation 中显示可提交的等待表单,避免用户在等待 run 尚未持久化前提交。提交时按钮进入 loading 并禁用重复提交,随后调用 /embed/api/workflows/{workflowName}/runs/{runId}/user-input/stream 并继续消费 NDJSON runtime events;因此恢复执行后的 output chunk、计时卡片和最终结果会沿用与首次运行相同的 shared 流式展示路径,不会等到长任务结束后一次性刷新。提交成功后继续使用同一个 runId 渲染后续 output,失败时在等待卡片中展示错误。表单字段仍复用 workflow 节点的 params,包括文本、数字、布尔、JSON 和 file 参数。(issue #87、issue #89)
会话标题优先展示 context.conversation.setTitle()、最终 report 或用户手动重命名得到的标题,未设置时展示由 conversation_id 派生的默认标签;标题下方展示更新时间和消息数,不再展示完整 conversation id。列表项支持右键菜单,移动/窄屏通过更多按钮打开同一菜单,可重命名、删除或锁定标题;锁定后 embed 会继续执行 workflow,但忽略 workflow 代码推来的标题更新,手动解锁后才允许改名。Raw 输入框按 Enter 发送、Shift+Enter 换行;结构化 Form 输入在文本类输入框中按 Enter 发送,组合输入法输入中不会误触发。(issue #83)
配置加载、运行、成功、失败和配置不可用状态都会在触发区域直接显示;移动端会折成单列,并保持输入区和输出/消息区可滚动。
Webhook
Webhook 用于外部系统触发 workflow。配置页把签名入口、body 映射和过滤规则放在同一张操作卡里,Webhook delivery logs 独立展示投递结果;完整 run 排查仍进入项目菜单下的“执行日志”表格和右侧抽屉。配置项包括:
- 是否启用。
- 目标版本。
X-Workflow-Webhook-Secret签名头。- body 到 workflow input 的映射,例如
event.text -> message。 - 请求体过滤规则。Webhook 配置页会展示当前 body filters 的分组数量、条件数量、路径、操作符和值;未配置时会明确显示“未配置”。默认不配置时所有通过签名校验的请求都会触发;配置后只有请求体字段命中过滤条件才会创建 run。过滤规则按“任意分组命中即可”判断,分组内所有条件都必须命中。被过滤的请求返回成功响应但不会出现在执行日志表格。(issue #72, issue #73)
POST /webhooks/{workflowName}/trigger。服务端也接受 GitLab Project Hooks 常用的 X-Gitlab-Token 作为兼容 fallback。
过滤规则使用和映射相同的 body path 语法,$ 表示完整 JSON body。条件支持 equals、not_equals、contains、starts_with、ends_with、matches、in、exists 和 missing。例如只允许 GitLab push,以及以 @workflow-review 或 @workflow-build 开头的 note:
移动端与深色模式
追踪
本文档首版由 issue #32 记录。公开 Embed 页面重构由 issue #35 记录。日志只读回放由 issue #43 记录。执行记录分页、进行中记录与入口设置简化由 issue #45 记录。版本准备态、在线保留策略和版本上下线管理由 issue #60 记录。项目详情全量统计、运行中任务卡片、停止任务和 7 天折线趋势由 issue #74 记录。KV 数据库分页与全量 Keys 统计由 issue #75 记录。版本页发布日志详情空区域移除由 issue #76 记录。首页 Embed 新页面打开与 Global KV 间距调整由 issue #77 记录。项目运行队列、并发控制和停止全部任务由 issue #78 记录。server web 极简界面重构由 issue #79 记录。项目知识库由 issue #86 记录。默认页面行为对齐server/web/src/App.tsx、DetailPage.tsx、GlobalKVPage.tsx、server/src/storage.ts、server/src/routes/api.ts 和 server/src/routes/embed.ts。